From f30d94b20a567ad971b984a0a08b2f7ad848303a Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 8 Jun 2022 17:30:32 +0200 Subject: [PATCH 001/422] - setup for Java 18 Former-commit-id: 378a7681864e7115b5825507635647b0ded8515d --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 1617cd6fb5..33feb14ae6 100755 --- a/pom.xml +++ b/pom.xml @@ -75,9 +75,9 @@ UTF-8 - 17 - 17 - 16 + 18 + 18 + 18 1.6.21 ${jdk.language.version} ${jdk.language.version} From ea397c6f3468f5910e6e3647d64ab48b91a01199 Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 8 Jun 2022 17:43:37 +0200 Subject: [PATCH 002/422] - remove unused table width adjuster Former-commit-id: cc48039c6354c4d48620141bacdfc9f8567419eb --- .../java/mediathek/tool/table/MVTable.java | 3 - .../mediathek/tool/table/WidthAdjuster.java | 80 ------------------- 2 files changed, 83 deletions(-) delete mode 100644 src/main/java/mediathek/tool/table/WidthAdjuster.java diff --git a/src/main/java/mediathek/tool/table/MVTable.java b/src/main/java/mediathek/tool/table/MVTable.java index 9012e60efc..e4d3da3ef1 100644 --- a/src/main/java/mediathek/tool/table/MVTable.java +++ b/src/main/java/mediathek/tool/table/MVTable.java @@ -56,9 +56,6 @@ public MVTable(int maxColumns, boolean @NotNull [] visibleColumStore, setAutoCreateRowSorter(true); setAutoResizeMode(JTable.AUTO_RESIZE_OFF); - getTableHeader().addMouseListener(new WidthAdjuster(this)); - - breite = new int[maxSpalten]; Arrays.fill(breite,-1); diff --git a/src/main/java/mediathek/tool/table/WidthAdjuster.java b/src/main/java/mediathek/tool/table/WidthAdjuster.java deleted file mode 100644 index be5807e6c6..0000000000 --- a/src/main/java/mediathek/tool/table/WidthAdjuster.java +++ /dev/null @@ -1,80 +0,0 @@ -package mediathek.tool.table; - -import javax.swing.*; -import javax.swing.table.TableCellRenderer; -import javax.swing.table.TableColumn; -import java.awt.*; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.util.List; - -class WidthAdjuster extends MouseAdapter { - - private static final int EPSILON = 5; //boundary sensitivity - private final JTable table; - private List listeSortKeys = null; - - protected WidthAdjuster(final JTable table) { - this.table = table; - } - - @Override - public void mousePressed(final MouseEvent evt) { - if (evt.getClickCount() == 1) { - if (table.getRowSorter() != null) { - listeSortKeys = table.getRowSorter().getSortKeys(); - } else { - listeSortKeys = null; - } - } - if (evt.getClickCount() > 1 && isUsingResizeCursor()) { - resizeColumn(getLeftColumn(evt.getPoint())); - } - } - - private boolean isUsingResizeCursor() { - final Cursor cursor = table.getTableHeader().getCursor(); - return cursor.equals(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR)) - || cursor.equals(Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR)); - } - - //if near the boundary, will choose left column - private int getLeftColumn(final Point pt) { - pt.x -= EPSILON; - return table.getTableHeader().columnAtPoint(pt); - } - - private void resizeColumn(final int col) { - final TableColumn tc = table.getColumnModel().getColumn(col); - TableCellRenderer tcr = tc.getHeaderRenderer(); - - if (tcr == null) { - tcr = table.getTableHeader().getDefaultRenderer(); - } - - Object obj = tc.getHeaderValue(); - Component comp = tcr.getTableCellRendererComponent(table, obj, false, false, 0, 0); - int maxWidth = comp.getPreferredSize().width; - - for (int i = 0, ub = table.getRowCount(); i != ub; ++i) { - tcr = table.getCellRenderer(i, col); - obj = table.getValueAt(i, col); - comp = tcr.getTableCellRendererComponent(table, obj, false, false, i, col); - final int w = comp.getPreferredSize().width; - if (w > maxWidth) { - maxWidth = w; - } - } - - maxWidth += 10; //and room to grow... - tc.setPreferredWidth(maxWidth); //remembers the value - tc.setWidth(maxWidth); //forces layout, repaint - - if (listeSortKeys != null) { - if (!listeSortKeys.isEmpty()) { - table.getRowSorter().setSortKeys(listeSortKeys); - } - } - } - -} From 4ea8f42278617d5a57634f3aedb4daaf47799bbc Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 8 Jun 2022 18:29:22 +0200 Subject: [PATCH 003/422] - auto-adjust row height in film table Former-commit-id: beb63906b4c2155d35be8a8de2ab1b7c14ec7036 --- .../tool/cellrenderer/CellRendererFilme.java | 75 ++++++++++++------- .../mediathek/tool/table/MVFilmTable.java | 5 ++ 2 files changed, 55 insertions(+), 25 deletions(-) diff --git a/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java b/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java index c5af2a3b1b..6a4426c21e 100644 --- a/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java +++ b/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java @@ -15,6 +15,8 @@ import javax.swing.*; import java.awt.*; +import java.util.ArrayList; +import java.util.Objects; public class CellRendererFilme extends CellRendererBaseWithStart { private static final Logger logger = LogManager.getLogger(CellRendererFilme.class); @@ -48,9 +50,10 @@ private JTextArea createTextArea(String content) { var textArea = new JTextArea(); textArea.setLineWrap(true); textArea.setWrapStyleWord(true); - textArea.setText(content); + textArea.setText(Objects.toString(content, "")); textArea.setForeground(getForeground()); textArea.setBackground(getBackground()); + textArea.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); var fontSize = textArea.getFont().getSize2D(); var labelFont = UIManager.getFont("Label.font"); @@ -59,6 +62,27 @@ private JTextArea createTextArea(String content) { return textArea; } + private final java.util.List> rowAndCellHeightList = new ArrayList<>(); + + private void adjustRowHeight(JComponent comp, JTable table, int row, int column) { + int cWidth = table.getCellRect(row, column, false).width; + comp.setSize(new Dimension(cWidth, 1000)); + + int preferredHeight = comp.getPreferredSize().height; + while (rowAndCellHeightList.size() <= row) { + rowAndCellHeightList.add(new ArrayList<>(column)); + } + var cellHeightList = rowAndCellHeightList.get(row); + while (cellHeightList.size() <= column) { + cellHeightList.add(0); + } + cellHeightList.set(column, preferredHeight); + int max = cellHeightList.stream().max(Integer::compare).get(); + if (table.getRowHeight(row) != max) { + table.setRowHeight(row, max); + } + } + @Override public Component getTableCellRendererComponent( JTable table, @@ -75,19 +99,19 @@ public Component getTableCellRendererComponent( final int columnModelIndex = table.convertColumnIndexToModel(column); final DatenFilm datenFilm = (DatenFilm) table.getModel().getValueAt(rowModelIndex, DatenFilm.FILM_REF); final DatenDownload datenDownload = Daten.getInstance().getListeDownloadsButton().getDownloadUrlFilm(datenFilm.getUrlNormalQuality()); - final boolean isBookMarked = datenFilm.isBookmarked(); - final var mvTable = (MVTable)table; + final boolean isBookMarked = datenFilm.isBookmarked(); + final var mvTable = (MVTable) table; setFont((mvTable.getDefaultFont())); - //shortcut if we want to have line breaks, use text areas and skip the rest - if (mvTable.isLineBreak()) { - switch (columnModelIndex) { - case DatenFilm.FILM_THEMA, DatenFilm.FILM_TITEL, DatenFilm.FILM_URL -> { - var textArea = createTextArea(value.toString()); - applyColorSettings(textArea, datenFilm, datenDownload, isSelected, isBookMarked); - return textArea; - } + //for thema, titel and URL we will auto adjust row height + switch (columnModelIndex) { + case DatenFilm.FILM_THEMA, DatenFilm.FILM_TITEL, DatenFilm.FILM_URL -> { + var textArea = createTextArea(value.toString()); + applyColorSettings(textArea, datenFilm, datenDownload, isSelected, isBookMarked); + textArea.validate(); + adjustRowHeight(textArea, table, row, column); + return textArea; } } @@ -106,24 +130,24 @@ public Component getTableCellRendererComponent( case DatenFilm.FILM_AUFZEICHNEN: handleButtonDownloadColumn(isSelected); break; - + case DatenFilm.FILM_MERKEN: handleButtonBookmarkColumn(isBookMarked, isSelected, datenFilm.isLivestream()); break; - + case DatenFilm.FILM_SENDER: if (mvTable.showSenderIcons()) { - Dimension targetDim = getSenderCellDimension(table, row,columnModelIndex); + Dimension targetDim = getSenderCellDimension(table, row, columnModelIndex); setSenderIcon(value.toString(), targetDim); } break; - case DatenFilm.FILM_TITEL: - var title = datenFilm.getTitle(); - var columnWidth = table.getColumnModel().getColumn(columnModelIndex).getWidth(); - if (columnWidth < table.getFontMetrics(table.getFont()).stringWidth(title)) - setToolTipText(title); - break; + case DatenFilm.FILM_TITEL: + var title = datenFilm.getTitle(); + var columnWidth = table.getColumnModel().getColumn(columnModelIndex).getWidth(); + if (columnWidth < table.getFontMetrics(table.getFont()).stringWidth(title)) + setToolTipText(title); + break; } applyColorSettings(this, datenFilm, datenDownload, isSelected, isBookMarked); @@ -136,11 +160,13 @@ public Component getTableCellRendererComponent( /** * Apply the specific horizontal alignment to the cell based on column + * * @param columnModelIndex the current column index */ private void applyHorizontalAlignment(final int columnModelIndex) { switch (columnModelIndex) { - case DatenFilm.FILM_NR, DatenFilm.FILM_DATUM, DatenFilm.FILM_ZEIT, DatenFilm.FILM_DAUER, DatenFilm.FILM_ABSPIELEN, DatenFilm.FILM_AUFZEICHNEN, DatenFilm.FILM_MERKEN -> setHorizontalAlignment(SwingConstants.CENTER); + case DatenFilm.FILM_NR, DatenFilm.FILM_DATUM, DatenFilm.FILM_ZEIT, DatenFilm.FILM_DAUER, DatenFilm.FILM_ABSPIELEN, DatenFilm.FILM_AUFZEICHNEN, DatenFilm.FILM_MERKEN -> + setHorizontalAlignment(SwingConstants.CENTER); case DatenFilm.FILM_GROESSE -> setHorizontalAlignment(SwingConstants.RIGHT); } } @@ -173,7 +199,7 @@ private void applyColorSettings(Component c, @NotNull DatenFilm datenFilm, Daten if (geoMelden) { //only apply geo block colors when we haven´t changed the background for seen history if (!hasBeenSeen) { - setupGeoblockingBackground(c, datenFilm.getGeo().orElse(""), isSelected); + setupGeoblockingBackground(c, datenFilm.getGeo().orElse(""), isSelected); } } } @@ -209,13 +235,12 @@ private void handleButtonDownloadColumn(final boolean isSelected) { // Button Aufzeichnen setIconAndToolTip(isSelected, normalDownloadIcon, selectedDownloadIcon, "Film aufzeichnen"); } - + private void handleButtonBookmarkColumn(final boolean isBookMarked, final boolean isSelected, boolean isLivestream) { if (isLivestream) { setIcon(null); setToolTipText(""); - } - else { + } else { // Button Merken setToolTipText(isBookMarked ? "Film aus Merkliste entfernen" : "Film merken"); setIcon(isBookMarked ? (isSelected ? selectedBookmarkIconHighlighted : selectedBookmarkIcon) : normalBookmarkIcon); diff --git a/src/main/java/mediathek/tool/table/MVFilmTable.java b/src/main/java/mediathek/tool/table/MVFilmTable.java index a892c94120..2b8b51ec7b 100644 --- a/src/main/java/mediathek/tool/table/MVFilmTable.java +++ b/src/main/java/mediathek/tool/table/MVFilmTable.java @@ -63,6 +63,11 @@ protected void saveDefaultFontSize() { config.setProperty(ApplicationConfiguration.TAB_FILM_FONT_SIZE, fontSize); } + @Override + public void calculateRowHeight() { + // do nothing, cell renderer will adjust row height... + } + private void resetFilmeTab(int i) { //logger.debug("resetFilmeTab()"); From c0ce73035a8b7c2ea0d30c012ac20b13c2e19e4d Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 8 Jun 2022 22:54:36 +0200 Subject: [PATCH 004/422] Revert "- auto-adjust row height in film table" This reverts commit 1613dfec1a35f78e7bde81ed1a5998a2e6da04f0 [formerly 533ce6ae455aee47e10a04d47a54f13999856f1a]. Former-commit-id: b08ef45be64cdfc368d174a5de3ff31002fd2ecf --- .../tool/cellrenderer/CellRendererFilme.java | 75 +++++++------------ .../mediathek/tool/table/MVFilmTable.java | 5 -- 2 files changed, 25 insertions(+), 55 deletions(-) diff --git a/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java b/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java index 6a4426c21e..c5af2a3b1b 100644 --- a/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java +++ b/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java @@ -15,8 +15,6 @@ import javax.swing.*; import java.awt.*; -import java.util.ArrayList; -import java.util.Objects; public class CellRendererFilme extends CellRendererBaseWithStart { private static final Logger logger = LogManager.getLogger(CellRendererFilme.class); @@ -50,10 +48,9 @@ private JTextArea createTextArea(String content) { var textArea = new JTextArea(); textArea.setLineWrap(true); textArea.setWrapStyleWord(true); - textArea.setText(Objects.toString(content, "")); + textArea.setText(content); textArea.setForeground(getForeground()); textArea.setBackground(getBackground()); - textArea.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); var fontSize = textArea.getFont().getSize2D(); var labelFont = UIManager.getFont("Label.font"); @@ -62,27 +59,6 @@ private JTextArea createTextArea(String content) { return textArea; } - private final java.util.List> rowAndCellHeightList = new ArrayList<>(); - - private void adjustRowHeight(JComponent comp, JTable table, int row, int column) { - int cWidth = table.getCellRect(row, column, false).width; - comp.setSize(new Dimension(cWidth, 1000)); - - int preferredHeight = comp.getPreferredSize().height; - while (rowAndCellHeightList.size() <= row) { - rowAndCellHeightList.add(new ArrayList<>(column)); - } - var cellHeightList = rowAndCellHeightList.get(row); - while (cellHeightList.size() <= column) { - cellHeightList.add(0); - } - cellHeightList.set(column, preferredHeight); - int max = cellHeightList.stream().max(Integer::compare).get(); - if (table.getRowHeight(row) != max) { - table.setRowHeight(row, max); - } - } - @Override public Component getTableCellRendererComponent( JTable table, @@ -99,19 +75,19 @@ public Component getTableCellRendererComponent( final int columnModelIndex = table.convertColumnIndexToModel(column); final DatenFilm datenFilm = (DatenFilm) table.getModel().getValueAt(rowModelIndex, DatenFilm.FILM_REF); final DatenDownload datenDownload = Daten.getInstance().getListeDownloadsButton().getDownloadUrlFilm(datenFilm.getUrlNormalQuality()); - final boolean isBookMarked = datenFilm.isBookmarked(); - final var mvTable = (MVTable) table; + final boolean isBookMarked = datenFilm.isBookmarked(); + final var mvTable = (MVTable)table; setFont((mvTable.getDefaultFont())); - //for thema, titel and URL we will auto adjust row height - switch (columnModelIndex) { - case DatenFilm.FILM_THEMA, DatenFilm.FILM_TITEL, DatenFilm.FILM_URL -> { - var textArea = createTextArea(value.toString()); - applyColorSettings(textArea, datenFilm, datenDownload, isSelected, isBookMarked); - textArea.validate(); - adjustRowHeight(textArea, table, row, column); - return textArea; + //shortcut if we want to have line breaks, use text areas and skip the rest + if (mvTable.isLineBreak()) { + switch (columnModelIndex) { + case DatenFilm.FILM_THEMA, DatenFilm.FILM_TITEL, DatenFilm.FILM_URL -> { + var textArea = createTextArea(value.toString()); + applyColorSettings(textArea, datenFilm, datenDownload, isSelected, isBookMarked); + return textArea; + } } } @@ -130,24 +106,24 @@ public Component getTableCellRendererComponent( case DatenFilm.FILM_AUFZEICHNEN: handleButtonDownloadColumn(isSelected); break; - + case DatenFilm.FILM_MERKEN: handleButtonBookmarkColumn(isBookMarked, isSelected, datenFilm.isLivestream()); break; - + case DatenFilm.FILM_SENDER: if (mvTable.showSenderIcons()) { - Dimension targetDim = getSenderCellDimension(table, row, columnModelIndex); + Dimension targetDim = getSenderCellDimension(table, row,columnModelIndex); setSenderIcon(value.toString(), targetDim); } break; - case DatenFilm.FILM_TITEL: - var title = datenFilm.getTitle(); - var columnWidth = table.getColumnModel().getColumn(columnModelIndex).getWidth(); - if (columnWidth < table.getFontMetrics(table.getFont()).stringWidth(title)) - setToolTipText(title); - break; + case DatenFilm.FILM_TITEL: + var title = datenFilm.getTitle(); + var columnWidth = table.getColumnModel().getColumn(columnModelIndex).getWidth(); + if (columnWidth < table.getFontMetrics(table.getFont()).stringWidth(title)) + setToolTipText(title); + break; } applyColorSettings(this, datenFilm, datenDownload, isSelected, isBookMarked); @@ -160,13 +136,11 @@ public Component getTableCellRendererComponent( /** * Apply the specific horizontal alignment to the cell based on column - * * @param columnModelIndex the current column index */ private void applyHorizontalAlignment(final int columnModelIndex) { switch (columnModelIndex) { - case DatenFilm.FILM_NR, DatenFilm.FILM_DATUM, DatenFilm.FILM_ZEIT, DatenFilm.FILM_DAUER, DatenFilm.FILM_ABSPIELEN, DatenFilm.FILM_AUFZEICHNEN, DatenFilm.FILM_MERKEN -> - setHorizontalAlignment(SwingConstants.CENTER); + case DatenFilm.FILM_NR, DatenFilm.FILM_DATUM, DatenFilm.FILM_ZEIT, DatenFilm.FILM_DAUER, DatenFilm.FILM_ABSPIELEN, DatenFilm.FILM_AUFZEICHNEN, DatenFilm.FILM_MERKEN -> setHorizontalAlignment(SwingConstants.CENTER); case DatenFilm.FILM_GROESSE -> setHorizontalAlignment(SwingConstants.RIGHT); } } @@ -199,7 +173,7 @@ private void applyColorSettings(Component c, @NotNull DatenFilm datenFilm, Daten if (geoMelden) { //only apply geo block colors when we haven´t changed the background for seen history if (!hasBeenSeen) { - setupGeoblockingBackground(c, datenFilm.getGeo().orElse(""), isSelected); + setupGeoblockingBackground(c, datenFilm.getGeo().orElse(""), isSelected); } } } @@ -235,12 +209,13 @@ private void handleButtonDownloadColumn(final boolean isSelected) { // Button Aufzeichnen setIconAndToolTip(isSelected, normalDownloadIcon, selectedDownloadIcon, "Film aufzeichnen"); } - + private void handleButtonBookmarkColumn(final boolean isBookMarked, final boolean isSelected, boolean isLivestream) { if (isLivestream) { setIcon(null); setToolTipText(""); - } else { + } + else { // Button Merken setToolTipText(isBookMarked ? "Film aus Merkliste entfernen" : "Film merken"); setIcon(isBookMarked ? (isSelected ? selectedBookmarkIconHighlighted : selectedBookmarkIcon) : normalBookmarkIcon); diff --git a/src/main/java/mediathek/tool/table/MVFilmTable.java b/src/main/java/mediathek/tool/table/MVFilmTable.java index 2b8b51ec7b..a892c94120 100644 --- a/src/main/java/mediathek/tool/table/MVFilmTable.java +++ b/src/main/java/mediathek/tool/table/MVFilmTable.java @@ -63,11 +63,6 @@ protected void saveDefaultFontSize() { config.setProperty(ApplicationConfiguration.TAB_FILM_FONT_SIZE, fontSize); } - @Override - public void calculateRowHeight() { - // do nothing, cell renderer will adjust row height... - } - private void resetFilmeTab(int i) { //logger.debug("resetFilmeTab()"); From 30902b45ad90128ba70e69659d0a830eb048103d Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 9 Jun 2022 08:01:34 +0200 Subject: [PATCH 005/422] satisfy static analyzer warning Former-commit-id: 79e9556b7a24c527586b757817ebfdc0f2627826 --- src/main/java/mediathek/tool/table/MVTable.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/mediathek/tool/table/MVTable.java b/src/main/java/mediathek/tool/table/MVTable.java index e4d3da3ef1..a890038b5c 100644 --- a/src/main/java/mediathek/tool/table/MVTable.java +++ b/src/main/java/mediathek/tool/table/MVTable.java @@ -408,7 +408,8 @@ public void resetTabelle() { * @return the configuration data as string. */ private String prepareTableConfigurationData() { - String b, r; + StringBuilder b; + StringBuilder r; int[] reihe_ = new int[maxSpalten]; int[] breite_ = new int[maxSpalten]; for (int i = 0; i < reihe_.length && i < getModel().getColumnCount(); ++i) { @@ -420,11 +421,11 @@ private String prepareTableConfigurationData() { breite_[i] = model.getColumn(convertColumnIndexToView(i)).getWidth(); } - b = Integer.toString(breite_[0]); - r = Integer.toString(reihe_[0]); + b = new StringBuilder(Integer.toString(breite_[0])); + r = new StringBuilder(Integer.toString(reihe_[0])); for (int i = 1; i < breite.length; i++) { - b = b + ',' + breite_[i]; - r = r + ',' + reihe_[i]; + b.append(',').append(breite_[i]); + r.append(',').append(reihe_[i]); } listeSortKeys = this.getRowSorter().getSortKeys(); From 4b0ef495e3b911906737ef4587acbdda3d4a5e1f Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 9 Jun 2022 09:59:37 +0200 Subject: [PATCH 006/422] - create common toolbar Former-commit-id: 06a7f78918c7bd39698027431c04f4df72ebbcf1 --- src/main/java/mediathek/mac/MediathekGuiMac.java | 12 ++++++++++++ src/main/java/mediathek/mainwindow/MediathekGui.java | 12 +++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/main/java/mediathek/mac/MediathekGuiMac.java b/src/main/java/mediathek/mac/MediathekGuiMac.java index a9c7a3ce7c..bd0fe06779 100644 --- a/src/main/java/mediathek/mac/MediathekGuiMac.java +++ b/src/main/java/mediathek/mac/MediathekGuiMac.java @@ -1,5 +1,6 @@ package mediathek.mac; +import com.formdev.flatlaf.util.SystemInfo; import mediathek.config.Konstanten; import mediathek.gui.actions.ShowAboutAction; import mediathek.gui.messages.DownloadFinishedEvent; @@ -13,6 +14,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import javax.swing.*; import java.awt.*; import java.io.IOException; @@ -135,5 +137,15 @@ private void setupUserInterfaceForOsx() { if (desktop.isSupported(Desktop.Action.APP_PREFERENCES)) desktop.setPreferencesHandler(e -> getSettingsDialog().setVisible(true)); + + getRootPane().putClientProperty("apple.awt.windowTitleVisible", false); + System.setProperty("apple.awt.application.appearance", "system"); + + if (SystemInfo.isMacFullWindowContentSupported) { + getRootPane().putClientProperty("apple.awt.fullWindowContent", true); + getRootPane().putClientProperty("apple.awt.transparentTitleBar", true); + + commonToolBar.add(Box.createHorizontalStrut(70),0); + } } } diff --git a/src/main/java/mediathek/mainwindow/MediathekGui.java b/src/main/java/mediathek/mainwindow/MediathekGui.java index afdf37f701..e264c4a8b5 100644 --- a/src/main/java/mediathek/mainwindow/MediathekGui.java +++ b/src/main/java/mediathek/mainwindow/MediathekGui.java @@ -122,7 +122,7 @@ public class MediathekGui extends JFrame { private final HashMap menuListeners = new HashMap<>(); private final JCheckBoxMenuItem cbBandwidthDisplay = new JCheckBoxMenuItem("Bandbreitennutzung"); private final JFXPanel statusBarPanel = new JFXPanel(); - private final LoadFilmListAction loadFilmListAction; + protected final LoadFilmListAction loadFilmListAction; private final SearchProgramUpdateAction searchProgramUpdateAction; private final MemoryMonitorAction showMemoryMonitorAction = new MemoryMonitorAction(); private final InfoDialog filmInfo; @@ -151,6 +151,13 @@ public class MediathekGui extends JFrame { */ private WeakReference indicatorLayout; + protected JToolBar commonToolBar = new JToolBar(); + protected void createCommonToolBar() { + commonToolBar.add(loadFilmListAction); + + getContentPane().add(commonToolBar, BorderLayout.PAGE_START); + } + public MediathekGui() { ui = this; @@ -163,6 +170,9 @@ public MediathekGui() { var contentPane = getContentPane(); contentPane.setLayout(new BorderLayout()); + + createCommonToolBar(); + contentPane.add(statusBarPanel, BorderLayout.PAGE_END); setIconAndWindowImage(); From 178644c2f42569f601d201ae767859a736b72a4a Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 9 Jun 2022 10:18:48 +0200 Subject: [PATCH 007/422] - move load filmlist button to common toolbar Former-commit-id: 80ace9088c8b333daefef27a28f456abce4064ba --- .../mediathek/gui/actions/LoadFilmListAction.java | 1 + .../mediathek/javafx/filterpanel/FXFilmToolBar.java | 2 -- .../mediathek/javafx/filterpanel/FilmActionPanel.java | 6 ++---- src/main/java/mediathek/mainwindow/MediathekGui.java | 3 ++- .../mediathek/res/programm/fxml/film_toolbar.fxml | 11 ----------- 5 files changed, 5 insertions(+), 18 deletions(-) diff --git a/src/main/java/mediathek/gui/actions/LoadFilmListAction.java b/src/main/java/mediathek/gui/actions/LoadFilmListAction.java index d6711cdd61..16cb700171 100644 --- a/src/main/java/mediathek/gui/actions/LoadFilmListAction.java +++ b/src/main/java/mediathek/gui/actions/LoadFilmListAction.java @@ -16,6 +16,7 @@ public LoadFilmListAction(MediathekGui mediathekGui) { putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0)); putValue(Action.SMALL_ICON, IconFontSwing.buildIcon(FontAwesome.CLOUD_DOWNLOAD, 16)); putValue(Action.NAME, "Neue Filmliste laden..."); + putValue(Action.SHORT_DESCRIPTION, "Neue Filmliste laden"); } @Override public void actionPerformed(ActionEvent e) { diff --git a/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java b/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java index 0371f1677f..0b69ee81f4 100644 --- a/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java +++ b/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java @@ -18,8 +18,6 @@ import java.net.URL; public class FXFilmToolBar extends ToolBar { - @FXML public Button btnDownloadFilmList; - @FXML public Button btnFilmInfo; @FXML Button btnPlay; diff --git a/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java b/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java index 795651ac43..1557a98700 100644 --- a/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java +++ b/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java @@ -298,12 +298,12 @@ private void setupConfigListeners() { @Handler private void handleFilmlistWriteStartEvent(FilmListWriteStartEvent e) { - Platform.runLater(() -> toolBar.btnDownloadFilmList.setDisable(true)); + SwingUtilities.invokeLater(() -> MediathekGui.ui().loadFilmListAction.setEnabled(false)); } @Handler private void handleFilmlistWriteStopEvent(FilmListWriteStopEvent e) { - Platform.runLater(() -> toolBar.btnDownloadFilmList.setDisable(false)); + SwingUtilities.invokeLater(() -> MediathekGui.ui().loadFilmListAction.setEnabled(true)); } private void searchFieldFinalAction() { @@ -422,8 +422,6 @@ public void updateThemaBox() { private void setupToolBar() { toolBar = new FXFilmToolBar(); - toolBar.btnDownloadFilmList.setOnAction(e -> SwingUtilities.invokeLater( - () -> MediathekGui.ui().performFilmListLoadOperation(false))); toolBar.btnFilmInfo.setOnAction( e -> SwingUtilities.invokeLater(MediathekGui.ui().getFilmInfoDialog()::showInfo)); toolBar.btnPlay.setOnAction(evt -> diff --git a/src/main/java/mediathek/mainwindow/MediathekGui.java b/src/main/java/mediathek/mainwindow/MediathekGui.java index e264c4a8b5..cdd93fd362 100644 --- a/src/main/java/mediathek/mainwindow/MediathekGui.java +++ b/src/main/java/mediathek/mainwindow/MediathekGui.java @@ -122,7 +122,7 @@ public class MediathekGui extends JFrame { private final HashMap menuListeners = new HashMap<>(); private final JCheckBoxMenuItem cbBandwidthDisplay = new JCheckBoxMenuItem("Bandbreitennutzung"); private final JFXPanel statusBarPanel = new JFXPanel(); - protected final LoadFilmListAction loadFilmListAction; + public final LoadFilmListAction loadFilmListAction; private final SearchProgramUpdateAction searchProgramUpdateAction; private final MemoryMonitorAction showMemoryMonitorAction = new MemoryMonitorAction(); private final InfoDialog filmInfo; @@ -154,6 +154,7 @@ public class MediathekGui extends JFrame { protected JToolBar commonToolBar = new JToolBar(); protected void createCommonToolBar() { commonToolBar.add(loadFilmListAction); + commonToolBar.addSeparator(); getContentPane().add(commonToolBar, BorderLayout.PAGE_START); } diff --git a/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml b/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml index 426bc3edc2..a3e613f1b3 100644 --- a/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml +++ b/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml @@ -9,17 +9,6 @@ - - - - - - - From 91bc6b6384a29125d8201d03c3734d2e793d17a7 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 9 Jun 2022 10:48:56 +0200 Subject: [PATCH 009/422] - move edit blacklist to common toolbar Former-commit-id: 46393e83709be8c7c4c042bb47235f3f1507a557 --- .../gui/actions/EditBlacklistAction.java | 29 +++++++++++++++++++ .../filterpanel/EditBlacklistButton.java | 26 ----------------- .../mediathek/mainwindow/MediathekGui.java | 26 +++++++++-------- .../res/programm/fxml/film_toolbar.fxml | 2 -- 4 files changed, 43 insertions(+), 40 deletions(-) create mode 100644 src/main/java/mediathek/gui/actions/EditBlacklistAction.java delete mode 100644 src/main/java/mediathek/javafx/filterpanel/EditBlacklistButton.java diff --git a/src/main/java/mediathek/gui/actions/EditBlacklistAction.java b/src/main/java/mediathek/gui/actions/EditBlacklistAction.java new file mode 100644 index 0000000000..bbc0b52e99 --- /dev/null +++ b/src/main/java/mediathek/gui/actions/EditBlacklistAction.java @@ -0,0 +1,29 @@ +package mediathek.gui.actions; + +import jiconfont.icons.font_awesome.FontAwesome; +import jiconfont.swing.IconFontSwing; +import mediathek.config.Daten; +import mediathek.gui.dialog.DialogLeer; +import mediathek.gui.dialogEinstellungen.PanelBlacklist; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class EditBlacklistAction extends AbstractAction { + private final JFrame parent; + + public EditBlacklistAction(JFrame parent) { + this.parent = parent; + + putValue(NAME, "Blacklist bearbeiten..."); + putValue(SMALL_ICON, IconFontSwing.buildIcon(FontAwesome.SKYATLAS, 16f)); + putValue(SHORT_DESCRIPTION, "Blacklist bearbeiten"); + } + + @Override + public void actionPerformed(ActionEvent e) { + DialogLeer dialog = new DialogLeer(parent, true); + dialog.init("Blacklist", new PanelBlacklist(Daten.getInstance(), null, PanelBlacklist.class.getName() + "_3")); + dialog.setVisible(true); + } +} diff --git a/src/main/java/mediathek/javafx/filterpanel/EditBlacklistButton.java b/src/main/java/mediathek/javafx/filterpanel/EditBlacklistButton.java deleted file mode 100644 index a5f46a11e0..0000000000 --- a/src/main/java/mediathek/javafx/filterpanel/EditBlacklistButton.java +++ /dev/null @@ -1,26 +0,0 @@ -package mediathek.javafx.filterpanel; - -import javafx.scene.control.Button; -import javafx.scene.control.Tooltip; -import mediathek.config.Daten; -import mediathek.gui.dialog.DialogLeer; -import mediathek.gui.dialogEinstellungen.PanelBlacklist; -import org.controlsfx.glyphfont.FontAwesome; -import org.controlsfx.glyphfont.GlyphFont; -import org.controlsfx.glyphfont.GlyphFontRegistry; - -import javax.swing.*; - -public class EditBlacklistButton extends Button { - public EditBlacklistButton() { - super(); - GlyphFont fontAwesome = GlyphFontRegistry.font("FontAwesome"); - setGraphic(fontAwesome.create(FontAwesome.Glyph.SKYATLAS).size(16d)); - setTooltip(new Tooltip("Blacklist bearbeiten")); - setOnAction(e -> SwingUtilities.invokeLater(() -> { - DialogLeer dialog = new DialogLeer(null, true); - dialog.init("Blacklist", new PanelBlacklist(Daten.getInstance(), null, PanelBlacklist.class.getName() + "_3")); - dialog.setVisible(true); - })); - } -} diff --git a/src/main/java/mediathek/mainwindow/MediathekGui.java b/src/main/java/mediathek/mainwindow/MediathekGui.java index b4cee7a8cf..ff564ec9d3 100644 --- a/src/main/java/mediathek/mainwindow/MediathekGui.java +++ b/src/main/java/mediathek/mainwindow/MediathekGui.java @@ -98,6 +98,7 @@ public class MediathekGui extends JFrame { * "Pointer" to UI */ private static MediathekGui ui; + public final LoadFilmListAction loadFilmListAction; /** * Number of active downloads */ @@ -122,7 +123,6 @@ public class MediathekGui extends JFrame { private final HashMap menuListeners = new HashMap<>(); private final JCheckBoxMenuItem cbBandwidthDisplay = new JCheckBoxMenuItem("Bandbreitennutzung"); private final JFXPanel statusBarPanel = new JFXPanel(); - public final LoadFilmListAction loadFilmListAction; private final SearchProgramUpdateAction searchProgramUpdateAction; private final MemoryMonitorAction showMemoryMonitorAction = new MemoryMonitorAction(); private final InfoDialog filmInfo; @@ -132,6 +132,8 @@ public class MediathekGui extends JFrame { * the global configuration for this app. */ protected Configuration config = ApplicationConfiguration.getConfiguration(); + protected EditBlacklistAction editBlacklistAction = new EditBlacklistAction(this); + protected JToolBar commonToolBar = new JToolBar(); /** * Bandwidth monitoring for downloads. */ @@ -150,17 +152,6 @@ public class MediathekGui extends JFrame { * A weak reference to the table data model filtering progress indicator(s). */ private WeakReference indicatorLayout; - - protected JToolBar commonToolBar = new JToolBar(); - protected void createCommonToolBar() { - commonToolBar.add(loadFilmListAction); - commonToolBar.addSeparator(); - commonToolBar.add(manageAboAction); - commonToolBar.addSeparator(); - - getContentPane().add(commonToolBar, BorderLayout.PAGE_START); - } - public MediathekGui() { ui = this; @@ -254,6 +245,17 @@ public static MediathekGui ui() { return ui; } + protected void createCommonToolBar() { + commonToolBar.add(loadFilmListAction); + commonToolBar.addSeparator(); + commonToolBar.add(editBlacklistAction); + commonToolBar.addSeparator(); + commonToolBar.add(manageAboAction); + commonToolBar.addSeparator(); + + getContentPane().add(commonToolBar, BorderLayout.PAGE_START); + } + /** * Check if we encountered invalid regexps and warn user if necessary. * This needs to be delayed unfortunately as we can see result only after table has been filled. diff --git a/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml b/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml index bf36d8223e..b49d1a82e9 100644 --- a/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml +++ b/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml @@ -4,7 +4,6 @@ - @@ -50,7 +49,6 @@ - From 20327bcaec2f3f5e01e9ec2df02fa85a2c182ee7 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 9 Jun 2022 11:03:23 +0200 Subject: [PATCH 010/422] - change help icon Former-commit-id: 6f5c18cf8490307ed68aa6bf73b5e27124d94d0c --- .../java/mediathek/gui/actions/ShowOnlineHelpAction.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/mediathek/gui/actions/ShowOnlineHelpAction.java b/src/main/java/mediathek/gui/actions/ShowOnlineHelpAction.java index 040991dc09..45bfbf1667 100644 --- a/src/main/java/mediathek/gui/actions/ShowOnlineHelpAction.java +++ b/src/main/java/mediathek/gui/actions/ShowOnlineHelpAction.java @@ -1,8 +1,7 @@ package mediathek.gui.actions; +import com.formdev.flatlaf.icons.FlatHelpButtonIcon; import javafx.application.Platform; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.Konstanten; import mediathek.mainwindow.MediathekGui; import mediathek.tool.javafx.FXErrorDialog; @@ -15,7 +14,7 @@ public class ShowOnlineHelpAction extends AbstractAction { public ShowOnlineHelpAction() { super(); putValue(NAME, "Online-Hilfe anzeigen..."); - putValue(SMALL_ICON, IconFontSwing.buildIcon(FontAwesome.QUESTION_CIRCLE_O, 16)); + putValue(SMALL_ICON, new FlatHelpButtonIcon()); } @Override From 9fded56371deec045cc849873d217c8fccdb4feb Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 9 Jun 2022 11:45:25 +0200 Subject: [PATCH 011/422] - use svg icon for download Former-commit-id: 6fd584f1ffd20a2dc108618533227ab3e5ff9226 --- pom.xml | 5 +++++ .../mediathek/gui/actions/LoadFilmListAction.java | 11 ++++++++--- .../resources/icons/fontawesome/cloud-arrow-down.svg | 1 + 3 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 src/main/resources/icons/fontawesome/cloud-arrow-down.svg diff --git a/pom.xml b/pom.xml index 33feb14ae6..a3f11722d0 100755 --- a/pom.xml +++ b/pom.xml @@ -162,6 +162,11 @@ flatlaf ${flatlaf.version} + + com.formdev + flatlaf-extras + ${flatlaf.version} + es.blackleg diff --git a/src/main/java/mediathek/gui/actions/LoadFilmListAction.java b/src/main/java/mediathek/gui/actions/LoadFilmListAction.java index 16cb700171..a0bf4c1eae 100644 --- a/src/main/java/mediathek/gui/actions/LoadFilmListAction.java +++ b/src/main/java/mediathek/gui/actions/LoadFilmListAction.java @@ -1,7 +1,6 @@ package mediathek.gui.actions; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; +import com.formdev.flatlaf.extras.FlatSVGIcon; import mediathek.mainwindow.MediathekGui; import javax.swing.*; @@ -11,10 +10,16 @@ public class LoadFilmListAction extends AbstractAction { private final MediathekGui mediathekGui; + public static FlatSVGIcon createSVGIcon(String resource, float height) { + FlatSVGIcon icon = new FlatSVGIcon("icons/fontawesome/cloud-arrow-down.svg"); + float scaleFactor = (1f / icon.getIconHeight()) * 14f; + return icon.derive(scaleFactor); + } + public LoadFilmListAction(MediathekGui mediathekGui) { this.mediathekGui = mediathekGui; putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0)); - putValue(Action.SMALL_ICON, IconFontSwing.buildIcon(FontAwesome.CLOUD_DOWNLOAD, 16)); + putValue(Action.SMALL_ICON, createSVGIcon("icons/fontawesome/cloud-arrow-down.svg", 14f)); putValue(Action.NAME, "Neue Filmliste laden..."); putValue(Action.SHORT_DESCRIPTION, "Neue Filmliste laden"); } diff --git a/src/main/resources/icons/fontawesome/cloud-arrow-down.svg b/src/main/resources/icons/fontawesome/cloud-arrow-down.svg new file mode 100644 index 0000000000..244c0c6675 --- /dev/null +++ b/src/main/resources/icons/fontawesome/cloud-arrow-down.svg @@ -0,0 +1 @@ + \ No newline at end of file From 6c6588fb39541b66abd5a519f38c5d231f44cb6a Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 9 Jun 2022 12:01:25 +0200 Subject: [PATCH 012/422] - move icon creation function to utilities Former-commit-id: f1268b37d0f053db4c33409706cf53b3025cd5a0 --- .../mediathek/gui/actions/LoadFilmListAction.java | 10 ++-------- src/main/java/mediathek/tool/SVGIconUtilities.java | 11 +++++++++++ 2 files changed, 13 insertions(+), 8 deletions(-) create mode 100644 src/main/java/mediathek/tool/SVGIconUtilities.java diff --git a/src/main/java/mediathek/gui/actions/LoadFilmListAction.java b/src/main/java/mediathek/gui/actions/LoadFilmListAction.java index a0bf4c1eae..c1a6192193 100644 --- a/src/main/java/mediathek/gui/actions/LoadFilmListAction.java +++ b/src/main/java/mediathek/gui/actions/LoadFilmListAction.java @@ -1,7 +1,7 @@ package mediathek.gui.actions; -import com.formdev.flatlaf.extras.FlatSVGIcon; import mediathek.mainwindow.MediathekGui; +import mediathek.tool.SVGIconUtilities; import javax.swing.*; import java.awt.event.ActionEvent; @@ -10,16 +10,10 @@ public class LoadFilmListAction extends AbstractAction { private final MediathekGui mediathekGui; - public static FlatSVGIcon createSVGIcon(String resource, float height) { - FlatSVGIcon icon = new FlatSVGIcon("icons/fontawesome/cloud-arrow-down.svg"); - float scaleFactor = (1f / icon.getIconHeight()) * 14f; - return icon.derive(scaleFactor); - } - public LoadFilmListAction(MediathekGui mediathekGui) { this.mediathekGui = mediathekGui; putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0)); - putValue(Action.SMALL_ICON, createSVGIcon("icons/fontawesome/cloud-arrow-down.svg", 14f)); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/cloud-arrow-down.svg")); putValue(Action.NAME, "Neue Filmliste laden..."); putValue(Action.SHORT_DESCRIPTION, "Neue Filmliste laden"); } diff --git a/src/main/java/mediathek/tool/SVGIconUtilities.java b/src/main/java/mediathek/tool/SVGIconUtilities.java new file mode 100644 index 0000000000..19951ec13b --- /dev/null +++ b/src/main/java/mediathek/tool/SVGIconUtilities.java @@ -0,0 +1,11 @@ +package mediathek.tool; + +import com.formdev.flatlaf.extras.FlatSVGIcon; + +public class SVGIconUtilities { + public static FlatSVGIcon createSVGIcon(String resource) { + FlatSVGIcon icon = new FlatSVGIcon(resource); + float scaleFactor = (1f / icon.getIconHeight()) * 15f; + return icon.derive(scaleFactor); + } +} From 96fbad29a77eb8170297551f426c6adf38dd5428 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 9 Jun 2022 13:11:35 +0200 Subject: [PATCH 013/422] - change more icons to svg Former-commit-id: 488ca0a053ff7c0ea9ec2cc29d5d267e668a2906 --- src/main/java/mediathek/gui/actions/EditBlacklistAction.java | 2 ++ src/main/java/mediathek/gui/actions/ManageAboAction.kt | 5 ++--- src/main/resources/icons/fontawesome/database.svg | 1 + src/main/resources/icons/fontawesome/skyatlas.svg | 1 + 4 files changed, 6 insertions(+), 3 deletions(-) create mode 100755 src/main/resources/icons/fontawesome/database.svg create mode 100755 src/main/resources/icons/fontawesome/skyatlas.svg diff --git a/src/main/java/mediathek/gui/actions/EditBlacklistAction.java b/src/main/java/mediathek/gui/actions/EditBlacklistAction.java index bbc0b52e99..30a95640d4 100644 --- a/src/main/java/mediathek/gui/actions/EditBlacklistAction.java +++ b/src/main/java/mediathek/gui/actions/EditBlacklistAction.java @@ -5,6 +5,7 @@ import mediathek.config.Daten; import mediathek.gui.dialog.DialogLeer; import mediathek.gui.dialogEinstellungen.PanelBlacklist; +import mediathek.tool.SVGIconUtilities; import javax.swing.*; import java.awt.event.ActionEvent; @@ -17,6 +18,7 @@ public EditBlacklistAction(JFrame parent) { putValue(NAME, "Blacklist bearbeiten..."); putValue(SMALL_ICON, IconFontSwing.buildIcon(FontAwesome.SKYATLAS, 16f)); + putValue(SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/skyatlas.svg")); putValue(SHORT_DESCRIPTION, "Blacklist bearbeiten"); } diff --git a/src/main/java/mediathek/gui/actions/ManageAboAction.kt b/src/main/java/mediathek/gui/actions/ManageAboAction.kt index 61c13db27a..4807afd795 100644 --- a/src/main/java/mediathek/gui/actions/ManageAboAction.kt +++ b/src/main/java/mediathek/gui/actions/ManageAboAction.kt @@ -1,9 +1,8 @@ package mediathek.gui.actions -import jiconfont.icons.font_awesome.FontAwesome -import jiconfont.swing.IconFontSwing import mediathek.gui.abo.ManageAboDialog import mediathek.mainwindow.MediathekGui +import mediathek.tool.SVGIconUtilities import java.awt.event.ActionEvent import javax.swing.AbstractAction @@ -22,7 +21,7 @@ class ManageAboAction : AbstractAction() { init { putValue(NAME, "Abos verwalten...") - putValue(SMALL_ICON, IconFontSwing.buildIcon(FontAwesome.DATABASE, 16f)) + putValue(SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/database.svg")) putValue(SHORT_DESCRIPTION, "Abos verwalten") } } \ No newline at end of file diff --git a/src/main/resources/icons/fontawesome/database.svg b/src/main/resources/icons/fontawesome/database.svg new file mode 100755 index 0000000000..c5365634de --- /dev/null +++ b/src/main/resources/icons/fontawesome/database.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/resources/icons/fontawesome/skyatlas.svg b/src/main/resources/icons/fontawesome/skyatlas.svg new file mode 100755 index 0000000000..7ad2b1507e --- /dev/null +++ b/src/main/resources/icons/fontawesome/skyatlas.svg @@ -0,0 +1 @@ + \ No newline at end of file From 04c8295aea7e2aecb5a3b3932b1efe2c2f7a0698 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 9 Jun 2022 13:15:24 +0200 Subject: [PATCH 014/422] - change more icons to svg Former-commit-id: da590ac1861337d250172bc4b97cc91b76082f67 --- src/main/java/mediathek/gui/actions/ShowOnlineHelpAction.java | 4 ++-- src/main/resources/icons/fontawesome/circle-question.svg | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100755 src/main/resources/icons/fontawesome/circle-question.svg diff --git a/src/main/java/mediathek/gui/actions/ShowOnlineHelpAction.java b/src/main/java/mediathek/gui/actions/ShowOnlineHelpAction.java index 45bfbf1667..b4811250ee 100644 --- a/src/main/java/mediathek/gui/actions/ShowOnlineHelpAction.java +++ b/src/main/java/mediathek/gui/actions/ShowOnlineHelpAction.java @@ -1,9 +1,9 @@ package mediathek.gui.actions; -import com.formdev.flatlaf.icons.FlatHelpButtonIcon; import javafx.application.Platform; import mediathek.config.Konstanten; import mediathek.mainwindow.MediathekGui; +import mediathek.tool.SVGIconUtilities; import mediathek.tool.javafx.FXErrorDialog; import javax.swing.*; @@ -14,7 +14,7 @@ public class ShowOnlineHelpAction extends AbstractAction { public ShowOnlineHelpAction() { super(); putValue(NAME, "Online-Hilfe anzeigen..."); - putValue(SMALL_ICON, new FlatHelpButtonIcon()); + putValue(SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/circle-question.svg")); } @Override diff --git a/src/main/resources/icons/fontawesome/circle-question.svg b/src/main/resources/icons/fontawesome/circle-question.svg new file mode 100755 index 0000000000..824e772104 --- /dev/null +++ b/src/main/resources/icons/fontawesome/circle-question.svg @@ -0,0 +1 @@ + \ No newline at end of file From f4e552a2ff6a103c89ce7a81c77febbc31a92af0 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 9 Jun 2022 13:25:51 +0200 Subject: [PATCH 015/422] - change more icons to svg Former-commit-id: d8eedd3e503a29739b9093bd6584b009f05855a8 --- src/main/java/mediathek/gui/actions/SettingsAction.java | 6 +++--- src/main/java/mediathek/mainwindow/MediathekGui.java | 4 +++- src/main/resources/icons/fontawesome/gears.svg | 1 + 3 files changed, 7 insertions(+), 4 deletions(-) create mode 100755 src/main/resources/icons/fontawesome/gears.svg diff --git a/src/main/java/mediathek/gui/actions/SettingsAction.java b/src/main/java/mediathek/gui/actions/SettingsAction.java index 639d0036b2..7ba80b7210 100644 --- a/src/main/java/mediathek/gui/actions/SettingsAction.java +++ b/src/main/java/mediathek/gui/actions/SettingsAction.java @@ -1,8 +1,7 @@ package mediathek.gui.actions; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.mainwindow.MediathekGui; +import mediathek.tool.SVGIconUtilities; import javax.swing.*; import java.awt.event.ActionEvent; @@ -15,7 +14,8 @@ public SettingsAction(MediathekGui mediathekGui) { this.mediathekGui = mediathekGui; putValue(Action.NAME, "Einstellungen..."); putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_F4, 0)); - putValue(Action.SMALL_ICON, IconFontSwing.buildIcon(FontAwesome.COGS, 16)); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/gears.svg")); + putValue(Action.SHORT_DESCRIPTION, "Einstellungen öffnen"); } @Override public void actionPerformed(ActionEvent e) { diff --git a/src/main/java/mediathek/mainwindow/MediathekGui.java b/src/main/java/mediathek/mainwindow/MediathekGui.java index ff564ec9d3..7b8e880d52 100644 --- a/src/main/java/mediathek/mainwindow/MediathekGui.java +++ b/src/main/java/mediathek/mainwindow/MediathekGui.java @@ -106,6 +106,7 @@ public class MediathekGui extends JFrame { protected final Daten daten = Daten.getInstance(); protected final JTabbedPane tabbedPane = new JTabbedPane(); protected final JMenu jMenuHilfe = new JMenu(); + protected final SettingsAction settingsAction = new SettingsAction(this); private final JMenu jMenuDatei = new JMenu(); private final JMenu jMenuFilme = new JMenu(); private final JMenuBar jMenuBar = new JMenuBar(); @@ -252,6 +253,7 @@ protected void createCommonToolBar() { commonToolBar.addSeparator(); commonToolBar.add(manageAboAction); commonToolBar.addSeparator(); + commonToolBar.add(settingsAction); getContentPane().add(commonToolBar, BorderLayout.PAGE_START); } @@ -830,7 +832,7 @@ private void createFileMenu() { //on macOS we will use native handlers instead... if (!SystemUtils.IS_OS_MAC_OSX) { jMenuDatei.addSeparator(); - jMenuDatei.add(new SettingsAction(this)); + jMenuDatei.add(settingsAction); jMenuDatei.addSeparator(); jMenuDatei.add(new QuitAction(this)); } diff --git a/src/main/resources/icons/fontawesome/gears.svg b/src/main/resources/icons/fontawesome/gears.svg new file mode 100755 index 0000000000..ab68353b54 --- /dev/null +++ b/src/main/resources/icons/fontawesome/gears.svg @@ -0,0 +1 @@ + \ No newline at end of file From eef00e8263286351a03abf67ec4c96a9c10b6d78 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 9 Jun 2022 13:29:45 +0200 Subject: [PATCH 016/422] - change more icons to svg Former-commit-id: ff2ae5ef15efe88845924e48908cc24a9b5181cb --- src/main/java/mediathek/gui/actions/CreateNewAboAction.kt | 5 ++--- src/main/resources/icons/fontawesome/plus.svg | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) create mode 100755 src/main/resources/icons/fontawesome/plus.svg diff --git a/src/main/java/mediathek/gui/actions/CreateNewAboAction.kt b/src/main/java/mediathek/gui/actions/CreateNewAboAction.kt index 213874c440..f5d802565d 100644 --- a/src/main/java/mediathek/gui/actions/CreateNewAboAction.kt +++ b/src/main/java/mediathek/gui/actions/CreateNewAboAction.kt @@ -1,8 +1,7 @@ package mediathek.gui.actions -import jiconfont.icons.font_awesome.FontAwesome -import jiconfont.swing.IconFontSwing import mediathek.daten.ListeAbo +import mediathek.tool.SVGIconUtilities import java.awt.event.ActionEvent import javax.swing.AbstractAction @@ -13,6 +12,6 @@ class CreateNewAboAction(private val listeAbo: ListeAbo) : AbstractAction() { init { putValue(NAME, "Abo anlegen...") - putValue(SMALL_ICON, IconFontSwing.buildIcon(FontAwesome.PLUS, 16f)) + putValue(SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/plus.svg")) } } \ No newline at end of file diff --git a/src/main/resources/icons/fontawesome/plus.svg b/src/main/resources/icons/fontawesome/plus.svg new file mode 100755 index 0000000000..c9add91226 --- /dev/null +++ b/src/main/resources/icons/fontawesome/plus.svg @@ -0,0 +1 @@ + \ No newline at end of file From bae1c26fd84bb3cf58f0817e80623b83a00d541d Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 9 Jun 2022 13:48:04 +0200 Subject: [PATCH 017/422] - change more icons to svg Former-commit-id: f15883ebcfd3315f0f085a2bfb2503c16f15629e --- .../gui/actions/ManageBookmarkAction.java | 24 +++++++++++++++++++ .../mediathek/mainwindow/MediathekGui.java | 11 +++++---- .../icons/fontawesome/file-lines.svg | 1 + 3 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 src/main/java/mediathek/gui/actions/ManageBookmarkAction.java create mode 100755 src/main/resources/icons/fontawesome/file-lines.svg diff --git a/src/main/java/mediathek/gui/actions/ManageBookmarkAction.java b/src/main/java/mediathek/gui/actions/ManageBookmarkAction.java new file mode 100644 index 0000000000..a3c54617f3 --- /dev/null +++ b/src/main/java/mediathek/gui/actions/ManageBookmarkAction.java @@ -0,0 +1,24 @@ +package mediathek.gui.actions; + +import javafx.application.Platform; +import mediathek.mainwindow.MediathekGui; +import mediathek.tool.SVGIconUtilities; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class ManageBookmarkAction extends AbstractAction { + private final MediathekGui mediathekGui; + + public ManageBookmarkAction(MediathekGui mediathekGui) { + this.mediathekGui = mediathekGui; + putValue(Action.NAME, "Merkliste verwalten..."); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/file-lines.svg")); + putValue(Action.SHORT_DESCRIPTION, "Merkliste verwalten"); + } + + @Override + public void actionPerformed(ActionEvent e) { + Platform.runLater(() -> mediathekGui.tabFilme.showBookmarkWindow()); + } +} diff --git a/src/main/java/mediathek/mainwindow/MediathekGui.java b/src/main/java/mediathek/mainwindow/MediathekGui.java index 7b8e880d52..edd941e175 100644 --- a/src/main/java/mediathek/mainwindow/MediathekGui.java +++ b/src/main/java/mediathek/mainwindow/MediathekGui.java @@ -135,6 +135,7 @@ public class MediathekGui extends JFrame { protected Configuration config = ApplicationConfiguration.getConfiguration(); protected EditBlacklistAction editBlacklistAction = new EditBlacklistAction(this); protected JToolBar commonToolBar = new JToolBar(); + protected ManageBookmarkAction manageBookmarkAction = new ManageBookmarkAction(this); /** * Bandwidth monitoring for downloads. */ @@ -153,6 +154,7 @@ public class MediathekGui extends JFrame { * A weak reference to the table data model filtering progress indicator(s). */ private WeakReference indicatorLayout; + public MediathekGui() { ui = this; @@ -253,6 +255,8 @@ protected void createCommonToolBar() { commonToolBar.addSeparator(); commonToolBar.add(manageAboAction); commonToolBar.addSeparator(); + commonToolBar.add(manageBookmarkAction); + commonToolBar.addSeparator(); commonToolBar.add(settingsAction); getContentPane().add(commonToolBar, BorderLayout.PAGE_START); @@ -537,6 +541,7 @@ private void handleTableModelChangeEvent(TableModelChangeEvent evt) { } }); } + @Handler private void handleBandwidthMonitorStateChangedEvent(BandwidthMonitorStateChangedEvent e) { final var vis = config.getBoolean(ApplicationConfiguration.APPLICATION_UI_BANDWIDTH_MONITOR_VISIBLE, false); @@ -857,9 +862,6 @@ private void createViewMenu() { }); - JMenuItem showBookmarkList = new JMenuItem("Merkliste anzeigen"); - showBookmarkList.addActionListener(l -> JavaFxUtils.invokeInFxThreadAndWait(() -> tabFilme.showBookmarkWindow())); - jMenuAnsicht.addSeparator(); jMenuAnsicht.add(showMemoryMonitorAction); jMenuAnsicht.add(cbBandwidthDisplay); @@ -868,7 +870,7 @@ private void createViewMenu() { jMenuAnsicht.addSeparator(); jMenuAnsicht.add(new ShowFilmInformationAction(true)); jMenuAnsicht.addSeparator(); - jMenuAnsicht.add(showBookmarkList); + jMenuAnsicht.add(manageBookmarkAction); } private void createHelpMenu() { @@ -1117,4 +1119,5 @@ public void run() { Log4jShutdownCallbackRegistry.Companion.execute(); } } + } diff --git a/src/main/resources/icons/fontawesome/file-lines.svg b/src/main/resources/icons/fontawesome/file-lines.svg new file mode 100755 index 0000000000..9083428c28 --- /dev/null +++ b/src/main/resources/icons/fontawesome/file-lines.svg @@ -0,0 +1 @@ + \ No newline at end of file From febc4dda9c91669401369f33ebc972f00bf6d390 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 9 Jun 2022 13:56:02 +0200 Subject: [PATCH 018/422] - change more icons to svg Former-commit-id: f68acf90dbb2c4b5500b2a5e2a121cdda657a3a1 --- .../mediathek/javafx/filterpanel/FXFilmToolBar.java | 2 -- .../mediathek/javafx/filterpanel/FilmActionPanel.java | 3 --- src/main/java/mediathek/mac/MediathekGuiMac.java | 1 - src/main/java/mediathek/mainwindow/MediathekGui.java | 4 ---- .../mediathek/res/programm/fxml/film_toolbar.fxml | 11 ----------- 5 files changed, 21 deletions(-) diff --git a/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java b/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java index a23fb2483b..2e65cc1106 100644 --- a/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java +++ b/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java @@ -28,8 +28,6 @@ public class FXFilmToolBar extends ToolBar { @FXML ToggleButton btnShowBookmarkedMovies; - @FXML Button btnManageBookMarks; - @FXML Button btnShowFilter; @FXML diff --git a/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java b/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java index 90da5d88ac..92f0fb7baf 100644 --- a/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java +++ b/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java @@ -433,9 +433,6 @@ private void setupToolBar() { toolBar.btnBookmark.setOnAction(e -> SwingUtilities.invokeLater( () -> MediathekGui.ui().tabFilme.bookmarkFilmAction.actionPerformed(null))); - toolBar.btnManageBookMarks.setOnAction(e -> - SwingUtilities.invokeLater( - () -> MediathekGui.ui().tabFilme.bookmarkManageListAction.actionPerformed(null))); toolBar.btnShowFilter.setOnAction(e -> SwingUtilities.invokeLater(() -> { if (filterDialog != null) { diff --git a/src/main/java/mediathek/mac/MediathekGuiMac.java b/src/main/java/mediathek/mac/MediathekGuiMac.java index bd0fe06779..b626b5f030 100644 --- a/src/main/java/mediathek/mac/MediathekGuiMac.java +++ b/src/main/java/mediathek/mac/MediathekGuiMac.java @@ -139,7 +139,6 @@ private void setupUserInterfaceForOsx() { desktop.setPreferencesHandler(e -> getSettingsDialog().setVisible(true)); getRootPane().putClientProperty("apple.awt.windowTitleVisible", false); - System.setProperty("apple.awt.application.appearance", "system"); if (SystemInfo.isMacFullWindowContentSupported) { getRootPane().putClientProperty("apple.awt.fullWindowContent", true); diff --git a/src/main/java/mediathek/mainwindow/MediathekGui.java b/src/main/java/mediathek/mainwindow/MediathekGui.java index edd941e175..2b5cfbb6c6 100644 --- a/src/main/java/mediathek/mainwindow/MediathekGui.java +++ b/src/main/java/mediathek/mainwindow/MediathekGui.java @@ -250,13 +250,9 @@ public static MediathekGui ui() { protected void createCommonToolBar() { commonToolBar.add(loadFilmListAction); - commonToolBar.addSeparator(); commonToolBar.add(editBlacklistAction); - commonToolBar.addSeparator(); commonToolBar.add(manageAboAction); - commonToolBar.addSeparator(); commonToolBar.add(manageBookmarkAction); - commonToolBar.addSeparator(); commonToolBar.add(settingsAction); getContentPane().add(commonToolBar, BorderLayout.PAGE_START); diff --git a/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml b/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml index b49d1a82e9..fc06039c40 100644 --- a/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml +++ b/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml @@ -61,17 +61,6 @@ - - - - From 06c50a8c996ed4e9ba7d6b466cf5123eebef47cf Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 9 Jun 2022 14:07:09 +0200 Subject: [PATCH 019/422] - merge MV 14 changes Former-commit-id: f6c4e659d368995f744f905e8e21bef263f367ae --- CHANGELOG.md | 5 +++ .../gui/actions/EditBlacklistAction.java | 9 ++++++ .../actions/ShowBlacklistDialogAction.java | 31 ------------------- .../mediathek/gui/tabs/tab_film/GuiFilme.java | 12 +------ .../mediathek/mainwindow/MediathekGui.java | 2 +- 5 files changed, 16 insertions(+), 43 deletions(-) delete mode 100644 src/main/java/mediathek/gui/actions/ShowBlacklistDialogAction.java diff --git a/CHANGELOG.md b/CHANGELOG.md index a2a6a622f0..ccdf1badcd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,9 @@ +**14.0.0** + +- **BUGFIX:** *"Filme/Blacklist öffnen..."* Eintrag wurde in *"Filme/Blacklist bearbeiten..."* umbenannt und die Icons vereinheitlicht. + **13.9.1** +- - Die Binaries für macOS waren beschädigt und mussten erneuert werden. **13.9.0** diff --git a/src/main/java/mediathek/gui/actions/EditBlacklistAction.java b/src/main/java/mediathek/gui/actions/EditBlacklistAction.java index 30a95640d4..ace94c9e76 100644 --- a/src/main/java/mediathek/gui/actions/EditBlacklistAction.java +++ b/src/main/java/mediathek/gui/actions/EditBlacklistAction.java @@ -5,10 +5,13 @@ import mediathek.config.Daten; import mediathek.gui.dialog.DialogLeer; import mediathek.gui.dialogEinstellungen.PanelBlacklist; +import mediathek.tool.GuiFunktionen; import mediathek.tool.SVGIconUtilities; +import org.apache.commons.lang3.SystemUtils; import javax.swing.*; import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; public class EditBlacklistAction extends AbstractAction { private final JFrame parent; @@ -20,6 +23,12 @@ public EditBlacklistAction(JFrame parent) { putValue(SMALL_ICON, IconFontSwing.buildIcon(FontAwesome.SKYATLAS, 16f)); putValue(SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/skyatlas.svg")); putValue(SHORT_DESCRIPTION, "Blacklist bearbeiten"); + KeyStroke keyStroke; + if (SystemUtils.IS_OS_MAC_OSX) + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_F9, GuiFunktionen.getPlatformControlKey()); + else + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_B, GuiFunktionen.getPlatformControlKey()); + putValue(Action.ACCELERATOR_KEY, keyStroke); } @Override diff --git a/src/main/java/mediathek/gui/actions/ShowBlacklistDialogAction.java b/src/main/java/mediathek/gui/actions/ShowBlacklistDialogAction.java deleted file mode 100644 index 67fb9cb7c4..0000000000 --- a/src/main/java/mediathek/gui/actions/ShowBlacklistDialogAction.java +++ /dev/null @@ -1,31 +0,0 @@ -package mediathek.gui.actions; - -import mediathek.config.Daten; -import mediathek.gui.dialog.DialogLeer; -import mediathek.gui.dialogEinstellungen.PanelBlacklist; -import mediathek.mainwindow.MediathekGui; -import mediathek.res.GetIcon; - -import javax.swing.*; -import java.awt.event.ActionEvent; - -public class ShowBlacklistDialogAction extends AbstractAction { - private static final String PANEL_BLACKLIST_NAME_POSTFIX = "_2"; - private final JFrame parent; - private final Daten daten; - - public ShowBlacklistDialogAction(JFrame parent, Daten daten) { - this.daten = daten; - this.parent = parent; - - putValue(NAME, "Blacklist öffnen..."); - putValue(SMALL_ICON, GetIcon.getProgramIcon("menue-blacklist.png", 16, 16)); - } - - @Override - public void actionPerformed(ActionEvent e) { - DialogLeer dialog = new DialogLeer(parent, true); - dialog.init("Blacklist", new PanelBlacklist(daten, MediathekGui.ui(), PanelBlacklist.class.getName() + PANEL_BLACKLIST_NAME_POSTFIX)); - dialog.setVisible(true); - } -} diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index bc62210fcf..608a26de5c 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -24,7 +24,6 @@ import mediathek.filmeSuchen.ListenerFilmeLaden; import mediathek.filmeSuchen.ListenerFilmeLadenEvent; import mediathek.gui.TabPaneIndex; -import mediathek.gui.actions.ShowBlacklistDialogAction; import mediathek.gui.actions.ShowFilmInformationAction; import mediathek.gui.actions.UrlHyperlinkAction; import mediathek.gui.dialog.DialogAboNoSet; @@ -270,15 +269,6 @@ public void installMenuEntries(JMenu menu) { miBookmarkFilm.setIcon(IconFontSwing.buildIcon(FontAwesome.BOOKMARK_O, 16)); miBookmarkFilm.addActionListener(bookmarkFilmAction); - JMenuItem miOpenBlacklist = new JMenuItem("Blacklist öffnen"); - if (SystemUtils.IS_OS_MAC_OSX) - miOpenBlacklist.setAccelerator( - KeyStroke.getKeyStroke(KeyEvent.VK_F9, InputEvent.META_DOWN_MASK)); - else - miOpenBlacklist.setAccelerator( - KeyStroke.getKeyStroke(KeyEvent.VK_B, KeyEvent.CTRL_DOWN_MASK)); - miOpenBlacklist.setAction(new ShowBlacklistDialogAction(mediathekGui, daten)); - if (!SystemUtils.IS_OS_MAC_OSX) cbkShowDescription.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F10, 0)); @@ -298,7 +288,7 @@ public void installMenuEntries(JMenu menu) { menu.add(miMarkFilmAsSeen); menu.add(miMarkFilmAsUnseen); menu.addSeparator(); - menu.add(miOpenBlacklist); + menu.add(mediathekGui.editBlacklistAction); menu.addSeparator(); menu.add(cbkShowDescription); } diff --git a/src/main/java/mediathek/mainwindow/MediathekGui.java b/src/main/java/mediathek/mainwindow/MediathekGui.java index 2b5cfbb6c6..656f65ae65 100644 --- a/src/main/java/mediathek/mainwindow/MediathekGui.java +++ b/src/main/java/mediathek/mainwindow/MediathekGui.java @@ -133,7 +133,7 @@ public class MediathekGui extends JFrame { * the global configuration for this app. */ protected Configuration config = ApplicationConfiguration.getConfiguration(); - protected EditBlacklistAction editBlacklistAction = new EditBlacklistAction(this); + public EditBlacklistAction editBlacklistAction = new EditBlacklistAction(this); protected JToolBar commonToolBar = new JToolBar(); protected ManageBookmarkAction manageBookmarkAction = new ManageBookmarkAction(this); /** From 341b5089c2128344eac742ca3ecce639e61e3747 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 9 Jun 2022 14:08:28 +0200 Subject: [PATCH 020/422] bump to version 14 nightly Former-commit-id: e73a58c8f040b6c7e0ac38dcf36e18ff6285b345 --- src/main/java/mediathek/config/Konstanten.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/mediathek/config/Konstanten.java b/src/main/java/mediathek/config/Konstanten.java index 5d4bf84dc6..fdd7268eff 100644 --- a/src/main/java/mediathek/config/Konstanten.java +++ b/src/main/java/mediathek/config/Konstanten.java @@ -28,11 +28,11 @@ public class Konstanten { public static final long MINIMUM_MEMORY_THRESHOLD = 768 * FileUtils.ONE_MB; - public static final Version MVVERSION = new Version(13,9,1); + public static final Version MVVERSION = new Version(14,0,0); /** * Is this a nightly or a production build? */ - public static final boolean APP_IS_NIGHTLY = false; + public static final boolean APP_IS_NIGHTLY = true; public static final String EXTERNAL_UPDATE_PROPERTY = "externalUpdateCheck"; public static final String MACOS_OFFICIAL_APP = "OSX_OFFICIAL_APP"; public static final URL FXML_FILM_DESCRIPTION_PANEL_URL = Konstanten.class.getResource("/mediathek/res/programm/fxml/filmdescription.fxml"); From a2aaabfc42e811f60cb3bbd1333bbd5783d16f28 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 9 Jun 2022 14:12:06 +0200 Subject: [PATCH 021/422] fix icon bug edit blacklist Former-commit-id: 989556ad8164b8f3321ae5eda10a42d1038434e3 --- src/main/java/mediathek/gui/actions/EditBlacklistAction.java | 5 +---- src/main/resources/icons/fontawesome/rectangle-list.svg | 1 + src/main/resources/icons/fontawesome/skyatlas.svg | 1 - 3 files changed, 2 insertions(+), 5 deletions(-) create mode 100755 src/main/resources/icons/fontawesome/rectangle-list.svg delete mode 100755 src/main/resources/icons/fontawesome/skyatlas.svg diff --git a/src/main/java/mediathek/gui/actions/EditBlacklistAction.java b/src/main/java/mediathek/gui/actions/EditBlacklistAction.java index ace94c9e76..dcc921d25b 100644 --- a/src/main/java/mediathek/gui/actions/EditBlacklistAction.java +++ b/src/main/java/mediathek/gui/actions/EditBlacklistAction.java @@ -1,7 +1,5 @@ package mediathek.gui.actions; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.Daten; import mediathek.gui.dialog.DialogLeer; import mediathek.gui.dialogEinstellungen.PanelBlacklist; @@ -20,8 +18,7 @@ public EditBlacklistAction(JFrame parent) { this.parent = parent; putValue(NAME, "Blacklist bearbeiten..."); - putValue(SMALL_ICON, IconFontSwing.buildIcon(FontAwesome.SKYATLAS, 16f)); - putValue(SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/skyatlas.svg")); + putValue(SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/rectangle-list.svg")); putValue(SHORT_DESCRIPTION, "Blacklist bearbeiten"); KeyStroke keyStroke; if (SystemUtils.IS_OS_MAC_OSX) diff --git a/src/main/resources/icons/fontawesome/rectangle-list.svg b/src/main/resources/icons/fontawesome/rectangle-list.svg new file mode 100755 index 0000000000..cfc387c5fd --- /dev/null +++ b/src/main/resources/icons/fontawesome/rectangle-list.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/resources/icons/fontawesome/skyatlas.svg b/src/main/resources/icons/fontawesome/skyatlas.svg deleted file mode 100755 index 7ad2b1507e..0000000000 --- a/src/main/resources/icons/fontawesome/skyatlas.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file From 8368349c13f6274c560e2a22d64f2f19bd4b67cf Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 9 Jun 2022 15:15:24 +0200 Subject: [PATCH 022/422] - convert toggle blacklist action Former-commit-id: 04f71f48223c5761414253d516949fc70cf02206 --- .../gui/actions/ToggleBlacklistAction.java | 61 ++++++++++++++ .../javafx/filterpanel/BlacklistButton.java | 75 ------------------ .../mediathek/mainwindow/MediathekGui.java | 6 +- .../icons/fontawesome/list-check.svg | 1 + .../res/programm/button-blacklist-aus.png | Bin 404 -> 0 bytes .../res/programm/button-blacklist-ein.png | Bin 702 -> 0 bytes .../res/programm/fxml/film_toolbar.fxml | 11 +-- 7 files changed, 70 insertions(+), 84 deletions(-) create mode 100644 src/main/java/mediathek/gui/actions/ToggleBlacklistAction.java delete mode 100644 src/main/java/mediathek/javafx/filterpanel/BlacklistButton.java create mode 100755 src/main/resources/icons/fontawesome/list-check.svg delete mode 100644 src/main/resources/mediathek/res/programm/button-blacklist-aus.png delete mode 100644 src/main/resources/mediathek/res/programm/button-blacklist-ein.png diff --git a/src/main/java/mediathek/gui/actions/ToggleBlacklistAction.java b/src/main/java/mediathek/gui/actions/ToggleBlacklistAction.java new file mode 100644 index 0000000000..7d4d530c3b --- /dev/null +++ b/src/main/java/mediathek/gui/actions/ToggleBlacklistAction.java @@ -0,0 +1,61 @@ +package mediathek.gui.actions; + +import com.formdev.flatlaf.extras.FlatSVGIcon; +import mediathek.config.Daten; +import mediathek.config.MVConfig; +import mediathek.gui.messages.BlacklistChangedEvent; +import mediathek.tool.MessageBus; +import mediathek.tool.SVGIconUtilities; +import net.engio.mbassy.listener.Handler; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; + +public class ToggleBlacklistAction extends AbstractAction { + private final FlatSVGIcon enabledIcon; + private final FlatSVGIcon disabledIcon; + private boolean blacklist_is_on; + + public ToggleBlacklistAction() { + enabledIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/list-check.svg"); + + disabledIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/list-check.svg"); + disabledIcon.setColorFilter(new FlatSVGIcon.ColorFilter(color -> Color.RED)); + + blacklist_is_on = Boolean.parseBoolean(MVConfig.get(MVConfig.Configs.SYSTEM_BLACKLIST_ON)); + setupState(); + + MessageBus.getMessageBus().subscribe(this); + } + + @Handler + private void handleBlacklistChangedEvent(BlacklistChangedEvent e) { + //config was changed outside + SwingUtilities.invokeLater(() -> { + blacklist_is_on = Boolean.parseBoolean(MVConfig.get(MVConfig.Configs.SYSTEM_BLACKLIST_ON)); + setupState(); + }); + } + + private void setupState() { + if (blacklist_is_on) { + putValue(NAME, "Blacklist ausschalten"); + putValue(SHORT_DESCRIPTION, "Blacklist ausschalten"); + putValue(SMALL_ICON, enabledIcon); + } else { + putValue(NAME, "Blacklist einschalten"); + putValue(SHORT_DESCRIPTION, "Blacklist einschalten"); + putValue(SMALL_ICON, disabledIcon); + } + } + + @Override + public void actionPerformed(ActionEvent e) { + blacklist_is_on = !blacklist_is_on; + + MVConfig.add(MVConfig.Configs.SYSTEM_BLACKLIST_ON, Boolean.toString(blacklist_is_on)); + Daten.getInstance().getListeBlacklist().filterListe(); + MessageBus.getMessageBus().publishAsync(new BlacklistChangedEvent()); + } +} diff --git a/src/main/java/mediathek/javafx/filterpanel/BlacklistButton.java b/src/main/java/mediathek/javafx/filterpanel/BlacklistButton.java deleted file mode 100644 index 732f268550..0000000000 --- a/src/main/java/mediathek/javafx/filterpanel/BlacklistButton.java +++ /dev/null @@ -1,75 +0,0 @@ -package mediathek.javafx.filterpanel; - -import javafx.application.Platform; -import javafx.beans.property.BooleanProperty; -import javafx.beans.property.SimpleBooleanProperty; -import javafx.scene.control.Button; -import javafx.scene.control.Tooltip; -import javafx.scene.image.Image; -import javafx.scene.image.ImageView; -import mediathek.config.Daten; -import mediathek.config.MVConfig; -import mediathek.gui.messages.BlacklistChangedEvent; -import mediathek.tool.MessageBus; -import net.engio.mbassy.listener.Handler; - -import javax.swing.*; - -public class BlacklistButton extends Button { - private final Image offImage = new Image(getClass().getResourceAsStream("/mediathek/res/programm/button-blacklist-aus.png"),0,18,true,true); - private final ImageView offImageView = new ImageView(offImage); - private final Image onImage = new Image(getClass().getResourceAsStream("/mediathek/res/programm/button-blacklist-ein.png"),0,18,true,true); - private final ImageView onImageView = new ImageView(onImage); - private final BooleanProperty activeProperty = new SimpleBooleanProperty(false); - private final Tooltip tooltipOn = new Tooltip("Blacklist ausschalten"); - private final Tooltip tooltipOff = new Tooltip("Blacklist einschalten"); - - public BlacklistButton() { - super(); - - final boolean isOn = Boolean.parseBoolean(MVConfig.get(MVConfig.Configs.SYSTEM_BLACKLIST_ON)); - if (isOn) - setupOn(); - else - setupOff(); - - //set initial state - activeProperty.addListener((observable, oldValue, newValue) -> { - if (newValue) { - setupOn(); - } else { - setupOff(); - } - }); - - final var daten = Daten.getInstance(); - final var messageBus = MessageBus.getMessageBus(); - messageBus.subscribe(this); - - activeProperty.setValue(isOn); - activeProperty.addListener((observable, oldValue, newValue) -> SwingUtilities.invokeLater(() -> { - MVConfig.add(MVConfig.Configs.SYSTEM_BLACKLIST_ON, Boolean.toString(newValue)); - daten.getListeBlacklist().filterListe(); - messageBus.publishAsync(new BlacklistChangedEvent()); - })); - - setOnAction(value -> activeProperty.setValue(!activeProperty.getValue())); - } - - @Handler - private void handleBlacklistChangedEvent(BlacklistChangedEvent e) { - //config was changed outside - final boolean on = Boolean.parseBoolean(MVConfig.get(MVConfig.Configs.SYSTEM_BLACKLIST_ON)); - Platform.runLater(() -> activeProperty.setValue(on)); - } - - private void setupOn() { - setGraphic(onImageView); - setTooltip(tooltipOn); - } - - private void setupOff() { - setGraphic(offImageView); - setTooltip(tooltipOff); - } -} diff --git a/src/main/java/mediathek/mainwindow/MediathekGui.java b/src/main/java/mediathek/mainwindow/MediathekGui.java index 656f65ae65..10d7b36ad5 100644 --- a/src/main/java/mediathek/mainwindow/MediathekGui.java +++ b/src/main/java/mediathek/mainwindow/MediathekGui.java @@ -129,13 +129,14 @@ public class MediathekGui extends JFrame { private final InfoDialog filmInfo; public GuiFilme tabFilme; public GuiDownloads tabDownloads; + public EditBlacklistAction editBlacklistAction = new EditBlacklistAction(this); /** * the global configuration for this app. */ protected Configuration config = ApplicationConfiguration.getConfiguration(); - public EditBlacklistAction editBlacklistAction = new EditBlacklistAction(this); protected JToolBar commonToolBar = new JToolBar(); protected ManageBookmarkAction manageBookmarkAction = new ManageBookmarkAction(this); + protected ToggleBlacklistAction toggleBlacklistAction = new ToggleBlacklistAction(); /** * Bandwidth monitoring for downloads. */ @@ -250,7 +251,10 @@ public static MediathekGui ui() { protected void createCommonToolBar() { commonToolBar.add(loadFilmListAction); + commonToolBar.addSeparator(); + commonToolBar.add(toggleBlacklistAction); commonToolBar.add(editBlacklistAction); + commonToolBar.addSeparator(); commonToolBar.add(manageAboAction); commonToolBar.add(manageBookmarkAction); commonToolBar.add(settingsAction); diff --git a/src/main/resources/icons/fontawesome/list-check.svg b/src/main/resources/icons/fontawesome/list-check.svg new file mode 100755 index 0000000000..09c7a7952e --- /dev/null +++ b/src/main/resources/icons/fontawesome/list-check.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/resources/mediathek/res/programm/button-blacklist-aus.png b/src/main/resources/mediathek/res/programm/button-blacklist-aus.png deleted file mode 100644 index 84c9fb85e5058b36c6256de7556a7cf8a2afd2e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 404 zcmV;F0c-w=P)y>TXa`Ol2o4nV1v>BJ zUI*{d#;W1@*QvuvFJHBfhU-z$D~cpbRvN+T_S=ZZ507_-{bmabhgKF=wh`s%fmUxY z>Wq>stZg)I+1`&UAM{A3sYogatvINs8c?J?HqGdJjiOO?k^@zHLvCykN%ceFXYZr6xDlwTzhWk z%w`sg-U-u-EBAa^9 yT}&nCTGf;*dHLg0v~SLTj@>_7dmz=cj*GwJBzgfwA=dH$0000A9H$oiY^w5VSIj^&Zx6-nS~4yL7}b%kc5Q&ulqqe=je<&@p#VM8A>U7 zZ}eh=?VC!(JF@|o_^xx|z^=YHQg}>nF;}ITDmSyNTj%ibE*s^8&-8OhI zVyH?LS8+8B@Xn*#H2%_~ay2$u>)Hr5SmvJ0H9;)s0`t%3uZ4JGPcX)yz4v%L3%d)r z1Bd--nSDCTi9KQQ%_64DG!=SRV=y^3S$CnyM|u3AXZXP|>u=Yw!ID!syz|@{xx*!w zjRwv;c0cSko?43tMhvdvSX*8LAeNZ(a1Oxc>LwwCCI@(kB&x5u&WQ-qkEbcQ5-m{+ zkwg^ZA`{Ohnu_a@kMf8JKu$#D%jl!g!H@)3^Z4ibiji=oix>$2N~?Hdb>o`=uYmhN k`j4+$w;@+~0CK>mU-)1Z!eyBtS^xk507*qoM6N<$f<|LDlmGw# diff --git a/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml b/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml index fc06039c40..87cc439531 100644 --- a/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml +++ b/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml @@ -3,7 +3,6 @@ - @@ -36,7 +35,9 @@ - + + + - - - - - - From 413ba1668b4163133f8706b3185985fd67b88c08 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 9 Jun 2022 15:21:21 +0200 Subject: [PATCH 023/422] - add toggle blacklist to menu Former-commit-id: 1ab1d845e784dc988f94058cbf2d6e6973baefa2 --- src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java | 1 + src/main/java/mediathek/mainwindow/MediathekGui.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index 608a26de5c..48ee6e48ec 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -288,6 +288,7 @@ public void installMenuEntries(JMenu menu) { menu.add(miMarkFilmAsSeen); menu.add(miMarkFilmAsUnseen); menu.addSeparator(); + menu.add(mediathekGui.toggleBlacklistAction); menu.add(mediathekGui.editBlacklistAction); menu.addSeparator(); menu.add(cbkShowDescription); diff --git a/src/main/java/mediathek/mainwindow/MediathekGui.java b/src/main/java/mediathek/mainwindow/MediathekGui.java index 10d7b36ad5..e19b2efeec 100644 --- a/src/main/java/mediathek/mainwindow/MediathekGui.java +++ b/src/main/java/mediathek/mainwindow/MediathekGui.java @@ -130,13 +130,13 @@ public class MediathekGui extends JFrame { public GuiFilme tabFilme; public GuiDownloads tabDownloads; public EditBlacklistAction editBlacklistAction = new EditBlacklistAction(this); + public ToggleBlacklistAction toggleBlacklistAction = new ToggleBlacklistAction(); /** * the global configuration for this app. */ protected Configuration config = ApplicationConfiguration.getConfiguration(); protected JToolBar commonToolBar = new JToolBar(); protected ManageBookmarkAction manageBookmarkAction = new ManageBookmarkAction(this); - protected ToggleBlacklistAction toggleBlacklistAction = new ToggleBlacklistAction(); /** * Bandwidth monitoring for downloads. */ From ca36fd1d99df1a18e591760dec56622103706e89 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 9 Jun 2022 17:27:52 +0200 Subject: [PATCH 024/422] - replace filmlist icons to svg Former-commit-id: d2ddaae76fee6ed9bb8b8f88f2cfbb44d6ebe487 --- .../tool/cellrenderer/CellRendererFilme.java | 97 ++++++++++++------- .../resources/icons/fontawesome/bookmark.svg | 1 + .../resources/icons/fontawesome/download.svg | 1 + src/main/resources/icons/fontawesome/play.svg | 1 + src/main/resources/icons/fontawesome/stop.svg | 1 + 5 files changed, 64 insertions(+), 37 deletions(-) create mode 100644 src/main/resources/icons/fontawesome/bookmark.svg create mode 100644 src/main/resources/icons/fontawesome/download.svg create mode 100644 src/main/resources/icons/fontawesome/play.svg create mode 100644 src/main/resources/icons/fontawesome/stop.svg diff --git a/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java b/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java index c5af2a3b1b..9b35f62dd7 100644 --- a/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java +++ b/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java @@ -1,13 +1,13 @@ package mediathek.tool.cellrenderer; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; +import com.formdev.flatlaf.extras.FlatSVGIcon; import mediathek.config.Daten; import mediathek.config.MVColor; import mediathek.controller.history.SeenHistoryController; import mediathek.controller.starter.Start; import mediathek.daten.DatenDownload; import mediathek.daten.DatenFilm; +import mediathek.tool.SVGIconUtilities; import mediathek.tool.table.MVTable; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -18,30 +18,42 @@ public class CellRendererFilme extends CellRendererBaseWithStart { private static final Logger logger = LogManager.getLogger(CellRendererFilme.class); - private final Icon selectedStopIcon; - private final Icon normalStopIcon; + private final FlatSVGIcon selectedStopIcon; + private final FlatSVGIcon normalStopIcon; private final SeenHistoryController history = new SeenHistoryController(); - private final Icon selectedDownloadIcon; - private final Icon normalDownloadIcon; - private final Icon selectedPlayIcon; - private final Icon normalPlayIcon; - private final Icon selectedBookmarkIcon; - private final Icon normalBookmarkIcon; - private final Icon selectedBookmarkIconHighlighted; + private final FlatSVGIcon selectedDownloadIcon; + private final FlatSVGIcon normalDownloadIcon; + private final FlatSVGIcon selectedPlayIcon; + private final FlatSVGIcon normalPlayIcon; + private final FlatSVGIcon selectedBookmarkIcon; + private final FlatSVGIcon normalBookmarkIcon; + private final FlatSVGIcon selectedBookmarkIconHighlighted; public CellRendererFilme() { - selectedDownloadIcon = IconFontSwing.buildIcon(FontAwesome.DOWNLOAD, 16, Color.WHITE); - normalDownloadIcon = IconFontSwing.buildIcon(FontAwesome.DOWNLOAD, 16); + var whiteColorFilter = new FlatSVGIcon.ColorFilter(color -> Color.WHITE); - selectedPlayIcon = IconFontSwing.buildIcon(FontAwesome.PLAY, 16, Color.WHITE); - normalPlayIcon = IconFontSwing.buildIcon(FontAwesome.PLAY, 16); + selectedDownloadIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/download.svg"); + selectedDownloadIcon.setColorFilter(whiteColorFilter); - selectedStopIcon = IconFontSwing.buildIcon(FontAwesome.STOP, 16, Color.WHITE); - normalStopIcon = IconFontSwing.buildIcon(FontAwesome.STOP, 16); + normalDownloadIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/download.svg"); - selectedBookmarkIcon = IconFontSwing.buildIcon(FontAwesome.BOOKMARK, 16, Color.BLUE); - selectedBookmarkIconHighlighted = IconFontSwing.buildIcon(FontAwesome.BOOKMARK, 16, Color.yellow); - normalBookmarkIcon = IconFontSwing.buildIcon(FontAwesome.BOOKMARK_O, 16); + selectedPlayIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/play.svg"); + selectedPlayIcon.setColorFilter(whiteColorFilter); + + normalPlayIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/play.svg"); + + selectedStopIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/stop.svg"); + selectedStopIcon.setColorFilter(whiteColorFilter); + + normalStopIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/stop.svg"); + + selectedBookmarkIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/bookmark.svg"); + selectedBookmarkIcon.setColorFilter(whiteColorFilter); + + selectedBookmarkIconHighlighted = SVGIconUtilities.createSVGIcon("icons/fontawesome/bookmark.svg"); + selectedBookmarkIconHighlighted.setColorFilter(new FlatSVGIcon.ColorFilter(color -> Color.ORANGE)); + + normalBookmarkIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/bookmark.svg"); } private JTextArea createTextArea(String content) { @@ -75,8 +87,8 @@ public Component getTableCellRendererComponent( final int columnModelIndex = table.convertColumnIndexToModel(column); final DatenFilm datenFilm = (DatenFilm) table.getModel().getValueAt(rowModelIndex, DatenFilm.FILM_REF); final DatenDownload datenDownload = Daten.getInstance().getListeDownloadsButton().getDownloadUrlFilm(datenFilm.getUrlNormalQuality()); - final boolean isBookMarked = datenFilm.isBookmarked(); - final var mvTable = (MVTable)table; + final boolean isBookMarked = datenFilm.isBookmarked(); + final var mvTable = (MVTable) table; setFont((mvTable.getDefaultFont())); @@ -106,24 +118,24 @@ public Component getTableCellRendererComponent( case DatenFilm.FILM_AUFZEICHNEN: handleButtonDownloadColumn(isSelected); break; - + case DatenFilm.FILM_MERKEN: handleButtonBookmarkColumn(isBookMarked, isSelected, datenFilm.isLivestream()); break; - + case DatenFilm.FILM_SENDER: if (mvTable.showSenderIcons()) { - Dimension targetDim = getSenderCellDimension(table, row,columnModelIndex); + Dimension targetDim = getSenderCellDimension(table, row, columnModelIndex); setSenderIcon(value.toString(), targetDim); } break; - case DatenFilm.FILM_TITEL: - var title = datenFilm.getTitle(); - var columnWidth = table.getColumnModel().getColumn(columnModelIndex).getWidth(); - if (columnWidth < table.getFontMetrics(table.getFont()).stringWidth(title)) - setToolTipText(title); - break; + case DatenFilm.FILM_TITEL: + var title = datenFilm.getTitle(); + var columnWidth = table.getColumnModel().getColumn(columnModelIndex).getWidth(); + if (columnWidth < table.getFontMetrics(table.getFont()).stringWidth(title)) + setToolTipText(title); + break; } applyColorSettings(this, datenFilm, datenDownload, isSelected, isBookMarked); @@ -136,11 +148,13 @@ public Component getTableCellRendererComponent( /** * Apply the specific horizontal alignment to the cell based on column + * * @param columnModelIndex the current column index */ private void applyHorizontalAlignment(final int columnModelIndex) { switch (columnModelIndex) { - case DatenFilm.FILM_NR, DatenFilm.FILM_DATUM, DatenFilm.FILM_ZEIT, DatenFilm.FILM_DAUER, DatenFilm.FILM_ABSPIELEN, DatenFilm.FILM_AUFZEICHNEN, DatenFilm.FILM_MERKEN -> setHorizontalAlignment(SwingConstants.CENTER); + case DatenFilm.FILM_NR, DatenFilm.FILM_DATUM, DatenFilm.FILM_ZEIT, DatenFilm.FILM_DAUER, DatenFilm.FILM_ABSPIELEN, DatenFilm.FILM_AUFZEICHNEN, DatenFilm.FILM_MERKEN -> + setHorizontalAlignment(SwingConstants.CENTER); case DatenFilm.FILM_GROESSE -> setHorizontalAlignment(SwingConstants.RIGHT); } } @@ -173,7 +187,7 @@ private void applyColorSettings(Component c, @NotNull DatenFilm datenFilm, Daten if (geoMelden) { //only apply geo block colors when we haven´t changed the background for seen history if (!hasBeenSeen) { - setupGeoblockingBackground(c, datenFilm.getGeo().orElse(""), isSelected); + setupGeoblockingBackground(c, datenFilm.getGeo().orElse(""), isSelected); } } } @@ -209,16 +223,25 @@ private void handleButtonDownloadColumn(final boolean isSelected) { // Button Aufzeichnen setIconAndToolTip(isSelected, normalDownloadIcon, selectedDownloadIcon, "Film aufzeichnen"); } - + private void handleButtonBookmarkColumn(final boolean isBookMarked, final boolean isSelected, boolean isLivestream) { if (isLivestream) { setIcon(null); setToolTipText(""); - } - else { + } else { // Button Merken setToolTipText(isBookMarked ? "Film aus Merkliste entfernen" : "Film merken"); - setIcon(isBookMarked ? (isSelected ? selectedBookmarkIconHighlighted : selectedBookmarkIcon) : normalBookmarkIcon); + if (isBookMarked) { + if (isSelected) { + setIcon(selectedBookmarkIconHighlighted); + } else + setIcon(selectedBookmarkIconHighlighted); + } else { + if (isSelected) { + setIcon(selectedBookmarkIcon); + } else + setIcon(normalBookmarkIcon); + } } } } diff --git a/src/main/resources/icons/fontawesome/bookmark.svg b/src/main/resources/icons/fontawesome/bookmark.svg new file mode 100644 index 0000000000..f47c6862d3 --- /dev/null +++ b/src/main/resources/icons/fontawesome/bookmark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/resources/icons/fontawesome/download.svg b/src/main/resources/icons/fontawesome/download.svg new file mode 100644 index 0000000000..9ae838e19e --- /dev/null +++ b/src/main/resources/icons/fontawesome/download.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/resources/icons/fontawesome/play.svg b/src/main/resources/icons/fontawesome/play.svg new file mode 100644 index 0000000000..360d13ba3c --- /dev/null +++ b/src/main/resources/icons/fontawesome/play.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/resources/icons/fontawesome/stop.svg b/src/main/resources/icons/fontawesome/stop.svg new file mode 100644 index 0000000000..6f3cdf1504 --- /dev/null +++ b/src/main/resources/icons/fontawesome/stop.svg @@ -0,0 +1 @@ + \ No newline at end of file From bbc0a336639fc0d816a81668b8aacc659d9fac49 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 9 Jun 2022 21:35:18 +0200 Subject: [PATCH 025/422] - bump to MV 14 Former-commit-id: 21999726a01a0140ac5fe7aaa61a2c4c1d799d0d --- pom.xml | 2 +- .../mediathek/gui/tabs/tab_film/GuiFilme.java | 29 +++++++++++-------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/pom.xml b/pom.xml index a3f11722d0..87c5ee2bf0 100755 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ de.mediathekview MediathekView - 13.9.1 + 14.0.0 jar ${project.groupId}:${project.artifactId} diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index 48ee6e48ec..4721e6fe95 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -243,30 +243,35 @@ public void installViewMenuEntry(JMenu jMenuAnsicht) { @Override public void installMenuEntries(JMenu menu) { + KeyStroke keyStroke; + JMenuItem miPlayFilm = new JMenuItem("Film abspielen"); if (SystemUtils.IS_OS_MAC_OSX) - miPlayFilm.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F6, InputEvent.META_DOWN_MASK)); - else miPlayFilm.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, KeyEvent.CTRL_DOWN_MASK)); - miPlayFilm.setIcon(IconFontSwing.buildIcon(FontAwesome.PLAY, 16)); + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_F6, GuiFunktionen.getPlatformControlKey()); + else + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_P, GuiFunktionen.getPlatformControlKey()); + miPlayFilm.setAccelerator(keyStroke); + miPlayFilm.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/play.svg")); miPlayFilm.addActionListener(playAction); JMenuItem miRecordFilm = new JMenuItem("Film aufzeichnen"); - if (SystemUtils.IS_OS_MAC_OSX) - miRecordFilm.setAccelerator( - KeyStroke.getKeyStroke(KeyEvent.VK_F7, InputEvent.META_DOWN_MASK)); + if (SystemUtils.IS_OS_MAC_OSX) { + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_F7, GuiFunktionen.getPlatformControlKey()); + } else - miRecordFilm.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D, KeyEvent.CTRL_DOWN_MASK)); - miRecordFilm.setIcon(IconFontSwing.buildIcon(FontAwesome.DOWNLOAD, 16)); + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_D, GuiFunktionen.getPlatformControlKey()); + miRecordFilm.setAccelerator(keyStroke); + miRecordFilm.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/download.svg")); miRecordFilm.addActionListener(saveFilmAction); JMenuItem miBookmarkFilm = new JMenuItem("Film merken"); if (SystemUtils.IS_OS_MAC_OSX) { - miBookmarkFilm.setAccelerator( - KeyStroke.getKeyStroke(KeyEvent.VK_F8, InputEvent.META_DOWN_MASK)); + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_F8, GuiFunktionen.getPlatformControlKey()); } else { - miBookmarkFilm.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_B, KeyEvent.CTRL_DOWN_MASK)); + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_B, GuiFunktionen.getPlatformControlKey()); } - miBookmarkFilm.setIcon(IconFontSwing.buildIcon(FontAwesome.BOOKMARK_O, 16)); + miBookmarkFilm.setAccelerator(keyStroke); + miBookmarkFilm.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/bookmark.svg")); miBookmarkFilm.addActionListener(bookmarkFilmAction); if (!SystemUtils.IS_OS_MAC_OSX) From 4ed1fe3168a4505ffbfdf734961dd8a0a0656ecb Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 9 Jun 2022 22:11:46 +0200 Subject: [PATCH 026/422] - convert menu items to actions Former-commit-id: cec7afca0529e092f67ee1588685e046860332ec --- .../gui/actions/StartAllDownloadsAction.java | 23 ++ .../actions/StartAllDownloadsTimedAction.java | 20 + .../gui/actions/StopAllDownloadsAction.java | 21 ++ .../gui/tabs/tab_downloads/GuiDownloads.java | 341 ++++++++---------- .../icons/fontawesome/angles-down.svg | 1 + 5 files changed, 225 insertions(+), 181 deletions(-) create mode 100644 src/main/java/mediathek/gui/actions/StartAllDownloadsAction.java create mode 100644 src/main/java/mediathek/gui/actions/StartAllDownloadsTimedAction.java create mode 100644 src/main/java/mediathek/gui/actions/StopAllDownloadsAction.java create mode 100755 src/main/resources/icons/fontawesome/angles-down.svg diff --git a/src/main/java/mediathek/gui/actions/StartAllDownloadsAction.java b/src/main/java/mediathek/gui/actions/StartAllDownloadsAction.java new file mode 100644 index 0000000000..d7ea04c425 --- /dev/null +++ b/src/main/java/mediathek/gui/actions/StartAllDownloadsAction.java @@ -0,0 +1,23 @@ +package mediathek.gui.actions; + +import mediathek.gui.tabs.tab_downloads.GuiDownloads; +import mediathek.tool.SVGIconUtilities; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class StartAllDownloadsAction extends AbstractAction { + private final GuiDownloads guiDownloads; + + public StartAllDownloadsAction(GuiDownloads guiDownloads) { + this.guiDownloads = guiDownloads; + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/angles-down.svg")); + putValue(Action.SHORT_DESCRIPTION, "Alle Downloads starten"); + putValue(Action.NAME, "Alle Downloads starten"); + } + + @Override + public void actionPerformed(ActionEvent e) { + guiDownloads.starten(true); + } +} diff --git a/src/main/java/mediathek/gui/actions/StartAllDownloadsTimedAction.java b/src/main/java/mediathek/gui/actions/StartAllDownloadsTimedAction.java new file mode 100644 index 0000000000..36f7cdaa09 --- /dev/null +++ b/src/main/java/mediathek/gui/actions/StartAllDownloadsTimedAction.java @@ -0,0 +1,20 @@ +package mediathek.gui.actions; + +import mediathek.gui.tabs.tab_downloads.GuiDownloads; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class StartAllDownloadsTimedAction extends AbstractAction { + private final GuiDownloads guiDownloads; + + public StartAllDownloadsTimedAction(GuiDownloads guiDownloads) { + this.guiDownloads = guiDownloads; + putValue(Action.NAME, "Alle Downloads zeitverzögert starten..."); + } + + @Override + public void actionPerformed(ActionEvent e) { + guiDownloads.startAllDownloadsAtSpecificTime(); + } +} diff --git a/src/main/java/mediathek/gui/actions/StopAllDownloadsAction.java b/src/main/java/mediathek/gui/actions/StopAllDownloadsAction.java new file mode 100644 index 0000000000..04d4c66cce --- /dev/null +++ b/src/main/java/mediathek/gui/actions/StopAllDownloadsAction.java @@ -0,0 +1,21 @@ +package mediathek.gui.actions; + +import mediathek.gui.tabs.tab_downloads.GuiDownloads; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class StopAllDownloadsAction extends AbstractAction { + + private final GuiDownloads guiDownloads; + + public StopAllDownloadsAction(GuiDownloads guiDownloads) { + this.guiDownloads = guiDownloads; + putValue(Action.NAME, "Alle Downloads stoppen"); + } + + @Override + public void actionPerformed(ActionEvent e) { + guiDownloads.stoppen(true); + } +} diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java index 1340d412ee..ba57b29c0b 100644 --- a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java @@ -26,6 +26,9 @@ import mediathek.filmeSuchen.ListenerFilmeLadenEvent; import mediathek.gui.TabPaneIndex; import mediathek.gui.actions.ShowFilmInformationAction; +import mediathek.gui.actions.StartAllDownloadsAction; +import mediathek.gui.actions.StartAllDownloadsTimedAction; +import mediathek.gui.actions.StopAllDownloadsAction; import mediathek.gui.dialog.DialogBeendenZeit; import mediathek.gui.dialog.DialogEditAbo; import mediathek.gui.dialog.DialogEditDownload; @@ -98,10 +101,13 @@ public class GuiDownloads extends AGuiTabPanel { private final AtomicBoolean tabVisible = new AtomicBoolean(false); private final JCheckBoxMenuItem cbShowDownloadDescription = new JCheckBoxMenuItem("Filmbeschreibung anzeigen"); private final Configuration config = ApplicationConfiguration.getConfiguration(); - + private final MarkFilmAsSeenAction markFilmAsSeenAction = new MarkFilmAsSeenAction(); + private final MarkFilmAsUnseenAction markFilmAsUnseenAction = new MarkFilmAsUnseenAction(); + protected StartAllDownloadsAction startAllDownloadsAction = new StartAllDownloadsAction(this); + protected StartAllDownloadsTimedAction startAllDownloadsTimedAction = new StartAllDownloadsTimedAction(this); + protected StopAllDownloadsAction stopAllDownloadsAction = new StopAllDownloadsAction(this); private boolean onlyAbos; private boolean onlyDownloads; - private boolean onlyWaiting; private boolean onlyNotStarted; private boolean onlyStarted; @@ -114,6 +120,19 @@ public class GuiDownloads extends AGuiTabPanel { private TModelDownload model; private DownloadTabInformationLabel filmInfoLabel; private MVDownloadsTable tabelle; + // Variables declaration - do not modify//GEN-BEGIN:variables + // Generated using JFormDesigner non-commercial license + private JSplitPane jSplitPane1; + private JPanel jPanelFilterExtern; + private JComboBox cbDisplayCategories; + private JComboBox cbView; + private JButton btnClear; + private JSpinner jSpinnerAnzahlDownloads; + private JSpinner jSpinner1; + private JEditorPane txtDownload; + private JScrollPane downloadListScrollPane; + private JFXPanel fxDescriptionPanel; + private JFXPanel toolBarPanel; public GuiDownloads(Daten aDaten, MediathekGui mediathekGui) { super(); @@ -380,16 +399,6 @@ private void setupDownloadRateLimitSpinner() { @Override public void installMenuEntries(JMenu menu) { - JMenuItem miDownloadsStartAll = new JMenuItem("Alle Downloads starten"); - miDownloadsStartAll.setIcon(IconFontSwing.buildIcon(FontAwesome.ANGLE_DOUBLE_DOWN, 16)); - miDownloadsStartAll.addActionListener(e -> starten(true)); - - JMenuItem miDownloadStartTimed = new JMenuItem("Alle Downloads zeitverzögert starten..."); - miDownloadStartTimed.addActionListener(e -> startAllDownloadsAtSpecificTime()); - - JMenuItem miStopAllDownloads = new JMenuItem("Alle Downloads stoppen"); - miStopAllDownloads.addActionListener(e -> stoppen(true)); - JMenuItem miStopWaitingDownloads = new JMenuItem("Wartende Downloads stoppen"); miStopWaitingDownloads.addActionListener(e -> stopAllWaitingDownloads()); @@ -466,9 +475,9 @@ public void fertig(ListenerFilmeLadenEvent event) { } }); - menu.add(miDownloadsStartAll); - menu.add(miDownloadStartTimed); - menu.add(miStopAllDownloads); + menu.add(startAllDownloadsAction); + menu.add(startAllDownloadsTimedAction); + menu.add(stopAllDownloadsAction); menu.add(miStopWaitingDownloads); menu.add(miUpdateDownloads); menu.add(miCleanupDownloads); @@ -528,10 +537,6 @@ public void stoppen(boolean alle) { filmStartenWiederholenStoppen(alle, false, true, false); } - private final MarkFilmAsSeenAction markFilmAsSeenAction = new MarkFilmAsSeenAction(); - - private final MarkFilmAsUnseenAction markFilmAsUnseenAction = new MarkFilmAsUnseenAction(); - private void setupKeyMappings() { final InputMap im = tabelle.getInputMap(); im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), ACTION_MAP_KEY_EDIT_DOWNLOAD); @@ -1257,6 +1262,141 @@ protected List getSelFilme() { return arrayFilme; } + private void initComponents() { + jSplitPane1 = new JSplitPane(); + jPanelFilterExtern = new JPanel(); + var panel3 = new JPanel(); + var label1 = new JLabel(); + cbDisplayCategories = new JComboBox<>(); + var label2 = new JLabel(); + cbView = new JComboBox<>(); + btnClear = new JButton(); + var panel2 = new JPanel(); + var jLabel3 = new JLabel(); + jSpinnerAnzahlDownloads = new JSpinner(); + var lblBandwidth = new JLabel(); + var jLabel1 = new JLabel(); + jSpinner1 = new JSpinner(); + var spDownload = new JScrollPane(); + txtDownload = new JEditorPane(); + var downloadListArea = new JPanel(); + downloadListScrollPane = new JScrollPane(); + fxDescriptionPanel = new JFXPanel(); + toolBarPanel = new JFXPanel(); + + //======== this ======== + setLayout(new BorderLayout()); + + //======== jSplitPane1 ======== + { + jSplitPane1.setDividerLocation(330); + + //======== jPanelFilterExtern ======== + { + jPanelFilterExtern.setPreferredSize(new Dimension(200, 644)); + jPanelFilterExtern.setLayout(new MigLayout( + new LC().insets("0").hideMode(3).gridGap("0", "0"), //NON-NLS + // columns + new AC() + .grow().fill(), + // rows + new AC() + .gap() + .fill().gap() + .grow().fill())); + + //======== panel3 ======== + { + panel3.setBorder(new TitledBorder("Anzeige")); //NON-NLS + panel3.setLayout(new MigLayout( + new LC().insets("5").hideMode(3).gridGap("5", "5"), //NON-NLS + // columns + new AC() + .fill().gap() + .grow().fill(), + // rows + new AC() + .fill().gap() + .fill().gap() + .fill())); + + //---- label1 ---- + label1.setText("Typ:"); //NON-NLS + panel3.add(label1, new CC().cell(0, 0)); + panel3.add(cbDisplayCategories, new CC().cell(1, 0)); + + //---- label2 ---- + label2.setText("Status:"); //NON-NLS + panel3.add(label2, new CC().cell(0, 1)); + panel3.add(cbView, new CC().cell(1, 1)); + + //---- btnClear ---- + btnClear.setIcon(new ImageIcon(getClass().getResource("/mediathek/res/muster/button-clear.png"))); //NON-NLS + btnClear.setToolTipText("Filter zur\u00fccksetzen"); //NON-NLS + panel3.add(btnClear, new CC().cell(0, 2, 2, 1).alignX("right").growX(0).width("32:32:32").height("32:32:32")); //NON-NLS + } + jPanelFilterExtern.add(panel3, new CC().cell(0, 0)); + + //======== panel2 ======== + { + panel2.setBorder(new TitledBorder("Downloads")); //NON-NLS + panel2.setLayout(new MigLayout( + new LC().insets("5").hideMode(3).gridGap("5", "5"), //NON-NLS + // columns + new AC() + .fill().gap() + .fill(), + // rows + new AC() + .gap() + .fill())); + + //---- jLabel3 ---- + jLabel3.setText("gleichzeitig:"); //NON-NLS + panel2.add(jLabel3, new CC().cell(0, 0)); + panel2.add(jSpinnerAnzahlDownloads, new CC().cell(1, 0)); + + //---- lblBandwidth ---- + lblBandwidth.setText("max. Bandbreite:"); //NON-NLS + panel2.add(lblBandwidth, new CC().cell(0, 1)); + + //---- jLabel1 ---- + jLabel1.setText("KiB/s"); //NON-NLS + panel2.add(jLabel1, new CC().cell(2, 1)); + + //---- jSpinner1 ---- + jSpinner1.setModel(new SpinnerNumberModel(0, 0, 1048576, 1)); + jSpinner1.setToolTipText("\nBandbreitenbegrenzung eines Downloads in XX Kilobytes pro Sekunde.\n
WICHTIG:
ENTWEDER
den Wert \u00fcber die Pfeiltasten \u00e4ndern
ODER
Zahlen eingeben UND ENTER-Taste dr\u00fccken!
\n"); //NON-NLS + panel2.add(jSpinner1, new CC().cell(1, 1)); + } + jPanelFilterExtern.add(panel2, new CC().cell(0, 1)); + + //======== spDownload ======== + { + spDownload.setPreferredSize(new Dimension(14, 150)); + + //---- txtDownload ---- + txtDownload.setEditable(false); + txtDownload.setOpaque(false); + txtDownload.setPreferredSize(new Dimension(10, 500)); + spDownload.setViewportView(txtDownload); + } + jPanelFilterExtern.add(spDownload, new CC().cell(0, 2)); + } + jSplitPane1.setLeftComponent(jPanelFilterExtern); + + //======== downloadListArea ======== + { + downloadListArea.setLayout(new BorderLayout()); + downloadListArea.add(downloadListScrollPane, BorderLayout.CENTER); + downloadListArea.add(fxDescriptionPanel, BorderLayout.SOUTH); + } + jSplitPane1.setRightComponent(downloadListArea); + } + add(jSplitPane1, BorderLayout.CENTER); + add(toolBarPanel, BorderLayout.NORTH); + } + public class BeobMausTabelle extends MouseAdapter { private final ShowFilmInformationAction showFilmInformationAction = new ShowFilmInformationAction(false); @@ -1388,10 +1528,7 @@ private void showMenu(MouseEvent evt) { jPopupMenu.addSeparator(); - JMenuItem itemAlleStarten = new JMenuItem("Alle Downloads starten"); - itemAlleStarten.setIcon(IconFontSwing.buildIcon(FontAwesome.ANGLE_DOUBLE_DOWN, 16)); - itemAlleStarten.addActionListener(arg0 -> starten(true)); - jPopupMenu.add(itemAlleStarten); + jPopupMenu.add(startAllDownloadsAction); JMenuItem itemAlleStoppen = new JMenuItem("Alle Downloads stoppen"); itemAlleStoppen.addActionListener(arg0 -> stoppen(true)); @@ -1601,162 +1738,4 @@ public void actionPerformed(ActionEvent e) { reloadTable(); } } - - /** - * This method is called from within the constructor to - * initialize the form. - * WARNING: Do NOT modify this code. The content of this method is - * always regenerated by the Form Editor. - */ - // //GEN-BEGIN:initComponents - // Generated using JFormDesigner non-commercial license - private void initComponents() { - jSplitPane1 = new JSplitPane(); - jPanelFilterExtern = new JPanel(); - var panel3 = new JPanel(); - var label1 = new JLabel(); - cbDisplayCategories = new JComboBox<>(); - var label2 = new JLabel(); - cbView = new JComboBox<>(); - btnClear = new JButton(); - var panel2 = new JPanel(); - var jLabel3 = new JLabel(); - jSpinnerAnzahlDownloads = new JSpinner(); - var lblBandwidth = new JLabel(); - var jLabel1 = new JLabel(); - jSpinner1 = new JSpinner(); - var spDownload = new JScrollPane(); - txtDownload = new JEditorPane(); - var downloadListArea = new JPanel(); - downloadListScrollPane = new JScrollPane(); - fxDescriptionPanel = new JFXPanel(); - toolBarPanel = new JFXPanel(); - - //======== this ======== - setLayout(new BorderLayout()); - - //======== jSplitPane1 ======== - { - jSplitPane1.setDividerLocation(330); - - //======== jPanelFilterExtern ======== - { - jPanelFilterExtern.setPreferredSize(new Dimension(200, 644)); - jPanelFilterExtern.setLayout(new MigLayout( - new LC().insets("0").hideMode(3).gridGap("0", "0"), //NON-NLS - // columns - new AC() - .grow().fill(), - // rows - new AC() - .gap() - .fill().gap() - .grow().fill())); - - //======== panel3 ======== - { - panel3.setBorder(new TitledBorder("Anzeige")); //NON-NLS - panel3.setLayout(new MigLayout( - new LC().insets("5").hideMode(3).gridGap("5", "5"), //NON-NLS - // columns - new AC() - .fill().gap() - .grow().fill(), - // rows - new AC() - .fill().gap() - .fill().gap() - .fill())); - - //---- label1 ---- - label1.setText("Typ:"); //NON-NLS - panel3.add(label1, new CC().cell(0, 0)); - panel3.add(cbDisplayCategories, new CC().cell(1, 0)); - - //---- label2 ---- - label2.setText("Status:"); //NON-NLS - panel3.add(label2, new CC().cell(0, 1)); - panel3.add(cbView, new CC().cell(1, 1)); - - //---- btnClear ---- - btnClear.setIcon(new ImageIcon(getClass().getResource("/mediathek/res/muster/button-clear.png"))); //NON-NLS - btnClear.setToolTipText("Filter zur\u00fccksetzen"); //NON-NLS - panel3.add(btnClear, new CC().cell(0, 2, 2, 1).alignX("right").growX(0).width("32:32:32").height("32:32:32")); //NON-NLS - } - jPanelFilterExtern.add(panel3, new CC().cell(0, 0)); - - //======== panel2 ======== - { - panel2.setBorder(new TitledBorder("Downloads")); //NON-NLS - panel2.setLayout(new MigLayout( - new LC().insets("5").hideMode(3).gridGap("5", "5"), //NON-NLS - // columns - new AC() - .fill().gap() - .fill(), - // rows - new AC() - .gap() - .fill())); - - //---- jLabel3 ---- - jLabel3.setText("gleichzeitig:"); //NON-NLS - panel2.add(jLabel3, new CC().cell(0, 0)); - panel2.add(jSpinnerAnzahlDownloads, new CC().cell(1, 0)); - - //---- lblBandwidth ---- - lblBandwidth.setText("max. Bandbreite:"); //NON-NLS - panel2.add(lblBandwidth, new CC().cell(0, 1)); - - //---- jLabel1 ---- - jLabel1.setText("KiB/s"); //NON-NLS - panel2.add(jLabel1, new CC().cell(2, 1)); - - //---- jSpinner1 ---- - jSpinner1.setModel(new SpinnerNumberModel(0, 0, 1048576, 1)); - jSpinner1.setToolTipText("\nBandbreitenbegrenzung eines Downloads in XX Kilobytes pro Sekunde.\n
WICHTIG:
ENTWEDER
den Wert \u00fcber die Pfeiltasten \u00e4ndern
ODER
Zahlen eingeben UND ENTER-Taste dr\u00fccken!
\n"); //NON-NLS - panel2.add(jSpinner1, new CC().cell(1, 1)); - } - jPanelFilterExtern.add(panel2, new CC().cell(0, 1)); - - //======== spDownload ======== - { - spDownload.setPreferredSize(new Dimension(14, 150)); - - //---- txtDownload ---- - txtDownload.setEditable(false); - txtDownload.setOpaque(false); - txtDownload.setPreferredSize(new Dimension(10, 500)); - spDownload.setViewportView(txtDownload); - } - jPanelFilterExtern.add(spDownload, new CC().cell(0, 2)); - } - jSplitPane1.setLeftComponent(jPanelFilterExtern); - - //======== downloadListArea ======== - { - downloadListArea.setLayout(new BorderLayout()); - downloadListArea.add(downloadListScrollPane, BorderLayout.CENTER); - downloadListArea.add(fxDescriptionPanel, BorderLayout.SOUTH); - } - jSplitPane1.setRightComponent(downloadListArea); - } - add(jSplitPane1, BorderLayout.CENTER); - add(toolBarPanel, BorderLayout.NORTH); - }//
//GEN-END:initComponents - - // Variables declaration - do not modify//GEN-BEGIN:variables - // Generated using JFormDesigner non-commercial license - private JSplitPane jSplitPane1; - private JPanel jPanelFilterExtern; - private JComboBox cbDisplayCategories; - private JComboBox cbView; - private JButton btnClear; - private JSpinner jSpinnerAnzahlDownloads; - private JSpinner jSpinner1; - private JEditorPane txtDownload; - private JScrollPane downloadListScrollPane; - private JFXPanel fxDescriptionPanel; - private JFXPanel toolBarPanel; - // End of variables declaration//GEN-END:variables } diff --git a/src/main/resources/icons/fontawesome/angles-down.svg b/src/main/resources/icons/fontawesome/angles-down.svg new file mode 100755 index 0000000000..f48dff1e03 --- /dev/null +++ b/src/main/resources/icons/fontawesome/angles-down.svg @@ -0,0 +1 @@ + \ No newline at end of file From 0d844b8a6fa517d9a59caab7a077d3c86f0aa932 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 9 Jun 2022 22:22:04 +0200 Subject: [PATCH 027/422] - convert menu items to actions Former-commit-id: 968063568ef4ac25afd384b34658f5b014e5baa3 --- .../actions/RefreshDownloadListAction.java | 24 +++++++++++++++++++ .../StopAllWaitingDownloadsAction.java | 20 ++++++++++++++++ .../gui/tabs/tab_downloads/GuiDownloads.java | 22 ++++++----------- .../icons/fontawesome/arrows-rotate.svg | 1 + 4 files changed, 52 insertions(+), 15 deletions(-) create mode 100644 src/main/java/mediathek/gui/actions/RefreshDownloadListAction.java create mode 100644 src/main/java/mediathek/gui/actions/StopAllWaitingDownloadsAction.java create mode 100755 src/main/resources/icons/fontawesome/arrows-rotate.svg diff --git a/src/main/java/mediathek/gui/actions/RefreshDownloadListAction.java b/src/main/java/mediathek/gui/actions/RefreshDownloadListAction.java new file mode 100644 index 0000000000..e5ab47ca52 --- /dev/null +++ b/src/main/java/mediathek/gui/actions/RefreshDownloadListAction.java @@ -0,0 +1,24 @@ +package mediathek.gui.actions; + +import mediathek.gui.tabs.tab_downloads.GuiDownloads; +import mediathek.tool.SVGIconUtilities; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; + +public class RefreshDownloadListAction extends AbstractAction { + private final GuiDownloads guiDownloads; + + public RefreshDownloadListAction(GuiDownloads guiDownloads) { + this.guiDownloads = guiDownloads; + putValue(Action.NAME, "Liste der Downloads aktualisieren"); + putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_W, KeyEvent.CTRL_DOWN_MASK)); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/arrows-rotate.svg")); + } + + @Override + public void actionPerformed(ActionEvent e) { + guiDownloads.updateDownloads(); + } +} diff --git a/src/main/java/mediathek/gui/actions/StopAllWaitingDownloadsAction.java b/src/main/java/mediathek/gui/actions/StopAllWaitingDownloadsAction.java new file mode 100644 index 0000000000..69150dafef --- /dev/null +++ b/src/main/java/mediathek/gui/actions/StopAllWaitingDownloadsAction.java @@ -0,0 +1,20 @@ +package mediathek.gui.actions; + +import mediathek.gui.tabs.tab_downloads.GuiDownloads; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class StopAllWaitingDownloadsAction extends AbstractAction { + private final GuiDownloads guiDownloads; + + public StopAllWaitingDownloadsAction(GuiDownloads guiDownloads) { + this.guiDownloads = guiDownloads; + putValue(Action.NAME, "Wartende Downloads stoppen"); + } + + @Override + public void actionPerformed(ActionEvent e) { + guiDownloads.stopAllWaitingDownloads(); + } +} diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java index ba57b29c0b..cc18ff0cb9 100644 --- a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java @@ -25,10 +25,7 @@ import mediathek.filmeSuchen.ListenerFilmeLaden; import mediathek.filmeSuchen.ListenerFilmeLadenEvent; import mediathek.gui.TabPaneIndex; -import mediathek.gui.actions.ShowFilmInformationAction; -import mediathek.gui.actions.StartAllDownloadsAction; -import mediathek.gui.actions.StartAllDownloadsTimedAction; -import mediathek.gui.actions.StopAllDownloadsAction; +import mediathek.gui.actions.*; import mediathek.gui.dialog.DialogBeendenZeit; import mediathek.gui.dialog.DialogEditAbo; import mediathek.gui.dialog.DialogEditDownload; @@ -106,6 +103,8 @@ public class GuiDownloads extends AGuiTabPanel { protected StartAllDownloadsAction startAllDownloadsAction = new StartAllDownloadsAction(this); protected StartAllDownloadsTimedAction startAllDownloadsTimedAction = new StartAllDownloadsTimedAction(this); protected StopAllDownloadsAction stopAllDownloadsAction = new StopAllDownloadsAction(this); + protected StopAllWaitingDownloadsAction stopAllWaitingDownloadsAction = new StopAllWaitingDownloadsAction(this); + protected RefreshDownloadListAction refreshDownloadListAction = new RefreshDownloadListAction(this); private boolean onlyAbos; private boolean onlyDownloads; private boolean onlyWaiting; @@ -399,22 +398,15 @@ private void setupDownloadRateLimitSpinner() { @Override public void installMenuEntries(JMenu menu) { - JMenuItem miStopWaitingDownloads = new JMenuItem("Wartende Downloads stoppen"); - miStopWaitingDownloads.addActionListener(e -> stopAllWaitingDownloads()); - - JMenuItem miUpdateDownloads = new JMenuItem("Liste der Downloads aktualisieren"); - miUpdateDownloads.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W, KeyEvent.CTRL_DOWN_MASK)); - miUpdateDownloads.setIcon(IconFontSwing.buildIcon(FontAwesome.REFRESH, 16)); - miUpdateDownloads.addActionListener(e -> updateDownloads()); daten.getFilmeLaden().addAdListener(new ListenerFilmeLaden() { @Override public void start(ListenerFilmeLadenEvent event) { - miUpdateDownloads.setEnabled(false); + refreshDownloadListAction.setEnabled(false); } @Override public void fertig(ListenerFilmeLadenEvent event) { - miUpdateDownloads.setEnabled(true); + refreshDownloadListAction.setEnabled(true); } }); @@ -478,8 +470,8 @@ public void fertig(ListenerFilmeLadenEvent event) { menu.add(startAllDownloadsAction); menu.add(startAllDownloadsTimedAction); menu.add(stopAllDownloadsAction); - menu.add(miStopWaitingDownloads); - menu.add(miUpdateDownloads); + menu.add(stopAllWaitingDownloadsAction); + menu.add(refreshDownloadListAction); menu.add(miCleanupDownloads); menu.addSeparator(); menu.add(miStartDownloads); diff --git a/src/main/resources/icons/fontawesome/arrows-rotate.svg b/src/main/resources/icons/fontawesome/arrows-rotate.svg new file mode 100755 index 0000000000..aea50ab3e9 --- /dev/null +++ b/src/main/resources/icons/fontawesome/arrows-rotate.svg @@ -0,0 +1 @@ + \ No newline at end of file From a615cf26ca448581fc2caac6eadb3b599e2866d8 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 9 Jun 2022 22:40:40 +0200 Subject: [PATCH 028/422] - convert menu items to actions Former-commit-id: 8e848fbf933e5b1379361a9252d7822a42afebbe --- .../actions/CleanupDownloadListAction.java | 22 ++++++++++++ .../gui/actions/InvertSelectionAction.java | 20 +++++++++++ .../gui/tabs/tab_downloads/GuiDownloads.java | 36 ++++++------------- .../resources/icons/fontawesome/eraser.svg | 1 + 4 files changed, 54 insertions(+), 25 deletions(-) create mode 100644 src/main/java/mediathek/gui/actions/CleanupDownloadListAction.java create mode 100644 src/main/java/mediathek/gui/actions/InvertSelectionAction.java create mode 100755 src/main/resources/icons/fontawesome/eraser.svg diff --git a/src/main/java/mediathek/gui/actions/CleanupDownloadListAction.java b/src/main/java/mediathek/gui/actions/CleanupDownloadListAction.java new file mode 100644 index 0000000000..814f8e0e1c --- /dev/null +++ b/src/main/java/mediathek/gui/actions/CleanupDownloadListAction.java @@ -0,0 +1,22 @@ +package mediathek.gui.actions; + +import mediathek.gui.tabs.tab_downloads.GuiDownloads; +import mediathek.tool.SVGIconUtilities; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class CleanupDownloadListAction extends AbstractAction { + private final GuiDownloads guiDownloads; + + public CleanupDownloadListAction(GuiDownloads guiDownloads) { + this.guiDownloads = guiDownloads; + putValue(Action.NAME, "Liste säubern"); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/eraser.svg")); + } + + @Override + public void actionPerformed(ActionEvent e) { + guiDownloads.cleanupDownloads(); + } +} diff --git a/src/main/java/mediathek/gui/actions/InvertSelectionAction.java b/src/main/java/mediathek/gui/actions/InvertSelectionAction.java new file mode 100644 index 0000000000..513f9223f3 --- /dev/null +++ b/src/main/java/mediathek/gui/actions/InvertSelectionAction.java @@ -0,0 +1,20 @@ +package mediathek.gui.actions; + +import mediathek.gui.tabs.tab_downloads.GuiDownloads; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class InvertSelectionAction extends AbstractAction { + private final GuiDownloads guiDownloads; + + public InvertSelectionAction(GuiDownloads guiDownloads) { + this.guiDownloads = guiDownloads; + putValue(Action.NAME, "Auswahl umkehren"); + } + + @Override + public void actionPerformed(ActionEvent e) { + guiDownloads.getTableComponent().invertSelection(); + } +} diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java index cc18ff0cb9..719eb0dd97 100644 --- a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java @@ -81,7 +81,6 @@ public class GuiDownloads extends AGuiTabPanel { private static final String COMBO_VIEW_RUN_ONLY = "nur laufende"; private static final String COMBO_VIEW_FINISHED_ONLY = "nur abgeschlossene"; - private static final String MENU_ITEM_TEXT_CLEANUP_DOWNLOADS = "Liste säubern"; private static final String ACTION_MAP_KEY_EDIT_DOWNLOAD = "dl_aendern"; private static final String ACTION_MAP_KEY_DELETE_DOWNLOAD = "dl_delete"; private static final String ACTION_MAP_KEY_MARK_AS_SEEN = "seen"; @@ -105,6 +104,8 @@ public class GuiDownloads extends AGuiTabPanel { protected StopAllDownloadsAction stopAllDownloadsAction = new StopAllDownloadsAction(this); protected StopAllWaitingDownloadsAction stopAllWaitingDownloadsAction = new StopAllWaitingDownloadsAction(this); protected RefreshDownloadListAction refreshDownloadListAction = new RefreshDownloadListAction(this); + protected CleanupDownloadListAction cleanupDownloadListAction = new CleanupDownloadListAction(this); + protected InvertSelectionAction invertSelectionAction = new InvertSelectionAction(this); private boolean onlyAbos; private boolean onlyDownloads; private boolean onlyWaiting; @@ -396,6 +397,10 @@ private void setupDownloadRateLimitSpinner() { }); } + public MVDownloadsTable getTableComponent() { + return tabelle; + } + @Override public void installMenuEntries(JMenu menu) { daten.getFilmeLaden().addAdListener(new ListenerFilmeLaden() { @@ -410,10 +415,6 @@ public void fertig(ListenerFilmeLadenEvent event) { } }); - JMenuItem miCleanupDownloads = new JMenuItem(MENU_ITEM_TEXT_CLEANUP_DOWNLOADS); - miCleanupDownloads.setIcon(IconFontSwing.buildIcon(FontAwesome.ERASER, 16)); - miCleanupDownloads.addActionListener(e -> cleanupDownloads()); - JMenuItem miStartDownloads = new JMenuItem("Ausgewählte Downloads starten"); miStartDownloads.setIcon(IconFontSwing.buildIcon(FontAwesome.CARET_DOWN, 16)); miStartDownloads.addActionListener(e -> starten(false)); @@ -449,9 +450,6 @@ public void fertig(ListenerFilmeLadenEvent event) { miPlayDownload.setIcon(IconFontSwing.buildIcon(FontAwesome.PLAY, 16)); miPlayDownload.addActionListener(e -> filmAbspielen()); - JMenuItem miInvertSelection = new JMenuItem("Auswahl umkehren"); - miInvertSelection.addActionListener(e -> tabelle.invertSelection()); - JMenuItem miShutdownAfterDownload = new JMenuItem("Aktion nach abgeschlossenen Downloads..."); miShutdownAfterDownload.setIcon(IconFontSwing.buildIcon(FontAwesome.POWER_OFF, 16)); miShutdownAfterDownload.addActionListener(e -> { @@ -472,7 +470,7 @@ public void fertig(ListenerFilmeLadenEvent event) { menu.add(stopAllDownloadsAction); menu.add(stopAllWaitingDownloadsAction); menu.add(refreshDownloadListAction); - menu.add(miCleanupDownloads); + menu.add(cleanupDownloadListAction); menu.addSeparator(); menu.add(miStartDownloads); menu.add(miStopDownloads); @@ -487,7 +485,7 @@ public void fertig(ListenerFilmeLadenEvent event) { menu.add(miMarkFilmAsUnseen); menu.add(miPlayDownload); menu.addSeparator(); - menu.add(miInvertSelection); + menu.add(invertSelectionAction); menu.addSeparator(); menu.add(miShutdownAfterDownload); } @@ -1519,27 +1517,15 @@ private void showMenu(MouseEvent evt) { itemAendern.addActionListener(arg0 -> editDownload()); jPopupMenu.addSeparator(); - jPopupMenu.add(startAllDownloadsAction); - - JMenuItem itemAlleStoppen = new JMenuItem("Alle Downloads stoppen"); - itemAlleStoppen.addActionListener(arg0 -> stoppen(true)); - jPopupMenu.add(itemAlleStoppen); + jPopupMenu.add(stopAllDownloadsAction); JMenuItem itemWartendeStoppen = new JMenuItem("wartende Downloads stoppen"); jPopupMenu.add(itemWartendeStoppen); itemWartendeStoppen.addActionListener(arg0 -> stopAllWaitingDownloads()); - JMenuItem itemAktualisieren = new JMenuItem("Liste der Downloads aktualisieren"); - itemAktualisieren.setIcon(IconFontSwing.buildIcon(FontAwesome.REFRESH, 16)); - jPopupMenu.add(itemAktualisieren); - itemAktualisieren.addActionListener(arg0 -> updateDownloads()); - - JMenuItem itemAufraeumen = new JMenuItem(MENU_ITEM_TEXT_CLEANUP_DOWNLOADS); - itemAufraeumen.setIcon(IconFontSwing.buildIcon(FontAwesome.ERASER, 16)); - jPopupMenu.add(itemAufraeumen); - itemAufraeumen.addActionListener(arg0 -> cleanupDownloads()); - + jPopupMenu.add(refreshDownloadListAction); + jPopupMenu.add(cleanupDownloadListAction); jPopupMenu.addSeparator(); // Film abspielen diff --git a/src/main/resources/icons/fontawesome/eraser.svg b/src/main/resources/icons/fontawesome/eraser.svg new file mode 100755 index 0000000000..f458da4c31 --- /dev/null +++ b/src/main/resources/icons/fontawesome/eraser.svg @@ -0,0 +1 @@ + \ No newline at end of file From 185303bab5eb800ae443df6f9c024c2e958bcda1 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 9 Jun 2022 23:26:18 +0200 Subject: [PATCH 029/422] - convert menu items to actions Former-commit-id: fa40caf7931b2a60d64c67b2356415216424ecdd --- src/main/java/mediathek/config/Icons.java | 2 - .../gui/actions/AdvanceDownloadsAction.java | 22 +++++ .../gui/actions/DeferDownloadsAction.java | 22 +++++ .../gui/actions/PlayDownloadAction.java | 22 +++++ .../actions/ShutdownAfterDownloadAction.java | 32 ++++++++ .../gui/actions/StartDownloadsAction.java | 22 +++++ .../gui/actions/StopDownloadsAction.java | 20 +++++ .../gui/tabs/tab_downloads/GuiDownloads.java | 76 ++++-------------- .../resources/icons/fontawesome/arrow-up.svg | 1 + .../icons/fontawesome/caret-down.svg | 1 + .../resources/icons/fontawesome/clock.svg | 1 + .../resources/icons/fontawesome/power-off.svg | 1 + .../res/programm/menue-vorziehen.png | Bin 851 -> 0 bytes 13 files changed, 161 insertions(+), 61 deletions(-) create mode 100644 src/main/java/mediathek/gui/actions/AdvanceDownloadsAction.java create mode 100644 src/main/java/mediathek/gui/actions/DeferDownloadsAction.java create mode 100644 src/main/java/mediathek/gui/actions/PlayDownloadAction.java create mode 100644 src/main/java/mediathek/gui/actions/ShutdownAfterDownloadAction.java create mode 100644 src/main/java/mediathek/gui/actions/StartDownloadsAction.java create mode 100644 src/main/java/mediathek/gui/actions/StopDownloadsAction.java create mode 100755 src/main/resources/icons/fontawesome/arrow-up.svg create mode 100755 src/main/resources/icons/fontawesome/caret-down.svg create mode 100755 src/main/resources/icons/fontawesome/clock.svg create mode 100755 src/main/resources/icons/fontawesome/power-off.svg delete mode 100644 src/main/resources/mediathek/res/programm/menue-vorziehen.png diff --git a/src/main/java/mediathek/config/Icons.java b/src/main/java/mediathek/config/Icons.java index 078756ac04..8ad93dd856 100644 --- a/src/main/java/mediathek/config/Icons.java +++ b/src/main/java/mediathek/config/Icons.java @@ -27,8 +27,6 @@ public class Icons { public static final ImageIcon ICON_DIALOG_EIN_SW = GetIcon.getProgramIcon("dialog-ein-sw.png", 16, 16); - public static final ImageIcon ICON_MENUE_VORZIEHEN = GetIcon.getProgramIcon("menue-vorziehen.png", 16, 16); - public static final ImageIcon ICON_TABELLE_EIN = GetIcon.getProgramIcon("tabelle-ein.png", 16, 16); public static final ImageIcon ICON_TABELLE_AUS = GetIcon.getProgramIcon("tabelle-aus.png", 5, 5); diff --git a/src/main/java/mediathek/gui/actions/AdvanceDownloadsAction.java b/src/main/java/mediathek/gui/actions/AdvanceDownloadsAction.java new file mode 100644 index 0000000000..d8d0fc27f4 --- /dev/null +++ b/src/main/java/mediathek/gui/actions/AdvanceDownloadsAction.java @@ -0,0 +1,22 @@ +package mediathek.gui.actions; + +import mediathek.gui.tabs.tab_downloads.GuiDownloads; +import mediathek.tool.SVGIconUtilities; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class AdvanceDownloadsAction extends AbstractAction { + private final GuiDownloads guiDownloads; + + public AdvanceDownloadsAction(GuiDownloads guiDownloads) { + this.guiDownloads = guiDownloads; + putValue(Action.NAME, "Downloads vorziehen"); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/arrow-up.svg")); + } + + @Override + public void actionPerformed(ActionEvent e) { + guiDownloads.downloadsVorziehen(); + } +} diff --git a/src/main/java/mediathek/gui/actions/DeferDownloadsAction.java b/src/main/java/mediathek/gui/actions/DeferDownloadsAction.java new file mode 100644 index 0000000000..feb4b3a5d4 --- /dev/null +++ b/src/main/java/mediathek/gui/actions/DeferDownloadsAction.java @@ -0,0 +1,22 @@ +package mediathek.gui.actions; + +import mediathek.gui.tabs.tab_downloads.GuiDownloads; +import mediathek.tool.SVGIconUtilities; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class DeferDownloadsAction extends AbstractAction { + private final GuiDownloads guiDownloads; + + public DeferDownloadsAction(GuiDownloads guiDownloads) { + this.guiDownloads = guiDownloads; + putValue(Action.NAME, "Downloads zurückstellen"); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/clock.svg")); + } + + @Override + public void actionPerformed(ActionEvent e) { + guiDownloads.downloadLoeschen(false); + } +} diff --git a/src/main/java/mediathek/gui/actions/PlayDownloadAction.java b/src/main/java/mediathek/gui/actions/PlayDownloadAction.java new file mode 100644 index 0000000000..62f0e6fe8d --- /dev/null +++ b/src/main/java/mediathek/gui/actions/PlayDownloadAction.java @@ -0,0 +1,22 @@ +package mediathek.gui.actions; + +import mediathek.gui.tabs.tab_downloads.GuiDownloads; +import mediathek.tool.SVGIconUtilities; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class PlayDownloadAction extends AbstractAction { + private final GuiDownloads guiDownloads; + + public PlayDownloadAction(GuiDownloads guiDownloads) { + this.guiDownloads = guiDownloads; + putValue(Action.NAME, "Gespeicherten Film abspielen"); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/play.svg")); + } + + @Override + public void actionPerformed(ActionEvent e) { + guiDownloads.filmAbspielen(); + } +} diff --git a/src/main/java/mediathek/gui/actions/ShutdownAfterDownloadAction.java b/src/main/java/mediathek/gui/actions/ShutdownAfterDownloadAction.java new file mode 100644 index 0000000000..0c2149763b --- /dev/null +++ b/src/main/java/mediathek/gui/actions/ShutdownAfterDownloadAction.java @@ -0,0 +1,32 @@ +package mediathek.gui.actions; + +import mediathek.config.Daten; +import mediathek.mainwindow.MediathekGui; +import mediathek.tool.SVGIconUtilities; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class ShutdownAfterDownloadAction extends AbstractAction { + private final MediathekGui mediathekGui; + + public ShutdownAfterDownloadAction(MediathekGui mediathekGui) { + this.mediathekGui = mediathekGui; + putValue(Action.NAME, "Aktion nach abgeschlossenen Downloads..."); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/power-off.svg")); + } + + @Override + public void actionPerformed(ActionEvent e) { + if (Daten.getInstance().getListeDownloads().unfinishedDownloads() > 0) { + // ansonsten gibts keine laufenden Downloads auf die man warten sollte + mediathekGui.beenden(true, false); + } else { + JOptionPane.showMessageDialog(mediathekGui, + "Die Downloads müssen zuerst gestartet werden.", + "Keine laufenden Downloads", + JOptionPane.ERROR_MESSAGE); + + } + } +} diff --git a/src/main/java/mediathek/gui/actions/StartDownloadsAction.java b/src/main/java/mediathek/gui/actions/StartDownloadsAction.java new file mode 100644 index 0000000000..cbd2b168e0 --- /dev/null +++ b/src/main/java/mediathek/gui/actions/StartDownloadsAction.java @@ -0,0 +1,22 @@ +package mediathek.gui.actions; + +import mediathek.gui.tabs.tab_downloads.GuiDownloads; +import mediathek.tool.SVGIconUtilities; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class StartDownloadsAction extends AbstractAction { + private final GuiDownloads guiDownloads; + + public StartDownloadsAction(GuiDownloads guiDownloads) { + this.guiDownloads = guiDownloads; + putValue(Action.NAME, "Ausgewählte Downloads starten"); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/caret-down.svg")); + } + + @Override + public void actionPerformed(ActionEvent e) { + guiDownloads.starten(false); + } +} diff --git a/src/main/java/mediathek/gui/actions/StopDownloadsAction.java b/src/main/java/mediathek/gui/actions/StopDownloadsAction.java new file mode 100644 index 0000000000..1d06c05ce0 --- /dev/null +++ b/src/main/java/mediathek/gui/actions/StopDownloadsAction.java @@ -0,0 +1,20 @@ +package mediathek.gui.actions; + +import mediathek.gui.tabs.tab_downloads.GuiDownloads; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class StopDownloadsAction extends AbstractAction { + private final GuiDownloads guiDownloads; + + public StopDownloadsAction(GuiDownloads guiDownloads) { + this.guiDownloads = guiDownloads; + putValue(Action.NAME, "Ausgewählte Downloads stoppen"); + } + + @Override + public void actionPerformed(ActionEvent e) { + guiDownloads.stoppen(false); + } +} diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java index 719eb0dd97..ae0a3b1f5d 100644 --- a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java @@ -106,6 +106,12 @@ public class GuiDownloads extends AGuiTabPanel { protected RefreshDownloadListAction refreshDownloadListAction = new RefreshDownloadListAction(this); protected CleanupDownloadListAction cleanupDownloadListAction = new CleanupDownloadListAction(this); protected InvertSelectionAction invertSelectionAction = new InvertSelectionAction(this); + protected ShutdownAfterDownloadAction shutdownAfterDownloadAction = new ShutdownAfterDownloadAction(mediathekGui); + protected PlayDownloadAction playDownloadAction = new PlayDownloadAction(this); + protected StopDownloadsAction stopDownloadsAction = new StopDownloadsAction(this); + protected StartDownloadsAction startDownloadsAction = new StartDownloadsAction(this); + protected DeferDownloadsAction deferDownloadsAction = new DeferDownloadsAction(this); + protected AdvanceDownloadsAction advanceDownloadsAction = new AdvanceDownloadsAction(this); private boolean onlyAbos; private boolean onlyDownloads; private boolean onlyWaiting; @@ -415,21 +421,6 @@ public void fertig(ListenerFilmeLadenEvent event) { } }); - JMenuItem miStartDownloads = new JMenuItem("Ausgewählte Downloads starten"); - miStartDownloads.setIcon(IconFontSwing.buildIcon(FontAwesome.CARET_DOWN, 16)); - miStartDownloads.addActionListener(e -> starten(false)); - - JMenuItem miStopDownloads = new JMenuItem("Ausgewählte Downloads stoppen"); - miStopDownloads.addActionListener(e -> stoppen(false)); - - JMenuItem miDownloadsVorziehen = new JMenuItem("Downloads vorziehen"); - miDownloadsVorziehen.setIcon(Icons.ICON_MENUE_VORZIEHEN); - miDownloadsVorziehen.addActionListener(e -> downloadsVorziehen()); - - JMenuItem miDownloadsZurueckstellen = new JMenuItem("Downloads zurückstellen"); - miDownloadsZurueckstellen.setIcon(IconFontSwing.buildIcon(FontAwesome.CLOCK_O, 16)); - miDownloadsZurueckstellen.addActionListener(e -> downloadLoeschen(false)); - JMenuItem miDownloadsLoeschen = new JMenuItem("Downloads aus Liste entfernen"); miDownloadsLoeschen.setIcon(IconFontSwing.buildIcon(FontAwesome.TRASH_O, 16)); miDownloadsLoeschen.addActionListener(e -> downloadLoeschen(true)); @@ -446,25 +437,6 @@ public void fertig(ListenerFilmeLadenEvent event) { miMarkFilmAsUnseen.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, KeyEvent.CTRL_DOWN_MASK)); miMarkFilmAsUnseen.addActionListener(markFilmAsUnseenAction); - JMenuItem miPlayDownload = new JMenuItem("Gespeicherten Film abspielen"); - miPlayDownload.setIcon(IconFontSwing.buildIcon(FontAwesome.PLAY, 16)); - miPlayDownload.addActionListener(e -> filmAbspielen()); - - JMenuItem miShutdownAfterDownload = new JMenuItem("Aktion nach abgeschlossenen Downloads..."); - miShutdownAfterDownload.setIcon(IconFontSwing.buildIcon(FontAwesome.POWER_OFF, 16)); - miShutdownAfterDownload.addActionListener(e -> { - if (daten.getListeDownloads().unfinishedDownloads() > 0) { - // ansonsten gibts keine laufenden Downloads auf die man warten sollte - mediathekGui.beenden(true, false); - } else { - JOptionPane.showMessageDialog(this, - "Die Downloads müssen zuerst gestartet werden.", - "Keine laufenden Downloads", - JOptionPane.ERROR_MESSAGE); - - } - }); - menu.add(startAllDownloadsAction); menu.add(startAllDownloadsTimedAction); menu.add(stopAllDownloadsAction); @@ -472,10 +444,10 @@ public void fertig(ListenerFilmeLadenEvent event) { menu.add(refreshDownloadListAction); menu.add(cleanupDownloadListAction); menu.addSeparator(); - menu.add(miStartDownloads); - menu.add(miStopDownloads); - menu.add(miDownloadsVorziehen); - menu.add(miDownloadsZurueckstellen); + menu.add(startDownloadsAction); + menu.add(stopDownloadsAction); + menu.add(advanceDownloadsAction); + menu.add(deferDownloadsAction); menu.add(miDownloadsLoeschen); menu.add(miEditDownload); menu.addSeparator(); @@ -483,11 +455,11 @@ public void fertig(ListenerFilmeLadenEvent event) { menu.addSeparator(); menu.add(miMarkFilmAsSeen); menu.add(miMarkFilmAsUnseen); - menu.add(miPlayDownload); + menu.add(playDownloadAction); menu.addSeparator(); menu.add(invertSelectionAction); menu.addSeparator(); - menu.add(miShutdownAfterDownload); + menu.add(shutdownAfterDownloadAction); } private void setupDescriptionPanel() { @@ -909,7 +881,7 @@ public synchronized void editDownload() { } } - private void downloadsVorziehen() { + public void downloadsVorziehen() { ArrayList arrayDownloads = getSelDownloads(); if (arrayDownloads.isEmpty()) { return; @@ -1483,7 +1455,7 @@ private void showMenu(MouseEvent evt) { } // Download starten JMenuItem itemStarten = new JMenuItem("Download starten"); - itemStarten.setIcon(IconFontSwing.buildIcon(FontAwesome.CARET_DOWN, 16)); + itemStarten.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/caret-down.svg")); itemStarten.setEnabled(!wartenOderLaufen); jPopupMenu.add(itemStarten); itemStarten.addActionListener(arg0 -> filmStartenWiederholenStoppen(false, true, true, false)); @@ -1495,16 +1467,8 @@ private void showMenu(MouseEvent evt) { itemStoppen.addActionListener(arg0 -> filmStartenWiederholenStoppen(false, false, true, false)); jPopupMenu.addSeparator(); - - JMenuItem itemVorziehen = new JMenuItem("Download vorziehen"); - itemVorziehen.setIcon(Icons.ICON_MENUE_VORZIEHEN); - jPopupMenu.add(itemVorziehen); - itemVorziehen.addActionListener(arg0 -> downloadsVorziehen()); - - JMenuItem itemLoeschen = new JMenuItem("Download zurückstellen"); - itemLoeschen.setIcon(IconFontSwing.buildIcon(FontAwesome.CLOCK_O, 16)); - jPopupMenu.add(itemLoeschen); - itemLoeschen.addActionListener(arg0 -> downloadLoeschen(false /* dauerhaft */)); + jPopupMenu.add(advanceDownloadsAction); + jPopupMenu.add(deferDownloadsAction); //dauerhaft löschen JMenuItem itemDauerhaftLoeschen = new JMenuItem("Download aus Liste entfernen"); itemDauerhaftLoeschen.setIcon(IconFontSwing.buildIcon(FontAwesome.TRASH_O, 16)); @@ -1527,13 +1491,7 @@ private void showMenu(MouseEvent evt) { jPopupMenu.add(refreshDownloadListAction); jPopupMenu.add(cleanupDownloadListAction); jPopupMenu.addSeparator(); - - // Film abspielen - JMenuItem itemPlayerDownload = new JMenuItem("gespeicherten Film (Datei) abspielen"); - itemPlayerDownload.setIcon(IconFontSwing.buildIcon(FontAwesome.PLAY, 16)); - - itemPlayerDownload.addActionListener(e -> filmAbspielen()); - jPopupMenu.add(itemPlayerDownload); + jPopupMenu.add(playDownloadAction); // Film löschen JMenuItem itemDeleteDownload = new JMenuItem("gespeicherten Film (Datei) löschen"); itemDeleteDownload.setIcon(IconFontSwing.buildIcon(FontAwesome.TIMES, 16)); diff --git a/src/main/resources/icons/fontawesome/arrow-up.svg b/src/main/resources/icons/fontawesome/arrow-up.svg new file mode 100755 index 0000000000..97b3239556 --- /dev/null +++ b/src/main/resources/icons/fontawesome/arrow-up.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/resources/icons/fontawesome/caret-down.svg b/src/main/resources/icons/fontawesome/caret-down.svg new file mode 100755 index 0000000000..dfebcf66c1 --- /dev/null +++ b/src/main/resources/icons/fontawesome/caret-down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/resources/icons/fontawesome/clock.svg b/src/main/resources/icons/fontawesome/clock.svg new file mode 100755 index 0000000000..9dff515142 --- /dev/null +++ b/src/main/resources/icons/fontawesome/clock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/resources/icons/fontawesome/power-off.svg b/src/main/resources/icons/fontawesome/power-off.svg new file mode 100755 index 0000000000..a3fe006388 --- /dev/null +++ b/src/main/resources/icons/fontawesome/power-off.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/resources/mediathek/res/programm/menue-vorziehen.png b/src/main/resources/mediathek/res/programm/menue-vorziehen.png deleted file mode 100644 index bc89af77f820e211717c2863a697f7849b51f23d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 851 zcmV-Z1FZasP)z@;j(q!3lK=n!AY({UO#lFTB>(_`g8%^e{{R4h=>PzA zFaQARU;qF*m;eA5Z<1fdMgRZ;$w@>(RCwB)lD}^obsUDD@BPgA;=9;2O`F7aue5D!lB}E{txQG z()9CRt%Tn$T?@xbho=8qJ5!wI+^_A#XOaBHlk%I(SK1?^g@2W(wvOV zbz_ZVBQE(;PCxwMqfh+y<9BO;?_IyMz{$?=p{at&QktC|je*>-QJ9!K z_50$&db{Z@-!2#!o#E+EW*MDKz%A%cq9tukmXf%A3Y|7&OC$RI4?lilW%2usR%3hl zAAwHt;2WGiKg+$xENFw1f`pA~TRc-V$yfqaNOl@64h-jv!jT6~U0+<-Znyla8ZeAd z1C~F3m&+G?bn`KBX6{Wry-!K1&~Jj9fLg5{pMU#>a(6fU$;s?@6;cF9c)7phGCcrJ>n>$;rn}^Xjv97zy2!w%pQ8&!xT{JhY1(P-M<&;dn*(HGU_6_ zZC;z1#W$X>T3JS*I(Xf-N>nZB9tov`gC1Xs#Q64;$&xPYD#G~o7lE3gCLNi zUaupB&^^!F@B98xwOT1$TUr|PJnulO6*%2ER{BhH$8nwd(W4Ko=JWYK(&=<{aBy&4 d)3orf0RSz07j=AiS!4hJ002ovPDHLkV1hxNh6Mls From fee98affc4e31be43b7524f22b6087fa112347bc Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 9 Jun 2022 23:34:22 +0200 Subject: [PATCH 030/422] - convert menu items to actions Former-commit-id: f24bb933e111989358885407b5f0183870d209b9 --- .../gui/actions/DeleteDownloadsAction.java | 22 +++++++++++++++++++ .../gui/tabs/tab_downloads/GuiDownloads.java | 13 +++-------- .../resources/icons/fontawesome/trash-can.svg | 1 + 3 files changed, 26 insertions(+), 10 deletions(-) create mode 100644 src/main/java/mediathek/gui/actions/DeleteDownloadsAction.java create mode 100755 src/main/resources/icons/fontawesome/trash-can.svg diff --git a/src/main/java/mediathek/gui/actions/DeleteDownloadsAction.java b/src/main/java/mediathek/gui/actions/DeleteDownloadsAction.java new file mode 100644 index 0000000000..72567404ae --- /dev/null +++ b/src/main/java/mediathek/gui/actions/DeleteDownloadsAction.java @@ -0,0 +1,22 @@ +package mediathek.gui.actions; + +import mediathek.gui.tabs.tab_downloads.GuiDownloads; +import mediathek.tool.SVGIconUtilities; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class DeleteDownloadsAction extends AbstractAction { + private final GuiDownloads guiDownloads; + + public DeleteDownloadsAction(GuiDownloads guiDownloads) { + this.guiDownloads = guiDownloads; + putValue(Action.NAME, "Downloads aus Liste entfernen"); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/trash-can.svg")); + } + + @Override + public void actionPerformed(ActionEvent e) { + guiDownloads.downloadLoeschen(true); + } +} diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java index ae0a3b1f5d..962a416280 100644 --- a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java @@ -112,6 +112,7 @@ public class GuiDownloads extends AGuiTabPanel { protected StartDownloadsAction startDownloadsAction = new StartDownloadsAction(this); protected DeferDownloadsAction deferDownloadsAction = new DeferDownloadsAction(this); protected AdvanceDownloadsAction advanceDownloadsAction = new AdvanceDownloadsAction(this); + protected DeleteDownloadsAction deleteDownloadsAction = new DeleteDownloadsAction(this); private boolean onlyAbos; private boolean onlyDownloads; private boolean onlyWaiting; @@ -421,10 +422,6 @@ public void fertig(ListenerFilmeLadenEvent event) { } }); - JMenuItem miDownloadsLoeschen = new JMenuItem("Downloads aus Liste entfernen"); - miDownloadsLoeschen.setIcon(IconFontSwing.buildIcon(FontAwesome.TRASH_O, 16)); - miDownloadsLoeschen.addActionListener(e -> downloadLoeschen(true)); - JMenuItem miEditDownload = new JMenuItem("Download ändern"); miEditDownload.setIcon(IconFontSwing.buildIcon(FontAwesome.PENCIL_SQUARE_O, 16)); miEditDownload.addActionListener(e -> editDownload()); @@ -448,7 +445,7 @@ public void fertig(ListenerFilmeLadenEvent event) { menu.add(stopDownloadsAction); menu.add(advanceDownloadsAction); menu.add(deferDownloadsAction); - menu.add(miDownloadsLoeschen); + menu.add(deleteDownloadsAction); menu.add(miEditDownload); menu.addSeparator(); menu.add(cbShowDownloadDescription); @@ -1469,11 +1466,7 @@ private void showMenu(MouseEvent evt) { jPopupMenu.addSeparator(); jPopupMenu.add(advanceDownloadsAction); jPopupMenu.add(deferDownloadsAction); - //dauerhaft löschen - JMenuItem itemDauerhaftLoeschen = new JMenuItem("Download aus Liste entfernen"); - itemDauerhaftLoeschen.setIcon(IconFontSwing.buildIcon(FontAwesome.TRASH_O, 16)); - jPopupMenu.add(itemDauerhaftLoeschen); - itemDauerhaftLoeschen.addActionListener(arg0 -> downloadLoeschen(true /* dauerhaft */)); + jPopupMenu.add(deleteDownloadsAction); //Download ändern JMenuItem itemAendern = new JMenuItem("Download ändern"); itemAendern.setIcon(IconFontSwing.buildIcon(FontAwesome.PENCIL_SQUARE_O, 16)); diff --git a/src/main/resources/icons/fontawesome/trash-can.svg b/src/main/resources/icons/fontawesome/trash-can.svg new file mode 100755 index 0000000000..f9a1d420c5 --- /dev/null +++ b/src/main/resources/icons/fontawesome/trash-can.svg @@ -0,0 +1 @@ + \ No newline at end of file From 568e66b37cdca10fc0d960c52b6ad96d482799da Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 9 Jun 2022 23:40:17 +0200 Subject: [PATCH 031/422] - convert menu items to actions Former-commit-id: 143480b3c9774891f695d95058604d30513051b4 --- .../gui/actions/EditDownloadAction.java | 22 +++++++++++++++++++ .../gui/tabs/tab_downloads/GuiDownloads.java | 13 +++-------- .../icons/fontawesome/pen-to-square.svg | 1 + 3 files changed, 26 insertions(+), 10 deletions(-) create mode 100644 src/main/java/mediathek/gui/actions/EditDownloadAction.java create mode 100755 src/main/resources/icons/fontawesome/pen-to-square.svg diff --git a/src/main/java/mediathek/gui/actions/EditDownloadAction.java b/src/main/java/mediathek/gui/actions/EditDownloadAction.java new file mode 100644 index 0000000000..300fc937cb --- /dev/null +++ b/src/main/java/mediathek/gui/actions/EditDownloadAction.java @@ -0,0 +1,22 @@ +package mediathek.gui.actions; + +import mediathek.gui.tabs.tab_downloads.GuiDownloads; +import mediathek.tool.SVGIconUtilities; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class EditDownloadAction extends AbstractAction { + private final GuiDownloads guiDownloads; + + public EditDownloadAction(GuiDownloads guiDownloads) { + this.guiDownloads = guiDownloads; + putValue(Action.NAME, "Download ändern"); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/pen-to-square.svg")); + } + + @Override + public void actionPerformed(ActionEvent e) { + guiDownloads.editDownload(); + } +} diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java index 962a416280..c41e4548f0 100644 --- a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java @@ -113,6 +113,7 @@ public class GuiDownloads extends AGuiTabPanel { protected DeferDownloadsAction deferDownloadsAction = new DeferDownloadsAction(this); protected AdvanceDownloadsAction advanceDownloadsAction = new AdvanceDownloadsAction(this); protected DeleteDownloadsAction deleteDownloadsAction = new DeleteDownloadsAction(this); + protected EditDownloadAction editDownloadAction = new EditDownloadAction(this); private boolean onlyAbos; private boolean onlyDownloads; private boolean onlyWaiting; @@ -422,10 +423,6 @@ public void fertig(ListenerFilmeLadenEvent event) { } }); - JMenuItem miEditDownload = new JMenuItem("Download ändern"); - miEditDownload.setIcon(IconFontSwing.buildIcon(FontAwesome.PENCIL_SQUARE_O, 16)); - miEditDownload.addActionListener(e -> editDownload()); - JMenuItem miMarkFilmAsSeen = new JMenuItem("Filme als gesehen markieren"); miMarkFilmAsSeen.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_G, KeyEvent.CTRL_DOWN_MASK)); miMarkFilmAsSeen.addActionListener(markFilmAsSeenAction); @@ -446,7 +443,7 @@ public void fertig(ListenerFilmeLadenEvent event) { menu.add(advanceDownloadsAction); menu.add(deferDownloadsAction); menu.add(deleteDownloadsAction); - menu.add(miEditDownload); + menu.add(editDownloadAction); menu.addSeparator(); menu.add(cbShowDownloadDescription); menu.addSeparator(); @@ -1467,11 +1464,7 @@ private void showMenu(MouseEvent evt) { jPopupMenu.add(advanceDownloadsAction); jPopupMenu.add(deferDownloadsAction); jPopupMenu.add(deleteDownloadsAction); - //Download ändern - JMenuItem itemAendern = new JMenuItem("Download ändern"); - itemAendern.setIcon(IconFontSwing.buildIcon(FontAwesome.PENCIL_SQUARE_O, 16)); - jPopupMenu.add(itemAendern); - itemAendern.addActionListener(arg0 -> editDownload()); + jPopupMenu.add(editDownloadAction); jPopupMenu.addSeparator(); jPopupMenu.add(startAllDownloadsAction); diff --git a/src/main/resources/icons/fontawesome/pen-to-square.svg b/src/main/resources/icons/fontawesome/pen-to-square.svg new file mode 100755 index 0000000000..e8ad32ad64 --- /dev/null +++ b/src/main/resources/icons/fontawesome/pen-to-square.svg @@ -0,0 +1 @@ + \ No newline at end of file From cf9203f3a54a64381760b0e8ebd7c2b999edeb33 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 9 Jun 2022 23:58:04 +0200 Subject: [PATCH 032/422] - convert menu items to actions Former-commit-id: 162f9af9991c252c8a033fe74dea36f4b2c3f423 --- .../gui/actions/DeleteDownloadAction.java | 22 ++++++++++++++++++ .../gui/actions/OpenTargetFolderAction.java | 22 ++++++++++++++++++ .../gui/tabs/tab_downloads/GuiDownloads.java | 23 +++++-------------- .../icons/fontawesome/folder-open.svg | 1 + .../resources/icons/fontawesome/xmark.svg | 1 + 5 files changed, 52 insertions(+), 17 deletions(-) create mode 100644 src/main/java/mediathek/gui/actions/DeleteDownloadAction.java create mode 100644 src/main/java/mediathek/gui/actions/OpenTargetFolderAction.java create mode 100755 src/main/resources/icons/fontawesome/folder-open.svg create mode 100755 src/main/resources/icons/fontawesome/xmark.svg diff --git a/src/main/java/mediathek/gui/actions/DeleteDownloadAction.java b/src/main/java/mediathek/gui/actions/DeleteDownloadAction.java new file mode 100644 index 0000000000..bd03e7b249 --- /dev/null +++ b/src/main/java/mediathek/gui/actions/DeleteDownloadAction.java @@ -0,0 +1,22 @@ +package mediathek.gui.actions; + +import mediathek.gui.tabs.tab_downloads.GuiDownloads; +import mediathek.tool.SVGIconUtilities; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class DeleteDownloadAction extends AbstractAction { + private final GuiDownloads guiDownloads; + + public DeleteDownloadAction(GuiDownloads guiDownloads) { + this.guiDownloads = guiDownloads; + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/xmark.svg")); + putValue(Action.NAME, "gespeicherten Film (Datei) löschen"); + } + + @Override + public void actionPerformed(ActionEvent e) { + guiDownloads.filmLoeschen_(); + } +} diff --git a/src/main/java/mediathek/gui/actions/OpenTargetFolderAction.java b/src/main/java/mediathek/gui/actions/OpenTargetFolderAction.java new file mode 100644 index 0000000000..ea46d8d4af --- /dev/null +++ b/src/main/java/mediathek/gui/actions/OpenTargetFolderAction.java @@ -0,0 +1,22 @@ +package mediathek.gui.actions; + +import mediathek.gui.tabs.tab_downloads.GuiDownloads; +import mediathek.tool.SVGIconUtilities; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class OpenTargetFolderAction extends AbstractAction { + private final GuiDownloads guiDownloads; + + public OpenTargetFolderAction(GuiDownloads guiDownloads) { + this.guiDownloads = guiDownloads; + putValue(Action.NAME, "Zielordner öffnen"); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg")); + } + + @Override + public void actionPerformed(ActionEvent e) { + guiDownloads.zielordnerOeffnen(); + } +} diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java index c41e4548f0..1f4c826c25 100644 --- a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java @@ -10,8 +10,6 @@ import javafx.scene.control.Alert; import javafx.scene.control.TabPane; import javafx.stage.Modality; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.Daten; import mediathek.config.Icons; import mediathek.config.Konstanten; @@ -114,6 +112,8 @@ public class GuiDownloads extends AGuiTabPanel { protected AdvanceDownloadsAction advanceDownloadsAction = new AdvanceDownloadsAction(this); protected DeleteDownloadsAction deleteDownloadsAction = new DeleteDownloadsAction(this); protected EditDownloadAction editDownloadAction = new EditDownloadAction(this); + protected DeleteDownloadAction deleteDownloadAction = new DeleteDownloadAction(this); + protected OpenTargetFolderAction openTargetFolderAction = new OpenTargetFolderAction(this); private boolean onlyAbos; private boolean onlyDownloads; private boolean onlyWaiting; @@ -883,7 +883,7 @@ public void downloadsVorziehen() { daten.getListeDownloads().downloadsVorziehen(arrayDownloads); } - private void zielordnerOeffnen() { + public void zielordnerOeffnen() { DatenDownload datenDownload = getSelDownload(); if (datenDownload == null) { return; @@ -901,7 +901,7 @@ public void filmAbspielen() { OpenPlayerAction.filmAbspielen(mediathekGui, s); } - private void filmLoeschen_() { + public void filmLoeschen_() { DatenDownload datenDownload = getSelDownload(); if (datenDownload == null) { return; @@ -1427,7 +1427,6 @@ private void buttonTable(int row, int column) { } } } - private void showMenu(MouseEvent evt) { p = evt.getPoint(); final int nr = tabelle.rowAtPoint(p); @@ -1478,18 +1477,8 @@ private void showMenu(MouseEvent evt) { jPopupMenu.add(cleanupDownloadListAction); jPopupMenu.addSeparator(); jPopupMenu.add(playDownloadAction); - // Film löschen - JMenuItem itemDeleteDownload = new JMenuItem("gespeicherten Film (Datei) löschen"); - itemDeleteDownload.setIcon(IconFontSwing.buildIcon(FontAwesome.TIMES, 16)); - - itemDeleteDownload.addActionListener(e -> filmLoeschen_()); - jPopupMenu.add(itemDeleteDownload); - // Zielordner öffnen - JMenuItem itemOeffnen = new JMenuItem("Zielordner öffnen"); - itemOeffnen.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 16)); - jPopupMenu.add(itemOeffnen); - itemOeffnen.addActionListener(e -> zielordnerOeffnen()); - + jPopupMenu.add(deleteDownloadAction); + jPopupMenu.add(openTargetFolderAction); jPopupMenu.addSeparator(); //Abo ändern diff --git a/src/main/resources/icons/fontawesome/folder-open.svg b/src/main/resources/icons/fontawesome/folder-open.svg new file mode 100755 index 0000000000..1e177ec153 --- /dev/null +++ b/src/main/resources/icons/fontawesome/folder-open.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/resources/icons/fontawesome/xmark.svg b/src/main/resources/icons/fontawesome/xmark.svg new file mode 100755 index 0000000000..654bfe754b --- /dev/null +++ b/src/main/resources/icons/fontawesome/xmark.svg @@ -0,0 +1 @@ + \ No newline at end of file From 95a9cf2029031a1d78ca1d34b2a2c059f789096b Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 10 Jun 2022 06:33:18 +0200 Subject: [PATCH 033/422] - convert menu items to actions Former-commit-id: f2c5c1f68cda6990a63a3e1e53da248026b45498 --- src/main/java/mediathek/config/Icons.java | 1 - .../gui/tabs/tab_downloads/GuiDownloads.java | 6 ++---- src/main/resources/icons/fontawesome/broom.svg | 1 + .../mediathek/res/programm/button-clear.png | Bin 773 -> 0 bytes 4 files changed, 3 insertions(+), 5 deletions(-) create mode 100755 src/main/resources/icons/fontawesome/broom.svg delete mode 100644 src/main/resources/mediathek/res/programm/button-clear.png diff --git a/src/main/java/mediathek/config/Icons.java b/src/main/java/mediathek/config/Icons.java index 8ad93dd856..b8bfa5be99 100644 --- a/src/main/java/mediathek/config/Icons.java +++ b/src/main/java/mediathek/config/Icons.java @@ -34,7 +34,6 @@ public class Icons { public static final ImageIcon ICON_BUTTON_ADD = GetIcon.getProgramIcon("button-add.png", 16, 16); public static final ImageIcon ICON_BUTTON_MOVE_DOWN = GetIcon.getProgramIcon("button-move-down.png", 16, 16); public static final ImageIcon ICON_BUTTON_MOVE_UP = GetIcon.getProgramIcon("button-move-up.png", 16, 16); - public static final ImageIcon ICON_BUTTON_CLEAR = GetIcon.getProgramIcon("button-clear.png", 16, 16); // Icons TABBED_PANE public static final ImageIcon ICON_TAB_FILM = GetIcon.getProgramIcon("tab-film.png", 32, 32); diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java index 1f4c826c25..db0c2910d5 100644 --- a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java @@ -11,7 +11,6 @@ import javafx.scene.control.TabPane; import javafx.stage.Modality; import mediathek.config.Daten; -import mediathek.config.Icons; import mediathek.config.Konstanten; import mediathek.config.MVConfig; import mediathek.controller.history.MVUsedUrl; @@ -550,7 +549,6 @@ private void init() { new int[]{DatenDownload.DOWNLOAD_BUTTON_START, DatenDownload.DOWNLOAD_BUTTON_DEL}, true, MVConfig.Configs.SYSTEM_TAB_DOWNLOAD_LINEBREAK)); - btnClear.setIcon(Icons.ICON_BUTTON_CLEAR); btnClear.addActionListener(l -> { cbDisplayCategories.setSelectedIndex(0); cbView.setSelectedIndex(0); @@ -1287,7 +1285,7 @@ private void initComponents() { panel3.add(cbView, new CC().cell(1, 1)); //---- btnClear ---- - btnClear.setIcon(new ImageIcon(getClass().getResource("/mediathek/res/muster/button-clear.png"))); //NON-NLS + btnClear.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/broom.svg")); //NON-NLS btnClear.setToolTipText("Filter zur\u00fccksetzen"); //NON-NLS panel3.add(btnClear, new CC().cell(0, 2, 2, 1).alignX("right").growX(0).width("32:32:32").height("32:32:32")); //NON-NLS } @@ -1542,7 +1540,7 @@ private void showMenu(MouseEvent evt) { } else { menuPath = "Datei->Einstellungen->Set bearbeiten"; } - MVMessageDialog.showMessageDialog(mediathekGui, "Bitte legen Sie im Menü \"" + menuPath + "\" ein Programm zum Abspielen fest.", + JOptionPane.showMessageDialog(mediathekGui, "Bitte legen Sie im Menü \"" + menuPath + "\" ein Programm zum Abspielen fest.", "Kein Videoplayer!", JOptionPane.INFORMATION_MESSAGE); }); } diff --git a/src/main/resources/icons/fontawesome/broom.svg b/src/main/resources/icons/fontawesome/broom.svg new file mode 100755 index 0000000000..fb61ff8ae9 --- /dev/null +++ b/src/main/resources/icons/fontawesome/broom.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/resources/mediathek/res/programm/button-clear.png b/src/main/resources/mediathek/res/programm/button-clear.png deleted file mode 100644 index e6c8e8b9f341cbf3a1795631ccaafd14b0e0c911..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 773 zcmV+g1N!`lP)5 zl1pfmXB38?@0*$EOcI;hNJ5iHyw#`_D<+Ey(t?XhOLl^ng^SURf+GA21zi{y1{{!5 z5QL7#g;8=>?7hOBWP~@Qv%4i?;^z?o7$2sh!G| zts6JQ<4rGsx`hNvL;&d8r%uFIa{QCF&sB3vJ3Xhnz3G3(a_m%-#>gcR&Ll0E*wJ36qI~C2ft2!

!GOpKDtrJ2lSaNQ|Hgjn@93PsJ(gZ`2+IBy96 z0wag|dVeYUtj2Xcip4zE2Iz1NmGK?q-I(0Ec_nX5@MXf0%df=eg{JcD1;K)00000NkvXXu0mjf Dmc(vD From 0733951f09d5502339ebd3f4f8dcb32e95be1f3f Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 10 Jun 2022 07:07:41 +0200 Subject: [PATCH 034/422] - convert icons Former-commit-id: 32d303af5627a26efddc7a5344b694f37b3eb827 --- src/main/java/mediathek/gui/dialog/DialogBeenden.kt | 5 ++--- src/main/java/mediathek/gui/dialog/DialogBeendenZeit.java | 5 ++--- src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java | 8 +++----- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/main/java/mediathek/gui/dialog/DialogBeenden.kt b/src/main/java/mediathek/gui/dialog/DialogBeenden.kt index aea762f0bc..12e0457eca 100644 --- a/src/main/java/mediathek/gui/dialog/DialogBeenden.kt +++ b/src/main/java/mediathek/gui/dialog/DialogBeenden.kt @@ -1,12 +1,11 @@ package mediathek.gui.dialog -import jiconfont.icons.font_awesome.FontAwesome -import jiconfont.swing.IconFontSwing import mediathek.config.Daten import mediathek.file.GetFile import mediathek.javafx.AppTerminationIndefiniteProgress import mediathek.mainwindow.MediathekGui import mediathek.tool.EscapeKeyHandler +import mediathek.tool.SVGIconUtilities import java.awt.BorderLayout import java.awt.event.WindowAdapter import java.awt.event.WindowEvent @@ -39,7 +38,7 @@ class DialogBeenden(parent: JFrame) : JDialog(parent, true) { private val btnContinue: JButton = JButton("Weiter") private val cbShutdownComputer: JCheckBox = JCheckBox("Rechner herunterfahren") private val btnCancel: JButton = JButton("Abbrechen") - private val jButtonHilfe: JButton = JButton(IconFontSwing.buildIcon(FontAwesome.QUESTION_CIRCLE_O, 16f)) + private val jButtonHilfe: JButton = JButton(SVGIconUtilities.createSVGIcon("icons/fontawesome/circle-question.svg")) /** * Return whether the user still wants to terminate the application. diff --git a/src/main/java/mediathek/gui/dialog/DialogBeendenZeit.java b/src/main/java/mediathek/gui/dialog/DialogBeendenZeit.java index dea62c0650..5ac8c4b4c4 100644 --- a/src/main/java/mediathek/gui/dialog/DialogBeendenZeit.java +++ b/src/main/java/mediathek/gui/dialog/DialogBeendenZeit.java @@ -1,12 +1,11 @@ package mediathek.gui.dialog; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.Daten; import mediathek.daten.DatenDownload; import mediathek.file.GetFile; import mediathek.javafx.AppTerminationIndefiniteProgress; import mediathek.tool.EscapeKeyHandler; +import mediathek.tool.SVGIconUtilities; import javax.swing.*; import java.awt.*; @@ -96,7 +95,7 @@ public void windowClosing(WindowEvent e) { comboActions.setModel(getComboBoxModel()); comboActions.addActionListener(e -> setCbShutdownCoputer()); - jButtonHilfe.setIcon(IconFontSwing.buildIcon(FontAwesome.QUESTION_CIRCLE_O, 16)); + jButtonHilfe.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/circle-question.svg")); jButtonHilfe.addActionListener(e -> new DialogHilfe(parent, true, new GetFile().getHilfeSuchen(GetFile.PFAD_HILFETEXT_BEENDEN)).setVisible(true)); setCbShutdownCoputer(); diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index 4721e6fe95..2cb1689848 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -11,8 +11,6 @@ import javafx.scene.control.ButtonBar; import javafx.scene.control.ButtonType; import javafx.util.Duration; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.Daten; import mediathek.config.Konstanten; import mediathek.config.MVConfig; @@ -991,21 +989,21 @@ private void buttonTable(int row, int column) { private JMenuItem createPlayItem() { JMenuItem item = new JMenuItem("Film abspielen"); - item.setIcon(IconFontSwing.buildIcon(FontAwesome.PLAY, 16)); + item.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/play.svg")); item.addActionListener(playAction); return item; } private JMenuItem createSaveFilmItem() { JMenuItem item = new JMenuItem("Film aufzeichnen"); - item.setIcon(IconFontSwing.buildIcon(FontAwesome.DOWNLOAD, 16)); + item.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/download.svg")); item.addActionListener(saveFilmAction); return item; } private JMenuItem createBookmarkFilmItem() { JMenuItem item = new JMenuItem("Film merken"); - item.setIcon(IconFontSwing.buildIcon(FontAwesome.BOOKMARK_O, 16)); + item.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/bookmark.svg")); item.addActionListener(bookmarkFilmAction); return item; } From daf881cd487f4d5d9b105da50f118be6626e71ab Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 10 Jun 2022 07:16:06 +0200 Subject: [PATCH 035/422] - remove unused code Former-commit-id: f55f0e9afc03ce8902f548bae663d25a80758c8f --- src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index 2cb1689848..10807e4322 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -85,7 +85,6 @@ public class GuiFilme extends AGuiTabPanel { public final PlayFilmAction playAction = new PlayFilmAction(); public final SaveFilmAction saveFilmAction = new SaveFilmAction(); public final BookmarkFilmAction bookmarkFilmAction = new BookmarkFilmAction(); - public final BookmarkManageListAction bookmarkManageListAction = new BookmarkManageListAction(); private final MarkFilmAsSeenAction markFilmAsSeenAction = new MarkFilmAsSeenAction(); private final MarkFilmAsUnseenAction markFilmAsUnseenAction = new MarkFilmAsUnseenAction(); private final JScrollPane filmListScrollPane = new JScrollPane(); @@ -866,13 +865,6 @@ public void actionPerformed(ActionEvent e) { } } - public class BookmarkManageListAction extends AbstractAction { - @Override - public void actionPerformed(ActionEvent ae) { - showBookmarkWindow(); - } - } - private class CopyUrlToClipboardAction extends AbstractAction { private final FilmResolution.Enum resolution; From 600f2d620931e2ac778593179cabddb7bc907a50 Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 10 Jun 2022 07:21:51 +0200 Subject: [PATCH 036/422] - convert to svg icon Former-commit-id: d602ebd192503dfca35335ed52790372f6ee1744 --- src/main/java/mediathek/gui/dialog/DialogEditAbo.java | 4 +--- src/main/java/mediathek/gui/dialog/DialogEditDownload.java | 3 ++- .../java/mediathek/gui/dialog/DialogFilmBeschreibung.java | 4 +--- src/main/java/mediathek/gui/dialog/DialogNewSet.java | 6 ++---- .../java/mediathek/gui/dialog/reset/ResetSettingsPanel.java | 5 ++--- .../mediathek/gui/dialogEinstellungen/PanelBlacklist.java | 2 +- .../gui/dialogEinstellungen/PanelEinstellungenGeo.java | 5 ++--- .../gui/dialogEinstellungen/PanelProgrammPfade.java | 3 ++- .../mediathek/gui/dialogEinstellungen/PanelPsetLang.java | 2 +- 9 files changed, 14 insertions(+), 20 deletions(-) diff --git a/src/main/java/mediathek/gui/dialog/DialogEditAbo.java b/src/main/java/mediathek/gui/dialog/DialogEditAbo.java index c471bb639a..7540dce72c 100644 --- a/src/main/java/mediathek/gui/dialog/DialogEditAbo.java +++ b/src/main/java/mediathek/gui/dialog/DialogEditAbo.java @@ -1,7 +1,5 @@ package mediathek.gui.dialog; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.Daten; import mediathek.config.MVColor; import mediathek.daten.abo.AboTags; @@ -91,7 +89,7 @@ public DialogEditAbo(final JFrame parent, DatenAbo aktA, boolean isMultiEditMode EscapeKeyHandler.installHandler(this, this::dispose); - jButtonHelp.setIcon(IconFontSwing.buildIcon(FontAwesome.QUESTION_CIRCLE_O, 16)); + jButtonHelp.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/circle-question.svg")); jButtonHelp.addActionListener(e -> new DialogHilfe(parent, true, new GetFile().getHilfeSuchen(GetFile.PFAD_HILFETEXT_DIALOG_ADD_ABO)).setVisible(true)); if (comboboxPSet.getModel().getSize() == 0) { diff --git a/src/main/java/mediathek/gui/dialog/DialogEditDownload.java b/src/main/java/mediathek/gui/dialog/DialogEditDownload.java index 1e75b247e5..efbb28cdad 100644 --- a/src/main/java/mediathek/gui/dialog/DialogEditDownload.java +++ b/src/main/java/mediathek/gui/dialog/DialogEditDownload.java @@ -10,6 +10,7 @@ import mediathek.file.GetFile; import mediathek.tool.EscapeKeyHandler; import mediathek.tool.MVMessageDialog; +import mediathek.tool.SVGIconUtilities; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -354,7 +355,7 @@ private void addExtraFeld(int i, GridBagLayout gridbag, GridBagConstraints c) { jButtonReset.setIcon(IconFontSwing.buildIcon(FontAwesome.REFRESH, 16)); jButtonReset.addActionListener(e -> textfeldListe[DatenDownload.DOWNLOAD_PROGRAMM_AUFRUF_ARRAY].setText(orgProgArray)); JButton jButtonHelp = new JButton(""); - jButtonHelp.setIcon(IconFontSwing.buildIcon(FontAwesome.QUESTION_CIRCLE_O, 16)); + jButtonHelp.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/circle-question.svg")); jButtonHelp.setToolTipText("Hilfe anzeigen"); jButtonHelp.addActionListener(e -> new DialogHilfe(parent, true, new GetFile().getHilfeSuchen(GetFile.PFAD_HILFETEXT_EDIT_DOWNLOAD_PROG)).setVisible(true)); diff --git a/src/main/java/mediathek/gui/dialog/DialogFilmBeschreibung.java b/src/main/java/mediathek/gui/dialog/DialogFilmBeschreibung.java index ca70fe9d75..0dc109750e 100644 --- a/src/main/java/mediathek/gui/dialog/DialogFilmBeschreibung.java +++ b/src/main/java/mediathek/gui/dialog/DialogFilmBeschreibung.java @@ -2,8 +2,6 @@ import javafx.application.Platform; import javafx.scene.control.Alert; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.Daten; import mediathek.config.Konstanten; import mediathek.config.MVConfig; @@ -51,7 +49,7 @@ public DialogFilmBeschreibung(JFrame parent, DatenFilm datenFilm) { dispose(); }); - jButtonHilfe.setIcon(IconFontSwing.buildIcon(FontAwesome.QUESTION_CIRCLE_O, 16)); + jButtonHilfe.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/circle-question.svg")); jButtonHilfe.addActionListener(e -> Platform.runLater(() -> { Alert alert = new Alert(Alert.AlertType.INFORMATION); alert.setHeaderText("Hilfe zu " + TITLE); diff --git a/src/main/java/mediathek/gui/dialog/DialogNewSet.java b/src/main/java/mediathek/gui/dialog/DialogNewSet.java index 7681a07532..73715ca058 100644 --- a/src/main/java/mediathek/gui/dialog/DialogNewSet.java +++ b/src/main/java/mediathek/gui/dialog/DialogNewSet.java @@ -1,16 +1,14 @@ package mediathek.gui.dialog; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.Daten; import mediathek.file.GetFile; import mediathek.tool.EscapeKeyHandler; +import mediathek.tool.SVGIconUtilities; import javax.swing.*; import javax.swing.border.TitledBorder; import java.awt.*; -@SuppressWarnings("serial") public class DialogNewSet extends JDialog { public boolean ok; public boolean morgen = true; @@ -64,7 +62,7 @@ Es werden alle Programmsets (auch eigene)\s beenden(); } }); - jButtonSetHelp.setIcon(IconFontSwing.buildIcon(FontAwesome.QUESTION_CIRCLE_O, 16)); + jButtonSetHelp.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/circle-question.svg")); jButtonSetHelp.addActionListener(e -> new DialogHilfe(parent, true, new GetFile().getHilfeSuchen(GetFile.PFAD_HILFETEXT_RESET_SET)).setVisible(true)); EscapeKeyHandler.installHandler(this, () -> { diff --git a/src/main/java/mediathek/gui/dialog/reset/ResetSettingsPanel.java b/src/main/java/mediathek/gui/dialog/reset/ResetSettingsPanel.java index 10fdbe458e..05a1e17728 100644 --- a/src/main/java/mediathek/gui/dialog/reset/ResetSettingsPanel.java +++ b/src/main/java/mediathek/gui/dialog/reset/ResetSettingsPanel.java @@ -1,7 +1,5 @@ package mediathek.gui.dialog.reset; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.Daten; import mediathek.daten.ListePsetVorlagen; import mediathek.file.GetFile; @@ -10,6 +8,7 @@ import mediathek.mainwindow.MediathekGui; import mediathek.tool.GuiFunktionenProgramme; import mediathek.tool.MessageBus; +import mediathek.tool.SVGIconUtilities; import mediathek.tool.swing.MultilineLabel; import org.jdesktop.swingx.VerticalLayout; @@ -25,7 +24,7 @@ public class ResetSettingsPanel extends JPanel { public ResetSettingsPanel(JFrame parent) { initComponents(); - jButtonHilfeReset.setIcon(IconFontSwing.buildIcon(FontAwesome.QUESTION_CIRCLE_O, 16)); + jButtonHilfeReset.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/circle-question.svg")); jButtonHilfeReset.addActionListener(e -> new DialogHilfe(parent, true, new GetFile().getHilfeSuchen(GetFile.PFAD_HILFETEXT_RESET)).setVisible(true)); jButtonResetSets.addActionListener(e -> { Daten.listePset.clear(); diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/PanelBlacklist.java b/src/main/java/mediathek/gui/dialogEinstellungen/PanelBlacklist.java index 803aafedba..9703e8d9c5 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/PanelBlacklist.java +++ b/src/main/java/mediathek/gui/dialogEinstellungen/PanelBlacklist.java @@ -49,7 +49,7 @@ public PanelBlacklist(Daten d, JFrame parentComponent, String nname) { initComponents(); name = nname; - jButtonHilfe.setIcon(IconFontSwing.buildIcon(FontAwesome.QUESTION_CIRCLE_O, 16)); + jButtonHilfe.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/circle-question.svg")); jButtonTabelleLoeschen.setIcon(IconFontSwing.buildIcon(FontAwesome.TRASH_O, 16)); jButtonAendern.setEnabled(jTableBlacklist.getSelectionModel().getSelectedItemsCount() == 1); diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/PanelEinstellungenGeo.java b/src/main/java/mediathek/gui/dialogEinstellungen/PanelEinstellungenGeo.java index 504df270d7..d8f8badbdc 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/PanelEinstellungenGeo.java +++ b/src/main/java/mediathek/gui/dialogEinstellungen/PanelEinstellungenGeo.java @@ -1,7 +1,5 @@ package mediathek.gui.dialogEinstellungen; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.Daten; import mediathek.daten.GeoblockingField; import mediathek.file.GetFile; @@ -10,6 +8,7 @@ import mediathek.gui.messages.GeoStateChangedEvent; import mediathek.tool.ApplicationConfiguration; import mediathek.tool.MessageBus; +import mediathek.tool.SVGIconUtilities; import org.apache.commons.configuration2.Configuration; import org.jdesktop.swingx.VerticalLayout; @@ -62,7 +61,7 @@ private void init() { config.setProperty(ApplicationConfiguration.GEO_REPORT, jCheckBoxMarkieren.isSelected()); filterBlacklistAndNotifyChanges(); }); - jButtonHilfe.setIcon(IconFontSwing.buildIcon(FontAwesome.QUESTION_CIRCLE_O, 16)); + jButtonHilfe.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/circle-question.svg")); jButtonHilfe.addActionListener(e -> new DialogHilfe(parentComponent, true, new GetFile().getHilfeSuchen(GetFile.PFAD_HILFETEXT_GEO)).setVisible(true)); } diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/PanelProgrammPfade.java b/src/main/java/mediathek/gui/dialogEinstellungen/PanelProgrammPfade.java index c6a153d187..e4cc06db9a 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/PanelProgrammPfade.java +++ b/src/main/java/mediathek/gui/dialogEinstellungen/PanelProgrammPfade.java @@ -6,6 +6,7 @@ import mediathek.file.GetFile; import mediathek.gui.dialog.DialogHilfe; import mediathek.tool.GuiFunktionenProgramme; +import mediathek.tool.SVGIconUtilities; import org.apache.commons.lang3.SystemUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -36,7 +37,7 @@ public PanelProgrammPfade(JFrame parentFrame, boolean vvlc, boolean fffmpeg) { private void init() { jButtonVlcPfad.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 16)); jButtonFFmpegPfad.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 16)); - jButtonHilfe.setIcon(IconFontSwing.buildIcon(FontAwesome.QUESTION_CIRCLE_O, 16)); + jButtonHilfe.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/circle-question.svg")); jPanelVlc.setVisible(vlc); jPanelFFmpeg.setVisible(ffmpeg); diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java b/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java index ce85373ae4..a8de52f09c 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java +++ b/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java @@ -74,7 +74,7 @@ private void handleProgramSetChanged(ProgramSetChangedEvent e) { } private void init() { - jButtonHilfe.setIcon(IconFontSwing.buildIcon(FontAwesome.QUESTION_CIRCLE_O, 16)); + jButtonHilfe.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/circle-question.svg")); jButtonGruppePfad.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 16)); jButtonProgPlus.setIcon(Icons.ICON_BUTTON_ADD); jButtonProgMinus.setIcon(Icons.ICON_BUTTON_REMOVE); From 20d0964821f6b69b2c0dac3531109511770dc996 Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 10 Jun 2022 07:25:57 +0200 Subject: [PATCH 037/422] - convert to svg icon Former-commit-id: 7e573233b1a104dcaf04fd0da95822f8823c9f11 --- src/main/java/mediathek/gui/dialog/DialogAddDownload.java | 2 +- .../java/mediathek/gui/dialog/DialogAddMoreDownload.java | 7 ++----- .../mediathek/gui/dialog/DialogProgrammOrdnerOeffnen.java | 5 ++--- .../java/mediathek/gui/dialog/MVPanelDownloadZiel.java | 7 ++----- .../dialogEinstellungen/PanelEinstellungenErweitert.java | 5 ++--- .../gui/dialogEinstellungen/PanelFilmlisteLaden.java | 4 +--- .../gui/dialogEinstellungen/PanelProgrammPfade.java | 6 ++---- .../mediathek/gui/dialogEinstellungen/PanelPsetImport.java | 3 ++- .../mediathek/gui/dialogEinstellungen/PanelPsetKurz.java | 7 +++---- .../mediathek/gui/dialogEinstellungen/PanelPsetLang.java | 4 ++-- 10 files changed, 19 insertions(+), 31 deletions(-) diff --git a/src/main/java/mediathek/gui/dialog/DialogAddDownload.java b/src/main/java/mediathek/gui/dialog/DialogAddDownload.java index 4d84cd8a2b..1e476d5263 100644 --- a/src/main/java/mediathek/gui/dialog/DialogAddDownload.java +++ b/src/main/java/mediathek/gui/dialog/DialogAddDownload.java @@ -303,7 +303,7 @@ private void tus() { } private void setupZielButton() { - jButtonZiel.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 16)); + jButtonZiel.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg")); jButtonZiel.setText(""); jButtonZiel.addActionListener(l -> { var initialDirectory = ""; diff --git a/src/main/java/mediathek/gui/dialog/DialogAddMoreDownload.java b/src/main/java/mediathek/gui/dialog/DialogAddMoreDownload.java index 312610e11c..6caa33942f 100644 --- a/src/main/java/mediathek/gui/dialog/DialogAddMoreDownload.java +++ b/src/main/java/mediathek/gui/dialog/DialogAddMoreDownload.java @@ -6,10 +6,7 @@ import mediathek.config.MVConfig; import mediathek.daten.DatenPset; import mediathek.mainwindow.MediathekGui; -import mediathek.tool.ApplicationConfiguration; -import mediathek.tool.EscapeKeyHandler; -import mediathek.tool.FileDialogs; -import mediathek.tool.FilenameUtils; +import mediathek.tool.*; import org.apache.commons.configuration2.Configuration; import org.jdesktop.swingx.VerticalLayout; @@ -61,7 +58,7 @@ public DialogAddMoreDownload(JFrame parent, DatenPset pSet) { beenden(); }); - jButtonPath.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 16)); + jButtonPath.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg")); jButtonPath.addActionListener(l -> { var initialDirectory = ""; var cbItem = Objects.requireNonNull(jComboBoxPath.getSelectedItem()).toString(); diff --git a/src/main/java/mediathek/gui/dialog/DialogProgrammOrdnerOeffnen.java b/src/main/java/mediathek/gui/dialog/DialogProgrammOrdnerOeffnen.java index f3a8bc6cfd..c7d2e351d6 100644 --- a/src/main/java/mediathek/gui/dialog/DialogProgrammOrdnerOeffnen.java +++ b/src/main/java/mediathek/gui/dialog/DialogProgrammOrdnerOeffnen.java @@ -1,9 +1,8 @@ package mediathek.gui.dialog; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.tool.EscapeKeyHandler; import mediathek.tool.MVMessageDialog; +import mediathek.tool.SVGIconUtilities; import org.apache.commons.lang3.SystemUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -24,7 +23,7 @@ public DialogProgrammOrdnerOeffnen(java.awt.Frame parent, boolean modal, String super(parent, modal); parentComponent = parent; initComponents(); - jButtonZiel.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 16)); + jButtonZiel.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg")); setTitle(titel); jTextArea1.setText(text); jButtonOk.addActionListener(l -> { diff --git a/src/main/java/mediathek/gui/dialog/MVPanelDownloadZiel.java b/src/main/java/mediathek/gui/dialog/MVPanelDownloadZiel.java index 46a682a436..df36aa842a 100644 --- a/src/main/java/mediathek/gui/dialog/MVPanelDownloadZiel.java +++ b/src/main/java/mediathek/gui/dialog/MVPanelDownloadZiel.java @@ -6,10 +6,7 @@ import mediathek.config.MVConfig; import mediathek.config.StandardLocations; import mediathek.daten.DatenDownload; -import mediathek.tool.FileSpecifier; -import mediathek.tool.FilenameUtils; -import mediathek.tool.GuiFunktionen; -import mediathek.tool.MVMessageDialog; +import mediathek.tool.*; import org.apache.commons.lang3.SystemUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -40,7 +37,7 @@ public MVPanelDownloadZiel(JFrame p, DatenDownload download, boolean letzterPfad parent = p; datenDownload = download; letztenPfadAnzeigen = letzterPfad; - jButtonPath.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 16)); + jButtonPath.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg")); jButtonDelPath.setIcon(IconFontSwing.buildIcon(FontAwesome.TRASH_O, 16)); jLabelExists.setText(""); jButtonPath.addActionListener(new ZielBeobachter()); diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/PanelEinstellungenErweitert.java b/src/main/java/mediathek/gui/dialogEinstellungen/PanelEinstellungenErweitert.java index 69c5637e28..1e3b8e9c27 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/PanelEinstellungenErweitert.java +++ b/src/main/java/mediathek/gui/dialogEinstellungen/PanelEinstellungenErweitert.java @@ -1,13 +1,12 @@ package mediathek.gui.dialogEinstellungen; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.Konstanten; import mediathek.config.MVConfig; import mediathek.gui.messages.ProgramLocationChangedEvent; import mediathek.mainwindow.MediathekGui; import mediathek.tool.MVMessageDialog; import mediathek.tool.MessageBus; +import mediathek.tool.SVGIconUtilities; import mediathek.tool.TextCopyPasteHandler; import net.engio.mbassy.listener.Handler; import net.miginfocom.layout.AC; @@ -94,7 +93,7 @@ private void init() { } private void setIcon() { - var icon = IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 16); + var icon = SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg"); jButtonProgrammDateimanager.setIcon(icon); jButtonProgrammVideoplayer.setIcon(icon); jButtonProgrammUrl.setIcon(icon); diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/PanelFilmlisteLaden.java b/src/main/java/mediathek/gui/dialogEinstellungen/PanelFilmlisteLaden.java index 9eda660652..74efcfd332 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/PanelFilmlisteLaden.java +++ b/src/main/java/mediathek/gui/dialogEinstellungen/PanelFilmlisteLaden.java @@ -1,7 +1,5 @@ package mediathek.gui.dialogEinstellungen; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.MVConfig; import mediathek.controller.SenderFilmlistLoadApprover; import mediathek.gui.messages.FilmListImportTypeChangedEvent; @@ -143,7 +141,7 @@ public boolean hasSenderSelectionChanged() { private void init() { initRadio(); - jButtonDateiAuswaehlen.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 16)); + jButtonDateiAuswaehlen.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg")); jButtonDateiAuswaehlen.addActionListener(l -> { var loadFile = FileDialogs.chooseLoadFileLocation(MediathekGui.ui(),"Filmliste laden", ""); if (loadFile != null) { diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/PanelProgrammPfade.java b/src/main/java/mediathek/gui/dialogEinstellungen/PanelProgrammPfade.java index e4cc06db9a..3f6c5bb66a 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/PanelProgrammPfade.java +++ b/src/main/java/mediathek/gui/dialogEinstellungen/PanelProgrammPfade.java @@ -1,7 +1,5 @@ package mediathek.gui.dialogEinstellungen; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.MVConfig; import mediathek.file.GetFile; import mediathek.gui.dialog.DialogHilfe; @@ -35,8 +33,8 @@ public PanelProgrammPfade(JFrame parentFrame, boolean vvlc, boolean fffmpeg) { } private void init() { - jButtonVlcPfad.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 16)); - jButtonFFmpegPfad.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 16)); + jButtonVlcPfad.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg")); + jButtonFFmpegPfad.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg")); jButtonHilfe.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/circle-question.svg")); jPanelVlc.setVisible(vlc); diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetImport.java b/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetImport.java index 008fca9f42..ba56ef273c 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetImport.java +++ b/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetImport.java @@ -9,6 +9,7 @@ import mediathek.gui.PanelVorlage; import mediathek.mainwindow.MediathekGui; import mediathek.tool.GuiFunktionenProgramme; +import mediathek.tool.SVGIconUtilities; import mediathek.tool.TextCopyPasteHandler; import mediathek.tool.models.TModel; import org.apache.commons.lang3.SystemUtils; @@ -37,7 +38,7 @@ public PanelPsetImport(Daten d, JFrame parentComponent) { private void init() { jButtonAktualisieren.setIcon(IconFontSwing.buildIcon(FontAwesome.REFRESH, 16)); - jButtonPfad.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 16)); + jButtonPfad.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg")); jComboBoxBs.setModel(new DefaultComboBoxModel<>(ListePsetVorlagen.BS)); jComboBoxBs.addActionListener(e -> tabelleLaden()); jButtonImportDatei.setEnabled(false); diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetKurz.java b/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetKurz.java index 53d4d727ae..1e38d85ce8 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetKurz.java +++ b/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetKurz.java @@ -1,7 +1,5 @@ package mediathek.gui.dialogEinstellungen; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.Daten; import mediathek.daten.DatenProg; import mediathek.daten.DatenPset; @@ -10,6 +8,7 @@ import mediathek.gui.messages.ProgramSetChangedEvent; import mediathek.mainwindow.MediathekGui; import mediathek.tool.MessageBus; +import mediathek.tool.SVGIconUtilities; import mediathek.tool.TextCopyPasteHandler; import net.engio.mbassy.listener.Handler; import org.apache.commons.lang3.SystemUtils; @@ -34,7 +33,7 @@ public class PanelPsetKurz extends PanelVorlage { public PanelPsetKurz(Daten d, JFrame parentComponent, ListePset llistePset) { super(d, parentComponent); initComponents(); - jButtonZiel.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 16)); + jButtonZiel.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg")); listePset = llistePset; jListPset.setModel(new DefaultComboBoxModel<>(listePset.getObjectDataCombo())); if (!listePset.isEmpty()) { @@ -176,7 +175,7 @@ private void setFeld(JPanel panel, String name, String[] arr) { c.gridx = 2; c.weightx = 0; JButton button = new JButton(); - button.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 16)); + button.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg")); button.addActionListener(new ZielBeobachter(textField, arr, DatenProg.PROGRAMM_PROGRAMMPFAD)); button.setToolTipText("Programm auswählen"); gridbag.setConstraints(button, c); diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java b/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java index a8de52f09c..e3f02fbe40 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java +++ b/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java @@ -75,12 +75,12 @@ private void handleProgramSetChanged(ProgramSetChangedEvent e) { private void init() { jButtonHilfe.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/circle-question.svg")); - jButtonGruppePfad.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 16)); + jButtonGruppePfad.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg")); jButtonProgPlus.setIcon(Icons.ICON_BUTTON_ADD); jButtonProgMinus.setIcon(Icons.ICON_BUTTON_REMOVE); jButtonProgAuf.setIcon(Icons.ICON_BUTTON_MOVE_UP); jButtonProgAb.setIcon(Icons.ICON_BUTTON_MOVE_DOWN); - jButtonProgPfad.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 16)); + jButtonProgPfad.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg")); jButtonGruppeNeu.setIcon(Icons.ICON_BUTTON_ADD); jButtonGruppeLoeschen.setIcon(Icons.ICON_BUTTON_REMOVE); jButtonGruppeAuf.setIcon(Icons.ICON_BUTTON_MOVE_UP); From 95e088858a743e3f79b4d856cde48c384af4c2f7 Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 10 Jun 2022 07:30:47 +0200 Subject: [PATCH 038/422] - convert to svg icon Former-commit-id: 3d78eea0dcfcda7ccaa35992daad5a846b77a63d --- src/main/java/mediathek/gui/dialog/DialogAddDownload.java | 4 +--- src/main/java/mediathek/gui/dialog/DialogAddMoreDownload.java | 4 +--- src/main/java/mediathek/gui/dialog/MVPanelDownloadZiel.java | 4 +--- .../mediathek/gui/dialogEinstellungen/PanelBlacklist.java | 4 +--- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/main/java/mediathek/gui/dialog/DialogAddDownload.java b/src/main/java/mediathek/gui/dialog/DialogAddDownload.java index 1e476d5263..f7b09d4d7a 100644 --- a/src/main/java/mediathek/gui/dialog/DialogAddDownload.java +++ b/src/main/java/mediathek/gui/dialog/DialogAddDownload.java @@ -3,8 +3,6 @@ import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.Daten; import mediathek.config.Konstanten; import mediathek.config.MVColor; @@ -321,7 +319,7 @@ private void setupZielButton() { } private void setupDeleteHistoryButton() { - jButtonDelHistory.setIcon(IconFontSwing.buildIcon(FontAwesome.TRASH_O, 16)); + jButtonDelHistory.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/trash-can.svg")); jButtonDelHistory.addActionListener(e -> { MVConfig.add(MVConfig.Configs.SYSTEM_DIALOG_DOWNLOAD__PFADE_ZUM_SPEICHERN, ""); jComboBoxPfad.setModel(new DefaultComboBoxModel<>(new String[]{orgPfad})); diff --git a/src/main/java/mediathek/gui/dialog/DialogAddMoreDownload.java b/src/main/java/mediathek/gui/dialog/DialogAddMoreDownload.java index 6caa33942f..7313487c28 100644 --- a/src/main/java/mediathek/gui/dialog/DialogAddMoreDownload.java +++ b/src/main/java/mediathek/gui/dialog/DialogAddMoreDownload.java @@ -1,7 +1,5 @@ package mediathek.gui.dialog; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.MVColor; import mediathek.config.MVConfig; import mediathek.daten.DatenPset; @@ -74,7 +72,7 @@ public DialogAddMoreDownload(JFrame parent, DatenPset pSet) { }); - jButtonDelPath.setIcon(IconFontSwing.buildIcon(FontAwesome.TRASH_O, 16)); + jButtonDelPath.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/trash-can.svg")); jButtonDelPath.addActionListener(e -> { MVConfig.add(MVConfig.Configs.SYSTEM_DIALOG_DOWNLOAD__PFADE_ZUM_SPEICHERN, ""); jComboBoxPath.setModel(new DefaultComboBoxModel<>(new String[]{pSet.getZielPfad()})); diff --git a/src/main/java/mediathek/gui/dialog/MVPanelDownloadZiel.java b/src/main/java/mediathek/gui/dialog/MVPanelDownloadZiel.java index df36aa842a..72eae029d5 100644 --- a/src/main/java/mediathek/gui/dialog/MVPanelDownloadZiel.java +++ b/src/main/java/mediathek/gui/dialog/MVPanelDownloadZiel.java @@ -1,7 +1,5 @@ package mediathek.gui.dialog; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.MVColor; import mediathek.config.MVConfig; import mediathek.config.StandardLocations; @@ -38,7 +36,7 @@ public MVPanelDownloadZiel(JFrame p, DatenDownload download, boolean letzterPfad datenDownload = download; letztenPfadAnzeigen = letzterPfad; jButtonPath.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg")); - jButtonDelPath.setIcon(IconFontSwing.buildIcon(FontAwesome.TRASH_O, 16)); + jButtonDelPath.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/trash-can.svg")); jLabelExists.setText(""); jButtonPath.addActionListener(new ZielBeobachter()); jButtonDelPath.addActionListener(e -> { diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/PanelBlacklist.java b/src/main/java/mediathek/gui/dialogEinstellungen/PanelBlacklist.java index 9703e8d9c5..3a109c5e6b 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/PanelBlacklist.java +++ b/src/main/java/mediathek/gui/dialogEinstellungen/PanelBlacklist.java @@ -1,7 +1,5 @@ package mediathek.gui.dialogEinstellungen; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.Daten; import mediathek.config.Konstanten; import mediathek.config.MVConfig; @@ -50,7 +48,7 @@ public PanelBlacklist(Daten d, JFrame parentComponent, String nname) { initComponents(); name = nname; jButtonHilfe.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/circle-question.svg")); - jButtonTabelleLoeschen.setIcon(IconFontSwing.buildIcon(FontAwesome.TRASH_O, 16)); + jButtonTabelleLoeschen.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/trash-can.svg")); jButtonAendern.setEnabled(jTableBlacklist.getSelectionModel().getSelectedItemsCount() == 1); From 5b424a28eded949aae1e90b05b7c72ea8d04cac8 Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 10 Jun 2022 07:50:41 +0200 Subject: [PATCH 039/422] - replace filmlist icons to svg Former-commit-id: f5eec9c89bfc5898d4ba8963797055a1d423f6da --- .../cellrenderer/CellRendererDownloads.java | 47 +++++++++++-------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/src/main/java/mediathek/tool/cellrenderer/CellRendererDownloads.java b/src/main/java/mediathek/tool/cellrenderer/CellRendererDownloads.java index 2b27e6db6d..2b268ce8d5 100644 --- a/src/main/java/mediathek/tool/cellrenderer/CellRendererDownloads.java +++ b/src/main/java/mediathek/tool/cellrenderer/CellRendererDownloads.java @@ -1,10 +1,10 @@ package mediathek.tool.cellrenderer; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; +import com.formdev.flatlaf.extras.FlatSVGIcon; import mediathek.config.MVColor; import mediathek.controller.starter.Start; import mediathek.daten.DatenDownload; +import mediathek.tool.SVGIconUtilities; import mediathek.tool.table.MVTable; import org.apache.commons.lang3.SystemUtils; import org.apache.logging.log4j.LogManager; @@ -22,34 +22,41 @@ public class CellRendererDownloads extends CellRendererBaseWithStart { private final static String DOWNLOAD_ENTFERNEN = "Download entfernen"; private final static String PLAY_DOWNLOADED_FILM = "gespeicherten Film abspielen"; private static final Logger logger = LogManager.getLogger(CellRendererDownloads.class); - private final Icon film_start_tab; + private final FlatSVGIcon film_start_tab; private final Icon film_start_sw_tab; private final Border emptyBorder = BorderFactory.createEmptyBorder(); private final Border largeBorder = BorderFactory.createEmptyBorder(9, 2, 9, 2); private final JPanel panel; - private final Icon download_stop_tab; - private final Icon download_stop_sw_tab; - private final Icon download_start_tab; + private final FlatSVGIcon download_stop_tab; + private final FlatSVGIcon download_stop_sw_tab; + private final FlatSVGIcon download_start_tab; private final Icon download_start_sw_tab; - private final Icon download_clear_tab_selected; + private final FlatSVGIcon download_clear_tab_selected; private final Icon download_clear_sw_tab; - private final Icon download_del_tab_selected; + private final FlatSVGIcon download_del_tab_selected; private final Icon download_del_sw_tab; private JProgressBar progressBar; public CellRendererDownloads() { - download_stop_tab = IconFontSwing.buildIcon(FontAwesome.STOP, 16, Color.WHITE); - download_stop_sw_tab = IconFontSwing.buildIcon(FontAwesome.STOP, 16); - download_start_tab = IconFontSwing.buildIcon(FontAwesome.CARET_DOWN, 16, Color.WHITE); - download_start_sw_tab = IconFontSwing.buildIcon(FontAwesome.CARET_DOWN, 16); - download_clear_tab_selected = IconFontSwing.buildIcon(FontAwesome.ERASER, 16, Color.WHITE); - download_clear_sw_tab = IconFontSwing.buildIcon(FontAwesome.ERASER, 16); - - download_del_tab_selected = IconFontSwing.buildIcon(FontAwesome.TRASH, 16, Color.WHITE); - download_del_sw_tab = IconFontSwing.buildIcon(FontAwesome.TRASH, 16); - - film_start_tab = IconFontSwing.buildIcon(FontAwesome.PLAY, 16, Color.WHITE); - film_start_sw_tab = IconFontSwing.buildIcon(FontAwesome.PLAY, 16); + var whiteColorFilter = new FlatSVGIcon.ColorFilter(color -> Color.WHITE); + + download_stop_tab = SVGIconUtilities.createSVGIcon("icons/fontawesome/stop.svg"); + download_stop_tab.setColorFilter(whiteColorFilter); + download_stop_sw_tab = SVGIconUtilities.createSVGIcon("icons/fontawesome/stop.svg"); + download_start_tab = SVGIconUtilities.createSVGIcon("icons/fontawesome/caret-down.svg"); + download_start_tab.setColorFilter(whiteColorFilter); + download_start_sw_tab = SVGIconUtilities.createSVGIcon("icons/fontawesome/caret-down.svg"); + download_clear_tab_selected = SVGIconUtilities.createSVGIcon("icons/fontawesome/eraser.svg"); + download_clear_tab_selected.setColorFilter(whiteColorFilter); + download_clear_sw_tab = SVGIconUtilities.createSVGIcon("icons/fontawesome/eraser.svg"); + + download_del_tab_selected = SVGIconUtilities.createSVGIcon("icons/fontawesome/trash-can.svg"); + download_del_tab_selected.setColorFilter(whiteColorFilter); + download_del_sw_tab = SVGIconUtilities.createSVGIcon("icons/fontawesome/trash-can.svg"); + + film_start_tab = SVGIconUtilities.createSVGIcon("icons/fontawesome/play.svg"); + film_start_tab.setColorFilter(whiteColorFilter); + film_start_sw_tab = SVGIconUtilities.createSVGIcon("icons/fontawesome/play.svg"); setupProgressBar(); From 03f7e22b1cbdb3da55a763ac6cdbfd58336d41d7 Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 10 Jun 2022 07:55:45 +0200 Subject: [PATCH 040/422] - convert to svg icon Former-commit-id: 050bb2d50c51b8ccc021cabe66cc347b0dc28026 --- src/main/java/mediathek/gui/dialog/DialogEditDownload.java | 4 +--- .../mediathek/gui/dialogEinstellungen/PanelPsetImport.java | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/main/java/mediathek/gui/dialog/DialogEditDownload.java b/src/main/java/mediathek/gui/dialog/DialogEditDownload.java index efbb28cdad..fe19e8ffb6 100644 --- a/src/main/java/mediathek/gui/dialog/DialogEditDownload.java +++ b/src/main/java/mediathek/gui/dialog/DialogEditDownload.java @@ -1,7 +1,5 @@ package mediathek.gui.dialog; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.Icons; import mediathek.controller.starter.Start; import mediathek.daten.DatenDownload; @@ -352,7 +350,7 @@ private void addExtraFeld(int i, GridBagLayout gridbag, GridBagConstraints c) { JButton jButtonReset = new JButton(""); jButtonReset.setToolTipText("Reset"); - jButtonReset.setIcon(IconFontSwing.buildIcon(FontAwesome.REFRESH, 16)); + jButtonReset.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/arrows-rotate.svg")); jButtonReset.addActionListener(e -> textfeldListe[DatenDownload.DOWNLOAD_PROGRAMM_AUFRUF_ARRAY].setText(orgProgArray)); JButton jButtonHelp = new JButton(""); jButtonHelp.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/circle-question.svg")); diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetImport.java b/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetImport.java index ba56ef273c..37e8a9e818 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetImport.java +++ b/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetImport.java @@ -1,7 +1,5 @@ package mediathek.gui.dialogEinstellungen; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.Config; import mediathek.config.Daten; import mediathek.daten.ListePset; @@ -37,7 +35,7 @@ public PanelPsetImport(Daten d, JFrame parentComponent) { } private void init() { - jButtonAktualisieren.setIcon(IconFontSwing.buildIcon(FontAwesome.REFRESH, 16)); + jButtonAktualisieren.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/arrows-rotate.svg")); jButtonPfad.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg")); jComboBoxBs.setModel(new DefaultComboBoxModel<>(ListePsetVorlagen.BS)); jComboBoxBs.addActionListener(e -> tabelleLaden()); From 55dab32fc011fa0d09544a770a6d9dd2b5c2f916 Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 10 Jun 2022 08:09:03 +0200 Subject: [PATCH 041/422] - convert to svg icon Former-commit-id: ad1a2a80a3666816e2be1895a21945f581338586 --- src/main/java/mediathek/gui/abo/ManageAboPanel.java | 11 +++++------ src/main/resources/icons/fontawesome/check.svg | 1 + src/main/resources/icons/fontawesome/minus.svg | 1 + 3 files changed, 7 insertions(+), 6 deletions(-) create mode 100755 src/main/resources/icons/fontawesome/check.svg create mode 100755 src/main/resources/icons/fontawesome/minus.svg diff --git a/src/main/java/mediathek/gui/abo/ManageAboPanel.java b/src/main/java/mediathek/gui/abo/ManageAboPanel.java index d3595f73cd..70391f82b1 100644 --- a/src/main/java/mediathek/gui/abo/ManageAboPanel.java +++ b/src/main/java/mediathek/gui/abo/ManageAboPanel.java @@ -4,8 +4,6 @@ import javafx.fxml.FXMLLoader; import javafx.scene.Scene; import javafx.scene.layout.HBox; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.Daten; import mediathek.daten.abo.AboTags; import mediathek.daten.abo.DatenAbo; @@ -16,6 +14,7 @@ import mediathek.mainwindow.MediathekGui; import mediathek.tool.MessageBus; import mediathek.tool.NoSelectionErrorDialog; +import mediathek.tool.SVGIconUtilities; import mediathek.tool.cellrenderer.CellRendererAbo; import mediathek.tool.listener.BeobTableHeader; import mediathek.tool.models.TModelAbo; @@ -165,19 +164,19 @@ public void actionPerformed(ActionEvent e) { private JPopupMenu createContextMenu() { JMenuItem itemEinschalten = new JMenuItem("Abo einschalten"); - itemEinschalten.setIcon(IconFontSwing.buildIcon(FontAwesome.CHECK, 16)); + itemEinschalten.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/check.svg")); itemEinschalten.addActionListener(e -> changeAboActiveState(true)); JMenuItem itemDeaktivieren = new JMenuItem("Abo ausschalten"); - itemDeaktivieren.setIcon(IconFontSwing.buildIcon(FontAwesome.TIMES, 16)); + itemDeaktivieren.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/xmark.svg")); itemDeaktivieren.addActionListener(e -> changeAboActiveState(false)); JMenuItem itemLoeschen = new JMenuItem("Abo löschen"); - itemLoeschen.setIcon(IconFontSwing.buildIcon(FontAwesome.MINUS, 16)); + itemLoeschen.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/minus.svg")); itemLoeschen.addActionListener(e -> aboLoeschen()); JMenuItem itemAendern = new JMenuItem("Abo ändern"); - itemAendern.setIcon(IconFontSwing.buildIcon(FontAwesome.PENCIL_SQUARE_O, 16)); + itemAendern.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/pen-to-square.svg")); itemAendern.addActionListener(e -> editAbo()); JMenuItem itemNeu = new JMenuItem(); diff --git a/src/main/resources/icons/fontawesome/check.svg b/src/main/resources/icons/fontawesome/check.svg new file mode 100755 index 0000000000..008fef6698 --- /dev/null +++ b/src/main/resources/icons/fontawesome/check.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/resources/icons/fontawesome/minus.svg b/src/main/resources/icons/fontawesome/minus.svg new file mode 100755 index 0000000000..72c012e421 --- /dev/null +++ b/src/main/resources/icons/fontawesome/minus.svg @@ -0,0 +1 @@ + \ No newline at end of file From 2981b9239a80ab0cf3adfdd78a2989b2b815b34f Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 10 Jun 2022 08:18:22 +0200 Subject: [PATCH 042/422] - convert to svg icon Former-commit-id: 731fe0f2a80575c0441b56b62f6f05868437ad44 --- .../java/mediathek/gui/dialog/MeldungDownloadfehler.java | 1 - .../mediathek/gui/dialogEinstellungen/PanelPsetLang.java | 4 +--- src/main/java/mediathek/tool/SVGIconUtilities.java | 9 +++++++-- .../java/mediathek/tool/listener/BeobTableHeader.java | 7 +++---- .../resources/icons/fontawesome/triangle-exclamation.svg | 1 + 5 files changed, 12 insertions(+), 10 deletions(-) create mode 100755 src/main/resources/icons/fontawesome/triangle-exclamation.svg diff --git a/src/main/java/mediathek/gui/dialog/MeldungDownloadfehler.java b/src/main/java/mediathek/gui/dialog/MeldungDownloadfehler.java index 96da4fa708..be975a0e5a 100644 --- a/src/main/java/mediathek/gui/dialog/MeldungDownloadfehler.java +++ b/src/main/java/mediathek/gui/dialog/MeldungDownloadfehler.java @@ -12,7 +12,6 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -@SuppressWarnings("serial") public class MeldungDownloadfehler extends JDialog { private final Timer countdownTimer; diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java b/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java index e3f02fbe40..74f7a0e11e 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java +++ b/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java @@ -1,8 +1,6 @@ package mediathek.gui.dialogEinstellungen; import javafx.scene.control.Alert; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.*; import mediathek.controller.IoXmlSchreiben; import mediathek.controller.starter.RuntimeExec; @@ -86,7 +84,7 @@ private void init() { jButtonGruppeAuf.setIcon(Icons.ICON_BUTTON_MOVE_UP); jButtonGruppeAb.setIcon(Icons.ICON_BUTTON_MOVE_DOWN); - var exclamationIcon = IconFontSwing.buildIcon(FontAwesome.EXCLAMATION_TRIANGLE, 16); + var exclamationIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/triangle-exclamation.svg"); jLabelMeldungAbspielen.setIcon(exclamationIcon); jLabelMeldungSeichern.setIcon(exclamationIcon); diff --git a/src/main/java/mediathek/tool/SVGIconUtilities.java b/src/main/java/mediathek/tool/SVGIconUtilities.java index 19951ec13b..9796fe2a91 100644 --- a/src/main/java/mediathek/tool/SVGIconUtilities.java +++ b/src/main/java/mediathek/tool/SVGIconUtilities.java @@ -1,11 +1,16 @@ package mediathek.tool; import com.formdev.flatlaf.extras.FlatSVGIcon; +import org.jetbrains.annotations.NotNull; public class SVGIconUtilities { - public static FlatSVGIcon createSVGIcon(String resource) { + public static FlatSVGIcon createSVGIcon(@NotNull String resource) { + return createSVGIcon(resource, 15f); + } + + public static FlatSVGIcon createSVGIcon(@NotNull String resource, float height) { FlatSVGIcon icon = new FlatSVGIcon(resource); - float scaleFactor = (1f / icon.getIconHeight()) * 15f; + float scaleFactor = (1f / icon.getIconHeight()) * height; return icon.derive(scaleFactor); } } diff --git a/src/main/java/mediathek/tool/listener/BeobTableHeader.java b/src/main/java/mediathek/tool/listener/BeobTableHeader.java index daa322f9c1..7b91010a8e 100644 --- a/src/main/java/mediathek/tool/listener/BeobTableHeader.java +++ b/src/main/java/mediathek/tool/listener/BeobTableHeader.java @@ -1,8 +1,7 @@ package mediathek.tool.listener; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.MVConfig; +import mediathek.tool.SVGIconUtilities; import mediathek.tool.table.MVTable; import org.jetbrains.annotations.NotNull; @@ -76,7 +75,7 @@ private void createStaticMenuEntries() { miResetColumns.addActionListener(e -> tabelle.resetTabelle()); miDecreaseFont = new JMenuItem("Schrift verkleinern"); - miDecreaseFont.setIcon(IconFontSwing.buildIcon(FontAwesome.MINUS, 16)); + miDecreaseFont.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/minus.svg")); miDecreaseFont.addActionListener(e -> { var oldFont = tabelle.getDefaultFont(); final var oldSize = oldFont.getSize2D(); @@ -86,7 +85,7 @@ private void createStaticMenuEntries() { }); miIncreaseFont = new JMenuItem("Schrift vergrößern"); - miIncreaseFont.setIcon(IconFontSwing.buildIcon(FontAwesome.PLUS, 16)); + miIncreaseFont.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/plus.svg")); miIncreaseFont.addActionListener(e -> { var oldFont = tabelle.getDefaultFont(); final var oldSize = oldFont.getSize2D(); diff --git a/src/main/resources/icons/fontawesome/triangle-exclamation.svg b/src/main/resources/icons/fontawesome/triangle-exclamation.svg new file mode 100755 index 0000000000..d65b256abe --- /dev/null +++ b/src/main/resources/icons/fontawesome/triangle-exclamation.svg @@ -0,0 +1 @@ + \ No newline at end of file From 8292a34631d061f6f2d6abf30b31fce04691e9f4 Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 10 Jun 2022 08:22:20 +0200 Subject: [PATCH 043/422] - convert to svg icon Former-commit-id: 17939c14d5daa4a18e9ae4bb3c753545729e04e7 --- .../mediathek/gui/dialog/MeldungDownloadfehler.java | 5 ++--- .../gui/dialogEinstellungen/PanelDateinamen.java | 11 ++--------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/main/java/mediathek/gui/dialog/MeldungDownloadfehler.java b/src/main/java/mediathek/gui/dialog/MeldungDownloadfehler.java index be975a0e5a..f315893b3b 100644 --- a/src/main/java/mediathek/gui/dialog/MeldungDownloadfehler.java +++ b/src/main/java/mediathek/gui/dialog/MeldungDownloadfehler.java @@ -1,11 +1,10 @@ package mediathek.gui.dialog; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.Konstanten; import mediathek.config.MVConfig; import mediathek.daten.DatenDownload; import mediathek.tool.EscapeKeyHandler; +import mediathek.tool.SVGIconUtilities; import javax.swing.*; import java.awt.*; @@ -32,7 +31,7 @@ public MeldungDownloadfehler(Frame parent, String text, DatenDownload datenDownl jTextFieldTitel.setText(datenDownload.arr[DatenDownload.DOWNLOAD_TITEL]); jButtonOk.addActionListener(e -> dispose()); jLabelIcon.setText(""); - jLabelIcon.setIcon(IconFontSwing.buildIcon(FontAwesome.EXCLAMATION_TRIANGLE, 32)); + jLabelIcon.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/triangle-exclamation.svg", 32f)); //start the countdown... countdownTimer = new Timer(0, new CountdownAction()); diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/PanelDateinamen.java b/src/main/java/mediathek/gui/dialogEinstellungen/PanelDateinamen.java index bda855baab..5378323347 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/PanelDateinamen.java +++ b/src/main/java/mediathek/gui/dialogEinstellungen/PanelDateinamen.java @@ -1,16 +1,11 @@ package mediathek.gui.dialogEinstellungen; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.Daten; import mediathek.config.Icons; import mediathek.config.MVConfig; import mediathek.gui.PanelVorlage; import mediathek.gui.messages.ReplaceListChangedEvent; -import mediathek.tool.MessageBus; -import mediathek.tool.NoSelectionErrorDialog; -import mediathek.tool.ReplaceList; -import mediathek.tool.TextCopyPasteHandler; +import mediathek.tool.*; import mediathek.tool.models.NonEditableTableModel; import net.engio.mbassy.listener.Handler; @@ -24,10 +19,8 @@ import javax.swing.table.DefaultTableModel; import java.awt.*; -@SuppressWarnings("serial") public class PanelDateinamen extends PanelVorlage { public boolean ok; - public String ziel = ""; @Handler private void handleReplaceListChange(ReplaceListChangedEvent e) { @@ -44,7 +37,7 @@ public PanelDateinamen(Daten d, JFrame pparentComponent) { jLabelAlert.setVisible(false); jLabelAlert.setText(""); - jLabelAlert.setIcon(IconFontSwing.buildIcon(FontAwesome.EXCLAMATION_TRIANGLE, 32)); + jLabelAlert.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/triangle-exclamation.svg", 32f)); jButtonPlus.setIcon(Icons.ICON_BUTTON_ADD); jButtonMinus.setIcon(Icons.ICON_BUTTON_REMOVE); jButtonUp.setIcon(Icons.ICON_BUTTON_MOVE_UP); From ff56c43cf837bbafcd8e063e5e89c95f9452a0f3 Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 10 Jun 2022 08:25:38 +0200 Subject: [PATCH 044/422] - remove IconFontSwing dependency Former-commit-id: 3c3da47cb231dd2baf64c35d50863e7fef9c27f3 --- pom.xml | 5 ----- src/main/java/mediathek/Main.java | 5 ----- 2 files changed, 10 deletions(-) diff --git a/pom.xml b/pom.xml index 87c5ee2bf0..26d05f17c8 100755 --- a/pom.xml +++ b/pom.xml @@ -333,11 +333,6 @@ - - com.github.jiconfont - jiconfont-swing - 1.0.1 - com.github.jiconfont jiconfont-javafx diff --git a/src/main/java/mediathek/Main.java b/src/main/java/mediathek/Main.java index 4f08d2e715..bdb3f3f4f2 100644 --- a/src/main/java/mediathek/Main.java +++ b/src/main/java/mediathek/Main.java @@ -5,8 +5,6 @@ import com.sun.jna.platform.win32.VersionHelpers; import javafx.application.Platform; import javafx.embed.swing.JFXPanel; -import jiconfont.icons.font_awesome.FontAwesome; -import jiconfont.swing.IconFontSwing; import mediathek.config.*; import mediathek.controller.history.SeenHistoryMigrator; import mediathek.gui.dialog.DialogStarteinstellungen; @@ -368,9 +366,6 @@ public static void main(final String... args) { migrateOldConfigSettings(); - //register FontAwesome font here as it may already be used at first start dialog.. - IconFontSwing.register(FontAwesome.getIconFont()); - loadConfigurationData(); migrateSeenHistory(); From f01f425a8dee52431fd0c0c4f4e1713a671c1922 Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 10 Jun 2022 08:29:10 +0200 Subject: [PATCH 045/422] - remove IconFontSwing dependency Former-commit-id: e4144dbca850cde0fb1eb8f7e13ac2e627a27418 --- src/main/java/mediathek/config/Icons.java | 1 - .../mediathek/gui/dialogEinstellungen/PanelDateinamen.java | 2 +- .../java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/mediathek/config/Icons.java b/src/main/java/mediathek/config/Icons.java index b8bfa5be99..96099dbafa 100644 --- a/src/main/java/mediathek/config/Icons.java +++ b/src/main/java/mediathek/config/Icons.java @@ -31,7 +31,6 @@ public class Icons { public static final ImageIcon ICON_TABELLE_AUS = GetIcon.getProgramIcon("tabelle-aus.png", 5, 5); public static final ImageIcon ICON_BUTTON_REMOVE = GetIcon.getProgramIcon("button-remove.png", 16, 16); - public static final ImageIcon ICON_BUTTON_ADD = GetIcon.getProgramIcon("button-add.png", 16, 16); public static final ImageIcon ICON_BUTTON_MOVE_DOWN = GetIcon.getProgramIcon("button-move-down.png", 16, 16); public static final ImageIcon ICON_BUTTON_MOVE_UP = GetIcon.getProgramIcon("button-move-up.png", 16, 16); diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/PanelDateinamen.java b/src/main/java/mediathek/gui/dialogEinstellungen/PanelDateinamen.java index 5378323347..2662d8177f 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/PanelDateinamen.java +++ b/src/main/java/mediathek/gui/dialogEinstellungen/PanelDateinamen.java @@ -38,7 +38,7 @@ public PanelDateinamen(Daten d, JFrame pparentComponent) { jLabelAlert.setVisible(false); jLabelAlert.setText(""); jLabelAlert.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/triangle-exclamation.svg", 32f)); - jButtonPlus.setIcon(Icons.ICON_BUTTON_ADD); + jButtonPlus.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/plus.svg")); jButtonMinus.setIcon(Icons.ICON_BUTTON_REMOVE); jButtonUp.setIcon(Icons.ICON_BUTTON_MOVE_UP); jButtonDown.setIcon(Icons.ICON_BUTTON_MOVE_DOWN); diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java b/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java index 74f7a0e11e..4b631946c0 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java +++ b/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java @@ -74,12 +74,12 @@ private void handleProgramSetChanged(ProgramSetChangedEvent e) { private void init() { jButtonHilfe.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/circle-question.svg")); jButtonGruppePfad.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg")); - jButtonProgPlus.setIcon(Icons.ICON_BUTTON_ADD); + jButtonProgPlus.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/plus.svg")); jButtonProgMinus.setIcon(Icons.ICON_BUTTON_REMOVE); jButtonProgAuf.setIcon(Icons.ICON_BUTTON_MOVE_UP); jButtonProgAb.setIcon(Icons.ICON_BUTTON_MOVE_DOWN); jButtonProgPfad.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg")); - jButtonGruppeNeu.setIcon(Icons.ICON_BUTTON_ADD); + jButtonGruppeNeu.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/plus.svg")); jButtonGruppeLoeschen.setIcon(Icons.ICON_BUTTON_REMOVE); jButtonGruppeAuf.setIcon(Icons.ICON_BUTTON_MOVE_UP); jButtonGruppeAb.setIcon(Icons.ICON_BUTTON_MOVE_DOWN); From 229bfbfde84990b3a0b31adf78e63a6be31c0e74 Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 10 Jun 2022 08:31:10 +0200 Subject: [PATCH 046/422] - convert to svg Former-commit-id: 129f00ed94f2310afe28ffe602c83cbdde2bb91c --- src/main/java/mediathek/config/Icons.java | 1 - .../mediathek/gui/dialogEinstellungen/PanelDateinamen.java | 2 +- .../java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/mediathek/config/Icons.java b/src/main/java/mediathek/config/Icons.java index 96099dbafa..f3d028e78e 100644 --- a/src/main/java/mediathek/config/Icons.java +++ b/src/main/java/mediathek/config/Icons.java @@ -30,7 +30,6 @@ public class Icons { public static final ImageIcon ICON_TABELLE_EIN = GetIcon.getProgramIcon("tabelle-ein.png", 16, 16); public static final ImageIcon ICON_TABELLE_AUS = GetIcon.getProgramIcon("tabelle-aus.png", 5, 5); - public static final ImageIcon ICON_BUTTON_REMOVE = GetIcon.getProgramIcon("button-remove.png", 16, 16); public static final ImageIcon ICON_BUTTON_MOVE_DOWN = GetIcon.getProgramIcon("button-move-down.png", 16, 16); public static final ImageIcon ICON_BUTTON_MOVE_UP = GetIcon.getProgramIcon("button-move-up.png", 16, 16); diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/PanelDateinamen.java b/src/main/java/mediathek/gui/dialogEinstellungen/PanelDateinamen.java index 2662d8177f..0974ff132f 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/PanelDateinamen.java +++ b/src/main/java/mediathek/gui/dialogEinstellungen/PanelDateinamen.java @@ -39,7 +39,7 @@ public PanelDateinamen(Daten d, JFrame pparentComponent) { jLabelAlert.setText(""); jLabelAlert.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/triangle-exclamation.svg", 32f)); jButtonPlus.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/plus.svg")); - jButtonMinus.setIcon(Icons.ICON_BUTTON_REMOVE); + jButtonMinus.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/minus.svg")); jButtonUp.setIcon(Icons.ICON_BUTTON_MOVE_UP); jButtonDown.setIcon(Icons.ICON_BUTTON_MOVE_DOWN); jButtonReset.addActionListener(e -> { diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java b/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java index 4b631946c0..6ead102898 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java +++ b/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java @@ -75,12 +75,12 @@ private void init() { jButtonHilfe.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/circle-question.svg")); jButtonGruppePfad.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg")); jButtonProgPlus.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/plus.svg")); - jButtonProgMinus.setIcon(Icons.ICON_BUTTON_REMOVE); + jButtonProgMinus.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/minus.svg")); jButtonProgAuf.setIcon(Icons.ICON_BUTTON_MOVE_UP); jButtonProgAb.setIcon(Icons.ICON_BUTTON_MOVE_DOWN); jButtonProgPfad.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg")); jButtonGruppeNeu.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/plus.svg")); - jButtonGruppeLoeschen.setIcon(Icons.ICON_BUTTON_REMOVE); + jButtonGruppeLoeschen.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/minus.svg")); jButtonGruppeAuf.setIcon(Icons.ICON_BUTTON_MOVE_UP); jButtonGruppeAb.setIcon(Icons.ICON_BUTTON_MOVE_DOWN); From 5eecf5d7b6e381c9c926e42fc4c0c0ca4badf2c0 Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 10 Jun 2022 08:34:38 +0200 Subject: [PATCH 047/422] - convert to svg Former-commit-id: 54ed67ee07a14d128a4941b940298ea195a81aff --- src/main/java/mediathek/config/Icons.java | 3 --- .../gui/dialogEinstellungen/PanelDateinamen.java | 5 ++--- .../gui/dialogEinstellungen/PanelPsetLang.java | 13 ++++++++----- src/main/resources/icons/fontawesome/arrow-down.svg | 1 + 4 files changed, 11 insertions(+), 11 deletions(-) create mode 100755 src/main/resources/icons/fontawesome/arrow-down.svg diff --git a/src/main/java/mediathek/config/Icons.java b/src/main/java/mediathek/config/Icons.java index f3d028e78e..5a4c052cdd 100644 --- a/src/main/java/mediathek/config/Icons.java +++ b/src/main/java/mediathek/config/Icons.java @@ -30,9 +30,6 @@ public class Icons { public static final ImageIcon ICON_TABELLE_EIN = GetIcon.getProgramIcon("tabelle-ein.png", 16, 16); public static final ImageIcon ICON_TABELLE_AUS = GetIcon.getProgramIcon("tabelle-aus.png", 5, 5); - public static final ImageIcon ICON_BUTTON_MOVE_DOWN = GetIcon.getProgramIcon("button-move-down.png", 16, 16); - public static final ImageIcon ICON_BUTTON_MOVE_UP = GetIcon.getProgramIcon("button-move-up.png", 16, 16); - // Icons TABBED_PANE public static final ImageIcon ICON_TAB_FILM = GetIcon.getProgramIcon("tab-film.png", 32, 32); public static final ImageIcon ICON_TAB_DOWNLOAD = GetIcon.getProgramIcon("tab-download.png", 32, 32); diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/PanelDateinamen.java b/src/main/java/mediathek/gui/dialogEinstellungen/PanelDateinamen.java index 0974ff132f..ced3647d77 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/PanelDateinamen.java +++ b/src/main/java/mediathek/gui/dialogEinstellungen/PanelDateinamen.java @@ -1,7 +1,6 @@ package mediathek.gui.dialogEinstellungen; import mediathek.config.Daten; -import mediathek.config.Icons; import mediathek.config.MVConfig; import mediathek.gui.PanelVorlage; import mediathek.gui.messages.ReplaceListChangedEvent; @@ -40,8 +39,8 @@ public PanelDateinamen(Daten d, JFrame pparentComponent) { jLabelAlert.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/triangle-exclamation.svg", 32f)); jButtonPlus.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/plus.svg")); jButtonMinus.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/minus.svg")); - jButtonUp.setIcon(Icons.ICON_BUTTON_MOVE_UP); - jButtonDown.setIcon(Icons.ICON_BUTTON_MOVE_DOWN); + jButtonUp.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/arrow-up.svg")); + jButtonDown.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/arrow-down.svg")); jButtonReset.addActionListener(e -> { ReplaceList.init(); tabelleLaden(); diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java b/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java index 6ead102898..9d1d0e3f76 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java +++ b/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java @@ -1,7 +1,10 @@ package mediathek.gui.dialogEinstellungen; import javafx.scene.control.Alert; -import mediathek.config.*; +import mediathek.config.Daten; +import mediathek.config.Konstanten; +import mediathek.config.MVColor; +import mediathek.config.MVConfig; import mediathek.controller.IoXmlSchreiben; import mediathek.controller.starter.RuntimeExec; import mediathek.daten.DatenProg; @@ -76,13 +79,13 @@ private void init() { jButtonGruppePfad.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg")); jButtonProgPlus.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/plus.svg")); jButtonProgMinus.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/minus.svg")); - jButtonProgAuf.setIcon(Icons.ICON_BUTTON_MOVE_UP); - jButtonProgAb.setIcon(Icons.ICON_BUTTON_MOVE_DOWN); + jButtonProgAuf.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/arrow-up.svg")); + jButtonProgAb.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/arrow-down.svg")); jButtonProgPfad.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/folder-open.svg")); jButtonGruppeNeu.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/plus.svg")); jButtonGruppeLoeschen.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/minus.svg")); - jButtonGruppeAuf.setIcon(Icons.ICON_BUTTON_MOVE_UP); - jButtonGruppeAb.setIcon(Icons.ICON_BUTTON_MOVE_DOWN); + jButtonGruppeAuf.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/arrow-up.svg")); + jButtonGruppeAb.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/arrow-down.svg")); var exclamationIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/triangle-exclamation.svg"); jLabelMeldungAbspielen.setIcon(exclamationIcon); diff --git a/src/main/resources/icons/fontawesome/arrow-down.svg b/src/main/resources/icons/fontawesome/arrow-down.svg new file mode 100755 index 0000000000..dac4e00889 --- /dev/null +++ b/src/main/resources/icons/fontawesome/arrow-down.svg @@ -0,0 +1 @@ + \ No newline at end of file From e7cd5a3dbdd7d8a0deac5bf7f6bd62d9b20e7708 Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 10 Jun 2022 08:46:30 +0200 Subject: [PATCH 048/422] - convert to svg Former-commit-id: 78d4a761d0db726896c71626d9b6bc4f5223916b --- src/main/java/mediathek/config/Icons.java | 3 --- src/main/java/mediathek/gui/dialog/DialogEditDownload.java | 3 +-- .../mediathek/tool/cellrenderer/CellRendererProgramme.java | 3 ++- .../java/mediathek/tool/cellrenderer/CellRendererPset.java | 3 ++- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/java/mediathek/config/Icons.java b/src/main/java/mediathek/config/Icons.java index 5a4c052cdd..f080cf8eb2 100644 --- a/src/main/java/mediathek/config/Icons.java +++ b/src/main/java/mediathek/config/Icons.java @@ -25,9 +25,6 @@ import java.awt.*; public class Icons { - public static final ImageIcon ICON_DIALOG_EIN_SW = GetIcon.getProgramIcon("dialog-ein-sw.png", 16, 16); - - public static final ImageIcon ICON_TABELLE_EIN = GetIcon.getProgramIcon("tabelle-ein.png", 16, 16); public static final ImageIcon ICON_TABELLE_AUS = GetIcon.getProgramIcon("tabelle-aus.png", 5, 5); // Icons TABBED_PANE diff --git a/src/main/java/mediathek/gui/dialog/DialogEditDownload.java b/src/main/java/mediathek/gui/dialog/DialogEditDownload.java index fe19e8ffb6..39719cbbfb 100644 --- a/src/main/java/mediathek/gui/dialog/DialogEditDownload.java +++ b/src/main/java/mediathek/gui/dialog/DialogEditDownload.java @@ -1,6 +1,5 @@ package mediathek.gui.dialog; -import mediathek.config.Icons; import mediathek.controller.starter.Start; import mediathek.daten.DatenDownload; import mediathek.daten.DatenProg; @@ -52,7 +51,7 @@ public DialogEditDownload(JFrame parent, boolean modal, DatenDownload ddownload, datenDownload = ddownload; gestartet = ggestartet; jScrollPane1.getVerticalScrollBar().setUnitIncrement(16); - ja_sw_16 = Icons.ICON_DIALOG_EIN_SW; + ja_sw_16 = SVGIconUtilities.createSVGIcon("icons/fontawesome/check.svg"); columnModel = colModel; orgProgArray = datenDownload.arr[DatenDownload.DOWNLOAD_PROGRAMM_AUFRUF_ARRAY]; diff --git a/src/main/java/mediathek/tool/cellrenderer/CellRendererProgramme.java b/src/main/java/mediathek/tool/cellrenderer/CellRendererProgramme.java index a6e2728821..f0504329fd 100644 --- a/src/main/java/mediathek/tool/cellrenderer/CellRendererProgramme.java +++ b/src/main/java/mediathek/tool/cellrenderer/CellRendererProgramme.java @@ -2,6 +2,7 @@ import mediathek.config.Icons; import mediathek.daten.DatenProg; +import mediathek.tool.SVGIconUtilities; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -11,7 +12,7 @@ @SuppressWarnings("serial") public class CellRendererProgramme extends DefaultTableCellRenderer { - private static final ImageIcon ja_16 = Icons.ICON_TABELLE_EIN; + private static final ImageIcon ja_16 = SVGIconUtilities.createSVGIcon("icons/fontawesome/check.svg"); private static final ImageIcon nein_12 = Icons.ICON_TABELLE_AUS; private static final Logger logger = LogManager.getLogger(); diff --git a/src/main/java/mediathek/tool/cellrenderer/CellRendererPset.java b/src/main/java/mediathek/tool/cellrenderer/CellRendererPset.java index 165014553f..96ddb602fc 100644 --- a/src/main/java/mediathek/tool/cellrenderer/CellRendererPset.java +++ b/src/main/java/mediathek/tool/cellrenderer/CellRendererPset.java @@ -2,6 +2,7 @@ import mediathek.config.Icons; import mediathek.daten.DatenPset; +import mediathek.tool.SVGIconUtilities; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -11,7 +12,7 @@ @SuppressWarnings("serial") public class CellRendererPset extends DefaultTableCellRenderer { - private static final ImageIcon ja_16 = Icons.ICON_TABELLE_EIN; + private static final ImageIcon ja_16 = SVGIconUtilities.createSVGIcon("icons/fontawesome/check.svg"); private static final ImageIcon nein_12 = Icons.ICON_TABELLE_AUS; private static final Logger logger = LogManager.getLogger(); From 56dc9a527be4dc0df62117dc385dad03079a51aa Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 10 Jun 2022 08:46:56 +0200 Subject: [PATCH 049/422] - convert to svg Former-commit-id: 83680cb9d976542f6130f6f9e51f3d5b42a41e76 --- src/main/java/mediathek/tool/cellrenderer/CellRendererPset.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/mediathek/tool/cellrenderer/CellRendererPset.java b/src/main/java/mediathek/tool/cellrenderer/CellRendererPset.java index 96ddb602fc..7aa6d402ba 100644 --- a/src/main/java/mediathek/tool/cellrenderer/CellRendererPset.java +++ b/src/main/java/mediathek/tool/cellrenderer/CellRendererPset.java @@ -10,7 +10,6 @@ import javax.swing.table.DefaultTableCellRenderer; import java.awt.*; -@SuppressWarnings("serial") public class CellRendererPset extends DefaultTableCellRenderer { private static final ImageIcon ja_16 = SVGIconUtilities.createSVGIcon("icons/fontawesome/check.svg"); private static final ImageIcon nein_12 = Icons.ICON_TABELLE_AUS; From 15166845bb862e5efdcd1a2ba40744297dc97d7b Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 10 Jun 2022 08:51:14 +0200 Subject: [PATCH 050/422] - convert to svg Former-commit-id: fde6c3f9b40efdd391e5644b140854058b4747a7 --- src/main/java/mediathek/config/Icons.java | 2 -- .../mediathek/tool/cellrenderer/CellRendererProgramme.java | 5 +---- .../java/mediathek/tool/cellrenderer/CellRendererPset.java | 6 ++---- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/main/java/mediathek/config/Icons.java b/src/main/java/mediathek/config/Icons.java index f080cf8eb2..f8ea356c16 100644 --- a/src/main/java/mediathek/config/Icons.java +++ b/src/main/java/mediathek/config/Icons.java @@ -25,8 +25,6 @@ import java.awt.*; public class Icons { - public static final ImageIcon ICON_TABELLE_AUS = GetIcon.getProgramIcon("tabelle-aus.png", 5, 5); - // Icons TABBED_PANE public static final ImageIcon ICON_TAB_FILM = GetIcon.getProgramIcon("tab-film.png", 32, 32); public static final ImageIcon ICON_TAB_DOWNLOAD = GetIcon.getProgramIcon("tab-download.png", 32, 32); diff --git a/src/main/java/mediathek/tool/cellrenderer/CellRendererProgramme.java b/src/main/java/mediathek/tool/cellrenderer/CellRendererProgramme.java index f0504329fd..e2784a8a25 100644 --- a/src/main/java/mediathek/tool/cellrenderer/CellRendererProgramme.java +++ b/src/main/java/mediathek/tool/cellrenderer/CellRendererProgramme.java @@ -1,6 +1,5 @@ package mediathek.tool.cellrenderer; -import mediathek.config.Icons; import mediathek.daten.DatenProg; import mediathek.tool.SVGIconUtilities; import org.apache.logging.log4j.LogManager; @@ -10,10 +9,8 @@ import javax.swing.table.DefaultTableCellRenderer; import java.awt.*; -@SuppressWarnings("serial") public class CellRendererProgramme extends DefaultTableCellRenderer { private static final ImageIcon ja_16 = SVGIconUtilities.createSVGIcon("icons/fontawesome/check.svg"); - private static final ImageIcon nein_12 = Icons.ICON_TABELLE_AUS; private static final Logger logger = LogManager.getLogger(); public CellRendererProgramme() { @@ -37,7 +34,7 @@ public Component getTableCellRendererComponent( if (getText().equals(Boolean.TRUE.toString())) { setIcon(ja_16); } else { - setIcon(nein_12); + setIcon(null); } setText(""); } diff --git a/src/main/java/mediathek/tool/cellrenderer/CellRendererPset.java b/src/main/java/mediathek/tool/cellrenderer/CellRendererPset.java index 7aa6d402ba..7e28787130 100644 --- a/src/main/java/mediathek/tool/cellrenderer/CellRendererPset.java +++ b/src/main/java/mediathek/tool/cellrenderer/CellRendererPset.java @@ -1,6 +1,5 @@ package mediathek.tool.cellrenderer; -import mediathek.config.Icons; import mediathek.daten.DatenPset; import mediathek.tool.SVGIconUtilities; import org.apache.logging.log4j.LogManager; @@ -12,7 +11,6 @@ public class CellRendererPset extends DefaultTableCellRenderer { private static final ImageIcon ja_16 = SVGIconUtilities.createSVGIcon("icons/fontawesome/check.svg"); - private static final ImageIcon nein_12 = Icons.ICON_TABELLE_AUS; private static final Logger logger = LogManager.getLogger(); public CellRendererPset() { @@ -49,7 +47,7 @@ public Component getTableCellRendererComponent( if (datenPset.istAbspielen()) { setIcon(ja_16); } else { - setIcon(nein_12); + setIcon(null); } } if (c == DatenPset.PROGRAMMSET_IST_SPEICHERN) { @@ -58,7 +56,7 @@ public Component getTableCellRendererComponent( if (datenPset.istSpeichern()) { setIcon(ja_16); } else { - setIcon(nein_12); + setIcon(null); } } } catch (Exception ex) { From 548e3884232f98ae44964ff2eae6992c74ca3f64 Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 10 Jun 2022 10:15:29 +0200 Subject: [PATCH 051/422] - bump to MV 14 Former-commit-id: 7cfa2adb45fcecd02028c2474a1bbdd6c7de468f --- CHANGELOG.md | 3 ++- .../mediathek/res/programm/fxml/download_toolbar.fxml | 5 ++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ccdf1badcd..f013a0c21a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,10 @@ **14.0.0** - **BUGFIX:** *"Filme/Blacklist öffnen..."* Eintrag wurde in *"Filme/Blacklist bearbeiten..."* umbenannt und die Icons vereinheitlicht. +- Im Download-Tab wurde der "Filter anzeigen/ausblenden"-Button an die anderen ToolBar-Buttons angegliedert. **13.9.1** -- + - Die Binaries für macOS waren beschädigt und mussten erneuert werden. **13.9.0** diff --git a/src/main/resources/mediathek/res/programm/fxml/download_toolbar.fxml b/src/main/resources/mediathek/res/programm/fxml/download_toolbar.fxml index aee00bba2e..33c9be6c94 100644 --- a/src/main/resources/mediathek/res/programm/fxml/download_toolbar.fxml +++ b/src/main/resources/mediathek/res/programm/fxml/download_toolbar.fxml @@ -1,8 +1,7 @@ - - + - + - - - - - - - - - - - - - - - - - - From 9b237af161dc3df189bb62f4f56780a1aedeb94a Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 10 Jun 2022 15:15:36 +0200 Subject: [PATCH 059/422] doc changes Former-commit-id: ba83af9a3f5d2d91e2be5e2598f7bee927716a3f --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8710549414..bb1fb2de86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ - **BUGFIX:** *"Filme/Blacklist öffnen..."* Eintrag wurde in *"Filme/Blacklist bearbeiten..."* umbenannt und die Icons vereinheitlicht. - **BUGFIX:** Manchmal inkorrekte Darstellung des Bookmark-Status im Film-Tab wurde korrigiert. +- **FEATURE:** Die ToolBar im Download-Tab kann nun zum Schweben abgetrennt werden. +- **FEATURE:** Icons wurden durch hochauflösende Vektorvarianten ersetzt. - Im Download-Tab wurde der "Filter anzeigen/ausblenden"-Button an die anderen ToolBar-Buttons angegliedert. **13.9.1** From e9edc2ac862d097f383fe79320c86c8348bfe15f Mon Sep 17 00:00:00 2001 From: Christian F Date: Sat, 11 Jun 2022 14:02:34 +0200 Subject: [PATCH 060/422] - remove show bookmarked films only from toolbar as it is located in the filter panel already where it belongs to. Former-commit-id: 9be4f8aa42c43acc1905c9542fa6e83d616efdda --- .../javafx/filterpanel/FXFilmToolBar.java | 2 -- .../javafx/filterpanel/FilmActionPanel.java | 24 ------------------- .../res/programm/fxml/film_toolbar.fxml | 9 ------- 3 files changed, 35 deletions(-) diff --git a/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java b/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java index 844f9a603b..06ddddfd53 100644 --- a/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java +++ b/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java @@ -24,8 +24,6 @@ public class FXFilmToolBar extends ToolBar { @FXML Button btnBookmark; - @FXML ToggleButton btnShowBookmarkedMovies; - @FXML Button btnShowFilter; @FXML diff --git a/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java b/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java index 20832a22ba..3a054226a6 100644 --- a/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java +++ b/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java @@ -42,8 +42,6 @@ public class FilmActionPanel { private final PauseTransition finalActionTrans = new PauseTransition(Duration.seconds(1)); private final Tooltip tooltipSearchIrgendwo = new Tooltip("Suche in Beschreibung aktiviert"); private final Tooltip tooltipSearchRegular = new Tooltip("Suche in Beschreibung deaktiviert"); - private final Tooltip bookmarklistSelected = new Tooltip("Alle Filme anzeigen"); - private final Tooltip bookmarklistDeselected = new Tooltip("Gemerkte Filme anzeigen"); private final FilterConfiguration filterConfig; private final ObservableList availableFilters; public ReadOnlyStringWrapper roSearchStringProperty = new ReadOnlyStringWrapper(); @@ -369,26 +367,6 @@ private void setupForIrgendwoSearch() { toolBar.btnSearchThroughDescription.setTooltip(tooltipSearchIrgendwo); } - private void setupShowBookmarkedMoviesButton() { - toolBar.btnShowBookmarkedMovies.setSelected(false); - toolBar.btnShowBookmarkedMovies.setTooltip(bookmarklistDeselected); - toolBar.btnShowBookmarkedMovies.setOnAction( - event -> - viewSettingsPane - .cbShowBookMarkedOnly - .selectedProperty() - .set(toolBar.btnShowBookmarkedMovies.isSelected())); - showBookMarkedOnly.addListener( - (observable, oldValue, benabled) -> { - toolBar.btnShowBookmarkedMovies.setTooltip( - benabled ? bookmarklistSelected : bookmarklistDeselected); - toolBar.btnShowBookmarkedMovies.setSelected(benabled); - if (benabled) { - toolBar.jfxSearchField.clear(); - } - }); - } - public void updateThemaBox() { final var items = themaBox.getItems(); items.clear(); @@ -447,8 +425,6 @@ public Scene getFilmActionPanelScene() { setupSearchThroughDescriptionButton(); - setupShowBookmarkedMoviesButton(); - Daten.getInstance().getFilmeLaden().addAdListener( new ListenerFilmeLaden() { @Override diff --git a/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml b/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml index e92620483d..3c0d5f1bf8 100644 --- a/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml +++ b/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml @@ -36,15 +36,6 @@ - - - - - - - - - From 60cca607efe7e610843c30492aedca3a5c2ce55c Mon Sep 17 00:00:00 2001 From: Christian F Date: Sun, 12 Jun 2022 13:21:36 +0200 Subject: [PATCH 061/422] cleanup common toolbar Former-commit-id: b22a96ba62cbfb0f8eea5da650dba85129726701 --- src/main/java/mediathek/mainwindow/MediathekGui.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/mediathek/mainwindow/MediathekGui.java b/src/main/java/mediathek/mainwindow/MediathekGui.java index 5b465a970a..93def74dfc 100644 --- a/src/main/java/mediathek/mainwindow/MediathekGui.java +++ b/src/main/java/mediathek/mainwindow/MediathekGui.java @@ -253,12 +253,12 @@ public static MediathekGui ui() { protected void createCommonToolBar() { commonToolBar.add(loadFilmListAction); commonToolBar.add(showFilmInformationAction); - commonToolBar.addSeparator(); commonToolBar.add(toggleBlacklistAction); - commonToolBar.add(editBlacklistAction); commonToolBar.addSeparator(); + commonToolBar.add(editBlacklistAction); commonToolBar.add(manageAboAction); commonToolBar.add(manageBookmarkAction); + commonToolBar.addSeparator(); commonToolBar.add(settingsAction); getContentPane().add(commonToolBar, BorderLayout.PAGE_START); From 73a34a7136dbb1d181d3516c8ae601ff29c4f0ca Mon Sep 17 00:00:00 2001 From: Christian F Date: Mon, 20 Jun 2022 11:22:36 +0200 Subject: [PATCH 062/422] make common toolbar floatable on win and linux Former-commit-id: 472b4237a4714205bb2ff38c61fc8fc9b64f0935 --- src/main/java/mediathek/mainwindow/MediathekGui.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/mediathek/mainwindow/MediathekGui.java b/src/main/java/mediathek/mainwindow/MediathekGui.java index 93def74dfc..719b14d55b 100644 --- a/src/main/java/mediathek/mainwindow/MediathekGui.java +++ b/src/main/java/mediathek/mainwindow/MediathekGui.java @@ -261,6 +261,11 @@ protected void createCommonToolBar() { commonToolBar.addSeparator(); commonToolBar.add(settingsAction); + if (!SystemUtils.IS_OS_MAC_OSX) { + commonToolBar.setFloatable(true); + commonToolBar.setName("Allgemein"); + } + getContentPane().add(commonToolBar, BorderLayout.PAGE_START); } From 37d4cdb7701bf6cf116065aca5c67ddd05487aae Mon Sep 17 00:00:00 2001 From: Christian F Date: Mon, 20 Jun 2022 12:14:48 +0200 Subject: [PATCH 063/422] - create swing toolbar Former-commit-id: 640be804b1064a333bcc280efa6eceaace39dbed --- .../mediathek/gui/tabs/tab_film/GuiFilme.java | 68 ++++++++++++------- 1 file changed, 45 insertions(+), 23 deletions(-) diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index f645e4825c..7092bb0325 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -122,6 +122,7 @@ public GuiFilme(Daten aDaten, MediathekGui mediathekGui) { createFilmListArea(); createExtensionArea(); + createToolBar(); createFilmActionPanel(); // add film description panel @@ -145,6 +146,17 @@ public GuiFilme(Daten aDaten, MediathekGui mediathekGui) { setupActionListeners(); } + private void createToolBar() { + toolBar.setFloatable(true); + toolBar.setName("Filme"); + + toolBar.add(playFilmAction); + toolBar.add(saveFilmAction); + toolBar.addSeparator(); + toolBar.add(bookmarkFilmAction); + + } + @Override public void tabelleSpeichern() { if (tabelle != null) { @@ -157,9 +169,15 @@ private void setupFilmListTable() { filmListScrollPane.setViewportView(tabelle); } + private final JToolBar toolBar = new JToolBar(); + private void createFilmActionPanel() { + var tempPanel = new JPanel(); + tempPanel.setLayout(new BorderLayout()); fxFilmActionPanel = new JFXPanel(); - add(fxFilmActionPanel, BorderLayout.NORTH); + tempPanel.add(toolBar, BorderLayout.NORTH); + tempPanel.add(fxFilmActionPanel, BorderLayout.SOUTH); + add(tempPanel, BorderLayout.NORTH); } /** @@ -242,26 +260,6 @@ public void installViewMenuEntry(JMenu jMenuAnsicht) { public void installMenuEntries(JMenu menu) { KeyStroke keyStroke; - JMenuItem miRecordFilm = new JMenuItem("Film aufzeichnen"); - if (SystemUtils.IS_OS_MAC_OSX) { - keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_F7, GuiFunktionen.getPlatformControlKey()); - } - else - keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_D, GuiFunktionen.getPlatformControlKey()); - miRecordFilm.setAccelerator(keyStroke); - miRecordFilm.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/download.svg")); - miRecordFilm.addActionListener(saveFilmAction); - - JMenuItem miBookmarkFilm = new JMenuItem("Film merken"); - if (SystemUtils.IS_OS_MAC_OSX) { - keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_F8, GuiFunktionen.getPlatformControlKey()); - } else { - keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_B, GuiFunktionen.getPlatformControlKey()); - } - miBookmarkFilm.setAccelerator(keyStroke); - miBookmarkFilm.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/bookmark.svg")); - miBookmarkFilm.addActionListener(bookmarkFilmAction); - if (!SystemUtils.IS_OS_MAC_OSX) cbkShowDescription.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F10, 0)); @@ -275,8 +273,8 @@ public void installMenuEntries(JMenu menu) { miMarkFilmAsUnseen.addActionListener(markFilmAsUnseenAction); menu.add(playFilmAction); - menu.add(miRecordFilm); - menu.add(miBookmarkFilm); + menu.add(saveFilmAction); + menu.add(bookmarkFilmAction); menu.addSeparator(); menu.add(miMarkFilmAsSeen); menu.add(miMarkFilmAsUnseen); @@ -825,6 +823,18 @@ public void onFailure(@NotNull Throwable thrown) { } public class SaveFilmAction extends AbstractAction { + public SaveFilmAction() { + putValue(Action.SHORT_DESCRIPTION,"Film downloaden"); + putValue(Action.NAME, "Film downloaden"); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/download.svg")); + KeyStroke keyStroke; + if (SystemUtils.IS_OS_MAC_OSX) { + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_F7, GuiFunktionen.getPlatformControlKey()); + } + else + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_D, GuiFunktionen.getPlatformControlKey()); + putValue(Action.ACCELERATOR_KEY, keyStroke); + } @Override public void actionPerformed(ActionEvent e) { @@ -833,6 +843,18 @@ public void actionPerformed(ActionEvent e) { } public class BookmarkFilmAction extends AbstractAction { + public BookmarkFilmAction() { + KeyStroke keyStroke; + if (SystemUtils.IS_OS_MAC_OSX) { + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_F8, GuiFunktionen.getPlatformControlKey()); + } else { + keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_B, GuiFunktionen.getPlatformControlKey()); + } + putValue(Action.ACCELERATOR_KEY, keyStroke); + putValue(Action.SHORT_DESCRIPTION, "Film merken"); + putValue(Action.NAME, "Film merken"); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/bookmark.svg")); + } @Override public void actionPerformed(ActionEvent e) { From 03faeb0fe28532bb0d9a25847d8d82caba41e0d7 Mon Sep 17 00:00:00 2001 From: Christian F Date: Mon, 20 Jun 2022 12:20:41 +0200 Subject: [PATCH 064/422] - remove javafx buttons Former-commit-id: a2da3f14b0170344749e0fb31afc760e059395ea --- .../mediathek/gui/tabs/tab_film/GuiFilme.java | 8 ++--- .../javafx/filterpanel/FXFilmToolBar.java | 6 ---- .../javafx/filterpanel/FilmActionPanel.java | 9 ------ .../res/programm/fxml/film_toolbar.fxml | 31 +------------------ 4 files changed, 4 insertions(+), 50 deletions(-) diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index 7092bb0325..279d03fe7b 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -147,6 +147,8 @@ public GuiFilme(Daten aDaten, MediathekGui mediathekGui) { } private void createToolBar() { + add(toolBar, BorderLayout.NORTH); + toolBar.setFloatable(true); toolBar.setName("Filme"); @@ -172,12 +174,8 @@ private void setupFilmListTable() { private final JToolBar toolBar = new JToolBar(); private void createFilmActionPanel() { - var tempPanel = new JPanel(); - tempPanel.setLayout(new BorderLayout()); fxFilmActionPanel = new JFXPanel(); - tempPanel.add(toolBar, BorderLayout.NORTH); - tempPanel.add(fxFilmActionPanel, BorderLayout.SOUTH); - add(tempPanel, BorderLayout.NORTH); + toolBar.add(fxFilmActionPanel); } /** diff --git a/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java b/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java index 06ddddfd53..41e8da24d3 100644 --- a/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java +++ b/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java @@ -18,12 +18,6 @@ import java.net.URL; public class FXFilmToolBar extends ToolBar { - @FXML Button btnPlay; - - @FXML Button btnRecord; - - @FXML Button btnBookmark; - @FXML Button btnShowFilter; @FXML diff --git a/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java b/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java index 3a054226a6..2e40de816a 100644 --- a/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java +++ b/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java @@ -400,15 +400,6 @@ public void updateThemaBox() { private void setupToolBar() { toolBar = new FXFilmToolBar(); - toolBar.btnPlay.setOnAction(evt -> - SwingUtilities.invokeLater( - () -> MediathekGui.ui().tabFilme.playFilmAction.actionPerformed(null))); - toolBar.btnRecord.setOnAction(e -> - SwingUtilities.invokeLater( - () -> MediathekGui.ui().tabFilme.saveFilmAction.actionPerformed(null))); - toolBar.btnBookmark.setOnAction(e -> - SwingUtilities.invokeLater( - () -> MediathekGui.ui().tabFilme.bookmarkFilmAction.actionPerformed(null))); toolBar.btnShowFilter.setOnAction(e -> SwingUtilities.invokeLater(() -> { if (filterDialog != null) { diff --git a/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml b/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml index 3c0d5f1bf8..2abc2a4bb5 100644 --- a/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml +++ b/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml @@ -5,37 +5,8 @@ - + - - - - - - - - From 50292555ae93911727e9583a5599712d4db39195 Mon Sep 17 00:00:00 2001 From: Christian F Date: Mon, 20 Jun 2022 12:54:37 +0200 Subject: [PATCH 065/422] convert film toolbar to HBox Former-commit-id: 370c840d23cbc290c60035ab48868516bcb138d4 --- src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java | 1 + .../java/mediathek/javafx/filterpanel/FXFilmToolBar.java | 8 ++++++-- .../mediathek/res/programm/fxml/film_toolbar.fxml | 5 +---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index 279d03fe7b..744fb4cd68 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -175,6 +175,7 @@ private void setupFilmListTable() { private void createFilmActionPanel() { fxFilmActionPanel = new JFXPanel(); + toolBar.addSeparator(); toolBar.add(fxFilmActionPanel); } diff --git a/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java b/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java index 41e8da24d3..42a96718b8 100644 --- a/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java +++ b/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java @@ -5,7 +5,11 @@ import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; -import javafx.scene.control.*; +import javafx.scene.control.Button; +import javafx.scene.control.ComboBox; +import javafx.scene.control.SingleSelectionModel; +import javafx.scene.control.ToggleButton; +import javafx.scene.layout.HBox; import mediathek.gui.messages.TableModelChangeEvent; import mediathek.tool.FilterConfiguration; import mediathek.tool.FilterDTO; @@ -17,7 +21,7 @@ import java.io.IOException; import java.net.URL; -public class FXFilmToolBar extends ToolBar { +public class FXFilmToolBar extends HBox { @FXML Button btnShowFilter; @FXML diff --git a/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml b/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml index 2abc2a4bb5..5cb286c6d4 100644 --- a/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml +++ b/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml @@ -2,13 +2,10 @@ - - - - + From 34ba63d21ce17a99202ac5a368221165cda50330 Mon Sep 17 00:00:00 2001 From: Christian F Date: Mon, 20 Jun 2022 13:07:56 +0200 Subject: [PATCH 066/422] convert film toolbar to HBox Former-commit-id: 60f26ffa0181aac23a3fe6282ebdf3695df27902 --- .../mediathek/gui/tabs/tab_film/GuiFilme.java | 35 ++++++++++++++----- .../javafx/filterpanel/FXFilmToolBar.java | 3 -- .../javafx/filterpanel/FilmActionPanel.java | 13 +------ .../res/programm/fxml/film_toolbar.fxml | 13 ++----- 4 files changed, 31 insertions(+), 33 deletions(-) diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index 744fb4cd68..fa1dc56f3e 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -123,7 +123,6 @@ public GuiFilme(Daten aDaten, MediathekGui mediathekGui) { createFilmListArea(); createExtensionArea(); createToolBar(); - createFilmActionPanel(); // add film description panel extensionArea.add(fxDescriptionPanel); @@ -146,6 +145,26 @@ public GuiFilme(Daten aDaten, MediathekGui mediathekGui) { setupActionListeners(); } + public class ShowFilterDialogAction extends AbstractAction { + public ShowFilterDialogAction() { + putValue(Action.NAME, "Filterdialog anzeigen"); + putValue(Action.SHORT_DESCRIPTION, "Filter anzeigen"); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/filter.svg")); + putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_F12, 0)); + } + + @Override + public void actionPerformed(ActionEvent e) { + var dlg = filmActionPanel.filterDialog; + if (dlg != null) { + if (!dlg.isVisible()) { + dlg.setVisible(true); + } + } + } + } + protected ShowFilterDialogAction showFilterDialogAction = new ShowFilterDialogAction(); + private void createToolBar() { add(toolBar, BorderLayout.NORTH); @@ -156,7 +175,13 @@ private void createToolBar() { toolBar.add(saveFilmAction); toolBar.addSeparator(); toolBar.add(bookmarkFilmAction); - + toolBar.addSeparator(); + toolBar.add(showFilterDialogAction); + fxFilmActionPanel = new JFXPanel(); + toolBar.addSeparator(); + toolBar.add(fxFilmActionPanel); + toolBar.addSeparator(); + toolBar.add("test", new JButton("a")); } @Override @@ -173,12 +198,6 @@ private void setupFilmListTable() { private final JToolBar toolBar = new JToolBar(); - private void createFilmActionPanel() { - fxFilmActionPanel = new JFXPanel(); - toolBar.addSeparator(); - toolBar.add(fxFilmActionPanel); - } - /** * Update the property with the current number of selected entries from the JTable. */ diff --git a/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java b/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java index 42a96718b8..01c9e1e190 100644 --- a/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java +++ b/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java @@ -5,7 +5,6 @@ import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; -import javafx.scene.control.Button; import javafx.scene.control.ComboBox; import javafx.scene.control.SingleSelectionModel; import javafx.scene.control.ToggleButton; @@ -22,8 +21,6 @@ import java.net.URL; public class FXFilmToolBar extends HBox { - @FXML Button btnShowFilter; - @FXML FXSearchControl jfxSearchField; diff --git a/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java b/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java index 2e40de816a..f54ff11a84 100644 --- a/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java +++ b/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java @@ -398,19 +398,8 @@ public void updateThemaBox() { themaBox.getSelectionModel().select(0); } - private void setupToolBar() { - toolBar = new FXFilmToolBar(); - toolBar.btnShowFilter.setOnAction(e -> - SwingUtilities.invokeLater(() -> { - if (filterDialog != null) { - filterDialog.setVisible( - !filterDialog.isVisible()); // Toggle Dialog display on button press - } - })); - } - public Scene getFilmActionPanelScene() { - setupToolBar(); + toolBar = new FXFilmToolBar(); setupSearchField(); diff --git a/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml b/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml index 5cb286c6d4..83a9872671 100644 --- a/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml +++ b/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml @@ -1,6 +1,8 @@ - + + + @@ -12,15 +14,6 @@ - - From 48bc91c75427c2245f67b156a9410fb76ae4b94e Mon Sep 17 00:00:00 2001 From: Christian F Date: Mon, 20 Jun 2022 13:20:49 +0200 Subject: [PATCH 067/422] use standard action Former-commit-id: 7b59a69aeea3665e6397bef27e5a14a4bbcf2030 --- .../mediathek/gui/tabs/tab_film/GuiFilme.java | 21 ++++++++++++++++++- .../mediathek/mainwindow/MediathekGui.java | 15 +------------ 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index fa1dc56f3e..b185ec4d56 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -163,7 +163,7 @@ public void actionPerformed(ActionEvent e) { } } } - protected ShowFilterDialogAction showFilterDialogAction = new ShowFilterDialogAction(); + public ShowFilterDialogAction showFilterDialogAction = new ShowFilterDialogAction(); private void createToolBar() { add(toolBar, BorderLayout.NORTH); @@ -182,6 +182,25 @@ private void createToolBar() { toolBar.add(fxFilmActionPanel); toolBar.addSeparator(); toolBar.add("test", new JButton("a")); + + Daten.getInstance().getFilmeLaden().addAdListener( + new ListenerFilmeLaden() { + @Override + public void start(ListenerFilmeLadenEvent event) { + playFilmAction.setEnabled(false); + saveFilmAction.setEnabled(false); + bookmarkFilmAction.setEnabled(false); + showFilterDialogAction.setEnabled(false); + } + + @Override + public void fertig(ListenerFilmeLadenEvent event) { + playFilmAction.setEnabled(true); + saveFilmAction.setEnabled(true); + bookmarkFilmAction.setEnabled(true); + showFilterDialogAction.setEnabled(true); + } + }); } @Override diff --git a/src/main/java/mediathek/mainwindow/MediathekGui.java b/src/main/java/mediathek/mainwindow/MediathekGui.java index 719b14d55b..05306cf24e 100644 --- a/src/main/java/mediathek/mainwindow/MediathekGui.java +++ b/src/main/java/mediathek/mainwindow/MediathekGui.java @@ -68,7 +68,6 @@ import javax.swing.*; import java.awt.*; -import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.IOException; @@ -857,23 +856,11 @@ private void createViewMenu() { getBandwidthMonitorController().setVisibility(); }); - JMenuItem showFilmFilterDialog = new JMenuItem("Filterdialog anzeigen"); - showFilmFilterDialog.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F12, 0)); - showFilmFilterDialog.addActionListener(l -> { - var dlg = tabFilme.filmActionPanel.filterDialog; - if (dlg != null) { - if (!dlg.isVisible()) { - dlg.setVisible(true); - } - } - - }); - jMenuAnsicht.addSeparator(); jMenuAnsicht.add(showMemoryMonitorAction); jMenuAnsicht.add(cbBandwidthDisplay); jMenuAnsicht.addSeparator(); - jMenuAnsicht.add(showFilmFilterDialog); + jMenuAnsicht.add(tabFilme.showFilterDialogAction); jMenuAnsicht.addSeparator(); jMenuAnsicht.add(showFilmInformationAction); jMenuAnsicht.addSeparator(); From bc7c0555a7b6738dd5049bef5c0a36a1b4b821f4 Mon Sep 17 00:00:00 2001 From: Christian F Date: Mon, 20 Jun 2022 13:21:09 +0200 Subject: [PATCH 068/422] disable debug code Former-commit-id: 1770e63cd42cffb793fa19a5ee6ee9923484f399 --- src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index b185ec4d56..3404cbb0df 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -180,8 +180,8 @@ private void createToolBar() { fxFilmActionPanel = new JFXPanel(); toolBar.addSeparator(); toolBar.add(fxFilmActionPanel); - toolBar.addSeparator(); - toolBar.add("test", new JButton("a")); + //toolBar.addSeparator(); + //toolBar.add("test", new JButton("a")); Daten.getInstance().getFilmeLaden().addAdListener( new ListenerFilmeLaden() { From 74651a29d51b9ab2ef4363fc871b6cf3dac3e8f1 Mon Sep 17 00:00:00 2001 From: Christian F Date: Mon, 20 Jun 2022 13:23:19 +0200 Subject: [PATCH 069/422] fix layout issue Former-commit-id: dc81ccbca1a58d41f6f03cb0450e56cd4244238f --- .../resources/mediathek/res/programm/fxml/film_toolbar.fxml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml b/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml index 83a9872671..6305cb5b30 100644 --- a/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml +++ b/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml @@ -5,17 +5,14 @@ - - + - - From ba50b88915de3053ee454ab100779886b2db6598 Mon Sep 17 00:00:00 2001 From: Christian F Date: Mon, 20 Jun 2022 15:37:59 +0200 Subject: [PATCH 070/422] add new swing button Former-commit-id: 037733bbb495c64c8274617d2ea517412c070185 --- .../mediathek/gui/tabs/tab_film/GuiFilme.java | 41 ++++++++++++-- .../tabs/tab_film/GuiFilmeModelHelper.java | 2 +- .../javafx/filterpanel/FilmActionPanel.java | 54 ++++++++++--------- .../icons/fontawesome/envelope-open-text.svg | 1 + 4 files changed, 70 insertions(+), 28 deletions(-) create mode 100755 src/main/resources/icons/fontawesome/envelope-open-text.svg diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index 3404cbb0df..7fd6b7a01e 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -165,6 +165,40 @@ public void actionPerformed(ActionEvent e) { } public ShowFilterDialogAction showFilterDialogAction = new ShowFilterDialogAction(); + public class ToggleSearchThroughDescriptionAction extends AbstractAction { + public ToggleSearchThroughDescriptionAction() { + boolean bSearchThroughDescription = ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.SEARCH_USE_FILM_DESCRIPTIONS, false); + setupToolTip(bSearchThroughDescription); + //putValue(Action.NAME, "Beschreibung"); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/envelope-open-text.svg")); + } + + private void setupToolTip(boolean active) { + if (active) + putValue(Action.SHORT_DESCRIPTION, "Suche in Beschreibung aktiviert"); + else + putValue(Action.SHORT_DESCRIPTION, "Suche in Beschreibung deaktiviert"); + } + + @Override + public void actionPerformed(ActionEvent e) { + JavaFxUtils.invokeInFxThreadAndWait(() -> filmActionPanel.toggleSearchThroughDescriptionButton()); + + boolean bSearchThroughDescription = ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.SEARCH_USE_FILM_DESCRIPTIONS, false); + JToggleButton source = (JToggleButton) e.getSource(); + source.setSelected(bSearchThroughDescription); + setupToolTip(bSearchThroughDescription); + } + } + protected ToggleSearchThroughDescriptionAction toggleSearchThroughDescriptionAction = new ToggleSearchThroughDescriptionAction(); + + private void addSearchThroughDescriptionToolBarButton() { + JToggleButton searchThroughDescriptionButton = new JToggleButton(toggleSearchThroughDescriptionAction); + boolean bSearchThroughDescription = ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.SEARCH_USE_FILM_DESCRIPTIONS, false); + searchThroughDescriptionButton.setSelected(bSearchThroughDescription); + toolBar.add(searchThroughDescriptionButton); + } + private void createToolBar() { add(toolBar, BorderLayout.NORTH); @@ -178,10 +212,11 @@ private void createToolBar() { toolBar.addSeparator(); toolBar.add(showFilterDialogAction); fxFilmActionPanel = new JFXPanel(); + fxFilmActionPanel.setMaximumSize(new Dimension(500,100)); toolBar.addSeparator(); toolBar.add(fxFilmActionPanel); - //toolBar.addSeparator(); - //toolBar.add("test", new JButton("a")); + toolBar.addSeparator(); + addSearchThroughDescriptionToolBarButton(); Daten.getInstance().getFilmeLaden().addAdListener( new ListenerFilmeLaden() { @@ -745,7 +780,7 @@ private void setupActionListeners() { filmActionPanel.showLivestreamsOnly.addListener(reloadTableListener); filmActionPanel.filmLengthSlider.lowValueChangingProperty().addListener(reloadTableListener2); filmActionPanel.filmLengthSlider.highValueChangingProperty().addListener(reloadTableListener2); - filmActionPanel.searchThroughDescription.addListener((os, o, n) -> { + filmActionPanel.searchThroughDescriptionProperty.addListener((os, o, n) -> { if (!filmActionPanel.roSearchStringProperty.getReadOnlyProperty().isEmpty().get()) reloadTableDataTransition.playFromStart(); }); diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilmeModelHelper.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilmeModelHelper.java index d6d86e0463..4c35361bdc 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilmeModelHelper.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilmeModelHelper.java @@ -105,7 +105,7 @@ private void updateFilterVars() { dontShowTrailers = filmActionPanel.dontShowTrailers.getValue(); dontShowGebaerdensprache = filmActionPanel.dontShowSignLanguage.getValue(); dontShowAudioVersions = filmActionPanel.dontShowAudioVersions.getValue(); - searchThroughDescriptions = filmActionPanel.searchThroughDescription.getValue(); + searchThroughDescriptions = filmActionPanel.searchThroughDescriptionProperty.getValue(); arrIrgendwo = evaluateThemaTitel(); } diff --git a/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java b/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java index f54ff11a84..6dc5045ed7 100644 --- a/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java +++ b/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java @@ -3,10 +3,7 @@ import impl.org.controlsfx.autocompletion.SuggestionProvider; import javafx.animation.PauseTransition; import javafx.application.Platform; -import javafx.beans.property.BooleanProperty; -import javafx.beans.property.ReadOnlyObjectProperty; -import javafx.beans.property.ReadOnlyStringWrapper; -import javafx.beans.property.StringProperty; +import javafx.beans.property.*; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.scene.Scene; @@ -55,7 +52,7 @@ public class FilmActionPanel { public BooleanProperty dontShowTrailers; public BooleanProperty dontShowSignLanguage; public BooleanProperty dontShowAudioVersions; - public BooleanProperty searchThroughDescription; + public BooleanProperty searchThroughDescriptionProperty = new SimpleBooleanProperty(); public ReadOnlyObjectProperty zeitraumProperty; public ComboBox themaBox; public RangeSlider filmLengthSlider; @@ -332,28 +329,37 @@ private void setupSearchField() { } private void setupSearchThroughDescriptionButton() { - final boolean enabled = + final boolean enabled = + ApplicationConfiguration.getConfiguration() + .getBoolean(ApplicationConfiguration.SEARCH_USE_FILM_DESCRIPTIONS, false); + toolBar.btnSearchThroughDescription.setSelected(enabled); + + if (enabled) + setupForIrgendwoSearch(); + else + setupForRegularSearch(); + + toolBar.btnSearchThroughDescription.setOnAction(e -> toggleSearchThroughDescriptionButton()); + boolean bSearchThroughDescription = + ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.SEARCH_USE_FILM_DESCRIPTIONS, false); + toolBar.btnSearchThroughDescription.setSelected(bSearchThroughDescription); + searchThroughDescriptionProperty.setValue(bSearchThroughDescription); + } + + public void toggleSearchThroughDescriptionButton() { + boolean bSearchThroughDescription = + ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.SEARCH_USE_FILM_DESCRIPTIONS, false); + bSearchThroughDescription = !bSearchThroughDescription; ApplicationConfiguration.getConfiguration() - .getBoolean(ApplicationConfiguration.SEARCH_USE_FILM_DESCRIPTIONS, false); - toolBar.btnSearchThroughDescription.setSelected(enabled); + .setProperty(ApplicationConfiguration.SEARCH_USE_FILM_DESCRIPTIONS, bSearchThroughDescription); - if (enabled) setupForIrgendwoSearch(); - else setupForRegularSearch(); + if (bSearchThroughDescription) + setupForIrgendwoSearch(); + else + setupForRegularSearch(); - toolBar.btnSearchThroughDescription.setOnAction( - e -> { - final boolean bSearchThroughDescription = - toolBar.btnSearchThroughDescription.isSelected(); - ApplicationConfiguration.getConfiguration() - .setProperty( - ApplicationConfiguration.SEARCH_USE_FILM_DESCRIPTIONS, bSearchThroughDescription); - - if (bSearchThroughDescription) setupForIrgendwoSearch(); - else setupForRegularSearch(); - }); - - searchThroughDescription = toolBar.btnSearchThroughDescription.selectedProperty(); - } + searchThroughDescriptionProperty.setValue(bSearchThroughDescription); + } private void setupForRegularSearch() { toolBar.jfxSearchField.setMode(FXSearchControlFieldMode.THEMA_TITEL); diff --git a/src/main/resources/icons/fontawesome/envelope-open-text.svg b/src/main/resources/icons/fontawesome/envelope-open-text.svg new file mode 100755 index 0000000000..cf982d8a3e --- /dev/null +++ b/src/main/resources/icons/fontawesome/envelope-open-text.svg @@ -0,0 +1 @@ + \ No newline at end of file From 26322d8b88ba8946eb2eb5fe824d0759231f9c61 Mon Sep 17 00:00:00 2001 From: Christian F Date: Mon, 20 Jun 2022 15:53:03 +0200 Subject: [PATCH 071/422] - convert to swing search through description toggle button Former-commit-id: fdb680e8af145bab4fd7c447109f04a9119d9f2a --- .../mediathek/gui/tabs/tab_film/GuiFilme.java | 2 +- .../javafx/filterpanel/FXFilmToolBar.java | 3 --- .../javafx/filterpanel/FilmActionPanel.java | 15 +-------------- .../mediathek/res/programm/fxml/film_toolbar.fxml | 8 -------- 4 files changed, 2 insertions(+), 26 deletions(-) diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index 7fd6b7a01e..4148428f81 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -212,7 +212,7 @@ private void createToolBar() { toolBar.addSeparator(); toolBar.add(showFilterDialogAction); fxFilmActionPanel = new JFXPanel(); - fxFilmActionPanel.setMaximumSize(new Dimension(500,100)); + fxFilmActionPanel.setMaximumSize(new Dimension(450,100)); toolBar.addSeparator(); toolBar.add(fxFilmActionPanel); toolBar.addSeparator(); diff --git a/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java b/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java index 01c9e1e190..175970be81 100644 --- a/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java +++ b/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java @@ -7,7 +7,6 @@ import javafx.fxml.FXMLLoader; import javafx.scene.control.ComboBox; import javafx.scene.control.SingleSelectionModel; -import javafx.scene.control.ToggleButton; import javafx.scene.layout.HBox; import mediathek.gui.messages.TableModelChangeEvent; import mediathek.tool.FilterConfiguration; @@ -24,8 +23,6 @@ public class FXFilmToolBar extends HBox { @FXML FXSearchControl jfxSearchField; - @FXML ToggleButton btnSearchThroughDescription; - @FXML ComboBox filterSelect; public FXFilmToolBar() { diff --git a/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java b/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java index 6dc5045ed7..1e50978c3c 100644 --- a/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java +++ b/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java @@ -8,7 +8,6 @@ import javafx.collections.ObservableList; import javafx.scene.Scene; import javafx.scene.control.ComboBox; -import javafx.scene.control.Tooltip; import javafx.util.Duration; import javafx.util.StringConverter; import mediathek.config.Daten; @@ -37,8 +36,6 @@ public class FilmActionPanel { private static final Logger logger = LogManager.getLogger(); private final PauseTransition finalActionTrans = new PauseTransition(Duration.seconds(1)); - private final Tooltip tooltipSearchIrgendwo = new Tooltip("Suche in Beschreibung aktiviert"); - private final Tooltip tooltipSearchRegular = new Tooltip("Suche in Beschreibung deaktiviert"); private final FilterConfiguration filterConfig; private final ObservableList availableFilters; public ReadOnlyStringWrapper roSearchStringProperty = new ReadOnlyStringWrapper(); @@ -332,18 +329,12 @@ private void setupSearchThroughDescriptionButton() { final boolean enabled = ApplicationConfiguration.getConfiguration() .getBoolean(ApplicationConfiguration.SEARCH_USE_FILM_DESCRIPTIONS, false); - toolBar.btnSearchThroughDescription.setSelected(enabled); - if (enabled) setupForIrgendwoSearch(); else setupForRegularSearch(); - toolBar.btnSearchThroughDescription.setOnAction(e -> toggleSearchThroughDescriptionButton()); - boolean bSearchThroughDescription = - ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.SEARCH_USE_FILM_DESCRIPTIONS, false); - toolBar.btnSearchThroughDescription.setSelected(bSearchThroughDescription); - searchThroughDescriptionProperty.setValue(bSearchThroughDescription); + searchThroughDescriptionProperty.setValue(enabled); } public void toggleSearchThroughDescriptionButton() { @@ -363,14 +354,10 @@ public void toggleSearchThroughDescriptionButton() { private void setupForRegularSearch() { toolBar.jfxSearchField.setMode(FXSearchControlFieldMode.THEMA_TITEL); - - toolBar.btnSearchThroughDescription.setTooltip(tooltipSearchRegular); } private void setupForIrgendwoSearch() { toolBar.jfxSearchField.setMode(FXSearchControlFieldMode.IRGENDWO); - - toolBar.btnSearchThroughDescription.setTooltip(tooltipSearchIrgendwo); } public void updateThemaBox() { diff --git a/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml b/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml index 6305cb5b30..bee17bb595 100644 --- a/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml +++ b/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml @@ -1,11 +1,9 @@ - - @@ -14,10 +12,4 @@ - - - - - - From c3d5ba05b4ec98cb18f1efcdc7932da67915b8ee Mon Sep 17 00:00:00 2001 From: Christian F Date: Tue, 21 Jun 2022 13:17:22 +0200 Subject: [PATCH 072/422] - implement new search field with search history - History Storage NOT YET IMPLEMENTED! Former-commit-id: 20621367bf181aae81e5dc0a4380ef513bc8eb2f --- .../mediathek/gui/tabs/tab_film/GuiFilme.java | 305 ++++++++++++++---- .../tabs/tab_film/GuiFilmeModelHelper.java | 12 +- .../javafx/filterpanel/FXFilmToolBar.java | 35 -- .../javafx/filterpanel/FXSearchControl.kt | 80 ----- .../javafx/filterpanel/FilmActionPanel.java | 83 +---- .../javafx/filterpanel/SearchFieldData.java | 7 - .../res/programm/fxml/film_toolbar.fxml | 3 - 7 files changed, 266 insertions(+), 259 deletions(-) delete mode 100644 src/main/java/mediathek/javafx/filterpanel/FXSearchControl.kt delete mode 100644 src/main/java/mediathek/javafx/filterpanel/SearchFieldData.java diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index 4148428f81..e7e1689021 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -1,5 +1,8 @@ package mediathek.gui.tabs.tab_film; +import com.formdev.flatlaf.FlatClientProperties; +import com.formdev.flatlaf.extras.FlatSVGIcon; +import com.formdev.flatlaf.icons.FlatSearchWithHistoryIcon; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; @@ -35,6 +38,7 @@ import mediathek.javafx.buttonsPanel.ButtonsPanelController; import mediathek.javafx.descriptionPanel.DescriptionPanelController; import mediathek.javafx.filmtab.FilmTabInfoPane; +import mediathek.javafx.filterpanel.FXSearchControlFieldMode; import mediathek.javafx.filterpanel.FilmActionPanel; import mediathek.javafx.tool.JavaFxUtils; import mediathek.mainwindow.MediathekGui; @@ -52,10 +56,14 @@ import org.jetbrains.annotations.NotNull; import javax.swing.*; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; import javax.swing.table.TableModel; +import javax.swing.text.JTextComponent; import java.awt.*; import java.awt.event.*; import java.awt.print.PrinterException; +import java.beans.PropertyChangeSupport; import java.lang.reflect.InvocationTargetException; import java.net.URISyntaxException; import java.net.URLEncoder; @@ -64,6 +72,7 @@ import java.util.EnumSet; import java.util.List; import java.util.Optional; +import java.util.function.Consumer; public class GuiFilme extends AGuiTabPanel { @@ -165,40 +174,6 @@ public void actionPerformed(ActionEvent e) { } public ShowFilterDialogAction showFilterDialogAction = new ShowFilterDialogAction(); - public class ToggleSearchThroughDescriptionAction extends AbstractAction { - public ToggleSearchThroughDescriptionAction() { - boolean bSearchThroughDescription = ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.SEARCH_USE_FILM_DESCRIPTIONS, false); - setupToolTip(bSearchThroughDescription); - //putValue(Action.NAME, "Beschreibung"); - putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/envelope-open-text.svg")); - } - - private void setupToolTip(boolean active) { - if (active) - putValue(Action.SHORT_DESCRIPTION, "Suche in Beschreibung aktiviert"); - else - putValue(Action.SHORT_DESCRIPTION, "Suche in Beschreibung deaktiviert"); - } - - @Override - public void actionPerformed(ActionEvent e) { - JavaFxUtils.invokeInFxThreadAndWait(() -> filmActionPanel.toggleSearchThroughDescriptionButton()); - - boolean bSearchThroughDescription = ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.SEARCH_USE_FILM_DESCRIPTIONS, false); - JToggleButton source = (JToggleButton) e.getSource(); - source.setSelected(bSearchThroughDescription); - setupToolTip(bSearchThroughDescription); - } - } - protected ToggleSearchThroughDescriptionAction toggleSearchThroughDescriptionAction = new ToggleSearchThroughDescriptionAction(); - - private void addSearchThroughDescriptionToolBarButton() { - JToggleButton searchThroughDescriptionButton = new JToggleButton(toggleSearchThroughDescriptionAction); - boolean bSearchThroughDescription = ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.SEARCH_USE_FILM_DESCRIPTIONS, false); - searchThroughDescriptionButton.setSelected(bSearchThroughDescription); - toolBar.add(searchThroughDescriptionButton); - } - private void createToolBar() { add(toolBar, BorderLayout.NORTH); @@ -212,32 +187,252 @@ private void createToolBar() { toolBar.addSeparator(); toolBar.add(showFilterDialogAction); fxFilmActionPanel = new JFXPanel(); - fxFilmActionPanel.setMaximumSize(new Dimension(450,100)); + fxFilmActionPanel.setMaximumSize(new Dimension(100,100)); toolBar.addSeparator(); toolBar.add(fxFilmActionPanel); - toolBar.addSeparator(); - addSearchThroughDescriptionToolBarButton(); - - Daten.getInstance().getFilmeLaden().addAdListener( - new ListenerFilmeLaden() { - @Override - public void start(ListenerFilmeLadenEvent event) { - playFilmAction.setEnabled(false); - saveFilmAction.setEnabled(false); - bookmarkFilmAction.setEnabled(false); - showFilterDialogAction.setEnabled(false); + toolBar.add(new JLabel("Suche:")); + toolBar.add(searchField); + } + + @Handler + private void handleTableModelChange(TableModelChangeEvent e) { + if (e.active) { + try { + SwingUtilities.invokeAndWait(() -> playFilmAction.setEnabled(false)); + SwingUtilities.invokeAndWait(() -> saveFilmAction.setEnabled(false)); + SwingUtilities.invokeAndWait(() -> bookmarkFilmAction.setEnabled(false)); + SwingUtilities.invokeAndWait(() -> showFilterDialogAction.setEnabled(false)); + SwingUtilities.invokeAndWait(() -> fxFilmActionPanel.setEnabled(false)); + SwingUtilities.invokeAndWait(() -> searchField.setEnabled(false)); + } catch (InterruptedException | InvocationTargetException ex) { + throw new RuntimeException(ex); + } + } + else { + try { + SwingUtilities.invokeAndWait(() -> playFilmAction.setEnabled(true)); + SwingUtilities.invokeAndWait(() -> saveFilmAction.setEnabled(true)); + SwingUtilities.invokeAndWait(() -> bookmarkFilmAction.setEnabled(true)); + SwingUtilities.invokeAndWait(() -> showFilterDialogAction.setEnabled(true)); + SwingUtilities.invokeAndWait(() -> fxFilmActionPanel.setEnabled(true)); + SwingUtilities.invokeAndWait(() -> searchField.setEnabled(true)); + } catch (InterruptedException | InvocationTargetException ex) { + throw new RuntimeException(ex); + } + } + } + public class SearchField extends JTextField { + private final SearchHistoryButton searchHistoryButton = new SearchHistoryButton(); + private FXSearchControlFieldMode searchMode; + private static final String SEARCHMODE_PROPERTY_STRING = "searchMode"; + private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); + public void setSearchMode(FXSearchControlFieldMode mode) { + var oldValue = searchMode; + searchMode = mode; + pcs.firePropertyChange(SEARCHMODE_PROPERTY_STRING, oldValue, mode); + } + + public FXSearchControlFieldMode getSearchMode() { + return searchMode; + } + + public SearchField() { + super("", 20); + setMaximumSize(new Dimension(500, 100)); + + pcs.addPropertyChangeListener(SEARCHMODE_PROPERTY_STRING, evt -> setupHelperTexts()); + + //show clear icon when text is entered + putClientProperty(FlatClientProperties.TEXT_FIELD_SHOW_CLEAR_BUTTON, true); + //put placeholder text + boolean bSearchThroughDescription = ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.SEARCH_USE_FILM_DESCRIPTIONS, false); + if (bSearchThroughDescription) + setSearchMode(FXSearchControlFieldMode.IRGENDWO); + else + setSearchMode(FXSearchControlFieldMode.THEMA_TITEL); + + addActionListener(l -> { + //System.out.println("SEARCH FIRED FROM NEW SEARCH FIELD"); + String searchText = getText(); + if (!searchText.isEmpty()) { + var historyList = searchHistoryButton.getSearchHistory(); + if (!historyList.contains(searchText)) + historyList.add(searchText); + } + + loadTable(); + }); + + addKeyListener(new EscapeKeyAdapter()); + + installDocumentListener(); + + createTrailingComponents(); + + putClientProperty( FlatClientProperties.TEXT_FIELD_LEADING_COMPONENT, searchHistoryButton ); + putClientProperty("JTextField.clearCallback", (Consumer) textField -> clearSearchField()); + } + + private void installDocumentListener() { + getDocument().addDocumentListener(new DocumentListener() { + @Override + public void insertUpdate(DocumentEvent e) { + doCheck(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + doCheck(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + doCheck(); + } + + private void doCheck() { + var searchText = getText(); + checkPatternValidity(searchText); + setForegroundTextColor(searchText); + } + }); + } + + private final Color DEFAULT_FOREGROUND_COLOR = (Color)UIManager.get("TextField.foreground"); + private void setForegroundTextColor(String text) { + if (Filter.isPattern(text)) + setForeground(Color.BLUE); + else + setForeground(DEFAULT_FOREGROUND_COLOR); + } + private boolean isPatternValid(String text) { + return Filter.makePatternNoCache(text) != null; + } + private void checkPatternValidity(String text) { + if (Filter.isPattern(text)) + showErrorIndication(!isPatternValid(text)); + else + showErrorIndication(false); + } + + private void showErrorIndication(boolean hasError) { + if (hasError) + putClientProperty("JComponent.outline", "error"); + else + putClientProperty("JComponent.outline", ""); + } + /** + * Clear searchfield on escape key press. + */ + class EscapeKeyAdapter extends KeyAdapter { + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyChar() == KeyEvent.VK_ESCAPE) { + clearSearchField(); + } + } + } + private void clearSearchField() { + setText(""); + fireActionPerformed(); + } + + /** + * Sets tooltip and placeholder texts according to {@link SearchField#searchMode}. + */ + private void setupHelperTexts() { + String text; + if (searchMode == FXSearchControlFieldMode.THEMA_TITEL) + text = "Thema/Titel"; + else + text = "Thema/Titel/Beschreibung"; + putClientProperty( FlatClientProperties.PLACEHOLDER_TEXT, text); + + setToolTipText(text + " durchsuchen"); + } + + class ToggleSearchFieldToggleButton extends JToggleButton { + public ToggleSearchFieldToggleButton() { + FlatSVGIcon selectedIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/envelope-open-text.svg"); + selectedIcon.setColorFilter(new FlatSVGIcon.ColorFilter(color -> Color.BLUE)); + FlatSVGIcon normalIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/envelope-open-text.svg"); + normalIcon.setColorFilter(new FlatSVGIcon.ColorFilter(color -> Color.GRAY)); + setIcon(normalIcon); + setSelectedIcon(selectedIcon); + + boolean bSearchThroughDescription = ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.SEARCH_USE_FILM_DESCRIPTIONS, false); + setSelected(bSearchThroughDescription); + setupToolTip(bSearchThroughDescription); + + addActionListener(l -> { + switch (getSearchMode()) { + case IRGENDWO -> { + setSearchMode(FXSearchControlFieldMode.THEMA_TITEL); + setupToolTip(false); + } + case THEMA_TITEL -> { + setSearchMode(FXSearchControlFieldMode.IRGENDWO); + setupToolTip(true); + } } + //update config + ApplicationConfiguration.getConfiguration().setProperty(ApplicationConfiguration.SEARCH_USE_FILM_DESCRIPTIONS, getSearchMode() == FXSearchControlFieldMode.IRGENDWO); + + loadTable(); + }); + } + + private void setupToolTip(boolean active) { + if (active) + setToolTipText("Suche in Beschreibung aktiviert"); + else + setToolTipText("Suche in Beschreibung deaktiviert"); + } + } + private void createTrailingComponents() { + JToolBar searchToolbar = new JToolBar(); + searchToolbar.addSeparator(); + + searchToolbar.add(new ToggleSearchFieldToggleButton()); + putClientProperty(FlatClientProperties.TEXT_FIELD_TRAILING_COMPONENT, searchToolbar); + } - @Override - public void fertig(ListenerFilmeLadenEvent event) { - playFilmAction.setEnabled(true); - saveFilmAction.setEnabled(true); - bookmarkFilmAction.setEnabled(true); - showFilterDialogAction.setEnabled(true); + public class SearchHistoryButton extends JButton { + private final List historyList = new ArrayList<>(); + + private final JMenuItem miClearHistory = new JMenuItem("(Historie löschen)"); + + public SearchHistoryButton() { + super(new FlatSearchWithHistoryIcon( true )); + setToolTipText("Vorherige Suchen"); + + miClearHistory.addActionListener(l -> historyList.clear()); + addActionListener(l -> { + JPopupMenu popupMenu = new JPopupMenu(); + popupMenu.add(miClearHistory); + if (!historyList.isEmpty()) { + popupMenu.addSeparator(); + for (var item : historyList) { + JMenuItem historyItem = new JMenuItem(item); + historyItem.addActionListener(li -> { + searchField.setText(item); + searchField.fireActionPerformed(); + }); + popupMenu.add(historyItem); + } } + popupMenu.show( this, 0, this.getHeight() ); }); + } + + public List getSearchHistory() { + return historyList; + } + } } + protected SearchField searchField = new SearchField(); + @Override public void tabelleSpeichern() { if (tabelle != null) { @@ -330,8 +525,6 @@ public void installViewMenuEntry(JMenu jMenuAnsicht) { @Override public void installMenuEntries(JMenu menu) { - KeyStroke keyStroke; - if (!SystemUtils.IS_OS_MAC_OSX) cbkShowDescription.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F10, 0)); @@ -358,7 +551,7 @@ public void installMenuEntries(JMenu menu) { } private void setupFilmActionPanel() { - filmActionPanel = new FilmActionPanel(reloadTableDataTransition); + filmActionPanel = new FilmActionPanel(); JavaFxUtils.invokeInFxThreadAndWait( () -> fxFilmActionPanel.setScene(filmActionPanel.getFilmActionPanelScene())); } @@ -860,7 +1053,7 @@ private void loadTable() { var decoratedPool = daten.getDecoratedPool(); modelFuture = decoratedPool.submit(() -> { - final GuiFilmeModelHelper helper = new GuiFilmeModelHelper(filmActionPanel, historyController); + final GuiFilmeModelHelper helper = new GuiFilmeModelHelper(filmActionPanel, historyController, searchField); //Thread.sleep(5000); return helper.getFilteredTableModel(); }); diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilmeModelHelper.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilmeModelHelper.java index 4c35361bdc..4ea0bb98c6 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilmeModelHelper.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilmeModelHelper.java @@ -9,6 +9,7 @@ import mediathek.gui.tabs.tab_film.searchfilters.FinalStageFilterNoPatternWithDescription; import mediathek.gui.tabs.tab_film.searchfilters.FinalStagePatternFilter; import mediathek.gui.tabs.tab_film.searchfilters.FinalStagePatternFilterWithDescription; +import mediathek.javafx.filterpanel.FXSearchControlFieldMode; import mediathek.javafx.filterpanel.FilmActionPanel; import mediathek.javafx.filterpanel.FilmLengthSlider; import mediathek.tool.Filter; @@ -42,11 +43,14 @@ public class GuiFilmeModelHelper { private String[] arrIrgendwo; private long minLengthInSeconds; private long maxLengthInSeconds; + private final GuiFilme.SearchField newSearchField; public GuiFilmeModelHelper(@NotNull FilmActionPanel filmActionPanel, - @NotNull SeenHistoryController historyController) { + @NotNull SeenHistoryController historyController, + @NotNull GuiFilme.SearchField newSearchField) { this.filmActionPanel = filmActionPanel; this.historyController = historyController; + this.newSearchField = newSearchField; listeFilme = Daten.getInstance().getListeFilmeNachBlackList(); } @@ -63,7 +67,7 @@ private String getFilterThema() { private String[] evaluateThemaTitel() { String[] arrThemaTitel; - final String filterThemaTitel = filmActionPanel.roSearchStringProperty.getValueSafe(); + final String filterThemaTitel = newSearchField.getText(); if (Filter.isPattern(filterThemaTitel)) { arrThemaTitel = new String[]{filterThemaTitel}; } else { @@ -79,7 +83,7 @@ private String[] evaluateThemaTitel() { private boolean noFiltersAreSet() { return filmActionPanel.getViewSettingsPane().senderCheckList.getCheckModel().isEmpty() && getFilterThema().isEmpty() - && filmActionPanel.roSearchStringProperty.getValueSafe().isEmpty() + && newSearchField.getText().isEmpty() && ((int) filmActionPanel.filmLengthSlider.getLowValue() == 0) && ((int) filmActionPanel.filmLengthSlider.getHighValue() == FilmLengthSlider.UNLIMITED_VALUE) && !filmActionPanel.dontShowAbos.getValue() @@ -105,7 +109,7 @@ private void updateFilterVars() { dontShowTrailers = filmActionPanel.dontShowTrailers.getValue(); dontShowGebaerdensprache = filmActionPanel.dontShowSignLanguage.getValue(); dontShowAudioVersions = filmActionPanel.dontShowAudioVersions.getValue(); - searchThroughDescriptions = filmActionPanel.searchThroughDescriptionProperty.getValue(); + searchThroughDescriptions = newSearchField.getSearchMode() == FXSearchControlFieldMode.IRGENDWO; arrIrgendwo = evaluateThemaTitel(); } diff --git a/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java b/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java index 175970be81..e86beafbaa 100644 --- a/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java +++ b/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java @@ -1,6 +1,5 @@ package mediathek.javafx.filterpanel; -import javafx.application.Platform; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.fxml.FXML; @@ -8,11 +7,8 @@ import javafx.scene.control.ComboBox; import javafx.scene.control.SingleSelectionModel; import javafx.scene.layout.HBox; -import mediathek.gui.messages.TableModelChangeEvent; import mediathek.tool.FilterConfiguration; import mediathek.tool.FilterDTO; -import mediathek.tool.MessageBus; -import net.engio.mbassy.listener.Handler; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -20,9 +16,6 @@ import java.net.URL; public class FXFilmToolBar extends HBox { - @FXML - FXSearchControl jfxSearchField; - @FXML ComboBox filterSelect; public FXFilmToolBar() { @@ -33,40 +26,12 @@ public FXFilmToolBar() { fxmlLoader.setController(this); fxmlLoader.load(); setUpFilterSelect(); - - MessageBus.getMessageBus().subscribe(this); } catch (IOException e) { Logger logger = LogManager.getLogger(FXFilmToolBar.class); logger.error("Failed to load FXML!"); } } - /** - * maintain search field data values - */ - private SearchFieldData searchFieldData; - - @Handler - private void handleTableModelChangeEvent(TableModelChangeEvent e) { - if (e.active) { - Platform.runLater(() -> { - searchFieldData = new SearchFieldData(jfxSearchField.isFocused(), jfxSearchField.getCaretPosition()); - setDisable(true); - }); - } - else { - Platform.runLater(() -> { - setDisable(false); - if (searchFieldData.focused()) { - jfxSearchField.requestFocus(); - if (!jfxSearchField.getText().isEmpty()) { - jfxSearchField.positionCaret(searchFieldData.caretPosition()); - } - } - }); - } - } - private void setUpFilterSelect() { FilterConfiguration filterConfig = new FilterConfiguration(); ObservableList availableFilters = diff --git a/src/main/java/mediathek/javafx/filterpanel/FXSearchControl.kt b/src/main/java/mediathek/javafx/filterpanel/FXSearchControl.kt deleted file mode 100644 index b42235b1c1..0000000000 --- a/src/main/java/mediathek/javafx/filterpanel/FXSearchControl.kt +++ /dev/null @@ -1,80 +0,0 @@ -package mediathek.javafx.filterpanel - -import javafx.beans.value.ObservableValue -import javafx.event.EventHandler -import javafx.scene.Cursor -import javafx.scene.control.Tooltip -import javafx.scene.input.KeyCode -import javafx.scene.input.KeyEvent -import mediathek.tool.Filter -import org.controlsfx.control.textfield.CustomTextField -import org.controlsfx.glyphfont.FontAwesome -import org.controlsfx.glyphfont.GlyphFontRegistry - -class FXSearchControl : CustomTextField() { - - companion object { - private const val PROMPT_THEMA_TITEL = "Thema/Titel" - private const val PROMPT_IRGENDWO = "Thema/Titel/Beschreibung" - } - - private val themaTitelTooltip = Tooltip("Thema/Titel durchsuchen") - private val irgendwoTooltip = Tooltip("Thema/Titel/Beschreibung durchsuchen") - - fun setMode(mode: FXSearchControlFieldMode) { - when (mode) { - FXSearchControlFieldMode.THEMA_TITEL -> { - tooltip = themaTitelTooltip - promptText = PROMPT_THEMA_TITEL - } - FXSearchControlFieldMode.IRGENDWO -> { - tooltip = irgendwoTooltip - promptText = PROMPT_IRGENDWO - } - } - } - - init { - val fontAwesome = GlyphFontRegistry.font("FontAwesome") - left = fontAwesome.create(FontAwesome.Glyph.SEARCH) - right = fontAwesome.create(FontAwesome.Glyph.REMOVE) - onKeyPressed = EventHandler { event: KeyEvent -> - if (event.code == KeyCode.ESCAPE) { - if (text.isNotEmpty()) - text = "" - event.consume() - } - } - - val rightNode = right - rightNode.onMouseClicked = EventHandler { text = "" } - rightNode.cursor = Cursor.DEFAULT - rightNode.isVisible = false - - val textProperty = textProperty() - textProperty.addListener { _: ObservableValue?, _: String?, newValue: String -> - rightNode.isVisible = newValue.isNotEmpty() - } - - prefWidth = 350.0 - minWidth = 350.0 - maxWidth = 350.0 - - textProperty.addListener { _, _, newValue -> checkPatternValidity(newValue) } - } - - private fun checkPatternValidity(text: String) { - style = if (Filter.isPattern(text)) { - if (Filter.makePatternNoCache(text) == null) { - // invalid pattern - "-fx-text-fill: red" - } else { - //valid pattern - "-fx-text-fill: blue" - } - } else { - // regular search text, reset to default style - null - } - } -} \ No newline at end of file diff --git a/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java b/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java index 1e50978c3c..4d8f0fc323 100644 --- a/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java +++ b/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java @@ -1,14 +1,15 @@ package mediathek.javafx.filterpanel; import impl.org.controlsfx.autocompletion.SuggestionProvider; -import javafx.animation.PauseTransition; import javafx.application.Platform; -import javafx.beans.property.*; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.ReadOnlyObjectProperty; +import javafx.beans.property.ReadOnlyStringWrapper; +import javafx.beans.property.SimpleBooleanProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.scene.Scene; import javafx.scene.control.ComboBox; -import javafx.util.Duration; import javafx.util.StringConverter; import mediathek.config.Daten; import mediathek.filmeSuchen.ListenerFilmeLaden; @@ -17,7 +18,10 @@ import mediathek.gui.messages.FilmListWriteStartEvent; import mediathek.gui.messages.FilmListWriteStopEvent; import mediathek.mainwindow.MediathekGui; -import mediathek.tool.*; +import mediathek.tool.FilterConfiguration; +import mediathek.tool.FilterDTO; +import mediathek.tool.GermanStringSorter; +import mediathek.tool.MessageBus; import net.engio.mbassy.listener.Handler; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -35,7 +39,6 @@ */ public class FilmActionPanel { private static final Logger logger = LogManager.getLogger(); - private final PauseTransition finalActionTrans = new PauseTransition(Duration.seconds(1)); private final FilterConfiguration filterConfig; private final ObservableList availableFilters; public ReadOnlyStringWrapper roSearchStringProperty = new ReadOnlyStringWrapper(); @@ -60,11 +63,9 @@ public class FilmActionPanel { private FXFilmToolBar toolBar; private CommonViewSettingsPane viewSettingsPane; - private final PauseTransition reloadTableDataTransition; - public FilmActionPanel(PauseTransition reloadTableDataTransition) { + public FilmActionPanel() { this.filterConfig = new FilterConfiguration(); - this.reloadTableDataTransition = reloadTableDataTransition; setupViewSettingsPane(); setupDeleteFilterButton(); @@ -298,68 +299,6 @@ private void handleFilmlistWriteStopEvent(FilmListWriteStopEvent e) { SwingUtilities.invokeLater(() -> MediathekGui.ui().loadFilmListAction.setEnabled(true)); } - private void searchFieldFinalAction() { - final var text = toolBar.jfxSearchField.getText(); - boolean invokeTableFiltering = false; - - if (Filter.isPattern(text)) { - //only invoke filtering if we have regexp pattern and the pattern is valid - if (Filter.makePatternNoCache(text) != null) { - invokeTableFiltering = true; - } - } else { - invokeTableFiltering = true; - } - - if (invokeTableFiltering) - reloadTableDataTransition.playFromStart(); - } - - private void setupSearchField() { - toolBar.jfxSearchField.setMode(FXSearchControlFieldMode.THEMA_TITEL); - - final StringProperty textProperty = toolBar.jfxSearchField.textProperty(); - roSearchStringProperty.bind(textProperty); - - finalActionTrans.setOnFinished(e -> searchFieldFinalAction()); - textProperty.addListener((observable, oldValue, newValue) -> finalActionTrans.playFromStart()); - } - - private void setupSearchThroughDescriptionButton() { - final boolean enabled = - ApplicationConfiguration.getConfiguration() - .getBoolean(ApplicationConfiguration.SEARCH_USE_FILM_DESCRIPTIONS, false); - if (enabled) - setupForIrgendwoSearch(); - else - setupForRegularSearch(); - - searchThroughDescriptionProperty.setValue(enabled); - } - - public void toggleSearchThroughDescriptionButton() { - boolean bSearchThroughDescription = - ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.SEARCH_USE_FILM_DESCRIPTIONS, false); - bSearchThroughDescription = !bSearchThroughDescription; - ApplicationConfiguration.getConfiguration() - .setProperty(ApplicationConfiguration.SEARCH_USE_FILM_DESCRIPTIONS, bSearchThroughDescription); - - if (bSearchThroughDescription) - setupForIrgendwoSearch(); - else - setupForRegularSearch(); - - searchThroughDescriptionProperty.setValue(bSearchThroughDescription); - } - - private void setupForRegularSearch() { - toolBar.jfxSearchField.setMode(FXSearchControlFieldMode.THEMA_TITEL); - } - - private void setupForIrgendwoSearch() { - toolBar.jfxSearchField.setMode(FXSearchControlFieldMode.IRGENDWO); - } - public void updateThemaBox() { final var items = themaBox.getItems(); items.clear(); @@ -394,10 +333,6 @@ public void updateThemaBox() { public Scene getFilmActionPanelScene() { toolBar = new FXFilmToolBar(); - setupSearchField(); - - setupSearchThroughDescriptionButton(); - Daten.getInstance().getFilmeLaden().addAdListener( new ListenerFilmeLaden() { @Override diff --git a/src/main/java/mediathek/javafx/filterpanel/SearchFieldData.java b/src/main/java/mediathek/javafx/filterpanel/SearchFieldData.java deleted file mode 100644 index 426f700297..0000000000 --- a/src/main/java/mediathek/javafx/filterpanel/SearchFieldData.java +++ /dev/null @@ -1,7 +0,0 @@ -package mediathek.javafx.filterpanel; - -/** - * Stores necessary data related to search fields between table filter updates. - */ -record SearchFieldData(boolean focused, int caretPosition) { -} diff --git a/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml b/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml index bee17bb595..b9ec9238fa 100644 --- a/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml +++ b/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml @@ -3,13 +3,10 @@ - - - From 5a9633b6031ec16e50e49f737642d9662b58356b Mon Sep 17 00:00:00 2001 From: Christian F Date: Tue, 21 Jun 2022 13:20:23 +0200 Subject: [PATCH 073/422] - document changes Former-commit-id: 9132307789485866edf76b4793990d29365b9e51 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb1fb2de86..452307f699 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - **BUGFIX:** Manchmal inkorrekte Darstellung des Bookmark-Status im Film-Tab wurde korrigiert. - **FEATURE:** Die ToolBar im Download-Tab kann nun zum Schweben abgetrennt werden. - **FEATURE:** Icons wurden durch hochauflösende Vektorvarianten ersetzt. +- **FEATURE:** Neues Suchfeld inklusive Suchhistorie. - Im Download-Tab wurde der "Filter anzeigen/ausblenden"-Button an die anderen ToolBar-Buttons angegliedert. **13.9.1** From e79c1a2ee55c389fac033b9422b4e7c508e74e40 Mon Sep 17 00:00:00 2001 From: Christian F Date: Tue, 21 Jun 2022 13:22:05 +0200 Subject: [PATCH 074/422] - document changes Former-commit-id: 8fbbdea00f637848fabe6bf1a685c8872c872718 --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 452307f699..62b893d658 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,9 @@ - **BUGFIX:** *"Filme/Blacklist öffnen..."* Eintrag wurde in *"Filme/Blacklist bearbeiten..."* umbenannt und die Icons vereinheitlicht. - **BUGFIX:** Manchmal inkorrekte Darstellung des Bookmark-Status im Film-Tab wurde korrigiert. -- **FEATURE:** Die ToolBar im Download-Tab kann nun zum Schweben abgetrennt werden. +- **FEATURE:** Die Toolbars können nun zum Schweben in allen Tabs abgetrennt werden. - **FEATURE:** Icons wurden durch hochauflösende Vektorvarianten ersetzt. - **FEATURE:** Neues Suchfeld inklusive Suchhistorie. -- Im Download-Tab wurde der "Filter anzeigen/ausblenden"-Button an die anderen ToolBar-Buttons angegliedert. **13.9.1** From b74c982f256b19c69511db336dd206f3a8440404 Mon Sep 17 00:00:00 2001 From: Christian F Date: Tue, 21 Jun 2022 15:25:57 +0200 Subject: [PATCH 075/422] - implement search history load/save Former-commit-id: 8d2a7b534a429235c8b590c6e2e9ae5f92c0361f --- .../mediathek/gui/tabs/tab_film/GuiFilme.java | 525 ++++++++++-------- 1 file changed, 281 insertions(+), 244 deletions(-) diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index e7e1689021..b8a3e9ebab 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -1,5 +1,8 @@ package mediathek.gui.tabs.tab_film; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; import com.formdev.flatlaf.FlatClientProperties; import com.formdev.flatlaf.extras.FlatSVGIcon; import com.formdev.flatlaf.icons.FlatSearchWithHistoryIcon; @@ -68,10 +71,8 @@ import java.net.URISyntaxException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.EnumSet; import java.util.List; -import java.util.Optional; +import java.util.*; import java.util.function.Consumer; public class GuiFilme extends AGuiTabPanel { @@ -103,10 +104,14 @@ public class GuiFilme extends AGuiTabPanel { private final JFXPanel fxDescriptionPanel = new JFXPanel(); private final JFXPanel fxPsetButtonsPanel = new JFXPanel(); private final SeenHistoryController historyController = new SeenHistoryController(); + private final JToolBar toolBar = new JToolBar(); /** * The JavaFx Film action popup panel. */ public FilmActionPanel filmActionPanel; + public ShowFilterDialogAction showFilterDialogAction = new ShowFilterDialogAction(); + protected SearchField searchField = new SearchField(); + protected PauseTransition reloadTableDataTransition = new PauseTransition(Duration.millis(250d)); private Optional bookmarkWindowController = Optional.empty(); /** * The swing helper panel FilmAction bar. @@ -121,7 +126,10 @@ public class GuiFilme extends AGuiTabPanel { */ private ButtonsPanelController psetController; private MVFilmTable tabelle; - + /** + * We perform model filtering in the background the keep UI thread alive. + */ + private ListenableFuture modelFuture; public GuiFilme(Daten aDaten, MediathekGui mediathekGui) { super(); daten = aDaten; @@ -154,26 +162,6 @@ public GuiFilme(Daten aDaten, MediathekGui mediathekGui) { setupActionListeners(); } - public class ShowFilterDialogAction extends AbstractAction { - public ShowFilterDialogAction() { - putValue(Action.NAME, "Filterdialog anzeigen"); - putValue(Action.SHORT_DESCRIPTION, "Filter anzeigen"); - putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/filter.svg")); - putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_F12, 0)); - } - - @Override - public void actionPerformed(ActionEvent e) { - var dlg = filmActionPanel.filterDialog; - if (dlg != null) { - if (!dlg.isVisible()) { - dlg.setVisible(true); - } - } - } - } - public ShowFilterDialogAction showFilterDialogAction = new ShowFilterDialogAction(); - private void createToolBar() { add(toolBar, BorderLayout.NORTH); @@ -221,217 +209,6 @@ private void handleTableModelChange(TableModelChangeEvent e) { } } } - public class SearchField extends JTextField { - private final SearchHistoryButton searchHistoryButton = new SearchHistoryButton(); - private FXSearchControlFieldMode searchMode; - private static final String SEARCHMODE_PROPERTY_STRING = "searchMode"; - private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); - public void setSearchMode(FXSearchControlFieldMode mode) { - var oldValue = searchMode; - searchMode = mode; - pcs.firePropertyChange(SEARCHMODE_PROPERTY_STRING, oldValue, mode); - } - - public FXSearchControlFieldMode getSearchMode() { - return searchMode; - } - - public SearchField() { - super("", 20); - setMaximumSize(new Dimension(500, 100)); - - pcs.addPropertyChangeListener(SEARCHMODE_PROPERTY_STRING, evt -> setupHelperTexts()); - - //show clear icon when text is entered - putClientProperty(FlatClientProperties.TEXT_FIELD_SHOW_CLEAR_BUTTON, true); - //put placeholder text - boolean bSearchThroughDescription = ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.SEARCH_USE_FILM_DESCRIPTIONS, false); - if (bSearchThroughDescription) - setSearchMode(FXSearchControlFieldMode.IRGENDWO); - else - setSearchMode(FXSearchControlFieldMode.THEMA_TITEL); - - addActionListener(l -> { - //System.out.println("SEARCH FIRED FROM NEW SEARCH FIELD"); - String searchText = getText(); - if (!searchText.isEmpty()) { - var historyList = searchHistoryButton.getSearchHistory(); - if (!historyList.contains(searchText)) - historyList.add(searchText); - } - - loadTable(); - }); - - addKeyListener(new EscapeKeyAdapter()); - - installDocumentListener(); - - createTrailingComponents(); - - putClientProperty( FlatClientProperties.TEXT_FIELD_LEADING_COMPONENT, searchHistoryButton ); - putClientProperty("JTextField.clearCallback", (Consumer) textField -> clearSearchField()); - } - - private void installDocumentListener() { - getDocument().addDocumentListener(new DocumentListener() { - @Override - public void insertUpdate(DocumentEvent e) { - doCheck(); - } - - @Override - public void removeUpdate(DocumentEvent e) { - doCheck(); - } - - @Override - public void changedUpdate(DocumentEvent e) { - doCheck(); - } - - private void doCheck() { - var searchText = getText(); - checkPatternValidity(searchText); - setForegroundTextColor(searchText); - } - }); - } - - private final Color DEFAULT_FOREGROUND_COLOR = (Color)UIManager.get("TextField.foreground"); - private void setForegroundTextColor(String text) { - if (Filter.isPattern(text)) - setForeground(Color.BLUE); - else - setForeground(DEFAULT_FOREGROUND_COLOR); - } - private boolean isPatternValid(String text) { - return Filter.makePatternNoCache(text) != null; - } - private void checkPatternValidity(String text) { - if (Filter.isPattern(text)) - showErrorIndication(!isPatternValid(text)); - else - showErrorIndication(false); - } - - private void showErrorIndication(boolean hasError) { - if (hasError) - putClientProperty("JComponent.outline", "error"); - else - putClientProperty("JComponent.outline", ""); - } - /** - * Clear searchfield on escape key press. - */ - class EscapeKeyAdapter extends KeyAdapter { - @Override - public void keyPressed(KeyEvent e) { - if (e.getKeyChar() == KeyEvent.VK_ESCAPE) { - clearSearchField(); - } - } - } - private void clearSearchField() { - setText(""); - fireActionPerformed(); - } - - /** - * Sets tooltip and placeholder texts according to {@link SearchField#searchMode}. - */ - private void setupHelperTexts() { - String text; - if (searchMode == FXSearchControlFieldMode.THEMA_TITEL) - text = "Thema/Titel"; - else - text = "Thema/Titel/Beschreibung"; - putClientProperty( FlatClientProperties.PLACEHOLDER_TEXT, text); - - setToolTipText(text + " durchsuchen"); - } - - class ToggleSearchFieldToggleButton extends JToggleButton { - public ToggleSearchFieldToggleButton() { - FlatSVGIcon selectedIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/envelope-open-text.svg"); - selectedIcon.setColorFilter(new FlatSVGIcon.ColorFilter(color -> Color.BLUE)); - FlatSVGIcon normalIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/envelope-open-text.svg"); - normalIcon.setColorFilter(new FlatSVGIcon.ColorFilter(color -> Color.GRAY)); - setIcon(normalIcon); - setSelectedIcon(selectedIcon); - - boolean bSearchThroughDescription = ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.SEARCH_USE_FILM_DESCRIPTIONS, false); - setSelected(bSearchThroughDescription); - setupToolTip(bSearchThroughDescription); - - addActionListener(l -> { - switch (getSearchMode()) { - case IRGENDWO -> { - setSearchMode(FXSearchControlFieldMode.THEMA_TITEL); - setupToolTip(false); - } - case THEMA_TITEL -> { - setSearchMode(FXSearchControlFieldMode.IRGENDWO); - setupToolTip(true); - } - } - //update config - ApplicationConfiguration.getConfiguration().setProperty(ApplicationConfiguration.SEARCH_USE_FILM_DESCRIPTIONS, getSearchMode() == FXSearchControlFieldMode.IRGENDWO); - - loadTable(); - }); - } - - private void setupToolTip(boolean active) { - if (active) - setToolTipText("Suche in Beschreibung aktiviert"); - else - setToolTipText("Suche in Beschreibung deaktiviert"); - } - } - private void createTrailingComponents() { - JToolBar searchToolbar = new JToolBar(); - searchToolbar.addSeparator(); - - searchToolbar.add(new ToggleSearchFieldToggleButton()); - putClientProperty(FlatClientProperties.TEXT_FIELD_TRAILING_COMPONENT, searchToolbar); - } - - public class SearchHistoryButton extends JButton { - private final List historyList = new ArrayList<>(); - - private final JMenuItem miClearHistory = new JMenuItem("(Historie löschen)"); - - public SearchHistoryButton() { - super(new FlatSearchWithHistoryIcon( true )); - setToolTipText("Vorherige Suchen"); - - miClearHistory.addActionListener(l -> historyList.clear()); - addActionListener(l -> { - JPopupMenu popupMenu = new JPopupMenu(); - popupMenu.add(miClearHistory); - if (!historyList.isEmpty()) { - popupMenu.addSeparator(); - for (var item : historyList) { - JMenuItem historyItem = new JMenuItem(item); - historyItem.addActionListener(li -> { - searchField.setText(item); - searchField.fireActionPerformed(); - }); - popupMenu.add(historyItem); - } - } - popupMenu.show( this, 0, this.getHeight() ); - }); - } - - public List getSearchHistory() { - return historyList; - } - } - } - - protected SearchField searchField = new SearchField(); @Override public void tabelleSpeichern() { @@ -445,8 +222,6 @@ private void setupFilmListTable() { filmListScrollPane.setViewportView(tabelle); } - private final JToolBar toolBar = new JToolBar(); - /** * Update the property with the current number of selected entries from the JTable. */ @@ -943,8 +718,6 @@ private void setInfoStatusbar() { MessageBus.getMessageBus().publishAsync(new UpdateStatusBarLeftDisplayEvent()); } - protected PauseTransition reloadTableDataTransition = new PauseTransition(Duration.millis(250d)); - private void setupActionListeners() { Platform.runLater(() -> { reloadTableDataTransition.setOnFinished(e -> { @@ -1032,11 +805,6 @@ private void setupZeitraumListener() { filmActionPanel.zeitraumProperty.addListener((observable, oldValue, newValue) -> trans.playFromStart()); } - /** - * We perform model filtering in the background the keep UI thread alive. - */ - private ListenableFuture modelFuture; - private void loadTable() { if (modelFuture != null) { if (!modelFuture.isDone()) { @@ -1087,6 +855,275 @@ public void onFailure(@NotNull Throwable thrown) { decoratedPool); } + public class ShowFilterDialogAction extends AbstractAction { + public ShowFilterDialogAction() { + putValue(Action.NAME, "Filterdialog anzeigen"); + putValue(Action.SHORT_DESCRIPTION, "Filter anzeigen"); + putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/filter.svg")); + putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_F12, 0)); + } + + @Override + public void actionPerformed(ActionEvent e) { + var dlg = filmActionPanel.filterDialog; + if (dlg != null) { + if (!dlg.isVisible()) { + dlg.setVisible(true); + } + } + } + } + + public class SearchField extends JTextField { + private static final String SEARCHMODE_PROPERTY_STRING = "searchMode"; + private final SearchHistoryButton searchHistoryButton = new SearchHistoryButton(); + private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); + private final Color DEFAULT_FOREGROUND_COLOR = (Color)UIManager.get("TextField.foreground"); + private FXSearchControlFieldMode searchMode; + + public SearchField() { + super("", 20); + setMaximumSize(new Dimension(500, 100)); + + pcs.addPropertyChangeListener(SEARCHMODE_PROPERTY_STRING, evt -> setupHelperTexts()); + + //show clear icon when text is entered + putClientProperty(FlatClientProperties.TEXT_FIELD_SHOW_CLEAR_BUTTON, true); + //put placeholder text + boolean bSearchThroughDescription = ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.SEARCH_USE_FILM_DESCRIPTIONS, false); + if (bSearchThroughDescription) + setSearchMode(FXSearchControlFieldMode.IRGENDWO); + else + setSearchMode(FXSearchControlFieldMode.THEMA_TITEL); + + addActionListener(l -> { + //System.out.println("SEARCH FIRED FROM NEW SEARCH FIELD"); + String searchText = getText(); + if (!searchText.isEmpty()) { + searchHistoryButton.addHistoryEntry(searchText); + } + + loadTable(); + }); + + addKeyListener(new EscapeKeyAdapter()); + + installDocumentListener(); + + createTrailingComponents(); + + putClientProperty( FlatClientProperties.TEXT_FIELD_LEADING_COMPONENT, searchHistoryButton ); + putClientProperty("JTextField.clearCallback", (Consumer) textField -> clearSearchField()); + } + + public FXSearchControlFieldMode getSearchMode() { + return searchMode; + } + + public void setSearchMode(FXSearchControlFieldMode mode) { + var oldValue = searchMode; + searchMode = mode; + pcs.firePropertyChange(SEARCHMODE_PROPERTY_STRING, oldValue, mode); + } + + private void installDocumentListener() { + getDocument().addDocumentListener(new DocumentListener() { + @Override + public void insertUpdate(DocumentEvent e) { + doCheck(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + doCheck(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + doCheck(); + } + + private void doCheck() { + var searchText = getText(); + checkPatternValidity(searchText); + setForegroundTextColor(searchText); + } + }); + } + + private void setForegroundTextColor(String text) { + if (Filter.isPattern(text)) + setForeground(Color.BLUE); + else + setForeground(DEFAULT_FOREGROUND_COLOR); + } + private boolean isPatternValid(String text) { + return Filter.makePatternNoCache(text) != null; + } + private void checkPatternValidity(String text) { + if (Filter.isPattern(text)) + showErrorIndication(!isPatternValid(text)); + else + showErrorIndication(false); + } + + private void showErrorIndication(boolean hasError) { + if (hasError) + putClientProperty("JComponent.outline", "error"); + else + putClientProperty("JComponent.outline", ""); + } + + private void clearSearchField() { + setText(""); + fireActionPerformed(); + } + + /** + * Sets tooltip and placeholder texts according to {@link SearchField#searchMode}. + */ + private void setupHelperTexts() { + String text; + if (searchMode == FXSearchControlFieldMode.THEMA_TITEL) + text = "Thema/Titel"; + else + text = "Thema/Titel/Beschreibung"; + putClientProperty( FlatClientProperties.PLACEHOLDER_TEXT, text); + + setToolTipText(text + " durchsuchen"); + } + + private void createTrailingComponents() { + JToolBar searchToolbar = new JToolBar(); + searchToolbar.addSeparator(); + + searchToolbar.add(new ToggleSearchFieldToggleButton()); + putClientProperty(FlatClientProperties.TEXT_FIELD_TRAILING_COMPONENT, searchToolbar); + } + + /** + * Clear searchfield on escape key press. + */ + class EscapeKeyAdapter extends KeyAdapter { + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyChar() == KeyEvent.VK_ESCAPE) { + clearSearchField(); + } + } + } + + class ToggleSearchFieldToggleButton extends JToggleButton { + public ToggleSearchFieldToggleButton() { + FlatSVGIcon selectedIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/envelope-open-text.svg"); + selectedIcon.setColorFilter(new FlatSVGIcon.ColorFilter(color -> Color.BLUE)); + FlatSVGIcon normalIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/envelope-open-text.svg"); + normalIcon.setColorFilter(new FlatSVGIcon.ColorFilter(color -> Color.GRAY)); + setIcon(normalIcon); + setSelectedIcon(selectedIcon); + + boolean bSearchThroughDescription = ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.SEARCH_USE_FILM_DESCRIPTIONS, false); + setSelected(bSearchThroughDescription); + setupToolTip(bSearchThroughDescription); + + addActionListener(l -> { + switch (getSearchMode()) { + case IRGENDWO -> { + setSearchMode(FXSearchControlFieldMode.THEMA_TITEL); + setupToolTip(false); + } + case THEMA_TITEL -> { + setSearchMode(FXSearchControlFieldMode.IRGENDWO); + setupToolTip(true); + } + } + //update config + ApplicationConfiguration.getConfiguration().setProperty(ApplicationConfiguration.SEARCH_USE_FILM_DESCRIPTIONS, getSearchMode() == FXSearchControlFieldMode.IRGENDWO); + + loadTable(); + }); + } + + private void setupToolTip(boolean active) { + if (active) + setToolTipText("Suche in Beschreibung aktiviert"); + else + setToolTipText("Suche in Beschreibung deaktiviert"); + } + } + + public class SearchHistoryButton extends JButton { + private static final Logger logger = LogManager.getLogger(); + private static final String SEARCH_HISTORY_CONFIG = "search.history.items"; + private final List historyList = new ArrayList<>(); + private final JMenuItem miClearHistory = new JMenuItem("Historie löschen"); + + public SearchHistoryButton() { + super(new FlatSearchWithHistoryIcon( true )); + setToolTipText("Vorherige Suchen"); + + miClearHistory.addActionListener(l -> { + historyList.clear(); + saveHistory(); + }); + addActionListener(l -> { + JPopupMenu popupMenu = new JPopupMenu(); + popupMenu.add(miClearHistory); + if (!historyList.isEmpty()) { + popupMenu.addSeparator(); + for (var item : historyList) { + JMenuItem historyItem = new JMenuItem(item); + historyItem.addActionListener(li -> { + searchField.setText(item); + searchField.fireActionPerformed(); + }); + popupMenu.add(historyItem); + } + } + popupMenu.show( this, 0, this.getHeight() ); + }); + + loadHistory(); + } + + public void addHistoryEntry(String text) { + if (!historyList.contains(text)) { + historyList.add(text); + Collections.sort(historyList); + saveHistory(); + } + + } + + private void loadHistory() { + try { + ObjectMapper mapper = new ObjectMapper(); + var json = ApplicationConfiguration.getConfiguration().getString(SEARCH_HISTORY_CONFIG, ""); + if (!json.isEmpty()) { + List entries = mapper.readValue(json, new TypeReference<>() {}); + if (!entries.isEmpty()) { + historyList.addAll(entries); + Collections.sort(historyList); + } + } + } + catch (JsonProcessingException ex) { + logger.error("Failed to load search history", ex); + } + } + + private void saveHistory() { + ObjectMapper mapper = new ObjectMapper(); + try { + var json = mapper.writeValueAsString(historyList); + ApplicationConfiguration.getConfiguration().setProperty(SEARCH_HISTORY_CONFIG, json); + } catch (JsonProcessingException e) { + logger.error("Failed to write search history", e); + } + } + } + } + public class SaveFilmAction extends AbstractAction { public SaveFilmAction() { putValue(Action.SHORT_DESCRIPTION,"Film downloaden"); From 701289371dfbc85da407c2956619af02c7cd4dad Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 22 Jun 2022 11:58:00 +0200 Subject: [PATCH 076/422] - move nightly to jdk 18 Former-commit-id: 22ee5b49ce684ab8214a2db6b9e9c428b70824fb --- .github/workflows/nightly.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 2d18a89596..8eb2a23986 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -18,11 +18,11 @@ jobs: id: check-if-changed with: paths: .github/workflows src/ res/ pom.xml .install4j/ .mvn/ - - name: Set up JDK 17 + - name: Set up JDK 18 if: steps.check-if-changed.outputs.changed == 'true' uses: actions/setup-java@v1 with: - java-version: 17 + java-version: 18 - name: Cache local Maven repository if: steps.check-if-changed.outputs.changed == 'true' @@ -73,10 +73,10 @@ jobs: - uses: actions/checkout@v2 with: ref: develop - - name: Set up JDK 17 + - name: Set up JDK 18 uses: actions/setup-java@v1 with: - java-version: 17 + java-version: 18 - uses: AdoptOpenJDK/install-jdk@v1 with: version: '8' From e8c3db3c546562f00c7809ddfb05341ebdf86ca9 Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 22 Jun 2022 11:58:15 +0200 Subject: [PATCH 077/422] - remove spacing from hbox Former-commit-id: 637c3025df7f260a5db38b247e2a1b742dbf234b --- .../resources/mediathek/res/programm/fxml/film_toolbar.fxml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml b/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml index b9ec9238fa..873eb83984 100644 --- a/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml +++ b/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml @@ -3,7 +3,7 @@ - + From 9eb42b85b6d7db41deb722a3e465072bee8b3147 Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 22 Jun 2022 12:16:43 +0200 Subject: [PATCH 078/422] - bump library versions Former-commit-id: 8ded2ea393a243d4f9a490038f60cec642ceb5f0 --- pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 26d05f17c8..e71973763f 100755 --- a/pom.xml +++ b/pom.xml @@ -82,8 +82,8 @@ ${jdk.language.version} ${jdk.language.version} - 22.0.0 - 3.21.0 + 23.0.0 + 3.23.1 2.7 2.11.0 3.12.0 @@ -110,9 +110,9 @@ 3.2.1 3.0.0-M5 11.0 - 4.2.0 - 4.9.3 - 3.0.0 + 4.6.1 + 4.10.0 + 3.1.0 4.6.3 3.36.0.3 17.1.6 From c5570b25858b84d674448dd98320da239bedbcb3 Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 22 Jun 2022 12:21:22 +0200 Subject: [PATCH 079/422] - move to jdk 18 Former-commit-id: b1c97c4dafe6521f2e0ff9384f839c1132a5dff0 --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c50deabae0..78269b2590 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,10 +8,10 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Set up JDK 17 + - name: Set up JDK 18 uses: actions/setup-java@v1 with: - java-version: 17 + java-version: 18 - name: Cache local Maven repository uses: actions/cache@v2 with: From 88ab86cf8e9bfb2186c6e021d01894e956c83260 Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 22 Jun 2022 13:00:17 +0200 Subject: [PATCH 080/422] reformat code Former-commit-id: 96ee700c5b00218593196412beb420399650f22b --- .../javafx/filterpanel/FXFilmToolBar.java | 69 +++++++++---------- 1 file changed, 34 insertions(+), 35 deletions(-) diff --git a/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java b/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java index e86beafbaa..c24d38c4ab 100644 --- a/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java +++ b/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java @@ -16,43 +16,42 @@ import java.net.URL; public class FXFilmToolBar extends HBox { - @FXML ComboBox filterSelect; - - public FXFilmToolBar() { - try { - URL url = getClass().getResource("/mediathek/res/programm/fxml/film_toolbar.fxml"); - FXMLLoader fxmlLoader = new FXMLLoader(url); - fxmlLoader.setRoot(this); - fxmlLoader.setController(this); - fxmlLoader.load(); - setUpFilterSelect(); - } catch (IOException e) { - Logger logger = LogManager.getLogger(FXFilmToolBar.class); - logger.error("Failed to load FXML!"); + @FXML + ComboBox filterSelect; + private ObservableList availableFilters; + + public FXFilmToolBar() { + try { + URL url = getClass().getResource("/mediathek/res/programm/fxml/film_toolbar.fxml"); + FXMLLoader fxmlLoader = new FXMLLoader(url); + fxmlLoader.setRoot(this); + fxmlLoader.setController(this); + fxmlLoader.load(); + setUpFilterSelect(); + } catch (IOException e) { + Logger logger = LogManager.getLogger(FXFilmToolBar.class); + logger.error("Failed to load FXML!"); + } } - } - - private void setUpFilterSelect() { - FilterConfiguration filterConfig = new FilterConfiguration(); - ObservableList availableFilters = - FXCollections.observableArrayList(filterConfig.getAvailableFilters()); - FilterConfiguration.addAvailableFiltersObserver( - () -> { - availableFilters.clear(); - availableFilters.addAll(filterConfig.getAvailableFilters()); + + private void setUpFilterSelect() { + FilterConfiguration filterConfig = new FilterConfiguration(); + availableFilters = FXCollections.observableArrayList(filterConfig.getAvailableFilters()); + + FilterConfiguration.addAvailableFiltersObserver(() -> { + availableFilters.clear(); + availableFilters.addAll(filterConfig.getAvailableFilters()); }); - SingleSelectionModel selectionModel = filterSelect.getSelectionModel(); - FilterConfiguration.addCurrentFiltersObserver(selectionModel::select); - filterSelect.setItems(availableFilters); - selectionModel.select(filterConfig.getCurrentFilter()); - selectionModel - .selectedItemProperty() - .addListener( - (observableValue, oldValue, newValue) -> { - if (newValue != null && !newValue.equals(filterConfig.getCurrentFilter())) { + SingleSelectionModel selectionModel = filterSelect.getSelectionModel(); + FilterConfiguration.addCurrentFiltersObserver(selectionModel::select); + filterSelect.setItems(availableFilters); + + selectionModel.select(filterConfig.getCurrentFilter()); + selectionModel.selectedItemProperty().addListener((observableValue, oldValue, newValue) -> { + if (newValue != null && !newValue.equals(filterConfig.getCurrentFilter())) { filterConfig.setCurrentFilter(newValue); - } - }); - } + } + }); + } } From 518055515b98d0ed8eba084ec7b93ce47f15ff58 Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 22 Jun 2022 13:29:08 +0200 Subject: [PATCH 081/422] - add swing filter selection combobox Former-commit-id: 2fd86250a9f6730087bde1e4e79794e4f1275c78 --- .../gui/FilterSelectionComboBoxModel.java | 54 +++++++++++++++++++ .../mediathek/gui/tabs/tab_film/GuiFilme.java | 5 ++ 2 files changed, 59 insertions(+) create mode 100644 src/main/java/mediathek/gui/FilterSelectionComboBoxModel.java diff --git a/src/main/java/mediathek/gui/FilterSelectionComboBoxModel.java b/src/main/java/mediathek/gui/FilterSelectionComboBoxModel.java new file mode 100644 index 0000000000..2b41f313f7 --- /dev/null +++ b/src/main/java/mediathek/gui/FilterSelectionComboBoxModel.java @@ -0,0 +1,54 @@ +package mediathek.gui; + +import javafx.application.Platform; +import mediathek.tool.FilterConfiguration; +import mediathek.tool.FilterDTO; + +import javax.swing.*; +import java.util.ArrayList; +import java.util.List; + +public class FilterSelectionComboBoxModel extends DefaultComboBoxModel { + private final FilterConfiguration filterConfiguration = new FilterConfiguration(); + private final List availableFilters = new ArrayList<>(); + + @Override + public void setSelectedItem(Object anObject) { + if (anObject != null) { + Platform.runLater(() -> filterConfiguration.setCurrentFilter((FilterDTO) anObject)); + //filterConfiguration.setCurrentFilter((FilterDTO) anObject); + } + } + + @Override + public Object getSelectedItem() { + return filterConfiguration.getCurrentFilter(); + } + + public FilterSelectionComboBoxModel() { + availableFilters.addAll(filterConfiguration.getAvailableFilters()); + FilterConfiguration.addAvailableFiltersObserver(() -> { + System.out.println("FILTER LIST CHANGED"); + SwingUtilities.invokeLater(() -> { + availableFilters.clear(); + availableFilters.addAll(filterConfiguration.getAvailableFilters()); + this.fireContentsChanged(this, 0, availableFilters.size()); + }); + }); + FilterConfiguration.addCurrentFiltersObserver(filterDTO -> { + System.out.println("CURRENT FILTER CHANGED"); + SwingUtilities.invokeLater(() -> fireContentsChanged(this, 0, availableFilters.size())); + + }); + } + + @Override + public int getSize() { + return availableFilters.size(); + } + + @Override + public FilterDTO getElementAt(int index) { + return availableFilters.get(index); + } +} diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index b8a3e9ebab..521f3a975b 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -27,6 +27,7 @@ import mediathek.daten.blacklist.BlacklistRule; import mediathek.filmeSuchen.ListenerFilmeLaden; import mediathek.filmeSuchen.ListenerFilmeLadenEvent; +import mediathek.gui.FilterSelectionComboBoxModel; import mediathek.gui.TabPaneIndex; import mediathek.gui.actions.PlayFilmAction; import mediathek.gui.actions.UrlHyperlinkAction; @@ -180,6 +181,10 @@ private void createToolBar() { toolBar.add(fxFilmActionPanel); toolBar.add(new JLabel("Suche:")); toolBar.add(searchField); + toolBar.addSeparator(); + var filterSelectionComboBox = new JComboBox<>(new FilterSelectionComboBoxModel()); + filterSelectionComboBox.setMaximumSize(new Dimension(150,100)); + toolBar.add(filterSelectionComboBox); } @Handler From d21ec5a0067b7baff62bbc1db80b68613e06c518 Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 22 Jun 2022 13:46:37 +0200 Subject: [PATCH 082/422] - remove fx film toolbar - finalize conversion to swing combo selection Former-commit-id: 8805f34e4379e00fae22e47fbf6ea3fa7bb322df --- .../gui/FilterSelectionComboBoxModel.java | 12 ++-- .../mediathek/gui/tabs/tab_film/GuiFilme.java | 18 ++---- .../javafx/filterpanel/FXFilmToolBar.java | 57 ------------------- .../javafx/filterpanel/FilmActionPanel.java | 24 -------- .../res/programm/fxml/film_toolbar.fxml | 12 ---- 5 files changed, 11 insertions(+), 112 deletions(-) delete mode 100644 src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java delete mode 100644 src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml diff --git a/src/main/java/mediathek/gui/FilterSelectionComboBoxModel.java b/src/main/java/mediathek/gui/FilterSelectionComboBoxModel.java index 2b41f313f7..6c27620607 100644 --- a/src/main/java/mediathek/gui/FilterSelectionComboBoxModel.java +++ b/src/main/java/mediathek/gui/FilterSelectionComboBoxModel.java @@ -1,6 +1,5 @@ package mediathek.gui; -import javafx.application.Platform; import mediathek.tool.FilterConfiguration; import mediathek.tool.FilterDTO; @@ -15,8 +14,7 @@ public class FilterSelectionComboBoxModel extends DefaultComboBoxModel filterConfiguration.setCurrentFilter((FilterDTO) anObject)); - //filterConfiguration.setCurrentFilter((FilterDTO) anObject); + filterConfiguration.setCurrentFilter((FilterDTO) anObject); } } @@ -36,8 +34,12 @@ public FilterSelectionComboBoxModel() { }); }); FilterConfiguration.addCurrentFiltersObserver(filterDTO -> { - System.out.println("CURRENT FILTER CHANGED"); - SwingUtilities.invokeLater(() -> fireContentsChanged(this, 0, availableFilters.size())); + SwingUtilities.invokeLater(() -> { + if (getSelectedItem() != filterDTO) { + System.out.println("CURRENT FILTER CHANGED"); + fireContentsChanged(this, 0, availableFilters.size()); + } + }); }); } diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index 521f3a975b..8b879064c9 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -114,10 +114,7 @@ public class GuiFilme extends AGuiTabPanel { protected SearchField searchField = new SearchField(); protected PauseTransition reloadTableDataTransition = new PauseTransition(Duration.millis(250d)); private Optional bookmarkWindowController = Optional.empty(); - /** - * The swing helper panel FilmAction bar. - */ - private JFXPanel fxFilmActionPanel; + private boolean stopBeob; private FilmTabInfoPane filmInfoLabel; private JCheckBoxMenuItem cbShowButtons; @@ -175,16 +172,13 @@ private void createToolBar() { toolBar.add(bookmarkFilmAction); toolBar.addSeparator(); toolBar.add(showFilterDialogAction); - fxFilmActionPanel = new JFXPanel(); - fxFilmActionPanel.setMaximumSize(new Dimension(100,100)); - toolBar.addSeparator(); - toolBar.add(fxFilmActionPanel); - toolBar.add(new JLabel("Suche:")); - toolBar.add(searchField); toolBar.addSeparator(); var filterSelectionComboBox = new JComboBox<>(new FilterSelectionComboBoxModel()); filterSelectionComboBox.setMaximumSize(new Dimension(150,100)); toolBar.add(filterSelectionComboBox); + toolBar.addSeparator(); + toolBar.add(new JLabel("Suche:")); + toolBar.add(searchField); } @Handler @@ -195,7 +189,6 @@ private void handleTableModelChange(TableModelChangeEvent e) { SwingUtilities.invokeAndWait(() -> saveFilmAction.setEnabled(false)); SwingUtilities.invokeAndWait(() -> bookmarkFilmAction.setEnabled(false)); SwingUtilities.invokeAndWait(() -> showFilterDialogAction.setEnabled(false)); - SwingUtilities.invokeAndWait(() -> fxFilmActionPanel.setEnabled(false)); SwingUtilities.invokeAndWait(() -> searchField.setEnabled(false)); } catch (InterruptedException | InvocationTargetException ex) { throw new RuntimeException(ex); @@ -207,7 +200,6 @@ private void handleTableModelChange(TableModelChangeEvent e) { SwingUtilities.invokeAndWait(() -> saveFilmAction.setEnabled(true)); SwingUtilities.invokeAndWait(() -> bookmarkFilmAction.setEnabled(true)); SwingUtilities.invokeAndWait(() -> showFilterDialogAction.setEnabled(true)); - SwingUtilities.invokeAndWait(() -> fxFilmActionPanel.setEnabled(true)); SwingUtilities.invokeAndWait(() -> searchField.setEnabled(true)); } catch (InterruptedException | InvocationTargetException ex) { throw new RuntimeException(ex); @@ -332,8 +324,6 @@ public void installMenuEntries(JMenu menu) { private void setupFilmActionPanel() { filmActionPanel = new FilmActionPanel(); - JavaFxUtils.invokeInFxThreadAndWait( - () -> fxFilmActionPanel.setScene(filmActionPanel.getFilmActionPanelScene())); } private void setupPsetButtonsPanel() { diff --git a/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java b/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java deleted file mode 100644 index c24d38c4ab..0000000000 --- a/src/main/java/mediathek/javafx/filterpanel/FXFilmToolBar.java +++ /dev/null @@ -1,57 +0,0 @@ -package mediathek.javafx.filterpanel; - -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import javafx.fxml.FXML; -import javafx.fxml.FXMLLoader; -import javafx.scene.control.ComboBox; -import javafx.scene.control.SingleSelectionModel; -import javafx.scene.layout.HBox; -import mediathek.tool.FilterConfiguration; -import mediathek.tool.FilterDTO; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.io.IOException; -import java.net.URL; - -public class FXFilmToolBar extends HBox { - @FXML - ComboBox filterSelect; - private ObservableList availableFilters; - - public FXFilmToolBar() { - try { - URL url = getClass().getResource("/mediathek/res/programm/fxml/film_toolbar.fxml"); - FXMLLoader fxmlLoader = new FXMLLoader(url); - fxmlLoader.setRoot(this); - fxmlLoader.setController(this); - fxmlLoader.load(); - setUpFilterSelect(); - } catch (IOException e) { - Logger logger = LogManager.getLogger(FXFilmToolBar.class); - logger.error("Failed to load FXML!"); - } - } - - private void setUpFilterSelect() { - FilterConfiguration filterConfig = new FilterConfiguration(); - availableFilters = FXCollections.observableArrayList(filterConfig.getAvailableFilters()); - - FilterConfiguration.addAvailableFiltersObserver(() -> { - availableFilters.clear(); - availableFilters.addAll(filterConfig.getAvailableFilters()); - }); - - SingleSelectionModel selectionModel = filterSelect.getSelectionModel(); - FilterConfiguration.addCurrentFiltersObserver(selectionModel::select); - filterSelect.setItems(availableFilters); - - selectionModel.select(filterConfig.getCurrentFilter()); - selectionModel.selectedItemProperty().addListener((observableValue, oldValue, newValue) -> { - if (newValue != null && !newValue.equals(filterConfig.getCurrentFilter())) { - filterConfig.setCurrentFilter(newValue); - } - }); - } -} diff --git a/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java b/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java index 4d8f0fc323..3d40684f3a 100644 --- a/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java +++ b/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java @@ -1,19 +1,15 @@ package mediathek.javafx.filterpanel; import impl.org.controlsfx.autocompletion.SuggestionProvider; -import javafx.application.Platform; import javafx.beans.property.BooleanProperty; import javafx.beans.property.ReadOnlyObjectProperty; import javafx.beans.property.ReadOnlyStringWrapper; import javafx.beans.property.SimpleBooleanProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; -import javafx.scene.Scene; import javafx.scene.control.ComboBox; import javafx.util.StringConverter; import mediathek.config.Daten; -import mediathek.filmeSuchen.ListenerFilmeLaden; -import mediathek.filmeSuchen.ListenerFilmeLadenEvent; import mediathek.gui.actions.ManageAboAction; import mediathek.gui.messages.FilmListWriteStartEvent; import mediathek.gui.messages.FilmListWriteStopEvent; @@ -61,7 +57,6 @@ public class FilmActionPanel { /** Stores the list of thema strings used for autocompletion. */ private SuggestionProvider themaSuggestionProvider; - private FXFilmToolBar toolBar; private CommonViewSettingsPane viewSettingsPane; public FilmActionPanel() { @@ -329,23 +324,4 @@ public void updateThemaBox() { themaSuggestionProvider.addPossibleSuggestions(items); themaBox.getSelectionModel().select(0); } - - public Scene getFilmActionPanelScene() { - toolBar = new FXFilmToolBar(); - - Daten.getInstance().getFilmeLaden().addAdListener( - new ListenerFilmeLaden() { - @Override - public void start(ListenerFilmeLadenEvent event) { - Platform.runLater(() -> toolBar.setDisable(true)); - } - - @Override - public void fertig(ListenerFilmeLadenEvent event) { - Platform.runLater(() -> toolBar.setDisable(false)); - } - }); - - return new Scene(toolBar); - } } diff --git a/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml b/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml deleted file mode 100644 index 873eb83984..0000000000 --- a/src/main/resources/mediathek/res/programm/fxml/film_toolbar.fxml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - From 22bf18e06f0688c71380c6fe94e9f04ca0a725ae Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 22 Jun 2022 13:48:10 +0200 Subject: [PATCH 083/422] disable debug output Former-commit-id: 6d07e5bc2274a3ece70680c3157d51f8e3fc2052 --- src/main/java/mediathek/gui/FilterSelectionComboBoxModel.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/mediathek/gui/FilterSelectionComboBoxModel.java b/src/main/java/mediathek/gui/FilterSelectionComboBoxModel.java index 6c27620607..2d6af698cb 100644 --- a/src/main/java/mediathek/gui/FilterSelectionComboBoxModel.java +++ b/src/main/java/mediathek/gui/FilterSelectionComboBoxModel.java @@ -26,7 +26,7 @@ public Object getSelectedItem() { public FilterSelectionComboBoxModel() { availableFilters.addAll(filterConfiguration.getAvailableFilters()); FilterConfiguration.addAvailableFiltersObserver(() -> { - System.out.println("FILTER LIST CHANGED"); + //System.out.println("FILTER LIST CHANGED"); SwingUtilities.invokeLater(() -> { availableFilters.clear(); availableFilters.addAll(filterConfiguration.getAvailableFilters()); @@ -36,7 +36,7 @@ public FilterSelectionComboBoxModel() { FilterConfiguration.addCurrentFiltersObserver(filterDTO -> { SwingUtilities.invokeLater(() -> { if (getSelectedItem() != filterDTO) { - System.out.println("CURRENT FILTER CHANGED"); + //System.out.println("CURRENT FILTER CHANGED"); fireContentsChanged(this, 0, availableFilters.size()); } }); From b3495e5d999761b3d4ddbf5ad0270fbdc51ed6a7 Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 22 Jun 2022 13:51:01 +0200 Subject: [PATCH 084/422] properly disable and enable filter combo Former-commit-id: ea123d398eec58fecdf09a20509f2ef0047c2283 --- src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index 8b879064c9..394e535ec4 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -112,6 +112,8 @@ public class GuiFilme extends AGuiTabPanel { public FilmActionPanel filmActionPanel; public ShowFilterDialogAction showFilterDialogAction = new ShowFilterDialogAction(); protected SearchField searchField = new SearchField(); + protected JComboBox filterSelectionComboBox = new JComboBox<>(new FilterSelectionComboBoxModel()); + protected PauseTransition reloadTableDataTransition = new PauseTransition(Duration.millis(250d)); private Optional bookmarkWindowController = Optional.empty(); @@ -173,7 +175,6 @@ private void createToolBar() { toolBar.addSeparator(); toolBar.add(showFilterDialogAction); toolBar.addSeparator(); - var filterSelectionComboBox = new JComboBox<>(new FilterSelectionComboBoxModel()); filterSelectionComboBox.setMaximumSize(new Dimension(150,100)); toolBar.add(filterSelectionComboBox); toolBar.addSeparator(); @@ -190,6 +191,7 @@ private void handleTableModelChange(TableModelChangeEvent e) { SwingUtilities.invokeAndWait(() -> bookmarkFilmAction.setEnabled(false)); SwingUtilities.invokeAndWait(() -> showFilterDialogAction.setEnabled(false)); SwingUtilities.invokeAndWait(() -> searchField.setEnabled(false)); + SwingUtilities.invokeAndWait(() -> filterSelectionComboBox.setEnabled(false)); } catch (InterruptedException | InvocationTargetException ex) { throw new RuntimeException(ex); } @@ -201,6 +203,7 @@ private void handleTableModelChange(TableModelChangeEvent e) { SwingUtilities.invokeAndWait(() -> bookmarkFilmAction.setEnabled(true)); SwingUtilities.invokeAndWait(() -> showFilterDialogAction.setEnabled(true)); SwingUtilities.invokeAndWait(() -> searchField.setEnabled(true)); + SwingUtilities.invokeAndWait(() -> filterSelectionComboBox.setEnabled(true)); } catch (InterruptedException | InvocationTargetException ex) { throw new RuntimeException(ex); } From 3fc95cedd7f9d2fad907d7dd633f176c55a4b664 Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 22 Jun 2022 15:22:54 +0200 Subject: [PATCH 085/422] cleanup Former-commit-id: bac7839ecdaa660478d30aad2af8b93d54bbe5d6 --- .../gui/FilterSelectionComboBoxModel.java | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/main/java/mediathek/gui/FilterSelectionComboBoxModel.java b/src/main/java/mediathek/gui/FilterSelectionComboBoxModel.java index 2d6af698cb..fabdb37972 100644 --- a/src/main/java/mediathek/gui/FilterSelectionComboBoxModel.java +++ b/src/main/java/mediathek/gui/FilterSelectionComboBoxModel.java @@ -33,15 +33,12 @@ public FilterSelectionComboBoxModel() { this.fireContentsChanged(this, 0, availableFilters.size()); }); }); - FilterConfiguration.addCurrentFiltersObserver(filterDTO -> { - SwingUtilities.invokeLater(() -> { - if (getSelectedItem() != filterDTO) { - //System.out.println("CURRENT FILTER CHANGED"); - fireContentsChanged(this, 0, availableFilters.size()); - } - }); - - }); + FilterConfiguration.addCurrentFiltersObserver(filterDTO -> SwingUtilities.invokeLater(() -> { + if (getSelectedItem() != filterDTO) { + //System.out.println("CURRENT FILTER CHANGED"); + fireContentsChanged(this, 0, availableFilters.size()); + } + })); } @Override From 653feba53768a35666fd00479c29bc35d06a396d Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 23 Jun 2022 15:27:35 +0200 Subject: [PATCH 086/422] fix selection Former-commit-id: 29d7e3d108843711cd920e5d3e1304ba4385979d --- src/main/java/mediathek/gui/FilterSelectionComboBoxModel.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/mediathek/gui/FilterSelectionComboBoxModel.java b/src/main/java/mediathek/gui/FilterSelectionComboBoxModel.java index fabdb37972..78e6c1d4c3 100644 --- a/src/main/java/mediathek/gui/FilterSelectionComboBoxModel.java +++ b/src/main/java/mediathek/gui/FilterSelectionComboBoxModel.java @@ -13,6 +13,7 @@ public class FilterSelectionComboBoxModel extends DefaultComboBoxModel Date: Thu, 23 Jun 2022 15:43:58 +0200 Subject: [PATCH 087/422] - code cleanup Former-commit-id: 19ea5333a518da864ccb3fd5103c590353fdadbf --- .../mediathek/gui/tabs/tab_film/GuiFilme.java | 5 ++--- .../javafx/filterpanel/FilmActionPanel.java | 16 ---------------- .../java/mediathek/mainwindow/MediathekGui.java | 10 ++++++++++ 3 files changed, 12 insertions(+), 19 deletions(-) diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index 394e535ec4..68fa1500a4 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -170,16 +170,15 @@ private void createToolBar() { toolBar.add(playFilmAction); toolBar.add(saveFilmAction); - toolBar.addSeparator(); toolBar.add(bookmarkFilmAction); toolBar.addSeparator(); - toolBar.add(showFilterDialogAction); - toolBar.addSeparator(); filterSelectionComboBox.setMaximumSize(new Dimension(150,100)); toolBar.add(filterSelectionComboBox); toolBar.addSeparator(); toolBar.add(new JLabel("Suche:")); toolBar.add(searchField); + toolBar.addSeparator(); + toolBar.add(showFilterDialogAction); } @Handler diff --git a/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java b/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java index 3d40684f3a..4bba905cb3 100644 --- a/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java +++ b/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java @@ -11,14 +11,10 @@ import javafx.util.StringConverter; import mediathek.config.Daten; import mediathek.gui.actions.ManageAboAction; -import mediathek.gui.messages.FilmListWriteStartEvent; -import mediathek.gui.messages.FilmListWriteStopEvent; import mediathek.mainwindow.MediathekGui; import mediathek.tool.FilterConfiguration; import mediathek.tool.FilterDTO; import mediathek.tool.GermanStringSorter; -import mediathek.tool.MessageBus; -import net.engio.mbassy.listener.Handler; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.controlsfx.control.RangeSlider; @@ -75,8 +71,6 @@ public FilmActionPanel() { setupFilterSelection(); setupDeleteCurrentFilterButton(); setupAddNewFilterButton(); - - MessageBus.getMessageBus().subscribe(this); } private void setupAddNewFilterButton() { @@ -284,16 +278,6 @@ private void setupConfigListeners() { .addListener(((observable, oldValue, newValue) -> filterConfig.setZeitraum(newValue))); } - @Handler - private void handleFilmlistWriteStartEvent(FilmListWriteStartEvent e) { - SwingUtilities.invokeLater(() -> MediathekGui.ui().loadFilmListAction.setEnabled(false)); - } - - @Handler - private void handleFilmlistWriteStopEvent(FilmListWriteStopEvent e) { - SwingUtilities.invokeLater(() -> MediathekGui.ui().loadFilmListAction.setEnabled(true)); - } - public void updateThemaBox() { final var items = themaBox.getItems(); items.clear(); diff --git a/src/main/java/mediathek/mainwindow/MediathekGui.java b/src/main/java/mediathek/mainwindow/MediathekGui.java index 05306cf24e..584c369334 100644 --- a/src/main/java/mediathek/mainwindow/MediathekGui.java +++ b/src/main/java/mediathek/mainwindow/MediathekGui.java @@ -867,6 +867,16 @@ private void createViewMenu() { jMenuAnsicht.add(manageBookmarkAction); } + @Handler + private void handleFilmlistWriteStartEvent(FilmListWriteStartEvent e) { + SwingUtilities.invokeLater(() -> loadFilmListAction.setEnabled(false)); + } + + @Handler + private void handleFilmlistWriteStopEvent(FilmListWriteStopEvent e) { + SwingUtilities.invokeLater(() -> loadFilmListAction.setEnabled(true)); + } + private void createHelpMenu() { jMenuHilfe.add(new ShowOnlineHelpAction()); jMenuHilfe.addSeparator(); From 3eb079ba45e19b6ea7c9224eadb36510b9658f81 Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 24 Jun 2022 11:27:01 +0200 Subject: [PATCH 088/422] - move back to swing Former-commit-id: 89f7634e11e181f3e1f236b25787f305cee90531 --- .../gui/dialog/DialogFilmBeschreibung.java | 28 ++++--------- .../java/mediathek/tool/SwingErrorDialog.java | 41 +++++++++++++++++++ 2 files changed, 50 insertions(+), 19 deletions(-) create mode 100644 src/main/java/mediathek/tool/SwingErrorDialog.java diff --git a/src/main/java/mediathek/gui/dialog/DialogFilmBeschreibung.java b/src/main/java/mediathek/gui/dialog/DialogFilmBeschreibung.java index 0dc109750e..d0d563bc0b 100644 --- a/src/main/java/mediathek/gui/dialog/DialogFilmBeschreibung.java +++ b/src/main/java/mediathek/gui/dialog/DialogFilmBeschreibung.java @@ -1,7 +1,5 @@ package mediathek.gui.dialog; -import javafx.application.Platform; -import javafx.scene.control.Alert; import mediathek.config.Daten; import mediathek.config.Konstanten; import mediathek.config.MVConfig; @@ -9,11 +7,8 @@ import mediathek.daten.DatenFilm; import mediathek.daten.DatenPset; import mediathek.daten.ListePset; -import mediathek.javafx.tool.JFXHiddenApplication; -import mediathek.javafx.tool.JavaFxUtils; import mediathek.mainwindow.MediathekGui; import mediathek.tool.*; -import mediathek.tool.javafx.FXErrorDialog; import net.miginfocom.layout.AC; import net.miginfocom.layout.CC; import net.miginfocom.layout.LC; @@ -50,15 +45,14 @@ public DialogFilmBeschreibung(JFrame parent, DatenFilm datenFilm) { }); jButtonHilfe.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/circle-question.svg")); - jButtonHilfe.addActionListener(e -> Platform.runLater(() -> { - Alert alert = new Alert(Alert.AlertType.INFORMATION); - alert.setHeaderText("Hilfe zu " + TITLE); - alert.setContentText(""" + jButtonHilfe.addActionListener(e -> { + var message = """ Diese Funktion richtet sich z.B. an Benutzer,welche eine angepasste Beschreibung der Sendung in Form der Infodatei ("Filmname.txt") anlegen und durch Drittprogramme einlesen lassen wollen. - Achtung: Diese Änderungen gehen nach dem Neuladen einer Filmliste verloren."""); - JFXHiddenApplication.showAlert(alert, MediathekGui.ui()); - })); + Achtung: Diese Änderungen gehen nach dem Neuladen einer Filmliste verloren. + """; + JOptionPane.showMessageDialog(this, message, Konstanten.PROGRAMMNAME, JOptionPane.INFORMATION_MESSAGE); + }); jButtonSpeichern.addActionListener(e -> { datenFilm.setDescription(jTextArea1.getText()); @@ -91,15 +85,11 @@ Diese Funktion richtet sich z.B. an Benutzer,welche eine angepasste Beschreibung var url = HttpUrl.parse(datenFilm.getUrlNormalQuality()); file.writeInfoFile(datenFilm, path, url); - JavaFxUtils.invokeInFxThreadAndWait(() -> { - Alert alert = new Alert(Alert.AlertType.INFORMATION); - alert.setHeaderText("Infodatei schreiben"); - alert.setContentText("Infodatei wurde erfolgreich geschrieben."); - JFXHiddenApplication.showAlert(alert, MediathekGui.ui()); - }); + JOptionPane.showMessageDialog(this, "Infodatei wurde erfolgreich geschrieben.", + Konstanten.PROGRAMMNAME, JOptionPane.INFORMATION_MESSAGE); } catch (IOException ex) { - JavaFxUtils.invokeInFxThreadAndWait(() -> FXErrorDialog.showErrorDialog(Konstanten.PROGRAMMNAME, "Infodatei schreiben", "Ein unbekannter Fehler ist aufgetreten!", ex)); + SwingErrorDialog.showExceptionMessage(this,"Ein unbekannter Fehler ist aufgetreten!", ex); logger.error("Ziel: {}", path.toAbsolutePath().toString(), ex); } } diff --git a/src/main/java/mediathek/tool/SwingErrorDialog.java b/src/main/java/mediathek/tool/SwingErrorDialog.java new file mode 100644 index 0000000000..1cb49ccdd4 --- /dev/null +++ b/src/main/java/mediathek/tool/SwingErrorDialog.java @@ -0,0 +1,41 @@ +package mediathek.tool; + +import mediathek.config.Konstanten; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.*; +import java.io.PrintWriter; +import java.io.StringWriter; + +public class SwingErrorDialog { + public static void showExceptionMessage(@NotNull Component parentComponent, + @NotNull String messageText, + @NotNull Exception exception) throws HeadlessException { + + StringWriter stringWriter = new StringWriter(); + exception.printStackTrace(new PrintWriter(stringWriter)); + + JLabel message = new JLabel(messageText); + message.setBorder(BorderFactory.createEmptyBorder(3, 0, 10, 0)); + + JTextArea text = new JTextArea(); + text.setEditable(false); + text.setFont(UIManager.getFont("Label.font")); + text.setText(stringWriter.toString()); + text.setCaretPosition(0); + + JScrollPane scroller = new JScrollPane(text); + scroller.setPreferredSize(new Dimension(400, 200)); + + JPanel panel = new JPanel(); + panel.setLayout(new BorderLayout()); + + panel.add(message, BorderLayout.NORTH); + panel.add(scroller, BorderLayout.SOUTH); + + JOptionPane.showMessageDialog(parentComponent, panel, Konstanten.PROGRAMMNAME, + JOptionPane.ERROR_MESSAGE); + + } +} From 299160f66fbffa6c81a316db98fdd04864d59a7d Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 24 Jun 2022 11:34:33 +0200 Subject: [PATCH 089/422] - use linux aarch64 javafx jars from project instead of JDK builds Former-commit-id: d2017a6e52738d974ba32364ac0f5a69c624e7ba --- pom.xml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index e71973763f..b7a0a7d48b 100755 --- a/pom.xml +++ b/pom.xml @@ -1026,7 +1026,7 @@ - linux + linux-aarch64 arm @@ -1035,42 +1035,36 @@ javafx-base ${javafx.version} ${javafx.platform} - provided org.openjfx javafx-controls ${javafx.version} ${javafx.platform} - provided org.openjfx javafx-media ${javafx.version} ${javafx.platform} - provided org.openjfx javafx-swing ${javafx.version} ${javafx.platform} - provided org.openjfx javafx-fxml ${javafx.version} ${javafx.platform} - provided org.openjfx javafx-graphics ${javafx.version} ${javafx.platform} - provided From 37a579ad337a7dcc7391bcc43263cdb1de18b1ee Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 24 Jun 2022 12:00:39 +0200 Subject: [PATCH 090/422] - remove unused code - reformat Former-commit-id: f83ddaf4ca19c8e168e898a02f783ee89483d7e3 --- .../filterpanel/CommonViewSettingsPane.java | 205 ++++++++++-------- 1 file changed, 110 insertions(+), 95 deletions(-) diff --git a/src/main/java/mediathek/javafx/filterpanel/CommonViewSettingsPane.java b/src/main/java/mediathek/javafx/filterpanel/CommonViewSettingsPane.java index 6c0ad14dfe..262cda0189 100644 --- a/src/main/java/mediathek/javafx/filterpanel/CommonViewSettingsPane.java +++ b/src/main/java/mediathek/javafx/filterpanel/CommonViewSettingsPane.java @@ -24,112 +24,127 @@ import java.util.ResourceBundle; public class CommonViewSettingsPane extends VBox implements Initializable { - private static final Logger logger = LogManager.getLogger(CommonViewSettingsPane.class); - @FXML public Button btnDeleteFilterSettings; - @FXML public CheckBox cbShowOnlyHd; - @FXML public CheckBox cbShowSubtitlesOnly; - @FXML public CheckBox cbShowNewOnly; - @FXML public CheckBox cbShowBookMarkedOnly; - @FXML public CheckBox cbShowOnlyLivestreams; - @FXML public CheckBox cbShowUnseenOnly; - @FXML public CheckBox cbDontShowAbos; - @FXML public CheckBox cbDontShowGebaerdensprache; - @FXML public CheckBox cbDontShowTrailers; - @FXML public CheckBox cbDontShowAudioVersions; - @FXML public SenderBoxNode senderCheckList; - @FXML public ThemaComboBox themaComboBox; - @FXML public FilmLenghtSliderNode filmLengthSliderNode; - @FXML public ZeitraumSpinner zeitraumSpinner; - @FXML public Button btnDeleteCurrentFilter; - @FXML private Label themaLabel; - @FXML private ComboBox filterSelect; - @FXML private Button btnAddNewFilter; - private boolean deleteCurrentFilterButtonDisabled; + private static final Logger logger = LogManager.getLogger(CommonViewSettingsPane.class); + @FXML + public Button btnDeleteFilterSettings; + @FXML + public CheckBox cbShowOnlyHd; + @FXML + public CheckBox cbShowSubtitlesOnly; + @FXML + public CheckBox cbShowNewOnly; + @FXML + public CheckBox cbShowBookMarkedOnly; + @FXML + public CheckBox cbShowOnlyLivestreams; + @FXML + public CheckBox cbShowUnseenOnly; + @FXML + public CheckBox cbDontShowAbos; + @FXML + public CheckBox cbDontShowGebaerdensprache; + @FXML + public CheckBox cbDontShowTrailers; + @FXML + public CheckBox cbDontShowAudioVersions; + @FXML + public SenderBoxNode senderCheckList; + @FXML + public ThemaComboBox themaComboBox; + @FXML + public FilmLenghtSliderNode filmLengthSliderNode; + @FXML + public ZeitraumSpinner zeitraumSpinner; + @FXML + public Button btnDeleteCurrentFilter; + @FXML + private Label themaLabel; + @FXML + private ComboBox filterSelect; + @FXML + private Button btnAddNewFilter; + private boolean deleteCurrentFilterButtonDisabled; - public CommonViewSettingsPane() { - super(); + public CommonViewSettingsPane() { + super(); - try { - URL url = getClass().getResource("/mediathek/res/programm/fxml/filter_settings_pane.fxml"); - FXMLLoader fxmlLoader = new FXMLLoader(url); - fxmlLoader.setRoot(this); - fxmlLoader.setController(this); - fxmlLoader.load(); - } catch (IOException e) { - logger.error("Failed to load FXML!", e); + try { + URL url = getClass().getResource("/mediathek/res/programm/fxml/filter_settings_pane.fxml"); + FXMLLoader fxmlLoader = new FXMLLoader(url); + fxmlLoader.setRoot(this); + fxmlLoader.setController(this); + fxmlLoader.load(); + } catch (IOException e) { + logger.error("Failed to load FXML!", e); + } } - } - /** - * Prevent user from changing filter settings while the swing table model gets updated. - * - * @param evt the model event - */ - @Handler - private void handleTableModelChangeEvent(TableModelChangeEvent evt) { - Platform.runLater( - () -> { - final boolean disable = evt.active; - btnDeleteFilterSettings.setDisable(disable); - cbShowOnlyHd.setDisable(disable); - cbShowSubtitlesOnly.setDisable(disable); - cbShowNewOnly.setDisable(disable); - cbShowBookMarkedOnly.setDisable(disable); - cbShowOnlyLivestreams.setDisable(disable); - cbShowUnseenOnly.setDisable(disable); - cbDontShowAbos.setDisable(disable); - cbDontShowGebaerdensprache.setDisable(disable); - cbDontShowTrailers.setDisable(disable); - cbDontShowAudioVersions.setDisable(disable); - senderCheckList.setDisable(disable); - themaComboBox.setDisable(disable); - filmLengthSliderNode.setDisable(disable); - zeitraumSpinner.setDisable(disable); - filterSelect.setDisable(disable); - btnDeleteCurrentFilter.setDisable(disable || deleteCurrentFilterButtonDisabled); - btnAddNewFilter.setDisable(disable); - }); - } - - public void disableDeleteCurrentFilterButton(boolean disable) { - deleteCurrentFilterButtonDisabled = disable; - btnDeleteCurrentFilter.setDisable(disable); - } + /** + * Prevent user from changing filter settings while the swing table model gets updated. + * + * @param evt the model event + */ + @Handler + private void handleTableModelChangeEvent(TableModelChangeEvent evt) { + Platform.runLater( + () -> { + final boolean disable = evt.active; + btnDeleteFilterSettings.setDisable(disable); + cbShowOnlyHd.setDisable(disable); + cbShowSubtitlesOnly.setDisable(disable); + cbShowNewOnly.setDisable(disable); + cbShowBookMarkedOnly.setDisable(disable); + cbShowOnlyLivestreams.setDisable(disable); + cbShowUnseenOnly.setDisable(disable); + cbDontShowAbos.setDisable(disable); + cbDontShowGebaerdensprache.setDisable(disable); + cbDontShowTrailers.setDisable(disable); + cbDontShowAudioVersions.setDisable(disable); + senderCheckList.setDisable(disable); + themaComboBox.setDisable(disable); + filmLengthSliderNode.setDisable(disable); + zeitraumSpinner.setDisable(disable); + filterSelect.setDisable(disable); + btnDeleteCurrentFilter.setDisable(disable || deleteCurrentFilterButtonDisabled); + btnAddNewFilter.setDisable(disable); + }); + } - @Override - public void initialize(URL url, ResourceBundle resourceBundle) { - themaLabel.setMinWidth(USE_PREF_SIZE); - // font size is greater on tested ubuntu linux :( - if (SystemUtils.IS_OS_LINUX) themaLabel.setPrefWidth(50d); - else themaLabel.setPrefWidth(45d); + public void disableDeleteCurrentFilterButton(boolean disable) { + deleteCurrentFilterButtonDisabled = disable; + btnDeleteCurrentFilter.setDisable(disable); + } - MessageBus.getMessageBus().subscribe(this); - } + @Override + public void initialize(URL url, ResourceBundle resourceBundle) { + themaLabel.setMinWidth(USE_PREF_SIZE); + // font size is greater on tested ubuntu linux :( + if (SystemUtils.IS_OS_LINUX) themaLabel.setPrefWidth(50d); + else themaLabel.setPrefWidth(45d); - public void setFilterSelectionChangeListener(ChangeListener changeListener) { - filterSelect.getSelectionModel().selectedItemProperty().addListener(changeListener); - } + MessageBus.getMessageBus().subscribe(this); + } - public FilterDTO getSelectedFilter() { - return filterSelect.getValue(); - } + public void setFilterSelectionChangeListener(ChangeListener changeListener) { + filterSelect.getSelectionModel().selectedItemProperty().addListener(changeListener); + } - public void setAvailableFilters(ObservableList filters) { - filterSelect.setItems(filters); - } + public void setAvailableFilters(ObservableList filters) { + filterSelect.setItems(filters); + } - public void selectFilter(FilterDTO filter) { - SingleSelectionModel selectionModel = filterSelect.getSelectionModel(); - if (!filter.equals(selectionModel.getSelectedItem())) { - selectionModel.select(filter); + public void selectFilter(FilterDTO filter) { + SingleSelectionModel selectionModel = filterSelect.getSelectionModel(); + if (!filter.equals(selectionModel.getSelectedItem())) { + selectionModel.select(filter); + } } - } - public void setAddNewFilterButtonEventHandler(EventHandler eventHandler) { - btnAddNewFilter.setOnAction(eventHandler); - } + public void setAddNewFilterButtonEventHandler(EventHandler eventHandler) { + btnAddNewFilter.setOnAction(eventHandler); + } - public void setFilterSelectionStringConverter(StringConverter filterStringConverter) { - filterSelect.setConverter(filterStringConverter); - } + public void setFilterSelectionStringConverter(StringConverter filterStringConverter) { + filterSelect.setConverter(filterStringConverter); + } } From 9552b0683e96cadf00e55fa86f50730c9245ad47 Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 24 Jun 2022 12:52:24 +0200 Subject: [PATCH 091/422] - show geoinfo as icons instead of text - document changes Former-commit-id: 3ade020a2bf216c0f7d9263c79c453de8b28b35c --- CHANGELOG.md | 2 + .../tool/cellrenderer/CellRendererFilme.java | 47 +++++++++++++++++++ .../resources/icons/fontawesome/lock-open.svg | 1 + src/main/resources/icons/fontawesome/lock.svg | 1 + 4 files changed, 51 insertions(+) create mode 100755 src/main/resources/icons/fontawesome/lock-open.svg create mode 100755 src/main/resources/icons/fontawesome/lock.svg diff --git a/CHANGELOG.md b/CHANGELOG.md index 62b893d658..852fcbb2bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,12 @@ **14.0.0** +- User Interface für macOS überarbeitet - **BUGFIX:** *"Filme/Blacklist öffnen..."* Eintrag wurde in *"Filme/Blacklist bearbeiten..."* umbenannt und die Icons vereinheitlicht. - **BUGFIX:** Manchmal inkorrekte Darstellung des Bookmark-Status im Film-Tab wurde korrigiert. - **FEATURE:** Die Toolbars können nun zum Schweben in allen Tabs abgetrennt werden. - **FEATURE:** Icons wurden durch hochauflösende Vektorvarianten ersetzt. - **FEATURE:** Neues Suchfeld inklusive Suchhistorie. +- **FEATURE:** Geoinformationen werden nun als *gesperrt/entsperrt* Icons dargestellt. Detailinformationen werden über Tooltip-Text eingeblendet. **13.9.1** diff --git a/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java b/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java index 9b35f62dd7..ef8e080021 100644 --- a/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java +++ b/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java @@ -7,6 +7,7 @@ import mediathek.controller.starter.Start; import mediathek.daten.DatenDownload; import mediathek.daten.DatenFilm; +import mediathek.tool.ApplicationConfiguration; import mediathek.tool.SVGIconUtilities; import mediathek.tool.table.MVTable; import org.apache.logging.log4j.LogManager; @@ -29,6 +30,11 @@ public class CellRendererFilme extends CellRendererBaseWithStart { private final FlatSVGIcon normalBookmarkIcon; private final FlatSVGIcon selectedBookmarkIconHighlighted; + private final FlatSVGIcon lockedIcon; + private final FlatSVGIcon lockedIconSelected; + private final FlatSVGIcon unlockedIcon; + private final FlatSVGIcon unlockedIconSelected; + public CellRendererFilme() { var whiteColorFilter = new FlatSVGIcon.ColorFilter(color -> Color.WHITE); @@ -54,6 +60,16 @@ public CellRendererFilme() { selectedBookmarkIconHighlighted.setColorFilter(new FlatSVGIcon.ColorFilter(color -> Color.ORANGE)); normalBookmarkIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/bookmark.svg"); + + lockedIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/lock.svg"); + + lockedIconSelected = SVGIconUtilities.createSVGIcon("icons/fontawesome/lock.svg"); + lockedIconSelected.setColorFilter(whiteColorFilter); + + unlockedIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/lock-open.svg"); + + unlockedIconSelected = SVGIconUtilities.createSVGIcon("icons/fontawesome/lock-open.svg"); + unlockedIconSelected.setColorFilter(whiteColorFilter); } private JTextArea createTextArea(String content) { @@ -136,6 +152,10 @@ public Component getTableCellRendererComponent( if (columnWidth < table.getFontMetrics(table.getFont()).stringWidth(title)) setToolTipText(title); break; + + case DatenFilm.FILM_GEO: + drawGeolocationIcons(datenFilm, isSelected); + break; } applyColorSettings(this, datenFilm, datenDownload, isSelected, isBookMarked); @@ -146,6 +166,33 @@ public Component getTableCellRendererComponent( return this; } + private void drawGeolocationIcons(@NotNull DatenFilm film, boolean isSelected) { + setHorizontalAlignment(SwingConstants.CENTER); + setText(""); + film.getGeo().ifPresentOrElse(geoString -> { + setToolTipText(geoString); + if (geoString.contains(config.getString(ApplicationConfiguration.GEO_LOCATION))) { + // we are unlocked + if (isSelected) + setIcon(unlockedIconSelected); + else + setIcon(unlockedIcon); + } + else { + //locked + if (isSelected) + setIcon(lockedIconSelected); + else + setIcon(lockedIcon); + } + }, () -> { + setToolTipText("Keine Geoinformationen vorhanden"); + if (isSelected) + setIcon(unlockedIconSelected); + else + setIcon(unlockedIcon); + }); + } /** * Apply the specific horizontal alignment to the cell based on column * diff --git a/src/main/resources/icons/fontawesome/lock-open.svg b/src/main/resources/icons/fontawesome/lock-open.svg new file mode 100755 index 0000000000..e67c635f03 --- /dev/null +++ b/src/main/resources/icons/fontawesome/lock-open.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/resources/icons/fontawesome/lock.svg b/src/main/resources/icons/fontawesome/lock.svg new file mode 100755 index 0000000000..f919ac61f3 --- /dev/null +++ b/src/main/resources/icons/fontawesome/lock.svg @@ -0,0 +1 @@ + \ No newline at end of file From 997e048e1cf717dd5814f0257351773c4c9c3423 Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 24 Jun 2022 13:00:43 +0200 Subject: [PATCH 092/422] - use geo icons in download tab as well Former-commit-id: dda9f544e9881781c9b35d2432a2f701795ced88 --- .../CellRendererBaseWithStart.java | 46 +++++++++++++++++ .../cellrenderer/CellRendererDownloads.java | 4 ++ .../tool/cellrenderer/CellRendererFilme.java | 51 ++----------------- 3 files changed, 53 insertions(+), 48 deletions(-) diff --git a/src/main/java/mediathek/tool/cellrenderer/CellRendererBaseWithStart.java b/src/main/java/mediathek/tool/cellrenderer/CellRendererBaseWithStart.java index 860e320685..95a8ba00e7 100644 --- a/src/main/java/mediathek/tool/cellrenderer/CellRendererBaseWithStart.java +++ b/src/main/java/mediathek/tool/cellrenderer/CellRendererBaseWithStart.java @@ -1,12 +1,16 @@ package mediathek.tool.cellrenderer; +import com.formdev.flatlaf.extras.FlatSVGIcon; import mediathek.config.MVColor; import mediathek.controller.starter.Start; +import mediathek.daten.DatenFilm; import mediathek.gui.messages.GeoStateChangedEvent; import mediathek.tool.ApplicationConfiguration; import mediathek.tool.MessageBus; +import mediathek.tool.SVGIconUtilities; import net.engio.mbassy.listener.Handler; import org.apache.commons.configuration2.Configuration; +import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.*; @@ -16,11 +20,53 @@ */ public class CellRendererBaseWithStart extends CellRendererBase { protected final Configuration config = ApplicationConfiguration.getConfiguration(); + protected final FlatSVGIcon lockedIcon; + protected final FlatSVGIcon lockedIconSelected; + protected final FlatSVGIcon unlockedIcon; + protected final FlatSVGIcon unlockedIconSelected; protected boolean geoMelden; + protected FlatSVGIcon.ColorFilter whiteColorFilter = new FlatSVGIcon.ColorFilter(color -> Color.WHITE); public CellRendererBaseWithStart() { MessageBus.getMessageBus().subscribe(this); geoMelden = config.getBoolean(ApplicationConfiguration.GEO_REPORT, false); + + lockedIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/lock.svg"); + + lockedIconSelected = SVGIconUtilities.createSVGIcon("icons/fontawesome/lock.svg"); + lockedIconSelected.setColorFilter(whiteColorFilter); + + unlockedIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/lock-open.svg"); + + unlockedIconSelected = SVGIconUtilities.createSVGIcon("icons/fontawesome/lock-open.svg"); + unlockedIconSelected.setColorFilter(whiteColorFilter); + } + + protected void drawGeolocationIcons(@NotNull DatenFilm film, boolean isSelected) { + setHorizontalAlignment(SwingConstants.CENTER); + setText(""); + film.getGeo().ifPresentOrElse(geoString -> { + setToolTipText(geoString); + if (geoString.contains(config.getString(ApplicationConfiguration.GEO_LOCATION))) { + // we are unlocked + if (isSelected) + setIcon(unlockedIconSelected); + else + setIcon(unlockedIcon); + } else { + //locked + if (isSelected) + setIcon(lockedIconSelected); + else + setIcon(lockedIcon); + } + }, () -> { + setToolTipText("Keine Geoinformationen vorhanden"); + if (isSelected) + setIcon(unlockedIconSelected); + else + setIcon(unlockedIcon); + }); } @Handler diff --git a/src/main/java/mediathek/tool/cellrenderer/CellRendererDownloads.java b/src/main/java/mediathek/tool/cellrenderer/CellRendererDownloads.java index 2b268ce8d5..c4338827d0 100644 --- a/src/main/java/mediathek/tool/cellrenderer/CellRendererDownloads.java +++ b/src/main/java/mediathek/tool/cellrenderer/CellRendererDownloads.java @@ -201,6 +201,10 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole setSenderIcon(value.toString(), targetDim); } break; + + case DatenDownload.DOWNLOAD_GEO: + drawGeolocationIcons(datenDownload.film, isSelected); + break; } if (columnModelIndex == DatenDownload.DOWNLOAD_TITEL) { diff --git a/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java b/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java index ef8e080021..303472a076 100644 --- a/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java +++ b/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java @@ -7,7 +7,6 @@ import mediathek.controller.starter.Start; import mediathek.daten.DatenDownload; import mediathek.daten.DatenFilm; -import mediathek.tool.ApplicationConfiguration; import mediathek.tool.SVGIconUtilities; import mediathek.tool.table.MVTable; import org.apache.logging.log4j.LogManager; @@ -30,14 +29,7 @@ public class CellRendererFilme extends CellRendererBaseWithStart { private final FlatSVGIcon normalBookmarkIcon; private final FlatSVGIcon selectedBookmarkIconHighlighted; - private final FlatSVGIcon lockedIcon; - private final FlatSVGIcon lockedIconSelected; - private final FlatSVGIcon unlockedIcon; - private final FlatSVGIcon unlockedIconSelected; - public CellRendererFilme() { - var whiteColorFilter = new FlatSVGIcon.ColorFilter(color -> Color.WHITE); - selectedDownloadIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/download.svg"); selectedDownloadIcon.setColorFilter(whiteColorFilter); @@ -60,16 +52,6 @@ public CellRendererFilme() { selectedBookmarkIconHighlighted.setColorFilter(new FlatSVGIcon.ColorFilter(color -> Color.ORANGE)); normalBookmarkIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/bookmark.svg"); - - lockedIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/lock.svg"); - - lockedIconSelected = SVGIconUtilities.createSVGIcon("icons/fontawesome/lock.svg"); - lockedIconSelected.setColorFilter(whiteColorFilter); - - unlockedIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/lock-open.svg"); - - unlockedIconSelected = SVGIconUtilities.createSVGIcon("icons/fontawesome/lock-open.svg"); - unlockedIconSelected.setColorFilter(whiteColorFilter); } private JTextArea createTextArea(String content) { @@ -153,9 +135,9 @@ public Component getTableCellRendererComponent( setToolTipText(title); break; - case DatenFilm.FILM_GEO: - drawGeolocationIcons(datenFilm, isSelected); - break; + case DatenFilm.FILM_GEO: + drawGeolocationIcons(datenFilm, isSelected); + break; } applyColorSettings(this, datenFilm, datenDownload, isSelected, isBookMarked); @@ -166,33 +148,6 @@ public Component getTableCellRendererComponent( return this; } - private void drawGeolocationIcons(@NotNull DatenFilm film, boolean isSelected) { - setHorizontalAlignment(SwingConstants.CENTER); - setText(""); - film.getGeo().ifPresentOrElse(geoString -> { - setToolTipText(geoString); - if (geoString.contains(config.getString(ApplicationConfiguration.GEO_LOCATION))) { - // we are unlocked - if (isSelected) - setIcon(unlockedIconSelected); - else - setIcon(unlockedIcon); - } - else { - //locked - if (isSelected) - setIcon(lockedIconSelected); - else - setIcon(lockedIcon); - } - }, () -> { - setToolTipText("Keine Geoinformationen vorhanden"); - if (isSelected) - setIcon(unlockedIconSelected); - else - setIcon(unlockedIcon); - }); - } /** * Apply the specific horizontal alignment to the cell based on column * From 01c113c631da207dc486b9a49b5d2d4c9616cda1 Mon Sep 17 00:00:00 2001 From: Christian F Date: Sun, 26 Jun 2022 12:02:18 +0200 Subject: [PATCH 093/422] - add a test for unsupported ORF ttml file format Former-commit-id: c822e7f4d870c447424eccbe8eeffeb5feb936d2 --- .../tool/TimedTextMarkupLanguageParserTest.java | 13 +++++++++++++ src/test/resources/ttml/ttml_fail_test_file.ttml | 2 ++ 2 files changed, 15 insertions(+) create mode 100644 src/test/resources/ttml/ttml_fail_test_file.ttml diff --git a/src/test/java/mediathek/tool/TimedTextMarkupLanguageParserTest.java b/src/test/java/mediathek/tool/TimedTextMarkupLanguageParserTest.java index 4073094234..020e7b2155 100644 --- a/src/test/java/mediathek/tool/TimedTextMarkupLanguageParserTest.java +++ b/src/test/java/mediathek/tool/TimedTextMarkupLanguageParserTest.java @@ -1,9 +1,12 @@ package mediathek.tool; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import java.io.File; import java.text.SimpleDateFormat; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class TimedTextMarkupLanguageParserTest { @@ -64,4 +67,14 @@ void test_aboveHundred() throws Exception { assertTrue(srtString.endsWith(",400")); } } + + @Test + @DisplayName("Fail for unsupported TTML format") + void unsupported_ttml_file_format() { + try (TimedTextMarkupLanguageParser parser = new TimedTextMarkupLanguageParser()) { + var file = new File("src/test/resources/ttml/ttml_fail_test_file.ttml"); + var res = parser.parse(file.toPath()); + assertFalse(res); + } + } } \ No newline at end of file diff --git a/src/test/resources/ttml/ttml_fail_test_file.ttml b/src/test/resources/ttml/ttml_fail_test_file.ttml new file mode 100644 index 0000000000..cc3eb74cd3 --- /dev/null +++ b/src/test/resources/ttml/ttml_fail_test_file.ttml @@ -0,0 +1,2 @@ + +TTML

*

*

*

Die Fotos sind nicht <br/>zufällig passiert!

Die Koch und ich haben im Büro <br/>ein bisschen rumgealbert.

Dabei hab ich sie fotografiert.

Rumgealbert!

Mehr war da nicht.

Bedauerlicherweise, oder?!

Ich brauch dringend frische Luft! <br/>- Seit wann spionierst du mir nach?

He!

Hast du zu mir so wenig Vertrauen?

Mir ist das Handy runtergefallen; <br/>da hab ich mir die Fotos angeschaut.

Wer sind Sie?

Halt! Stehen bleiben!

* Hundegebell *

* Eine Scheibe zerbricht. *

Gar nix?

Nix!

Moment!

Der Körpertemperatur nach <br/>ist er erst seit 30 Minuten tot.

Zwei Einschusslöcher <br/>in der Herzgegend.

Nach der Obduktion kann ich <br/>euch mehr sagen.

Danke! Im Nebenraum wurde <br/>eine Scheibe von außen eingeschlagen

Das Opfer muss das gehört haben <br/>und hat sich mit dem Messer gewehrt.

Sämtliche Ausweise und <br/>persönliche Unterlagen sind weg.

Den Laptop hat er auch mitgenommen.

Hoppla!

Schau!

Das Handy hat er übersehen.

Die Nachbarn haben die Schüsse <br/>gehört und uns informiert.

Bei dem Toten handelt es sich <br/>um Andreas Eder aus Deutschland.

Er hat vor kurzem die Villa gemietet.

Nimm dir das Handy vor, <br/>sämtliche Anrufe. Danke!

Klaus!

Danke.

Das ist interessant.

Das ist doch Katharina Schneider.

Die Chefin von der Multimedia- <br/>Agentur Redstone.

Ihr Mann, Lukas Schneider, gilt als <br/>vermisst, seit 3 Monaten in Hamburg.

Es hat einen Abschiedsbrief gegeben, <br/>aber sonst keinerlei Spuren.

Fotos, Unterlagen, Notizen - <br/>der Eder hat ihnen nachspioniert.

Das Luxushotel Alpenhof steht <br/>für Innovation, ...

.. Qualität und <br/>höchste Dienstleistung.

In der Klarheit hab ich auch <br/>versucht, das Logo zu gestalten.

Es prägt sich beim Betrachter <br/>leicht ein.

Und erzielt dadurch <br/>einen hohen Wiedererkennungseffekt.

Mich haben Sie überzeugt, Frau Koch!

Ist das Logo nicht zu simpel?

Wir haben den Rohentwurf <br/>gemeinsam besprochen!

So etwas haben wir uns nicht <br/>vorgestellt!

Katharina, was ist los? <br/>- Das ist doch gar nix!

Machen wir eine kurze Pause!

Grüß Gott. Kofler, Kripo Kitzbühel. <br/>Mein Kollege, Herr Lechner.

Frau Schneider? <br/>- Ja.

Herr Black, Frau Koch.

Folgendes, Frau Schneider: <br/>Kennen Sie diesen Mann?

Dieser Mann hat vor zwei Tagen <br/>in unserem Garten herumgeschnüffelt.

Ja, genau! Als wir ihn <br/>entdeckt haben, ist er weggelaufen.

Herumgeschnüffelt?

Wahrscheinlich wollte er einbrechen!

Was sollte er sonst <br/>in unserem Garten gesucht haben?!

Das möchten wir von Ihnen wissen!

Herr Eder hat Sie alle drei <br/>beschattet.

Wir haben Fotos von Ihnen <br/>bei ihm gefunden.

Ich hab diesen Mann vor zwei Tagen <br/>zum ersten Mal gesehen!

Was ist mit Ihnen, Frau Koch?

Tut mir leid. Ich hab den Mann <br/>noch nie gesehen.

Er Sie anscheinend schon. <br/>Und gestern wurde er ermordet!

Sie können sich nicht erklären, <br/>warum er Sie fotografiert hat?

Frau Schneider, zuerst <br/>verschwindet Ihr Ehemann spurlos.

Dann wird ein Mann ermordet, der <br/>Sie offensichtlich beschattet hat.

Und Sie haben von all dem nichts <br/>bemerkt. Das ist hochinteressant!

Diesen Eder hab ich nicht gekannt.

Aber den Lukas Schneider. <br/>Das war ein sehr charmanter Mann!

Der hat mit seiner Frau zusammen <br/>die Agentur Redstone aufgebaut.

Sind das jetzt alle?

Die würden auch noch passen.

Die Agentur ist spezialisiert <br/>auf Homepages ...

.. und Marketing-Aktivitäten <br/>von Gaststätten und Hotels.

Heute hat schon jede Jausen-Station <br/>ihre eigene Homepage!

Das wäre mein Text für die Homepage.

Vater, wenn der genauso gelungen <br/>ist wie deine Fotos, ...

.. dann würde ich die Idee von <br/>deiner eigenen Homepage überdenken.

Gut, dann werde ich professionelle <br/>Hilfe in Anspruch nehmen.

Für meine Homepage.

Vielleicht eine Agentur. <br/>- Die Agentur Redstone?

Der Täter hat aus etwa vier Metern <br/>Entfernung auf ihn geschossen.

Der Tote weist zwei Einschüsse <br/>mitten ins Herz auf.

Die Projektile stammen aus einer <br/>Pistole mit dem Kaliber 9 mm Para.

Und das Fabrikat?

Das stellen die Ballistik-Experten <br/>gerade fest.

Dann wird überprüft, ob die Waffe ...

.. schon einmal bei einem Verbrechen <br/>verwendet worden ist.

Würd mich ned wundern.

Wir bräuchten den Vermissten-Akt <br/>von Lukas Schneider.

Uns würde auch interessieren, ob <br/>gegen Andreas Eder etwas vorliegt.

Dankschön. Servas!

Ja. Tschüs.

Die Hamburger Kollegen schicken uns <br/>den Polizei-Akt.

Vielleicht gibt's eine Verbindung <br/>zwischen dem Eder und dem Schneider.

Eder hat mehrmals mit einem Münchner <br/>Textilunternehmen telefoniert.

Vor zwei Tagen hat er Cornelia Koch <br/>angerufen. Die Grafikerin.

Sie hat uns doch erzählt, <br/>dass sie ihn nicht kennt!

Keiner der Verdächtigen <br/>hat ein Alibi.

Diese Agentur scheint <br/>ziemlich erfolgreich zu sein.

Black ist seit zwei Monaten dort.

Eine Agentur-Mitarbeiterin erzählt, <br/>dass die beiden ein Paar sind.

Frau Schneider hat das Verschwinden <br/>ihres Mannes schnell verkraftet!

Ãœber den Andreas Eder <br/>wissen wir noch gar nix!

Ruf in dieser Münchner Firma an.

Ich frag mich, was der Eder <br/>in Kitzbühel g'macht hat.

* Handy *

Katharina Schneider. Guten Tag.

Hallo?

Hallo?!

Lassen Sie mich endlich in Ruhe!

Wie oft soll ich es Ihnen <br/>noch sagen?!

Ich habe Andreas Eder weder gekannt, <br/>noch hab ich mit ihm telefoniert!

Er hat Sie vor zwei Tagen angerufen!

Welcher würde Ihnen besser gefallen?

Sie haben einen besseren Geschmack <br/>als Frau Schneider!

Vor zwei Tagen, sagen Sie?

Vor ... zwei ... Tagen?

Mit diesem Eder hab ich <br/>bestimmt nicht telefoniert.

Es rief ein gewisser Norbert Lang an.

Ich hatte vorher noch nie <br/>mit ihm gesprochen.

Er wollte sich heute Mittag <br/>mit mir treffen.

Er sagte, er wisse, <br/>wo Lukas Schneider ist.

Er ist leider nicht gekommen.

Und da ruft er Sie an, <br/>nicht Katharina Schneider?

Ja. Hat mich auch gewundert.

Wahrscheinlich wollte sich jemand <br/>einen Scherz mit mir erlauben.

Einen schlechten! Wie war <br/>Ihr Verhältnis zu Lukas Schneider?

Rein geschäftlich, <br/>wenn Sie das meinen!

Ich hab der Agentur Redstone <br/>Arbeitsproben von mir geschickt.

Und seitdem, seit zwei Jahren, <br/>arbeite ich regelmäßig für sie.

Warum ist der Herr Schneider <br/>verschwunden? Was glauben Sie?

Das müssen Sie seine Frau fragen!

Man sollte über diese Homepage <br/>auch einen Tisch reservieren können.

Wir benötigen <br/>professionelle Fotos und Texte!

Dann hab ich noch an eine Kategorie <br/>'Rezepte' gedacht.

Vielleicht unter 'Koflers Klassiker'.

* Klopfen *

Guten Tag. Entschuldigung.

Katharina, hast du einen Moment Zeit?

Ich bin gleich wieder zurück.

Gräfin!

Sie kommen!

Diese Schneider ist nicht <br/>so unschuldig, wie sie tut.

Die hat irgendwas mit dem <br/>Verschwinden ihres Mannes zu tun.

Sie erbt sein ganzes Vermögen!

Aber erst in zehn Jahren.

Wenn die Leiche ihres Mannes <br/>nicht gefunden wird, ...

.. kann sie das Erbe <br/>erst in zehn Jahren antreten.

So lange dauert es, ...

.. bis man einen Vermissten ...

.. für tot erklären kann.

Trotzdem: Irgendwas <br/>verheimlicht sie uns.

Vielleicht hat der Eder rausgefunden, <br/>dass sie ihren Mann umgebracht hat.

Und hat sie damit erpresst.

Gräfin, Sie sind wunderbar!

Lukas Schneider ist nach einem <br/>Geschäftstermin spurlos verschwunden

Einige Tage später hat seine Frau <br/>diesen Abschiedsbrief bekommen.

Darin schreibt er, dass er <br/>ein neues Leben beginnen möchte.

Eigenartig! Der haut einfach so ab. <br/>- Das passiert öfters.

Und dass er sein Vermögen dalässt?! <br/>- Ja, das ist wirklich eigenartig!

Die Hamburger Polizei <br/>hat in seinem Hotelzimmer ...

.. nur einen Koffer und <br/>einige Kleidungsstücke gefunden.

Die Spurensicherung!

Bei der Waffe handelt es sich <br/>um eine Beretta 92 FS.

Mit dieser Waffe wurde in Österreich <br/>noch kein Gewaltverbrechen verübt.

Der Eder war für <br/>diese Münchner Textilfirma tätig.

Vielleicht hat er auch <br/>für andere Unternehmen gearbeitet.

Schade, dass wir seinen Laptop <br/>noch nicht g'funden haben.

Vielleicht können wir <br/>seinen Provider rausfinden.

Im Internet hinterlässt jeder <br/>Spuren.

Was machst du da?

Ich frag mich schon die ganze Zeit, <br/>wohin diese Tür führt.

Nirgendwohin.

Komm, wir gehen ins Wohnzimmer.

Das ist sein Zimmer.

Du hast es seit seinem Verschwinden <br/>nicht mehr betreten.

Komm!

Du musst mit deiner Vergangenheit <br/>Frieden schließen!

Sonst haben wir beide keine Chance!

Ich hab heute schon wieder <br/>so einen Anruf bekommen!

Ich hab dir von Anfang an gesagt: <br/>Geh zur Polizei!

Vielleicht steckt dein Mann dahinter.

Was würdest du tun, wenn er plötzlich <br/>wieder vor der Tür steht?

Gut.

Ich hab verstanden.

Hör bitte auf!

Es tut mir leid!

Die Anrufe, der Mord - <br/>ich weiß überhaupt nichts mehr!

Bitte hör auf!

Warum? Du hast dich entschieden!

Ich hab mich entschieden!

Nichts! Der Eder hat alle Spuren <br/>im Internet g'löscht.

Außer der Münchner Firma und <br/>dem Telefonat mit der Koch ...

.. haben wir keine Informationen!

Und die Fotos. Aber warum hat er <br/>die drei beschattet?

Vom Hin- und Herrennen wird's <br/>auch nicht besser!

In Hamburg liegt nix <br/>gegen den Eder vor.

Anscheinend gibt es keine Verbindung <br/>zwischen ihm und dem Schneider.

Der Eder hat sich beim Telefonat mit <br/>der Koch als Norbert Lang ausgegeben

Dann müsste er Lukas Schneider <br/>gekannt haben.

Eigenartig, dass er sich nicht <br/>direkt an die Schneider wendet.

Die Koch hat als Grafikerin <br/>einen guten Ruf in der Werbebranche.

Sie ist alleinstehend, <br/>keine Schulden.

Ich schau mich in der Villa um. <br/>- Ich schau zum Black.

Ich leb seit zwei Monaten <br/>in Kitzbühel.

Zuvor hab ich einige Jahre in London <br/>eine Direktmarketing-Agentur geführt.

Was hat Sie nach Kitzbühel <br/>verschlagen?

Ein Sommerurlaub.

Mein Vater ist Engländer. <br/>Daher mein Name - Black.

Meine Mutter ist eine Innsbruckerin; <br/>Tirol ist quasi meine zweite Heimat.

In Kitzbühel hab ich Katharina kennen <br/>gelernt und bin dort hängen geblieben

Und Sie haben keine Ahnung, warum <br/>Herr Eder Sie fotografiert hat?

Den habe ich - außer im Garten - <br/>noch nie zuvor gesehen.

Das wissen Sie ja bereits.

Haben Sie Lukas Schneider <br/>je getroffen?

Nein. Ich lege auch keinen Wert <br/>darauf, ihm jemals zu begegnen!

Andreas Eder.

Hubert Reichelt.

Norbert Lang.

Robert Stern. Das ist der Letzte.

Schau einmal!

Warum fotografiert jemand <br/>einen Brunnen?

Vielleicht, weil er da drinnen <br/>was versteckt hat.

Aber was?

Und wo ist der Brunnen?

* Geräusch *

*

Grüß euch!

An der Terrassentür <br/>sind Einbruchspuren.

Alle Zimmer sind durchwühlt. Wir <br/>überprüfen, ob was gestohlen wurde.

Wenn's was Neues gibt, ... <br/>- .. meld ich mich.

Guten Abend. Dürfen wir Ihnen <br/>ein paar Fragen stellen?

Aber kurz bitte!

Ich muss ihn überrascht haben.

Plötzlich hat mich <br/>was Hartes am Kopf getroffen.

Als ich wieder aufgewacht bin, <br/>hab ich sofort Edward angerufen.

Haben Sie den Täter erkannt?

Wo waren Sie, als das passiert ist?

In der Agentur.

Dafür gibt's keine Zeugen, <br/>nehm ich an.

Na und?!

Sie verdächtigen doch nicht mich?! <br/>Das geht mir zu weit!

In einem Mordfall müssen wir <br/>jeder Möglichkeit nachgehen!

Haben Sie eine Ahnung, was <br/>der Täter hier gesucht haben könnte?

Warum sollte der Black <br/>in die eigene Villa einbrechen?

Ich glaub eher, dass die Koch <br/>etwas damit zu tun hat.

Vielleicht sollen wir <br/>genau das denken!

* Telefon *

Kofler. Danke für den Rückruf!

Einen Moment, ich notiere.

Mhm.

Ja.

Danke vielmals.

Ja, tschüs. Baba.

Die Kollegen aus Hamburg.

Der Eder wird in Hamburg unter <br/>dem Namen Norbert Lang gesucht.

Wegen Internet-Betrügereien.

Insgesamt hat er damit <br/>sechs Millionen Euro ergaunert!

Das klingt so, als hätte er <br/>Komplizen gehabt.

Vielleicht gibt es einen Zusammen- <br/>hang mit Lukas Schneider.

Dann wären die sechs Millionen <br/>das Motiv für den Mord am Eder.

Und vielleicht für das Verschwinden <br/>vom Lukas Schneider.

Dann hat der Einbrecher das Geld <br/>in der Villa gesucht.

Wie und wann ist es dorthin <br/>gekommen?

Wenn ich ein neues Leben anfangen <br/>will, nehm ich mein Geld mit!

Außer er wird daran gehindert. <br/>- Wo ist der Lukas Schneider?

Wer hat noch von diesen Internet- <br/>Betrügereien g'wusst?

Und wo ist das Geld?

Vielleicht in dem Brunnen, <br/>den der Eder fotografiert hat.

Also, für mich ist das sonnenklar.

Die Schneider hat was <br/>mit dem Fall zu tun!

Ich würde ihre Pistole überprüfen.

Das haben wir. Ihre Waffe <br/>ist registriert.

Damit wurde Eder nicht erschossen.

Ich trau der nicht. Irgendwo hab ich <br/>diesen Brunnen schon einmal gesehen.

Aber ich weiß nicht mehr, wo.

Mir sagt das Foto nichts.

Was soll da versteckt sein?

Das würden wir auch gerne wissen. <br/>Danke für eure Bemühungen!

Schönen Tag noch.

Wo hab ich diesen Brunnen <br/>schon einmal gesehen?

Mein Mann hat <br/>keine Betrügereien begangen!

Unsere Agentur läuft hervorragend.

Warum sollte er sich <br/>auf krumme Geschäfte einlassen?!

Weil sechs Millionen Euro <br/>sehr viel Geld sind.

Sechs Millionen?!

Wie hätte er das vor mir <br/>verheimlichen können?

Was spielen Sie uns da vor?!

Geben Sie zu, dass Sie davon <br/>gewusst haben!

Mein Mann ist spurlos verschwunden. <br/>Bei mir wurde eingebrochen.

Ich hab was auf den Kopf gekriegt!

Ein Unbekannter schnüffelt <br/>in meinem Garten herum.

Ich bin kein Verbrecher! <br/>Ich bin das Opfer!

Ist gut, Schatz.

Gut.

Sie sind so ruhig, Herr Black.

Haben Sie irgendetwas <br/>von diesen Betrügereien gewusst?

Ich habe Lukas Schneider <br/>nicht gekannt!

Wir werden noch rausfinden, wie <br/>unschuldig Sie beide wirklich sind!

Unter diesem Baum hab ich einmal <br/>zehn solche Herrenpilze gefunden!

Schwammerlsuchen ist sehr spannend!

Hab daraus sofort ein köstliches ...

Das ist der Brunnen!

Nach den neuen Nägeln zu urteilen, <br/>ist der Brunnen ...

.. vor gar nicht so langer Zeit <br/>erst zugenagelt worden.

Das würde ja bedeuten ...

Halten Sie mal!

Und?

Oh Gott!

Das war gute Arbeit, Vater! <br/>Gratulation!

Danke. Schwammerlsuchen ist <br/>doch aufregender, als man glaubt.

Es handelt sich <br/>vermutlich um Lukas Schneider.

Er wurde erschossen.

Er ist schon seit einigen Monaten tot <br/>- das sieht man am Verwesungszustand.

Der Abschiedsbrief war fingiert!

Genaueres nach der Obduktion.

Da! Der ist neben der Leiche g'legen.

Ned grad umweltgerecht entsorgt.

Wenigstens is er ned feucht 'worden.

Das könnte der Laptop <br/>vom Andreas Eder sein.

Den sollen sich die Experten <br/>in Innsbruck anschauen.

Ich hab das Gebiss der Leiche ...

.. mit den Röntgenbildern vom Gebiss <br/>von Lukas Schneider verglichen.

Sie stimmen hundertprozentig überein!

Damit hab ich gerechnet.

Aber rechnen ist nicht gleich wissen.

Apropos wissen! Wir dürften es <br/>mit einem Doppelmörder zu tun haben!

Auch hier ist <br/>das Projektil 9 mm Para.

Lukas Schneider wurde mit derselben <br/>Waffe ermordet wie Andreas Eder.

Er ist tot.

Ermordet.

Also hat er es nicht <br/>mehr geschafft, abzuhauen.

Sie wirken nicht sehr überrascht.

Es erleichtert, <br/>endlich Gewissheit zu haben.

Sind Sie auch erleichtert, <br/>Herr Black?

Lukas Schneider kann Ihnen den Platz <br/>jetzt nicht mehr streitig machen.

Ihre Frechheiten bin ich <br/>mittlerweile gewöhnt!

Schatz ...

Der Eder hat die Leiche <br/>Ihres Mannes im Brunnen gefunden.

Er hat geahnt, dass Sie es waren - <br/>also hat er Sie erpresst.

Sechs Millionen. <br/>War das sein Todesurteil?

Was?!

Ich war es nicht. Sag ihnen, <br/>dass ich nichts damit zu tun habe.

Das ist ...

Das sind doch Hirngespinste!

Zum letzten Mal:

Ich war es nicht!

Eder und seine Komplizen haben <br/>Tausende Internetbenutzer geschädigt

Die hatten <br/>mehrere Briefkastenfirmen.

Mit Hauptsitz auf der Insel Jersey, <br/>im Ärmelkanal.

Und eigene Dependance in London.

London?

London.

Das könnte bedeuten, dass der Black <br/>auch in die Sache involviert war.

Gute Nacht.

Kroisleitner, überprüf noch mal <br/>genau die Marketingfirma vom Black!

Was?!

Das könnte alles nur Tarnung sein!

Ich hab die Telefonate vom Black <br/>und der Koch überprüft.

Beim Black war <br/>nix Außergewöhnliches.

Aber die Koch hat mehrmals mit <br/>dem Lukas Schneider telefoniert.

Die hatten ja beruflich <br/>miteinander zu tun.

Sie hat auch nach seinem Verschwin- <br/>den versucht, ihn zu erreichen!

Das ist wirklich interessant!

Vielleicht weiß sie auch über <br/>die Internet-Betrügereien Bescheid.

Ja, ich weiß, wie spät es ist! <br/>Servas, danke.

Der Black hat seine Firma <br/>zu einem guten Preis verkauft.

Scheint alles in Ordnung zu sein.

Was ihn als Mörder nicht <br/>ausschließt!

Genauso wenig wie <br/>die Schneider und die Koch.

Sie geben zu, Lukas Schneider <br/>mehrmals angerufen zu haben.

Auch nach seinem Verschwinden. <br/>Was wollten Sie denn von ihm?

Ich konnte mir nicht vorstellen, <br/>dass er einfach ...

Er ist immer sehr fair <br/>zu mir gewesen.

Hat Herr Schneider Ihnen gegenüber <br/>die Internet-Betrügereien erwähnt?

Nein, mit keinem Wort.

Wir haben uns über Sie erkundigt!

Geben S' zu, dass S' mit Schneider <br/>ein Verhältnis g'habt haben!

Wer sagt das?

Sie haben uns von Anfang an belogen!

Lukas Schneider hat Ihnen vertraut.

Und Ihnen von seinen Internet- <br/>Betrügereien erzählt.

Sie haben ihn überredet, die sechs <br/>Millionen zur Seite zu schaffen.

Dann haben Sie ihn ermordet! <br/>- Märchen!

Die äußerst privaten E-Mails zwi- <br/>schen Ihnen und Herrn Schneider ...

.. stammen wahrscheinlich auch <br/>von der bösen Fee?!

Jetzt wär der richtige Zeitpunkt <br/>für die Wahrheit, Frau Koch.

Seine Ehe war schon lange kaputt.

Ich mochte Lukas, von Anfang an.

Und irgendwann ...

Ja, irgendwann ist es dann passiert!

Aber ich hab ihn nicht umgebracht!

Hat Frau Schneider <br/>von dem Verhältnis gewusst?

Nein. Weiß ich nicht.

Wenn sie es wusste, dann hat sie es <br/>Lukas und mich niemals spüren lassen.

Der Lukas Schneider war vor seinem <br/>Verschwinden mehrmals in London!

Karin, entschuldige!

Hallo!

Wir waren wieder <br/>in dieser Redstone-Agentur.

Die Pistole von der Schneider <br/>ist verschwunden!

Ihr müsst die Schneider <br/>observieren lassen!

Wir haben Peilsender an den Autos <br/>von der Schneider, ...

.. dem Black und der Koch <br/>anbringen lassen.

So wissen wir immer, wo wir die drei <br/>finden. Ihr könnt nach Hause fahren!

Dann wollen wir nicht länger stören!

Wiedersehen.

Die Mordwaffe wurde <br/>vor sechs Monaten gestohlen.

Jetzt rat einmal, wo.

In London.

Schon wieder London! <br/>Das kann kein Zufall sein.

Die Aufzeichnungen der Daten <br/>vom Laptop sind da.

Daraus geht hervor, dass Schneider <br/>und Eder zusammengearbeitet haben.

Aber eine dritte Person taucht <br/>bei den Betrügereien nicht auf.

Vielleicht waren sie nur zu zweit.

* Türglocke *

* Türglocke *

Hoch!

Na los!

Wo ist das Geld?

Ich weiß nicht, wovon Sie sprechen.

Ich wusste von Anfang an <br/>von Ihrem Verhältnis zu meinem Mann.

Nach seinem Tod hab ich mich gefragt, <br/>wo die sechs Millionen sind.

Mir war sofort klar, dass er Ihnen <br/>das Geld gegeben hat!

Wo ist es?

Öffnen!

Ins Bad!

Na los!

Du hast etwas, das mir gehört!

Bist sicher, Kroisleitner?

Gut, wir sind schon unterwegs.

Die Kollegen in Innsbruck haben <br/>die Laptop-Dateien rekonstruiert.

Daraus geht hervor, dass der Black, <br/>Lukas Schneider und der Eder ...

.. in diese Internet-Betrügereien <br/>involviert waren.

Der Schneider und der Eder sind tot.

Der Black hat sie umgebracht. Sein <br/>Auto steht vor der Villa Schneider.

Wir machen eine kleine Reise.

Steig ein!

* Türglocke *

Scheint keiner da zu sein.

Aber sein Auto steht da.

Kroisleitner!

Klaus!

Danke.

Die Schneider hat die Koch <br/>überfallen ...

..und ist mit dem Black weggefahren.

* heftiges Hupen *

Wo fahren wir hin?

Das Geld reicht für uns beide.

Wir lieben uns doch!

Bist du verrückt?!

Es tut mir leid.

Die anonymen Anrufe.

Der Einbruch.

Das warst du!

Du hast das Geld bei mir gesucht!

Was bist du für ein Mensch?!

Fahr da rechts ran!

Sie haben angehalten.

Komm her!

Los!

Wir gehen da hoch!

Was soll das?!

Wir gehen da hoch! Los!

Das ist der Forstweg zum Brunnen.

Der wird doch nicht etwa ...

Du hast meinen Mann umgebracht.

Er hat unser Konto abgeräumt und <br/>wollte mit dem Geld verschwinden!

Ich habe so was geahnt.

Zuerst hab ich angenommen, <br/>dass er dir das Geld gegeben hat.

He!

Du kannst das Sterben wohl <br/>nicht erwarten!

Black, die Waffe weg!

Bleiben Sie, wo Sie sind! Auf einen <br/>Toten mehr kommt's mir nicht mehr an!

Legen Sie die Waffe weg!

Werfen SIE die Waffe weg!

Zum letzten Mal: die Waffe runter!

* Schuss *

Warum musste der Eder sterben?

Er wollte die sechs Millionen. <br/>Um jeden Preis.

Und er hat Schneiders Leiche <br/>im Brunnen entdeckt.

Ich war vor ein paar Tagen dort, <br/>um zu sehen, ob alles in Ordnung ist.

Er muss mich wohl verfolgt <br/>und dabei beobachtet haben.

Und der Eder hat herausgefunden, <br/>dass Sie der Dritte im Bunde waren.

Sie haben dem Schneider <br/>die Tipps gegeben.

Als der mit dem Geld abhauen wollte, <br/>haben Sie ihn umgebracht.

Und Sie, Frau Koch, waren <br/>in diese Betrügereien eingeweiht!

Wir wollten zusammen nach Argentinien <br/>gehen und ein neues Leben anfangen.

Er hat mir das Geld zur Aufbewahrung <br/>gegeben. Wollte kurz weg ...

.. und kam nicht zurück.

Nur Sie haben von den Machenschaften <br/>Ihres Mannes nichts gewusst.

Dafür aber von seinem Verhältnis <br/>zur Frau Koch.

Ja, Lukas hat mich betrogen <br/>und verlassen.

Die sechs Millionen wären <br/>eine Art Abfindung gewesen.

Ihr Verhalten wird <br/>ein gerichtliches Nachspiel haben!

Mitwisserschaft ist auch <br/>kein Kavaliersdelikt.

Und Sie, Herr Black, ...

.. müssen sich wegen zweifachen <br/>Mordes vor Gericht verantworten!

Abführen!

(Gräfin:) Die Schneider hat <br/>den Mörder im eigenen Haus gehabt!

Sie war noch sehr in ihn verliebt!

(Hannes:) Der Black wollte <br/>alles andere als Liebe.

Mhm, Sauvignon Blanc!

Aus dem Kamptal.

Vater, wann können wir endlich <br/>deine Homepage bewundern?

Ich hab mir etwas überlegt.

Die Pochlarner Stuben sind <br/>durch Mundpropaganda das geworden,...

.. was sie heute sind.

Was kann es für ein Restaurant <br/>Ehrenvolleres geben, ...

.. als durch Mundpropaganda <br/>populär zu werden?!

Untertitel: Toni Gschöpf

ORF 2007 <br/>untertitel@orf.at

From d464374f26908837db465eb08740fa72f0172c59 Mon Sep 17 00:00:00 2001 From: Christian F Date: Sun, 26 Jun 2022 12:33:04 +0200 Subject: [PATCH 094/422] - "decompress" sender and thema fields in readable filmlist Former-commit-id: 402afd96c2b361705d6e40d0721e1e7698b63c1e --- .../filmlisten/writer/FilmListWriter.java | 40 ++++++++++++++----- .../actions/export/FilmListExportAction.java | 22 ++++------ .../export/FilmListExportWorkerTask.java | 3 ++ 3 files changed, 40 insertions(+), 25 deletions(-) diff --git a/src/main/java/mediathek/filmlisten/writer/FilmListWriter.java b/src/main/java/mediathek/filmlisten/writer/FilmListWriter.java index 63dc661fe4..4a07bb20b2 100644 --- a/src/main/java/mediathek/filmlisten/writer/FilmListWriter.java +++ b/src/main/java/mediathek/filmlisten/writer/FilmListWriter.java @@ -25,9 +25,11 @@ public class FilmListWriter { private static final Logger logger = LogManager.getLogger(FilmListWriter.class); private static final String TAG_JSON_LIST = "X"; + private final boolean readable; private String sender = ""; private String thema = ""; - private final boolean readable; + private boolean compressSenderTag = true; + private boolean compressThemaTag = true; public FilmListWriter(boolean readable) { this.readable = readable; @@ -166,21 +168,37 @@ private void writeTitel(JsonGenerator jg, DatenFilm datenFilm) throws IOExceptio private void writeSender(JsonGenerator jg, DatenFilm datenFilm) throws IOException { String tempSender = datenFilm.getSender(); - if (tempSender.equals(sender)) { - jg.writeString(""); - } else { - sender = tempSender; - jg.writeString(tempSender); + if (compressSenderTag) { + if (tempSender.equals(sender)) { + jg.writeString(""); + } else { + sender = tempSender; + jg.writeString(tempSender); + } } + else + jg.writeString(tempSender); + } + + public void setCompressThemaTag(boolean compressThemaTag) { + this.compressThemaTag = compressThemaTag; + } + + public void setCompressSenderTag(boolean compress) { + compressSenderTag = compress; } private void writeThema(JsonGenerator jg, DatenFilm datenFilm) throws IOException { - if (datenFilm.getThema().equals(thema)) { - jg.writeString(""); - } else { - thema = datenFilm.getThema(); - jg.writeString(datenFilm.getThema()); + if (compressThemaTag) { + if (datenFilm.getThema().equals(thema)) { + jg.writeString(""); + } else { + thema = datenFilm.getThema(); + jg.writeString(datenFilm.getThema()); + } } + else + jg.writeString(datenFilm.getThema()); } private void writeZeit(JsonGenerator jg, DatenFilm datenFilm) throws IOException { diff --git a/src/main/java/mediathek/gui/actions/export/FilmListExportAction.java b/src/main/java/mediathek/gui/actions/export/FilmListExportAction.java index 8ab4127d7e..fc17d80fc6 100644 --- a/src/main/java/mediathek/gui/actions/export/FilmListExportAction.java +++ b/src/main/java/mediathek/gui/actions/export/FilmListExportAction.java @@ -1,7 +1,5 @@ package mediathek.gui.actions.export; -import javafx.scene.control.Alert; -import javafx.stage.Modality; import mediathek.config.Konstanten; import mediathek.javafx.tool.FXProgressPane; import mediathek.javafx.tool.JavaFxUtils; @@ -49,21 +47,17 @@ private void export(File selectedFile) { } private void showError() { - Alert alert = new Alert(Alert.AlertType.ERROR); - alert.setTitle(Konstanten.PROGRAMMNAME); - alert.setHeaderText(HEADER); - alert.setContentText("Es gab einen Fehler beim Export der Filmliste."); - alert.initModality(Modality.APPLICATION_MODAL); - alert.showAndWait(); + SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(MediathekGui.ui(), + "Es gab einen Fehler beim Export der Filmliste.", + Konstanten.PROGRAMMNAME, + JOptionPane.ERROR_MESSAGE)); } private void showSuccess() { - Alert alert = new Alert(Alert.AlertType.INFORMATION); - alert.setTitle(Konstanten.PROGRAMMNAME); - alert.setHeaderText(HEADER); - alert.setContentText("Der Export wurde erfolgreich beendet."); - alert.initModality(Modality.APPLICATION_MODAL); - alert.showAndWait(); + SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(MediathekGui.ui(), + "Der Export wurde erfolgreich abgeschlossen.", + Konstanten.PROGRAMMNAME, + JOptionPane.INFORMATION_MESSAGE)); } @Override diff --git a/src/main/java/mediathek/gui/actions/export/FilmListExportWorkerTask.java b/src/main/java/mediathek/gui/actions/export/FilmListExportWorkerTask.java index 9262003ca2..8bd40dd476 100644 --- a/src/main/java/mediathek/gui/actions/export/FilmListExportWorkerTask.java +++ b/src/main/java/mediathek/gui/actions/export/FilmListExportWorkerTask.java @@ -22,6 +22,9 @@ public FilmListExportWorkerTask(File selectedFile, boolean readable) { @Override protected Void call() { FilmListWriter writer = new FilmListWriter(readable); + // do not "compress" the sender tag + writer.setCompressSenderTag(false); + writer.setCompressThemaTag(false); writer.writeFilmList(selectedFile.getAbsolutePath(), Daten.getInstance().getListeFilme(), prog -> updateProgress(prog, 1d)); From 5be71cd4517df7a16337517b807909c9a4820405 Mon Sep 17 00:00:00 2001 From: Christian F Date: Mon, 27 Jun 2022 11:12:37 +0200 Subject: [PATCH 095/422] - reset filter dialog position in help menu Former-commit-id: 62e4fadbd690e7d23749dd451d6ec6ea165aaa95 --- CHANGELOG.md | 1 + .../gui/actions/ResetFilterDialogPosition.kt | 15 +++++++++++++++ .../java/mediathek/mainwindow/MediathekGui.java | 2 ++ 3 files changed, 18 insertions(+) create mode 100644 src/main/java/mediathek/gui/actions/ResetFilterDialogPosition.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 852fcbb2bd..c3f78deef5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - **FEATURE:** Icons wurden durch hochauflösende Vektorvarianten ersetzt. - **FEATURE:** Neues Suchfeld inklusive Suchhistorie. - **FEATURE:** Geoinformationen werden nun als *gesperrt/entsperrt* Icons dargestellt. Detailinformationen werden über Tooltip-Text eingeblendet. +- **FEATURE:** Ein manchmal unsichtbarer Filter-Dialog kann nun über das Menü *Hilfe/Filterdialog-Position zurücksetzen* auf den sichtbaren Monitorbereich zurückgesetzt werden. **13.9.1** diff --git a/src/main/java/mediathek/gui/actions/ResetFilterDialogPosition.kt b/src/main/java/mediathek/gui/actions/ResetFilterDialogPosition.kt new file mode 100644 index 0000000000..6f1359c0eb --- /dev/null +++ b/src/main/java/mediathek/gui/actions/ResetFilterDialogPosition.kt @@ -0,0 +1,15 @@ +package mediathek.gui.actions + +import mediathek.mainwindow.MediathekGui +import java.awt.event.ActionEvent +import javax.swing.AbstractAction + +class ResetFilterDialogPosition(private val mediathekGui: MediathekGui) : AbstractAction() { + init { + putValue(NAME, "Filterdialog-Position zurücksetzen") + } + + override fun actionPerformed(e: ActionEvent) { + mediathekGui.tabFilme?.filmActionPanel?.filterDialog?.setLocation(100, 100) + } +} \ No newline at end of file diff --git a/src/main/java/mediathek/mainwindow/MediathekGui.java b/src/main/java/mediathek/mainwindow/MediathekGui.java index 584c369334..e6cbae2d22 100644 --- a/src/main/java/mediathek/mainwindow/MediathekGui.java +++ b/src/main/java/mediathek/mainwindow/MediathekGui.java @@ -885,6 +885,8 @@ private void createHelpMenu() { jMenuHilfe.add(new ResetAboHistoryAction(this)); jMenuHilfe.add(new DeleteLocalFilmlistAction(this)); jMenuHilfe.addSeparator(); + jMenuHilfe.add(new ResetFilterDialogPosition(this)); + jMenuHilfe.addSeparator(); //do not show menu entry if we have external update support var externalUpdateCheck = System.getProperty(Konstanten.EXTERNAL_UPDATE_PROPERTY); if (externalUpdateCheck == null || !externalUpdateCheck.equalsIgnoreCase("true")) { From 2d39a065b1e982d91e3a888d7ebd73fe208232d9 Mon Sep 17 00:00:00 2001 From: Christian F Date: Mon, 27 Jun 2022 11:20:48 +0200 Subject: [PATCH 096/422] - use kotlin 1.7 Former-commit-id: 0d9ffca0d78340ed20b9a2686f267341e3a9e740 --- pom.xml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index b7a0a7d48b..d7b3a128be 100755 --- a/pom.xml +++ b/pom.xml @@ -78,7 +78,7 @@ 18 18 18 - 1.6.21 + 1.7.0 ${jdk.language.version} ${jdk.language.version} @@ -550,9 +550,6 @@ target/generated-sources/annotations ${kotlin.language.target} - - -Xuse-ir - @@ -566,9 +563,6 @@ ${project.basedir}/src/test/kotlin ${kotlin.language.target} - - -Xuse-ir - From e63914e4ab6587ed0c416698f99d65e56d75b818 Mon Sep 17 00:00:00 2001 From: Christian F Date: Mon, 27 Jun 2022 11:35:11 +0200 Subject: [PATCH 097/422] - remove support for setting app memory. Future versions of MV will use shenandoah GC to automatically free memory Former-commit-id: f12ed08ddec55b1848ceda1bcf5ab933a3d8c7c0 --- .../gui/actions/SetAppMemoryAction.kt | 78 ------------------- .../java/mediathek/mac/MediathekGuiMac.java | 11 --- .../mediathek/mainwindow/MediathekGui.java | 22 +----- 3 files changed, 1 insertion(+), 110 deletions(-) delete mode 100644 src/main/java/mediathek/gui/actions/SetAppMemoryAction.kt diff --git a/src/main/java/mediathek/gui/actions/SetAppMemoryAction.kt b/src/main/java/mediathek/gui/actions/SetAppMemoryAction.kt deleted file mode 100644 index f4cb7ef19c..0000000000 --- a/src/main/java/mediathek/gui/actions/SetAppMemoryAction.kt +++ /dev/null @@ -1,78 +0,0 @@ -package mediathek.gui.actions - -import mediathek.config.Konstanten -import mediathek.mainwindow.MediathekGui -import mediathek.tool.GuiFunktionenProgramme -import mediathek.tool.javafx.FXErrorDialog -import okio.buffer -import okio.sink -import org.apache.commons.lang3.SystemUtils -import org.apache.logging.log4j.LogManager -import java.awt.Desktop -import java.awt.event.ActionEvent -import java.io.File -import java.io.IOException -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.Paths -import javax.swing.AbstractAction -import javax.swing.JOptionPane - -class SetAppMemoryAction : AbstractAction() { - override fun actionPerformed(e: ActionEvent) { - val fileName: String - val optionsPath: Path - if (SystemUtils.IS_OS_MAC_OSX) { - //on macOS we cannot write into app bundle, we have to use a file in our config dir.. - fileName = MAC_VMOPTIONS_FILE - optionsPath = Paths.get(fileName) - if (Files.notExists(optionsPath)) { - //create dummy file - try { - optionsPath.sink().buffer().use { sink -> - sink.writeUtf8("### SEIEN SIE VORSICHTIG! FEHLERHAFTE EINTRÄGE VERHINDERN DEN START DER ANWENDUNG!\n") - sink.writeUtf8("-Xmx2G") - } - } catch (ex: IOException) { - logger.error(ex) - FXErrorDialog.showErrorDialogWithoutParent(Konstanten.PROGRAMMNAME, "Schreibvorgang fehlgeschlagen", - """ - Die Datei '${optionsPath.normalize().toAbsolutePath()}' konnte nicht geschrieben werden. - Bitte wenden Sie sich bei Fragen an das Forum! - """.trimIndent(), ex) - } - } - } else { - fileName = WIN_VMOPTIONS_FILE - optionsPath = Paths.get(GuiFunktionenProgramme.getPathToApplicationJar(), fileName) - } - val fileStr = optionsPath.normalize().toAbsolutePath().toString() - if (Desktop.isDesktopSupported()) { - try { - Desktop.getDesktop().open(optionsPath.normalize().toAbsolutePath().toFile()) - } catch (ex: Exception) { - logger.error("Failed to open vm options file", ex) - FXErrorDialog.showErrorDialogWithoutParent(Konstanten.PROGRAMMNAME, "Datei konnte nicht geöffnet werden", - """ - Es trat ein Fehler beim Öffnen der Datei auf. - Diese Funktion wird nur durch die offiziellen MediathekView-Apps unterstützt. - Bitte wenden Sie sich mit dem unten sichtbaren Fehler an das Forum. - """.trimIndent(), ex) - } - } else { - logger.error("Desktop is not supported") - JOptionPane.showMessageDialog(MediathekGui.ui(), "Datei konnte nicht geöffnet werden, da Java auf Ihrem System das Öffnen nicht unterstützt.
" + - "Bitte bearbeiten Sie manuell die Datei '" + fileStr + "'") - } - } - - companion object { - private const val WIN_VMOPTIONS_FILE = "MediathekView.vmoptions" - private val MAC_VMOPTIONS_FILE = SystemUtils.USER_HOME + File.separator + ".mediathek3" + File.separator + "MediathekView_vmoptions.txt" - private val logger = LogManager.getLogger() - } - - init { - putValue(NAME, "Speicherzuweisung ändern...") - } -} \ No newline at end of file diff --git a/src/main/java/mediathek/mac/MediathekGuiMac.java b/src/main/java/mediathek/mac/MediathekGuiMac.java index b626b5f030..f9089a0638 100644 --- a/src/main/java/mediathek/mac/MediathekGuiMac.java +++ b/src/main/java/mediathek/mac/MediathekGuiMac.java @@ -1,7 +1,6 @@ package mediathek.mac; import com.formdev.flatlaf.util.SystemInfo; -import mediathek.config.Konstanten; import mediathek.gui.actions.ShowAboutAction; import mediathek.gui.messages.DownloadFinishedEvent; import mediathek.gui.messages.DownloadStartEvent; @@ -22,16 +21,6 @@ public class MediathekGuiMac extends MediathekGui { protected static final Logger logger = LogManager.getLogger(MediathekGuiMac.class); private final OsxPowerManager powerManager = new OsxPowerManager(); - @Override - protected boolean officialLauncherInUse() { - boolean macOSBinaryInuse = true; - final var osxOfficialApp = System.getProperty(Konstanten.MACOS_OFFICIAL_APP); - if (osxOfficialApp == null || osxOfficialApp.isEmpty() || osxOfficialApp.equalsIgnoreCase("false")) { - macOSBinaryInuse = false; - } - return macOSBinaryInuse; - } - @Override protected void installAdditionalHelpEntries() { //unused on macOS diff --git a/src/main/java/mediathek/mainwindow/MediathekGui.java b/src/main/java/mediathek/mainwindow/MediathekGui.java index e6cbae2d22..beb91f93ca 100644 --- a/src/main/java/mediathek/mainwindow/MediathekGui.java +++ b/src/main/java/mediathek/mainwindow/MediathekGui.java @@ -893,28 +893,8 @@ private void createHelpMenu() { jMenuHilfe.add(searchProgramUpdateAction); } jMenuHilfe.add(new ShowProgramInfosAction()); - /* - we use Shenandoah GC and a signed app on macOS. Therefore there is no need to modify vm settings. - */ - if (officialLauncherInUse() && !SystemUtils.IS_OS_MAC_OSX) { - jMenuHilfe.addSeparator(); - jMenuHilfe.add(new SetAppMemoryAction()); - } - installAdditionalHelpEntries(); - } - /** - * Test if MediathekView is launched by our official downloads. - * @return true if official binary is used, false otherwise. - */ - protected boolean officialLauncherInUse() { - boolean winBinaryInUse = true; - final var externalUpdateCheck = System.getProperty(Konstanten.EXTERNAL_UPDATE_PROPERTY); - if (externalUpdateCheck == null || !externalUpdateCheck.equalsIgnoreCase("true")) { - winBinaryInUse = false; - } - - return winBinaryInUse; + installAdditionalHelpEntries(); } protected void installChangeGlobalFontSettingMenuEntry() { From 63175f1e052c6167e48f00b1f4c7a191a44258fd Mon Sep 17 00:00:00 2001 From: Christian F Date: Mon, 27 Jun 2022 13:12:30 +0200 Subject: [PATCH 098/422] - check for incorrect JVM parameters and display warning dialog when necessary Former-commit-id: 63079eb719727e3e22a4ee5ee69b46a67e0a12c4 --- CHANGELOG.md | 1 + src/main/java/mediathek/Main.java | 47 ++++++++++++++++++++++ src/main/java/mediathek/config/Config.java | 9 +++++ 3 files changed, 57 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3f78deef5..25c84d3729 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ **14.0.0** - User Interface für macOS überarbeitet +- Das Programm überprüft beim Start ob die JVM-Parameter korrekt sind und gibt ansonsten eine Warnung aus. Dies kann über die Paramter `-nj` oder `--no-jvm-param-checks` deaktiviert werden. - **BUGFIX:** *"Filme/Blacklist öffnen..."* Eintrag wurde in *"Filme/Blacklist bearbeiten..."* umbenannt und die Icons vereinheitlicht. - **BUGFIX:** Manchmal inkorrekte Darstellung des Bookmark-Status im Film-Tab wurde korrigiert. - **FEATURE:** Die Toolbars können nun zum Schweben in allen Tabs abgetrennt werden. diff --git a/src/main/java/mediathek/Main.java b/src/main/java/mediathek/Main.java index bdb3f3f4f2..5cf96cec9f 100644 --- a/src/main/java/mediathek/Main.java +++ b/src/main/java/mediathek/Main.java @@ -291,6 +291,50 @@ private static void setupFlatLaf() { UIManager.put("Table.alternateRowColor", JTABLE_ALTERNATE_ROW_COLOR); } + /** + * Check if Shenandoah GC settings are supplied to JVM. + * Otherwise display warning dialog. + */ + private static void checkJVMSettings() { + RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean(); + boolean correctParameters = false; + var paramList = runtimeMXBean.getInputArguments(); + + var useShenandoahGC = paramList.stream().filter(s -> s.equalsIgnoreCase("-XX:+UseShenandoahGC")).findAny().stream().count(); + var shenandoahHeuristics = paramList.stream().filter(s -> s.equalsIgnoreCase("-XX:ShenandoahGCHeuristics=compact")).findAny().stream().count(); + var stringDedup = paramList.stream().filter(s -> s.equalsIgnoreCase("-XX:+UseStringDeduplication")).findAny().stream().count(); + var maxRamPct = paramList.stream().filter(s -> s.startsWith("-XX:MaxRAMPercentage=")).findAny().stream().count(); + + //Incorrect VM params + var mxParamCount = paramList.stream().filter(s -> s.startsWith("-Xmx")).findAny().stream().count(); + + if ((useShenandoahGC > 0) + && (shenandoahHeuristics > 0) + && (stringDedup > 0) + && (maxRamPct > 0) + && (mxParamCount == 0)) + correctParameters = true; + + if (!correctParameters) { + //show error dialog + logger.warn("Detected incorrect JVM parameters! Please modify your settings"); + JOptionPane.showMessageDialog(null, + "" + + "Inkorrekte JVM Parameter erkannt

" + + "Bitte stellen Sie sicher, dass die folgenden Parameter an die JVM übergeben werden:
" + + "
    " + + "
  • -XX:+UseShenandoahGC
  • " + + "
  • -XX:ShenandoahGCHeuristics=compact
  • " + + "
  • -XX:+UseStringDeduplication
  • " + + "
  • -XX:MaxRAMPercentage=xx.x
  • " + + "

" + + "-Xmx sollte nicht mehr genutzt werden!" + + "", + Konstanten.PROGRAMMNAME, + JOptionPane.WARNING_MESSAGE); + } + } + /** * @param args the command line arguments */ @@ -321,6 +365,9 @@ public static void main(final String... args) { setupDockIcon(); setupFlatLaf(); + if (!Config.isDisableJvmParameterChecks()) + checkJVMSettings(); + if (SystemUtils.IS_OS_WINDOWS) { if (!VersionHelpers.IsWindows10OrGreater()) logger.warn("This Operating System configuration is too old and will be unsupported in the next updates."); diff --git a/src/main/java/mediathek/config/Config.java b/src/main/java/mediathek/config/Config.java index bd9250c23f..39538b733c 100644 --- a/src/main/java/mediathek/config/Config.java +++ b/src/main/java/mediathek/config/Config.java @@ -56,6 +56,15 @@ public class Config { private static boolean helpRequested; @CommandLine.Option(names = {"-f", "--disable-file-logging"}, description = "Speichern des Log output in Datei deaktivieren") private static boolean fileLoggingDisabled; + /** + * Disable JVM parameter checks on startup. + */ + @CommandLine.Option(names = {"-nj", "--no-jvm-param-checks"}, description = "JVM Parameter-Prüfung deaktivieren") + private static boolean disableJvmParameterChecks; + + public static boolean isDisableJvmParameterChecks() { + return disableJvmParameterChecks; + } public static boolean isInstallThreadCheckingRepaintManager() { return installThreadCheckingRepaintManager; From 346c9ebc6a4f7730dbf0cc1cd81ff71bd88eda2e Mon Sep 17 00:00:00 2001 From: Christian F Date: Tue, 28 Jun 2022 16:12:48 +0200 Subject: [PATCH 099/422] - make filter dialog visible by toggle action Former-commit-id: cfa5f4854637cad5502263451ddd1b71384a09d5 --- .../mediathek/gui/tabs/tab_film/GuiFilme.java | 22 ++++- .../javafx/filterpanel/FilmActionPanel.java | 5 +- .../javafx/filterpanel/SwingFilterDialog.java | 80 ++++++++++--------- 3 files changed, 63 insertions(+), 44 deletions(-) diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index 68fa1500a4..13d864323d 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -178,9 +178,17 @@ private void createToolBar() { toolBar.add(new JLabel("Suche:")); toolBar.add(searchField); toolBar.addSeparator(); - toolBar.add(showFilterDialogAction); + + //disable text + showFilterToggleBtn.setText(""); + final boolean visible = ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.FilterDialog.VISIBLE, false); + showFilterToggleBtn.setSelected(visible); + + toolBar.add(showFilterToggleBtn); } + protected JToggleButton showFilterToggleBtn = new JToggleButton(showFilterDialogAction); + @Handler private void handleTableModelChange(TableModelChangeEvent e) { if (e.active) { @@ -325,7 +333,7 @@ public void installMenuEntries(JMenu menu) { } private void setupFilmActionPanel() { - filmActionPanel = new FilmActionPanel(); + filmActionPanel = new FilmActionPanel(showFilterToggleBtn); } private void setupPsetButtonsPanel() { @@ -864,8 +872,14 @@ public ShowFilterDialogAction() { public void actionPerformed(ActionEvent e) { var dlg = filmActionPanel.filterDialog; if (dlg != null) { - if (!dlg.isVisible()) { - dlg.setVisible(true); + var visible = dlg.isVisible(); + visible = !visible; + + if (e.getSource() instanceof JToggleButton btn) { + dlg.setVisible(visible); + btn.setSelected(visible); + } else { + dlg.setVisible(visible); } } } diff --git a/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java b/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java index 4bba905cb3..8750f3e682 100644 --- a/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java +++ b/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java @@ -19,6 +19,7 @@ import org.apache.logging.log4j.Logger; import org.controlsfx.control.RangeSlider; import org.controlsfx.control.textfield.TextFields; +import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.util.ArrayList; @@ -55,14 +56,14 @@ public class FilmActionPanel { private CommonViewSettingsPane viewSettingsPane; - public FilmActionPanel() { + public FilmActionPanel(@NotNull JToggleButton filterToggleBtn) { this.filterConfig = new FilterConfiguration(); setupViewSettingsPane(); setupDeleteFilterButton(); SwingUtilities.invokeLater( - () -> filterDialog = new SwingFilterDialog(MediathekGui.ui(), viewSettingsPane)); + () -> filterDialog = new SwingFilterDialog(MediathekGui.ui(), viewSettingsPane, filterToggleBtn)); restoreConfigSettings(); diff --git a/src/main/java/mediathek/javafx/filterpanel/SwingFilterDialog.java b/src/main/java/mediathek/javafx/filterpanel/SwingFilterDialog.java index e1dc77e8a0..a38fb1965f 100644 --- a/src/main/java/mediathek/javafx/filterpanel/SwingFilterDialog.java +++ b/src/main/java/mediathek/javafx/filterpanel/SwingFilterDialog.java @@ -11,6 +11,7 @@ import mediathek.tool.MessageBus; import net.engio.mbassy.listener.Handler; import org.apache.commons.configuration2.sync.LockMode; +import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.*; @@ -20,9 +21,12 @@ public class SwingFilterDialog extends JDialog { private final JFXPanel fxPanel = new JFXPanel(); + private final JToggleButton filterToggleButton; - public SwingFilterDialog(Frame owner, CommonViewSettingsPane content) { + public SwingFilterDialog(Frame owner, CommonViewSettingsPane content, @NotNull JToggleButton filterToggleButton) { super(owner); + this.filterToggleButton = filterToggleButton; + setDefaultCloseOperation(HIDE_ON_CLOSE); setTitle("Filter"); setType(Type.UTILITY); @@ -33,7 +37,7 @@ public SwingFilterDialog(Frame owner, CommonViewSettingsPane content) { pack(); restoreWindowSizeFromConfig(); restoreDialogVisibility(); - registerWindowSizeListener(); + addComponentListener(new FilterDialogComponentListener()); }); }); @@ -90,46 +94,46 @@ private void restoreWindowSizeFromConfig() { } - private void registerWindowSizeListener() { - addComponentListener(new ComponentAdapter() { - @Override - public void componentResized(ComponentEvent e) { - storeWindowPosition(e); - } + public class FilterDialogComponentListener extends ComponentAdapter { + @Override + public void componentResized(ComponentEvent e) { + storeWindowPosition(e); + } - @Override - public void componentMoved(ComponentEvent e) { - storeWindowPosition(e); - } + @Override + public void componentMoved(ComponentEvent e) { + storeWindowPosition(e); + } - @Override - public void componentShown(ComponentEvent e) { - storeDialogVisibility(); - } + @Override + public void componentShown(ComponentEvent e) { + storeDialogVisibility(); + filterToggleButton.setSelected(true); + } - @Override - public void componentHidden(ComponentEvent e) { - storeWindowPosition(e); - storeDialogVisibility(); - } + @Override + public void componentHidden(ComponentEvent e) { + storeWindowPosition(e); + storeDialogVisibility(); - private void storeWindowPosition(ComponentEvent e) { - var config = ApplicationConfiguration.getConfiguration(); - var component = e.getComponent(); - - var dims = component.getSize(); - var loc = component.getLocation(); - try { - config.lock(LockMode.WRITE); - config.setProperty(ApplicationConfiguration.FilterDialog.WIDTH, dims.width); - config.setProperty(ApplicationConfiguration.FilterDialog.HEIGHT, dims.height); - config.setProperty(ApplicationConfiguration.FilterDialog.X, loc.x); - config.setProperty(ApplicationConfiguration.FilterDialog.Y, loc.y); - } finally { - config.unlock(LockMode.WRITE); - } - } - }); + filterToggleButton.setSelected(false); + } + private void storeWindowPosition(ComponentEvent e) { + var config = ApplicationConfiguration.getConfiguration(); + var component = e.getComponent(); + + var dims = component.getSize(); + var loc = component.getLocation(); + try { + config.lock(LockMode.WRITE); + config.setProperty(ApplicationConfiguration.FilterDialog.WIDTH, dims.width); + config.setProperty(ApplicationConfiguration.FilterDialog.HEIGHT, dims.height); + config.setProperty(ApplicationConfiguration.FilterDialog.X, loc.x); + config.setProperty(ApplicationConfiguration.FilterDialog.Y, loc.y); + } finally { + config.unlock(LockMode.WRITE); + } + } } } From ee9930f04a6cbcedc26b3a8e2061187fb8809d1e Mon Sep 17 00:00:00 2001 From: Christian F Date: Tue, 28 Jun 2022 16:16:22 +0200 Subject: [PATCH 100/422] - remove unnecessary code Former-commit-id: 37ef1771668e57fee7e82d3de3cc775e21b86ed4 --- src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index 13d864323d..d15839834a 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -875,13 +875,7 @@ public void actionPerformed(ActionEvent e) { var visible = dlg.isVisible(); visible = !visible; - if (e.getSource() instanceof JToggleButton btn) { - dlg.setVisible(visible); - btn.setSelected(visible); - } else { - dlg.setVisible(visible); - } - } + dlg.setVisible(visible); } } } From 6c90729ca3c06ed1aeb15836bedf2907fb68e04b Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 29 Jun 2022 09:47:28 +0200 Subject: [PATCH 101/422] - remove warning suppression Former-commit-id: 14d064db7e4282550b8fd7d5ba2895a3092fb805 --- src/main/java/mediathek/tool/EscapeKeyHandler.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/mediathek/tool/EscapeKeyHandler.java b/src/main/java/mediathek/tool/EscapeKeyHandler.java index 40aba5a4f5..4e07582f87 100644 --- a/src/main/java/mediathek/tool/EscapeKeyHandler.java +++ b/src/main/java/mediathek/tool/EscapeKeyHandler.java @@ -7,7 +7,6 @@ import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; -@SuppressWarnings("serial") public class EscapeKeyHandler { private static final String CANCEL_KEY_HANDLER = "key_cancel"; From 894422ff5bff79452a84b5bd2f5065ff461f9f9b Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 29 Jun 2022 10:04:06 +0200 Subject: [PATCH 102/422] - fix filter dialog visibility toggle Former-commit-id: 6cf0c2598394c349edbd8ffcf94e6455517cfe9e --- .../mediathek/gui/tabs/tab_film/GuiFilme.java | 36 ++++++++++--------- .../javafx/filterpanel/SwingFilterDialog.java | 18 ++++++++++ .../mediathek/mainwindow/MediathekGui.java | 2 +- 3 files changed, 39 insertions(+), 17 deletions(-) diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index d15839834a..1d350d2ace 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -110,13 +110,13 @@ public class GuiFilme extends AGuiTabPanel { * The JavaFx Film action popup panel. */ public FilmActionPanel filmActionPanel; - public ShowFilterDialogAction showFilterDialogAction = new ShowFilterDialogAction(); + public ToggleFilterDialogVisibilityAction toggleFilterDialogVisibilityAction = new ToggleFilterDialogVisibilityAction(); protected SearchField searchField = new SearchField(); protected JComboBox filterSelectionComboBox = new JComboBox<>(new FilterSelectionComboBoxModel()); protected PauseTransition reloadTableDataTransition = new PauseTransition(Duration.millis(250d)); + protected FilterVisibilityToggleButton btnToggleFilterDialogVisibility = new FilterVisibilityToggleButton(toggleFilterDialogVisibilityAction); private Optional bookmarkWindowController = Optional.empty(); - private boolean stopBeob; private FilmTabInfoPane filmInfoLabel; private JCheckBoxMenuItem cbShowButtons; @@ -130,6 +130,7 @@ public class GuiFilme extends AGuiTabPanel { * We perform model filtering in the background the keep UI thread alive. */ private ListenableFuture modelFuture; + public GuiFilme(Daten aDaten, MediathekGui mediathekGui) { super(); daten = aDaten; @@ -179,16 +180,9 @@ private void createToolBar() { toolBar.add(searchField); toolBar.addSeparator(); - //disable text - showFilterToggleBtn.setText(""); - final boolean visible = ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.FilterDialog.VISIBLE, false); - showFilterToggleBtn.setSelected(visible); - - toolBar.add(showFilterToggleBtn); + toolBar.add(btnToggleFilterDialogVisibility); } - protected JToggleButton showFilterToggleBtn = new JToggleButton(showFilterDialogAction); - @Handler private void handleTableModelChange(TableModelChangeEvent e) { if (e.active) { @@ -196,7 +190,7 @@ private void handleTableModelChange(TableModelChangeEvent e) { SwingUtilities.invokeAndWait(() -> playFilmAction.setEnabled(false)); SwingUtilities.invokeAndWait(() -> saveFilmAction.setEnabled(false)); SwingUtilities.invokeAndWait(() -> bookmarkFilmAction.setEnabled(false)); - SwingUtilities.invokeAndWait(() -> showFilterDialogAction.setEnabled(false)); + SwingUtilities.invokeAndWait(() -> toggleFilterDialogVisibilityAction.setEnabled(false)); SwingUtilities.invokeAndWait(() -> searchField.setEnabled(false)); SwingUtilities.invokeAndWait(() -> filterSelectionComboBox.setEnabled(false)); } catch (InterruptedException | InvocationTargetException ex) { @@ -208,7 +202,7 @@ private void handleTableModelChange(TableModelChangeEvent e) { SwingUtilities.invokeAndWait(() -> playFilmAction.setEnabled(true)); SwingUtilities.invokeAndWait(() -> saveFilmAction.setEnabled(true)); SwingUtilities.invokeAndWait(() -> bookmarkFilmAction.setEnabled(true)); - SwingUtilities.invokeAndWait(() -> showFilterDialogAction.setEnabled(true)); + SwingUtilities.invokeAndWait(() -> toggleFilterDialogVisibilityAction.setEnabled(true)); SwingUtilities.invokeAndWait(() -> searchField.setEnabled(true)); SwingUtilities.invokeAndWait(() -> filterSelectionComboBox.setEnabled(true)); } catch (InterruptedException | InvocationTargetException ex) { @@ -333,7 +327,7 @@ public void installMenuEntries(JMenu menu) { } private void setupFilmActionPanel() { - filmActionPanel = new FilmActionPanel(showFilterToggleBtn); + filmActionPanel = new FilmActionPanel(btnToggleFilterDialogVisibility); } private void setupPsetButtonsPanel() { @@ -860,8 +854,17 @@ public void onFailure(@NotNull Throwable thrown) { decoratedPool); } - public class ShowFilterDialogAction extends AbstractAction { - public ShowFilterDialogAction() { + static class FilterVisibilityToggleButton extends JToggleButton { + public FilterVisibilityToggleButton(Action a) { + super(a); + setText(""); + final boolean visible = ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.FilterDialog.VISIBLE, false); + setSelected(visible); + } + } + + public class ToggleFilterDialogVisibilityAction extends AbstractAction { + public ToggleFilterDialogVisibilityAction() { putValue(Action.NAME, "Filterdialog anzeigen"); putValue(Action.SHORT_DESCRIPTION, "Filter anzeigen"); putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/filter.svg")); @@ -875,7 +878,8 @@ public void actionPerformed(ActionEvent e) { var visible = dlg.isVisible(); visible = !visible; - dlg.setVisible(visible); } + dlg.setVisible(visible); + } } } diff --git a/src/main/java/mediathek/javafx/filterpanel/SwingFilterDialog.java b/src/main/java/mediathek/javafx/filterpanel/SwingFilterDialog.java index a38fb1965f..6dba2d0bf0 100644 --- a/src/main/java/mediathek/javafx/filterpanel/SwingFilterDialog.java +++ b/src/main/java/mediathek/javafx/filterpanel/SwingFilterDialog.java @@ -17,6 +17,7 @@ import java.awt.*; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; +import java.awt.event.KeyEvent; import java.util.NoSuchElementException; public class SwingFilterDialog extends JDialog { @@ -27,6 +28,9 @@ public SwingFilterDialog(Frame owner, CommonViewSettingsPane content, @NotNull J super(owner); this.filterToggleButton = filterToggleButton; + ToggleVisibilityKeyHandler handler = new ToggleVisibilityKeyHandler(this); + handler.installHandler(filterToggleButton.getAction()); + setDefaultCloseOperation(HIDE_ON_CLOSE); setTitle("Filter"); setType(Type.UTILITY); @@ -94,6 +98,20 @@ private void restoreWindowSizeFromConfig() { } + static class ToggleVisibilityKeyHandler { + private static final String TOGGLE_FILTER_VISIBILITY = "toggle_dialog_visibility"; + private final JRootPane rootPane; + public ToggleVisibilityKeyHandler(JDialog dlg) { + this.rootPane = dlg.getRootPane(); + } + + public void installHandler(Action action) { + final var inputMap = rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); + inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_F12, 0), TOGGLE_FILTER_VISIBILITY); + rootPane.getActionMap().put(TOGGLE_FILTER_VISIBILITY, action); + } + } + public class FilterDialogComponentListener extends ComponentAdapter { @Override public void componentResized(ComponentEvent e) { diff --git a/src/main/java/mediathek/mainwindow/MediathekGui.java b/src/main/java/mediathek/mainwindow/MediathekGui.java index beb91f93ca..1b679c4328 100644 --- a/src/main/java/mediathek/mainwindow/MediathekGui.java +++ b/src/main/java/mediathek/mainwindow/MediathekGui.java @@ -860,7 +860,7 @@ private void createViewMenu() { jMenuAnsicht.add(showMemoryMonitorAction); jMenuAnsicht.add(cbBandwidthDisplay); jMenuAnsicht.addSeparator(); - jMenuAnsicht.add(tabFilme.showFilterDialogAction); + jMenuAnsicht.add(tabFilme.toggleFilterDialogVisibilityAction); jMenuAnsicht.addSeparator(); jMenuAnsicht.add(showFilmInformationAction); jMenuAnsicht.addSeparator(); From a0e103eb938b821f708a4a147152d941f437821b Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 29 Jun 2022 10:30:02 +0200 Subject: [PATCH 103/422] - replace JavaFX alerts with swing code Former-commit-id: d0f2a1480cf2400b62930b27c5fda0fa7637b789 --- .../java/mediathek/filmlisten/FilmeLaden.java | 35 +++++++------------ 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/src/main/java/mediathek/filmlisten/FilmeLaden.java b/src/main/java/mediathek/filmlisten/FilmeLaden.java index fb737a39be..57d5fb4a18 100644 --- a/src/main/java/mediathek/filmlisten/FilmeLaden.java +++ b/src/main/java/mediathek/filmlisten/FilmeLaden.java @@ -1,8 +1,6 @@ package mediathek.filmlisten; import com.google.common.base.Stopwatch; -import javafx.application.Platform; -import javafx.scene.control.Alert; import mediathek.config.Daten; import mediathek.config.Konstanten; import mediathek.config.StandardLocations; @@ -14,14 +12,13 @@ import mediathek.gui.actions.FilmListWriteWorkerTask; import mediathek.javafx.FilmListFilterTask; import mediathek.javafx.tool.FXProgressPane; -import mediathek.javafx.tool.JFXHiddenApplication; import mediathek.javafx.tool.JavaFxUtils; import mediathek.mainwindow.MediathekGui; import mediathek.tool.ApplicationConfiguration; import mediathek.tool.FilmListUpdateType; import mediathek.tool.GuiFunktionen; +import mediathek.tool.SwingErrorDialog; import mediathek.tool.http.MVHttpClient; -import mediathek.tool.javafx.FXErrorDialog; import okhttp3.HttpUrl; import okhttp3.Request; import okhttp3.Response; @@ -46,7 +43,6 @@ public class FilmeLaden { private static final Logger logger = LogManager.getLogger(FilmeLaden.class); private static final String NETWORK_NOT_AVAILABLE = "Netzwerk nicht verfügbar"; - private static final String DIALOG_TITLE = "Filmliste laden"; private static final String NO_UPDATE_AVAILABLE = "Es ist keine aktuellere Filmliste verfügbar."; /** * HTTP error code for not found. @@ -86,7 +82,7 @@ public synchronized void fertig(ListenerFilmeLadenEvent event) { private void showNoUpdateAvailableDialog() { JOptionPane.showMessageDialog(MediathekGui.ui(), NO_UPDATE_AVAILABLE, - DIALOG_TITLE, JOptionPane.INFORMATION_MESSAGE); + Konstanten.PROGRAMMNAME, JOptionPane.INFORMATION_MESSAGE); } /** @@ -125,23 +121,20 @@ private boolean hasNewRemoteFilmlist() { } } catch (UnknownHostException ex) { logger.debug(ex); - if (showDialogs) - Platform.runLater(() -> - FXErrorDialog.showErrorDialog(Konstanten.PROGRAMMNAME, DIALOG_TITLE, NETWORK_NOT_AVAILABLE, ex)); + if (showDialogs) { + SwingErrorDialog.showExceptionMessage(MediathekGui.ui(), NETWORK_NOT_AVAILABLE, ex); + } else logger.warn(NETWORK_NOT_AVAILABLE); } catch (IOException ex) { logger.error("IOxception:", ex); - Platform.runLater(() -> - FXErrorDialog.showErrorDialog(Konstanten.PROGRAMMNAME, DIALOG_TITLE, - "Netzwerkfehler aufgetreten!", ex)); + SwingErrorDialog.showExceptionMessage(MediathekGui.ui(), "Netzwerkfehler aufgetreten!", ex); } catch (Exception ex) { logger.error("check for filmliste.id failed", ex); - if (showDialogs) - Platform.runLater(() -> - FXErrorDialog.showErrorDialog(Konstanten.PROGRAMMNAME, DIALOG_TITLE, - "Ein unbekannter Fehler ist aufgetreten.", ex)); + if (showDialogs) { + SwingErrorDialog.showExceptionMessage(MediathekGui.ui(), "Ein unbekannter Fehler ist aufgetreten.", ex); + } } return result; @@ -287,12 +280,10 @@ private void undEnde(ListenerFilmeLadenEvent event) { if (event.fehler) { logger.info(""); logger.info("Filmliste laden war fehlerhaft, alte Liste wird wieder geladen"); - Platform.runLater(() -> { - Alert alert = new Alert(Alert.AlertType.ERROR); - alert.setHeaderText("Fehler"); - alert.setContentText("Das Laden der Filmliste hat nicht geklappt!"); - JFXHiddenApplication.showAlert(alert, ui); - }); + JOptionPane.showMessageDialog(MediathekGui.ui(), + "Das Laden der Filmliste hat nicht geklappt!", + Konstanten.PROGRAMMNAME, + JOptionPane.ERROR_MESSAGE); // dann die alte Liste wieder laden listeFilme.clear(); From 87194a9976226c198a8816765751f98c949c34a7 Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 29 Jun 2022 10:34:20 +0200 Subject: [PATCH 104/422] - tune exception text view size Former-commit-id: e18e9b71f2681c37f88466863bfbceff08bd469f --- src/main/java/mediathek/tool/SwingErrorDialog.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/mediathek/tool/SwingErrorDialog.java b/src/main/java/mediathek/tool/SwingErrorDialog.java index 1cb49ccdd4..737abaa6db 100644 --- a/src/main/java/mediathek/tool/SwingErrorDialog.java +++ b/src/main/java/mediathek/tool/SwingErrorDialog.java @@ -26,7 +26,7 @@ public static void showExceptionMessage(@NotNull Component parentComponent, text.setCaretPosition(0); JScrollPane scroller = new JScrollPane(text); - scroller.setPreferredSize(new Dimension(400, 200)); + scroller.setPreferredSize(new Dimension(640, 350)); JPanel panel = new JPanel(); panel.setLayout(new BorderLayout()); From 17897893f813786d9e78464d3880c935759a647e Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 29 Jun 2022 10:46:10 +0200 Subject: [PATCH 105/422] - use zulu for 64 bit arm builds without JavaFX as it will be downloaded as maven dependency Former-commit-id: 9cc7899f3969fda74d941baa4e1344b650778ab5 --- .install4j/mediathekview_arm.install4j | 2 +- pom.xml | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.install4j/mediathekview_arm.install4j b/.install4j/mediathekview_arm.install4j index 99420d9f84..14386d4753 100644 --- a/.install4j/mediathekview_arm.install4j +++ b/.install4j/mediathekview_arm.install4j @@ -5,7 +5,7 @@ - + diff --git a/pom.xml b/pom.xml index d7b3a128be..9b92a87c9c 100755 --- a/pom.xml +++ b/pom.xml @@ -122,9 +122,11 @@ 17/jdk-17.0.2+8 - + 17/17.0.2+9 - 17 + + 18/18.0.1 + 18 -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=compact -XX:MaxRAMPercentage=50.0 From c4e5356441012fc7ccabb491ac45b84c794f6c69 Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 29 Jun 2022 11:16:29 +0200 Subject: [PATCH 106/422] - show CC icon in title when subtitles are available Former-commit-id: 7c4336a4cb1e558c04cc5d7de3898fa7b3863c6f --- CHANGELOG.md | 1 + .../tool/cellrenderer/CellRendererFilme.java | 24 +++++++++++++++++++ .../icons/fontawesome/closed-captioning.svg | 1 + 3 files changed, 26 insertions(+) create mode 100755 src/main/resources/icons/fontawesome/closed-captioning.svg diff --git a/CHANGELOG.md b/CHANGELOG.md index 25c84d3729..681d87a8bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - **FEATURE:** Neues Suchfeld inklusive Suchhistorie. - **FEATURE:** Geoinformationen werden nun als *gesperrt/entsperrt* Icons dargestellt. Detailinformationen werden über Tooltip-Text eingeblendet. - **FEATURE:** Ein manchmal unsichtbarer Filter-Dialog kann nun über das Menü *Hilfe/Filterdialog-Position zurücksetzen* auf den sichtbaren Monitorbereich zurückgesetzt werden. +- **FEATURE:** Untertitelverfügbarkeit wird nun über ein `cc`-Icon an Anfang des Titels angezeigt. Die Spalte `UT` wird in einer späteren Programmversion entfernt werden. **13.9.1** diff --git a/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java b/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java index 303472a076..a9344af54d 100644 --- a/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java +++ b/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java @@ -29,6 +29,8 @@ public class CellRendererFilme extends CellRendererBaseWithStart { private final FlatSVGIcon normalBookmarkIcon; private final FlatSVGIcon selectedBookmarkIconHighlighted; + private final FlatSVGIcon subtitleIcon; + private final FlatSVGIcon subtitleIconSelected; public CellRendererFilme() { selectedDownloadIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/download.svg"); selectedDownloadIcon.setColorFilter(whiteColorFilter); @@ -52,6 +54,10 @@ public CellRendererFilme() { selectedBookmarkIconHighlighted.setColorFilter(new FlatSVGIcon.ColorFilter(color -> Color.ORANGE)); normalBookmarkIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/bookmark.svg"); + + subtitleIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/closed-captioning.svg"); + subtitleIconSelected = SVGIconUtilities.createSVGIcon("icons/fontawesome/closed-captioning.svg"); + subtitleIconSelected.setColorFilter(whiteColorFilter); } private JTextArea createTextArea(String content) { @@ -133,6 +139,8 @@ public Component getTableCellRendererComponent( var columnWidth = table.getColumnModel().getColumn(columnModelIndex).getWidth(); if (columnWidth < table.getFontMetrics(table.getFont()).stringWidth(title)) setToolTipText(title); + setText(title); + setSubtitleIcon(datenFilm, isSelected); break; case DatenFilm.FILM_GEO: @@ -148,6 +156,22 @@ public Component getTableCellRendererComponent( return this; } + /** + * Show "cc" icon when subtitle is available + * @param datenFilm film information + * @param isSelected is row selected. + */ + private void setSubtitleIcon(@NotNull DatenFilm datenFilm, boolean isSelected) { + if (datenFilm.hasSubtitle()) { + Icon icon; + if (isSelected) + icon = subtitleIconSelected; + else + icon = subtitleIcon; + setIcon(icon); + } + } + /** * Apply the specific horizontal alignment to the cell based on column * diff --git a/src/main/resources/icons/fontawesome/closed-captioning.svg b/src/main/resources/icons/fontawesome/closed-captioning.svg new file mode 100755 index 0000000000..a3413d62b9 --- /dev/null +++ b/src/main/resources/icons/fontawesome/closed-captioning.svg @@ -0,0 +1 @@ + \ No newline at end of file From bc1bbfae93b930f878b8f08e61b2d6083652d49e Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 29 Jun 2022 11:49:33 +0200 Subject: [PATCH 107/422] - refactor URL decompression code Former-commit-id: ddd0178389392fc4e67f275e6506647d9c8753f3 --- src/main/java/mediathek/daten/DatenFilm.java | 27 +++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/main/java/mediathek/daten/DatenFilm.java b/src/main/java/mediathek/daten/DatenFilm.java index eee1c1c7d9..6d36896bbc 100644 --- a/src/main/java/mediathek/daten/DatenFilm.java +++ b/src/main/java/mediathek/daten/DatenFilm.java @@ -46,6 +46,10 @@ public class DatenFilm implements Comparable { public static final int FILM_DATUM_LONG = 15; public static final int FILM_REF = 16; // no getter/setter access // Referenz auf this public static final int MAX_ELEM = 17; + /** + * Compressed URLs are missing the base normal quality URL and are indicated by the pipe-symbol. + */ + public static final char COMPRESSION_MARKER = '|'; /** * The database instance for all descriptions. */ @@ -444,13 +448,10 @@ private String getUrlNormalOrRequested(@NotNull FilmResolution.Enum resolution) ret = getUrlNormalQuality(); else { try { - // check if url contains pipe symbol... - final int indexPipe = requestedUrl.indexOf('|'); - if (indexPipe == -1) { //No + if (isUrlCompressed(requestedUrl)) + ret = decompressUrl(requestedUrl); + else ret = requestedUrl; - } else { //Yes - ret = decompressUrl(requestedUrl, indexPipe); - } } catch (Exception e) { ret = ""; logger.error("getUrlNormalOrRequested(auflösung: {}, requestedUrl: {})", resolution, requestedUrl, e); @@ -460,7 +461,19 @@ private String getUrlNormalOrRequested(@NotNull FilmResolution.Enum resolution) return ret; } - private String decompressUrl(@NotNull final String requestedUrl, final int indexPipe) { + /** + * URLs are considered compressed if they contain a '|'-symbol in the text. + * They need to be decompressed before use. + * @param requestedUrl the string to be checked. + * @return true if url is compressed, false otherwise. + */ + public boolean isUrlCompressed(@NotNull String requestedUrl) { + final int indexPipe = requestedUrl.indexOf(COMPRESSION_MARKER); + return indexPipe != -1; + } + + private String decompressUrl(@NotNull final String requestedUrl) throws NumberFormatException, IndexOutOfBoundsException { + final int indexPipe = requestedUrl.indexOf(COMPRESSION_MARKER); final int i = Integer.parseInt(requestedUrl.substring(0, indexPipe)); return getUrlNormalQuality().substring(0, i) + requestedUrl.substring(indexPipe + 1); } From b10f55d5a86a254e299b314bb9eafe55f147015b Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 30 Jun 2022 10:28:05 +0200 Subject: [PATCH 108/422] - remove unused JavaFX code - fix analyzer warning Former-commit-id: ae0b5db27c4de4906ca2fa9d6da7c77a451a52a6 --- .../java/mediathek/gui/abo/ManageAboDialog.kt | 2 +- .../mediathek/gui/tabs/tab_film/GuiFilme.java | 7 -- .../tabs/tab_film/SaveDownloadController.java | 100 --------------- .../gui/tabs/tab_film/SaveDownloadDialog.java | 45 ------- .../programm/fxml/save_download_dialog.fxml | 119 ------------------ 5 files changed, 1 insertion(+), 272 deletions(-) delete mode 100644 src/main/java/mediathek/gui/tabs/tab_film/SaveDownloadController.java delete mode 100644 src/main/java/mediathek/gui/tabs/tab_film/SaveDownloadDialog.java delete mode 100644 src/main/resources/mediathek/res/programm/fxml/save_download_dialog.fxml diff --git a/src/main/java/mediathek/gui/abo/ManageAboDialog.kt b/src/main/java/mediathek/gui/abo/ManageAboDialog.kt index c9fabdcc7f..0b18c663b6 100644 --- a/src/main/java/mediathek/gui/abo/ManageAboDialog.kt +++ b/src/main/java/mediathek/gui/abo/ManageAboDialog.kt @@ -36,7 +36,7 @@ class ManageAboDialog(owner: Frame?) : JDialog(owner) { setSize(width, height) setLocation(x,y) } - catch(e: NoSuchElementException) { + catch(_: NoSuchElementException) { } finally { config.unlock(LockMode.READ) diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index 1d350d2ace..8b40e5e09c 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -579,13 +579,6 @@ private void saveFilm(DatenFilm datenFilm, DatenPset pSet) { "Ohne Programm-Sets können keine Downloads gestartet werden.", Konstanten.PROGRAMMNAME, JOptionPane.ERROR_MESSAGE); } else { - //FIXME remove for production!!! - /*SaveDownloadDialog dlg = new SaveDownloadDialog(datenFilm, pSet); - dlg.setVisible(true); - if (dlg.controller.success()) - System.out.println("SUCCESS"); - else - System.out.println("NO SUCCESS");*/ // dann alle Downloads im Dialog abfragen Optional res = filmActionPanel.showOnlyHd.getValue() ? Optional.of(FilmResolution.Enum.HIGH_QUALITY) : Optional.empty(); diff --git a/src/main/java/mediathek/gui/tabs/tab_film/SaveDownloadController.java b/src/main/java/mediathek/gui/tabs/tab_film/SaveDownloadController.java deleted file mode 100644 index bfbffb374e..0000000000 --- a/src/main/java/mediathek/gui/tabs/tab_film/SaveDownloadController.java +++ /dev/null @@ -1,100 +0,0 @@ -package mediathek.gui.tabs.tab_film; - -import javafx.embed.swing.SwingFXUtils; -import javafx.fxml.FXML; -import javafx.fxml.Initializable; -import javafx.scene.control.Button; -import javafx.scene.control.ComboBox; -import javafx.scene.control.Label; -import javafx.scene.image.ImageView; -import mediathek.config.Daten; -import mediathek.daten.DatenFilm; -import mediathek.daten.DatenPset; -import mediathek.javafx.tool.JavaFxUtils; -import mediathek.tool.sender_icon_cache.MVSenderIconCache; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import java.net.URL; -import java.util.ResourceBundle; - -public class SaveDownloadController implements Initializable { - private final JDialog dialog; - @FXML - private Button saveBtn; - @FXML - private Button cancelBtn; - @FXML - private Label lblThema; - @FXML - private Label lblTitle; - @FXML - ImageView ivSender; - @FXML - ComboBox cBxPSet; - - private boolean success; - private final DatenFilm film; - private final DatenPset pSet; - - public SaveDownloadController(@NotNull JDialog dialog, @NotNull DatenFilm film, @Nullable DatenPset pSet) { - this.dialog = dialog; - this.film = film; - this.pSet = pSet; - } - - private void setupButtonBarActions() { - saveBtn.setOnAction(e -> { - success = true; - dialog.dispose(); - }); - - cancelBtn.setOnAction(e -> { - success = false; - dialog.dispose(); - }); - } - - private void setupSenderLogo() { - var icn = MVSenderIconCache.get(film.getSender()); - icn.ifPresentOrElse(icon -> { - var image = SwingFXUtils.toFXImage(JavaFxUtils.toBufferedImage(icon),null); - ivSender.setImage(image); - }, () -> ivSender.setImage(null)); - } - - private void setupProgramSetComboBox() { - var saveOnlyPSets = Daten.listePset.getListeSpeichern(); - for (var pSet : saveOnlyPSets) { - cBxPSet.getItems().add(pSet.arr[DatenPset.PROGRAMMSET_NAME]); - } - - if (pSet != null) { - cBxPSet.getSelectionModel().select(pSet.arr[DatenPset.PROGRAMMSET_NAME]); - } - else - cBxPSet.getSelectionModel().selectFirst(); - - if (cBxPSet.getItems().size() <= 1) { - cBxPSet.setDisable(true); - } - - //TODO change resolution on PSet change... - } - - @Override - public void initialize(URL location, ResourceBundle resources) { - setupButtonBarActions(); - setupSenderLogo(); - - lblThema.setText(film.getThema()); - lblTitle.setText(film.getTitle()); - - setupProgramSetComboBox(); - } - - public boolean success() { - return success; - } -} diff --git a/src/main/java/mediathek/gui/tabs/tab_film/SaveDownloadDialog.java b/src/main/java/mediathek/gui/tabs/tab_film/SaveDownloadDialog.java deleted file mode 100644 index f4f3243eeb..0000000000 --- a/src/main/java/mediathek/gui/tabs/tab_film/SaveDownloadDialog.java +++ /dev/null @@ -1,45 +0,0 @@ -package mediathek.gui.tabs.tab_film; - -import javafx.embed.swing.JFXPanel; -import javafx.fxml.FXMLLoader; -import javafx.scene.Scene; -import javafx.scene.layout.BorderPane; -import mediathek.daten.DatenFilm; -import mediathek.daten.DatenPset; -import mediathek.javafx.tool.JavaFxUtils; -import mediathek.tool.GuiFunktionen; - -import javax.swing.*; -import java.awt.*; -import java.io.IOException; -import java.net.URL; - -class SaveDownloadDialog extends JDialog { - public SaveDownloadController controller; - - public SaveDownloadDialog(DatenFilm datenFilm, DatenPset pSet) { - setTitle("FX Film Speichern"); - setModal(true); - JFXPanel fxPanel = new JFXPanel(); - - var contentPane = getContentPane(); - contentPane.setLayout(new BorderLayout()); - contentPane.add(fxPanel, BorderLayout.CENTER); - JavaFxUtils.invokeInFxThreadAndWait(() -> { - try { - URL url = getClass().getResource("/mediathek/res/programm/fxml/save_download_dialog.fxml"); - FXMLLoader fxmlLoader = new FXMLLoader(url); - controller = new SaveDownloadController(this, datenFilm, pSet); - fxmlLoader.setController(controller); - BorderPane p = fxmlLoader.load(); - fxPanel.setScene(new Scene(p)); - } catch (IOException e) { - e.printStackTrace(); - } - SwingUtilities.invokeLater(() -> { - pack(); - GuiFunktionen.centerOnScreen(this, false); - }); - }); - } -} diff --git a/src/main/resources/mediathek/res/programm/fxml/save_download_dialog.fxml b/src/main/resources/mediathek/res/programm/fxml/save_download_dialog.fxml deleted file mode 100644 index 48501145cb..0000000000 --- a/src/main/resources/mediathek/res/programm/fxml/save_download_dialog.fxml +++ /dev/null @@ -1,119 +0,0 @@ - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From ccaa5be32f6377aa70a0d529714a660e13203fe9 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 30 Jun 2022 10:42:47 +0200 Subject: [PATCH 109/422] - decompress URLs for readable filmlist export file by default Former-commit-id: d8589d0b21d96298eb399a4775d6d7482673ba89 --- src/main/java/mediathek/daten/DatenFilm.java | 4 +-- .../filmlisten/writer/FilmListWriter.java | 33 +++++++++++++++++-- .../export/FilmListExportWorkerTask.java | 1 + 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/main/java/mediathek/daten/DatenFilm.java b/src/main/java/mediathek/daten/DatenFilm.java index 6d36896bbc..d90142db5f 100644 --- a/src/main/java/mediathek/daten/DatenFilm.java +++ b/src/main/java/mediathek/daten/DatenFilm.java @@ -467,12 +467,12 @@ private String getUrlNormalOrRequested(@NotNull FilmResolution.Enum resolution) * @param requestedUrl the string to be checked. * @return true if url is compressed, false otherwise. */ - public boolean isUrlCompressed(@NotNull String requestedUrl) { + public static boolean isUrlCompressed(@NotNull String requestedUrl) { final int indexPipe = requestedUrl.indexOf(COMPRESSION_MARKER); return indexPipe != -1; } - private String decompressUrl(@NotNull final String requestedUrl) throws NumberFormatException, IndexOutOfBoundsException { + public String decompressUrl(@NotNull final String requestedUrl) throws NumberFormatException, IndexOutOfBoundsException { final int indexPipe = requestedUrl.indexOf(COMPRESSION_MARKER); final int i = Integer.parseInt(requestedUrl.substring(0, indexPipe)); return getUrlNormalQuality().substring(0, i) + requestedUrl.substring(indexPipe + 1); diff --git a/src/main/java/mediathek/filmlisten/writer/FilmListWriter.java b/src/main/java/mediathek/filmlisten/writer/FilmListWriter.java index 4a07bb20b2..8bcd8960cb 100644 --- a/src/main/java/mediathek/filmlisten/writer/FilmListWriter.java +++ b/src/main/java/mediathek/filmlisten/writer/FilmListWriter.java @@ -11,6 +11,7 @@ import org.apache.commons.lang3.SystemUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; import java.io.BufferedOutputStream; import java.io.File; @@ -145,9 +146,9 @@ private void writeEntry(DatenFilm datenFilm, JsonGenerator jg) throws IOExceptio jg.writeString(datenFilm.getWebsiteLink()); jg.writeString(datenFilm.getUrlSubtitle()); skipEntry(jg); //DatenFilm.FILM_URL_RTMP - jg.writeString(datenFilm.getUrlLowQuality()); + writeLowQualityUrl(jg, datenFilm); skipEntry(jg); //DatenFilm.URL_RTMP_KLEIN - jg.writeString(datenFilm.getUrlHighQuality()); + writeHighQualityUrl(jg, datenFilm); skipEntry(jg); //DatenFilm.FILM_URL_RTMP_HD jg.writeString(datenFilm.getDatumLong()); skipEntry(jg); //DatenFilm.FILM_URL_HISTORY @@ -157,6 +158,34 @@ private void writeEntry(DatenFilm datenFilm, JsonGenerator jg) throws IOExceptio jg.writeEndArray(); } + private void writeLowQualityUrl(@NotNull JsonGenerator jg, @NotNull DatenFilm datenFilm) throws IOException { + String url = datenFilm.getUrlLowQuality(); + if (decompressUrls) { + if (DatenFilm.isUrlCompressed(url)) { + url = datenFilm.decompressUrl(url); + } + } + + jg.writeString(url); + } + + private void writeHighQualityUrl(@NotNull JsonGenerator jg, @NotNull DatenFilm datenFilm) throws IOException { + String url = datenFilm.getUrlHighQuality(); + if (decompressUrls) { + if (DatenFilm.isUrlCompressed(url)) { + url = datenFilm.decompressUrl(url); + } + } + + jg.writeString(url); + } + + public void setDecompressUrls(boolean decompressUrls) { + this.decompressUrls = decompressUrls; + } + + private boolean decompressUrls; + private void skipEntry(JsonGenerator jg) throws IOException { jg.writeString(""); } diff --git a/src/main/java/mediathek/gui/actions/export/FilmListExportWorkerTask.java b/src/main/java/mediathek/gui/actions/export/FilmListExportWorkerTask.java index 8bd40dd476..68e2122843 100644 --- a/src/main/java/mediathek/gui/actions/export/FilmListExportWorkerTask.java +++ b/src/main/java/mediathek/gui/actions/export/FilmListExportWorkerTask.java @@ -25,6 +25,7 @@ protected Void call() { // do not "compress" the sender tag writer.setCompressSenderTag(false); writer.setCompressThemaTag(false); + writer.setDecompressUrls(true); writer.writeFilmList(selectedFile.getAbsolutePath(), Daten.getInstance().getListeFilme(), prog -> updateProgress(prog, 1d)); From 06ead4fb94bf195c783eab83b3fe05fc7575fc9a Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 30 Jun 2022 11:56:09 +0200 Subject: [PATCH 110/422] - convert to swing dialog Former-commit-id: 9cf9483c85e72a6e53cbb0f7f3c21fdde9229597 --- .../mediathek/gui/tabs/tab_film/GuiFilme.java | 29 ++++++------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index 8b40e5e09c..fc25352bcd 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -13,9 +13,6 @@ import javafx.application.Platform; import javafx.beans.value.ChangeListener; import javafx.embed.swing.JFXPanel; -import javafx.scene.control.Alert; -import javafx.scene.control.ButtonBar; -import javafx.scene.control.ButtonType; import javafx.util.Duration; import mediathek.config.Daten; import mediathek.config.Konstanten; @@ -587,26 +584,18 @@ private void saveFilm(DatenFilm datenFilm, DatenPset pSet) { } } - private synchronized void bookmarkFilm() { + private void bookmarkFilm() { var movies = getSelFilme(); final long size = movies.size(); if (size > 250) { - JavaFxUtils.invokeInFxThreadAndWait(() -> { - ButtonType yes = new ButtonType("Ja", ButtonBar.ButtonData.YES); - ButtonType no = new ButtonType("Nein", ButtonBar.ButtonData.NO); - Alert alert = new Alert(Alert.AlertType.WARNING, - String.format( - "Möchten Sie wirklich %d Einträge bearbeiten?%nDas Programm könnte während der Operation nicht reagieren.", - size), - yes, - no); - alert.setTitle("Merkliste"); - alert.showAndWait().filter(r -> r == yes).ifPresent(response -> - SwingUtilities.invokeLater(() -> { - daten.getListeBookmarkList().checkAndBookmarkMovies(movies); - repaint(); - })); - }); + var reply = JOptionPane.showConfirmDialog(this, + String.format("Möchten Sie wirklich %d Einträge der Merkliste bearbeiten?%nDas Programm könnte während der Operation nicht reagieren.", size), + Konstanten.PROGRAMMNAME, + JOptionPane.YES_NO_OPTION); + if (reply == JOptionPane.YES_OPTION) { + daten.getListeBookmarkList().checkAndBookmarkMovies(movies); + repaint(); + } } else { daten.getListeBookmarkList().checkAndBookmarkMovies(movies); repaint(); From b04d06b571164e6c477485b614c71effb0f100e9 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 30 Jun 2022 12:30:23 +0200 Subject: [PATCH 111/422] - add windows ARM target Former-commit-id: c4d5960ba2524c4fbf672539eb40399dfa2628bc --- pom.xml | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/pom.xml b/pom.xml index 9b92a87c9c..6f1fc13cbc 100755 --- a/pom.xml +++ b/pom.xml @@ -805,6 +805,127 @@ + + windows_aarch64 + + + windows + aarch64 + + + + win + + + + org.openjfx + javafx-base + ${javafx.version} + ${javafx.platform} + provided + + + org.openjfx + javafx-controls + ${javafx.version} + ${javafx.platform} + provided + + + org.openjfx + javafx-media + ${javafx.version} + ${javafx.platform} + provided + + + org.openjfx + javafx-swing + ${javafx.version} + ${javafx.platform} + provided + + + org.openjfx + javafx-fxml + ${javafx.version} + ${javafx.platform} + provided + + + org.openjfx + javafx-graphics + ${javafx.version} + ${javafx.platform} + provided + + + + + + com.coderplus.maven.plugins + copy-rename-maven-plugin + ${copy-rename-maven-plugin.version} + + + copy-file + generate-sources + + copy + + + + + res/bin/ffmpeg.exe + target/res/bin/ffmpeg.exe + + + res/bin/ffmpeg.txt + target/res/bin/ffmpeg.txt + + + + + + + + org.apache.maven.plugins + maven-shade-plugin + ${maven-shade-plugin.version} + + + package + + shade + + + + + res/* + *:*:*:linux + *:*:*:mac + + + false + + + ${mainclass} + + true + + + + + + + + + + + + windows_32bit From 45cf9f756bcde739668cccb2524c86f16a84ae0f Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 30 Jun 2022 12:34:43 +0200 Subject: [PATCH 112/422] - add windows ARM target Former-commit-id: 0e040184da316ec80a7ded703cb104323d94ed5c --- pom.xml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pom.xml b/pom.xml index 6f1fc13cbc..07f97292b8 100755 --- a/pom.xml +++ b/pom.xml @@ -821,42 +821,36 @@ org.openjfx javafx-base ${javafx.version} - ${javafx.platform} provided org.openjfx javafx-controls ${javafx.version} - ${javafx.platform} provided org.openjfx javafx-media ${javafx.version} - ${javafx.platform} provided org.openjfx javafx-swing ${javafx.version} - ${javafx.platform} provided org.openjfx javafx-fxml ${javafx.version} - ${javafx.platform} provided org.openjfx javafx-graphics ${javafx.version} - ${javafx.platform} provided From 93e19bcecfda2f4e3859b82fe83ca97b38e6c2e2 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 30 Jun 2022 12:48:16 +0200 Subject: [PATCH 113/422] - remove Win ARM64 target Former-commit-id: 3d5cdfed46779d9725377b2fe4964be5fdb98361 --- pom.xml | 115 -------------------------------------------------------- 1 file changed, 115 deletions(-) diff --git a/pom.xml b/pom.xml index 07f97292b8..9b92a87c9c 100755 --- a/pom.xml +++ b/pom.xml @@ -805,121 +805,6 @@ - - windows_aarch64 - - - windows - aarch64 - - - - win - - - - org.openjfx - javafx-base - ${javafx.version} - provided - - - org.openjfx - javafx-controls - ${javafx.version} - provided - - - org.openjfx - javafx-media - ${javafx.version} - provided - - - org.openjfx - javafx-swing - ${javafx.version} - provided - - - org.openjfx - javafx-fxml - ${javafx.version} - provided - - - org.openjfx - javafx-graphics - ${javafx.version} - provided - - - - - - com.coderplus.maven.plugins - copy-rename-maven-plugin - ${copy-rename-maven-plugin.version} - - - copy-file - generate-sources - - copy - - - - - res/bin/ffmpeg.exe - target/res/bin/ffmpeg.exe - - - res/bin/ffmpeg.txt - target/res/bin/ffmpeg.txt - - - - - - - - org.apache.maven.plugins - maven-shade-plugin - ${maven-shade-plugin.version} - - - package - - shade - - - - - res/* - *:*:*:linux - *:*:*:mac - - - false - - - ${mainclass} - - true - - - - - - - - - - - - windows_32bit From 83224677fa4198cc7678bce029aa6463292c1b05 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 30 Jun 2022 12:54:40 +0200 Subject: [PATCH 114/422] - remove 32 bit targets Former-commit-id: 77df223b2492556731d9b84d8f2b13b65c7a101e --- .install4j/mediathekview_windows32.install4j | 1069 ------------------ pom.xml | 139 --- 2 files changed, 1208 deletions(-) delete mode 100644 .install4j/mediathekview_windows32.install4j diff --git a/.install4j/mediathekview_windows32.install4j b/.install4j/mediathekview_windows32.install4j deleted file mode 100644 index 08d2dac76f..0000000000 --- a/.install4j/mediathekview_windows32.install4j +++ /dev/null @@ -1,1069 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - sys.installationDir - - - context.getBooleanVariable("sys.confirmedUpdateInstallation") - - - - - - ${form:welcomeMessage} - - !context.isConsole() - - - - - - String message = context.getMessage("ConsoleWelcomeLabel", - context.getApplicationName()); - return console.askOkCancel(message, true); - - - - - - - - updateCheck - - - - - ${i18n:ClickNext} - - - - - - !context.getBooleanVariable("sys.confirmedUpdateInstallation") - - - - - sys.installationDir - - - context.getVariable("sys.responseFile") == null - - - - - - ${i18n:SelectDirLabel(${compiler:sys.fullName})} - - - - - - - - suggestAppDir - validateApplicationId - existingDirWarning - checkWritable - manualEntryAllowed - checkFreeSpace - showRequiredDiskSpace - showFreeDiskSpace - allowSpacesOnUnix - validationScript - standardValidation - - - - - - - - - - - ${i18n:UninstallerMenuEntry(${compiler:sys.fullName})} - - - !context.getBooleanVariable("sys.programGroupDisabled") - - - - ${compiler:sys.fullName} ${compiler:sys.version} - - - - - - - ${i18n:WizardPreparing} - - - - - - - - - ${form:finishedMessage} - - - - - - - - - - ${i18n:UninstallerMenuEntry(${compiler:sys.fullName})} - - - - - - - - - - - - - - - - - ${form:welcomeMessage} - - !context.isConsole() - - - - - - String message = context.getMessage("ConfirmUninstall", - context.getApplicationName()); - return console.askYesNo(message, true); - - - - - - - - - - - - - - - ${i18n:UninstallerPreparing} - - - - - - - - - - ${form:successMessage} - - - - - - - - - - - - ${compiler:sys.install4jHome}/resource/updater_16.png - - - - - ${compiler:sys.install4jHome}/resource/updater_32.png - - - - - ${compiler:sys.install4jHome}/resource/updater_48.png - - - - - ${compiler:sys.install4jHome}/resource/updater_128.png - - - - - ${compiler:sys.install4jHome}/resource/updater_256.png - - - - update - - ${i18n:updater.WindowTitle("${compiler:sys.fullName}")} - - - - - - - - - - - - - ${installer:updatesUrl?:${compiler:sys.updatesUrl}} - updateDescriptor - - - - - - - - - ((UpdateDescriptor)context.getVariable("updateDescriptor")).getPossibleUpdateEntry() - - - - updateDescriptorEntry - - - - - - - context.getVariable("updateDescriptorEntry") != null - - - - - - - - - - - ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getNewVersion() - - - - updaterNewVersion - - - - - - - - ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getFileSizeVerbose() - - - - updaterDownloadSize - - - - - - - - ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getComment() - - - - updaterComment - - - - - - - - ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getURL().toExternalForm() - - - - updaterDownloadUrl - - - - - - - - ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).isArchive() ? - Boolean.TRUE : Boolean.FALSE - - - - isArchive - - - - - - - - ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getFileName().toLowerCase().endsWith(".dmg") - - - - isDmg - - - - - - - - - - - - - context.getVariable("updateDescriptorEntry") != null - - - - - - - - ${i18n:updater.NewVersionAvailableSubtitle("${compiler:sys.fullName}")} - - ${i18n:updater.NewVersionAvailableTitle} - - - - - ${i18n:updater.CurrentVersionLabel} - - - 128 - 0 - 0 - 255 - - - - - ${installer:sys.version} - - - - - - - ${i18n:updater.NewVersionLabel} - - - 0 - 128 - 0 - 255 - - - - - ${installer:updaterNewVersion} - - - - - - - context.goForward(1, false, false); - - - ${i18n:updater.ShowComments} - - ((String)context.getVariable("updaterComment")).length() > 0 - - - - - - - - ${i18n:updater.DownloadLocationLabel} - - - - - ${installer:sys.downloadsDir} - ${i18n:updater.DownloadToLabel} - - updaterDownloadLocation - - - - - ${i18n:updater.DownloadSizeLabel} - ${installer:updaterDownloadSize} - - - - - - - - ${i18n:updater.CommentsSubTitle} - ${i18n:updater.CommentsTitle} - - false // This screen is only shown if the user clicks the "Show comments" hyperlink label in the previous screen. - - if (context.isConsole()) { - context.goBackInHistory(1); - } - return true; - - WizardContext wizardContext = context.getWizardContext(); - wizardContext.setControlButtonVisible(ControlButtonType.NEXT, false); - wizardContext.setControlButtonVisible(ControlButtonType.CANCEL, false); - - - - - - ${i18n:updater.CommentsLabel} - - !context.isConsole() - - labelText - - - - - ${installer:updaterComment} - - - - - textSource - displayedText - displayedTextFile - variableName - - - - - - - console.waitForEnter(); - return true; - - - - - - - - - - ${i18n:updater.DownloadSubTitle} - ${i18n:updater.DownloadTitle} - - context.getWizardContext().setControlButtonVisible(ControlButtonType.NEXT, false); - context.getWizardContext().setControlButtonVisible(ControlButtonType.PREVIOUS, false); - context.goForward(1, true, true); - - - - - - - context.getVariable("updaterDownloadLocation") + - File.separator + - ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getFileName() - - - - updaterDownloadFile - - - - - - - ${installer:updaterDownloadFile} - - - ${installer:updaterDownloadUrl} - - - - - - - - ${installer:updaterDownloadFile} - - - - 755 - - - - - - - statusVisible - initialStatusMessage - - - - - - - - - ${i18n:updater.FinishTitle} - - !(context.getBooleanVariable("isArchive") && - context.getBooleanVariable("isDmg")) - - - - - - - !context.getBooleanVariable("isArchive") && - ((Integer)context.getVariable("updaterLaunchSelection")).intValue() == 0 - - - - - - - - - - import com.install4j.runtime.installer.frontend.SplashProgressInterface; - - List<String> args = new ArrayList<String>(); - String installationDirectory = context.getInstallationDirectory().getPath(); - if (context.isUnattended()) { - args.add("-q"); - args.add("-wait"); - args.add("20"); - if (context.getProgressInterface() instanceof SplashProgressInterface) { - args.add("-splash"); - args.add("Installing"); - } - } else if (context.isConsole()) { - args.add("-c"); - } - args.add("-dir"); - args.add(installationDirectory); - - return args.toArray(new String[args.size()]); - - - - installerArguments - - - - - - - ${installer:installerArguments} - - - - ${installer:updaterDownloadFile} - - - - - ${installer:updaterDownloadLocation} - - - - - - - - - - - - - !context.isConsole() - - labelText - - - - - - - ${i18n:updater.FinishInfoText("${compiler:sys.fullName}")} - - - !context.isConsole() - - labelText - - - - - ${i18n:updater.LaunchUpdaterQuestion} - - - - - - - - - - - ${i18n:updater.LaunchUpdaterLabel} - ${i18n:updater.DoNotLaunchUpdaterLabel} - - updaterLaunchSelection - - !context.getBooleanVariable("isArchive") - - - - - - - Util.showPath((String)context.getVariable("updaterDownloadFile")); - - - - ${i18n:updater.OpenContainingFolderLabel} - - - !context.isConsole() - - - - - - - - - - - - ${i18n:updater.FinishTitle} - - context.getBooleanVariable("isArchive") && context.getBooleanVariable("isDmg") - - - - - - - context.getBooleanVariable("updaterOpenDmg") - - - - - - - - - - - - Util.showPath((String)context.getVariable("updaterDownloadFile")); - return true; - - - - - - - - - - - - - - !context.isConsole() - - labelText - - - - - - - ${i18n:updater.FinishInfoText("${compiler:sys.fullName}")} - - - !context.isConsole() - - labelText - - - - - ${i18n:updater.LaunchUpdaterQuestion} - - - - - - - - - - ${i18n:updater.OpenContainingFolderLabel} - - - updaterOpenDmg - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/pom.xml b/pom.xml index 9b92a87c9c..c4588c7928 100755 --- a/pom.xml +++ b/pom.xml @@ -122,8 +122,6 @@ 17/jdk-17.0.2+8 - - 17/17.0.2+9 18/18.0.1 18 @@ -137,21 +135,6 @@ -XX:MaxRAMPercentage=50.0 -XX:+UseStringDeduplication --add-opens java.desktop/sun.awt.X11=ALL-UNNAMED -Dfile.encoding=UTF-8 -DexternalUpdateCheck - - -Xmx1G -Xss1048576 -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=compact -XX:MaxRAMPercentage=100.0 - -XX:+UseStringDeduplication -Dfile.encoding=UTF-8 -DexternalUpdateCheck - --add-exports javafx.controls/com.sun.javafx.scene.control.inputmap=ALL-UNNAMED - --add-exports javafx.base/com.sun.javafx.event=ALL-UNNAMED - --add-exports javafx.controls/com.sun.javafx.scene.control.behavior=ALL-UNNAMED - --add-exports javafx.graphics/com.sun.javafx.scene.traversal=ALL-UNNAMED - - - -Xmx1280M -XX:+UseG1GC -XX:+UseStringDeduplication -Dfile.encoding=UTF-8 - -DexternalUpdateCheck --add-exports javafx.controls/com.sun.javafx.scene.control.inputmap=ALL-UNNAMED - --add-exports javafx.base/com.sun.javafx.event=ALL-UNNAMED --add-exports - javafx.controls/com.sun.javafx.scene.control.behavior=ALL-UNNAMED --add-exports - javafx.graphics/com.sun.javafx.scene.traversal=ALL-UNNAMED --add-opens java.desktop/sun.awt.X11=ALL-UNNAMED - ./install4j9.0.5 @@ -805,128 +788,6 @@ - - windows_32bit - - - windows - x86 - - - - win - windows32 - - - - org.openjfx - javafx-base - ${javafx.version} - ${javafx.platform} - provided - - - org.openjfx - javafx-controls - ${javafx.version} - ${javafx.platform} - provided - - - org.openjfx - javafx-media - ${javafx.version} - ${javafx.platform} - provided - - - org.openjfx - javafx-swing - ${javafx.version} - ${javafx.platform} - provided - - - org.openjfx - javafx-fxml - ${javafx.version} - ${javafx.platform} - provided - - - org.openjfx - javafx-graphics - ${javafx.version} - ${javafx.platform} - provided - - - - - - com.coderplus.maven.plugins - copy-rename-maven-plugin - ${copy-rename-maven-plugin.version} - - - copy-file - generate-sources - - copy - - - - - res/bin/ffmpeg32.exe - target/res/bin/ffmpeg.exe - - - res/bin/ffmpeg32.txt - target/res/bin/ffmpeg.txt - - - - - - - - org.apache.maven.plugins - maven-shade-plugin - ${maven-shade-plugin.version} - - - package - - shade - - - - - res/* - *:*:*:linux - *:*:*:mac - - - false - - - ${mainclass} - - true - - - - - - - - - - - - linux_64bit From ea42562f44eb914b9382eb5b621bb6849fb35c89 Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 1 Jul 2022 13:07:15 +0200 Subject: [PATCH 115/422] - convert JavaFX alerts to swing alerts Former-commit-id: 9d30a6a2ced45c81da2d1ce6d9c1edaccc8eeea8 --- .../gui/tabs/tab_downloads/GuiDownloads.java | 14 ++++---------- .../java/mediathek/mainwindow/MediathekGui.java | 16 +++++----------- 2 files changed, 9 insertions(+), 21 deletions(-) diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java index 57ffdf6873..dbd1b3ab4a 100644 --- a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java @@ -7,9 +7,7 @@ import javafx.embed.swing.JFXPanel; import javafx.fxml.FXMLLoader; import javafx.scene.Scene; -import javafx.scene.control.Alert; import javafx.scene.control.TabPane; -import javafx.stage.Modality; import mediathek.config.Daten; import mediathek.config.Konstanten; import mediathek.config.MVConfig; @@ -963,14 +961,10 @@ public void startAllDownloadsAtSpecificTime() { // Film dessen Start schon auf fertig/fehler steht wird wieder gestartet // wird immer vom Benutzer aufgerufen if (tabelle.getRowCount() == 0) { - Platform.runLater(() -> { - Alert alert = new Alert(Alert.AlertType.INFORMATION); - alert.setTitle(Konstanten.PROGRAMMNAME); - alert.setHeaderText("Keine Downloads vorhanden"); - alert.setContentText("Es sind keine Downloads in der Liste zum Starten vorhanden."); - alert.initModality(Modality.APPLICATION_MODAL); - alert.showAndWait(); - }); + JOptionPane.showMessageDialog(this, + "Es sind keine Downloads in der Liste zum Starten vorhanden.", + Konstanten.PROGRAMMNAME, + JOptionPane.INFORMATION_MESSAGE); return; } diff --git a/src/main/java/mediathek/mainwindow/MediathekGui.java b/src/main/java/mediathek/mainwindow/MediathekGui.java index 1b679c4328..5696b199ec 100644 --- a/src/main/java/mediathek/mainwindow/MediathekGui.java +++ b/src/main/java/mediathek/mainwindow/MediathekGui.java @@ -11,11 +11,9 @@ import javafx.event.EventHandler; import javafx.scene.Node; import javafx.scene.Scene; -import javafx.scene.control.Alert; import javafx.scene.control.ProgressIndicator; import javafx.scene.control.Tooltip; import javafx.scene.layout.HBox; -import javafx.stage.Modality; import javafx.stage.Stage; import mediathek.Main; import mediathek.config.Config; @@ -287,16 +285,12 @@ private void checkInvalidRegularExpressions() { """, regexStr); Filter.regExpErrorList.clear(); - Platform.runLater(() -> { - Alert alert = new Alert(Alert.AlertType.ERROR); - alert.setTitle(Konstanten.PROGRAMMNAME); - alert.setHeaderText("Ungültige reguläre Ausdrücke gefunden"); - alert.setContentText(message); - alert.initModality(Modality.APPLICATION_MODAL); - alert.show(); - }); + JOptionPane.showMessageDialog(this, + message, + Konstanten.PROGRAMMNAME, + JOptionPane.ERROR_MESSAGE); } - }, 30, TimeUnit.SECONDS); + }, 15, TimeUnit.SECONDS); } /** From ed815dbd1d2ae6cd80bf6548cced608fc22cbe0b Mon Sep 17 00:00:00 2001 From: Christian F Date: Tue, 5 Jul 2022 17:47:48 +0200 Subject: [PATCH 116/422] - convert JavaFX status bar and labels to swing - convert JavaFX memory monitor to swing - simplify export filmlist action for now - add developer menu - convert JavaFX tasks to swing - move download list info below table instead of statusbar - reduce reliance on message bus - a lot of code cleanup Former-commit-id: 948c1c9850d4d3c35d4b15f96819423e30c6a058 --- pom.xml | 19 + src/main/java/mediathek/Main.java | 224 +- .../java/mediathek/daten/DownloadInfos.java | 4 +- .../mediathek/daten/DownloadStartInfo.java | 4 + .../java/mediathek/daten/ListeDownloads.java | 2 +- src/main/java/mediathek/daten/ListeFilme.java | 24 +- .../daten/blacklist/ListeBlacklist.java | 2 +- .../FilmeImportierenAutoThread.java | 2 +- .../java/mediathek/filmlisten/FilmeLaden.java | 71 +- .../filmlisten/reader/FilmListReader.java | 4 +- .../filmlisten/writer/FilmListWriter.java | 2 +- src/main/java/mediathek/gui/MVTray.java | 2 +- .../gui/actions/FilmListExportAction.java | 60 + .../gui/actions/FilmListWriteWorkerTask.java | 28 - .../gui/actions/MemoryMonitorAction.java | 42 +- .../actions/export/FilmListExportAction.java | 75 - .../export/FilmListExportWorkerTask.java | 34 - .../java/mediathek/gui/tabs/AGuiTabPanel.java | 1 - .../gui/tabs/tab_downloads/AboLabel.java | 18 + .../ActiveDownloadsInfoLabel.java | 21 + .../DownloadStartInfoProperty.java | 46 + .../FailedDownloadsInfoLabel.java | 20 + .../FinishedDownloadsInfoLabel.java | 21 + .../gui/tabs/tab_downloads/GuiDownloads.java | 64 +- .../gui/tabs/tab_downloads/GuiDownloads.jfd | 177 - .../ManualDownloadsInfoLabel.java | 18 + .../tab_downloads/TotalDownloadsLabel.java | 24 + .../WaitingDownloadsInfoLabel.java | 21 + .../mediathek/gui/tabs/tab_film/GuiFilme.java | 82 +- .../gui/tasks/BlacklistFilterWorker.java | 23 + .../gui/tasks/FilmlistWriterWorker.java | 44 + .../mediathek/gui/tasks/RefreshAboWorker.java | 24 + .../mediathek/javafx/FilmListFilterTask.java | 43 - .../javafx/FilmListNetworkReaderTask.java | 24 - .../mediathek/javafx/FilmListReaderTask.java | 32 - .../javafx/GarbageCollectionButton.java | 21 - .../mediathek/javafx/InfoLabel/AboLabel.java | 16 - .../InfoLabel/ActiveDownloadsLabel.java | 26 - .../javafx/InfoLabel/ErrorLabel.java | 21 - .../javafx/InfoLabel/FinishedLabel.java | 19 - .../InfoLabel/GesamtdownloadsLabel.java | 22 - .../javafx/InfoLabel/NumDownloadsLabel.java | 16 - .../javafx/InfoLabel/WaitingLabel.java | 19 - .../java/mediathek/javafx/MemoryMonitor.java | 145 - .../mediathek/javafx/SelectedItemsLabel.java | 24 - .../mediathek/javafx/StatusBarController.java | 133 - .../DownloadTabInformationLabel.java | 118 - .../javafx/filmlist/FilmListAgeLabel.kt | 53 - .../filmlist/FilmListCreationDateLabel.java | 24 - .../javafx/filmlist/FilmListInfoPane.java | 48 - .../javafx/filmtab/FilmInfoLabel.java | 51 - .../javafx/filmtab/FilmTabInfoPane.java | 64 - .../mediathek/javafx/tool/ComputedLabel.java | 23 - .../mediathek/javafx/tool/FXProgressPane.java | 32 - .../DownloadInformationLabel.java} | 27 +- .../mediathek/mainwindow/FilmAgeLabel.java | 58 + .../mainwindow/FilmListCreationDateLabel.java | 22 + .../mainwindow/FilmSizeInfoLabel.java | 71 + .../mainwindow/ListSelectedItemsProperty.java | 23 + .../mediathek/mainwindow/MediathekGui.java | 188 +- .../mainwindow/MemoryUsagePanel.java | 89 + .../mainwindow/SelectedListItemsLabel.java | 21 + .../java/mediathek/mainwindow/StatusBar.java | 34 + .../java/org/jdesktop/beans/AbstractBean.java | 497 ++ .../beans/AbstractSerializableBean.java | 104 + .../java/org/jdesktop/beans/package-info.java | 26 + .../jdesktop/swingx/AbstractLayoutManager.kt | 51 + .../jdesktop/swingx/AbstractPatternPanel.java | 443 ++ .../org/jdesktop/swingx/AlphaPaintable.java | 58 + .../jdesktop/swingx/BackgroundPaintable.java | 60 + .../swingx/ForwardingRepaintManager.java | 214 + .../org/jdesktop/swingx/HorizontalLayout.java | 98 + .../java/org/jdesktop/swingx/JXBusyLabel.java | 369 ++ .../java/org/jdesktop/swingx/JXButton.java | 736 +++ .../jdesktop/swingx/JXCollapsiblePane.java | 1143 +++++ .../swingx/JXColorSelectionButton.java | 252 + .../java/org/jdesktop/swingx/JXComboBox.java | 885 ++++ .../org/jdesktop/swingx/JXDatePicker.java | 1005 ++++ .../java/org/jdesktop/swingx/JXDialog.java | 403 ++ .../org/jdesktop/swingx/JXEditorPane.java | 869 ++++ .../java/org/jdesktop/swingx/JXErrorPane.java | 654 +++ .../java/org/jdesktop/swingx/JXFindBar.java | 178 + .../java/org/jdesktop/swingx/JXFindPanel.java | 296 ++ .../jdesktop/swingx/JXFormattedTextField.java | 150 + .../java/org/jdesktop/swingx/JXFrame.java | 629 +++ .../jdesktop/swingx/JXGradientChooser.java | 787 +++ .../java/org/jdesktop/swingx/JXGraph.java | 1748 +++++++ .../java/org/jdesktop/swingx/JXHeader.java | 358 ++ .../java/org/jdesktop/swingx/JXHyperlink.java | 347 ++ .../org/jdesktop/swingx/JXImagePanel.java | 419 ++ .../java/org/jdesktop/swingx/JXImageView.java | 771 +++ .../java/org/jdesktop/swingx/JXLabel.java | 1318 +++++ src/main/java/org/jdesktop/swingx/JXList.java | 1568 ++++++ .../java/org/jdesktop/swingx/JXLoginPane.java | 1737 +++++++ .../java/org/jdesktop/swingx/JXMonthView.java | 1889 +++++++ .../org/jdesktop/swingx/JXMultiSplitPane.java | 590 +++ .../jdesktop/swingx/JXMultiThumbSlider.java | 404 ++ .../java/org/jdesktop/swingx/JXPanel.java | 693 +++ .../org/jdesktop/swingx/JXRadioGroup.java | 338 ++ .../java/org/jdesktop/swingx/JXRootPane.java | 458 ++ .../org/jdesktop/swingx/JXSearchField.java | 841 ++++ .../org/jdesktop/swingx/JXSearchPanel.java | 275 ++ .../java/org/jdesktop/swingx/JXStatusBar.java | 318 ++ .../java/org/jdesktop/swingx/JXTable.java | 4399 +++++++++++++++++ .../org/jdesktop/swingx/JXTableHeader.java | 752 +++ .../java/org/jdesktop/swingx/JXTaskPane.java | 625 +++ .../jdesktop/swingx/JXTaskPaneContainer.java | 169 + .../java/org/jdesktop/swingx/JXTextArea.java | 108 + .../java/org/jdesktop/swingx/JXTextField.java | 152 + .../org/jdesktop/swingx/JXTipOfTheDay.java | 460 ++ .../org/jdesktop/swingx/JXTitledPanel.java | 301 ++ .../jdesktop/swingx/JXTitledSeparator.java | 410 ++ src/main/java/org/jdesktop/swingx/JXTree.java | 1651 +++++++ .../java/org/jdesktop/swingx/JXTreeTable.java | 3336 +++++++++++++ .../org/jdesktop/swingx/Mnemonicable.java | 79 + .../org/jdesktop/swingx/MultiSplitLayout.java | 2211 +++++++++ .../org/jdesktop/swingx/RepaintManagerX.java | 66 + .../jdesktop/swingx/ScrollableSizeHint.java | 146 + .../java/org/jdesktop/swingx/StackLayout.java | 196 + .../org/jdesktop/swingx/SwingXUtilities.java | 603 +++ .../swingx/TranslucentRepaintManager.java | 51 + .../org/jdesktop/swingx/VerticalLayout.java | 123 + .../java/org/jdesktop/swingx/WrapLayout.java | 176 + .../swingx/action/AbstractActionExt.java | 443 ++ .../swingx/action/ActionContainerFactory.java | 586 +++ .../jdesktop/swingx/action/ActionFactory.java | 157 + .../jdesktop/swingx/action/ActionManager.java | 383 ++ .../jdesktop/swingx/action/BoundAction.java | 329 ++ .../swingx/action/CompositeAction.java | 137 + .../swingx/action/OpenBrowserAction.java | 122 + .../jdesktop/swingx/action/ServerAction.java | 324 ++ .../jdesktop/swingx/action/TargetManager.java | 280 ++ .../jdesktop/swingx/action/Targetable.java | 64 + .../swingx/action/TargetableAction.java | 140 + .../swingx/action/TargetableSupport.java | 72 + .../ToggleActionPropertyChangeListener.java | 168 + .../jdesktop/swingx/action/package-info.java | 102 + .../swingx/auth/DefaultUserNameStore.java | 193 + .../swingx/auth/JAASLoginService.java | 153 + .../swingx/auth/JDBCLoginService.java | 237 + .../org/jdesktop/swingx/auth/KeyChain.java | 198 + .../jdesktop/swingx/auth/LoginAdapter.java | 48 + .../org/jdesktop/swingx/auth/LoginEvent.java | 45 + .../jdesktop/swingx/auth/LoginListener.java | 60 + .../jdesktop/swingx/auth/LoginService.java | 290 ++ .../jdesktop/swingx/auth/PasswordStore.java | 55 + .../swingx/auth/SimpleLoginService.java | 60 + .../jdesktop/swingx/auth/UserNameStore.java | 62 + .../jdesktop/swingx/auth/UserPermissions.java | 126 + .../jdesktop/swingx/auth/package-info.java | 25 + .../AbstractAutoCompleteAdaptor.java | 118 + .../swingx/autocomplete/AutoComplete.java | 225 + .../AutoCompleteComboBoxEditor.java | 122 + .../autocomplete/AutoCompleteDecorator.java | 391 ++ .../autocomplete/AutoCompleteDocument.java | 547 ++ .../AutoCompleteStyledDocument.java | 168 + .../swingx/autocomplete/ComboBoxAdaptor.java | 117 + .../autocomplete/ComboBoxCellEditor.java | 110 + .../autocomplete/DelegatingDocumentEvent.java | 63 + .../swingx/autocomplete/ListAdaptor.java | 106 + .../autocomplete/ObjectToStringConverter.java | 105 + .../autocomplete/TextComponentAdaptor.java | 80 + .../swingx/autocomplete/package-info.java | 91 + .../workarounds/MacOSXPopupLocationFix.java | 213 + .../workarounds/package-info.java | 26 + .../swingx/border/DropShadowBorder.java | 440 ++ .../jdesktop/swingx/border/IconBorder.java | 288 ++ .../swingx/border/MatteBorderExt.java | 303 ++ .../jdesktop/swingx/border/package-info.java | 25 + .../calendar/AbstractDateSelectionModel.java | 329 ++ .../swingx/calendar/CalendarUtils.java | 649 +++ .../swingx/calendar/DatePickerFormatter.java | 248 + .../swingx/calendar/DateSelectionModel.java | 374 ++ .../jdesktop/swingx/calendar/DateSpan.java | 221 + .../jdesktop/swingx/calendar/DateUtils.java | 405 ++ .../swingx/calendar/DaySelectionModel.java | 296 ++ .../calendar/DefaultDateSelectionModel.java | 273 + .../calendar/SingleDaySelectionModel.java | 345 ++ .../swingx/calendar/package-info.java | 26 + .../color/EyeDropperColorChooserPanel.form | 125 + .../color/EyeDropperColorChooserPanel.java | 289 ++ .../swingx/color/GradientPreviewPanel.java | 285 ++ .../swingx/color/GradientThumbRenderer.java | 67 + .../swingx/color/GradientTrackRenderer.java | 103 + .../org/jdesktop/swingx/color/colorwell.png | Bin 0 -> 725 bytes .../java/org/jdesktop/swingx/color/mag.png | Bin 0 -> 1058 bytes .../jdesktop/swingx/color/package-info.java | 25 + .../swingx/combobox/EnumComboBoxModel.java | 192 + .../combobox/JXTextFieldComboBoxEditor.java | 60 + .../swingx/combobox/ListComboBoxModel.java | 124 + .../combobox/ListModelComboBoxWrapper.java | 51 + .../swingx/combobox/MapComboBoxModel.java | 133 + .../swingx/combobox/package-info.java | 25 + .../swingx/decorator/AbstractHighlighter.java | 285 ++ .../decorator/AlignmentHighlighter.java | 160 + .../swingx/decorator/BorderHighlighter.java | 255 + .../swingx/decorator/ColorHighlighter.java | 267 + .../swingx/decorator/ComponentAdapter.java | 567 +++ .../ComponentOrientationHighlighter.java | 93 + .../swingx/decorator/CompoundHighlighter.java | 243 + .../swingx/decorator/EnabledHighlighter.java | 112 + .../swingx/decorator/FontHighlighter.java | 117 + .../swingx/decorator/HighlightPredicate.java | 825 ++++ .../swingx/decorator/Highlighter.java | 103 + .../swingx/decorator/HighlighterFactory.java | 292 ++ .../swingx/decorator/IconHighlighter.java | 152 + .../swingx/decorator/PainterHighlighter.java | 253 + .../swingx/decorator/PatternPredicate.java | 223 + .../decorator/ResetDTCRColorHighlighter.java | 111 + .../swingx/decorator/SearchPredicate.java | 223 + .../decorator/ShadingColorHighlighter.java | 85 + .../swingx/decorator/ToolTipHighlighter.java | 131 + .../swingx/decorator/package-info.java | 33 + .../org/jdesktop/swingx/error/ErrorEvent.java | 55 + .../org/jdesktop/swingx/error/ErrorInfo.java | 259 + .../org/jdesktop/swingx/error/ErrorLevel.java | 51 + .../jdesktop/swingx/error/ErrorListener.java | 45 + .../jdesktop/swingx/error/ErrorReporter.java | 49 + .../jdesktop/swingx/error/ErrorSupport.java | 89 + .../jdesktop/swingx/error/package-info.java | 25 + .../swingx/event/CompoundFocusListener.java | 174 + .../swingx/event/DateSelectionEvent.java | 100 + .../swingx/event/DateSelectionListener.java | 31 + .../swingx/event/EventListenerMap.java | 136 + .../event/TableColumnModelExtListener.java | 78 + .../event/TreeExpansionBroadcaster.java | 115 + .../swingx/event/WeakEventListenerList.java | 275 ++ .../jdesktop/swingx/event/package-info.java | 38 + .../org/jdesktop/swingx/geom/Morphing2D.java | 705 +++ .../java/org/jdesktop/swingx/geom/Star2D.java | 340 ++ .../jdesktop/swingx/geom/package-info.java | 25 + .../swingx/graphics/BlendComposite.java | 976 ++++ .../swingx/graphics/ColorUtilities.java | 263 + .../swingx/graphics/FilterComposite.java | 119 + .../swingx/graphics/Graphics2DFacade.java | 452 ++ .../swingx/graphics/ReflectionRenderer.java | 512 ++ .../swingx/graphics/ShadowRenderer.java | 429 ++ .../swingx/graphics/package-info.java | 25 + .../hyperlink/AbstractHyperlinkAction.java | 144 + .../hyperlink/EditorPaneLinkVisitor.java | 137 + .../swingx/hyperlink/HyperlinkAction.java | 310 ++ .../jdesktop/swingx/hyperlink/LinkModel.java | 317 ++ .../swingx/hyperlink/LinkModelAction.java | 153 + .../swingx/icon/ColumnControlIcon.java | 84 + .../org/jdesktop/swingx/icon/EmptyIcon.java | 59 + .../org/jdesktop/swingx/icon/PainterIcon.java | 69 + .../jdesktop/swingx/icon/package-info.java | 36 + .../jdesktop/swingx/image/AbstractFilter.java | 97 + .../swingx/image/ColorTintFilter.java | 160 + .../jdesktop/swingx/image/FastBlurFilter.java | 216 + .../swingx/image/GaussianBlurFilter.java | 191 + .../swingx/image/StackBlurFilter.java | 154 + .../jdesktop/swingx/image/package-info.java | 25 + .../multislider/AbstractMultiThumbModel.java | 91 + .../multislider/DefaultMultiThumbModel.java | 116 + .../swingx/multislider/MultiThumbModel.java | 50 + .../jdesktop/swingx/multislider/Thumb.java | 55 + .../swingx/multislider/ThumbDataEvent.java | 57 + .../swingx/multislider/ThumbDataListener.java | 33 + .../swingx/multislider/ThumbListener.java | 29 + .../swingx/multislider/ThumbRenderer.java | 29 + .../swingx/multislider/TrackRenderer.java | 30 + .../swingx/multislider/package-info.java | 25 + .../multisplitpane/DefaultSplitPaneModel.java | 52 + .../swingx/multisplitpane/package-info.java | 24 + .../org/jdesktop/swingx/package-info.java | 144 + .../swingx/painter/AbstractAreaPainter.java | 267 + .../swingx/painter/AbstractLayoutPainter.java | 270 + .../swingx/painter/AbstractPainter.java | 438 ++ .../jdesktop/swingx/painter/AlphaPainter.java | 102 + .../jdesktop/swingx/painter/BusyPainter.java | 622 +++ .../swingx/painter/CheckerboardPainter.java | 197 + .../swingx/painter/CompoundPainter.java | 389 ++ .../jdesktop/swingx/painter/GlossPainter.java | 171 + .../jdesktop/swingx/painter/ImagePainter.java | 400 ++ .../jdesktop/swingx/painter/MattePainter.java | 114 + .../org/jdesktop/swingx/painter/Painter.java | 103 + .../jdesktop/swingx/painter/PainterPaint.java | 116 + .../jdesktop/swingx/painter/PainterUtils.java | 39 + .../org/jdesktop/swingx/painter/Painters.java | 40 + .../swingx/painter/PinstripePainter.java | 277 ++ .../swingx/painter/RectanglePainter.java | 265 + .../jdesktop/swingx/painter/ShapePainter.java | 211 + .../jdesktop/swingx/painter/TextPainter.java | 208 + .../painter/effects/AbstractAreaEffect.java | 399 ++ .../swingx/painter/effects/AreaEffect.java | 41 + .../painter/effects/GlowPathEffect.java | 45 + .../painter/effects/InnerGlowPathEffect.java | 44 + .../effects/InnerShadowPathEffect.java | 41 + .../painter/effects/NeonBorderEffect.java | 193 + .../painter/effects/ShadowPathEffect.java | 42 + .../swingx/painter/effects/package-info.java | 25 + .../jdesktop/swingx/painter/package-info.java | 25 + .../swingx/plaf/AbstractComponentAddon.java | 249 + .../swingx/plaf/AbstractUIChangeHandler.java | 30 + .../swingx/plaf/BuddyLayoutAndBorder.java | 264 + .../swingx/plaf/BuddyTextFieldUI.java | 88 + .../jdesktop/swingx/plaf/BusyLabelAddon.java | 59 + .../org/jdesktop/swingx/plaf/BusyLabelUI.java | 44 + .../swingx/plaf/ColumnControlButtonAddon.java | 50 + .../jdesktop/swingx/plaf/ComponentAddon.java | 55 + .../jdesktop/swingx/plaf/DatePickerAddon.java | 133 + .../jdesktop/swingx/plaf/DatePickerUI.java | 73 + .../jdesktop/swingx/plaf/DefaultsList.java | 142 + .../jdesktop/swingx/plaf/ErrorPaneAddon.java | 62 + .../org/jdesktop/swingx/plaf/ErrorPaneUI.java | 60 + .../org/jdesktop/swingx/plaf/HeaderAddon.java | 81 + .../org/jdesktop/swingx/plaf/HeaderUI.java | 31 + .../jdesktop/swingx/plaf/HyperlinkAddon.java | 47 + .../jdesktop/swingx/plaf/LoginPaneAddon.java | 98 + .../org/jdesktop/swingx/plaf/LoginPaneUI.java | 37 + .../swingx/plaf/LookAndFeelAddons.java | 533 ++ .../swingx/plaf/LookAndFeelUtils.java | 72 + .../jdesktop/swingx/plaf/MonthViewAddon.java | 60 + .../org/jdesktop/swingx/plaf/MonthViewUI.java | 67 + .../swingx/plaf/MultiThumbSliderAddon.java | 44 + .../swingx/plaf/MultiThumbSliderUI.java | 31 + .../swingx/plaf/PainterUIResource.java | 61 + .../swingx/plaf/PromptTextAreaUI.java | 52 + .../swingx/plaf/PromptTextFieldUI.java | 187 + .../jdesktop/swingx/plaf/PromptTextUI.java | 457 ++ .../org/jdesktop/swingx/plaf/SafeBorder.java | 95 + .../swingx/plaf/SearchFieldAddon.java | 123 + .../jdesktop/swingx/plaf/SearchFieldUI.java | 482 ++ .../jdesktop/swingx/plaf/ShapeUIResource.java | 94 + .../jdesktop/swingx/plaf/StatusBarAddon.java | 85 + .../org/jdesktop/swingx/plaf/StatusBarUI.java | 32 + .../org/jdesktop/swingx/plaf/TableAddon.java | 121 + .../swingx/plaf/TableHeaderAddon.java | 64 + .../jdesktop/swingx/plaf/TaskPaneAddon.java | 233 + .../swingx/plaf/TaskPaneContainerAddon.java | 143 + .../swingx/plaf/TaskPaneContainerUI.java | 32 + .../org/jdesktop/swingx/plaf/TaskPaneUI.java | 45 + .../jdesktop/swingx/plaf/TextUIWrapper.java | 164 + .../swingx/plaf/TipOfTheDayAddon.java | 87 + .../jdesktop/swingx/plaf/TipOfTheDayUI.java | 48 + .../swingx/plaf/TitledPanelAddon.java | 107 + .../jdesktop/swingx/plaf/TitledPanelUI.java | 50 + .../org/jdesktop/swingx/plaf/UIAction.java | 111 + .../swingx/plaf/UIColorHighlighterAddon.java | 131 + .../org/jdesktop/swingx/plaf/UIDependent.java | 36 + .../jdesktop/swingx/plaf/UIManagerExt.java | 721 +++ .../org/jdesktop/swingx/plaf/XListAddon.java | 100 + .../swingx/plaf/basic/BasicBusyLabelUI.java | 68 + .../basic/BasicCalendarHeaderHandler.java | 262 + .../basic/BasicCalendarRenderingHandler.java | 346 ++ .../swingx/plaf/basic/BasicDatePickerUI.java | 1670 +++++++ .../swingx/plaf/basic/BasicErrorPaneUI.java | 1098 ++++ .../swingx/plaf/basic/BasicHeaderUI.java | 454 ++ .../swingx/plaf/basic/BasicHyperlinkUI.java | 869 ++++ .../swingx/plaf/basic/BasicLoginPaneUI.java | 159 + .../plaf/basic/BasicLookAndFeelAddons.java | 53 + .../swingx/plaf/basic/BasicMonthViewUI.java | 2323 +++++++++ .../plaf/basic/BasicMultiThumbSliderUI.java | 95 + .../swingx/plaf/basic/BasicStatusBarUI.java | 625 +++ .../plaf/basic/BasicTaskPaneContainerUI.java | 136 + .../swingx/plaf/basic/BasicTaskPaneUI.java | 874 ++++ .../swingx/plaf/basic/BasicTipOfTheDayUI.java | 352 ++ .../swingx/plaf/basic/BasicTitledPanelUI.java | 339 ++ .../swingx/plaf/basic/CalendarAdapter.java | 127 + .../plaf/basic/CalendarCellContext.java | 214 + .../plaf/basic/CalendarHeaderHandler.java | 312 ++ .../plaf/basic/CalendarRenderingHandler.java | 60 + .../swingx/plaf/basic/CalendarState.java | 35 + .../swingx/plaf/basic/CapsLockSupport.java | 142 + .../basic/SpinningCalendarHeaderHandler.java | 512 ++ .../plaf/basic/TextCrossingPainter.java | 96 + .../plaf/basic/core/BasicTransferable.java | 283 ++ .../swingx/plaf/basic/core/BasicXListUI.java | 3119 ++++++++++++ .../basic/core/DragRecognitionSupport.java | 205 + .../swingx/plaf/basic/core/LazyActionMap.java | 165 + .../swingx/plaf/basic/core/ListSortUI.java | 503 ++ .../swingx/plaf/basic/package-info.java | 26 + .../basic/resources/DatePicker.properties | 11 + .../basic/resources/DatePicker_cs.properties | 9 + .../basic/resources/DatePicker_da.properties | 12 + .../basic/resources/DatePicker_de.properties | 8 + .../basic/resources/DatePicker_en.properties | 0 .../resources/DatePicker_en_GB.properties | 11 + .../resources/DatePicker_en_US.properties | 8 + .../basic/resources/DatePicker_es.properties | 4 + .../basic/resources/DatePicker_fr.properties | 8 + .../basic/resources/DatePicker_it.properties | 8 + .../basic/resources/DatePicker_nl.properties | 8 + .../resources/DatePicker_pl_PL.properties | 8 + .../resources/DatePicker_pt_BR.properties | 8 + .../basic/resources/DatePicker_sv.properties | 8 + .../plaf/basic/resources/ErrorPane.properties | 9 + .../basic/resources/ErrorPane_cs.properties | 9 + .../basic/resources/ErrorPane_da.properties | 9 + .../basic/resources/ErrorPane_de.properties | 9 + .../basic/resources/ErrorPane_en.properties | 0 .../basic/resources/ErrorPane_es.properties | 10 + .../basic/resources/ErrorPane_fr.properties | 9 + .../basic/resources/ErrorPane_it.properties | 9 + .../basic/resources/ErrorPane_nl.properties | 9 + .../basic/resources/ErrorPane_pl.properties | 9 + .../resources/ErrorPane_pt_BR.properties | 9 + .../basic/resources/ErrorPane_sv.properties | 9 + .../plaf/basic/resources/LoginPane.properties | 26 + .../basic/resources/LoginPane_cs.properties | 26 + .../basic/resources/LoginPane_de.properties | 25 + .../basic/resources/LoginPane_en.properties | 0 .../basic/resources/LoginPane_es.properties | 28 + .../basic/resources/LoginPane_fr.properties | 25 + .../basic/resources/LoginPane_it.properties | 26 + .../basic/resources/LoginPane_nl.properties | 26 + .../basic/resources/LoginPane_pl.properties | 24 + .../resources/LoginPane_pt_BR.properties | 23 + .../basic/resources/LoginPane_sv.properties | 26 + .../basic/resources/SearchField.properties | 4 + .../basic/resources/SearchField_de.properties | 4 + .../basic/resources/SearchField_en.properties | 0 .../basic/resources/TipOfTheDay.properties | 6 + .../plaf/basic/resources/TipOfTheDay24.gif | Bin 0 -> 742 bytes .../basic/resources/TipOfTheDay_cs.properties | 7 + .../basic/resources/TipOfTheDay_de.properties | 6 + .../basic/resources/TipOfTheDay_en.properties | 0 .../basic/resources/TipOfTheDay_es.properties | 6 + .../basic/resources/TipOfTheDay_fr.properties | 6 + .../basic/resources/TipOfTheDay_it.properties | 6 + .../basic/resources/TipOfTheDay_nl.properties | 6 + .../basic/resources/TipOfTheDay_pl.properties | 6 + .../resources/TipOfTheDay_pt_BR.properties | 6 + .../basic/resources/TipOfTheDay_sv.properties | 6 + .../swingx/plaf/basic/resources/clear.gif | Bin 0 -> 1026 bytes .../plaf/basic/resources/clear_pressed.gif | Bin 0 -> 1032 bytes .../plaf/basic/resources/clear_rollover.gif | Bin 0 -> 1025 bytes .../swingx/plaf/basic/resources/error16.png | Bin 0 -> 3582 bytes .../plaf/basic/resources/month-down.png | Bin 0 -> 3138 bytes .../swingx/plaf/basic/resources/month-up.png | Bin 0 -> 3139 bytes .../swingx/plaf/basic/resources/search.gif | Bin 0 -> 547 bytes .../plaf/basic/resources/search_popup.gif | Bin 0 -> 556 bytes .../plaf/basic/resources/swingx.properties | 59 + .../plaf/basic/resources/swingx_cs.properties | 61 + .../plaf/basic/resources/swingx_da.properties | 59 + .../plaf/basic/resources/swingx_de.properties | 60 + .../plaf/basic/resources/swingx_en.properties | 0 .../plaf/basic/resources/swingx_es.properties | 57 + .../plaf/basic/resources/swingx_fr.properties | 60 + .../plaf/basic/resources/swingx_it.properties | 59 + .../plaf/basic/resources/swingx_nl.properties | 59 + .../plaf/basic/resources/swingx_pl.properties | 59 + .../basic/resources/swingx_pt_BR.properties | 59 + .../plaf/basic/resources/swingx_sv.properties | 59 + .../plaf/linux/LinuxLookAndFeelAddons.java | 65 + .../swingx/plaf/linux/package-info.java | 26 + .../swingx/plaf/linux/resources/combo-gtk.png | Bin 0 -> 2904 bytes .../swingx/plaf/macosx/MacOSXErrorPaneUI.java | 215 + .../plaf/macosx/MacOSXLookAndFeelAddons.java | 48 + .../swingx/plaf/macosx/MacOSXStatusBarUI.java | 58 + .../swingx/plaf/macosx/package-info.java | 26 + .../macosx/resources/ErrorPane.properties | 7 + .../macosx/resources/ErrorPane_cs.properties | 7 + .../macosx/resources/ErrorPane_da.properties | 7 + .../macosx/resources/ErrorPane_de.properties | 7 + .../macosx/resources/ErrorPane_es.properties | 3 + .../macosx/resources/ErrorPane_fr.properties | 7 + .../macosx/resources/ErrorPane_it.properties | 7 + .../macosx/resources/ErrorPane_nl.properties | 7 + .../resources/ErrorPane_pt_BR.properties | 7 + .../macosx/resources/ErrorPane_sv.properties | 7 + .../swingx/plaf/macosx/resources/clear.png | Bin 0 -> 322 bytes .../plaf/macosx/resources/clear_pressed.png | Bin 0 -> 412 bytes .../plaf/macosx/resources/clear_rollover.png | Bin 0 -> 326 bytes .../plaf/macosx/resources/combo-osx.png | Bin 0 -> 2850 bytes .../swingx/plaf/macosx/resources/search.png | Bin 0 -> 331 bytes .../plaf/macosx/resources/search_popup.png | Bin 0 -> 954 bytes .../plaf/metal/MetalLookAndFeelAddons.java | 72 + .../swingx/plaf/metal/MetalStatusBarUI.java | 94 + .../swingx/plaf/metal/MetalTaskPaneUI.java | 84 + .../swingx/plaf/metal/package-info.java | 26 + .../swingx/plaf/misc/GlossyTaskPaneUI.java | 156 + .../swingx/plaf/misc/package-info.java | 25 + .../plaf/motif/MotifLookAndFeelAddons.java | 38 + .../swingx/plaf/motif/package-info.java | 26 + .../plaf/nimbus/NimbusLookAndFeelAddons.java | 38 + .../swingx/plaf/nimbus/NimbusTaskPaneUI.java | 167 + .../swingx/plaf/nimbus/package-info.java | 26 + .../jdesktop/swingx/plaf/package-info.java | 32 + .../swingx/plaf/synth/SynthBorder.java | 125 + .../jdesktop/swingx/plaf/synth/SynthUI.java | 45 + .../swingx/plaf/synth/SynthUtils.java | 200 + .../swingx/plaf/synth/SynthXListUI.java | 280 ++ .../jdesktop/swingx/plaf/synth/XRegion.java | 73 + .../WindowsClassicLookAndFeelAddons.java | 61 + .../windows/WindowsClassicStatusBarUI.java | 87 + .../windows/WindowsClassicTaskPaneUI.java | 80 + .../windows/WindowsLookAndFeelAddons.java | 93 + .../plaf/windows/WindowsStatusBarUI.java | 101 + .../plaf/windows/WindowsTaskPaneUI.java | 148 + .../plaf/windows/WindowsTipOfTheDayUI.java | 116 + .../swingx/plaf/windows/package-info.java | 26 + .../windows/resources/TipOfTheDay.properties | 2 + .../resources/TipOfTheDay_cs.properties | 3 + .../resources/TipOfTheDay_de.properties | 2 + .../resources/TipOfTheDay_es.properties | 2 + .../resources/TipOfTheDay_fr.properties | 2 + .../resources/TipOfTheDay_it.properties | 2 + .../resources/TipOfTheDay_nl.properties | 2 + .../resources/TipOfTheDay_pl.properties | 2 + .../resources/TipOfTheDay_pt_BR.properties | 2 + .../resources/TipOfTheDay_sv.properties | 2 + .../swingx/plaf/windows/resources/clear.gif | Bin 0 -> 222 bytes .../plaf/windows/resources/clear_pressed.gif | Bin 0 -> 660 bytes .../plaf/windows/resources/clear_rollover.gif | Bin 0 -> 701 bytes .../plaf/windows/resources/combo-w2k.png | Bin 0 -> 2833 bytes .../plaf/windows/resources/combo-xp.png | Bin 0 -> 2930 bytes .../swingx/plaf/windows/resources/search.gif | Bin 0 -> 609 bytes .../plaf/windows/resources/search_popup.gif | Bin 0 -> 273 bytes .../resources/search_popup_pressed.gif | Bin 0 -> 228 bytes .../resources/search_popup_rollover.gif | Bin 0 -> 363 bytes .../plaf/windows/resources/search_pressed.gif | Bin 0 -> 1161 bytes .../windows/resources/search_rollover.gif | Bin 0 -> 1207 bytes .../resources/silver-statusbar-left.png | Bin 0 -> 223 bytes .../resources/silver-statusbar-middle.png | Bin 0 -> 196 bytes .../resources/silver-statusbar-right.png | Bin 0 -> 370 bytes .../plaf/windows/resources/statusbar-left.png | Bin 0 -> 3200 bytes .../windows/resources/statusbar-middle.png | Bin 0 -> 2864 bytes .../windows/resources/statusbar-right.png | Bin 0 -> 3194 bytes .../plaf/windows/resources/tipoftheday.png | Bin 0 -> 1184 bytes .../jdesktop/swingx/prompt/BuddyButton.java | 62 + .../jdesktop/swingx/prompt/BuddySupport.java | 151 + .../jdesktop/swingx/prompt/PromptSupport.java | 327 ++ .../swingx/renderer/AbstractRenderer.java | 126 + .../swingx/renderer/BooleanValue.java | 32 + .../jdesktop/swingx/renderer/CellContext.java | 436 ++ .../swingx/renderer/CheckBoxProvider.java | 184 + .../swingx/renderer/ComponentProvider.java | 383 ++ .../swingx/renderer/DefaultListRenderer.java | 205 + .../swingx/renderer/DefaultTableRenderer.java | 186 + .../swingx/renderer/DefaultTreeRenderer.java | 165 + .../swingx/renderer/DefaultVisuals.java | 264 + .../swingx/renderer/FormatStringValue.java | 93 + .../swingx/renderer/HyperlinkProvider.java | 285 ++ .../jdesktop/swingx/renderer/IconAware.java | 33 + .../jdesktop/swingx/renderer/IconValue.java | 80 + .../jdesktop/swingx/renderer/IconValues.java | 85 + .../swingx/renderer/JRendererCheckBox.java | 311 ++ .../swingx/renderer/JRendererLabel.java | 299 ++ .../swingx/renderer/JRendererPanel.java | 85 + .../swingx/renderer/JXRendererHyperlink.java | 214 + .../swingx/renderer/LabelProvider.java | 121 + .../swingx/renderer/ListCellContext.java | 110 + .../renderer/LocalizableStringValue.java | 119 + .../jdesktop/swingx/renderer/MappedValue.java | 98 + .../swingx/renderer/MappedValues.java | 82 + .../swingx/renderer/PainterAware.java | 36 + .../jdesktop/swingx/renderer/StringValue.java | 79 + .../swingx/renderer/StringValues.java | 189 + .../swingx/renderer/TableCellContext.java | 187 + .../swingx/renderer/TreeCellContext.java | 278 ++ .../swingx/renderer/WrappingIconPanel.java | 304 ++ .../swingx/renderer/WrappingProvider.java | 398 ++ .../swingx/renderer/package-info.java | 25 + .../rollover/ListRolloverController.java | 112 + .../swingx/rollover/ListRolloverProducer.java | 49 + .../swingx/rollover/RolloverController.java | 235 + .../swingx/rollover/RolloverProducer.java | 281 ++ .../swingx/rollover/RolloverRenderer.java | 47 + .../rollover/TableRolloverController.java | 168 + .../rollover/TableRolloverProducer.java | 47 + .../rollover/TreeRolloverController.java | 108 + .../swingx/rollover/TreeRolloverProducer.java | 87 + .../swingx/search/AbstractSearchable.java | 661 +++ .../swingx/search/ListSearchable.java | 162 + .../search/NativeSearchFieldSupport.java | 111 + .../swingx/search/PatternMatcher.java | 34 + .../jdesktop/swingx/search/PatternModel.java | 620 +++ .../swingx/search/RecentSearches.java | 364 ++ .../jdesktop/swingx/search/SearchFactory.java | 549 ++ .../jdesktop/swingx/search/Searchable.java | 90 + .../swingx/search/TableSearchable.java | 335 ++ .../swingx/search/TreeSearchable.java | 167 + .../swingx/sort/DefaultSortController.java | 405 ++ .../swingx/sort/ListSortController.java | 93 + .../org/jdesktop/swingx/sort/RowFilters.java | 216 + .../jdesktop/swingx/sort/SortController.java | 279 ++ .../org/jdesktop/swingx/sort/SortUtils.java | 90 + .../swingx/sort/StringValueProvider.java | 50 + .../swingx/sort/StringValueRegistry.java | 193 + .../swingx/sort/TableSortController.java | 164 + .../swingx/table/ColumnControlButton.java | 1040 ++++ .../swingx/table/ColumnControlPopup.java | 105 + .../jdesktop/swingx/table/ColumnFactory.java | 465 ++ .../swingx/table/DatePickerCellEditor.java | 334 ++ .../table/DefaultTableColumnModelExt.java | 388 ++ .../swingx/table/NumberEditorExt.java | 335 ++ .../table/NumberEditorNumberFormat.java | 82 + .../swingx/table/NumberFormatExt.java | 102 + .../swingx/table/StrictNumberFormatter.java | 227 + .../jdesktop/swingx/table/TableColumnExt.java | 761 +++ .../swingx/table/TableColumnModelExt.java | 232 + .../table/TableRowHeightController.java | 215 + .../jdesktop/swingx/table/TableUtilities.java | 172 + .../jdesktop/swingx/table/package-info.java | 36 + .../jdesktop/swingx/text/NumberFormatExt.java | 99 + .../swingx/text/StrictNumberFormatter.java | 226 + .../org/jdesktop/swingx/tips/DefaultTip.java | 67 + .../swingx/tips/DefaultTipOfTheDayModel.java | 75 + .../org/jdesktop/swingx/tips/TipLoader.java | 86 + .../swingx/tips/TipOfTheDayModel.java | 65 + .../jdesktop/swingx/tips/package-info.java | 27 + .../swingx/tree/DefaultXTreeCellEditor.java | 178 + .../swingx/tree/DefaultXTreeCellRenderer.java | 98 + .../swingx/tree/TreeModelSupport.java | 338 ++ .../jdesktop/swingx/tree/TreeUtilities.java | 423 ++ .../jdesktop/swingx/tree/package-info.java | 25 + .../AbstractMutableTreeTableNode.java | 282 ++ .../treetable/AbstractTreeTableModel.java | 214 + .../DefaultMutableTreeTableNode.java | 88 + .../treetable/DefaultTreeTableModel.java | 453 ++ .../swingx/treetable/FileSystemModel.java | 234 + .../treetable/MutableTreeTableNode.java | 99 + .../treetable/SimpleFileSystemModel.java | 272 + .../swingx/treetable/TreeTableCellEditor.java | 218 + .../swingx/treetable/TreeTableModel.java | 137 + .../treetable/TreeTableModelProvider.java | 51 + .../swingx/treetable/TreeTableNode.java | 116 + .../swingx/treetable/package-info.java | 36 + .../org/jdesktop/swingx/util/Contract.java | 70 + .../swingx/util/GraphicsUtilities.java | 834 ++++ .../java/org/jdesktop/swingx/util/OS.java | 130 + .../org/jdesktop/swingx/util/PaintUtils.java | 538 ++ .../org/jdesktop/swingx/util/Separator.java | 46 + .../org/jdesktop/swingx/util/ShapeUtils.java | 118 + .../org/jdesktop/swingx/util/Utilities.java | 964 ++++ .../org/jdesktop/swingx/util/WindowUtils.java | 168 + .../jdesktop/swingx/util/package-info.java | 37 + .../org/jdesktop/swingx/VerticalLayout.kt | 82 - 630 files changed, 120710 insertions(+), 1762 deletions(-) create mode 100644 src/main/java/mediathek/gui/actions/FilmListExportAction.java delete mode 100644 src/main/java/mediathek/gui/actions/FilmListWriteWorkerTask.java delete mode 100644 src/main/java/mediathek/gui/actions/export/FilmListExportAction.java delete mode 100644 src/main/java/mediathek/gui/actions/export/FilmListExportWorkerTask.java create mode 100644 src/main/java/mediathek/gui/tabs/tab_downloads/AboLabel.java create mode 100644 src/main/java/mediathek/gui/tabs/tab_downloads/ActiveDownloadsInfoLabel.java create mode 100644 src/main/java/mediathek/gui/tabs/tab_downloads/DownloadStartInfoProperty.java create mode 100644 src/main/java/mediathek/gui/tabs/tab_downloads/FailedDownloadsInfoLabel.java create mode 100644 src/main/java/mediathek/gui/tabs/tab_downloads/FinishedDownloadsInfoLabel.java delete mode 100644 src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.jfd create mode 100644 src/main/java/mediathek/gui/tabs/tab_downloads/ManualDownloadsInfoLabel.java create mode 100644 src/main/java/mediathek/gui/tabs/tab_downloads/TotalDownloadsLabel.java create mode 100644 src/main/java/mediathek/gui/tabs/tab_downloads/WaitingDownloadsInfoLabel.java create mode 100644 src/main/java/mediathek/gui/tasks/BlacklistFilterWorker.java create mode 100644 src/main/java/mediathek/gui/tasks/FilmlistWriterWorker.java create mode 100644 src/main/java/mediathek/gui/tasks/RefreshAboWorker.java delete mode 100644 src/main/java/mediathek/javafx/FilmListFilterTask.java delete mode 100644 src/main/java/mediathek/javafx/FilmListNetworkReaderTask.java delete mode 100644 src/main/java/mediathek/javafx/FilmListReaderTask.java delete mode 100644 src/main/java/mediathek/javafx/GarbageCollectionButton.java delete mode 100644 src/main/java/mediathek/javafx/InfoLabel/AboLabel.java delete mode 100644 src/main/java/mediathek/javafx/InfoLabel/ActiveDownloadsLabel.java delete mode 100644 src/main/java/mediathek/javafx/InfoLabel/ErrorLabel.java delete mode 100644 src/main/java/mediathek/javafx/InfoLabel/FinishedLabel.java delete mode 100644 src/main/java/mediathek/javafx/InfoLabel/GesamtdownloadsLabel.java delete mode 100644 src/main/java/mediathek/javafx/InfoLabel/NumDownloadsLabel.java delete mode 100644 src/main/java/mediathek/javafx/InfoLabel/WaitingLabel.java delete mode 100644 src/main/java/mediathek/javafx/MemoryMonitor.java delete mode 100644 src/main/java/mediathek/javafx/SelectedItemsLabel.java delete mode 100644 src/main/java/mediathek/javafx/StatusBarController.java delete mode 100644 src/main/java/mediathek/javafx/downloadtab/DownloadTabInformationLabel.java delete mode 100644 src/main/java/mediathek/javafx/filmlist/FilmListAgeLabel.kt delete mode 100644 src/main/java/mediathek/javafx/filmlist/FilmListCreationDateLabel.java delete mode 100644 src/main/java/mediathek/javafx/filmlist/FilmListInfoPane.java delete mode 100644 src/main/java/mediathek/javafx/filmtab/FilmInfoLabel.java delete mode 100644 src/main/java/mediathek/javafx/filmtab/FilmTabInfoPane.java delete mode 100644 src/main/java/mediathek/javafx/tool/ComputedLabel.java delete mode 100644 src/main/java/mediathek/javafx/tool/FXProgressPane.java rename src/main/java/mediathek/{javafx/filmtab/FilmTabDownloadInformationLabel.java => mainwindow/DownloadInformationLabel.java} (57%) create mode 100644 src/main/java/mediathek/mainwindow/FilmAgeLabel.java create mode 100644 src/main/java/mediathek/mainwindow/FilmListCreationDateLabel.java create mode 100644 src/main/java/mediathek/mainwindow/FilmSizeInfoLabel.java create mode 100644 src/main/java/mediathek/mainwindow/ListSelectedItemsProperty.java create mode 100644 src/main/java/mediathek/mainwindow/MemoryUsagePanel.java create mode 100644 src/main/java/mediathek/mainwindow/SelectedListItemsLabel.java create mode 100644 src/main/java/mediathek/mainwindow/StatusBar.java create mode 100644 src/main/java/org/jdesktop/beans/AbstractBean.java create mode 100644 src/main/java/org/jdesktop/beans/AbstractSerializableBean.java create mode 100644 src/main/java/org/jdesktop/beans/package-info.java create mode 100644 src/main/java/org/jdesktop/swingx/AbstractLayoutManager.kt create mode 100644 src/main/java/org/jdesktop/swingx/AbstractPatternPanel.java create mode 100644 src/main/java/org/jdesktop/swingx/AlphaPaintable.java create mode 100644 src/main/java/org/jdesktop/swingx/BackgroundPaintable.java create mode 100644 src/main/java/org/jdesktop/swingx/ForwardingRepaintManager.java create mode 100644 src/main/java/org/jdesktop/swingx/HorizontalLayout.java create mode 100644 src/main/java/org/jdesktop/swingx/JXBusyLabel.java create mode 100644 src/main/java/org/jdesktop/swingx/JXButton.java create mode 100644 src/main/java/org/jdesktop/swingx/JXCollapsiblePane.java create mode 100644 src/main/java/org/jdesktop/swingx/JXColorSelectionButton.java create mode 100644 src/main/java/org/jdesktop/swingx/JXComboBox.java create mode 100644 src/main/java/org/jdesktop/swingx/JXDatePicker.java create mode 100644 src/main/java/org/jdesktop/swingx/JXDialog.java create mode 100644 src/main/java/org/jdesktop/swingx/JXEditorPane.java create mode 100644 src/main/java/org/jdesktop/swingx/JXErrorPane.java create mode 100644 src/main/java/org/jdesktop/swingx/JXFindBar.java create mode 100644 src/main/java/org/jdesktop/swingx/JXFindPanel.java create mode 100644 src/main/java/org/jdesktop/swingx/JXFormattedTextField.java create mode 100644 src/main/java/org/jdesktop/swingx/JXFrame.java create mode 100644 src/main/java/org/jdesktop/swingx/JXGradientChooser.java create mode 100644 src/main/java/org/jdesktop/swingx/JXGraph.java create mode 100644 src/main/java/org/jdesktop/swingx/JXHeader.java create mode 100644 src/main/java/org/jdesktop/swingx/JXHyperlink.java create mode 100644 src/main/java/org/jdesktop/swingx/JXImagePanel.java create mode 100644 src/main/java/org/jdesktop/swingx/JXImageView.java create mode 100644 src/main/java/org/jdesktop/swingx/JXLabel.java create mode 100644 src/main/java/org/jdesktop/swingx/JXList.java create mode 100644 src/main/java/org/jdesktop/swingx/JXLoginPane.java create mode 100644 src/main/java/org/jdesktop/swingx/JXMonthView.java create mode 100644 src/main/java/org/jdesktop/swingx/JXMultiSplitPane.java create mode 100644 src/main/java/org/jdesktop/swingx/JXMultiThumbSlider.java create mode 100644 src/main/java/org/jdesktop/swingx/JXPanel.java create mode 100644 src/main/java/org/jdesktop/swingx/JXRadioGroup.java create mode 100644 src/main/java/org/jdesktop/swingx/JXRootPane.java create mode 100644 src/main/java/org/jdesktop/swingx/JXSearchField.java create mode 100644 src/main/java/org/jdesktop/swingx/JXSearchPanel.java create mode 100644 src/main/java/org/jdesktop/swingx/JXStatusBar.java create mode 100644 src/main/java/org/jdesktop/swingx/JXTable.java create mode 100644 src/main/java/org/jdesktop/swingx/JXTableHeader.java create mode 100644 src/main/java/org/jdesktop/swingx/JXTaskPane.java create mode 100644 src/main/java/org/jdesktop/swingx/JXTaskPaneContainer.java create mode 100644 src/main/java/org/jdesktop/swingx/JXTextArea.java create mode 100644 src/main/java/org/jdesktop/swingx/JXTextField.java create mode 100644 src/main/java/org/jdesktop/swingx/JXTipOfTheDay.java create mode 100644 src/main/java/org/jdesktop/swingx/JXTitledPanel.java create mode 100644 src/main/java/org/jdesktop/swingx/JXTitledSeparator.java create mode 100644 src/main/java/org/jdesktop/swingx/JXTree.java create mode 100644 src/main/java/org/jdesktop/swingx/JXTreeTable.java create mode 100644 src/main/java/org/jdesktop/swingx/Mnemonicable.java create mode 100644 src/main/java/org/jdesktop/swingx/MultiSplitLayout.java create mode 100644 src/main/java/org/jdesktop/swingx/RepaintManagerX.java create mode 100644 src/main/java/org/jdesktop/swingx/ScrollableSizeHint.java create mode 100644 src/main/java/org/jdesktop/swingx/StackLayout.java create mode 100644 src/main/java/org/jdesktop/swingx/SwingXUtilities.java create mode 100644 src/main/java/org/jdesktop/swingx/TranslucentRepaintManager.java create mode 100644 src/main/java/org/jdesktop/swingx/VerticalLayout.java create mode 100644 src/main/java/org/jdesktop/swingx/WrapLayout.java create mode 100644 src/main/java/org/jdesktop/swingx/action/AbstractActionExt.java create mode 100644 src/main/java/org/jdesktop/swingx/action/ActionContainerFactory.java create mode 100644 src/main/java/org/jdesktop/swingx/action/ActionFactory.java create mode 100644 src/main/java/org/jdesktop/swingx/action/ActionManager.java create mode 100644 src/main/java/org/jdesktop/swingx/action/BoundAction.java create mode 100644 src/main/java/org/jdesktop/swingx/action/CompositeAction.java create mode 100644 src/main/java/org/jdesktop/swingx/action/OpenBrowserAction.java create mode 100644 src/main/java/org/jdesktop/swingx/action/ServerAction.java create mode 100644 src/main/java/org/jdesktop/swingx/action/TargetManager.java create mode 100644 src/main/java/org/jdesktop/swingx/action/Targetable.java create mode 100644 src/main/java/org/jdesktop/swingx/action/TargetableAction.java create mode 100644 src/main/java/org/jdesktop/swingx/action/TargetableSupport.java create mode 100644 src/main/java/org/jdesktop/swingx/action/ToggleActionPropertyChangeListener.java create mode 100644 src/main/java/org/jdesktop/swingx/action/package-info.java create mode 100644 src/main/java/org/jdesktop/swingx/auth/DefaultUserNameStore.java create mode 100644 src/main/java/org/jdesktop/swingx/auth/JAASLoginService.java create mode 100644 src/main/java/org/jdesktop/swingx/auth/JDBCLoginService.java create mode 100644 src/main/java/org/jdesktop/swingx/auth/KeyChain.java create mode 100644 src/main/java/org/jdesktop/swingx/auth/LoginAdapter.java create mode 100644 src/main/java/org/jdesktop/swingx/auth/LoginEvent.java create mode 100644 src/main/java/org/jdesktop/swingx/auth/LoginListener.java create mode 100644 src/main/java/org/jdesktop/swingx/auth/LoginService.java create mode 100644 src/main/java/org/jdesktop/swingx/auth/PasswordStore.java create mode 100644 src/main/java/org/jdesktop/swingx/auth/SimpleLoginService.java create mode 100644 src/main/java/org/jdesktop/swingx/auth/UserNameStore.java create mode 100644 src/main/java/org/jdesktop/swingx/auth/UserPermissions.java create mode 100644 src/main/java/org/jdesktop/swingx/auth/package-info.java create mode 100644 src/main/java/org/jdesktop/swingx/autocomplete/AbstractAutoCompleteAdaptor.java create mode 100644 src/main/java/org/jdesktop/swingx/autocomplete/AutoComplete.java create mode 100644 src/main/java/org/jdesktop/swingx/autocomplete/AutoCompleteComboBoxEditor.java create mode 100644 src/main/java/org/jdesktop/swingx/autocomplete/AutoCompleteDecorator.java create mode 100644 src/main/java/org/jdesktop/swingx/autocomplete/AutoCompleteDocument.java create mode 100644 src/main/java/org/jdesktop/swingx/autocomplete/AutoCompleteStyledDocument.java create mode 100644 src/main/java/org/jdesktop/swingx/autocomplete/ComboBoxAdaptor.java create mode 100644 src/main/java/org/jdesktop/swingx/autocomplete/ComboBoxCellEditor.java create mode 100644 src/main/java/org/jdesktop/swingx/autocomplete/DelegatingDocumentEvent.java create mode 100644 src/main/java/org/jdesktop/swingx/autocomplete/ListAdaptor.java create mode 100644 src/main/java/org/jdesktop/swingx/autocomplete/ObjectToStringConverter.java create mode 100644 src/main/java/org/jdesktop/swingx/autocomplete/TextComponentAdaptor.java create mode 100644 src/main/java/org/jdesktop/swingx/autocomplete/package-info.java create mode 100644 src/main/java/org/jdesktop/swingx/autocomplete/workarounds/MacOSXPopupLocationFix.java create mode 100644 src/main/java/org/jdesktop/swingx/autocomplete/workarounds/package-info.java create mode 100644 src/main/java/org/jdesktop/swingx/border/DropShadowBorder.java create mode 100644 src/main/java/org/jdesktop/swingx/border/IconBorder.java create mode 100644 src/main/java/org/jdesktop/swingx/border/MatteBorderExt.java create mode 100644 src/main/java/org/jdesktop/swingx/border/package-info.java create mode 100644 src/main/java/org/jdesktop/swingx/calendar/AbstractDateSelectionModel.java create mode 100644 src/main/java/org/jdesktop/swingx/calendar/CalendarUtils.java create mode 100644 src/main/java/org/jdesktop/swingx/calendar/DatePickerFormatter.java create mode 100644 src/main/java/org/jdesktop/swingx/calendar/DateSelectionModel.java create mode 100644 src/main/java/org/jdesktop/swingx/calendar/DateSpan.java create mode 100644 src/main/java/org/jdesktop/swingx/calendar/DateUtils.java create mode 100644 src/main/java/org/jdesktop/swingx/calendar/DaySelectionModel.java create mode 100644 src/main/java/org/jdesktop/swingx/calendar/DefaultDateSelectionModel.java create mode 100644 src/main/java/org/jdesktop/swingx/calendar/SingleDaySelectionModel.java create mode 100644 src/main/java/org/jdesktop/swingx/calendar/package-info.java create mode 100644 src/main/java/org/jdesktop/swingx/color/EyeDropperColorChooserPanel.form create mode 100644 src/main/java/org/jdesktop/swingx/color/EyeDropperColorChooserPanel.java create mode 100644 src/main/java/org/jdesktop/swingx/color/GradientPreviewPanel.java create mode 100644 src/main/java/org/jdesktop/swingx/color/GradientThumbRenderer.java create mode 100644 src/main/java/org/jdesktop/swingx/color/GradientTrackRenderer.java create mode 100644 src/main/java/org/jdesktop/swingx/color/colorwell.png create mode 100644 src/main/java/org/jdesktop/swingx/color/mag.png create mode 100644 src/main/java/org/jdesktop/swingx/color/package-info.java create mode 100644 src/main/java/org/jdesktop/swingx/combobox/EnumComboBoxModel.java create mode 100644 src/main/java/org/jdesktop/swingx/combobox/JXTextFieldComboBoxEditor.java create mode 100644 src/main/java/org/jdesktop/swingx/combobox/ListComboBoxModel.java create mode 100644 src/main/java/org/jdesktop/swingx/combobox/ListModelComboBoxWrapper.java create mode 100644 src/main/java/org/jdesktop/swingx/combobox/MapComboBoxModel.java create mode 100644 src/main/java/org/jdesktop/swingx/combobox/package-info.java create mode 100644 src/main/java/org/jdesktop/swingx/decorator/AbstractHighlighter.java create mode 100644 src/main/java/org/jdesktop/swingx/decorator/AlignmentHighlighter.java create mode 100644 src/main/java/org/jdesktop/swingx/decorator/BorderHighlighter.java create mode 100644 src/main/java/org/jdesktop/swingx/decorator/ColorHighlighter.java create mode 100644 src/main/java/org/jdesktop/swingx/decorator/ComponentAdapter.java create mode 100644 src/main/java/org/jdesktop/swingx/decorator/ComponentOrientationHighlighter.java create mode 100644 src/main/java/org/jdesktop/swingx/decorator/CompoundHighlighter.java create mode 100644 src/main/java/org/jdesktop/swingx/decorator/EnabledHighlighter.java create mode 100644 src/main/java/org/jdesktop/swingx/decorator/FontHighlighter.java create mode 100644 src/main/java/org/jdesktop/swingx/decorator/HighlightPredicate.java create mode 100644 src/main/java/org/jdesktop/swingx/decorator/Highlighter.java create mode 100644 src/main/java/org/jdesktop/swingx/decorator/HighlighterFactory.java create mode 100644 src/main/java/org/jdesktop/swingx/decorator/IconHighlighter.java create mode 100644 src/main/java/org/jdesktop/swingx/decorator/PainterHighlighter.java create mode 100644 src/main/java/org/jdesktop/swingx/decorator/PatternPredicate.java create mode 100644 src/main/java/org/jdesktop/swingx/decorator/ResetDTCRColorHighlighter.java create mode 100644 src/main/java/org/jdesktop/swingx/decorator/SearchPredicate.java create mode 100644 src/main/java/org/jdesktop/swingx/decorator/ShadingColorHighlighter.java create mode 100644 src/main/java/org/jdesktop/swingx/decorator/ToolTipHighlighter.java create mode 100644 src/main/java/org/jdesktop/swingx/decorator/package-info.java create mode 100644 src/main/java/org/jdesktop/swingx/error/ErrorEvent.java create mode 100644 src/main/java/org/jdesktop/swingx/error/ErrorInfo.java create mode 100644 src/main/java/org/jdesktop/swingx/error/ErrorLevel.java create mode 100644 src/main/java/org/jdesktop/swingx/error/ErrorListener.java create mode 100644 src/main/java/org/jdesktop/swingx/error/ErrorReporter.java create mode 100644 src/main/java/org/jdesktop/swingx/error/ErrorSupport.java create mode 100644 src/main/java/org/jdesktop/swingx/error/package-info.java create mode 100644 src/main/java/org/jdesktop/swingx/event/CompoundFocusListener.java create mode 100644 src/main/java/org/jdesktop/swingx/event/DateSelectionEvent.java create mode 100644 src/main/java/org/jdesktop/swingx/event/DateSelectionListener.java create mode 100644 src/main/java/org/jdesktop/swingx/event/EventListenerMap.java create mode 100644 src/main/java/org/jdesktop/swingx/event/TableColumnModelExtListener.java create mode 100644 src/main/java/org/jdesktop/swingx/event/TreeExpansionBroadcaster.java create mode 100644 src/main/java/org/jdesktop/swingx/event/WeakEventListenerList.java create mode 100644 src/main/java/org/jdesktop/swingx/event/package-info.java create mode 100644 src/main/java/org/jdesktop/swingx/geom/Morphing2D.java create mode 100644 src/main/java/org/jdesktop/swingx/geom/Star2D.java create mode 100644 src/main/java/org/jdesktop/swingx/geom/package-info.java create mode 100644 src/main/java/org/jdesktop/swingx/graphics/BlendComposite.java create mode 100644 src/main/java/org/jdesktop/swingx/graphics/ColorUtilities.java create mode 100644 src/main/java/org/jdesktop/swingx/graphics/FilterComposite.java create mode 100644 src/main/java/org/jdesktop/swingx/graphics/Graphics2DFacade.java create mode 100644 src/main/java/org/jdesktop/swingx/graphics/ReflectionRenderer.java create mode 100644 src/main/java/org/jdesktop/swingx/graphics/ShadowRenderer.java create mode 100644 src/main/java/org/jdesktop/swingx/graphics/package-info.java create mode 100644 src/main/java/org/jdesktop/swingx/hyperlink/AbstractHyperlinkAction.java create mode 100644 src/main/java/org/jdesktop/swingx/hyperlink/EditorPaneLinkVisitor.java create mode 100644 src/main/java/org/jdesktop/swingx/hyperlink/HyperlinkAction.java create mode 100644 src/main/java/org/jdesktop/swingx/hyperlink/LinkModel.java create mode 100644 src/main/java/org/jdesktop/swingx/hyperlink/LinkModelAction.java create mode 100644 src/main/java/org/jdesktop/swingx/icon/ColumnControlIcon.java create mode 100644 src/main/java/org/jdesktop/swingx/icon/EmptyIcon.java create mode 100644 src/main/java/org/jdesktop/swingx/icon/PainterIcon.java create mode 100644 src/main/java/org/jdesktop/swingx/icon/package-info.java create mode 100644 src/main/java/org/jdesktop/swingx/image/AbstractFilter.java create mode 100644 src/main/java/org/jdesktop/swingx/image/ColorTintFilter.java create mode 100644 src/main/java/org/jdesktop/swingx/image/FastBlurFilter.java create mode 100644 src/main/java/org/jdesktop/swingx/image/GaussianBlurFilter.java create mode 100644 src/main/java/org/jdesktop/swingx/image/StackBlurFilter.java create mode 100644 src/main/java/org/jdesktop/swingx/image/package-info.java create mode 100644 src/main/java/org/jdesktop/swingx/multislider/AbstractMultiThumbModel.java create mode 100644 src/main/java/org/jdesktop/swingx/multislider/DefaultMultiThumbModel.java create mode 100644 src/main/java/org/jdesktop/swingx/multislider/MultiThumbModel.java create mode 100644 src/main/java/org/jdesktop/swingx/multislider/Thumb.java create mode 100644 src/main/java/org/jdesktop/swingx/multislider/ThumbDataEvent.java create mode 100644 src/main/java/org/jdesktop/swingx/multislider/ThumbDataListener.java create mode 100644 src/main/java/org/jdesktop/swingx/multislider/ThumbListener.java create mode 100644 src/main/java/org/jdesktop/swingx/multislider/ThumbRenderer.java create mode 100644 src/main/java/org/jdesktop/swingx/multislider/TrackRenderer.java create mode 100644 src/main/java/org/jdesktop/swingx/multislider/package-info.java create mode 100644 src/main/java/org/jdesktop/swingx/multisplitpane/DefaultSplitPaneModel.java create mode 100644 src/main/java/org/jdesktop/swingx/multisplitpane/package-info.java create mode 100644 src/main/java/org/jdesktop/swingx/package-info.java create mode 100644 src/main/java/org/jdesktop/swingx/painter/AbstractAreaPainter.java create mode 100644 src/main/java/org/jdesktop/swingx/painter/AbstractLayoutPainter.java create mode 100644 src/main/java/org/jdesktop/swingx/painter/AbstractPainter.java create mode 100644 src/main/java/org/jdesktop/swingx/painter/AlphaPainter.java create mode 100644 src/main/java/org/jdesktop/swingx/painter/BusyPainter.java create mode 100644 src/main/java/org/jdesktop/swingx/painter/CheckerboardPainter.java create mode 100644 src/main/java/org/jdesktop/swingx/painter/CompoundPainter.java create mode 100644 src/main/java/org/jdesktop/swingx/painter/GlossPainter.java create mode 100644 src/main/java/org/jdesktop/swingx/painter/ImagePainter.java create mode 100644 src/main/java/org/jdesktop/swingx/painter/MattePainter.java create mode 100644 src/main/java/org/jdesktop/swingx/painter/Painter.java create mode 100644 src/main/java/org/jdesktop/swingx/painter/PainterPaint.java create mode 100644 src/main/java/org/jdesktop/swingx/painter/PainterUtils.java create mode 100644 src/main/java/org/jdesktop/swingx/painter/Painters.java create mode 100644 src/main/java/org/jdesktop/swingx/painter/PinstripePainter.java create mode 100644 src/main/java/org/jdesktop/swingx/painter/RectanglePainter.java create mode 100644 src/main/java/org/jdesktop/swingx/painter/ShapePainter.java create mode 100644 src/main/java/org/jdesktop/swingx/painter/TextPainter.java create mode 100644 src/main/java/org/jdesktop/swingx/painter/effects/AbstractAreaEffect.java create mode 100644 src/main/java/org/jdesktop/swingx/painter/effects/AreaEffect.java create mode 100644 src/main/java/org/jdesktop/swingx/painter/effects/GlowPathEffect.java create mode 100644 src/main/java/org/jdesktop/swingx/painter/effects/InnerGlowPathEffect.java create mode 100644 src/main/java/org/jdesktop/swingx/painter/effects/InnerShadowPathEffect.java create mode 100644 src/main/java/org/jdesktop/swingx/painter/effects/NeonBorderEffect.java create mode 100644 src/main/java/org/jdesktop/swingx/painter/effects/ShadowPathEffect.java create mode 100644 src/main/java/org/jdesktop/swingx/painter/effects/package-info.java create mode 100644 src/main/java/org/jdesktop/swingx/painter/package-info.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/AbstractComponentAddon.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/AbstractUIChangeHandler.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/BuddyLayoutAndBorder.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/BuddyTextFieldUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/BusyLabelAddon.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/BusyLabelUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/ColumnControlButtonAddon.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/ComponentAddon.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/DatePickerAddon.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/DatePickerUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/DefaultsList.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/ErrorPaneAddon.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/ErrorPaneUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/HeaderAddon.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/HeaderUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/HyperlinkAddon.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/LoginPaneAddon.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/LoginPaneUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/LookAndFeelAddons.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/LookAndFeelUtils.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/MonthViewAddon.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/MonthViewUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/MultiThumbSliderAddon.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/MultiThumbSliderUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/PainterUIResource.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/PromptTextAreaUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/PromptTextFieldUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/PromptTextUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/SafeBorder.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/SearchFieldAddon.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/SearchFieldUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/ShapeUIResource.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/StatusBarAddon.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/StatusBarUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/TableAddon.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/TableHeaderAddon.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/TaskPaneAddon.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/TaskPaneContainerAddon.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/TaskPaneContainerUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/TaskPaneUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/TextUIWrapper.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/TipOfTheDayAddon.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/TipOfTheDayUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/TitledPanelAddon.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/TitledPanelUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/UIAction.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/UIColorHighlighterAddon.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/UIDependent.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/UIManagerExt.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/XListAddon.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/BasicBusyLabelUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/BasicCalendarHeaderHandler.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/BasicCalendarRenderingHandler.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/BasicDatePickerUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/BasicErrorPaneUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/BasicHeaderUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/BasicHyperlinkUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/BasicLoginPaneUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/BasicLookAndFeelAddons.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/BasicMonthViewUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/BasicMultiThumbSliderUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/BasicStatusBarUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/BasicTaskPaneContainerUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/BasicTaskPaneUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/BasicTipOfTheDayUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/BasicTitledPanelUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/CalendarAdapter.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/CalendarCellContext.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/CalendarHeaderHandler.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/CalendarRenderingHandler.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/CalendarState.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/CapsLockSupport.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/SpinningCalendarHeaderHandler.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/TextCrossingPainter.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/core/BasicTransferable.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/core/BasicXListUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/core/DragRecognitionSupport.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/core/LazyActionMap.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/core/ListSortUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/package-info.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_cs.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_da.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_de.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_en.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_en_GB.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_en_US.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_es.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_fr.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_it.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_nl.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_pl_PL.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_pt_BR.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_sv.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_cs.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_da.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_de.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_en.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_es.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_fr.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_it.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_nl.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_pl.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_pt_BR.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_sv.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_cs.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_de.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_en.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_es.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_fr.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_it.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_nl.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_pl.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_pt_BR.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_sv.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/SearchField.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/SearchField_de.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/SearchField_en.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay24.gif create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_cs.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_de.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_en.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_es.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_fr.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_it.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_nl.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_pl.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_pt_BR.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_sv.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/clear.gif create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/clear_pressed.gif create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/clear_rollover.gif create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/error16.png create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/month-down.png create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/month-up.png create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/search.gif create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/search_popup.gif create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_cs.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_da.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_de.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_en.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_es.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_fr.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_it.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_nl.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_pl.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_pt_BR.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_sv.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/linux/LinuxLookAndFeelAddons.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/linux/package-info.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/linux/resources/combo-gtk.png create mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/MacOSXErrorPaneUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/MacOSXLookAndFeelAddons.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/MacOSXStatusBarUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/package-info.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_cs.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_da.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_de.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_es.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_fr.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_it.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_nl.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_pt_BR.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_sv.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/resources/clear.png create mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/resources/clear_pressed.png create mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/resources/clear_rollover.png create mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/resources/combo-osx.png create mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/resources/search.png create mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/resources/search_popup.png create mode 100644 src/main/java/org/jdesktop/swingx/plaf/metal/MetalLookAndFeelAddons.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/metal/MetalStatusBarUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/metal/MetalTaskPaneUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/metal/package-info.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/misc/GlossyTaskPaneUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/misc/package-info.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/motif/MotifLookAndFeelAddons.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/motif/package-info.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/nimbus/NimbusLookAndFeelAddons.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/nimbus/NimbusTaskPaneUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/nimbus/package-info.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/package-info.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/synth/SynthBorder.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/synth/SynthUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/synth/SynthUtils.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/synth/SynthXListUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/synth/XRegion.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/WindowsClassicLookAndFeelAddons.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/WindowsClassicStatusBarUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/WindowsClassicTaskPaneUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/WindowsLookAndFeelAddons.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/WindowsStatusBarUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/WindowsTaskPaneUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/WindowsTipOfTheDayUI.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/package-info.java create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_cs.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_de.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_es.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_fr.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_it.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_nl.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_pl.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_pt_BR.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_sv.properties create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/clear.gif create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/clear_pressed.gif create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/clear_rollover.gif create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/combo-w2k.png create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/combo-xp.png create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/search.gif create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/search_popup.gif create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/search_popup_pressed.gif create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/search_popup_rollover.gif create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/search_pressed.gif create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/search_rollover.gif create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/silver-statusbar-left.png create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/silver-statusbar-middle.png create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/silver-statusbar-right.png create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/statusbar-left.png create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/statusbar-middle.png create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/statusbar-right.png create mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/tipoftheday.png create mode 100644 src/main/java/org/jdesktop/swingx/prompt/BuddyButton.java create mode 100644 src/main/java/org/jdesktop/swingx/prompt/BuddySupport.java create mode 100644 src/main/java/org/jdesktop/swingx/prompt/PromptSupport.java create mode 100644 src/main/java/org/jdesktop/swingx/renderer/AbstractRenderer.java create mode 100644 src/main/java/org/jdesktop/swingx/renderer/BooleanValue.java create mode 100644 src/main/java/org/jdesktop/swingx/renderer/CellContext.java create mode 100644 src/main/java/org/jdesktop/swingx/renderer/CheckBoxProvider.java create mode 100644 src/main/java/org/jdesktop/swingx/renderer/ComponentProvider.java create mode 100644 src/main/java/org/jdesktop/swingx/renderer/DefaultListRenderer.java create mode 100644 src/main/java/org/jdesktop/swingx/renderer/DefaultTableRenderer.java create mode 100644 src/main/java/org/jdesktop/swingx/renderer/DefaultTreeRenderer.java create mode 100644 src/main/java/org/jdesktop/swingx/renderer/DefaultVisuals.java create mode 100644 src/main/java/org/jdesktop/swingx/renderer/FormatStringValue.java create mode 100644 src/main/java/org/jdesktop/swingx/renderer/HyperlinkProvider.java create mode 100644 src/main/java/org/jdesktop/swingx/renderer/IconAware.java create mode 100644 src/main/java/org/jdesktop/swingx/renderer/IconValue.java create mode 100644 src/main/java/org/jdesktop/swingx/renderer/IconValues.java create mode 100644 src/main/java/org/jdesktop/swingx/renderer/JRendererCheckBox.java create mode 100644 src/main/java/org/jdesktop/swingx/renderer/JRendererLabel.java create mode 100644 src/main/java/org/jdesktop/swingx/renderer/JRendererPanel.java create mode 100644 src/main/java/org/jdesktop/swingx/renderer/JXRendererHyperlink.java create mode 100644 src/main/java/org/jdesktop/swingx/renderer/LabelProvider.java create mode 100644 src/main/java/org/jdesktop/swingx/renderer/ListCellContext.java create mode 100644 src/main/java/org/jdesktop/swingx/renderer/LocalizableStringValue.java create mode 100644 src/main/java/org/jdesktop/swingx/renderer/MappedValue.java create mode 100644 src/main/java/org/jdesktop/swingx/renderer/MappedValues.java create mode 100644 src/main/java/org/jdesktop/swingx/renderer/PainterAware.java create mode 100644 src/main/java/org/jdesktop/swingx/renderer/StringValue.java create mode 100644 src/main/java/org/jdesktop/swingx/renderer/StringValues.java create mode 100644 src/main/java/org/jdesktop/swingx/renderer/TableCellContext.java create mode 100644 src/main/java/org/jdesktop/swingx/renderer/TreeCellContext.java create mode 100644 src/main/java/org/jdesktop/swingx/renderer/WrappingIconPanel.java create mode 100644 src/main/java/org/jdesktop/swingx/renderer/WrappingProvider.java create mode 100644 src/main/java/org/jdesktop/swingx/renderer/package-info.java create mode 100644 src/main/java/org/jdesktop/swingx/rollover/ListRolloverController.java create mode 100644 src/main/java/org/jdesktop/swingx/rollover/ListRolloverProducer.java create mode 100644 src/main/java/org/jdesktop/swingx/rollover/RolloverController.java create mode 100644 src/main/java/org/jdesktop/swingx/rollover/RolloverProducer.java create mode 100644 src/main/java/org/jdesktop/swingx/rollover/RolloverRenderer.java create mode 100644 src/main/java/org/jdesktop/swingx/rollover/TableRolloverController.java create mode 100644 src/main/java/org/jdesktop/swingx/rollover/TableRolloverProducer.java create mode 100644 src/main/java/org/jdesktop/swingx/rollover/TreeRolloverController.java create mode 100644 src/main/java/org/jdesktop/swingx/rollover/TreeRolloverProducer.java create mode 100644 src/main/java/org/jdesktop/swingx/search/AbstractSearchable.java create mode 100644 src/main/java/org/jdesktop/swingx/search/ListSearchable.java create mode 100644 src/main/java/org/jdesktop/swingx/search/NativeSearchFieldSupport.java create mode 100644 src/main/java/org/jdesktop/swingx/search/PatternMatcher.java create mode 100644 src/main/java/org/jdesktop/swingx/search/PatternModel.java create mode 100644 src/main/java/org/jdesktop/swingx/search/RecentSearches.java create mode 100644 src/main/java/org/jdesktop/swingx/search/SearchFactory.java create mode 100644 src/main/java/org/jdesktop/swingx/search/Searchable.java create mode 100644 src/main/java/org/jdesktop/swingx/search/TableSearchable.java create mode 100644 src/main/java/org/jdesktop/swingx/search/TreeSearchable.java create mode 100644 src/main/java/org/jdesktop/swingx/sort/DefaultSortController.java create mode 100644 src/main/java/org/jdesktop/swingx/sort/ListSortController.java create mode 100644 src/main/java/org/jdesktop/swingx/sort/RowFilters.java create mode 100644 src/main/java/org/jdesktop/swingx/sort/SortController.java create mode 100644 src/main/java/org/jdesktop/swingx/sort/SortUtils.java create mode 100644 src/main/java/org/jdesktop/swingx/sort/StringValueProvider.java create mode 100644 src/main/java/org/jdesktop/swingx/sort/StringValueRegistry.java create mode 100644 src/main/java/org/jdesktop/swingx/sort/TableSortController.java create mode 100644 src/main/java/org/jdesktop/swingx/table/ColumnControlButton.java create mode 100644 src/main/java/org/jdesktop/swingx/table/ColumnControlPopup.java create mode 100644 src/main/java/org/jdesktop/swingx/table/ColumnFactory.java create mode 100644 src/main/java/org/jdesktop/swingx/table/DatePickerCellEditor.java create mode 100644 src/main/java/org/jdesktop/swingx/table/DefaultTableColumnModelExt.java create mode 100644 src/main/java/org/jdesktop/swingx/table/NumberEditorExt.java create mode 100644 src/main/java/org/jdesktop/swingx/table/NumberEditorNumberFormat.java create mode 100644 src/main/java/org/jdesktop/swingx/table/NumberFormatExt.java create mode 100644 src/main/java/org/jdesktop/swingx/table/StrictNumberFormatter.java create mode 100644 src/main/java/org/jdesktop/swingx/table/TableColumnExt.java create mode 100644 src/main/java/org/jdesktop/swingx/table/TableColumnModelExt.java create mode 100644 src/main/java/org/jdesktop/swingx/table/TableRowHeightController.java create mode 100644 src/main/java/org/jdesktop/swingx/table/TableUtilities.java create mode 100644 src/main/java/org/jdesktop/swingx/table/package-info.java create mode 100644 src/main/java/org/jdesktop/swingx/text/NumberFormatExt.java create mode 100644 src/main/java/org/jdesktop/swingx/text/StrictNumberFormatter.java create mode 100644 src/main/java/org/jdesktop/swingx/tips/DefaultTip.java create mode 100644 src/main/java/org/jdesktop/swingx/tips/DefaultTipOfTheDayModel.java create mode 100644 src/main/java/org/jdesktop/swingx/tips/TipLoader.java create mode 100644 src/main/java/org/jdesktop/swingx/tips/TipOfTheDayModel.java create mode 100644 src/main/java/org/jdesktop/swingx/tips/package-info.java create mode 100644 src/main/java/org/jdesktop/swingx/tree/DefaultXTreeCellEditor.java create mode 100644 src/main/java/org/jdesktop/swingx/tree/DefaultXTreeCellRenderer.java create mode 100644 src/main/java/org/jdesktop/swingx/tree/TreeModelSupport.java create mode 100644 src/main/java/org/jdesktop/swingx/tree/TreeUtilities.java create mode 100644 src/main/java/org/jdesktop/swingx/tree/package-info.java create mode 100644 src/main/java/org/jdesktop/swingx/treetable/AbstractMutableTreeTableNode.java create mode 100644 src/main/java/org/jdesktop/swingx/treetable/AbstractTreeTableModel.java create mode 100644 src/main/java/org/jdesktop/swingx/treetable/DefaultMutableTreeTableNode.java create mode 100644 src/main/java/org/jdesktop/swingx/treetable/DefaultTreeTableModel.java create mode 100644 src/main/java/org/jdesktop/swingx/treetable/FileSystemModel.java create mode 100644 src/main/java/org/jdesktop/swingx/treetable/MutableTreeTableNode.java create mode 100644 src/main/java/org/jdesktop/swingx/treetable/SimpleFileSystemModel.java create mode 100644 src/main/java/org/jdesktop/swingx/treetable/TreeTableCellEditor.java create mode 100644 src/main/java/org/jdesktop/swingx/treetable/TreeTableModel.java create mode 100644 src/main/java/org/jdesktop/swingx/treetable/TreeTableModelProvider.java create mode 100644 src/main/java/org/jdesktop/swingx/treetable/TreeTableNode.java create mode 100644 src/main/java/org/jdesktop/swingx/treetable/package-info.java create mode 100644 src/main/java/org/jdesktop/swingx/util/Contract.java create mode 100644 src/main/java/org/jdesktop/swingx/util/GraphicsUtilities.java create mode 100644 src/main/java/org/jdesktop/swingx/util/OS.java create mode 100644 src/main/java/org/jdesktop/swingx/util/PaintUtils.java create mode 100644 src/main/java/org/jdesktop/swingx/util/Separator.java create mode 100644 src/main/java/org/jdesktop/swingx/util/ShapeUtils.java create mode 100644 src/main/java/org/jdesktop/swingx/util/Utilities.java create mode 100644 src/main/java/org/jdesktop/swingx/util/WindowUtils.java create mode 100644 src/main/java/org/jdesktop/swingx/util/package-info.java delete mode 100644 src/main/kotlin/org/jdesktop/swingx/VerticalLayout.kt diff --git a/pom.xml b/pom.xml index c4588c7928..81a7868000 100755 --- a/pom.xml +++ b/pom.xml @@ -142,6 +142,25 @@
+ + org.jfree + jfreechart + 1.5.3 + + + + org.swinglabs.swingx + swingx-mavensupport + 1.6.5-1 + + + + org.kohsuke.metainf-services + metainf-services + 1.9 + provided + + com.formdev flatlaf diff --git a/src/main/java/mediathek/Main.java b/src/main/java/mediathek/Main.java index 5cf96cec9f..3e7857864f 100644 --- a/src/main/java/mediathek/Main.java +++ b/src/main/java/mediathek/Main.java @@ -87,8 +87,7 @@ private static void removeMediaDb() { logger.info("Moving old unsupported media database to trash."); mediathek.tool.FileUtils.moveToTrash(mediaDbPath); } - } - catch (IOException ignored) { + } catch (IOException ignored) { } } @@ -123,8 +122,7 @@ private static void setupLogging() { final PatternLayout consolePattern; if (Config.isEnhancedLoggingEnabled() || Config.isDebugModeEnabled()) { consolePattern = PatternLayout.newBuilder().withPattern("[%-5level] [%t] %c - %msg%n").build(); - } - else { + } else { consolePattern = PatternLayout.newBuilder().withPattern(". %msg%n").build(); } @@ -228,13 +226,11 @@ private static void migrateOldConfigSettings() { try { SettingsMigrator migrator = new SettingsMigrator(settingsFile); migrator.migrate(); - } - catch (Exception e) { + } catch (Exception e) { logger.error("settings migration error", e); } } - } - else + } else logger.trace("nothing to migrate"); } } @@ -242,8 +238,7 @@ private static void migrateOldConfigSettings() { private static void printPortableModeInfo() { if (Config.isPortableMode()) { logger.info("Configuring baseFilePath {} for portable mode", Config.baseFilePath); - } - else + } else logger.info("Configuring for non-portable mode"); } @@ -276,16 +271,16 @@ private static void setupDockIcon() { } private static final Color JTABLE_ALTERNATE_ROW_COLOR = new Color(247, 247, 247); + private static void setupFlatLaf() { FlatLightLaf.setup(); - UIManager.put("TabbedPane.showTabSeparators", true ); + UIManager.put("TabbedPane.showTabSeparators", true); // install alternate row color only for windows >8 and macOS, Linux boolean installAlternateRowColor; if (SystemUtils.IS_OS_WINDOWS && VersionHelpers.IsWindows8OrGreater()) { installAlternateRowColor = true; - } - else installAlternateRowColor = SystemUtils.IS_OS_MAC_OSX || SystemUtils.IS_OS_LINUX; + } else installAlternateRowColor = SystemUtils.IS_OS_MAC_OSX || SystemUtils.IS_OS_LINUX; if (installAlternateRowColor) UIManager.put("Table.alternateRowColor", JTABLE_ALTERNATE_ROW_COLOR); @@ -339,95 +334,97 @@ private static void checkJVMSettings() { * @param args the command line arguments */ public static void main(final String... args) { - setupEnvironmentProperties(); - - if (GraphicsEnvironment.isHeadless()) { - System.err.println("Diese Version von MediathekView unterstützt keine Kommandozeilenausführung."); - System.exit(1); - } + EventQueue.invokeLater(() -> { + setupEnvironmentProperties(); - CommandLine cmd = new CommandLine(Config.class); - try { - var parseResult = cmd.parseArgs(args); - if (parseResult.isUsageHelpRequested()) { - cmd.usage(System.out); - System.exit(cmd.getCommandSpec().exitCodeOnUsageHelp()); + if (GraphicsEnvironment.isHeadless()) { + System.err.println("Diese Version von MediathekView unterstützt keine Kommandozeilenausführung."); + System.exit(1); } - Config.setPortableMode(parseResult.hasMatchedPositional(0)); - if (Config.isPortableMode()) { - StandardLocations.INSTANCE.setPortableBaseDirectory(Config.baseFilePath); - } + CommandLine cmd = new CommandLine(Config.class); + try { + var parseResult = cmd.parseArgs(args); + if (parseResult.isUsageHelpRequested()) { + cmd.usage(System.out); + System.exit(cmd.getCommandSpec().exitCodeOnUsageHelp()); + } - setupLogging(); - printPortableModeInfo(); + Config.setPortableMode(parseResult.hasMatchedPositional(0)); + if (Config.isPortableMode()) { + StandardLocations.INSTANCE.setPortableBaseDirectory(Config.baseFilePath); + } - setupDockIcon(); - setupFlatLaf(); + setupLogging(); + printPortableModeInfo(); - if (!Config.isDisableJvmParameterChecks()) - checkJVMSettings(); + setupDockIcon(); + setupFlatLaf(); - if (SystemUtils.IS_OS_WINDOWS) { - if (!VersionHelpers.IsWindows10OrGreater()) - logger.warn("This Operating System configuration is too old and will be unsupported in the next updates."); - } + if (!Config.isDisableJvmParameterChecks()) + checkJVMSettings(); - setupCpuAffinity(); + if (SystemUtils.IS_OS_WINDOWS) { + if (!VersionHelpers.IsWindows10OrGreater()) + logger.warn("This Operating System configuration is too old and will be unsupported in the next updates."); + } - initializeJavaFX(); + setupCpuAffinity(); - removeMediaDb(); + initializeJavaFX(); - JFXHiddenApplication.launchApplication(); - checkMemoryRequirements(); + removeMediaDb(); - installSingleInstanceHandler(); + JFXHiddenApplication.launchApplication(); + checkMemoryRequirements(); - printVersionInformation(); + installSingleInstanceHandler(); - printJvmParameters(); - printArguments(args); - } catch (CommandLine.ParameterException ex) { - try (var err = cmd.getErr()) { - err.println(ex.getMessage()); - if (!CommandLine.UnmatchedArgumentException.printSuggestions(ex, err)) { - ex.getCommandLine().usage(err); + printVersionInformation(); + + printJvmParameters(); + printArguments(args); + } catch (CommandLine.ParameterException ex) { + try (var err = cmd.getErr()) { + err.println(ex.getMessage()); + if (!CommandLine.UnmatchedArgumentException.printSuggestions(ex, err)) { + ex.getCommandLine().usage(err); + } + System.exit(cmd.getCommandSpec().exitCodeOnInvalidInput()); } - System.exit(cmd.getCommandSpec().exitCodeOnInvalidInput()); + } catch (Exception ex) { + logger.error("Command line parse error:", ex); + System.exit(cmd.getCommandSpec().exitCodeOnExecutionException()); } - } catch (Exception ex) { - logger.error("Command line parse error:", ex); - System.exit(cmd.getCommandSpec().exitCodeOnExecutionException()); - } - printDirectoryPaths(); + printDirectoryPaths(); - if (!isDebuggerAttached()) { - splashScreen = Optional.of(new SplashScreen()); - } - else { - logger.warn("Debugger detected -> Splash screen disabled..."); - } - splashScreen.ifPresent(SplashScreen::show); + if (!isDebuggerAttached()) { + splashScreen = Optional.of(new SplashScreen()); + } else { + logger.warn("Debugger detected -> Splash screen disabled..."); + } + splashScreen.ifPresent(SplashScreen::show); + + migrateOldConfigSettings(); - migrateOldConfigSettings(); + loadConfigurationData(); - loadConfigurationData(); + migrateSeenHistory(); + Daten.getInstance().launchHistoryDataLoading(); - migrateSeenHistory(); - Daten.getInstance().launchHistoryDataLoading(); - - Daten.getInstance().loadBookMarkData(); + Daten.getInstance().loadBookMarkData(); - if (SystemUtils.IS_OS_LINUX) - changeGlobalFontSize(); + if (SystemUtils.IS_OS_LINUX) + changeGlobalFontSize(); - startGuiMode(); + startGuiMode(); + }); } /** * Checks if the application has an debugger attached to it. + * * @return true if debugger was detected, false othewise. */ private static boolean isDebuggerAttached() { @@ -440,8 +437,7 @@ private static void changeGlobalFontSize() { logger.info("Custom font size found, changing global UI settings"); SwingUIFontChanger fc = new SwingUIFontChanger(); fc.changeFontSize(size); - } - catch (Exception e) { + } catch (Exception e) { logger.info("No custom font size found."); } } @@ -454,18 +450,17 @@ private static void migrateSeenHistory() { if (migrator.needsMigration()) { migrator.migrate(); } - } - catch (Exception e) { + } catch (Exception e) { logger.error("migrateSeenHistory", e); splashScreen.ifPresent(SplashScreen::close); FXErrorDialog.showErrorDialogWithoutParent(Konstanten.PROGRAMMNAME, - "Migration fehlgeschlagen", - """ - Bei der Migration der Historie der Filme ist ein Fehler aufgetreten. - Das Programm kann nicht fortfahren und wird beendet. - - Bitte überprüfen Sie die Fehlermeldung und suchen Sie Hilfe im Forum. - """, e); + "Migration fehlgeschlagen", + """ + Bei der Migration der Historie der Filme ist ein Fehler aufgetreten. + Das Programm kann nicht fortfahren und wird beendet. + + Bitte überprüfen Sie die Fehlermeldung und suchen Sie Hilfe im Forum. + """, e); System.exit(99); } } @@ -487,8 +482,7 @@ private static void loadConfigurationData() { Main.splashScreen.ifPresent(SplashScreen::close); var dialog = new DialogStarteinstellungen(null); - if (dialog.showDialog() == DialogStarteinstellungen.ResultCode.CANCELLED) - { + if (dialog.showDialog() == DialogStarteinstellungen.ResultCode.CANCELLED) { //show termination dialog JOptionPane.showMessageDialog(null, "Sie haben die Einrichtung des Programms abgebrochen.
" + @@ -508,8 +502,7 @@ private static void deleteSettingsDirectory() { .map(Path::toFile) //.peek(System.out::println) .forEach(File::delete); - } - catch (Exception ex) { + } catch (Exception ex) { logger.error("Got an error deleting settings directory", ex); } } @@ -555,39 +548,36 @@ private static void checkMemoryRequirements() { } private static void startGuiMode() { - SwingUtilities.invokeLater(() -> - { - splashScreen.ifPresent(s -> s.update(UIProgressState.INIT_FX)); - - splashScreen.ifPresent(s -> s.update(UIProgressState.FILE_CLEANUP)); - if (SystemUtils.IS_OS_MAC_OSX) { - checkForOfficialOSXAppUse(); - System.setProperty(MAC_SYSTEM_PROPERTY_APPLE_LAF_USE_SCREEN_MENU_BAR, Boolean.TRUE.toString()); - cleanupOsxFiles(); - } + splashScreen.ifPresent(s -> s.update(UIProgressState.INIT_FX)); - if (Config.isDebugModeEnabled() || Config.isInstallThreadCheckingRepaintManager()) { - // use for debugging EDT violations - RepaintManager.setCurrentManager(new ThreadCheckingRepaintManager()); - logger.info("Swing Thread checking repaint manager installed."); - } + splashScreen.ifPresent(s -> s.update(UIProgressState.FILE_CLEANUP)); + if (SystemUtils.IS_OS_MAC_OSX) { + checkForOfficialOSXAppUse(); + System.setProperty(MAC_SYSTEM_PROPERTY_APPLE_LAF_USE_SCREEN_MENU_BAR, Boolean.TRUE.toString()); + cleanupOsxFiles(); + } - splashScreen.ifPresent(s -> s.update(UIProgressState.START_UI)); - var window = getPlatformWindow(); - splashScreen.ifPresent(SplashScreen::close); - window.setVisible(true); + if (Config.isDebugModeEnabled() || Config.isInstallThreadCheckingRepaintManager()) { + // use for debugging EDT violations + RepaintManager.setCurrentManager(new ThreadCheckingRepaintManager()); + logger.info("Swing Thread checking repaint manager installed."); + } + + splashScreen.ifPresent(s -> s.update(UIProgressState.START_UI)); + var window = getPlatformWindow(); + splashScreen.ifPresent(SplashScreen::close); + window.setVisible(true); /* on windows there is a strange behaviour that the main window gets sent behind other open windows after the splash screen is closed. */ - if (SystemUtils.IS_OS_WINDOWS) { - window.toFront(); - window.requestFocus(); - } - //show a link to tutorial if we are in Austria and have never used MV before... - AustrianVlcCheck vlcCheck = new AustrianVlcCheck(); - vlcCheck.perform(); - }); + if (SystemUtils.IS_OS_WINDOWS) { + window.toFront(); + window.requestFocus(); + } + //show a link to tutorial if we are in Austria and have never used MV before... + AustrianVlcCheck vlcCheck = new AustrianVlcCheck(); + vlcCheck.perform(); } private static MediathekGui getPlatformWindow() { diff --git a/src/main/java/mediathek/daten/DownloadInfos.java b/src/main/java/mediathek/daten/DownloadInfos.java index cdfa1549b6..2946f2fcd0 100644 --- a/src/main/java/mediathek/daten/DownloadInfos.java +++ b/src/main/java/mediathek/daten/DownloadInfos.java @@ -106,8 +106,8 @@ private void handleTimerEvent(TimerEvent e) { private void makeDownloadInfos() { resetData(); - final var listeDownloads = Daten.getInstance().getListeDownloads(); - final var aktivDownloads = listeDownloads.getListOfStartsNotFinished(DatenDownload.QUELLE_ALLE); + final var aktivDownloads = Daten.getInstance() + .getListeDownloads().getListOfStartsNotFinished(DatenDownload.QUELLE_ALLE); // Liste gestarteter Downloads for (DatenDownload download : aktivDownloads) { diff --git a/src/main/java/mediathek/daten/DownloadStartInfo.java b/src/main/java/mediathek/daten/DownloadStartInfo.java index 079113dac8..575ef7645a 100644 --- a/src/main/java/mediathek/daten/DownloadStartInfo.java +++ b/src/main/java/mediathek/daten/DownloadStartInfo.java @@ -1,6 +1,10 @@ package mediathek.daten; public class DownloadStartInfo { + /** + * Size of the download list. + */ + public int total_num_download_list_entries = 0; public int total_starts = 0; public int num_abos = 0; public int num_downloads = 0; diff --git a/src/main/java/mediathek/daten/ListeDownloads.java b/src/main/java/mediathek/daten/ListeDownloads.java index dbb935c782..bb80ac4117 100644 --- a/src/main/java/mediathek/daten/ListeDownloads.java +++ b/src/main/java/mediathek/daten/ListeDownloads.java @@ -41,7 +41,6 @@ import java.util.*; import java.util.stream.Collectors; -@SuppressWarnings("serial") public class ListeDownloads extends LinkedList { private final Daten daten; @@ -415,6 +414,7 @@ public synchronized void listeNummerieren() { public synchronized DownloadStartInfo getStarts() { final DownloadStartInfo info = new DownloadStartInfo(); + info.total_num_download_list_entries = size(); for (DatenDownload download : this) { if (!download.istZurueckgestellt()) { diff --git a/src/main/java/mediathek/daten/ListeFilme.java b/src/main/java/mediathek/daten/ListeFilme.java index 843c24c190..3b40dc4274 100644 --- a/src/main/java/mediathek/daten/ListeFilme.java +++ b/src/main/java/mediathek/daten/ListeFilme.java @@ -4,6 +4,8 @@ import mediathek.tool.GermanStringSorter; import org.jetbrains.annotations.NotNull; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -12,13 +14,24 @@ public class ListeFilme extends ArrayList { public static final String FILMLISTE = "Filmliste"; - private final FilmListMetaData metaData = new FilmListMetaData(); + private static final String PCS_METADATA = "metaData"; + private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); public boolean neueFilme; + private FilmListMetaData metaData = new FilmListMetaData(); - public FilmListMetaData metaData() { + public FilmListMetaData getMetaData() { return metaData; } + public void setMetaData(FilmListMetaData meta) { + var oldValue = metaData; + metaData = meta; + this.pcs.firePropertyChange(PCS_METADATA, oldValue, metaData); + } + + public void addMetaDataChangeListener(PropertyChangeListener listener) { + this.pcs.addPropertyChangeListener(PCS_METADATA, listener); + } /** * Search all themas within list based on sender. * If sender is empty, return full list of themas. @@ -60,11 +73,6 @@ public synchronized void clear() { neueFilme = false; } - public synchronized void setMetaData(FilmListMetaData meta) { - metaData.setDatum(meta.getDatum()); - metaData.setId(meta.getId()); - } - /** * Find movie with given url and sendername * @param url String wiht URL @@ -100,7 +108,7 @@ public synchronized DatenFilm getFilmByUrl_klein_hoch_hd(String url) { * @return true if we need an update. */ public boolean needsUpdate() { - return (isEmpty()) || (metaData().isOlderThan(Konstanten.ALTER_FILMLISTE_SEKUNDEN_FUER_AUTOUPDATE)); + return (isEmpty()) || (getMetaData().isOlderThan(Konstanten.ALTER_FILMLISTE_SEKUNDEN_FUER_AUTOUPDATE)); } public synchronized long countNewFilms() { diff --git a/src/main/java/mediathek/daten/blacklist/ListeBlacklist.java b/src/main/java/mediathek/daten/blacklist/ListeBlacklist.java index a66a92fb7e..a8fc3b3dcb 100644 --- a/src/main/java/mediathek/daten/blacklist/ListeBlacklist.java +++ b/src/main/java/mediathek/daten/blacklist/ListeBlacklist.java @@ -102,7 +102,7 @@ public synchronized void filterListe() { loadCurrentFilterSettings(); if (completeFilmList != null && !completeFilmList.isEmpty()) { // Check if there are any movies - filteredList.setMetaData(completeFilmList.metaData()); + filteredList.setMetaData(completeFilmList.getMetaData()); this.parallelStream().forEach(entry -> { entry.convertToLowerCase(); diff --git a/src/main/java/mediathek/filmlisten/FilmeImportierenAutoThread.java b/src/main/java/mediathek/filmlisten/FilmeImportierenAutoThread.java index 35110935fc..c97be8cca5 100644 --- a/src/main/java/mediathek/filmlisten/FilmeImportierenAutoThread.java +++ b/src/main/java/mediathek/filmlisten/FilmeImportierenAutoThread.java @@ -27,7 +27,7 @@ public FilmeImportierenAutoThread(ListeFilme listeFilme, ListeFilme listeFilmeDi @Override public void run() { boolean ret; - if (listeFilme.isEmpty() || !listeFilme.metaData().canUseDiffList()) { + if (listeFilme.isEmpty() || !listeFilme.getMetaData().canUseDiffList()) { // dann eine komplette Liste laden listeFilme.clear(); ret = downloadAction.performDownload(StandardLocations.getFilmListUrl(FilmListDownloadType.FULL), listeFilme, days); diff --git a/src/main/java/mediathek/filmlisten/FilmeLaden.java b/src/main/java/mediathek/filmlisten/FilmeLaden.java index 57d5fb4a18..f69903b126 100644 --- a/src/main/java/mediathek/filmlisten/FilmeLaden.java +++ b/src/main/java/mediathek/filmlisten/FilmeLaden.java @@ -9,15 +9,12 @@ import mediathek.filmeSuchen.ListenerFilmeLaden; import mediathek.filmeSuchen.ListenerFilmeLadenEvent; import mediathek.filmlisten.reader.FilmListReader; -import mediathek.gui.actions.FilmListWriteWorkerTask; -import mediathek.javafx.FilmListFilterTask; -import mediathek.javafx.tool.FXProgressPane; -import mediathek.javafx.tool.JavaFxUtils; +import mediathek.gui.messages.FilmListReadStopEvent; +import mediathek.gui.tasks.BlacklistFilterWorker; +import mediathek.gui.tasks.FilmlistWriterWorker; +import mediathek.gui.tasks.RefreshAboWorker; import mediathek.mainwindow.MediathekGui; -import mediathek.tool.ApplicationConfiguration; -import mediathek.tool.FilmListUpdateType; -import mediathek.tool.GuiFunktionen; -import mediathek.tool.SwingErrorDialog; +import mediathek.tool.*; import mediathek.tool.http.MVHttpClient; import okhttp3.HttpUrl; import okhttp3.Request; @@ -29,6 +26,7 @@ import javax.swing.*; import javax.swing.event.EventListenerList; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; import java.net.UnknownHostException; import java.time.Instant; import java.time.LocalDateTime; @@ -94,7 +92,7 @@ private boolean hasNewRemoteFilmlist() { boolean result = false; logger.trace("hasNewRemoteFilmList()"); - final String id = Daten.getInstance().getListeFilme().metaData().getId(); + final String id = Daten.getInstance().getListeFilme().getMetaData().getId(); boolean showDialogs = GuiFunktionen.getFilmListUpdateType() != FilmListUpdateType.AUTOMATIC; HttpUrl filmListUrl = Konstanten.ROUTER_BASE_URL.resolve("filmliste.id"); @@ -160,7 +158,7 @@ private boolean performUpdateCheck(ListeFilme listeFilme, String dateiUrl) { //or somebody put a web adress into the text field if (dateiUrl.isEmpty() || dateiUrl.startsWith("http")) { //perform check only if we don´t want to use DIFF list... - if (!listeFilme.metaData().canUseDiffList()) { + if (!listeFilme.getMetaData().canUseDiffList()) { if (!hasNewRemoteFilmlist()) result = false; } @@ -185,7 +183,7 @@ public boolean loadFilmlist(String dateiUrl, boolean immerNeuLaden) { logger.trace("loadFilmlist(String,boolean)"); logger.info(""); - logger.info("Alte Liste erstellt am: {}", listeFilme.metaData().getGenerationDateTimeAsString()); + logger.info("Alte Liste erstellt am: {}", listeFilme.getMetaData().getGenerationDateTimeAsString()); logger.info(" Anzahl Filme: {}", listeFilme.size()); logger.info(" Anzahl Neue: {}", listeFilme.countNewFilms()); if (!istAmLaufen) { @@ -222,7 +220,7 @@ public void updateFilmlist(String dateiUrl) { // erhalten) UND auch gleich im Konfig-Ordner gespeichert logger.debug("Filme laden (Update), start"); logger.info(""); - logger.info("Alte Liste erstellt am: {}", daten.getListeFilme().metaData().getGenerationDateTimeAsString()); + logger.info("Alte Liste erstellt am: {}", daten.getListeFilme().getMetaData().getGenerationDateTimeAsString()); logger.info(" Anzahl Filme: {}", daten.getListeFilme().size()); logger.info(" Anzahl Neue: {}", daten.getListeFilme().countNewFilms()); if (!istAmLaufen) { @@ -258,16 +256,16 @@ private void undEnde(ListenerFilmeLadenEvent event) { // wenn nur ein Update if (!diffListe.isEmpty()) { logger.info("Liste Diff gelesen am: {}", readDate); - logger.info(" Liste Diff erstellt am: {}", diffListe.metaData().getGenerationDateTimeAsString()); + logger.info(" Liste Diff erstellt am: {}", diffListe.getMetaData().getGenerationDateTimeAsString()); logger.info(" Anzahl Filme: {}", diffListe.size()); listeFilme.updateFromFilmList(diffListe); - listeFilme.setMetaData(diffListe.metaData()); + listeFilme.setMetaData(diffListe.getMetaData()); Collections.sort(listeFilme); diffListe.clear(); } else { logger.info("Liste Kompl. gelesen am: {}", readDate); - logger.info(" Liste Kompl erstellt am: {}", listeFilme.metaData().getGenerationDateTimeAsString()); + logger.info(" Liste Kompl erstellt am: {}", listeFilme.getMetaData().getGenerationDateTimeAsString()); logger.info(" Anzahl Filme: {}", listeFilme.size()); } @@ -300,35 +298,34 @@ private void undEnde(ListenerFilmeLadenEvent event) { } logger.info(""); - logger.info("Jetzige Liste erstellt am: {}", listeFilme.metaData().getGenerationDateTimeAsString()); + logger.info("Jetzige Liste erstellt am: {}", listeFilme.getMetaData().getGenerationDateTimeAsString()); logger.info(" Anzahl Filme: {}", listeFilme.size()); logger.info(" Anzahl Neue: {}", listeFilme.countNewFilms()); logger.info(""); - JavaFxUtils.invokeInFxThreadAndWait(() -> { - FXProgressPane hb = new FXProgressPane(); + MessageBus.getMessageBus().publishAsync(new FilmListReadStopEvent()); + JLabel progLabel = MediathekGui.ui().progressLabel; + JProgressBar progressBar = MediathekGui.ui().progressBar; - FilmListFilterTask task = new FilmListFilterTask(true); - task.setOnRunning(e -> { - ui.getStatusBarController().getStatusBar().getRightItems().add(hb); - hb.lb.textProperty().bind(task.messageProperty()); - hb.prog.progressProperty().bind(task.progressProperty()); + try { + SwingUtilities.invokeAndWait(() -> { + ui.swingStatusBar.getStatusBar().add(progLabel); + ui.swingStatusBar.getStatusBar().add(progressBar); }); + } catch (InterruptedException | InvocationTargetException e) { + throw new RuntimeException(e); + } + var workerTask = CompletableFuture.runAsync(new RefreshAboWorker(progLabel, progressBar)) + .thenRun(new BlacklistFilterWorker(progLabel, progressBar)) + .thenRun(() -> SwingUtilities.invokeLater(() -> Daten.getInstance().getFilmeLaden().notifyFertig(new ListenerFilmeLadenEvent("", "", 100, 100, false)))); - CompletableFuture workerTask = CompletableFuture.runAsync(task); - if (writeFilmList) { - FilmListWriteWorkerTask writerTask = new FilmListWriteWorkerTask(Daten.getInstance()); - writerTask.setOnRunning(e -> { - hb.lb.textProperty().bind(writerTask.messageProperty()); - hb.prog.progressProperty().bind(writerTask.progressProperty()); - }); - workerTask = workerTask.thenRun(writerTask); - } - - workerTask.thenRun(() -> JavaFxUtils.invokeInFxThreadAndWait(() -> { - ui.getStatusBarController().getStatusBar().getRightItems().remove(hb); - })); - }); + if (writeFilmList) { + workerTask = workerTask.thenRun(new FilmlistWriterWorker(progLabel, progressBar)); + } + workerTask.thenRun(() -> SwingUtilities.invokeLater(() -> { + ui.swingStatusBar.getStatusBar().remove(progLabel); + ui.swingStatusBar.getStatusBar().remove(progressBar); + })); } private void fillHash(ListeFilme listeFilme) { diff --git a/src/main/java/mediathek/filmlisten/reader/FilmListReader.java b/src/main/java/mediathek/filmlisten/reader/FilmListReader.java index 5e95f8ce2c..cc0bd747e7 100644 --- a/src/main/java/mediathek/filmlisten/reader/FilmListReader.java +++ b/src/main/java/mediathek/filmlisten/reader/FilmListReader.java @@ -163,7 +163,7 @@ private void parseMetaData(JsonParser jp, ListeFilme listeFilme) throws IOExcept break; } if (jp.isExpectedStartArrayToken()) { - var meta = listeFilme.metaData(); + var meta = listeFilme.getMetaData(); jp.nextTextValue(); meta.setDatum(jp.nextTextValue()); jp.nextTextValue(); @@ -492,7 +492,7 @@ private void notifyProgress(String url, int iProgress) { private void notifyFertig(String url, ListeFilme liste) { logger.info("Liste Filme gelesen am: {}", DateTimeFormatter.ofPattern("dd.MM.yyyy, HH:mm") .format(LocalDateTime.ofInstant(Instant.now(), ZoneId.systemDefault()))); - logger.info(" erstellt am: {}", liste.metaData().getGenerationDateTimeAsString()); + logger.info(" erstellt am: {}", liste.getMetaData().getGenerationDateTimeAsString()); logger.info(" Anzahl Filme: {}", liste.size()); for (ListenerFilmeLaden l : listeners.getListeners(ListenerFilmeLaden.class)) { progressEvent.senderUrl = url; diff --git a/src/main/java/mediathek/filmlisten/writer/FilmListWriter.java b/src/main/java/mediathek/filmlisten/writer/FilmListWriter.java index 8bcd8960cb..18adc5d93c 100644 --- a/src/main/java/mediathek/filmlisten/writer/FilmListWriter.java +++ b/src/main/java/mediathek/filmlisten/writer/FilmListWriter.java @@ -57,7 +57,7 @@ private void checkOsxCacheDirectory() { } private void writeFormatHeader(JsonGenerator jg, ListeFilme listeFilme) throws IOException { - final var meta = listeFilme.metaData(); + final var meta = listeFilme.getMetaData(); jg.writeArrayFieldStart(ListeFilme.FILMLISTE); jg.writeString(""); //ListeFilme.FILMLISTE_DATUM_NR unused in newer versions diff --git a/src/main/java/mediathek/gui/MVTray.java b/src/main/java/mediathek/gui/MVTray.java index 6bba5e5a10..6b7ed95f2c 100644 --- a/src/main/java/mediathek/gui/MVTray.java +++ b/src/main/java/mediathek/gui/MVTray.java @@ -136,7 +136,7 @@ public void mouseClicked(MouseEvent e) { private String getTextInfos() { String strText = ""; - strText += "Filmliste erstellt: " + daten.getListeFilme().metaData().getGenerationDateTimeAsString() + " Uhr "; + strText += "Filmliste erstellt: " + daten.getListeFilme().getMetaData().getGenerationDateTimeAsString() + " Uhr "; strText += "\n"; strText += "Anz. Filme: " + daten.getListeFilme().size(); strText += "\n"; diff --git a/src/main/java/mediathek/gui/actions/FilmListExportAction.java b/src/main/java/mediathek/gui/actions/FilmListExportAction.java new file mode 100644 index 0000000000..bed00837dc --- /dev/null +++ b/src/main/java/mediathek/gui/actions/FilmListExportAction.java @@ -0,0 +1,60 @@ +package mediathek.gui.actions; + +import mediathek.config.Daten; +import mediathek.config.Konstanten; +import mediathek.filmlisten.writer.FilmListWriter; +import mediathek.mainwindow.MediathekGui; +import mediathek.tool.FileDialogs; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +/** + * Exports the current film list to JSON file. + */ +public class FilmListExportAction extends AbstractAction { + + public FilmListExportAction() { + putValue(NAME, "Lesbare Filmliste..."); + } + + private void showError() { + JOptionPane.showMessageDialog(MediathekGui.ui(), + "Es gab einen Fehler beim Export der Filmliste.", + Konstanten.PROGRAMMNAME, + JOptionPane.ERROR_MESSAGE); + } + + private void showSuccess() { + JOptionPane.showMessageDialog(MediathekGui.ui(), + "Der Export wurde erfolgreich abgeschlossen.", + Konstanten.PROGRAMMNAME, + JOptionPane.INFORMATION_MESSAGE); + } + + @Override + public void actionPerformed(ActionEvent e) { + setEnabled(false); + + var selectedFile = FileDialogs.chooseSaveFileLocation(MediathekGui.ui(), "Lesbare Filmliste sichern", ""); + if (selectedFile != null) { + try { + FilmListWriter writer = new FilmListWriter(true); + // do not "compress" the sender tag + writer.setCompressSenderTag(false); + writer.setCompressThemaTag(false); + writer.setDecompressUrls(true); + writer.writeFilmList(selectedFile.getAbsolutePath(), + Daten.getInstance().getListeFilme(), + prog -> { + }); + showSuccess(); + } catch (Exception ex) { + showError(); + } + } + + setEnabled(true); + } + +} diff --git a/src/main/java/mediathek/gui/actions/FilmListWriteWorkerTask.java b/src/main/java/mediathek/gui/actions/FilmListWriteWorkerTask.java deleted file mode 100644 index c69679963f..0000000000 --- a/src/main/java/mediathek/gui/actions/FilmListWriteWorkerTask.java +++ /dev/null @@ -1,28 +0,0 @@ -package mediathek.gui.actions; - -import javafx.concurrent.Task; -import mediathek.config.Daten; -import mediathek.config.StandardLocations; -import mediathek.filmlisten.writer.FilmListWriter; - -public class FilmListWriteWorkerTask extends Task { - - private final Daten daten; - - public FilmListWriteWorkerTask(Daten daten) { - super(); - this.daten = daten; - } - - @Override - protected Void call() { - FilmListWriter writer = new FilmListWriter(false); - updateMessage("Schreibe Filmliste"); - updateProgress(0d, 1d); - writer.writeFilmList(StandardLocations.getFilmlistFilePath(), - daten.getListeFilme(), - prog -> updateProgress(prog, 1d)); - - return null; - } -} diff --git a/src/main/java/mediathek/gui/actions/MemoryMonitorAction.java b/src/main/java/mediathek/gui/actions/MemoryMonitorAction.java index efdc16f808..03346c4fde 100644 --- a/src/main/java/mediathek/gui/actions/MemoryMonitorAction.java +++ b/src/main/java/mediathek/gui/actions/MemoryMonitorAction.java @@ -1,35 +1,49 @@ package mediathek.gui.actions; -import mediathek.javafx.MemoryMonitor; -import mediathek.javafx.tool.JavaFxUtils; +import mediathek.mainwindow.MemoryUsagePanel; +import org.jetbrains.annotations.NotNull; import javax.swing.*; +import java.awt.*; import java.awt.event.ActionEvent; +import java.util.concurrent.TimeUnit; public class MemoryMonitorAction extends AbstractAction { - private MemoryMonitor memoryMonitor; + private MemoryMonitorDialog dialog; + private final JFrame parent; - public MemoryMonitorAction() { - putValue(Action.NAME,"Speicherverbrauch anzeigen"); + public MemoryMonitorAction(@NotNull JFrame parent) { + this.parent = parent; + putValue(Action.NAME, "Speicherverbrauch anzeigen"); } public void closeMemoryMonitor() { - if (memoryMonitor != null) - JavaFxUtils.invokeInFxThreadAndWait(() -> memoryMonitor.close()); + if (dialog != null) { + dialog.dispose(); + } } public void showMemoryMonitor() { - JavaFxUtils.invokeInFxThreadAndWait(() -> { - if (memoryMonitor == null) { - memoryMonitor = new MemoryMonitor(); - } - - memoryMonitor.show(); - }); + if (dialog == null) + dialog = new MemoryMonitorDialog(parent); + dialog.setVisible(true); } @Override public void actionPerformed(ActionEvent e) { showMemoryMonitor(); } + + static class MemoryMonitorDialog extends JDialog { + public MemoryMonitorDialog(@NotNull JFrame parent) { + super(parent, "Speicherverbrauch", false); + setType(Type.UTILITY); + + MemoryUsagePanel panel = new MemoryUsagePanel(2, TimeUnit.MINUTES); + panel.setPreferredSize(new Dimension(480, 240)); + getContentPane().add(panel, BorderLayout.CENTER); + pack(); + panel.new MemoryUsageDataGenerator(1, TimeUnit.SECONDS).start(); + } + } } diff --git a/src/main/java/mediathek/gui/actions/export/FilmListExportAction.java b/src/main/java/mediathek/gui/actions/export/FilmListExportAction.java deleted file mode 100644 index fc17d80fc6..0000000000 --- a/src/main/java/mediathek/gui/actions/export/FilmListExportAction.java +++ /dev/null @@ -1,75 +0,0 @@ -package mediathek.gui.actions.export; - -import mediathek.config.Konstanten; -import mediathek.javafx.tool.FXProgressPane; -import mediathek.javafx.tool.JavaFxUtils; -import mediathek.mainwindow.MediathekGui; -import mediathek.tool.FileDialogs; -import org.controlsfx.control.StatusBar; - -import javax.swing.*; -import java.awt.event.ActionEvent; -import java.io.File; -import java.util.concurrent.CompletableFuture; - -/** - * Exports the current film list to JSON file. - */ -public class FilmListExportAction extends AbstractAction { - private final static String HEADER = "Export der Filmliste"; - private final MediathekGui gui; - - public FilmListExportAction(MediathekGui gui) { - super(); - this.gui = gui; - - putValue(NAME, "Lesbare Filmliste..."); - } - - private void export(File selectedFile) { - StatusBar bar = gui.getStatusBarController().getStatusBar(); - FXProgressPane hb = new FXProgressPane(); - - FilmListExportWorkerTask task = new FilmListExportWorkerTask(selectedFile, true); - task.setOnSucceeded(e -> { - bar.getRightItems().remove(hb); - showSuccess(); - }); - task.setOnFailed(e -> { - bar.getRightItems().remove(hb); - showError(); - }); - - bar.getRightItems().add(hb); - hb.prog.progressProperty().bind(task.progressProperty()); - - CompletableFuture.runAsync(task); - } - - private void showError() { - SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(MediathekGui.ui(), - "Es gab einen Fehler beim Export der Filmliste.", - Konstanten.PROGRAMMNAME, - JOptionPane.ERROR_MESSAGE)); - } - - private void showSuccess() { - SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(MediathekGui.ui(), - "Der Export wurde erfolgreich abgeschlossen.", - Konstanten.PROGRAMMNAME, - JOptionPane.INFORMATION_MESSAGE)); - } - - @Override - public void actionPerformed(ActionEvent e) { - setEnabled(false); - - var selectedFile = FileDialogs.chooseSaveFileLocation(gui,"Lesbare Filmliste sichern",""); - if (selectedFile != null) { - JavaFxUtils.invokeInFxThreadAndWait(() -> export(selectedFile)); - } - - setEnabled(true); - } - -} diff --git a/src/main/java/mediathek/gui/actions/export/FilmListExportWorkerTask.java b/src/main/java/mediathek/gui/actions/export/FilmListExportWorkerTask.java deleted file mode 100644 index 68e2122843..0000000000 --- a/src/main/java/mediathek/gui/actions/export/FilmListExportWorkerTask.java +++ /dev/null @@ -1,34 +0,0 @@ -package mediathek.gui.actions.export; - -import javafx.concurrent.Task; -import mediathek.config.Daten; -import mediathek.filmlisten.writer.FilmListWriter; - -import java.io.File; - -/** - * JavaFX worker task which will export the filmlist and handle progress reporting. - */ -class FilmListExportWorkerTask extends Task { - private final File selectedFile; - private final boolean readable; - - public FilmListExportWorkerTask(File selectedFile, boolean readable) { - super(); - this.selectedFile = selectedFile; - this.readable = readable; - } - - @Override - protected Void call() { - FilmListWriter writer = new FilmListWriter(readable); - // do not "compress" the sender tag - writer.setCompressSenderTag(false); - writer.setCompressThemaTag(false); - writer.setDecompressUrls(true); - writer.writeFilmList(selectedFile.getAbsolutePath(), - Daten.getInstance().getListeFilme(), - prog -> updateProgress(prog, 1d)); - return null; - } -} diff --git a/src/main/java/mediathek/gui/tabs/AGuiTabPanel.java b/src/main/java/mediathek/gui/tabs/AGuiTabPanel.java index bfbaca379d..de661f6428 100644 --- a/src/main/java/mediathek/gui/tabs/AGuiTabPanel.java +++ b/src/main/java/mediathek/gui/tabs/AGuiTabPanel.java @@ -26,7 +26,6 @@ public abstract class AGuiTabPanel extends JPanel { protected abstract Optional getCurrentlySelectedFilm(); public abstract void installMenuEntries(JMenu menu); - protected abstract void installTabInfoStatusBarControl(); public class MarkFilmAsSeenAction extends AbstractAction { diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/AboLabel.java b/src/main/java/mediathek/gui/tabs/tab_downloads/AboLabel.java new file mode 100644 index 0000000000..3fda09dade --- /dev/null +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/AboLabel.java @@ -0,0 +1,18 @@ +package mediathek.gui.tabs.tab_downloads; + +import mediathek.daten.DownloadStartInfo; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +public class AboLabel extends JLabel { + public AboLabel(@NotNull DownloadStartInfoProperty startInfoProperty) { + setToolTipText("Anzahl der Abos in der Liste"); + startInfoProperty.addStartInfoChangeListener(evt -> SwingUtilities.invokeLater(() -> process((DownloadStartInfo) evt.getNewValue()))); + + } + private void process(@NotNull DownloadStartInfo info) { + String abo = (info.num_abos == 1) ? "1 Abo" : info.num_abos + " Abos"; + setText(abo); + } +} diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/ActiveDownloadsInfoLabel.java b/src/main/java/mediathek/gui/tabs/tab_downloads/ActiveDownloadsInfoLabel.java new file mode 100644 index 0000000000..09e1ef2010 --- /dev/null +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/ActiveDownloadsInfoLabel.java @@ -0,0 +1,21 @@ +package mediathek.gui.tabs.tab_downloads; + +import mediathek.daten.DownloadStartInfo; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +public class ActiveDownloadsInfoLabel extends JLabel { + public ActiveDownloadsInfoLabel(@NotNull DownloadStartInfoProperty startInfoProperty) { + setToolTipText("Anzahl der aktiven Downloads"); + startInfoProperty.addStartInfoChangeListener(evt -> SwingUtilities.invokeLater(() -> process((DownloadStartInfo) evt.getNewValue()))); + } + + private void process(@NotNull DownloadStartInfo info) { + if (info.hasValues()) { + var numDownloads = (info.running == 1) ? "1 läuft" : info.running + " laufen"; + setText(numDownloads); + } else + setText("0 laufen"); + } +} diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/DownloadStartInfoProperty.java b/src/main/java/mediathek/gui/tabs/tab_downloads/DownloadStartInfoProperty.java new file mode 100644 index 0000000000..d546cb141b --- /dev/null +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/DownloadStartInfoProperty.java @@ -0,0 +1,46 @@ +package mediathek.gui.tabs.tab_downloads; + +import mediathek.config.Daten; +import mediathek.daten.DownloadStartInfo; +import mediathek.gui.messages.TimerEvent; +import mediathek.gui.messages.UpdateStatusBarLeftDisplayEvent; +import mediathek.tool.MessageBus; +import net.engio.mbassy.listener.Handler; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; + +public class DownloadStartInfoProperty { + private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); + private DownloadStartInfo info; + + public DownloadStartInfoProperty() { + setInfo(Daten.getInstance().getListeDownloads().getStarts()); + + MessageBus.getMessageBus().subscribe(this); + } + + @Handler + private void handleLeftDisplayUpdate(UpdateStatusBarLeftDisplayEvent e) { + setInfo(Daten.getInstance().getListeDownloads().getStarts()); + } + + @Handler + private void handleTimerEvent(TimerEvent e) { + setInfo(Daten.getInstance().getListeDownloads().getStarts()); + } + + public DownloadStartInfo getInfo() { + return info; + } + + public void setInfo(DownloadStartInfo info) { + var oldValue = this.info; + this.info = info; + this.pcs.firePropertyChange("info", oldValue, this.info); + } + + public void addStartInfoChangeListener(PropertyChangeListener listener) { + this.pcs.addPropertyChangeListener(listener); + } +} diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/FailedDownloadsInfoLabel.java b/src/main/java/mediathek/gui/tabs/tab_downloads/FailedDownloadsInfoLabel.java new file mode 100644 index 0000000000..0049fb1cd4 --- /dev/null +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/FailedDownloadsInfoLabel.java @@ -0,0 +1,20 @@ +package mediathek.gui.tabs.tab_downloads; + +import mediathek.daten.DownloadStartInfo; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +public class FailedDownloadsInfoLabel extends JLabel { + public FailedDownloadsInfoLabel(@NotNull DownloadStartInfoProperty startInfoProperty) { + setToolTipText("Anzahl der fehlerhaften Downloads"); + startInfoProperty.addStartInfoChangeListener(evt -> SwingUtilities.invokeLater(() -> process((DownloadStartInfo) evt.getNewValue()))); + } + + private void process(@NotNull DownloadStartInfo info) { + if (info.hasValues()) { + setText(info.error + " fehlerhaft"); + } else + setText("0 fehlerhaft"); + } +} diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/FinishedDownloadsInfoLabel.java b/src/main/java/mediathek/gui/tabs/tab_downloads/FinishedDownloadsInfoLabel.java new file mode 100644 index 0000000000..715bba102f --- /dev/null +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/FinishedDownloadsInfoLabel.java @@ -0,0 +1,21 @@ +package mediathek.gui.tabs.tab_downloads; + +import mediathek.daten.DownloadStartInfo; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +public class FinishedDownloadsInfoLabel extends JLabel { + public FinishedDownloadsInfoLabel(@NotNull DownloadStartInfoProperty startInfoProperty) { + setToolTipText("Anzahl der abgeschlossenen Downloads"); + startInfoProperty.addStartInfoChangeListener(evt -> SwingUtilities.invokeLater(() -> process((DownloadStartInfo) evt.getNewValue()))); + } + + private void process(@NotNull DownloadStartInfo info) { + if (info.hasValues()) { + String fin = (info.finished == 1) ? "1 fertig" : info.finished + " fertig"; + setText(fin); + } else + setText("0 fertig"); + } +} diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java index dbd1b3ab4a..eca2e0e019 100644 --- a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java @@ -27,7 +27,6 @@ import mediathek.gui.messages.*; import mediathek.gui.tabs.AGuiTabPanel; import mediathek.javafx.descriptionPanel.DescriptionPanelController; -import mediathek.javafx.downloadtab.DownloadTabInformationLabel; import mediathek.mainwindow.MediathekGui; import mediathek.tool.*; import mediathek.tool.cellrenderer.CellRendererDownloads; @@ -44,6 +43,7 @@ import org.apache.commons.lang3.SystemUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.jdesktop.swingx.JXStatusBar; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -92,6 +92,15 @@ public class GuiDownloads extends AGuiTabPanel { private final Configuration config = ApplicationConfiguration.getConfiguration(); private final MarkFilmAsSeenAction markFilmAsSeenAction = new MarkFilmAsSeenAction(); private final MarkFilmAsUnseenAction markFilmAsUnseenAction = new MarkFilmAsUnseenAction(); + private final JXStatusBar statusBar = new JXStatusBar(); + private final DownloadStartInfoProperty startInfoProperty = new DownloadStartInfoProperty(); + private final AboLabel lblAbos = new AboLabel(startInfoProperty); + private final TotalDownloadsLabel totalDownloadsLabel = new TotalDownloadsLabel(startInfoProperty); + private final ManualDownloadsInfoLabel manualDownloadsInfoLabel = new ManualDownloadsInfoLabel(startInfoProperty); + private final WaitingDownloadsInfoLabel waitingDownloadsInfoLabel = new WaitingDownloadsInfoLabel(startInfoProperty); + private final ActiveDownloadsInfoLabel activeDownloadsInfoLabel = new ActiveDownloadsInfoLabel(startInfoProperty); + private final FinishedDownloadsInfoLabel finishedDownloadsInfoLabel = new FinishedDownloadsInfoLabel(startInfoProperty); + private final FailedDownloadsInfoLabel failedDownloadsInfoLabel = new FailedDownloadsInfoLabel(startInfoProperty); protected StartAllDownloadsAction startAllDownloadsAction = new StartAllDownloadsAction(this); protected StartAllDownloadsTimedAction startAllDownloadsTimedAction = new StartAllDownloadsTimedAction(this); protected StopAllDownloadsAction stopAllDownloadsAction = new StopAllDownloadsAction(this); @@ -123,7 +132,6 @@ public class GuiDownloads extends AGuiTabPanel { * The internally used model. */ private TModelDownload model; - private DownloadTabInformationLabel filmInfoLabel; private MVDownloadsTable tabelle; // Variables declaration - do not modify//GEN-BEGIN:variables // Generated using JFormDesigner non-commercial license @@ -144,6 +152,7 @@ public GuiDownloads(Daten aDaten, MediathekGui mediathekGui) { this.mediathekGui = mediathekGui; initComponents(); + setupDownloadListStatusBar(); setupF4Key(mediathekGui); @@ -154,8 +163,6 @@ public GuiDownloads(Daten aDaten, MediathekGui mediathekGui) { init(); - installTabInfoStatusBarControl(); - setupFilmSelectionPropertyListener(mediathekGui); initTable(); @@ -175,6 +182,16 @@ public GuiDownloads(Daten aDaten, MediathekGui mediathekGui) { setupTaskbarMenu(); } + private void setupDownloadListStatusBar() { + statusBar.add(totalDownloadsLabel); + statusBar.add(lblAbos); + statusBar.add(manualDownloadsInfoLabel); + statusBar.add(activeDownloadsInfoLabel); + statusBar.add(waitingDownloadsInfoLabel); + statusBar.add(finishedDownloadsInfoLabel); + statusBar.add(failedDownloadsInfoLabel); + } + @Override public void tabelleSpeichern() { if (tabelle != null) { @@ -204,48 +221,19 @@ private void setupFilmSelectionPropertyListener(MediathekGui mediathekGui) { tabelle.getSelectionModel().addListSelectionListener(e -> { if (!e.getValueIsAdjusting()) { final int sel = tabelle.getSelectedRowCount(); - Platform.runLater(() -> mediathekGui.getSelectedItemsProperty().setValue(sel)); + mediathekGui.selectedListItemsProperty.setSelectedItems(sel); } }); addComponentListener(new ComponentAdapter() { @Override public void componentShown(ComponentEvent e) { final int sel = tabelle.getSelectedRowCount(); - Platform.runLater(() -> mediathekGui.getSelectedItemsProperty().setValue(sel)); + mediathekGui.selectedListItemsProperty.setSelectedItems(sel); onComponentShown(); } }); } - @Override - protected void installTabInfoStatusBarControl() { - final var leftItems = mediathekGui.getStatusBarController().getStatusBar().getLeftItems(); - - Platform.runLater(() -> { - filmInfoLabel = new DownloadTabInformationLabel(daten); - if (isVisible()) - leftItems.add(filmInfoLabel); - }); - - addComponentListener(new ComponentAdapter() { - @Override - public void componentShown(ComponentEvent e) { - Platform.runLater(() -> { - filmInfoLabel.setVisible(true); - leftItems.add(filmInfoLabel); - }); - } - - @Override - public void componentHidden(ComponentEvent e) { - Platform.runLater(() -> { - filmInfoLabel.setVisible(false); - leftItems.remove(filmInfoLabel); - }); - } - }); - } - private void setupDownloadListTable() { tabelle = new MVDownloadsTable(); downloadListScrollPane.setViewportView(tabelle); @@ -1301,7 +1289,11 @@ private void initComponents() { //======== downloadListArea ======== { downloadListArea.setLayout(new BorderLayout()); - downloadListArea.add(downloadListScrollPane, BorderLayout.CENTER); + JPanel tempPanel = new JPanel(); + tempPanel.setLayout(new BorderLayout()); + tempPanel.add(downloadListScrollPane, BorderLayout.CENTER); + tempPanel.add(statusBar, BorderLayout.SOUTH); + downloadListArea.add(tempPanel, BorderLayout.CENTER); downloadListArea.add(fxDescriptionPanel, BorderLayout.SOUTH); } jSplitPane1.setRightComponent(downloadListArea); diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.jfd b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.jfd deleted file mode 100644 index b30569b8f3..0000000000 --- a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.jfd +++ /dev/null @@ -1,177 +0,0 @@ -JFDML JFormDesigner: "7.0.2.0.298" Java: "11.0.7" encoding: "UTF-8" - -new FormModel { - contentType: "form/swing" - root: new FormRoot { - add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) { - name: "this" - add( new FormContainer( "javax.swing.JSplitPane", new FormLayoutManager( class javax.swing.JSplitPane ) ) { - name: "jSplitPane1" - "dividerLocation": 330 - add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { - "$layoutConstraints": "insets 0,hidemode 3,gap 0 0" - "$columnConstraints": "[grow,fill]" - "$rowConstraints": "[][fill][grow,fill]" - } ) { - name: "jPanelFilterExtern" - "preferredSize": new java.awt.Dimension( 200, 644 ) - add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { - "$columnConstraints": "[fill][grow,fill]" - "$rowConstraints": "[fill][fill][fill]" - "$layoutConstraints": "insets 5,hidemode 3,gap 5 5" - } ) { - name: "panel3" - "border": new javax.swing.border.TitledBorder( "Anzeige" ) - auxiliary() { - "JavaCodeGenerator.variableLocal": true - } - add( new FormComponent( "javax.swing.JLabel" ) { - name: "label1" - "text": "Typ:" - auxiliary() { - "JavaCodeGenerator.variableLocal": true - } - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 0" - } ) - add( new FormComponent( "javax.swing.JComboBox" ) { - name: "cbDisplayCategories" - auxiliary() { - "JavaCodeGenerator.typeParameters": "String" - } - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 1 0" - } ) - add( new FormComponent( "javax.swing.JLabel" ) { - name: "label2" - "text": "Status:" - auxiliary() { - "JavaCodeGenerator.variableLocal": true - } - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 1" - } ) - add( new FormComponent( "javax.swing.JComboBox" ) { - name: "cbView" - auxiliary() { - "JavaCodeGenerator.typeParameters": "String" - } - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 1 1" - } ) - add( new FormComponent( "javax.swing.JButton" ) { - name: "btnClear" - "icon": new com.jformdesigner.model.SwingIcon( 0, "/mediathek/res/muster/button-clear.png" ) - "toolTipText": "Filter zurücksetzen" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 2 2 1,alignx right,growx 0,width 32:32:32,height 32:32:32" - } ) - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 0" - } ) - add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { - "$columnConstraints": "[fill][fill]" - "$rowConstraints": "[][fill]" - "$layoutConstraints": "insets 5,hidemode 3,gap 5 5" - } ) { - name: "panel2" - "border": new javax.swing.border.TitledBorder( "Downloads" ) - auxiliary() { - "JavaCodeGenerator.variableLocal": true - } - add( new FormComponent( "javax.swing.JLabel" ) { - name: "jLabel3" - "text": "gleichzeitig:" - auxiliary() { - "JavaCodeGenerator.variableLocal": true - } - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 0" - } ) - add( new FormComponent( "javax.swing.JSpinner" ) { - name: "jSpinnerAnzahlDownloads" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 1 0" - } ) - add( new FormComponent( "javax.swing.JLabel" ) { - name: "lblBandwidth" - "text": "max. Bandbreite:" - auxiliary() { - "JavaCodeGenerator.variableLocal": true - } - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 1" - } ) - add( new FormComponent( "javax.swing.JLabel" ) { - name: "jLabel1" - "text": "KiB/s" - auxiliary() { - "JavaCodeGenerator.variableLocal": true - } - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 2 1" - } ) - add( new FormComponent( "javax.swing.JSpinner" ) { - name: "jSpinner1" - "model": new javax.swing.SpinnerNumberModel( 0, 0, 1048576, 1 ) - "toolTipText": "\nBandbreitenbegrenzung eines Downloads in XX Kilobytes pro Sekunde.\n
WICHTIG:
ENTWEDER
den Wert über die Pfeiltasten ändern
ODER
Zahlen eingeben UND ENTER-Taste drücken!
\n" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 1 1" - } ) - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 1" - } ) - add( new FormContainer( "javax.swing.JScrollPane", new FormLayoutManager( class javax.swing.JScrollPane ) ) { - name: "spDownload" - "preferredSize": new java.awt.Dimension( 14, 150 ) - auxiliary() { - "JavaCodeGenerator.variableLocal": true - } - add( new FormComponent( "javax.swing.JEditorPane" ) { - name: "txtDownload" - "editable": false - "opaque": false - "preferredSize": new java.awt.Dimension( 10, 500 ) - } ) - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 2" - } ) - }, new FormLayoutConstraints( class java.lang.String ) { - "value": "left" - } ) - add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) { - name: "downloadListArea" - auxiliary() { - "JavaCodeGenerator.variableName": "downloadListArea" - "JavaCodeGenerator.variableLocal": true - } - add( new FormContainer( "javax.swing.JScrollPane", new FormLayoutManager( class javax.swing.JScrollPane ) ) { - name: "downloadListScrollPane" - auxiliary() { - "JavaCodeGenerator.variableName": "downloadListScrollPane" - } - }, new FormLayoutConstraints( class java.lang.String ) { - "value": "Center" - } ) - add( new FormComponent( "javafx.embed.swing.JFXPanel" ) { - name: "fxDescriptionPanel" - }, new FormLayoutConstraints( class java.lang.String ) { - "value": "South" - } ) - }, new FormLayoutConstraints( class java.lang.String ) { - "value": "right" - } ) - }, new FormLayoutConstraints( class java.lang.String ) { - "value": "Center" - } ) - add( new FormComponent( "javafx.embed.swing.JFXPanel" ) { - name: "toolBarPanel" - }, new FormLayoutConstraints( class java.lang.String ) { - "value": "North" - } ) - }, new FormLayoutConstraints( null ) { - "size": new java.awt.Dimension( 640, 640 ) - "location": new java.awt.Point( 0, 0 ) - } ) - } -} diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/ManualDownloadsInfoLabel.java b/src/main/java/mediathek/gui/tabs/tab_downloads/ManualDownloadsInfoLabel.java new file mode 100644 index 0000000000..8110359ecf --- /dev/null +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/ManualDownloadsInfoLabel.java @@ -0,0 +1,18 @@ +package mediathek.gui.tabs.tab_downloads; + +import mediathek.daten.DownloadStartInfo; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +public class ManualDownloadsInfoLabel extends JLabel { + public ManualDownloadsInfoLabel(@NotNull DownloadStartInfoProperty startInfoProperty) { + setToolTipText("Anzahl der manuellen Downloads in der Downloadliste"); + startInfoProperty.addStartInfoChangeListener(evt -> SwingUtilities.invokeLater(() -> process((DownloadStartInfo) evt.getNewValue()))); + } + + private void process(@NotNull DownloadStartInfo info) { + String numDownloads = (info.num_downloads == 1) ? "1 Download" : info.num_downloads + " Downloads"; + setText(numDownloads); + } +} diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/TotalDownloadsLabel.java b/src/main/java/mediathek/gui/tabs/tab_downloads/TotalDownloadsLabel.java new file mode 100644 index 0000000000..9211ed9435 --- /dev/null +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/TotalDownloadsLabel.java @@ -0,0 +1,24 @@ +package mediathek.gui.tabs.tab_downloads; + +import mediathek.daten.DownloadStartInfo; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +public class TotalDownloadsLabel extends JLabel { + public TotalDownloadsLabel(@NotNull DownloadStartInfoProperty startInfoProperty) { + setToolTipText("Gesamtzahl aller Downloads"); + startInfoProperty.addStartInfoChangeListener(evt -> SwingUtilities.invokeLater(() -> process((DownloadStartInfo) evt.getNewValue()))); + } + + private void process(@NotNull DownloadStartInfo info) { + final int anz = info.total_num_download_list_entries; + final int diff = anz - info.total_starts; + String download = "Gesamtdownloads: " + anz; + if (diff >= 1) { + download += " (" + diff + " zurückgestellt)"; + } + setText(download); + } + +} diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/WaitingDownloadsInfoLabel.java b/src/main/java/mediathek/gui/tabs/tab_downloads/WaitingDownloadsInfoLabel.java new file mode 100644 index 0000000000..2b43fc9efc --- /dev/null +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/WaitingDownloadsInfoLabel.java @@ -0,0 +1,21 @@ +package mediathek.gui.tabs.tab_downloads; + +import mediathek.daten.DownloadStartInfo; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +public class WaitingDownloadsInfoLabel extends JLabel { + public WaitingDownloadsInfoLabel(@NotNull DownloadStartInfoProperty startInfoProperty) { + setToolTipText("Anzahl der wartenden Downloads"); + startInfoProperty.addStartInfoChangeListener(evt -> SwingUtilities.invokeLater(() -> process((DownloadStartInfo) evt.getNewValue()))); + } + + private void process(@NotNull DownloadStartInfo info) { + if (info.hasValues()) { + String waiting = (info.initialized == 1) ? "1 wartet" : info.initialized + " warten"; + setText(waiting); + } else + setText(""); + } +} diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index fc25352bcd..22ce346989 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -38,7 +38,6 @@ import mediathek.javafx.bookmark.BookmarkWindowController; import mediathek.javafx.buttonsPanel.ButtonsPanelController; import mediathek.javafx.descriptionPanel.DescriptionPanelController; -import mediathek.javafx.filmtab.FilmTabInfoPane; import mediathek.javafx.filterpanel.FXSearchControlFieldMode; import mediathek.javafx.filterpanel.FilmActionPanel; import mediathek.javafx.tool.JavaFxUtils; @@ -115,7 +114,6 @@ public class GuiFilme extends AGuiTabPanel { protected FilterVisibilityToggleButton btnToggleFilterDialogVisibility = new FilterVisibilityToggleButton(toggleFilterDialogVisibilityAction); private Optional bookmarkWindowController = Optional.empty(); private boolean stopBeob; - private FilmTabInfoPane filmInfoLabel; private JCheckBoxMenuItem cbShowButtons; /** * We need a strong reference here for message bus to work properly. Otherwise the buttons @@ -145,8 +143,6 @@ public GuiFilme(Daten aDaten, MediathekGui mediathekGui) { setupFilmListTable(); - installTabInfoStatusBarControl(); - setupFilmSelectionPropertyListener(mediathekGui); setupDescriptionPanel(); @@ -181,30 +177,26 @@ private void createToolBar() { } @Handler - private void handleTableModelChange(TableModelChangeEvent e) { + public void handleTableModelChange(TableModelChangeEvent e) { if (e.active) { - try { - SwingUtilities.invokeAndWait(() -> playFilmAction.setEnabled(false)); - SwingUtilities.invokeAndWait(() -> saveFilmAction.setEnabled(false)); - SwingUtilities.invokeAndWait(() -> bookmarkFilmAction.setEnabled(false)); - SwingUtilities.invokeAndWait(() -> toggleFilterDialogVisibilityAction.setEnabled(false)); - SwingUtilities.invokeAndWait(() -> searchField.setEnabled(false)); - SwingUtilities.invokeAndWait(() -> filterSelectionComboBox.setEnabled(false)); - } catch (InterruptedException | InvocationTargetException ex) { - throw new RuntimeException(ex); - } + SwingUtilities.invokeLater(() -> { + playFilmAction.setEnabled(false); + saveFilmAction.setEnabled(false); + bookmarkFilmAction.setEnabled(false); + toggleFilterDialogVisibilityAction.setEnabled(false); + searchField.setEnabled(false); + filterSelectionComboBox.setEnabled(false); + }); } else { - try { - SwingUtilities.invokeAndWait(() -> playFilmAction.setEnabled(true)); - SwingUtilities.invokeAndWait(() -> saveFilmAction.setEnabled(true)); - SwingUtilities.invokeAndWait(() -> bookmarkFilmAction.setEnabled(true)); - SwingUtilities.invokeAndWait(() -> toggleFilterDialogVisibilityAction.setEnabled(true)); - SwingUtilities.invokeAndWait(() -> searchField.setEnabled(true)); - SwingUtilities.invokeAndWait(() -> filterSelectionComboBox.setEnabled(true)); - } catch (InterruptedException | InvocationTargetException ex) { - throw new RuntimeException(ex); - } + SwingUtilities.invokeLater(() -> { + playFilmAction.setEnabled(true); + saveFilmAction.setEnabled(true); + bookmarkFilmAction.setEnabled(true); + toggleFilterDialogVisibilityAction.setEnabled(true); + searchField.setEnabled(true); + filterSelectionComboBox.setEnabled(true); + }); } } @@ -227,7 +219,7 @@ private void setupFilmSelectionPropertyListener(MediathekGui mediathekGui) { tabelle.getSelectionModel().addListSelectionListener(e -> { if (!e.getValueIsAdjusting()) { final int sel = tabelle.getSelectedRowCount(); - Platform.runLater(() -> mediathekGui.getSelectedItemsProperty().setValue(sel)); + mediathekGui.selectedListItemsProperty.setSelectedItems(sel); } }); @@ -235,42 +227,12 @@ private void setupFilmSelectionPropertyListener(MediathekGui mediathekGui) { @Override public void componentShown(ComponentEvent e) { final int sel = tabelle.getSelectedRowCount(); - Platform.runLater(() -> mediathekGui.getSelectedItemsProperty().setValue(sel)); + mediathekGui.selectedListItemsProperty.setSelectedItems(sel); onComponentShown(); } }); } - @Override - protected void installTabInfoStatusBarControl() { - final var leftItems = mediathekGui.getStatusBarController().getStatusBar().getLeftItems(); - - Platform.runLater(() -> { - filmInfoLabel = new FilmTabInfoPane(daten, this); - if (isVisible()) leftItems.add(filmInfoLabel); - }); - - addComponentListener(new ComponentAdapter() { - @Override - public void componentShown(ComponentEvent e) { - Platform.runLater( - () -> { - filmInfoLabel.setVisible(true); - leftItems.add(filmInfoLabel); - }); - } - - @Override - public void componentHidden(ComponentEvent e) { - Platform.runLater( - () -> { - filmInfoLabel.setVisible(false); - leftItems.remove(filmInfoLabel); - }); - } - }); - } - private void createFilmListArea() { add(filmListScrollPane, BorderLayout.CENTER); } @@ -794,7 +756,7 @@ private void loadTable() { } final var messageBus = MessageBus.getMessageBus(); - messageBus.publishAsync(new TableModelChangeEvent(true)); + messageBus.publish(new TableModelChangeEvent(true)); stopBeob = true; tabelle.getSpalten(); @@ -817,7 +779,7 @@ public void onSuccess(TableModel model) { updateFilmData(); stopBeob = false; tabelle.scrollToSelection(); - messageBus.publishAsync(new TableModelChangeEvent(false)); + messageBus.publish(new TableModelChangeEvent(false)); }); } @@ -829,7 +791,7 @@ public void onFailure(@NotNull Throwable thrown) { tabelle.setSpalten(); updateFilmData(); stopBeob = false; - messageBus.publishAsync(new TableModelChangeEvent(false)); + messageBus.publish(new TableModelChangeEvent(false)); }); } }, diff --git a/src/main/java/mediathek/gui/tasks/BlacklistFilterWorker.java b/src/main/java/mediathek/gui/tasks/BlacklistFilterWorker.java new file mode 100644 index 0000000000..313fa84dfb --- /dev/null +++ b/src/main/java/mediathek/gui/tasks/BlacklistFilterWorker.java @@ -0,0 +1,23 @@ +package mediathek.gui.tasks; + +import mediathek.config.Daten; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +public class BlacklistFilterWorker extends SwingWorker { + + public BlacklistFilterWorker(@NotNull JLabel progLabel, @NotNull JProgressBar progressBar) { + SwingUtilities.invokeLater(() -> { + progLabel.setText("Blacklist anwenden"); + progressBar.setIndeterminate(true); + }); + } + + @Override + protected Void doInBackground() { + Daten.getInstance().getListeBlacklist().filterListe(); + + return null; + } +} diff --git a/src/main/java/mediathek/gui/tasks/FilmlistWriterWorker.java b/src/main/java/mediathek/gui/tasks/FilmlistWriterWorker.java new file mode 100644 index 0000000000..584fbbd533 --- /dev/null +++ b/src/main/java/mediathek/gui/tasks/FilmlistWriterWorker.java @@ -0,0 +1,44 @@ +package mediathek.gui.tasks; + +import mediathek.config.Daten; +import mediathek.config.StandardLocations; +import mediathek.filmlisten.writer.FilmListWriter; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +public class FilmlistWriterWorker extends SwingWorker implements PropertyChangeListener { + private final JProgressBar progressBar; + + public FilmlistWriterWorker(@NotNull JLabel progLabel, @NotNull JProgressBar progressBar) { + this.progressBar = progressBar; + + addPropertyChangeListener(this); + + SwingUtilities.invokeLater(() -> { + progLabel.setText("Schreibe Filmliste"); + progressBar.setIndeterminate(false); + progressBar.setMinimum(0); + progressBar.setMaximum(100); + progressBar.setValue(0); + }); + } + + @Override + protected Void doInBackground() { + FilmListWriter writer = new FilmListWriter(false); + writer.writeFilmList(StandardLocations.getFilmlistFilePath(), + Daten.getInstance().getListeFilme(), + prog -> setProgress((int) (100.0 * prog))); + + return null; + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getPropertyName().equalsIgnoreCase("progress")) + SwingUtilities.invokeLater(() -> progressBar.setValue((int) evt.getNewValue())); + } +} diff --git a/src/main/java/mediathek/gui/tasks/RefreshAboWorker.java b/src/main/java/mediathek/gui/tasks/RefreshAboWorker.java new file mode 100644 index 0000000000..5b5288d93b --- /dev/null +++ b/src/main/java/mediathek/gui/tasks/RefreshAboWorker.java @@ -0,0 +1,24 @@ +package mediathek.gui.tasks; + +import mediathek.config.Daten; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +public class RefreshAboWorker extends SwingWorker { + + public RefreshAboWorker(@NotNull JLabel progLabel, @NotNull JProgressBar progressBar) { + SwingUtilities.invokeLater(() -> { + progLabel.setText("Abos eintragen"); + progressBar.setIndeterminate(true); + }); + } + + @Override + protected Void doInBackground() { + var daten = Daten.getInstance(); + daten.getListeAbo().setAboFuerFilm(daten.getListeFilme(), false); + + return null; + } +} diff --git a/src/main/java/mediathek/javafx/FilmListFilterTask.java b/src/main/java/mediathek/javafx/FilmListFilterTask.java deleted file mode 100644 index cac30c79a5..0000000000 --- a/src/main/java/mediathek/javafx/FilmListFilterTask.java +++ /dev/null @@ -1,43 +0,0 @@ -package mediathek.javafx; - -import javafx.concurrent.Task; -import mediathek.config.Daten; -import mediathek.filmeSuchen.ListenerFilmeLadenEvent; -import mediathek.gui.messages.FilmListReadStopEvent; -import mediathek.tool.MessageBus; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import javax.swing.*; - -public class FilmListFilterTask extends Task { - private final Daten daten = Daten.getInstance(); - private final boolean submitEvent; - private static final Logger logger = LogManager.getLogger(FilmListFilterTask.class); - - public FilmListFilterTask(boolean submitEvent) { - this.submitEvent = submitEvent; - } - - @Override - protected Void call() { - logger.trace("FilmListFilterTask started"); - - if (submitEvent) - MessageBus.getMessageBus().publishAsync(new FilmListReadStopEvent()); - - updateMessage("Abos eintragen"); - updateProgress(-1, 4); - daten.getListeAbo().setAboFuerFilm(daten.getListeFilme(), false); - - updateMessage("Alle Filter anwenden"); - updateProgress(-1, 4); - daten.getListeBlacklist().filterListe(); - - SwingUtilities.invokeLater(() -> daten.getFilmeLaden().notifyFertig(new ListenerFilmeLadenEvent("", "", 100, 100, false))); - - logger.trace("FilmListFilterTask finished"); - - return null; - } -} diff --git a/src/main/java/mediathek/javafx/FilmListNetworkReaderTask.java b/src/main/java/mediathek/javafx/FilmListNetworkReaderTask.java deleted file mode 100644 index e7c34136a0..0000000000 --- a/src/main/java/mediathek/javafx/FilmListNetworkReaderTask.java +++ /dev/null @@ -1,24 +0,0 @@ -package mediathek.javafx; - -import javafx.concurrent.Task; -import mediathek.config.Daten; -import mediathek.tool.FilmListUpdateType; -import mediathek.tool.GuiFunktionen; - -public class FilmListNetworkReaderTask extends Task { - - @Override - protected Void call() { - final Daten daten = Daten.getInstance(); - - updateProgress(-1, 4); - updateMessage("Prüfe Alter der Filmliste"); - - if (GuiFunktionen.getFilmListUpdateType() == FilmListUpdateType.AUTOMATIC && daten.getListeFilme().needsUpdate()) { - updateMessage("Lade Filmliste Netzwerk"); - daten.getFilmeLaden().loadFilmlist("", true); - } - - return null; - } -} diff --git a/src/main/java/mediathek/javafx/FilmListReaderTask.java b/src/main/java/mediathek/javafx/FilmListReaderTask.java deleted file mode 100644 index c7ca84470b..0000000000 --- a/src/main/java/mediathek/javafx/FilmListReaderTask.java +++ /dev/null @@ -1,32 +0,0 @@ -package mediathek.javafx; - -import javafx.concurrent.Task; -import mediathek.config.Daten; -import mediathek.config.StandardLocations; -import mediathek.filmlisten.reader.FilmListReader; -import mediathek.gui.messages.FilmListReadStartEvent; -import mediathek.tool.ApplicationConfiguration; -import mediathek.tool.MessageBus; - -public class FilmListReaderTask extends Task { - private final Daten daten; - - public FilmListReaderTask() { - super(); - daten = Daten.getInstance(); - } - - @Override - protected Void call() { - MessageBus.getMessageBus().publishAsync(new FilmListReadStartEvent()); - - updateProgress(-1, 4); - updateMessage("Lese lokale Filmliste"); - try (FilmListReader reader = new FilmListReader()) { - final int num_days = ApplicationConfiguration.getConfiguration().getInt(ApplicationConfiguration.FilmList.LOAD_NUM_DAYS,0); - reader.readFilmListe(StandardLocations.getFilmlistFilePath(), daten.getListeFilme(), num_days); - } - - return null; - } -} diff --git a/src/main/java/mediathek/javafx/GarbageCollectionButton.java b/src/main/java/mediathek/javafx/GarbageCollectionButton.java deleted file mode 100644 index 92f4016540..0000000000 --- a/src/main/java/mediathek/javafx/GarbageCollectionButton.java +++ /dev/null @@ -1,21 +0,0 @@ -package mediathek.javafx; - -import javafx.scene.control.Button; -import javafx.scene.control.Tooltip; -import org.controlsfx.glyphfont.FontAwesome; -import org.controlsfx.glyphfont.GlyphFont; -import org.controlsfx.glyphfont.GlyphFontRegistry; - -/** - * a JavaFX button which will simply perform the garbage collection when clicked - */ -public class GarbageCollectionButton extends Button { - private static final GlyphFont fontAwesome = GlyphFontRegistry.font("FontAwesome"); - - public GarbageCollectionButton() { - super("", fontAwesome.create(FontAwesome.Glyph.RECYCLE)); - setText(""); - setTooltip(new Tooltip("Garbage Collection durchführen")); - setOnAction(e -> System.gc()); - } -} diff --git a/src/main/java/mediathek/javafx/InfoLabel/AboLabel.java b/src/main/java/mediathek/javafx/InfoLabel/AboLabel.java deleted file mode 100644 index f481f033d2..0000000000 --- a/src/main/java/mediathek/javafx/InfoLabel/AboLabel.java +++ /dev/null @@ -1,16 +0,0 @@ -package mediathek.javafx.InfoLabel; - -import javafx.scene.control.Label; -import javafx.scene.control.Tooltip; -import mediathek.daten.DownloadStartInfo; - -public class AboLabel extends Label { - public AboLabel() { - setTooltip(new Tooltip("Anzahl der Abos in der Downloadliste")); - } - - public void updateLabel(DownloadStartInfo info) { - String abo = (info.num_abos == 1) ? "1 Abo" : info.num_abos + " Abos"; - setText(abo); - } -} diff --git a/src/main/java/mediathek/javafx/InfoLabel/ActiveDownloadsLabel.java b/src/main/java/mediathek/javafx/InfoLabel/ActiveDownloadsLabel.java deleted file mode 100644 index 2e3dcf04ae..0000000000 --- a/src/main/java/mediathek/javafx/InfoLabel/ActiveDownloadsLabel.java +++ /dev/null @@ -1,26 +0,0 @@ -package mediathek.javafx.InfoLabel; - -import javafx.scene.control.Label; -import javafx.scene.control.Tooltip; -import mediathek.config.Daten; -import mediathek.daten.DownloadStartInfo; - -public class ActiveDownloadsLabel extends Label { - public ActiveDownloadsLabel() { - setTooltip(new Tooltip("Anzahl der aktiven Downloads")); - } - - public void updateLabel(Daten daten, DownloadStartInfo info) { - String numDownloads; - - if (info.hasValues()) { - numDownloads = (info.running == 1) ? "1 läuft" : info.running + " laufen"; - - if (info.running > 0) { - numDownloads += " (" + daten.getDownloadInfos().getBandwidthStr() + ')'; - } - setText(numDownloads); - } else - setText(""); - } -} diff --git a/src/main/java/mediathek/javafx/InfoLabel/ErrorLabel.java b/src/main/java/mediathek/javafx/InfoLabel/ErrorLabel.java deleted file mode 100644 index 5c04f45940..0000000000 --- a/src/main/java/mediathek/javafx/InfoLabel/ErrorLabel.java +++ /dev/null @@ -1,21 +0,0 @@ -package mediathek.javafx.InfoLabel; - -import javafx.scene.control.Label; -import javafx.scene.control.Tooltip; -import mediathek.daten.DownloadStartInfo; - -public class ErrorLabel extends Label { - public ErrorLabel() { - setTooltip(new Tooltip("Anzahl der fehlerhaften Downloads")); - } - - public void updateLabel(DownloadStartInfo info) { - if (info.hasValues()) { - String textLinks = ""; - - if (info.error > 0) - textLinks += info.error + " fehlerhaft"; - setText(textLinks); - } - } -} diff --git a/src/main/java/mediathek/javafx/InfoLabel/FinishedLabel.java b/src/main/java/mediathek/javafx/InfoLabel/FinishedLabel.java deleted file mode 100644 index 4db71d1736..0000000000 --- a/src/main/java/mediathek/javafx/InfoLabel/FinishedLabel.java +++ /dev/null @@ -1,19 +0,0 @@ -package mediathek.javafx.InfoLabel; - -import javafx.scene.control.Label; -import javafx.scene.control.Tooltip; -import mediathek.daten.DownloadStartInfo; - -public class FinishedLabel extends Label { - public FinishedLabel() { - setTooltip(new Tooltip("Anzahl der abgeschlossenen Downloads")); - } - - public void updateLabel(DownloadStartInfo info) { - if (info.hasValues()) { - String fin = (info.finished == 1) ? "1 fertig" : info.finished + " fertig"; - setText(fin); - } else - setText(""); - } -} diff --git a/src/main/java/mediathek/javafx/InfoLabel/GesamtdownloadsLabel.java b/src/main/java/mediathek/javafx/InfoLabel/GesamtdownloadsLabel.java deleted file mode 100644 index ffba5dfae9..0000000000 --- a/src/main/java/mediathek/javafx/InfoLabel/GesamtdownloadsLabel.java +++ /dev/null @@ -1,22 +0,0 @@ -package mediathek.javafx.InfoLabel; - -import javafx.scene.control.Label; -import javafx.scene.control.Tooltip; -import mediathek.daten.DownloadStartInfo; -import mediathek.daten.ListeDownloads; - -public class GesamtdownloadsLabel extends Label { - public GesamtdownloadsLabel() { - setTooltip(new Tooltip("Gesamtzahl aller Downloads")); - } - - public void updateLabel(ListeDownloads listeDownloads, DownloadStartInfo info) { - final int anz = listeDownloads.size(); - final int diff = anz - info.total_starts; - String download = "Gesamtdownloads: " + anz; - if (diff >= 1) { - download += " (" + diff + " zurückgestellt)"; - } - setText(download); - } -} diff --git a/src/main/java/mediathek/javafx/InfoLabel/NumDownloadsLabel.java b/src/main/java/mediathek/javafx/InfoLabel/NumDownloadsLabel.java deleted file mode 100644 index 61ce1a78ca..0000000000 --- a/src/main/java/mediathek/javafx/InfoLabel/NumDownloadsLabel.java +++ /dev/null @@ -1,16 +0,0 @@ -package mediathek.javafx.InfoLabel; - -import javafx.scene.control.Label; -import javafx.scene.control.Tooltip; -import mediathek.daten.DownloadStartInfo; - -public class NumDownloadsLabel extends Label { - public NumDownloadsLabel() { - setTooltip(new Tooltip("Anzahl der manuellen Downloads in der Downloadliste")); - } - - public void updateLabel(DownloadStartInfo info) { - String numDownloads = (info.num_downloads == 1) ? "1 Download" : info.num_downloads + " Downloads"; - setText(numDownloads); - } -} diff --git a/src/main/java/mediathek/javafx/InfoLabel/WaitingLabel.java b/src/main/java/mediathek/javafx/InfoLabel/WaitingLabel.java deleted file mode 100644 index 63428cc7fb..0000000000 --- a/src/main/java/mediathek/javafx/InfoLabel/WaitingLabel.java +++ /dev/null @@ -1,19 +0,0 @@ -package mediathek.javafx.InfoLabel; - -import javafx.scene.control.Label; -import javafx.scene.control.Tooltip; -import mediathek.daten.DownloadStartInfo; - -public class WaitingLabel extends Label { - public WaitingLabel() { - setTooltip(new Tooltip("Anzahl der wartenden Downloads")); - } - - public void updateLabel(DownloadStartInfo info) { - if (info.hasValues()) { - String waiting = (info.initialized == 1) ? "1 wartet" : info.initialized + " warten"; - setText(waiting); - } else - setText(""); - } -} diff --git a/src/main/java/mediathek/javafx/MemoryMonitor.java b/src/main/java/mediathek/javafx/MemoryMonitor.java deleted file mode 100644 index 1ce21da80c..0000000000 --- a/src/main/java/mediathek/javafx/MemoryMonitor.java +++ /dev/null @@ -1,145 +0,0 @@ -package mediathek.javafx; - -import javafx.animation.*; -import javafx.beans.binding.NumberBinding; -import javafx.beans.property.LongProperty; -import javafx.beans.property.SimpleLongProperty; -import javafx.geometry.Insets; -import javafx.scene.Scene; -import javafx.scene.chart.LineChart; -import javafx.scene.chart.NumberAxis; -import javafx.scene.chart.XYChart; -import javafx.scene.control.Label; -import javafx.scene.input.MouseEvent; -import javafx.scene.layout.*; -import javafx.scene.paint.Color; -import javafx.stage.Stage; -import javafx.stage.StageStyle; -import javafx.util.Duration; -import mediathek.javafx.tool.JFXHiddenApplication; -import mediathek.tool.FileUtils; -import org.apache.commons.lang3.SystemUtils; - -import java.util.concurrent.atomic.AtomicInteger; - -public class MemoryMonitor extends Stage { - private static final int TIMELINE_SIZE = 60; - private final AtomicInteger time = new AtomicInteger(); - private final XYChart.Series series = new XYChart.Series<>(); - private Timeline updateMemoryTimer; - private LongProperty totalMemory; - private LongProperty freeMemory; - private LongProperty maxMemory; - private NumberBinding usedMemory; - private LineChart chart; - - public MemoryMonitor() { - super(); - initComponents(); - } - - private void initComponents() { - setTitle("Speicherverbrauch"); - initOwner(JFXHiddenApplication.getPrimaryStage()); - getIcons().add(JFXHiddenApplication.getApplicationImage()); - setAlwaysOnTop(true); - if (SystemUtils.IS_OS_MAC_OSX) - initStyle(StageStyle.UTILITY); - - createPropertiesAndBindings(); - - Scene scene = new Scene(createMemoryMonitor()); - setScene(scene); - - setOnHiding(e -> updateMemoryTimer.stop()); - setOnShowing(e -> updateMemoryTimer.play()); - - addEventHandler(MouseEvent.MOUSE_CLICKED, event -> { - final Animation animation = new Transition() { - { - setCycleDuration(Duration.millis(1000)); - setInterpolator(Interpolator.EASE_OUT); - } - - @Override - protected void interpolate(double frac) { - Color vColor = new Color(1, 0, 0, 1 - frac); - chart.setBackground(new Background(new BackgroundFill(vColor, CornerRadii.EMPTY, Insets.EMPTY))); - } - }; - animation.play(); - System.gc(); - }); - } - - private long toMegabytes(long bytes) { - return bytes / FileUtils.ONE_MB; - } - - private void createPropertiesAndBindings() { - totalMemory = new SimpleLongProperty(toMegabytes(Runtime.getRuntime().totalMemory())); - freeMemory = new SimpleLongProperty(toMegabytes(Runtime.getRuntime().freeMemory())); - maxMemory = new SimpleLongProperty(toMegabytes(Runtime.getRuntime().maxMemory())); - - usedMemory = totalMemory.subtract(freeMemory); - } - - private void createUpdateTimer() { - updateMemoryTimer = new Timeline(new KeyFrame(Duration.seconds(1), event -> { - totalMemory.set(toMegabytes(Runtime.getRuntime().totalMemory())); - freeMemory.set(toMegabytes(Runtime.getRuntime().freeMemory())); - maxMemory.set(toMegabytes(Runtime.getRuntime().maxMemory())); - - series.getData().add(new XYChart.Data<>(time.incrementAndGet(), usedMemory.getValue())); - if (series.getData().size() > TIMELINE_SIZE) { - series.getData().subList(0, series.getData().size() - TIMELINE_SIZE).clear(); - } - })); - updateMemoryTimer.setCycleCount(Animation.INDEFINITE); - updateMemoryTimer.play(); - } - - private Pane createMemoryMonitor() { - series.setName("Speicherverbrauch (MByte)"); - - createUpdateTimer(); - chart = createChart(); - - return new BorderPane(chart, createLabels(), null, null, null); - } - - private Pane createLabels() { - Label lblUsed = new Label(); - lblUsed.textProperty().bind(usedMemory.asString("Used: %,d")); - - Label lblFree = new Label(); - lblFree.textProperty().bind(freeMemory.asString("Free: %,d")); - - Label lblTotal = new Label(); - lblTotal.textProperty().bind(totalMemory.asString("Total: %,d")); - - Label lblMax = new Label(); - lblMax.textProperty().bind(maxMemory.asString("Max: %,d")); - - HBox labels = new HBox(lblUsed, lblFree, lblTotal, lblMax); - labels.setSpacing(10d); - - return labels; - } - - private LineChart createChart() { - NumberAxis xAxis = new NumberAxis(); - xAxis.setLabel("Laufzeit"); - xAxis.setForceZeroInRange(false); - - NumberAxis yAxis = new NumberAxis(); - yAxis.setLabel("Speicher"); - - LineChart chart = new LineChart<>(xAxis, yAxis); - chart.setAnimated(false); - chart.getData().add(series); - chart.createSymbolsProperty().setValue(false); - - return chart; - } -} diff --git a/src/main/java/mediathek/javafx/SelectedItemsLabel.java b/src/main/java/mediathek/javafx/SelectedItemsLabel.java deleted file mode 100644 index 60c2aba45a..0000000000 --- a/src/main/java/mediathek/javafx/SelectedItemsLabel.java +++ /dev/null @@ -1,24 +0,0 @@ -package mediathek.javafx; - -import javafx.beans.property.IntegerProperty; -import javafx.geometry.Insets; -import javafx.scene.control.Label; -import javafx.scene.control.Tooltip; -import javafx.scene.layout.StackPane; - -/** - * Displays the number of currently selected entries - */ -public class SelectedItemsLabel extends StackPane { - - public SelectedItemsLabel(IntegerProperty selectedItemsProperty) { - super(); - - Label textLabel = new Label(); - textLabel.setTooltip(new Tooltip("Ausgewählte Einträge der aktiven Tabelle")); - textLabel.textProperty().bind(selectedItemsProperty.asString()); - - setMargin(textLabel,new Insets(0,4,0,4)); - getChildren().add(textLabel); - } -} diff --git a/src/main/java/mediathek/javafx/StatusBarController.java b/src/main/java/mediathek/javafx/StatusBarController.java deleted file mode 100644 index c8287074c0..0000000000 --- a/src/main/java/mediathek/javafx/StatusBarController.java +++ /dev/null @@ -1,133 +0,0 @@ -package mediathek.javafx; - -import javafx.application.Platform; -import javafx.collections.ObservableList; -import javafx.scene.Node; -import javafx.scene.control.Label; -import javafx.scene.control.ProgressBar; -import javafx.scene.layout.HBox; -import javafx.scene.layout.Pane; -import javafx.scene.layout.Region; -import mediathek.config.Config; -import mediathek.config.Daten; -import mediathek.filmeSuchen.ListenerFilmeLaden; -import mediathek.filmeSuchen.ListenerFilmeLadenEvent; -import mediathek.javafx.filmlist.FilmListInfoPane; -import mediathek.tool.MessageBus; -import org.controlsfx.control.StatusBar; - -public class StatusBarController { - private final Label progressLabel = new Label(""); - private final ProgressBar progressBar = new ProgressBar(); - /** - * The new javafx based status bar - */ - private final StatusBar statusBar = new StatusBar(); - private final FilmListInfoPane filmListInfoPane; - private final GarbageCollectionButton btnGc = new GarbageCollectionButton(); - private Pane progressPane; - - public StatusBarController(Daten daten) { - filmListInfoPane = new FilmListInfoPane(daten); - - MessageBus.getMessageBus().subscribe(this); - - createProgressPane(); - - daten.getFilmeLaden().addAdListener(new ListenerFilmeLaden() { - @Override - public void start(ListenerFilmeLadenEvent event) { - addProgressItems(); - - if (Config.isDebugModeEnabled()) - Platform.runLater(() -> statusBar.setText(event.senderUrl)); - } - - @Override - public void progress(ListenerFilmeLadenEvent event) { - updateProgressBar(event); - } - - @Override - public void fertig(ListenerFilmeLadenEvent event) { - Platform.runLater(() -> progressBar.setProgress(0d)); - removeProgressItems(); - if (Config.isDebugModeEnabled()) - Platform.runLater(() -> statusBar.setText("")); - } - }); - } - - public StatusBar getStatusBar() { - return statusBar; - } - - private void createProgressPane() { - HBox hb = new HBox(); - hb.setSpacing(5d); - hb.setMinWidth(Region.USE_PREF_SIZE); - hb.getChildren().addAll(new VerticalSeparator(), - new CenteredBorderPane(progressLabel), - new CenteredBorderPane(progressBar) - ); - - progressPane = hb; - } - - private void updateProgressBar(ListenerFilmeLadenEvent event) { - Platform.runLater(() -> { - if (!progressBar.isVisible()) - progressBar.setVisible(true); - - if (event.max == 0 || event.progress == event.max) { - progressBar.setProgress(-1d); - } else { - final double max = event.max; - final double progress = event.progress; - - progressBar.setProgress(progress / max); - } - progressLabel.setText(event.text); - }); - } - - private void addProgressItems() { - Platform.runLater(() -> { - ObservableList rightItems = statusBar.getRightItems(); - //fix strange exception that duplicate was added... - if (!rightItems.contains(progressPane)) - rightItems.add(progressPane); - - }); - } - - private void removeProgressItems() { - Platform.runLater(() -> { - ObservableList rightItems = statusBar.getRightItems(); - rightItems.remove(progressPane); - }); - } - - private void setupLeftPane() { - ObservableList leftItems = statusBar.getLeftItems(); - - if (Config.isDebugModeEnabled()) { - leftItems.add(btnGc); - leftItems.add(new VerticalSeparator()); - } - } - - private void setupRightPane() { - statusBar.getRightItems().add(filmListInfoPane); - } - - public StatusBar createStatusBar() { - //reset text - statusBar.setText(""); - - setupLeftPane(); - setupRightPane(); - - return statusBar; - } -} diff --git a/src/main/java/mediathek/javafx/downloadtab/DownloadTabInformationLabel.java b/src/main/java/mediathek/javafx/downloadtab/DownloadTabInformationLabel.java deleted file mode 100644 index e2f25c2949..0000000000 --- a/src/main/java/mediathek/javafx/downloadtab/DownloadTabInformationLabel.java +++ /dev/null @@ -1,118 +0,0 @@ -package mediathek.javafx.downloadtab; - -import javafx.application.Platform; -import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; -import javafx.scene.layout.HBox; -import mediathek.config.Daten; -import mediathek.gui.messages.TimerEvent; -import mediathek.gui.messages.UpdateStatusBarLeftDisplayEvent; -import mediathek.javafx.CenteredBorderPane; -import mediathek.javafx.InfoLabel.*; -import mediathek.javafx.VerticalSeparator; -import mediathek.tool.MessageBus; -import net.engio.mbassy.listener.Handler; - - -public class DownloadTabInformationLabel extends HBox { - private final Daten daten; - private final GesamtdownloadsLabel overallDownloadLabel = new GesamtdownloadsLabel(); - private final AboLabel aboLabel = new AboLabel(); - private final NumDownloadsLabel numDownloadsLabel = new NumDownloadsLabel(); - private final ActiveDownloadsLabel activeDownloadLabel = new ActiveDownloadsLabel(); - private final WaitingLabel waitingLabel = new WaitingLabel(); - private final FinishedLabel finishedLabel = new FinishedLabel(); - private final ErrorLabel errorLabel = new ErrorLabel(); - private final HBox finishedBox = new HBox(); - private final HBox waitingBox = new HBox(); - private final HBox activeBox = new HBox(); - public DownloadTabInformationLabel(Daten daten) { - super(); - this.daten = daten; - - setupListeners(); - initLayout(); - } - - private void setupListeners() { - if (isVisible()) - MessageBus.getMessageBus().subscribe(this); - - visibleProperty().addListener(new ChangeListener<>() { - @Override - public void changed(ObservableValue observable, Boolean oldValue, Boolean newValue) { - if (newValue) { - MessageBus.getMessageBus().subscribe(this); - } else { - MessageBus.getMessageBus().unsubscribe(this); - } - } - }); - } - - private void initLayout() { - finishedBox.getChildren().addAll(new CenteredBorderPane(finishedLabel), new VerticalSeparator()); - waitingBox.getChildren().addAll(new CenteredBorderPane(waitingLabel), new VerticalSeparator()); - activeBox.getChildren().addAll(new CenteredBorderPane(activeDownloadLabel), new VerticalSeparator()); - - getChildren().addAll(new CenteredBorderPane(overallDownloadLabel), - new VerticalSeparator(), - new CenteredBorderPane(aboLabel), - new VerticalSeparator(), - new CenteredBorderPane(numDownloadsLabel), - new VerticalSeparator(), - activeBox, - waitingBox, - finishedBox, - new CenteredBorderPane(errorLabel)); - } - - @Handler - private void handleLeftDisplayUpdate(UpdateStatusBarLeftDisplayEvent e) { - Platform.runLater(this::getInfoTextDownloads); - } - - @Handler - private void handleTimerEvent(TimerEvent e) { - Platform.runLater(this::getInfoTextDownloads); - } - - private void getInfoTextDownloads() { - final var listeDownloads = daten.getListeDownloads(); - final var info = listeDownloads.getStarts(); - final var children = getChildren(); - - overallDownloadLabel.updateLabel(listeDownloads, info); - aboLabel.updateLabel(info); - numDownloadsLabel.updateLabel(info); - - if (info.running > 0) { - if (!children.contains(activeBox)) - children.add(activeBox); - activeDownloadLabel.updateLabel(daten, info); - } else - children.remove(activeBox); - - if (info.initialized > 0) { - if (!children.contains(waitingBox)) - children.add(waitingBox); - waitingLabel.updateLabel(info); - } else - children.remove(waitingBox); - - if (info.finished > 0) { - if (!children.contains(finishedBox)) - children.add(finishedBox); - finishedLabel.updateLabel(info); - } else - children.remove(finishedBox); - - if (info.error > 0) { - - if (!children.contains(errorLabel)) - children.add(new CenteredBorderPane(errorLabel)); - errorLabel.updateLabel(info); - } else - children.remove(errorLabel); - } -} diff --git a/src/main/java/mediathek/javafx/filmlist/FilmListAgeLabel.kt b/src/main/java/mediathek/javafx/filmlist/FilmListAgeLabel.kt deleted file mode 100644 index f35b536b68..0000000000 --- a/src/main/java/mediathek/javafx/filmlist/FilmListAgeLabel.kt +++ /dev/null @@ -1,53 +0,0 @@ -package mediathek.javafx.filmlist - -import javafx.animation.Animation -import javafx.animation.KeyFrame -import javafx.animation.Timeline -import javafx.scene.control.Tooltip -import javafx.util.Duration -import mediathek.config.Daten -import mediathek.javafx.tool.ComputedLabel - -/** - * Label which will compute the age of the filmlist when updated. - * Update cycle one second. - */ -class FilmListAgeLabel internal constructor() : ComputedLabel() { - private val timeline = Timeline(KeyFrame(Duration.millis(1_000.0), { setAgeToLabel() })) - private var oldAge = FilmListAge(0, 0) - - private fun setAgeToLabel() { - val listAge = calculateAge() - if (listAge != oldAge) { - setComputedText(computeAgeString(listAge)) - oldAge = listAge - } - } - - private data class FilmListAge(val hours: Long, val minutes: Long) - - private fun calculateAge(): FilmListAge { - val duration = java.time.Duration.ofSeconds(Daten.getInstance().listeFilme.metaData().ageInSeconds) - var minutes = duration.toMinutes() - val hours = minutes / 60 - minutes -= hours * 60 - return FilmListAge(hours, minutes) - } - - private fun computeAgeString(age: FilmListAge): String { - return try { - if (age.hours == 0L) - String.format("Alter: %dm", age.minutes) - else - String.format("Alter: %dh %dm", age.hours, age.minutes) - } catch (ex: IllegalArgumentException) { - "Ungültiges Alter" - } - } - - init { - timeline.cycleCount = Animation.INDEFINITE - timeline.play() - tooltip = Tooltip("Alter der Filmliste") - } -} \ No newline at end of file diff --git a/src/main/java/mediathek/javafx/filmlist/FilmListCreationDateLabel.java b/src/main/java/mediathek/javafx/filmlist/FilmListCreationDateLabel.java deleted file mode 100644 index 5b186aa70d..0000000000 --- a/src/main/java/mediathek/javafx/filmlist/FilmListCreationDateLabel.java +++ /dev/null @@ -1,24 +0,0 @@ -package mediathek.javafx.filmlist; - -import mediathek.config.Daten; -import mediathek.javafx.tool.ComputedLabel; - -/** - * Computed label which will display the creation date of the current film list. - */ -class FilmListCreationDateLabel extends ComputedLabel { - private final Daten daten; - - FilmListCreationDateLabel(Daten daten) { - super(); - this.daten = daten; - } - - /** - * Computes and displays the age of the list. - */ - public void computeCreationDate() { - //update text - setComputedText(String.format("Filmliste erstellt: %s Uhr", daten.getListeFilme().metaData().getGenerationDateTimeAsString())); - } -} diff --git a/src/main/java/mediathek/javafx/filmlist/FilmListInfoPane.java b/src/main/java/mediathek/javafx/filmlist/FilmListInfoPane.java deleted file mode 100644 index df16a80ff8..0000000000 --- a/src/main/java/mediathek/javafx/filmlist/FilmListInfoPane.java +++ /dev/null @@ -1,48 +0,0 @@ -package mediathek.javafx.filmlist; - -import javafx.application.Platform; -import javafx.scene.layout.HBox; -import mediathek.config.Daten; -import mediathek.filmeSuchen.ListenerFilmeLaden; -import mediathek.filmeSuchen.ListenerFilmeLadenEvent; -import mediathek.javafx.CenteredBorderPane; -import mediathek.javafx.VerticalSeparator; -import mediathek.tool.MessageBus; - -import javax.swing.*; - -/** - * Pane which will display common information about the current filmlist. - * Also handles updating the subcomponents based on a TimerEvent. - */ -public class FilmListInfoPane extends HBox { - private final FilmListCreationDateLabel filmListCreationDateLabel; - - public FilmListInfoPane(Daten daten) { - super(); - setSpacing(4d); - - SwingUtilities.invokeLater(() -> daten.getFilmeLaden().addAdListener(new ListenerFilmeLaden() { - @Override - public void start(ListenerFilmeLadenEvent event) { - MessageBus.getMessageBus().unsubscribe(this); - Platform.runLater(() -> setVisible(false)); - } - - @Override - public void fertig(ListenerFilmeLadenEvent event) { - MessageBus.getMessageBus().subscribe(this); - Platform.runLater(() -> { - filmListCreationDateLabel.computeCreationDate(); - setVisible(true); - }); - } - })); - - filmListCreationDateLabel = new FilmListCreationDateLabel(daten); - - getChildren().addAll(new CenteredBorderPane(filmListCreationDateLabel), - new VerticalSeparator(), - new CenteredBorderPane(new FilmListAgeLabel())); - } -} diff --git a/src/main/java/mediathek/javafx/filmtab/FilmInfoLabel.java b/src/main/java/mediathek/javafx/filmtab/FilmInfoLabel.java deleted file mode 100644 index 3885594ec7..0000000000 --- a/src/main/java/mediathek/javafx/filmtab/FilmInfoLabel.java +++ /dev/null @@ -1,51 +0,0 @@ -package mediathek.javafx.filmtab; - -import javafx.scene.control.Label; -import mediathek.config.Daten; -import mediathek.gui.tabs.tab_film.GuiFilme; - -public class FilmInfoLabel extends Label { - private final Daten daten; - private final GuiFilme tabFilme; - - public FilmInfoLabel(Daten daten, GuiFilme tabFilme) { - super(); - this.daten = daten; - this.tabFilme = tabFilme; - } - - private String createFilmLabel(final int rowCount) { - String textLinks; - if (rowCount == 1) - textLinks = "1 Film"; - else - textLinks = rowCount + " Filme"; - - return textLinks; - } - - private int oldGesamt = 0; - private int oldRowCount = 0; - - public void updateValues() { - String textLinks; - final int gesamt = daten.getListeFilme().size(); - final int rowCount = tabFilme.getTableRowCount(); - - if (gesamt == oldGesamt && rowCount == oldRowCount) - return; - - // Anzahl der Filme - if (gesamt == rowCount) { - textLinks = createFilmLabel(rowCount); - } else { - textLinks = createFilmLabel(rowCount); - textLinks += " (Insgesamt: " + gesamt + ")"; - } - - setText(textLinks); - - oldGesamt = gesamt; - oldRowCount = rowCount; - } -} diff --git a/src/main/java/mediathek/javafx/filmtab/FilmTabInfoPane.java b/src/main/java/mediathek/javafx/filmtab/FilmTabInfoPane.java deleted file mode 100644 index e285a860fa..0000000000 --- a/src/main/java/mediathek/javafx/filmtab/FilmTabInfoPane.java +++ /dev/null @@ -1,64 +0,0 @@ -package mediathek.javafx.filmtab; - -import javafx.application.Platform; -import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; -import javafx.scene.layout.HBox; -import mediathek.config.Daten; -import mediathek.gui.messages.DownloadInfoUpdateAvailableEvent; -import mediathek.gui.messages.TimerEvent; -import mediathek.gui.messages.UpdateStatusBarLeftDisplayEvent; -import mediathek.gui.tabs.tab_film.GuiFilme; -import mediathek.javafx.CenteredBorderPane; -import mediathek.javafx.VerticalSeparator; -import mediathek.tool.MessageBus; -import net.engio.mbassy.listener.Handler; - -public class FilmTabInfoPane extends HBox { - private final FilmTabDownloadInformationLabel downloadInformationLabel; - private final FilmInfoLabel filmInfoLabel; - - public FilmTabInfoPane(Daten daten, GuiFilme tabFilme) { - super(); - downloadInformationLabel = new FilmTabDownloadInformationLabel(daten); - filmInfoLabel = new FilmInfoLabel(daten,tabFilme); - - getChildren().addAll(new CenteredBorderPane(filmInfoLabel), - new VerticalSeparator(), - new CenteredBorderPane(downloadInformationLabel), - new VerticalSeparator()); - - if (isVisible()) - MessageBus.getMessageBus().subscribe(this); - - visibleProperty().addListener(new ChangeListener<>() { - @Override - public void changed(ObservableValue observable, Boolean oldValue, Boolean newValue) { - if (newValue) { - MessageBus.getMessageBus().subscribe(this); - } else { - MessageBus.getMessageBus().unsubscribe(this); - } - } - }); - } - - private void updateLayout() { - filmInfoLabel.updateValues(); - } - - @Handler - private void handleDownloadInfoUpdate(DownloadInfoUpdateAvailableEvent e) { - Platform.runLater(downloadInformationLabel::setInfoFilme); - } - @Handler - private void handleLeftDisplayUpdate(UpdateStatusBarLeftDisplayEvent e) { - Platform.runLater(this::updateLayout); - } - - - @Handler - private void handleTimerEvent(TimerEvent e) { - Platform.runLater(this::updateLayout); - } -} diff --git a/src/main/java/mediathek/javafx/tool/ComputedLabel.java b/src/main/java/mediathek/javafx/tool/ComputedLabel.java deleted file mode 100644 index c7c1e7734d..0000000000 --- a/src/main/java/mediathek/javafx/tool/ComputedLabel.java +++ /dev/null @@ -1,23 +0,0 @@ -package mediathek.javafx.tool; - -import javafx.scene.control.Label; - -/** - * A JavaFX Label which will adapt its minimum width. - */ -public abstract class ComputedLabel extends Label { - private double width = 0d; - - /** - * This sets the text of the label and adjusts the width of the label to prevent "jumping" text. - */ - public void setComputedText(String text) { - setText(text); - double curWidth = getWidth(); - if (curWidth >= width) { - width = curWidth; - setMinWidth(width); - } - - } -} diff --git a/src/main/java/mediathek/javafx/tool/FXProgressPane.java b/src/main/java/mediathek/javafx/tool/FXProgressPane.java deleted file mode 100644 index d19e5f4f76..0000000000 --- a/src/main/java/mediathek/javafx/tool/FXProgressPane.java +++ /dev/null @@ -1,32 +0,0 @@ -package mediathek.javafx.tool; - -import javafx.concurrent.Task; -import javafx.scene.control.Label; -import javafx.scene.control.ProgressBar; -import javafx.scene.control.ProgressIndicator; -import javafx.scene.layout.HBox; -import mediathek.javafx.CenteredBorderPane; -import mediathek.javafx.VerticalSeparator; - -public class FXProgressPane extends HBox { - public Label lb; - public ProgressBar prog; - - public FXProgressPane() { - super(); - setSpacing(4d); - - lb = new Label(); - prog = new ProgressBar(); - prog.setProgress(ProgressIndicator.INDETERMINATE_PROGRESS); - - getChildren().addAll(new VerticalSeparator(), - new CenteredBorderPane(lb), - new CenteredBorderPane(prog)); - } - - public void bindTask(Task task) { - lb.textProperty().bind(task.messageProperty()); - prog.progressProperty().bind(task.progressProperty()); - } -} diff --git a/src/main/java/mediathek/javafx/filmtab/FilmTabDownloadInformationLabel.java b/src/main/java/mediathek/mainwindow/DownloadInformationLabel.java similarity index 57% rename from src/main/java/mediathek/javafx/filmtab/FilmTabDownloadInformationLabel.java rename to src/main/java/mediathek/mainwindow/DownloadInformationLabel.java index b7f5b19746..77a0078833 100644 --- a/src/main/java/mediathek/javafx/filmtab/FilmTabDownloadInformationLabel.java +++ b/src/main/java/mediathek/mainwindow/DownloadInformationLabel.java @@ -1,27 +1,34 @@ -package mediathek.javafx.filmtab; +package mediathek.mainwindow; -import javafx.scene.control.Label; import mediathek.config.Daten; +import mediathek.gui.messages.DownloadInfoUpdateAvailableEvent; +import mediathek.tool.MessageBus; +import net.engio.mbassy.listener.Handler; -public class FilmTabDownloadInformationLabel extends Label { - private final Daten daten; +import javax.swing.*; - public FilmTabDownloadInformationLabel(Daten daten) { - super(); - this.daten = daten; +public class DownloadInformationLabel extends JLabel { + public DownloadInformationLabel() { + MessageBus.getMessageBus().subscribe(this); } - public void setInfoFilme() { + @Handler + private void handleDownloadInfoUpdate(DownloadInfoUpdateAvailableEvent e) { + SwingUtilities.invokeLater(this::setInfoFilme); + } + + private void setInfoFilme() { setText(getInfoTextDownloads()); } private String getInfoTextDownloads() { String textLinks; + final var daten = Daten.getInstance(); final var listeDownloads = daten.getListeDownloads(); final var info = listeDownloads.getStarts(); - final int anz = listeDownloads.size(); - textLinks = (anz == 1) ? "1 Download" : anz + " Downloads"; + textLinks = (info.total_num_download_list_entries == 1) ? + "1 Download" : info.total_num_download_list_entries + " Downloads"; if (info.hasValues()) { textLinks += ": "; diff --git a/src/main/java/mediathek/mainwindow/FilmAgeLabel.java b/src/main/java/mediathek/mainwindow/FilmAgeLabel.java new file mode 100644 index 0000000000..56d4441586 --- /dev/null +++ b/src/main/java/mediathek/mainwindow/FilmAgeLabel.java @@ -0,0 +1,58 @@ +package mediathek.mainwindow; + +import mediathek.config.Daten; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.time.Duration; +import java.util.IllegalFormatException; + +public class FilmAgeLabel extends JLabel implements ActionListener { + record FilmListAge(long hours, long minutes) { + } + + private FilmListAge calculateFilmListAge() { + var duration = Duration.ofSeconds(Daten.getInstance().getListeFilme().getMetaData().getAgeInSeconds()); + var minutes = duration.toMinutes(); + var hours = minutes / 60; + minutes -= hours * 60; + return new FilmListAge(hours, minutes); + } + + private final Timer timer; + private FilmListAge oldAge = new FilmListAge(0, 0); + + public FilmAgeLabel() { + setToolTipText("Alter der Filmliste"); + + setAgeToLabel(); + + timer = new Timer(1000, this); + timer.setRepeats(true); + timer.start(); + } + + private String computeAgeString(@NotNull FilmListAge age) throws IllegalFormatException { + if (age.hours == 0) { + return String.format("Alter: %dm", age.minutes); + } else { + return String.format("Alter: %dh %dm", age.hours, age.minutes); + } + } + + private void setAgeToLabel() { + var curAge = calculateFilmListAge(); + if (!curAge.equals(oldAge)) { + var result = computeAgeString(curAge); + setText(result); + oldAge = curAge; + } + } + + @Override + public void actionPerformed(ActionEvent e) { + setAgeToLabel(); + } +} diff --git a/src/main/java/mediathek/mainwindow/FilmListCreationDateLabel.java b/src/main/java/mediathek/mainwindow/FilmListCreationDateLabel.java new file mode 100644 index 0000000000..2b2c2c24cf --- /dev/null +++ b/src/main/java/mediathek/mainwindow/FilmListCreationDateLabel.java @@ -0,0 +1,22 @@ +package mediathek.mainwindow; + +import mediathek.config.Daten; +import mediathek.daten.FilmListMetaData; + +import javax.swing.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +public class FilmListCreationDateLabel extends JLabel implements PropertyChangeListener { + public FilmListCreationDateLabel() { + //works only on blacklist! + Daten.getInstance().getListeFilmeNachBlackList().addMetaDataChangeListener(this); + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + var metaData = (FilmListMetaData) evt.getNewValue(); + var text = String.format("Filmliste erstellt: %s Uhr", metaData.getGenerationDateTimeAsString()); + SwingUtilities.invokeLater(() -> setText(text)); + } +} diff --git a/src/main/java/mediathek/mainwindow/FilmSizeInfoLabel.java b/src/main/java/mediathek/mainwindow/FilmSizeInfoLabel.java new file mode 100644 index 0000000000..c5558769b6 --- /dev/null +++ b/src/main/java/mediathek/mainwindow/FilmSizeInfoLabel.java @@ -0,0 +1,71 @@ +package mediathek.mainwindow; + +import mediathek.config.Daten; +import mediathek.gui.messages.UpdateStatusBarLeftDisplayEvent; +import mediathek.tool.MessageBus; +import net.engio.mbassy.listener.Handler; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class FilmSizeInfoLabel extends JLabel implements ActionListener { + private int oldGesamt; + private int oldRowCount; + private final MediathekGui mediathekGui; + + private final Timer timer; + + public FilmSizeInfoLabel(@NotNull MediathekGui mediathekGui) { + this.mediathekGui = mediathekGui; + + timer = new Timer(1000, this); + timer.setRepeats(true); + timer.start(); + + MessageBus.getMessageBus().subscribe(this); + } + + @Handler + private void handleLeftDisplayUpdate(UpdateStatusBarLeftDisplayEvent e) { + SwingUtilities.invokeLater(this::updateValues); + } + + private void updateValues() { + String textLinks; + final int gesamt = Daten.getInstance().getListeFilme().size(); + final int rowCount = mediathekGui.tabFilme.getTableRowCount(); + + if (gesamt == oldGesamt && rowCount == oldRowCount) + return; + + // Anzahl der Filme + if (gesamt == rowCount) { + textLinks = createFilmLabel(rowCount); + } else { + textLinks = createFilmLabel(rowCount); + textLinks += " (Insgesamt: " + gesamt + ")"; + } + + setText(textLinks); + + oldGesamt = gesamt; + oldRowCount = rowCount; + } + + private String createFilmLabel(final int rowCount) { + String textLinks; + if (rowCount == 1) + textLinks = "1 Film"; + else + textLinks = rowCount + " Filme"; + + return textLinks; + } + + @Override + public void actionPerformed(ActionEvent e) { + updateValues(); + } +} diff --git a/src/main/java/mediathek/mainwindow/ListSelectedItemsProperty.java b/src/main/java/mediathek/mainwindow/ListSelectedItemsProperty.java new file mode 100644 index 0000000000..1104305cc3 --- /dev/null +++ b/src/main/java/mediathek/mainwindow/ListSelectedItemsProperty.java @@ -0,0 +1,23 @@ +package mediathek.mainwindow; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; + +public class ListSelectedItemsProperty { + private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); + private long selectedItems; + + public ListSelectedItemsProperty(long selectedItems) { + this.selectedItems = selectedItems; + } + + public void addSelectedItemsChangeListener(PropertyChangeListener listener) { + this.pcs.addPropertyChangeListener(listener); + } + + public void setSelectedItems(long selectedItems) { + long oldValue = this.selectedItems; + this.selectedItems = selectedItems; + this.pcs.firePropertyChange("sel_items", oldValue, selectedItems); + } +} diff --git a/src/main/java/mediathek/mainwindow/MediathekGui.java b/src/main/java/mediathek/mainwindow/MediathekGui.java index 5696b199ec..b53ad8c61a 100644 --- a/src/main/java/mediathek/mainwindow/MediathekGui.java +++ b/src/main/java/mediathek/mainwindow/MediathekGui.java @@ -1,35 +1,21 @@ package mediathek.mainwindow; import javafx.application.Platform; -import javafx.beans.property.IntegerProperty; import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.property.SimpleObjectProperty; -import javafx.collections.ObservableList; -import javafx.concurrent.WorkerStateEvent; -import javafx.embed.swing.JFXPanel; -import javafx.event.EventHandler; -import javafx.scene.Node; -import javafx.scene.Scene; -import javafx.scene.control.ProgressIndicator; -import javafx.scene.control.Tooltip; -import javafx.scene.layout.HBox; import javafx.stage.Stage; import mediathek.Main; -import mediathek.config.Config; -import mediathek.config.Daten; -import mediathek.config.Icons; -import mediathek.config.Konstanten; +import mediathek.config.*; import mediathek.controller.history.SeenHistoryController; import mediathek.controller.starter.Start; import mediathek.daten.DatenDownload; import mediathek.filmeSuchen.ListenerFilmeLaden; import mediathek.filmeSuchen.ListenerFilmeLadenEvent; import mediathek.filmlisten.FilmeLaden; +import mediathek.filmlisten.reader.FilmListReader; import mediathek.gui.MVTray; import mediathek.gui.TabPaneIndex; import mediathek.gui.actions.*; -import mediathek.gui.actions.export.FilmListExportAction; import mediathek.gui.actions.import_actions.ImportOldAbosAction; import mediathek.gui.actions.import_actions.ImportOldBlacklistAction; import mediathek.gui.actions.import_actions.ImportOldReplacementListAction; @@ -43,8 +29,9 @@ import mediathek.gui.messages.*; import mediathek.gui.tabs.tab_downloads.GuiDownloads; import mediathek.gui.tabs.tab_film.GuiFilme; -import mediathek.javafx.*; -import mediathek.javafx.tool.FXProgressPane; +import mediathek.gui.tasks.BlacklistFilterWorker; +import mediathek.gui.tasks.RefreshAboWorker; +import mediathek.javafx.ShutdownDialog; import mediathek.javafx.tool.JFXHiddenApplication; import mediathek.javafx.tool.JavaFxUtils; import mediathek.res.GetIcon; @@ -69,7 +56,6 @@ import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.IOException; -import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.NoSuchElementException; import java.util.concurrent.CompletableFuture; @@ -110,25 +96,33 @@ public class MediathekGui extends JFrame { private final JMenu jMenuDownload = new JMenu(); private final JMenu jMenuAbos = new JMenu(); private final JMenu jMenuAnsicht = new JMenu(); - /** - * this property keeps track how many items are currently selected in the active table view - */ - private final IntegerProperty selectedItemsProperty = new SimpleIntegerProperty(0); /** * Helper to determine what tab is currently active */ private final ObjectProperty tabPaneIndexProperty = new SimpleObjectProperty<>(TabPaneIndex.NONE); private final HashMap menuListeners = new HashMap<>(); private final JCheckBoxMenuItem cbBandwidthDisplay = new JCheckBoxMenuItem("Bandbreitennutzung"); - private final JFXPanel statusBarPanel = new JFXPanel(); private final SearchProgramUpdateAction searchProgramUpdateAction; - private final MemoryMonitorAction showMemoryMonitorAction = new MemoryMonitorAction(); + private final MemoryMonitorAction showMemoryMonitorAction = new MemoryMonitorAction(this); private final InfoDialog filmInfo; + public StatusBar swingStatusBar; public GuiFilme tabFilme; public GuiDownloads tabDownloads; public EditBlacklistAction editBlacklistAction = new EditBlacklistAction(this); public ToggleBlacklistAction toggleBlacklistAction = new ToggleBlacklistAction(); public ShowFilmInformationAction showFilmInformationAction = new ShowFilmInformationAction(); + /** + * this property keeps track how many items are currently selected in the active table view + */ + public ListSelectedItemsProperty selectedListItemsProperty = new ListSelectedItemsProperty(0); + /** + * Used for status bar progress. + */ + public JLabel progressLabel = new JLabel(); + /** + * Used for status bar progress. + */ + public JProgressBar progressBar = new JProgressBar(); /** * the global configuration for this app. */ @@ -141,7 +135,6 @@ public class MediathekGui extends JFrame { private BandwidthMonitorController bandwidthMonitor; private MVTray tray; private DialogEinstellungen dialogEinstellungen; - private StatusBarController statusBarController; private ProgramUpdateCheck programUpdateChecker; /** * Progress indicator thread for OS X and windows. @@ -149,10 +142,6 @@ public class MediathekGui extends JFrame { private IndicatorThread progressIndicatorThread; private ManageAboAction manageAboAction; private AutomaticFilmlistUpdate automaticFilmlistUpdate; - /** - * A weak reference to the table data model filtering progress indicator(s). - */ - private WeakReference indicatorLayout; public MediathekGui() { ui = this; @@ -164,10 +153,7 @@ public MediathekGui() { Main.splashScreen.ifPresent(s -> s.update(UIProgressState.LOAD_MAINWINDOW)); - var contentPane = getContentPane(); - contentPane.setLayout(new BorderLayout()); - - contentPane.add(statusBarPanel, BorderLayout.PAGE_END); + getContentPane().setLayout(new BorderLayout()); setIconAndWindowImage(); @@ -317,6 +303,7 @@ private void setupNotificationCenter() { /** * Return the platform-specific notification implementation. + * * @return generic or platform-specific notification implementation. */ protected INotificationCenter getNotificationCenter() { @@ -449,61 +436,70 @@ private void createMemoryMonitor() { * Read a local filmlist or load a new one in auto mode. */ private void loadFilmlist() { - Platform.runLater(() -> { - //don´t write filmlist when we are reading only... - var writeCondition = !(GuiFunktionen.getFilmListUpdateType() == FilmListUpdateType.AUTOMATIC && daten.getListeFilme().needsUpdate()); - Daten.dontWriteFilmlistOnStartup.set(writeCondition); - - FXProgressPane progressPane = new FXProgressPane(); - - FilmListReaderTask filmListReaderTask = new FilmListReaderTask(); - filmListReaderTask.setOnRunning(e -> { - statusBarController.getStatusBar().getRightItems().add(progressPane); - progressPane.bindTask(filmListReaderTask); - }); - - FilmListNetworkReaderTask networkTask = new FilmListNetworkReaderTask(); - networkTask.setOnRunning(e -> progressPane.bindTask(networkTask)); - - FilmListFilterTask filterTask = new FilmListFilterTask(true); - filterTask.setOnRunning(e -> progressPane.bindTask(filterTask)); - final EventHandler workerStateEventEventHandler = e -> statusBarController.getStatusBar().getRightItems().remove(progressPane); - filterTask.setOnSucceeded(workerStateEventEventHandler); - filterTask.setOnFailed(workerStateEventEventHandler); - - CompletableFuture.runAsync(filmListReaderTask) - .thenRun(networkTask) - .thenRun(filterTask); - - //reset after first load has happened - Daten.dontWriteFilmlistOnStartup.set(false); - }); - } - - public IntegerProperty getSelectedItemsProperty() { - return selectedItemsProperty; + //don´t write filmlist when we are reading only... + var writeCondition = !(GuiFunktionen.getFilmListUpdateType() == FilmListUpdateType.AUTOMATIC && daten.getListeFilme().needsUpdate()); + Daten.dontWriteFilmlistOnStartup.set(writeCondition); + + swingStatusBar.getStatusBar().add(progressLabel); + swingStatusBar.getStatusBar().add(progressBar); + + CompletableFuture.runAsync(() -> { + logger.trace("Reading local filmlist"); + MessageBus.getMessageBus().publishAsync(new FilmListReadStartEvent()); + + try (FilmListReader reader = new FilmListReader()) { + final int num_days = ApplicationConfiguration.getConfiguration().getInt(ApplicationConfiguration.FilmList.LOAD_NUM_DAYS, 0); + reader.readFilmListe(StandardLocations.getFilmlistFilePath(), daten.getListeFilme(), num_days); + } + MessageBus.getMessageBus().publishAsync(new FilmListReadStopEvent()); + }) + .thenRun(() -> { + logger.trace("Check for filmlist updates"); + if (GuiFunktionen.getFilmListUpdateType() == FilmListUpdateType.AUTOMATIC && daten.getListeFilme().needsUpdate()) { + daten.getFilmeLaden().loadFilmlist("", true); + } + }) + .thenRun(new RefreshAboWorker(progressLabel, progressBar)) + .thenRun(new BlacklistFilterWorker(progressLabel, progressBar)) + .thenRun(() -> SwingUtilities.invokeLater(() -> Daten.getInstance().getFilmeLaden().notifyFertig(new ListenerFilmeLadenEvent("", "", 100, 100, false)))) + .thenRun(() -> Daten.dontWriteFilmlistOnStartup.set(false)) + .thenRun(() -> SwingUtilities.invokeLater(() -> { + swingStatusBar.getStatusBar().remove(progressBar); + swingStatusBar.getStatusBar().remove(progressLabel); + })); } /** * Create the status bar item. */ private void createStatusBar() { - statusBarController = new StatusBarController(daten); + swingStatusBar = new StatusBar(this); + getContentPane().add(swingStatusBar, BorderLayout.SOUTH); - JavaFxUtils.invokeInFxThreadAndWait(() -> { - statusBarPanel.setScene(new Scene(statusBarController.createStatusBar())); - installSelectedItemsLabel(); - }); + createFilmlistDownloadProgress(); } - private void installSelectedItemsLabel() { - ObservableList leftItems = statusBarController.getStatusBar().getLeftItems(); - leftItems.add(0, new SelectedItemsLabel(selectedItemsProperty)); - leftItems.add(1, new VerticalSeparator()); - } + private void createFilmlistDownloadProgress() { + daten.getFilmeLaden().addAdListener(new ListenerFilmeLaden() { + @Override + public void start(ListenerFilmeLadenEvent event) { + swingStatusBar.getStatusBar().add(progressLabel); + swingStatusBar.getStatusBar().add(progressBar); + } - public StatusBarController getStatusBarController() { - return statusBarController; + @Override + public void progress(ListenerFilmeLadenEvent event) { + if (event.max == 0 || event.progress == event.max) { + progressBar.setIndeterminate(true); + } else { + progressBar.setIndeterminate(false); + progressBar.setMinimum(0); + progressBar.setMaximum(event.max); + progressBar.setValue(event.progress); + } + progressLabel.setText(event.text); + } + }); } public ObjectProperty tabPaneIndexProperty() { @@ -522,26 +518,6 @@ private void handleTabVisualSettingsChangedEvent(TabVisualSettingsChangedEvent e }); } - @Handler - private void handleTableModelChangeEvent(TableModelChangeEvent evt) { - Platform.runLater(() -> { - var statusBar = statusBarController.getStatusBar(); - var rightItems = statusBar.getRightItems(); - if (evt.active) { - HBox hb = new HBox(); - var progressIndicator = new ProgressIndicator(); - progressIndicator.setTooltip(new Tooltip("Filmdaten werden verarbeitet")); - hb.getChildren().add(progressIndicator); - rightItems.add(hb); - indicatorLayout = new WeakReference<>(hb); - } - else { - //hide progress and label - rightItems.remove(indicatorLayout.get()); - } - }); - } - @Handler private void handleBandwidthMonitorStateChangedEvent(BandwidthMonitorStateChangedEvent e) { final var vis = config.getBoolean(ApplicationConfiguration.APPLICATION_UI_BANDWIDTH_MONITOR_VISIBLE, false); @@ -824,7 +800,7 @@ private void createFileMenu() { jMenuDatei.add(loadFilmListAction); jMenuDatei.addSeparator(); var exportMenu = new JMenu("Export"); - exportMenu.add(new FilmListExportAction(this)); + exportMenu.add(new FilmListExportAction()); var importMenu = new JMenu("Import"); importMenu.add(new ImportOldAbosAction()); @@ -913,9 +889,21 @@ protected void initMenus() { tabFilme.installViewMenuEntry(jMenuAnsicht); createAboMenu(); + if (Config.isDebugModeEnabled()) + createDeveloperMenu(); createHelpMenu(); } + private void createDeveloperMenu() { + JMenu devMenu = new JMenu("Entwickler"); + + JMenuItem miGc = new JMenuItem("GC ausführen"); + miGc.addActionListener(l -> System.gc()); + + devMenu.add(miGc); + jMenuBar.add(devMenu); + } + private void createAboMenu() { jMenuAbos.add(new CreateNewAboAction(daten.getListeAbo())); jMenuAbos.add(new ShowAboHistoryAction(MediathekGui.ui())); diff --git a/src/main/java/mediathek/mainwindow/MemoryUsagePanel.java b/src/main/java/mediathek/mainwindow/MemoryUsagePanel.java new file mode 100644 index 0000000000..d827e7493c --- /dev/null +++ b/src/main/java/mediathek/mainwindow/MemoryUsagePanel.java @@ -0,0 +1,89 @@ +package mediathek.mainwindow; + +import org.jetbrains.annotations.NotNull; +import org.jfree.chart.ChartPanel; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.axis.DateAxis; +import org.jfree.chart.axis.NumberAxis; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.renderer.xy.XYSplineRenderer; +import org.jfree.chart.ui.RectangleInsets; +import org.jfree.data.time.Millisecond; +import org.jfree.data.time.TimeSeries; +import org.jfree.data.time.TimeSeriesCollection; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.text.DecimalFormat; +import java.util.concurrent.TimeUnit; + +public class MemoryUsagePanel extends JPanel { + + private final TimeSeries total; + + public MemoryUsagePanel(int maxAge, @NotNull TimeUnit timeUnit) { + + super(new BorderLayout()); + + total = new TimeSeries("Total Memory"); + total.setMaximumItemAge(TimeUnit.MILLISECONDS.convert(maxAge, timeUnit)); + TimeSeriesCollection dataset = new TimeSeriesCollection(); + dataset.addSeries(total); + + var domain = new DateAxis("Time"); + NumberAxis range = new NumberAxis("Memory"); + range.setAutoRange(true); + + var renderer = new XYSplineRenderer(); + renderer.setDefaultShapesVisible(false); + renderer.setSeriesPaint(0, Color.red); + + var plot = new XYPlot(dataset, domain, range, renderer); + plot.setBackgroundPaint(Color.BLACK); + plot.setDomainGridlinePaint(Color.white); + plot.setRangeGridlinePaint(Color.white); + plot.setAxisOffset(new RectangleInsets(5.0, 5.0, 5.0, 5.0)); + + domain.setAutoRange(true); + domain.setLowerMargin(0.0); + domain.setUpperMargin(0.0); + domain.setTickLabelsVisible(true); + + range.setStandardTickUnits(NumberAxis.createIntegerTickUnits()); + range.setNumberFormatOverride(new DecimalFormat("#######.##")); + + var chart = new JFreeChart(plot); + chart.removeLegend(); + add(new ChartPanel(chart)); + } + + private void addTotalObservation(double y) { + total.add(new Millisecond(), y); + } + + public class MemoryUsageDataGenerator extends Timer implements ActionListener { + + /** + * Constructor. + * + * @param interval the interval + */ + public MemoryUsageDataGenerator(int interval, @NotNull TimeUnit timeUnit) { + super((int) TimeUnit.MILLISECONDS.convert(interval, timeUnit), null); + addActionListener(this); + } + + /** + * Adds a new total memory reading (converted to MByte) to the dataset. + * + * @param event the action event. + */ + public void actionPerformed(ActionEvent event) { + long t = Runtime.getRuntime().totalMemory() / (1024 * 1024); + addTotalObservation(t); + } + + } +} diff --git a/src/main/java/mediathek/mainwindow/SelectedListItemsLabel.java b/src/main/java/mediathek/mainwindow/SelectedListItemsLabel.java new file mode 100644 index 0000000000..67c5d2c736 --- /dev/null +++ b/src/main/java/mediathek/mainwindow/SelectedListItemsLabel.java @@ -0,0 +1,21 @@ +package mediathek.mainwindow; + +import javax.swing.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +public class SelectedListItemsLabel extends JLabel implements PropertyChangeListener { + public SelectedListItemsLabel(MediathekGui mediathekGui) { + setText("0"); + setToolTipText("Ausgewählte Einträge der aktiven Tabelle"); + mediathekGui.selectedListItemsProperty.addSelectedItemsChangeListener(this); + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + SwingUtilities.invokeLater(() -> { + long items = (long) evt.getNewValue(); + setText(Long.toString(items)); + }); + } +} diff --git a/src/main/java/mediathek/mainwindow/StatusBar.java b/src/main/java/mediathek/mainwindow/StatusBar.java new file mode 100644 index 0000000000..6c407ddc5c --- /dev/null +++ b/src/main/java/mediathek/mainwindow/StatusBar.java @@ -0,0 +1,34 @@ +package mediathek.mainwindow; + +import org.jdesktop.swingx.JXStatusBar; + +import javax.swing.*; +import java.awt.*; + +public class StatusBar extends JComponent { + private final JXStatusBar statusBar = new JXStatusBar(); + + public StatusBar(MediathekGui mediathekGui) { + setLayout(new BorderLayout()); + + add(statusBar, BorderLayout.CENTER); + + statusBar.add(new SelectedListItemsLabel(mediathekGui)); + statusBar.add(new FilmSizeInfoLabel(mediathekGui)); + statusBar.add(new DownloadInformationLabel()); + + statusBar.add(new JPanel(), new JXStatusBar.Constraint(JXStatusBar.Constraint.ResizeBehavior.FILL)); + FilmListCreationDateLabel creationDateLabelSwing = new FilmListCreationDateLabel(); + statusBar.add(creationDateLabelSwing); + FilmAgeLabel ageLabel = new FilmAgeLabel(); + statusBar.add(ageLabel); + /*JProgressBar pbar2 = new JProgressBar(); + pbar2.setValue(92); + jxStatusBar.add(pbar2);*/ + } + + public JXStatusBar getStatusBar() { + return statusBar; + } + +} diff --git a/src/main/java/org/jdesktop/beans/AbstractBean.java b/src/main/java/org/jdesktop/beans/AbstractBean.java new file mode 100644 index 0000000000..2f8d099379 --- /dev/null +++ b/src/main/java/org/jdesktop/beans/AbstractBean.java @@ -0,0 +1,497 @@ +/* + * $Id: AbstractBean.java 4088 2011-11-17 19:53:49Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.beans; + +import java.beans.*; + +/** + *

+ * A convenience class from which to extend all non-visual AbstractBeans. It + * manages the PropertyChange notification system, making it relatively trivial + * to add support for property change events in getters/setters. + *

+ * + *

+ * A non-visual java bean is a Java class that conforms to the AbstractBean + * patterns to allow visual manipulation of the bean's properties and event + * handlers at design-time. + *

+ * + *

+ * Here is a simple example bean that contains one property, foo, and the proper + * pattern for implementing property change notification: + * + *


+ * public class ABean extends AbstractBean {
+ *     private String foo;
+ * 
+ *     public void setFoo(String newFoo) {
+ *         String old = getFoo();
+ *         this.foo = newFoo;
+ *         firePropertyChange("foo", old, getFoo());
+ *     }
+ * 
+ *     public String getFoo() {
+ *         return foo;
+ *     }
+ * }
+ * 
+ * + *

+ * + *

+ * You will notice that "getFoo()" is used in the setFoo method rather than + * accessing "foo" directly for the gets. This is done intentionally so that if + * a subclass overrides getFoo() to return, for instance, a constant value the + * property change notification system will continue to work properly. + *

+ * + *

+ * The firePropertyChange method takes into account the old value and the new + * value. Only if the two differ will it fire a property change event. So you + * can be assured from the above code fragment that a property change event will + * only occur if old is indeed different from getFoo() + *

+ * + *

+ * AbstractBean also supports vetoable + * {@link PropertyChangeEvent} events. These events are similar to + * PropertyChange events, except a special exception can be used + * to veto changing the property. For example, perhaps the property is changing + * from "fred" to "red", but a listener deems that "red" is unexceptable. In + * this case, the listener can fire a veto exception and the property must + * remain "fred". For example: + * + *


+ *  public class ABean extends AbstractBean {
+ *    private String foo;
+ *    
+ *    public void setFoo(String newFoo) throws PropertyVetoException {
+ *      String old = getFoo();
+ *      this.foo = newFoo;
+ *      fireVetoableChange("foo", old, getFoo());
+ *    }
+ *    public String getFoo() {
+ *      return foo;
+ *    }
+ *  }
+ * 
+ *  public class Tester {
+ *    public static void main(String... args) {
+ *      try {
+ *        ABean a = new ABean();
+ *        a.setFoo("fred");
+ *        a.addVetoableChangeListener(new VetoableChangeListener() {
+ *          public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException {
+ *            if ("red".equals(evt.getNewValue()) {
+ *              throw new PropertyVetoException("Cannot be red!", evt);
+ *            }
+ *          }
+ *        }
+ *        a.setFoo("red");
+ *      } catch (Exception e) {
+ *        e.printStackTrace(); // this will be executed
+ *      }
+ *    }
+ *  }
+ * 
+ * + *

+ *

+ * {@code AbstractBean} is not {@link java.io.Serializable}. Special care must + * be taken when creating {@code Serializable} subclasses, as the + * {@code Serializable} listeners will not be saved. Subclasses will need to + * manually save the serializable listeners. The {@link AbstractSerializableBean} + * is {@code Serializable} and already handles the listeners correctly. If + * possible, it is recommended that {@code Serializable} beans should extend + * {@code AbstractSerializableBean}. If it is not possible, the + * {@code AbstractSerializableBean} bean implementation provides details on + * how to correctly serialize an {@code AbstractBean} subclass. + *

+ * + * @see AbstractSerializableBean + * @status REVIEWED + * @author rbair + */ +@SuppressWarnings("nls") +public abstract class AbstractBean { + /** + * Helper class that manages all the property change notification machinery. + * PropertyChangeSupport cannot be extended directly because it requires + * a bean in the constructor, and the "this" argument is not valid until + * after super construction. Hence, delegation instead of extension + */ + private transient PropertyChangeSupport pcs; + + /** + * Helper class that manages all the veto property change notification machinery. + */ + private transient VetoableChangeSupport vcs; + + /** Creates a new instance of AbstractBean */ + protected AbstractBean() { + pcs = new PropertyChangeSupport(this); + vcs = new VetoableChangeSupport(this); + } + + /** + * Creates a new instance of AbstractBean, using the supplied PropertyChangeSupport and + * VetoableChangeSupport delegates. Neither of these may be null. + */ + protected AbstractBean(PropertyChangeSupport pcs, VetoableChangeSupport vcs) { + if (pcs == null) { + throw new NullPointerException("PropertyChangeSupport must not be null"); + } + if (vcs == null) { + throw new NullPointerException("VetoableChangeSupport must not be null"); + } + + this.pcs = pcs; + this.vcs = vcs; + } + + /** + * Add a PropertyChangeListener to the listener list. + * The listener is registered for all properties. + * The same listener object may be added more than once, and will be called + * as many times as it is added. + * If listener is null, no exception is thrown and no action + * is taken. + * + * @param listener The PropertyChangeListener to be added + */ + public final void addPropertyChangeListener(PropertyChangeListener listener) { + pcs.addPropertyChangeListener(listener); + } + + /** + * Remove a PropertyChangeListener from the listener list. + * This removes a PropertyChangeListener that was registered + * for all properties. + * If listener was added more than once to the same event + * source, it will be notified one less time after being removed. + * If listener is null, or was never added, no exception is + * thrown and no action is taken. + * + * @param listener The PropertyChangeListener to be removed + */ + public final void removePropertyChangeListener(PropertyChangeListener listener) { + pcs.removePropertyChangeListener(listener); + } + + /** + * Returns an array of all the listeners that were added to the + * PropertyChangeSupport object with addPropertyChangeListener(). + *

+ * If some listeners have been added with a named property, then + * the returned array will be a mixture of PropertyChangeListeners + * and PropertyChangeListenerProxys. If the calling + * method is interested in distinguishing the listeners then it must + * test each element to see if it's a + * PropertyChangeListenerProxy, perform the cast, and examine + * the parameter. + * + *

+     * PropertyChangeListener[] listeners = bean.getPropertyChangeListeners();
+     * for (int i = 0; i < listeners.length; i++) {
+     *     if (listeners[i] instanceof PropertyChangeListenerProxy) {
+     *     PropertyChangeListenerProxy proxy = 
+     *                    (PropertyChangeListenerProxy)listeners[i];
+     *     if (proxy.getPropertyName().equals("foo")) {
+     *       // proxy is a PropertyChangeListener which was associated
+     *       // with the property named "foo"
+     *     }
+     *   }
+     * }
+     *
+ * + * @see java.beans.PropertyChangeListenerProxy + * @return all of the PropertyChangeListeners added or an + * empty array if no listeners have been added + */ + public final PropertyChangeListener[] getPropertyChangeListeners() { + return pcs.getPropertyChangeListeners(); + } + + /** + * Add a PropertyChangeListener for a specific property. The listener + * will be invoked only when a call on firePropertyChange names that + * specific property. + * The same listener object may be added more than once. For each + * property, the listener will be invoked the number of times it was added + * for that property. + * If propertyName or listener is null, no + * exception is thrown and no action is taken. + * + * @param propertyName The name of the property to listen on. + * @param listener The PropertyChangeListener to be added + */ + public final void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { + pcs.addPropertyChangeListener(propertyName, listener); + } + + /** + * Remove a PropertyChangeListener for a specific property. + * If listener was added more than once to the same event + * source for the specified property, it will be notified one less time + * after being removed. + * If propertyName is null, no exception is thrown and no + * action is taken. + * If listener is null, or was never added for the specified + * property, no exception is thrown and no action is taken. + * + * @param propertyName The name of the property that was listened on. + * @param listener The PropertyChangeListener to be removed + */ + public final void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { + pcs.removePropertyChangeListener(propertyName, listener); + } + + /** + * Returns an array of all the listeners which have been associated + * with the named property. + * + * @param propertyName The name of the property being listened to + * @return all of the PropertyChangeListeners associated with + * the named property. If no such listeners have been added, + * or if propertyName is null, an empty array is + * returned. + */ + public final PropertyChangeListener[] getPropertyChangeListeners(String propertyName) { + return pcs.getPropertyChangeListeners(propertyName); + } + + /** + * Report a bound property update to any registered listeners. + * No event is fired if old and new are equal and non-null. + * + *

+ * This is merely a convenience wrapper around the more general + * firePropertyChange method that takes {@code + * PropertyChangeEvent} value. + * + * @param propertyName The programmatic name of the property + * that was changed. + * @param oldValue The old value of the property. + * @param newValue The new value of the property. + */ + protected final void firePropertyChange(String propertyName, Object oldValue, Object newValue) { + pcs.firePropertyChange(propertyName, oldValue, newValue); + } + + /** + * Fire an existing PropertyChangeEvent to any registered listeners. + * No event is fired if the given event's old and new values are + * equal and non-null. + * @param evt The PropertyChangeEvent object. + */ + protected final void firePropertyChange(PropertyChangeEvent evt) { + pcs.firePropertyChange(evt); + } + + + /** + * Report a bound indexed property update to any registered + * listeners. + *

+ * No event is fired if old and new values are equal + * and non-null. + * + *

+ * This is merely a convenience wrapper around the more general + * firePropertyChange method that takes {@code PropertyChangeEvent} value. + * + * @param propertyName The programmatic name of the property that + * was changed. + * @param index index of the property element that was changed. + * @param oldValue The old value of the property. + * @param newValue The new value of the property. + */ + protected final void fireIndexedPropertyChange(String propertyName, int index, + Object oldValue, Object newValue) { + pcs.fireIndexedPropertyChange(propertyName, index, oldValue, newValue); + } + + /** + * Check if there are any listeners for a specific property, including + * those registered on all properties. If propertyName + * is null, only check for listeners registered on all properties. + * + * @param propertyName the property name. + * @return true if there are one or more listeners for the given property + */ + protected final boolean hasPropertyChangeListeners(String propertyName) { + return pcs.hasListeners(propertyName); + } + + /** + * Check if there are any listeners for a specific property, including + * those registered on all properties. If propertyName + * is null, only check for listeners registered on all properties. + * + * @param propertyName the property name. + * @return true if there are one or more listeners for the given property + */ + protected final boolean hasVetoableChangeListeners(String propertyName) { + return vcs.hasListeners(propertyName); + } + + /** + * Add a VetoableListener to the listener list. + * The listener is registered for all properties. + * The same listener object may be added more than once, and will be called + * as many times as it is added. + * If listener is null, no exception is thrown and no action + * is taken. + * + * @param listener The VetoableChangeListener to be added + */ + + public final void addVetoableChangeListener(VetoableChangeListener listener) { + vcs.addVetoableChangeListener(listener); + } + + /** + * Remove a VetoableChangeListener from the listener list. + * This removes a VetoableChangeListener that was registered + * for all properties. + * If listener was added more than once to the same event + * source, it will be notified one less time after being removed. + * If listener is null, or was never added, no exception is + * thrown and no action is taken. + * + * @param listener The VetoableChangeListener to be removed + */ + public final void removeVetoableChangeListener(VetoableChangeListener listener) { + vcs.removeVetoableChangeListener(listener); + } + + /** + * Returns the list of VetoableChangeListeners. If named vetoable change listeners + * were added, then VetoableChangeListenerProxy wrappers will returned + *

+ * @return List of VetoableChangeListeners and VetoableChangeListenerProxys + * if named property change listeners were added. + */ + public final VetoableChangeListener[] getVetoableChangeListeners(){ + return vcs.getVetoableChangeListeners(); + } + + /** + * Add a VetoableChangeListener for a specific property. The listener + * will be invoked only when a call on fireVetoableChange names that + * specific property. + * The same listener object may be added more than once. For each + * property, the listener will be invoked the number of times it was added + * for that property. + * If propertyName or listener is null, no + * exception is thrown and no action is taken. + * + * @param propertyName The name of the property to listen on. + * @param listener The VetoableChangeListener to be added + */ + + public final void addVetoableChangeListener(String propertyName, + VetoableChangeListener listener) { + vcs.addVetoableChangeListener(propertyName, listener); + } + + /** + * Remove a VetoableChangeListener for a specific property. + * If listener was added more than once to the same event + * source for the specified property, it will be notified one less time + * after being removed. + * If propertyName is null, no exception is thrown and no + * action is taken. + * If listener is null, or was never added for the specified + * property, no exception is thrown and no action is taken. + * + * @param propertyName The name of the property that was listened on. + * @param listener The VetoableChangeListener to be removed + */ + + public final void removeVetoableChangeListener(String propertyName, + VetoableChangeListener listener) { + vcs.removeVetoableChangeListener(propertyName, listener); + } + + /** + * Returns an array of all the listeners which have been associated + * with the named property. + * + * @param propertyName The name of the property being listened to + * @return all the VetoableChangeListeners associated with + * the named property. If no such listeners have been added, + * or if propertyName is null, an empty array is + * returned. + */ + public final VetoableChangeListener[] getVetoableChangeListeners(String propertyName) { + return vcs.getVetoableChangeListeners(propertyName); + } + + /** + * Report a vetoable property update to any registered listeners. If + * anyone vetos the change, then fire a new event reverting everyone to + * the old value and then rethrow the PropertyVetoException. + *

+ * No event is fired if old and new are equal and non-null. + * + * @param propertyName The programmatic name of the property + * that is about to change.. + * @param oldValue The old value of the property. + * @param newValue The new value of the property. + * @exception PropertyVetoException if the recipient wishes the property + * change to be rolled back. + */ + protected final void fireVetoableChange(String propertyName, + Object oldValue, Object newValue) + throws PropertyVetoException { + vcs.fireVetoableChange(propertyName, oldValue, newValue); + } + + /** + * Fire a vetoable property update to any registered listeners. If + * anyone vetos the change, then fire a new event reverting everyone to + * the old value and then rethrow the PropertyVetoException. + *

+ * No event is fired if old and new are equal and non-null. + * + * @param evt The PropertyChangeEvent to be fired. + * @exception PropertyVetoException if the recipient wishes the property + * change to be rolled back. + */ + protected final void fireVetoableChange(PropertyChangeEvent evt) + throws PropertyVetoException { + vcs.fireVetoableChange(evt); + } + + /** + * {@inheritDoc} + */ + @Override + public Object clone() throws CloneNotSupportedException { + AbstractBean result = (AbstractBean) super.clone(); + result.pcs = new PropertyChangeSupport(result); + result.vcs = new VetoableChangeSupport(result); + return result; + } +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/beans/AbstractSerializableBean.java b/src/main/java/org/jdesktop/beans/AbstractSerializableBean.java new file mode 100644 index 0000000000..31c6c14a9e --- /dev/null +++ b/src/main/java/org/jdesktop/beans/AbstractSerializableBean.java @@ -0,0 +1,104 @@ +/* + * $Id: AbstractSerializableBean.java 4088 2011-11-17 19:53:49Z kschaefe $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.beans; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.beans.VetoableChangeListener; +import java.beans.VetoableChangeSupport; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +/** + * This subclass enhances {@code AbstractBean} by implementing the + * {@code Serializable} interface. {@code AbstractSerializableBean} correctly + * serializes all {@code Serializable} listeners that it contains. Implementors + * that need to extends {@code AbstractBean} or one of its subclasses and + * require serialization should use this class if possible. If it is not + * possible to extend this class, the implementation can guide implementors on + * how to properly serialize the listeners. + * + * @author Karl George Schaefer + * + * @see Serializable + * @see ObjectInputStream + * @see ObjectOutputStream + */ +@SuppressWarnings("serial") +public abstract class AbstractSerializableBean extends AbstractBean implements + Serializable { + /** + * Creates a new instance of {@code AbstractSerializableBean}. + */ + protected AbstractSerializableBean() { + super(); + } + + /** + * Creates a new instance of {@code AbstractSerializableBean}, using the + * supplied support delegates. Neither of these may be {@code null}. + * + * @param pcs + * the property change support class to use + * @param vcs + * the vetoable change support class to use + * @throws NullPointerException + * if any parameter is {@code null} + */ + protected AbstractSerializableBean(PropertyChangeSupport pcs, + VetoableChangeSupport vcs) { + super(pcs, vcs); + } + + private void writeObject(ObjectOutputStream s) throws IOException { + s.defaultWriteObject(); + + for (PropertyChangeListener l : getPropertyChangeListeners()) { + if (l instanceof Serializable) { + s.writeObject(l); + } + } + + for (VetoableChangeListener l : getVetoableChangeListeners()) { + if (l instanceof Serializable) { + s.writeObject(l); + } + } + + s.writeObject(null); + } + + private void readObject(ObjectInputStream s) throws ClassNotFoundException, + IOException { + s.defaultReadObject(); + + Object listenerOrNull; + while (null != (listenerOrNull = s.readObject())) { + if (listenerOrNull instanceof PropertyChangeListener) { + addPropertyChangeListener((PropertyChangeListener) listenerOrNull); + } else if (listenerOrNull instanceof VetoableChangeListener) { + addVetoableChangeListener((VetoableChangeListener) listenerOrNull); + } + } + } +} diff --git a/src/main/java/org/jdesktop/beans/package-info.java b/src/main/java/org/jdesktop/beans/package-info.java new file mode 100644 index 0000000000..f42ceaf8ea --- /dev/null +++ b/src/main/java/org/jdesktop/beans/package-info.java @@ -0,0 +1,26 @@ +/* + * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +/** + * Contains classes to extend the functionality defined in the + * {@code java.beans} package. + */ +package org.jdesktop.beans; + diff --git a/src/main/java/org/jdesktop/swingx/AbstractLayoutManager.kt b/src/main/java/org/jdesktop/swingx/AbstractLayoutManager.kt new file mode 100644 index 0000000000..4de450edfd --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/AbstractLayoutManager.kt @@ -0,0 +1,51 @@ +package org.jdesktop.swingx + +import java.awt.Component +import java.awt.Container +import java.awt.Dimension +import java.awt.LayoutManager +import java.io.Serial +import java.io.Serializable + +/** + * A simple abstract class to handle common layout implementations. Package-private as we do NOT + * want to export this as part of the public API. + * + * @author kschaefer + */ +abstract class AbstractLayoutManager : LayoutManager, Serializable { + /** + * {@inheritDoc} + * + * + * This implementation does nothing. + */ + override fun addLayoutComponent(name: String, comp: Component) { + //does nothing + } + + /** + * {@inheritDoc} + * + * + * This implementation does nothing. + */ + override fun removeLayoutComponent(comp: Component) { + // does nothing + } + + /** + * {@inheritDoc} + * + * + * This implementation defers to [.preferredLayoutSize]. + */ + override fun minimumLayoutSize(parent: Container): Dimension { + return preferredLayoutSize(parent) + } + + companion object { + @Serial + private val serialVersionUID = 1446292747820044161L + } +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/AbstractPatternPanel.java b/src/main/java/org/jdesktop/swingx/AbstractPatternPanel.java new file mode 100644 index 0000000000..779b8a2af2 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/AbstractPatternPanel.java @@ -0,0 +1,443 @@ +/* + * $Id: AbstractPatternPanel.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx; + +import org.jdesktop.swingx.action.AbstractActionExt; +import org.jdesktop.swingx.action.ActionContainerFactory; +import org.jdesktop.swingx.action.BoundAction; +import org.jdesktop.swingx.plaf.LookAndFeelAddons; +import org.jdesktop.swingx.plaf.UIManagerExt; +import org.jdesktop.swingx.search.PatternModel; + +import javax.swing.*; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import java.awt.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.Locale; + +/** + * Common base class of ui clients. + * + * Implements basic synchronization between PatternModel state and + * actions bound to it. + * + * + * + * PENDING: extending JXPanel is a convenience measure, should be extracted + * into a dedicated controller. + * PENDING: should be re-visited when swingx goes binding-aware + * + * @author Jeanette Winzenburg + */ +public abstract class AbstractPatternPanel extends JXPanel { + + public static final String SEARCH_FIELD_LABEL = "searchFieldLabel"; + public static final String SEARCH_FIELD_MNEMONIC = SEARCH_FIELD_LABEL + ".mnemonic"; + public static final String SEARCH_TITLE = "searchTitle"; + public static final String MATCH_ACTION_COMMAND = "match"; + + static { + // Hack to enforce loading of SwingX framework ResourceBundle + LookAndFeelAddons.getAddon(); + } + + protected JLabel searchLabel; + protected JTextField searchField; + protected JCheckBox matchCheck; + + protected PatternModel patternModel; + private ActionContainerFactory actionFactory; + + +//------------------------ actions + + /** + * Callback action bound to MATCH_ACTION_COMMAND. + */ + public abstract void match(); + + /** + * convenience method for type-cast to AbstractActionExt. + * + * @param key Key to retrieve action + * @return Action bound to this key + * @see AbstractActionExt + */ + protected AbstractActionExt getAction(String key) { + // PENDING: outside clients might add different types? + return (AbstractActionExt) getActionMap().get(key); + } + + /** + * creates and registers all actions for the default the actionMap. + */ + protected void initActions() { + initPatternActions(); + initExecutables(); + } + + /** + * creates and registers all "executable" actions. + * Meaning: the actions bound to a callback method on this. + * + * PENDING: not quite correctly factored? Name? + * + */ + protected void initExecutables() { + Action execute = createBoundAction(MATCH_ACTION_COMMAND, "match"); + getActionMap().put(JXDialog.EXECUTE_ACTION_COMMAND, + execute); + getActionMap().put(MATCH_ACTION_COMMAND, execute); + refreshEmptyFromModel(); + } + + /** + * creates actions bound to PatternModel's state. + */ + protected void initPatternActions() { + ActionMap map = getActionMap(); + map.put(PatternModel.MATCH_CASE_ACTION_COMMAND, + createModelStateAction(PatternModel.MATCH_CASE_ACTION_COMMAND, + "setCaseSensitive", getPatternModel().isCaseSensitive())); + map.put(PatternModel.MATCH_WRAP_ACTION_COMMAND, + createModelStateAction(PatternModel.MATCH_WRAP_ACTION_COMMAND, + "setWrapping", getPatternModel().isWrapping())); + map.put(PatternModel.MATCH_BACKWARDS_ACTION_COMMAND, + createModelStateAction(PatternModel.MATCH_BACKWARDS_ACTION_COMMAND, + "setBackwards", getPatternModel().isBackwards())); + map.put(PatternModel.MATCH_INCREMENTAL_ACTION_COMMAND, + createModelStateAction(PatternModel.MATCH_INCREMENTAL_ACTION_COMMAND, + "setIncremental", getPatternModel().isIncremental())); + } + + /** + * Returns a potentially localized value from the UIManager. The given key + * is prefixed by this component|s UIPREFIX before doing the + * lookup. The lookup respects this table's current locale + * property. Returns the key, if no value is found. + * + * @param key the bare key to look up in the UIManager. + * @return the value mapped to UIPREFIX + key or key if no value is found. + */ + protected String getUIString(String key) { + return getUIString(key, getLocale()); + } + + /** + * Returns a potentially localized value from the UIManager for the + * given locale. The given key + * is prefixed by this component's UIPREFIX before doing the + * lookup. Returns the key, if no value is found. + * + * @param key the bare key to look up in the UIManager. + * @param locale the locale use for lookup + * @return the value mapped to UIPREFIX + key in the given locale, + * or key if no value is found. + */ + protected String getUIString(String key, Locale locale) { + String text = UIManagerExt.getString(PatternModel.SEARCH_PREFIX + key, locale); + return text != null ? text : key; + } + + + /** + * creates, configures and returns a bound state action on a boolean property + * of the PatternModel. + * + * @param command the actionCommand - same as key to find localizable resources + * @param methodName the method on the PatternModel to call on item state changed + * @param initial the initial value of the property + * @return newly created action + */ + protected AbstractActionExt createModelStateAction(String command, String methodName, boolean initial) { + String actionName = getUIString(command); + BoundAction action = new BoundAction(actionName, + command); + action.setStateAction(); + action.registerCallback(getPatternModel(), methodName); + action.setSelected(initial); + return action; + } + + /** + * creates, configures and returns a bound action to the given method of + * this. + * + * @param actionCommand the actionCommand, same as key to find localizable resources + * @param methodName the method to call an actionPerformed. + * @return newly created action + */ + protected AbstractActionExt createBoundAction(String actionCommand, String methodName) { + String actionName = getUIString(actionCommand); + BoundAction action = new BoundAction(actionName, + actionCommand); + action.registerCallback(this, methodName); + return action; + } + +//------------------------ dynamic locale support + + + /** + * {@inheritDoc}

+ * Overridden to update locale-dependent properties. + * + * @see #updateLocaleState(Locale) + */ + @Override + public void setLocale(Locale l) { + updateLocaleState(l); + super.setLocale(l); + } + + /** + * Updates locale-dependent state. + * + * Here: updates registered column actions' locale-dependent state. + *

+ * + * PENDING: Try better to find all column actions including custom + * additions? Or move to columnControl? + * + * @see #setLocale(Locale) + */ + protected void updateLocaleState(Locale locale) { + for (Object key : getActionMap().allKeys()) { + if (key instanceof String) { + String keyString = getUIString((String) key, locale); + if (!key.equals(keyString)) { + getActionMap().get(key).putValue(Action.NAME, keyString); + + } + } + } + bindSearchLabel(locale); + } + + + //---------------------- synch patternModel <--> components + + /** + * called from listening to pattern property of PatternModel. + * + * This implementation calls match() if the model is in + * incremental state. + * + */ + protected void refreshPatternFromModel() { + if (getPatternModel().isIncremental()) { + match(); + } + } + + + /** + * returns the patternModel. Lazyly creates and registers a + * propertyChangeListener if null. + * + * @return current PatternModel if it exists or newly created + * one if it was not initialized before this call + */ + protected PatternModel getPatternModel() { + if (patternModel == null) { + patternModel = createPatternModel(); + patternModel.addPropertyChangeListener(getPatternModelListener()); + } + return patternModel; + } + + + /** + * factory method to create the PatternModel. + * Hook for subclasses to install custom models. + * + * @return newly created PatternModel + */ + protected PatternModel createPatternModel() { + return new PatternModel(); + } + + /** + * creates and returns a PropertyChangeListener to the PatternModel. + * + * NOTE: the patternModel is totally under control of this class - currently + * there's no need to keep a reference to the listener. + * + * @return created and bound to appropriate callback methods + * PropertyChangeListener + */ + protected PropertyChangeListener getPatternModelListener() { + return new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + String property = evt.getPropertyName(); + if ("pattern".equals(property)) { + refreshPatternFromModel(); + } else if ("rawText".equals(property)) { + refreshDocumentFromModel(); + } else if ("caseSensitive".equals(property)){ + getAction(PatternModel.MATCH_CASE_ACTION_COMMAND). + setSelected(((Boolean) evt.getNewValue()).booleanValue()); + } else if ("wrapping".equals(property)) { + getAction(PatternModel.MATCH_WRAP_ACTION_COMMAND). + setSelected(((Boolean) evt.getNewValue()).booleanValue()); + } else if ("backwards".equals(property)) { + getAction(PatternModel.MATCH_BACKWARDS_ACTION_COMMAND). + setSelected(((Boolean) evt.getNewValue()).booleanValue()); + } else if ("incremental".equals(property)) { + getAction(PatternModel.MATCH_INCREMENTAL_ACTION_COMMAND). + setSelected(((Boolean) evt.getNewValue()).booleanValue()); + + } else if ("empty".equals(property)) { + refreshEmptyFromModel(); + } + + } + + }; + } + + /** + * called from listening to empty property of PatternModel. + * + * this implementation synch's the enabled state of the action with + * MATCH_ACTION_COMMAND to !empty. + * + */ + protected void refreshEmptyFromModel() { + boolean enabled = !getPatternModel().isEmpty(); + getAction(MATCH_ACTION_COMMAND).setEnabled(enabled); + + } + + /** + * callback method from listening to searchField. + * + */ + protected void refreshModelFromDocument() { + getPatternModel().setRawText(searchField.getText()); + } + + /** + * callback method that updates document from the search field + * + */ + protected void refreshDocumentFromModel() { + if (searchField.getText().equals(getPatternModel().getRawText())) return; + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + searchField.setText(getPatternModel().getRawText()); + } + }); + } + + /** + * Create DocumentListener for the search field that calls + * corresponding callback method whenever the search field contents is being changed + * + * @return newly created DocumentListener + */ + protected DocumentListener getSearchFieldListener() { + return new DocumentListener() { + @Override + public void changedUpdate(DocumentEvent ev) { + // JW - really?? we've a PlainDoc without Attributes + refreshModelFromDocument(); + } + + @Override + public void insertUpdate(DocumentEvent ev) { + refreshModelFromDocument(); + } + + @Override + public void removeUpdate(DocumentEvent ev) { + refreshModelFromDocument(); + } + + }; + } + +//-------------------------- config helpers + + /** + * configure and bind components to/from PatternModel + */ + protected void bind() { + bindSearchLabel(getLocale()); + searchField.getDocument().addDocumentListener(getSearchFieldListener()); + getActionContainerFactory().configureButton(matchCheck, + (AbstractActionExt) getActionMap().get(PatternModel.MATCH_CASE_ACTION_COMMAND), + null); + + } + + /** + * Configures the searchLabel. + * Here: sets text and mnenomic properties form ui values, + * configures as label for searchField. + */ + protected void bindSearchLabel(Locale locale) { + searchLabel.setText(getUIString(SEARCH_FIELD_LABEL, locale)); + String mnemonic = getUIString(SEARCH_FIELD_MNEMONIC, locale); + if (mnemonic != SEARCH_FIELD_MNEMONIC) { + searchLabel.setDisplayedMnemonic(mnemonic.charAt(0)); + } + searchLabel.setLabelFor(searchField); + } + + /** + * @return current ActionContainerFactory. + * Will lazily create new factory if it does not exist + */ + protected ActionContainerFactory getActionContainerFactory() { + if (actionFactory == null) { + actionFactory = new ActionContainerFactory(null); + } + return actionFactory; + } + + /** + * Initialize all the incorporated components and models + */ + protected void initComponents() { + searchLabel = new JLabel(); + searchField = new JTextField(getSearchFieldWidth()) { + @Override + public Dimension getMaximumSize() { + Dimension superMax = super.getMaximumSize(); + superMax.height = getPreferredSize().height; + return superMax; + } + }; + matchCheck = new JCheckBox(); + } + + /** + * @return width in characters of the search field + */ + protected int getSearchFieldWidth() { + return 15; + } +} diff --git a/src/main/java/org/jdesktop/swingx/AlphaPaintable.java b/src/main/java/org/jdesktop/swingx/AlphaPaintable.java new file mode 100644 index 0000000000..a24e4e7e2a --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/AlphaPaintable.java @@ -0,0 +1,58 @@ +package org.jdesktop.swingx; + +/** + * An interface to describe an object that is capable of painting with an alpha value. + * + * @author kschaefer + */ +interface AlphaPaintable { + /** + * Get the current alpha value. + * + * @return the alpha translucency level for this component. This will be a value between 0 and + * 1, inclusive. + */ + float getAlpha(); + + /** + * Set the alpha transparency level for this component. This automatically causes a repaint of + * the component. + * + * @param alpha + * must be a value between 0 and 1 inclusive + * @throws IllegalArgumentException + * if the value is invalid + */ + void setAlpha(float alpha); + + /** + * Returns the state of the panel with respect to inheriting alpha values. + * + * @return {@code true} if this panel inherits alpha values; {@code false} + * otherwise + * @see #setInheritAlpha(boolean) + */ + boolean isInheritAlpha(); + + /** + * Determines if the effective alpha of this component should include the + * alpha of ancestors. + * + * @param inheritAlpha + * {@code true} to include ancestral alpha data; {@code false} + * otherwise + * @see #isInheritAlpha() + * @see #getEffectiveAlpha() + */ + void setInheritAlpha(boolean inheritAlpha); + + /** + * Unlike other properties, alpha can be set on a component, or on one of + * its parents. If the alpha of a parent component is .4, and the alpha on + * this component is .5, effectively the alpha for this component is .4 + * because the lowest alpha in the hierarchy "wins." + * + * @return the lowest alpha value in the hierarchy + */ + float getEffectiveAlpha(); +} diff --git a/src/main/java/org/jdesktop/swingx/BackgroundPaintable.java b/src/main/java/org/jdesktop/swingx/BackgroundPaintable.java new file mode 100644 index 0000000000..2ec3596db9 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/BackgroundPaintable.java @@ -0,0 +1,60 @@ +/* + * $Id: BackgroundPaintable.java 4188 2012-06-27 14:21:10Z kschaefe $ + * + * Copyright 2010 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx; + +import org.jdesktop.swingx.painter.Painter; + +/** + * An interface to define the common methods that are required for defining a background painter. + * + * @author kschaefer + */ +@SuppressWarnings("rawtypes") +public interface BackgroundPaintable { + /** + * Returns the current background painter. + * + * @return the current painter + * @see #setBackgroundPainter(Painter) + * @see #isPaintBorderInsets() + */ + Painter getBackgroundPainter(); + + /** + * Sets the new background painter. + * + * @param painter the new background painter; may be {@code null} + */ + void setBackgroundPainter(Painter painter); + + /** + * Determines whether this component paints its background paint underneath the border. + * + * @return {@code true} to paint under the border; {@code false} otherwise + */ + boolean isPaintBorderInsets(); + + /** + * + * @param paintBorderInsets + */ + void setPaintBorderInsets(boolean paintBorderInsets); +} diff --git a/src/main/java/org/jdesktop/swingx/ForwardingRepaintManager.java b/src/main/java/org/jdesktop/swingx/ForwardingRepaintManager.java new file mode 100644 index 0000000000..d85579518e --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/ForwardingRepaintManager.java @@ -0,0 +1,214 @@ +/* + * $Id: ForwardingRepaintManager.java 3690 2010-05-03 17:55:44Z kschaefe $ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx; + +import org.jdesktop.swingx.util.Contract; + +import javax.swing.*; +import java.applet.Applet; +import java.awt.*; + +/** + * A {@code RepaintManager} that is designed to forward all calls to a contained + * delegate. This class is designed for extension, such that subclasses should + * override method as appropriate and allow the original repaint manager to + * handle the rest of the work. + *

+ * Install a forwarding repaint manager: + * + *

+ * RepaintManager manager = RepaintManager.currentManager(this);
+ * RepaintManager frm = new ForwardingRepaintManager(manager);
+ * RepaintManager.setCurrentManager(frm);
+ * 
+ * + * @author Karl George Schaefer + * @author pietblok (original facade/delegate idea) + */ +public class ForwardingRepaintManager extends RepaintManager { + private RepaintManager delegate; + + /** + * Creates a new forwarding manager that forwards all calls to the delegate. + * + * @param delegate + * the manager backing this {@code ForwardingRepaintManager} + * @throws NullPointerException + * if {@code delegate} is {@code null} + */ + public ForwardingRepaintManager(RepaintManager delegate) { + this.delegate = Contract.asNotNull(delegate, "delegate is null"); + } + + /** + * {@inheritDoc} + */ + @Override + public void addDirtyRegion(Applet applet, int x, int y, int w, int h) { + delegate.addDirtyRegion(applet, x, y, w, h); + } + + /** + * {@inheritDoc} + */ + @Override + public void addDirtyRegion(JComponent c, int x, int y, int w, int h) { + delegate.addDirtyRegion(c, x, y, w, h); + } + + /** + * {@inheritDoc} + */ + @Override + public void addDirtyRegion(Window window, int x, int y, int w, int h) { + delegate.addDirtyRegion(window, x, y, w, h); + } + + /** + * {@inheritDoc} + */ + @Override + public synchronized void addInvalidComponent(JComponent invalidComponent) { + delegate.addInvalidComponent(invalidComponent); + } + + /** + * {@inheritDoc} + */ + @Override + public Rectangle getDirtyRegion(JComponent component) { + return delegate.getDirtyRegion(component); + } + + /** + * {@inheritDoc} + */ + @Override + public Dimension getDoubleBufferMaximumSize() { + return delegate.getDoubleBufferMaximumSize(); + } + + /** + * {@inheritDoc} + */ + @Override + public Image getOffscreenBuffer(Component c, int proposedWidth, int proposedHeight) { + return delegate.getOffscreenBuffer(c, proposedWidth, proposedHeight); + } + + /** + * {@inheritDoc} + */ + @Override + public Image getVolatileOffscreenBuffer(Component c, int proposedWidth, int proposedHeight) { + return delegate.getVolatileOffscreenBuffer(c, proposedWidth, proposedHeight); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isCompletelyDirty(JComponent component) { + return delegate.isCompletelyDirty(component); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isDoubleBufferingEnabled() { + return delegate.isDoubleBufferingEnabled(); + } + + /** + * {@inheritDoc} + */ + @Override + public void markCompletelyClean(JComponent component) { + delegate.markCompletelyClean(component); + } + + /** + * {@inheritDoc} + */ + @Override + public void markCompletelyDirty(JComponent component) { + delegate.markCompletelyDirty(component); + } + + /** + * {@inheritDoc} + */ + @Override + public void paintDirtyRegions() { + delegate.paintDirtyRegions(); + } + + /** + * {@inheritDoc} + */ + @Override + public synchronized void removeInvalidComponent(JComponent component) { + delegate.removeInvalidComponent(component); + } + + /** + * {@inheritDoc} + */ + @Override + public void setDoubleBufferingEnabled(boolean flag) { + delegate.setDoubleBufferingEnabled(flag); + } + + /** + * {@inheritDoc} + */ + @Override + public void setDoubleBufferMaximumSize(Dimension d) { + delegate.setDoubleBufferMaximumSize(d); + } + + /** + * {@inheritDoc} + */ + @Override + public synchronized String toString() { + return delegate.toString(); + } + + /** + * {@inheritDoc} + */ + @Override + public void validateInvalidComponents() { + delegate.validateInvalidComponents(); + } + + /** + * Gets the delegate repaint manager backing this forwarding repaint + * manager. + * + * @return the delegate for this forwarding manager + */ + public final RepaintManager getDelegateManager() { + return delegate; + } +} diff --git a/src/main/java/org/jdesktop/swingx/HorizontalLayout.java b/src/main/java/org/jdesktop/swingx/HorizontalLayout.java new file mode 100644 index 0000000000..bc6f1ecf79 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/HorizontalLayout.java @@ -0,0 +1,98 @@ +/* + * $Id: HorizontalLayout.java 4147 2012-02-01 17:13:24Z kschaefe $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.util.Separator; + +import java.awt.*; + +/** + * Organizes components in a horizontal layout. + * + * @author Romain Guy + * @author Karl Schaefer + */ +@JavaBean +public class HorizontalLayout extends AbstractLayoutManager { + private static final long serialVersionUID = 8640046926840737487L; + + private int gap; + + public HorizontalLayout() { + this(0); + } + + //TODO should we allow negative gaps? + public HorizontalLayout(int gap) { + this.gap = gap; + } + + public int getGap() { + return gap; + } + + //TODO should we allow negative gaps? + public void setGap(int gap) { + this.gap = gap; + } + + @Override + public void layoutContainer(Container parent) { + Insets insets = parent.getInsets(); + Dimension size = parent.getSize(); + + int height = size.height - insets.top - insets.bottom; + int width = insets.left; + + for (int i = 0, c = parent.getComponentCount(); i < c; i++) { + Component m = parent.getComponent(i); + + if (m.isVisible()) { + m.setBounds(width, insets.top, m.getPreferredSize().width, height); + width += m.getSize().width + gap; + } + } + } + + @Override + public Dimension preferredLayoutSize(Container parent) { + Dimension pref = new Dimension(0, 0); + Separator sep = new Separator(0, gap); + + for (int i = 0, c = parent.getComponentCount(); i < c; i++) { + Component m = parent.getComponent(i); + if (m.isVisible()) { + Dimension componentPreferredSize = + parent.getComponent(i).getPreferredSize(); + pref.height = Math.max(pref.height, componentPreferredSize.height); + pref.width += componentPreferredSize.width + sep.get(); + } + } + + Insets insets = parent.getInsets(); + pref.width += insets.left + insets.right; + pref.height += insets.top + insets.bottom; + + return pref; + } +} diff --git a/src/main/java/org/jdesktop/swingx/JXBusyLabel.java b/src/main/java/org/jdesktop/swingx/JXBusyLabel.java new file mode 100644 index 0000000000..5c7cd79b81 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXBusyLabel.java @@ -0,0 +1,369 @@ +/* + * $Id: JXBusyLabel.java 4147 2012-02-01 17:13:24Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.icon.PainterIcon; +import org.jdesktop.swingx.painter.BusyPainter; +import org.jdesktop.swingx.plaf.BusyLabelAddon; +import org.jdesktop.swingx.plaf.BusyLabelUI; +import org.jdesktop.swingx.plaf.LookAndFeelAddons; + +import javax.swing.*; +import javax.swing.plaf.LabelUI; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/** + *

A simple circular animation, useful for denoting an action is taking + * place that may take an unknown length of time to complete. Similar to an + * indeterminant JProgressBar, but with a different look.

+ * + *

For example: + *


+ *     JXFrame frame = new JXFrame("test", true);
+ *     JXBusyLabel label = new JXBusyLabel();
+ *     frame.add(label);
+ *     //...
+ *     label.setBusy(true);
+ * 

+ * Another more complicated example: + *

+ * JXBusyLabel label = new JXBusyLabel(new Dimension(100,84));
+ * BusyPainter painter = new BusyPainter(
+ * new Rectangle2D.Float(0, 0,13.500001f,1),
+ * new RoundRectangle2D.Float(12.5f,12.5f,59.0f,59.0f,10,10));
+ * painter.setTrailLength(5);
+ * painter.setPoints(31);
+ * painter.setFrame(1);
+ * label.setPreferredSize(new Dimension(100,84));
+ * label.setIcon(new EmptyIcon(100,84));
+ * label.setBusyPainter(painter);
+ *
+ * + * Another example: + *

+ *     JXBusyLabel label = new MyBusyLabel(new Dimension(100, 84));
+ * 
+ * + * where MyBusyLabel is:
+ *

+ * public class MyBusyLabel extends JXBusyLabel {
+ *     public MyBusyLabel(Dimension prefSize) {
+ *         super(prefSize);
+ *     }
+ *     
+ *     protected BusyLabel createBusyLabel(Dimension dim) {
+ *         BusyPainter painter = new BusyPainter(
+ *         new Rectangle2D.Float(0, 0,13.500001f,1),
+ *         new RoundRectangle2D.Float(12.5f,12.5f,59.0f,59.0f,10,10));
+ *         painter.setTrailLength(5);
+ *         painter.setPoints(31);
+ *         painter.setFrame(1);
+ *         
+ *         return painter;
+ *     }
+ * }
+ * 
+ * + * @author rbair + * @author joshy + * @author rah003 + * @author headw01 + */ +@JavaBean +public class JXBusyLabel extends JLabel { + + private static final long serialVersionUID = 5979268460848257147L; + private BusyPainter busyPainter; + private Timer busy; + private int delay; + /** Status flag to save/restore status of timer when moving component between containers. */ + private boolean wasBusyOnNotify = false; + + /** + * UI Class ID + */ + public final static String uiClassID = "BusyLabelUI"; + + /** + * Sets direction of rotation. Direction.RIGHT is the default + * value. Direction is taken from the very top point so Direction.RIGHT enables rotation clockwise. + * @param dir Direction of rotation. + */ + public void setDirection(BusyPainter.Direction dir) { + direction = dir; + getBusyPainter().setDirection(dir); + } + + private BusyPainter.Direction direction; + + /** + * Creates a default JXLoginPane instance + */ + static { + LookAndFeelAddons.contribute(new BusyLabelAddon()); + } + + { + // Initialize the delay from the UI class. + BusyLabelUI ui = (BusyLabelUI)getUI(); + if (ui != null) { + delay = ui.getDelay(); + } + } + + /** Creates a new instance of JXBusyLabel initialized to circular shape in bounds of 26 by 26 points.*/ + public JXBusyLabel() { + this(null); + } + + /** + * Creates a new instance of JXBusyLabel initialized to the arbitrary size and using default circular progress indicator. + * @param dim Preferred size of the label. + */ + public JXBusyLabel(Dimension dim) { + super(); + this.setPreferredSize(dim); + + // Initialize the BusyPainter. + getBusyPainter(); + } + + /** + * Initialize the BusyPainter and (this) JXBusyLabel with the given + * preferred size. This method is called automatically when the + * BusyPainter is set/changed. + * + * @param dim The new Preferred Size for the BusyLabel. + * + * @see #getBusyPainter() + * @see #setBusyPainter(BusyPainter) + */ + protected void initPainter(Dimension dim) { + BusyPainter busyPainter = getBusyPainter(); + + // headw01 + // TODO: Should we force the busyPainter to NOT be cached? + // I think we probably should, otherwise the UI will never + // be updated after the first paint. + if (null != busyPainter) { + busyPainter.setCacheable(false); + } + + PainterIcon icon = new PainterIcon(dim); + icon.setPainter(busyPainter); + this.setIcon(icon); + } + /** + * Create and return a BusyPpainter to use for the Label. This may + * be overridden to return any painter you like. By default, this + * method uses the UI (BusyLabelUI)to create a BusyPainter. + * @param dim Painter size. + * + * @see #getUI() + */ + protected BusyPainter createBusyPainter(Dimension dim) { + BusyPainter busyPainter = null; + + BusyLabelUI ui = (BusyLabelUI)getUI(); + if (ui != null) { + busyPainter = ui.getBusyPainter(dim); + + } + + return busyPainter; + } + + /** + *

Gets whether this JXBusyLabel is busy. If busy, then + * the JXBusyLabel instance will indicate that it is busy, + * generally by animating some state.

+ * + * @return true if this instance is busy + */ + public boolean isBusy() { + return busy != null; + } + + /** + *

Sets whether this JXBusyLabel instance should consider + * itself busy. A busy component may indicate that it is busy via animation, + * or some other means.

+ * + * @param busy whether this JXBusyLabel instance should + * consider itself busy + */ + public void setBusy(boolean busy) { + boolean old = isBusy(); + if (!old && busy) { + startAnimation(); + firePropertyChange("busy", old, isBusy()); + } else if (old && !busy) { + stopAnimation(); + firePropertyChange("busy", old, isBusy()); + } + } + + private void startAnimation() { + if(busy != null) { + stopAnimation(); + } + + busy = new Timer(delay, new ActionListener() { + BusyPainter busyPainter = getBusyPainter(); + int frame = busyPainter.getPoints(); + @Override + public void actionPerformed(ActionEvent e) { + frame = (frame+1)%busyPainter.getPoints(); + busyPainter.setFrame(direction == BusyPainter.Direction.LEFT ? busyPainter.getPoints() - frame : frame); + frameChanged(); + } + }); + busy.start(); + } + + + + + private void stopAnimation() { + if (busy != null) { + busy.stop(); + getBusyPainter().setFrame(-1); + repaint(); + busy = null; + } + } + + @Override + public void removeNotify() { + // fix for #698 + wasBusyOnNotify = isBusy(); + // fix for #626 + stopAnimation(); + super.removeNotify(); + } + + @Override + public void addNotify() { + super.addNotify(); + // fix for #698 + if (wasBusyOnNotify) { + // fix for #626 + startAnimation(); + } + } + + protected void frameChanged() { + repaint(); + } + + /** + * Returns the current BusyPainter. If no BusyPainter is currently + * set on this BusyLabel, the {@link #createBusyPainter(Dimension)} + * method is called to create one. Afterwards, + * {@link #initPainter(Dimension)} is called to update the BusyLabel + * with the created BusyPainter. + * + * @return the busyPainter + * + * @see #createBusyPainter(Dimension) + * @see #initPainter(Dimension) + */ + public final BusyPainter getBusyPainter() { + if (null == busyPainter) { + Dimension prefSize = getPreferredSize(); + + busyPainter = createBusyPainter((prefSize.width == 0 && prefSize.height == 0 && !isPreferredSizeSet()) ? null : prefSize); + + if (null != busyPainter) { + if (!isPreferredSizeSet() && (null == prefSize || prefSize.width == 0 || prefSize.height == 0)) { + Rectangle rt = busyPainter.getTrajectory().getBounds(); + Rectangle rp = busyPainter.getPointShape().getBounds(); + int max = Math.max(rp.width, rp.height); + prefSize = new Dimension(rt.width + max, rt.height + max); + } + + initPainter(prefSize); + } + } + return busyPainter; + } + + /** + * @param busyPainter the busyPainter to set + */ + public final void setBusyPainter(BusyPainter busyPainter) { + this.busyPainter = busyPainter; + initPainter(new Dimension(getIcon().getIconWidth(), getIcon().getIconHeight())); + } + + /** + * @return the delay + */ + public int getDelay() { + return delay; + } + + /** + * @param delay the delay to set + */ + public void setDelay(int delay) { + int old = getDelay(); + this.delay = delay; + if (old != getDelay()) { + if (busy != null && busy.isRunning()) { + busy.setDelay(getDelay()); + } + firePropertyChange("delay", old, getDelay()); + } + } + //------------------------------------------------------------- UI Logic + + /** + * Notification from the UIManager that the L&F has changed. + * Replaces the current UI object with the latest version from the + * UIManager. + * + * @see javax.swing.JComponent#updateUI + */ + @Override + public void updateUI() { + setUI((LabelUI) LookAndFeelAddons.getUI(this, BusyLabelUI.class)); + } + + /** + * Returns the name of the L&F class that renders this component. + * + * @return the string {@link #uiClassID} + * @see javax.swing.JComponent#getUIClassID + * @see javax.swing.UIDefaults#getUI + */ + @Override + public String getUIClassID() { + return uiClassID; + } + + +} + diff --git a/src/main/java/org/jdesktop/swingx/JXButton.java b/src/main/java/org/jdesktop/swingx/JXButton.java new file mode 100644 index 0000000000..cb33bdf5e3 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXButton.java @@ -0,0 +1,736 @@ +/* + * $Id: JXButton.java 4257 2012-11-14 17:08:20Z kschaefe $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.graphics.FilterComposite; +import org.jdesktop.swingx.painter.AbstractPainter; +import org.jdesktop.swingx.painter.Painter; +import org.jdesktop.swingx.painter.PainterPaint; +import org.jdesktop.swingx.util.GraphicsUtilities; +import org.jdesktop.swingx.util.PaintUtils; + +import javax.swing.*; +import javax.swing.plaf.basic.BasicGraphicsUtils; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.awt.image.BufferedImageOp; + +/** + *

A {@link Painter} enabled subclass of {@link JButton}. + * This class supports setting the foreground and background painters of the button separately.

+ * + *

For example, if you wanted to blur just the text on the button, and let everything else be + * handled by the UI delegate for your look and feel, then you could: + *


+ *  JXButton b = new JXButton("Execute");
+ *  AbstractPainter fgPainter = (AbstractPainter)b.getForegroundPainter();
+ *  StackBlurFilter filter = new StackBlurFilter();
+ *  fgPainter.setFilters(filter);
+ * 
+ * + *

If either the foreground painter or the background painter is set, + * then super.paintComponent() is not called. By setting both the foreground and background + * painters to null, you get exactly the same painting behavior as JButton.

+ * + * @author rbair + * @author rah003 + * @author Jan Stola + * @author Karl George Schaefer + */ +@JavaBean +@SuppressWarnings({ "nls", "serial" }) +public class JXButton extends JButton implements BackgroundPaintable { + private class BackgroundButton extends JButton { + + /** + * {@inheritDoc} + */ + @Override + public boolean isDefaultButton() { + return JXButton.this.isDefaultButton(); + } + + /** + * {@inheritDoc} + */ + @Override + public Icon getDisabledIcon() { + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public Icon getDisabledSelectedIcon() { + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public int getDisplayedMnemonicIndex() { + return -1; + } + + /** + * {@inheritDoc} + */ + @Override + public int getHorizontalAlignment() { + return JXButton.this.getHorizontalAlignment(); + } + + /** + * {@inheritDoc} + */ + @Override + public int getHorizontalTextPosition() { + return JXButton.this.getHorizontalTextPosition(); + } + + /** + * {@inheritDoc} + */ + @Override + public Icon getIcon() { + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public int getIconTextGap() { + return JXButton.this.getIconTextGap(); + } + + /** + * {@inheritDoc} + */ + @Override + public Insets getMargin() { + return JXButton.this.getMargin(); + } + + /** + * {@inheritDoc} + */ + @Override + public int getMnemonic() { + return -1; + } + + /** + * {@inheritDoc} + */ + @Override + public ButtonModel getModel() { + return JXButton.this.getModel(); + } + + /** + * {@inheritDoc} + */ + @Override + public Icon getPressedIcon() { + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public Icon getRolloverIcon() { + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public Icon getRolloverSelectedIcon() { + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public Icon getSelectedIcon() { + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public String getText() { + return ""; + } + + /** + * {@inheritDoc} + */ + @Override + public int getVerticalAlignment() { + return JXButton.this.getVerticalAlignment(); + } + + /** + * {@inheritDoc} + */ + @Override + public int getVerticalTextPosition() { + return JXButton.this.getVerticalTextPosition(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isBorderPainted() { + return JXButton.this.isBorderPainted(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isContentAreaFilled() { + return JXButton.this.isContentAreaFilled(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isFocusPainted() { + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isRolloverEnabled() { + return JXButton.this.isRolloverEnabled(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isSelected() { + return JXButton.this.isSelected(); + } + + } + + private class ForegroundButton extends JButton { + @Override + public Font getFont() { + return JXButton.this.getFont(); + } + + /** + * {@inheritDoc} + */ + @Override + public Color getForeground() { + if (fgPainter == null) { + return JXButton.this.getForeground(); + } + + return PaintUtils.setAlpha(JXButton.this.getForeground(), 0); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isDefaultButton() { + return JXButton.this.isDefaultButton(); + } + + /** + * {@inheritDoc} + */ + @Override + public Icon getDisabledIcon() { + return JXButton.this.getDisabledIcon(); + } + + /** + * {@inheritDoc} + */ + @Override + public Icon getDisabledSelectedIcon() { + return JXButton.this.getDisabledSelectedIcon(); + } + + /** + * {@inheritDoc} + */ + @Override + public int getDisplayedMnemonicIndex() { + return JXButton.this.getDisplayedMnemonicIndex(); + } + + /** + * {@inheritDoc} + */ + @Override + public int getHorizontalAlignment() { + return JXButton.this.getHorizontalAlignment(); + } + + /** + * {@inheritDoc} + */ + @Override + public int getHorizontalTextPosition() { + return JXButton.this.getHorizontalTextPosition(); + } + + /** + * {@inheritDoc} + */ + @Override + public Icon getIcon() { + return JXButton.this.getIcon(); + } + + /** + * {@inheritDoc} + */ + @Override + public int getIconTextGap() { + return JXButton.this.getIconTextGap(); + } + + /** + * {@inheritDoc} + */ + @Override + public Insets getMargin() { + return JXButton.this.getMargin(); + } + + /** + * {@inheritDoc} + */ + @Override + public int getMnemonic() { + return JXButton.this.getMnemonic(); + } + + /** + * {@inheritDoc} + */ + @Override + public ButtonModel getModel() { + return JXButton.this.getModel(); + } + + /** + * {@inheritDoc} + */ + @Override + public Icon getPressedIcon() { + return JXButton.this.getPressedIcon(); + } + + /** + * {@inheritDoc} + */ + @Override + public Icon getRolloverIcon() { + return JXButton.this.getRolloverIcon(); + } + + /** + * {@inheritDoc} + */ + @Override + public Icon getRolloverSelectedIcon() { + return JXButton.this.getRolloverSelectedIcon(); + } + + /** + * {@inheritDoc} + */ + @Override + public Icon getSelectedIcon() { + return JXButton.this.getSelectedIcon(); + } + + /** + * {@inheritDoc} + */ + @Override + public String getText() { + return JXButton.this.getText(); + } + + /** + * {@inheritDoc} + */ + @Override + public int getVerticalAlignment() { + return JXButton.this.getVerticalAlignment(); + } + + /** + * {@inheritDoc} + */ + @Override + public int getVerticalTextPosition() { + return JXButton.this.getVerticalTextPosition(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isBorderPainted() { + return JXButton.this.isBorderPainted(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isContentAreaFilled() { + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean hasFocus() { + return JXButton.this.hasFocus(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isFocusPainted() { + return JXButton.this.isFocusPainted(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isRolloverEnabled() { + return JXButton.this.isRolloverEnabled(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isSelected() { + return JXButton.this.isSelected(); + } + } + + private ForegroundButton fgStamp; + @SuppressWarnings("rawtypes") + private Painter fgPainter; + @SuppressWarnings("rawtypes") + private PainterPaint fgPaint; + private BackgroundButton bgStamp; + @SuppressWarnings("rawtypes") + private Painter bgPainter; + + private boolean paintBorderInsets = true; + + private Rectangle viewRect = new Rectangle(); + private Rectangle textRect = new Rectangle(); + private Rectangle iconRect = new Rectangle(); + + /** + * Creates a button with no set text or icon. + */ + public JXButton() { + init(); + } + + /** + * Creates a button with text. + * + * @param text + * the text of the button + */ + public JXButton(String text) { + super(text); + init(); + } + + /** + * Creates a button where properties are taken from the {@code Action} supplied. + * + * @param a + * the {@code Action} used to specify the new button + */ + public JXButton(Action a) { + super(a); + init(); + } + + /** + * Creates a button with an icon. + * + * @param icon + * the Icon image to display on the button + */ + public JXButton(Icon icon) { + super(icon); + init(); + } + + /** + * Creates a button with initial text and an icon. + * + * @param text + * the text of the button + * @param icon + * the Icon image to display on the button + */ + public JXButton(String text, Icon icon) { + super(text, icon); + init(); + } + + private void init() { + fgStamp = new ForegroundButton(); + } + + /** + * {@inheritDoc} + */ + @Override + @SuppressWarnings("rawtypes") + public Painter getBackgroundPainter() { + return bgPainter; + } + + /** + * {@inheritDoc} + */ + @Override + @SuppressWarnings("rawtypes") + public void setBackgroundPainter(Painter p) { + Painter old = getBackgroundPainter(); + this.bgPainter = p; + firePropertyChange("backgroundPainter", old, getBackgroundPainter()); + repaint(); + } + + /** + * @return the foreground painter for this button + */ + @SuppressWarnings("rawtypes") + public Painter getForegroundPainter() { + return fgPainter; + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public void setForegroundPainter(Painter p) { + Painter old = getForegroundPainter(); + this.fgPainter = p; + + if (fgPainter == null) { + fgPaint = null; + } else { + fgPaint = new PainterPaint(fgPainter, this); + + if (bgStamp == null) { + bgStamp = new BackgroundButton(); + } + } + + firePropertyChange("foregroundPainter", old, getForegroundPainter()); + repaint(); + } + + /** + * Returns true if the background painter should paint where the border is + * or false if it should only paint inside the border. This property is + * true by default. This property affects the width, height, + * and initial transform passed to the background painter. + */ + @Override + public boolean isPaintBorderInsets() { + return paintBorderInsets; + } + + /** + * Sets the paintBorderInsets property. + * Set to true if the background painter should paint where the border is + * or false if it should only paint inside the border. This property is true by default. + * This property affects the width, height, + * and initial transform passed to the background painter. + * + * This is a bound property. + */ + @Override + public void setPaintBorderInsets(boolean paintBorderInsets) { + boolean old = this.isPaintBorderInsets(); + this.paintBorderInsets = paintBorderInsets; + firePropertyChange("paintBorderInsets", old, isPaintBorderInsets()); + } + + /** + * {@inheritDoc} + */ + @Override + public Dimension getPreferredSize() { + if (getComponentCount() == 1 && getComponent(0) instanceof CellRendererPane) { + return BasicGraphicsUtils.getPreferredButtonSize(fgStamp, getIconTextGap()); + } + + return super.getPreferredSize(); + } + + /** + * {@inheritDoc} + */ + @Override + protected void paintComponent(Graphics g) { + if (fgPainter == null && bgPainter == null) { + super.paintComponent(g); + } else { + if (fgPainter == null) { + Graphics2D g2d = (Graphics2D) g.create(); + + try{ + paintWithoutForegroundPainter(g2d); + } finally { + g2d.dispose(); + } + } else if (fgPainter instanceof AbstractPainter && ((AbstractPainter) fgPainter).getFilters().length > 0) { + paintWithForegroundPainterWithFilters(g); + } else { + Graphics2D g2d = (Graphics2D) g.create(); + + try { + paintWithForegroundPainterWithoutFilters(g2d); + } finally { + g2d.dispose(); + } + } + } + } + + private void paintWithoutForegroundPainter(Graphics2D g2d) { + if (bgPainter == null) { + SwingUtilities.paintComponent(g2d, bgStamp, this, 0, 0, getWidth(), getHeight()); + } else { + SwingXUtilities.paintBackground(this, g2d); + } + + SwingUtilities.paintComponent(g2d, fgStamp, this, 0, 0, getWidth(), getHeight()); + } + + private void paintWithForegroundPainterWithoutFilters(Graphics2D g2d) { + paintWithoutForegroundPainter(g2d); + + if (getText() != null && !getText().isEmpty()) { + Insets i = getInsets(); + viewRect.x = i.left; + viewRect.y = i.top; + viewRect.width = getWidth() - (i.right + viewRect.x); + viewRect.height = getHeight() - (i.bottom + viewRect.y); + + textRect.x = textRect.y = textRect.width = textRect.height = 0; + iconRect.x = iconRect.y = iconRect.width = iconRect.height = 0; + + // layout the text and icon + String text = SwingUtilities.layoutCompoundLabel( + this, g2d.getFontMetrics(), getText(), getIcon(), + getVerticalAlignment(), getHorizontalAlignment(), + getVerticalTextPosition(), getHorizontalTextPosition(), + viewRect, iconRect, textRect, + getText() == null ? 0 : getIconTextGap()); + + if (!isPaintBorderInsets()) { + g2d.translate(i.left, i.top); + } + + g2d.setPaint(fgPaint); + BasicGraphicsUtils.drawStringUnderlineCharAt(g2d, text, getDisplayedMnemonicIndex(), + textRect.x, textRect.y + g2d.getFontMetrics().getAscent()); + } + } + + private void paintWithForegroundPainterWithFilters(Graphics g) { + BufferedImage im = GraphicsUtilities.createCompatibleTranslucentImage(getWidth(), getHeight()); + Graphics2D g2d = im.createGraphics(); + + try { + Graphics gfx = getComponentGraphics(g2d); + assert gfx == g2d; + + paintWithForegroundPainterWithoutFilters(g2d); + } finally { + g2d.dispose(); + } + + Graphics2D filtered = (Graphics2D) g.create(); + + try { + for (BufferedImageOp filter : ((AbstractPainter) fgPainter).getFilters()) { + filtered.setComposite(new FilterComposite(filtered.getComposite(), filter)); + } + + filtered.drawImage(im, 0, 0, this); + } finally { + filtered.dispose(); + } + } + + /** + * Notification from the UIManager that the L&F has changed. + * Replaces the current UI object with the latest version from the UIManager. + * + * @see javax.swing.JComponent#updateUI + */ + @Override + public void updateUI() { + super.updateUI(); + + if (bgStamp != null) { + bgStamp.updateUI(); + } + + if (fgStamp != null) { + fgStamp.updateUI(); + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/JXCollapsiblePane.java b/src/main/java/org/jdesktop/swingx/JXCollapsiblePane.java new file mode 100644 index 0000000000..382db15ba5 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXCollapsiblePane.java @@ -0,0 +1,1143 @@ +/* + * $Id: JXCollapsiblePane.java 4265 2012-11-28 18:15:57Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.util.GraphicsUtilities; + +import javax.swing.*; +import javax.swing.border.Border; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.image.BufferedImage; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +/** + * JXCollapsiblePane provides a component which can collapse or + * expand its content area with animation and fade in/fade out effects. + * It also acts as a standard container for other Swing components. + *

+ * The {@code JXCollapsiblePane} has a "content pane" that actually holds the + * displayed contents. This means that colors, fonts, and other display + * configuration items must be set on the content pane. + * + *


+ * // to set the font
+ * collapsiblePane.getContentPane().setFont(font);
+ * // to set the background color
+ * collapsiblePane.getContentPane().setBackground(Color.RED);
+ * 
+ * 
+ * + * For convenience, the {@code add} and {@code remove} methods forward to the + * content pane. The following code shows to ways to add a child to the + * content pane. + * + *

+ * // to add a child
+ * collapsiblePane.getContentPane().add(component);
+ * // to add a child
+ * collapsiblePane.add(component);
+ * 
+ * 
+ * + * To set the content pane, do not use {@code add}, use {@link #setContentPane(Container)}. + * + *

+ * In this example, the JXCollapsiblePane is used to build + * a Search pane which can be shown and hidden on demand. + * + *

+ * 
+ * JXCollapsiblePane cp = new JXCollapsiblePane();
+ *
+ * // JXCollapsiblePane can be used like any other container
+ * cp.setLayout(new BorderLayout());
+ *
+ * // the Controls panel with a textfield to filter the tree
+ * JPanel controls = new JPanel(new FlowLayout(FlowLayout.LEFT, 4, 0));
+ * controls.add(new JLabel("Search:"));
+ * controls.add(new JTextField(10));
+ * controls.add(new JButton("Refresh"));
+ * controls.setBorder(new TitledBorder("Filters"));
+ * cp.add("Center", controls);
+ *
+ * JXFrame frame = new JXFrame();
+ * frame.setLayout(new BorderLayout());
+ *
+ * // Put the "Controls" first
+ * frame.add("North", cp);
+ *
+ * // Then the tree - we assume the Controls would somehow filter the tree
+ * JScrollPane scroll = new JScrollPane(new JTree());
+ * frame.add("Center", scroll);
+ *
+ * // Show/hide the "Controls"
+ * JButton toggle = new JButton(cp.getActionMap().get(JXCollapsiblePane.TOGGLE_ACTION));
+ * toggle.setText("Show/Hide Search Panel");
+ * frame.add("South", toggle);
+ *
+ * frame.pack();
+ * frame.setVisible(true);
+ * 
+ * 
+ * + *

+ * The JXCollapsiblePane has a default toggle action registered + * under the name {@link #TOGGLE_ACTION}. Bind this action to a button and + * pressing the button will automatically toggle the pane between expanded + * and collapsed states. Additionally, you can define the icons to use through + * the {@link #EXPAND_ICON} and {@link #COLLAPSE_ICON} properties on the action. + * Example + *

+ * 
+ * // get the built-in toggle action
+ * Action toggleAction = collapsible.getActionMap().
+ *   get(JXCollapsiblePane.TOGGLE_ACTION);
+ *
+ * // use the collapse/expand icons from the JTree UI
+ * toggleAction.putValue(JXCollapsiblePane.COLLAPSE_ICON,
+ *                       UIManager.getIcon("Tree.expandedIcon"));
+ * toggleAction.putValue(JXCollapsiblePane.EXPAND_ICON,
+ *                       UIManager.getIcon("Tree.collapsedIcon"));
+ * 
+ * 
+ * + *

+ * Note: JXCollapsiblePane requires its parent container to have a + * {@link LayoutManager} using {@link #getPreferredSize()} when + * calculating its layout (example {@link VerticalLayout}, + * {@link BorderLayout}). + * + * @javabean.attribute + * name="isContainer" + * value="Boolean.TRUE" + * rtexpr="true" + * + * @javabean.attribute + * name="containerDelegate" + * value="getContentPane" + * + * @javabean.class + * name="JXCollapsiblePane" + * shortDescription="A pane which hides its content with an animation." + * stopClass="java.awt.Component" + * + * @author rbair (from the JDNC project) + * @author Frederic Lavigne + * @author Karl George Schaefer + */ +@JavaBean +public class JXCollapsiblePane extends JXPanel { + /** + * The direction defines how the collapsible pane will collapse. The + * constant names were designed by choosing a fixed point and then + * determining the collapsing direction from that fixed point. This means + * {@code RIGHT} expands to the right and this is probably the best + * expansion for a component in {@link BorderLayout#EAST}. + */ + public enum Direction { + /** + * Collapses left. Suitable for {@link BorderLayout#WEST}. + */ + LEFT(false), + + /** + * Collapses right. Suitable for {@link BorderLayout#EAST}. + */ + RIGHT(false), + + /** + * Collapses up. Suitable for {@link BorderLayout#NORTH}. + */ + UP(true), + + /** + * Collapses down. Suitable for {@link BorderLayout#SOUTH}. + */ + DOWN(true), + + /** + * Collapses toward the leading edge. Suitable for {@link BorderLayout#LINE_START}. + */ + LEADING(false) { + @Override + Direction getFixedDirection(ComponentOrientation co) { + return co.isLeftToRight() ? LEFT : RIGHT; + } + }, + + /** + * Collapses toward the trailing edge. Suitable for {@link BorderLayout#LINE_END}. + */ + TRAILING(false) { + @Override + Direction getFixedDirection(ComponentOrientation co) { + return co.isLeftToRight() ? RIGHT : LEFT; + } + }, + + /** + * Collapses toward the starting edge. Suitable for {@link BorderLayout#PAGE_START}. + */ + START(true) { + @Override + Direction getFixedDirection(ComponentOrientation co) { + return UP; + } + }, + + /** + * Collapses toward the ending edge. Suitable for {@link BorderLayout#PAGE_END}. + */ + END(true) { + @Override + Direction getFixedDirection(ComponentOrientation co) { + return DOWN; + } + }, + ; + + private final boolean vertical; + + private Direction(boolean vertical) { + this.vertical = vertical; + } + + /** + * Gets the orientation for this direction. + * + * @return {@code true} if the direction is vertical, {@code false} + * otherwise + */ + public boolean isVertical() { + return vertical; + } + + /** + * Gets the fixed direction equivalent to this direction for the specified orientation. + * + * @param co + * the component's orientation + * @return the fixed direction corresponding to the component's orietnation + */ + Direction getFixedDirection(ComponentOrientation co) { + return this; + } + } + + /** + * Toggles the JXCollapsiblePane state and updates its icon based on the + * JXCollapsiblePane "collapsed" status. + */ + private class ToggleAction extends AbstractAction implements + PropertyChangeListener { + public ToggleAction() { + super(TOGGLE_ACTION); + // the action must track the collapsed status of the pane to update its icon + JXCollapsiblePane.this.addPropertyChangeListener("collapsed", this); + } + + @Override + public void putValue(String key, Object newValue) { + super.putValue(key, newValue); + if (EXPAND_ICON.equals(key) || COLLAPSE_ICON.equals(key)) { + updateIcon(); + } + } + + @Override + public void actionPerformed(ActionEvent e) { + setCollapsed(!isCollapsed()); + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + updateIcon(); + } + + void updateIcon() { + if (isCollapsed()) { + putValue(SMALL_ICON, getValue(EXPAND_ICON)); + } else { + putValue(SMALL_ICON, getValue(COLLAPSE_ICON)); + } + } + } + + /** + * JXCollapsible has a built-in toggle action which can be bound to buttons. + * Accesses the action through + * collapsiblePane.getActionMap().get(JXCollapsiblePane.TOGGLE_ACTION). + */ + public final static String TOGGLE_ACTION = "toggle"; + + /** + * The icon used by the "toggle" action when the JXCollapsiblePane is + * expanded, i.e the icon which indicates the pane can be collapsed. + */ + public final static String COLLAPSE_ICON = "collapseIcon"; + + /** + * The icon used by the "toggle" action when the JXCollapsiblePane is + * collapsed, i.e the icon which indicates the pane can be expanded. + */ + public final static String EXPAND_ICON = "expandIcon"; + + /** + * Indicates whether the component is collapsed or expanded + */ + private boolean collapsed = false; + + /** + * Defines the orientation of the component. + */ + private Direction direction = Direction.UP; + + /** + * Timer used for doing the transparency animation (fade-in) + */ + private Timer animateTimer; + private AnimationListener animator; + private int currentDimension = -1; + private WrapperContainer wrapper; + private boolean useAnimation = true; + private AnimationParams animationParams; + private boolean collapseFiringState; + + /** + * Constructs a new JXCollapsiblePane with a {@link JXPanel} as content pane + * and a vertical {@link VerticalLayout} with a gap of 2 pixels as layout + * manager and a vertical orientation. + */ + public JXCollapsiblePane() { + this(Direction.UP); + } + + /** + * Constructs a new JXCollapsiblePane with a {@link JXPanel} as content pane and the specified + * direction. + * + * @param direction + * the direction to collapse the container + */ + public JXCollapsiblePane(Direction direction) { + super.setLayout(new BorderLayout()); + this.direction = direction; + animator = new AnimationListener(); + setAnimationParams(new AnimationParams(30, 8, 0.01f, 1.0f)); + + setContentPane(createContentPane()); + setDirection(direction); + + // add an action to automatically toggle the state of the pane + getActionMap().put(TOGGLE_ACTION, new ToggleAction()); + } + + /** + * Creates the content pane used by this collapsible pane. + * + * @return the content pane + */ + protected Container createContentPane() { + return new JXPanel(); + } + + /** + * @return the content pane + */ + public Container getContentPane() { + if (wrapper == null) { + return null; + } + + return (Container) wrapper.getView(); + } + + /** + * Sets the content pane of this JXCollapsiblePane. The {@code contentPanel} + * should implement {@code Scrollable} and return {@code true} from + * {@link Scrollable#getScrollableTracksViewportHeight()} and + * {@link Scrollable#getScrollableTracksViewportWidth()}. If the content + * pane fails to do so and a {@code JScrollPane} is added as a child, it is + * likely that the scroll pane will never correctly size. While it is not + * strictly necessary to implement {@code Scrollable} in this way, the + * default content pane does so. + * + * @param contentPanel + * the container delegate used to hold all of the contents + * for this collapsible pane + * @throws IllegalArgumentException + * if contentPanel is null + */ + public void setContentPane(Container contentPanel) { + if (contentPanel == null) { + throw new IllegalArgumentException("Content pane can't be null"); + } + + if (wrapper != null) { + //these next two lines are as they are because if I try to remove + //the "wrapper" component directly, then super.remove(comp) ends up + //calling remove(int), which is overridden in this class, leading to + //improper behavior. + assert super.getComponent(0) == wrapper; + super.remove(0); + } + + wrapper = new WrapperContainer(contentPanel); + wrapper.collapsedState = isCollapsed(); + wrapper.getView().setVisible(!wrapper.collapsedState); + super.addImpl(wrapper, BorderLayout.CENTER, -1); + } + + /** + * Overridden to redirect call to the content pane. + */ + @Override + public void setLayout(LayoutManager mgr) { + // wrapper can be null when setLayout is called by "super()" constructor + if (wrapper != null) { + getContentPane().setLayout(mgr); + } + } + + /** + * Overridden to redirect call to the content pane. + */ + @Override + protected void addImpl(Component comp, Object constraints, int index) { + getContentPane().add(comp, constraints, index); + } + + /** + * Overridden to redirect call to the content pane + */ + @Override + public void remove(Component comp) { + getContentPane().remove(comp); + } + + /** + * Overridden to redirect call to the content pane. + */ + @Override + public void remove(int index) { + getContentPane().remove(index); + } + + /** + * Overridden to redirect call to the content pane. + */ + @Override + public void removeAll() { + getContentPane().removeAll(); + } + + /** + * If true, enables the animation when pane is collapsed/expanded. If false, + * animation is turned off. + * + *

+ * When animated, the JXCollapsiblePane will progressively + * reduce (when collapsing) or enlarge (when expanding) the height of its + * content area until it becomes 0 or until it reaches the preferred height of + * the components it contains. The transparency of the content area will also + * change during the animation. + * + *

+ * If not animated, the JXCollapsiblePane will simply hide + * (collapsing) or show (expanding) its content area. + * + * @param animated + * @javabean.property bound="true" preferred="true" + */ + public void setAnimated(boolean animated) { + if (animated != useAnimation) { + useAnimation = animated; + + if (!animated) { + if (animateTimer.isRunning()) { + //TODO should we listen for animation state change? + //yes, but we're best off creating a UI delegate for these changes + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + currentDimension = -1; + } + }); + } else { + currentDimension = -1; + } + } + firePropertyChange("animated", !useAnimation, useAnimation); + } + } + + /** + * @return true if the pane is animated, false otherwise + * @see #setAnimated(boolean) + */ + public boolean isAnimated() { + return useAnimation; + } + + /** + * {@inheritDoc} + */ + @Override + public void setComponentOrientation(ComponentOrientation o) { + if (animateTimer.isRunning()) { + throw new IllegalStateException("cannot be change component orientation while collapsing."); + } + + super.setComponentOrientation(o); + } + + /** + * Changes the direction of this collapsible pane. Doing so changes the + * layout of the underlying content pane. If the chosen direction is + * vertical, a vertical layout with a gap of 2 pixels is chosen. Otherwise, + * a horizontal layout with a gap of 2 pixels is chosen. + * + * @see #getDirection() + * @param direction the new {@link Direction} for this collapsible pane + * @throws IllegalStateException when this method is called while a + * collapsing/restore operation is running + * @javabean.property + * bound="true" + * preferred="true" + */ + public void setDirection(Direction direction) { + if (animateTimer.isRunning()) { + throw new IllegalStateException("cannot be change direction while collapsing."); + } + + Direction oldValue = getDirection(); + this.direction = direction; + + if (direction.isVertical()) { + getContentPane().setLayout(new VerticalLayout(2)); + } else { + getContentPane().setLayout(new HorizontalLayout(2)); + } + + firePropertyChange("direction", oldValue, getDirection()); + } + + /** + * @return the current {@link Direction}. + * @see #setDirection(Direction) + */ + public Direction getDirection() { + return direction; + } + + /** + * @return true if the pane is collapsed, false if expanded + */ + public boolean isCollapsed() { + return collapsed; + } + + /** + * Expands or collapses this JXCollapsiblePane. + * + *

+ * If the component is collapsed and val is false, then this + * call expands the JXCollapsiblePane, such that the entire JXCollapsiblePane + * will be visible. If {@link #isAnimated()} returns true, the expansion will + * be accompanied by an animation. + * + *

+ * However, if the component is expanded and val is true, then + * this call collapses the JXCollapsiblePane, such that the entire + * JXCollapsiblePane will be invisible. If {@link #isAnimated()} returns true, + * the collapse will be accompanied by an animation. + * + *

+ * As of SwingX 1.6.3, JXCollapsiblePane only fires property change events when + * the component's state is accurate. This means that animated collapsible + * pane's only fire events once the animation is complete. + * + * @see #isAnimated() + * @see #setAnimated(boolean) + * @javabean.property + * bound="true" + * preferred="true" + */ + public void setCollapsed(boolean val) { + boolean oldValue = isCollapsed(); + this.collapsed = val; + + if (isAnimated() && isShowing()) { + if (oldValue == isCollapsed()) { + return; + } + + // this ensures that if the user reverses the animation + // before completion that no property change is fired + if (!animateTimer.isRunning()) { + collapseFiringState = oldValue; + } + + if (oldValue) { + int dimension = direction.isVertical() ? wrapper.getHeight() : wrapper.getWidth(); + int preferredDimension = direction.isVertical() ? getContentPane() + .getPreferredSize().height : getContentPane().getPreferredSize().width; + int delta = Math.max(8, preferredDimension / 10); + + setAnimationParams(new AnimationParams(30, delta, 0.01f, 1.0f)); + animator.reinit(dimension, preferredDimension); + wrapper.getView().setVisible(true); + } else { + int dimension = direction.isVertical() ? wrapper.getHeight() : wrapper.getWidth(); + setAnimationParams(new AnimationParams(30, Math.max(8, dimension / 10), 1.0f, 0.01f)); + animator.reinit(dimension, 0); + } + + animateTimer.start(); + } else { + wrapper.collapsedState = isCollapsed(); + wrapper.getView().setVisible(!isCollapsed()); + revalidate(); + repaint(); + + firePropertyChange("collapsed", oldValue, isCollapsed()); + } + } + + /** + * {@inheritDoc} + */ + @Override + public Border getBorder() { + if (getContentPane() instanceof JComponent) { + return ((JComponent) getContentPane()).getBorder(); + } + + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public void setBorder(Border border) { + if (getContentPane() instanceof JComponent) { + ((JComponent) getContentPane()).setBorder(border); + } + } + + /** + * {@inheritDoc} + *

+ * Internals of JXCollasiplePane are designed to be opaque because some Look and Feel + * implementations having painting issues otherwise. JXCollapsiblePane and its internals will + * respect {@code setOpaque}, calling this method will not only update the collapsible pane, but + * also all internals. This method does not modify the {@link #getContentPane() content pane}, + * as it is not considered an internal. + */ + @Override + public void setOpaque(boolean opaque) { + super.setOpaque(opaque); + + if (wrapper != null) { + wrapper.setOpaque(opaque); + } + } + + /** + * A collapsible pane always returns its preferred size for the minimum size + * to ensure that the collapsing happens correctly. + *

+ * To query the minimum size of the contents user {@code + * getContentPane().getMinimumSize()}. + * + * @return the preferred size of the component + */ + @Override + public Dimension getMinimumSize() { + return getPreferredSize(); + } + + /** + * Forwards to the content pane. + * + * @param minimumSize + * the size to set on the content pane + */ + @Override + public void setMinimumSize(Dimension minimumSize) { + getContentPane().setMinimumSize(minimumSize); + } + + /** + * The critical part of the animation of this JXCollapsiblePane + * relies on the calculation of its preferred size. During the animation, its + * preferred size (specially its height) will change, when expanding, from 0 + * to the preferred size of the content pane, and the reverse when collapsing. + * + * @return this component preferred size + */ + @Override + public Dimension getPreferredSize() { + /* + * The preferred size is calculated based on the current position of the + * component in its animation sequence. If the Component is expanded, then + * the preferred size will be the preferred size of the top component plus + * the preferred size of the embedded content container.

However, if the + * scroll up is in any state of animation, the height component of the + * preferred size will be the current height of the component (as contained + * in the currentDimension variable and when orientation is VERTICAL, otherwise + * the same applies to the width) + */ + Dimension dim = getContentPane().getPreferredSize(); + if (currentDimension != -1) { + if (direction.isVertical()) { + dim.height = currentDimension; + } else { + dim.width = currentDimension; + } + } else if(wrapper.collapsedState) { + if (direction.isVertical()) { + dim.height = 0; + } else { + dim.width = 0; + } + } + return dim; + } + + @Override + public void setPreferredSize(Dimension preferredSize) { + getContentPane().setPreferredSize(preferredSize); + } + + /** + * Sets the parameters controlling the animation + * + * @param params + * @throws IllegalArgumentException + * if params is null + */ + private void setAnimationParams(AnimationParams params) { + if (params == null) { throw new IllegalArgumentException( + "params can't be null"); } + if (animateTimer != null) { + animateTimer.stop(); + } + animationParams = params; + animateTimer = new Timer(animationParams.waitTime, animator); + animateTimer.setInitialDelay(0); + } + + /** + * Tagging interface for containers in a JXCollapsiblePane hierarchy who needs + * to be revalidated (invalidate/validate/repaint) when the pane is expanding + * or collapsing. Usually validating only the parent of the JXCollapsiblePane + * is enough but there might be cases where the parent's parent must be + * validated. + */ + public static interface CollapsiblePaneContainer { + Container getValidatingContainer(); + } + + /** + * Parameters controlling the animations + */ + private static class AnimationParams { + final int waitTime; + final int delta; + final float alphaStart; + final float alphaEnd; + + /** + * @param waitTime + * the amount of time in milliseconds to wait between calls to the + * animation thread + * @param delta + * the delta, in the direction as specified by the orientation, + * to inc/dec the size of the scroll up by + * @param alphaStart + * the starting alpha transparency level + * @param alphaEnd + * the ending alpha transparency level + */ + public AnimationParams(int waitTime, int delta, float alphaStart, + float alphaEnd) { + this.waitTime = waitTime; + this.delta = delta; + this.alphaStart = alphaStart; + this.alphaEnd = alphaEnd; + } + } + + /** + * This class actual provides the animation support for scrolling up/down this + * component. This listener is called whenever the animateTimer fires off. It + * fires off in response to scroll up/down requests. This listener is + * responsible for modifying the size of the content container and causing it + * to be repainted. + * + * @author Richard Bair + */ + private final class AnimationListener implements ActionListener { + /** + * Mutex used to ensure that the startDimension/finalDimension are not changed + * during a repaint operation. + */ + private final Object ANIMATION_MUTEX = "Animation Synchronization Mutex"; + /** + * This is the starting dimension when animating. If > finalDimension, then the + * animation is going to be to scroll up the component. If it is less than + * finalDimension, then the animation will scroll down the component. + */ + private int startDimension = 0; + /** + * This is the final dimension that the content container is going to be when + * scrolling is finished. + */ + private int finalDimension = 0; + /** + * The current alpha setting used during "animation" (fade-in/fade-out) + */ + @SuppressWarnings({"FieldCanBeLocal"}) + private float animateAlpha = 1.0f; + + @Override + public void actionPerformed(ActionEvent e) { + /* + * Pre-1) If startDimension == finalDimension, then we're done so stop the timer + * 1) Calculate whether we're contracting or expanding. 2) Calculate the + * delta (which is either positive or negative, depending on the results + * of (1)) 3) Calculate the alpha value 4) Resize the ContentContainer 5) + * Revalidate/Repaint the content container + */ + synchronized (ANIMATION_MUTEX) { + if (startDimension == finalDimension) { + animateTimer.stop(); + animateAlpha = animationParams.alphaEnd; + // keep the content pane hidden when it is collapsed, other it may + // still receive focus. + if (finalDimension > 0) { + currentDimension = -1; + wrapper.collapsedState = false; + validate(); + JXCollapsiblePane.this.firePropertyChange("collapsed", collapseFiringState, false); + return; + } else { + wrapper.collapsedState = true; + wrapper.getView().setVisible(false); + JXCollapsiblePane.this.firePropertyChange("collapsed", collapseFiringState, true); + } + } + + final boolean contracting = startDimension > finalDimension; + final int delta = contracting?-1 * animationParams.delta + :animationParams.delta; + int newDimension; + if (direction.isVertical()) { + newDimension = wrapper.getHeight() + delta; + } else { + newDimension = wrapper.getWidth() + delta; + } + if (contracting) { + if (newDimension < finalDimension) { + newDimension = finalDimension; + } + } else { + if (newDimension > finalDimension) { + newDimension = finalDimension; + } + } + int dimension; + if (direction.isVertical()) { + dimension = wrapper.getView().getPreferredSize().height; + } else { + dimension = wrapper.getView().getPreferredSize().width; + } + animateAlpha = (float)newDimension / (float)dimension; + + Rectangle bounds = wrapper.getBounds(); + + if (direction.isVertical()) { + int oldHeight = bounds.height; + bounds.height = newDimension; + wrapper.setBounds(bounds); + + if (direction.getFixedDirection(getComponentOrientation()) == Direction.DOWN) { + wrapper.setViewPosition(new Point(0, wrapper.getView().getPreferredSize().height - newDimension)); + } else { + wrapper.setViewPosition(new Point(0, newDimension)); + } + + bounds = getBounds(); + bounds.height = (bounds.height - oldHeight) + newDimension; + currentDimension = bounds.height; + } else { + int oldWidth = bounds.width; + bounds.width = newDimension; + wrapper.setBounds(bounds); + + if (direction.getFixedDirection(getComponentOrientation()) == Direction.RIGHT) { + wrapper.setViewPosition(new Point(wrapper.getView().getPreferredSize().width - newDimension, 0)); + } else { + wrapper.setViewPosition(new Point(newDimension, 0)); + } + + bounds = getBounds(); + bounds.width = (bounds.width - oldWidth) + newDimension; + currentDimension = bounds.width; + } + + setBounds(bounds); + startDimension = newDimension; + + // it happens the animateAlpha goes over the alphaStart/alphaEnd range + // this code ensures it stays in bounds. This behavior is seen when + // component such as JTextComponents are used in the container. + if (contracting) { + // alphaStart > animateAlpha > alphaEnd + if (animateAlpha < animationParams.alphaEnd) { + animateAlpha = animationParams.alphaEnd; + } + if (animateAlpha > animationParams.alphaStart) { + animateAlpha = animationParams.alphaStart; + } + } else { + // alphaStart < animateAlpha < alphaEnd + if (animateAlpha > animationParams.alphaEnd) { + animateAlpha = animationParams.alphaEnd; + } + if (animateAlpha < animationParams.alphaStart) { + animateAlpha = animationParams.alphaStart; + } + } + + wrapper.setAlpha(animateAlpha); + + validate(); + } + } + + void validate() { + Container parent = SwingUtilities.getAncestorOfClass( + CollapsiblePaneContainer.class, JXCollapsiblePane.this); + if (parent != null) { + parent = ((CollapsiblePaneContainer)parent).getValidatingContainer(); + } else { + parent = getParent(); + } + + if (parent != null) { + if (parent instanceof JComponent) { + ((JComponent)parent).revalidate(); + } else { + parent.invalidate(); + } + parent.doLayout(); + parent.repaint(); + } + } + + /** + * Reinitializes the timer for scrolling up/down the component. This method + * is properly synchronized, so you may make this call regardless of whether + * the timer is currently executing or not. + * + * @param startDimension + * @param stopDimension + */ + public void reinit(int startDimension, int stopDimension) { + synchronized (ANIMATION_MUTEX) { + this.startDimension = startDimension; + this.finalDimension = stopDimension; + animateAlpha = animationParams.alphaStart; + currentDimension = -1; + } + } + } + + private final class WrapperContainer extends JViewport implements AlphaPaintable { + boolean collapsedState; + private volatile float alpha; + private boolean oldOpaque; + + public WrapperContainer(Container c) { + alpha = 1.0f; + collapsedState = false; + setView(c); + + // we must ensure the container is opaque. It is not opaque it introduces + // painting glitches specially on Linux with JDK 1.5 and GTK look and feel. + // GTK look and feel calls setOpaque(false) + if (c instanceof JComponent && !c.isOpaque()) { + ((JComponent) c).setOpaque(true); + } + } + + /** + * {@inheritDoc}

+ * + * Overridden to not have JViewPort behaviour (that is scroll the view) + * but delegate to parent scrollRectToVisible just a JComponent does.

+ */ + @Override + public void scrollRectToVisible(Rectangle aRect) { + //avoids JViewport's implementation + //by using JXCollapsiblePane's it will delegate upward + //getting any core fixes, by avoiding c&p + JXCollapsiblePane.this.scrollRectToVisible(aRect); + } + + @Override + public float getAlpha() { + return alpha; + } + + @Override + public void setAlpha(float alpha) { + if (alpha < 0f || alpha > 1f) { + throw new IllegalArgumentException("invalid alpha value " + alpha); + } + + float oldValue = getAlpha(); + this.alpha = alpha; + + if (getAlpha() < 1f) { + if (oldValue == 1) { + //it used to be 1, but now is not. Save the oldOpaque + oldOpaque = isOpaque(); + setOpaque(false); + } + } else { + //restore the oldOpaque if it was true (since opaque is false now) + if (oldOpaque) { + setOpaque(true); + } + } + + firePropertyChange("alpha", oldValue, getAlpha()); + repaint(); + } + + @Override + public boolean isInheritAlpha() { + return false; + } + + @Override + public void setInheritAlpha(boolean inheritAlpha) { + //does nothing; always false; + } + + @Override + public float getEffectiveAlpha() { + return getAlpha(); + } + + //support for Java 7 painting improvements + protected boolean isPaintingOrigin() { + return getAlpha() < 1f; + } + + /** + * Overridden paint method to take into account the alpha setting. + * + * @param g + * the Graphics context in which to paint + */ + @Override + public void paint(Graphics g) { + //short circuit painting if no transparency + if (getAlpha() == 1f) { + super.paint(g); + } else { + //the component is translucent, so we need to render to + //an intermediate image before painting + // TODO should we cache this image? repaint to same image unless size changes? + BufferedImage img = GraphicsUtilities.createCompatibleTranslucentImage(getWidth(), getHeight()); + Graphics2D gfx = img.createGraphics(); + + try { + super.paint(gfx); + } finally { + gfx.dispose(); + } + + Graphics2D g2d = (Graphics2D) g; + Composite oldComp = g2d.getComposite(); + + try { + Composite alphaComp = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, getEffectiveAlpha()); + g2d.setComposite(alphaComp); + //TODO should we cache the image? + g2d.drawImage(img, null, 0, 0); + } finally { + g2d.setComposite(oldComp); + } + } + } + } + +// TEST CASE +// public static void main(String[] args) { +// SwingUtilities.invokeLater(new Runnable() { +// public void run() { +// JFrame f = new JFrame("Test Oriented Collapsible Pane"); +// +// f.add(new JLabel("Press Ctrl+F or Ctrl+G to collapse panes."), +// BorderLayout.NORTH); +// +// JTree tree1 = new JTree(); +// tree1.setBorder(BorderFactory.createEtchedBorder()); +// f.add(tree1); +// +// JXCollapsiblePane pane = new JXCollapsiblePane(Orientation.VERTICAL); +// pane.setCollapsed(true); +// JTree tree2 = new JTree(); +// tree2.setBorder(BorderFactory.createEtchedBorder()); +// pane.add(tree2); +// f.add(pane, BorderLayout.SOUTH); +// +// pane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( +// KeyStroke.getKeyStroke("ctrl F"), +// JXCollapsiblePane.TOGGLE_ACTION); +// +// pane = new JXCollapsiblePane(Orientation.HORIZONTAL); +// JTree tree3 = new JTree(); +// pane.add(tree3); +// tree3.setBorder(BorderFactory.createEtchedBorder()); +// f.add(pane, BorderLayout.WEST); +// +// pane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( +// KeyStroke.getKeyStroke("ctrl G"), +// JXCollapsiblePane.TOGGLE_ACTION); +// +// f.setSize(640, 480); +// f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); +// f.setVisible(true); +// } +// }); +// } +} diff --git a/src/main/java/org/jdesktop/swingx/JXColorSelectionButton.java b/src/main/java/org/jdesktop/swingx/JXColorSelectionButton.java new file mode 100644 index 0000000000..ef70db084f --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXColorSelectionButton.java @@ -0,0 +1,252 @@ +/* + * $Id: JXColorSelectionButton.java 4082 2011-11-15 18:39:43Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx; + +import org.jdesktop.swingx.color.EyeDropperColorChooserPanel; +import org.jdesktop.swingx.plaf.UIManagerExt; +import org.jdesktop.swingx.util.GraphicsUtilities; +import org.jdesktop.swingx.util.OS; +import org.jdesktop.swingx.util.PaintUtils; + +import javax.imageio.ImageIO; +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.geom.Ellipse2D; +import java.awt.image.BufferedImage; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +import static java.awt.RenderingHints.KEY_ANTIALIASING; +import static java.awt.RenderingHints.VALUE_ANTIALIAS_ON; + +/** + * A button which allows the user to select a single color. The button has a platform + * specific look. Ex: on Mac OS X it will mimic an NSColorWell. When the user + * clicks the button it will open a color chooser set to the current background + * color of the button. The new selected color will be stored in the background + * property and can be retrieved using the getBackground() method. As the user is + * choosing colors within the color chooser the background property will be updated. + * By listening to this property developers can make other parts of their programs + * update. + * + * @author joshua@marinacci.org + */ +public class JXColorSelectionButton extends JButton { + private BufferedImage colorwell; + private JDialog dialog = null; + private JColorChooser chooser = null; + private Color initialColor = null; + + /** + * Creates a new instance of JXColorSelectionButton + */ + public JXColorSelectionButton() { + this(Color.red); + } + + /** + * Creates a new instance of JXColorSelectionButton set to the specified color. + * @param col The default color + */ + public JXColorSelectionButton(Color col) { + setBackground(col); + this.addActionListener(new ActionHandler()); + this.setContentAreaFilled(false); + this.setOpaque(false); + + try { + colorwell = ImageIO.read(JXColorSelectionButton.class.getResourceAsStream("color/colorwell.png")); + } catch (Exception ex) { + ex.printStackTrace(); + } + + this.addPropertyChangeListener("background",new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent propertyChangeEvent) { + getChooser().setColor(getBackground()); + } + }); + } + + + /** + * A listener class to update the button's background when the selected + * color changes. + */ + private class ColorChangeListener implements ChangeListener { + public JXColorSelectionButton button; + public ColorChangeListener(JXColorSelectionButton button) { + this.button = button; + } + public void stateChanged(ChangeEvent changeEvent) { + button.setBackground(button.getChooser().getColor()); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void paintComponent(Graphics g) { + // want disabledForeground when disabled, current colour otherwise + final Color FILL_COLOR = isEnabled() ? PaintUtils.removeAlpha(getBackground()) + : UIManagerExt.getSafeColor("Button.disabledForeground", Color.LIGHT_GRAY); + + // draw the colorwell image (should only be on OSX) + if(OS.isMacOSX() && colorwell != null) { + Insets ins = new Insets(5,5,5,5); + GraphicsUtilities.tileStretchPaint(g, this, colorwell, ins); + + // fill in the color area + g.setColor(FILL_COLOR); + g.fillRect(ins.left, ins.top, + getWidth() - ins.left - ins.right, + getHeight() - ins.top - ins.bottom); + // draw the borders + g.setColor(PaintUtils.setBrightness(FILL_COLOR,0.85f)); + g.drawRect(ins.left, ins.top, + getWidth() - ins.left - ins.right - 1, + getHeight() - ins.top - ins.bottom - 1); + g.drawRect(ins.left + 1, ins.top + 1, + getWidth() - ins.left - ins.right - 3, + getHeight() - ins.top - ins.bottom - 3); + }else{ + Graphics2D g2 = (Graphics2D) g.create(); + + try { + g2.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON); + g2.setColor(Color.LIGHT_GRAY); + final int DIAM = Math.min(getWidth(), getHeight()); + final int inset = 3; + g2.fill(new Ellipse2D.Float(inset, inset, DIAM-2*inset, DIAM-2*inset)); + g2.setColor(FILL_COLOR); + final int border = 1; + g2.fill(new Ellipse2D.Float(inset+border, inset+border, DIAM-2*inset-2*border, DIAM-2*inset-2*border)); + } finally { + g2.dispose(); + } + + } + } + +// /** +// * Sample usage of JXColorSelectionButton +// * @param args not used +// */ +// public static void main(String[] args) { +// javax.swing.JFrame frame = new javax.swing.JFrame("Color Button Test"); +// frame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE); +// javax.swing.JPanel panel = new javax.swing.JPanel(); +// javax.swing.JComponent btn = new JXColorSelectionButton(); +// btn.setEnabled(true); +// panel.add(btn); +// panel.add(new javax.swing.JLabel("ColorSelectionButton test")); +// +// frame.add(panel); +// frame.pack(); +// frame.setVisible(true); +// } + + /** + * Conditionally create and show the color chooser dialog. + */ + private void showDialog() { + if (dialog == null) { + dialog = JColorChooser.createDialog(JXColorSelectionButton.this, + "Choose a color", true, getChooser(), + new ActionListener() { + public void actionPerformed(ActionEvent actionEvent) { + Color color = getChooser().getColor(); + if (color != null) { + setBackground(color); + } + } + }, + new ActionListener() { + public void actionPerformed(ActionEvent actionEvent) { + setBackground(initialColor); + } + }); + dialog.getContentPane().add(getChooser()); + getChooser().getSelectionModel().addChangeListener( + new ColorChangeListener(JXColorSelectionButton.this)); + } + + initialColor = getBackground(); + dialog.setVisible(true); + + } + + /** + * Get the JColorChooser that is used by this JXColorSelectionButton. This + * chooser instance is shared between all invocations of the chooser, but is unique to + * this instance of JXColorSelectionButton. + * @return the JColorChooser used by this JXColorSelectionButton + */ + public JColorChooser getChooser() { + if(chooser == null) { + chooser = new JColorChooser(); + // add the eyedropper color chooser panel + chooser.addChooserPanel(new EyeDropperColorChooserPanel()); + } + return chooser; + } + + /** + * Set the JColorChooser that is used by this JXColorSelectionButton. + * chooser instance is shared between all invocations of the chooser, + * but is unique to + * this instance of JXColorSelectionButton. + * @param chooser The new JColorChooser to use. + */ + public void setChooser(JColorChooser chooser) { + JColorChooser oldChooser = getChooser(); + this.chooser = chooser; + firePropertyChange("chooser",oldChooser,chooser); + } + + /** + * {@inheritDoc} + */ + @Override + public Dimension getPreferredSize() { + if (isPreferredSizeSet() || colorwell == null) { + return super.getPreferredSize(); + } + + return new Dimension(colorwell.getWidth(), colorwell.getHeight()); + } + + /** + * A private class to conditionally create and show the color chooser + * dialog. + */ + private class ActionHandler implements ActionListener { + + public void actionPerformed(ActionEvent actionEvent) { + showDialog(); + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/JXComboBox.java b/src/main/java/org/jdesktop/swingx/JXComboBox.java new file mode 100644 index 0000000000..226b280adb --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXComboBox.java @@ -0,0 +1,885 @@ +/* + * $Id: JXComboBox.java 4158 2012-02-03 18:29:40Z kschaefe $ + * + * Copyright 2010 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx; + +import org.jdesktop.swingx.decorator.ComponentAdapter; +import org.jdesktop.swingx.decorator.CompoundHighlighter; +import org.jdesktop.swingx.decorator.Highlighter; +import org.jdesktop.swingx.plaf.UIDependent; +import org.jdesktop.swingx.renderer.DefaultListRenderer; +import org.jdesktop.swingx.renderer.JRendererPanel; +import org.jdesktop.swingx.renderer.StringValue; +import org.jdesktop.swingx.rollover.RolloverRenderer; +import org.jdesktop.swingx.sort.StringValueRegistry; +import org.jdesktop.swingx.util.Contract; + +import javax.accessibility.Accessible; +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.plaf.UIResource; +import javax.swing.plaf.basic.ComboPopup; +import java.awt.*; +import java.awt.event.KeyEvent; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Vector; + +/** + * An enhanced {@code JComboBox} that provides the following additional functionality: + *

+ * Auto-starts edits correctly for AutoCompletion when inside a {@code JTable}. A normal {@code + * JComboBox} fails to recognize the first key stroke when it has been + * {@link org.jdesktop.swingx.autocomplete.AutoCompleteDecorator#decorate(JComboBox) decorated}. + *

+ * Adds highlighting support. + * + * @author Karl Schaefer + * @author Jeanette Winzenburg + */ +@SuppressWarnings({"nls", "serial"}) +public class JXComboBox extends JComboBox { + /** + * A decorator for the original ListCellRenderer. Needed to hook highlighters + * after messaging the delegate.

+ */ + public class DelegatingRenderer implements ListCellRenderer, RolloverRenderer, UIDependent { + /** the delegate. */ + private ListCellRenderer delegateRenderer; + private JRendererPanel wrapper; + + /** + * Instantiates a DelegatingRenderer with combo box's default renderer as delegate. + */ + public DelegatingRenderer() { + this(null); + } + + /** + * Instantiates a DelegatingRenderer with the given delegate. If the + * delegate is {@code null}, the default is created via the combo box's factory method. + * + * @param delegate the delegate to use, if {@code null} the combo box's default is + * created and used. + */ + public DelegatingRenderer(ListCellRenderer delegate) { + wrapper = new JRendererPanel(new BorderLayout()); + setDelegateRenderer(delegate); + } + + /** + * Sets the delegate. If the delegate is {@code null}, the default is created via the combo + * box's factory method. + * + * @param delegate + * the delegate to use, if null the list's default is created and used. + */ + public void setDelegateRenderer(ListCellRenderer delegate) { + if (delegate == null) { + delegate = createDefaultCellRenderer(); + } + delegateRenderer = delegate; + } + + /** + * Returns the delegate. + * + * @return the delegate renderer used by this renderer, guaranteed to + * not-null. + */ + public ListCellRenderer getDelegateRenderer() { + return delegateRenderer; + } + + /** + * {@inheritDoc} + */ + @Override + public void updateUI() { + wrapper.updateUI(); + + if (delegateRenderer instanceof UIDependent) { + ((UIDependent) delegateRenderer).updateUI(); + } else if (delegateRenderer instanceof Component) { + SwingUtilities.updateComponentTreeUI((Component) delegateRenderer); + } else if (delegateRenderer != null) { + try { + Component comp = delegateRenderer.getListCellRendererComponent( + getPopupListFor(JXComboBox.this), null, -1, false, false); + SwingUtilities.updateComponentTreeUI(comp); + } catch (Exception e) { + // nothing to do - renderer barked on off-range row + } + } + } + + // --------- implement ListCellRenderer + /** + * {@inheritDoc}

+ * + * Overridden to apply the highlighters, if any, after calling the delegate. + * The decorators are not applied if the row is invalid. + */ + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, + boolean isSelected, boolean cellHasFocus) { + Component comp = null; + + if (index == -1) { + comp = delegateRenderer.getListCellRendererComponent(list, value, + getSelectedIndex(), isSelected, cellHasFocus); + + if (isUseHighlightersForCurrentValue() && compoundHighlighter != null && getSelectedIndex() != -1) { + comp = compoundHighlighter.highlight(comp, getComponentAdapter(getSelectedIndex())); + + // this is done to "trick" BasicComboBoxUI.paintCurrentValue which resets all of + // the painted information after asking the list to render the value. the panel + // wrappers receives all of the post-rendering configuration, which is dutifully + // ignored by the real rendering component + wrapper.add(comp); + comp = wrapper; + } + } else { + comp = delegateRenderer.getListCellRendererComponent(list, value, index, + isSelected, cellHasFocus); + + if ((compoundHighlighter != null) && (index >= 0) && (index < getItemCount())) { + comp = compoundHighlighter.highlight(comp, getComponentAdapter(index)); + } + } + + return comp; + } + + // implement RolloverRenderer + + /** + * {@inheritDoc} + * + */ + @Override + public boolean isEnabled() { + return (delegateRenderer instanceof RolloverRenderer) && + ((RolloverRenderer) delegateRenderer).isEnabled(); + } + + /** + * {@inheritDoc} + */ + @Override + public void doClick() { + if (isEnabled()) { + ((RolloverRenderer) delegateRenderer).doClick(); + } + } + } + + @SuppressWarnings("hiding") + protected static class ComboBoxAdapter extends ComponentAdapter { + private final JXComboBox comboBox; + + /** + * Constructs a ListAdapter for the specified target + * JXList. + * + * @param component the target list. + */ + public ComboBoxAdapter(JXComboBox component) { + super(component); + comboBox = component; + } + + /** + * Typesafe accessor for the target component. + * + * @return the target component as a {@link JXComboBox} + */ + public JXComboBox getComboBox() { + return comboBox; + } + + /** + * A safe way to access the combo box's popup visibility. + * + * @return {@code true} if the popup is visible; {@code false} otherwise + */ + protected boolean isPopupVisible() { + if (comboBox.updatingUI) { + return false; + } + + return comboBox.isPopupVisible(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean hasFocus() { + if (isPopupVisible()) { + JList list = getPopupListFor(comboBox); + + return list != null && list.isFocusOwner() && (row == list.getLeadSelectionIndex()); + } + + return comboBox.isFocusOwner(); + } + + /** + * {@inheritDoc} + */ + @Override + public int getRowCount() { + return comboBox.getModel().getSize(); + } + + /** + * {@inheritDoc} + */ + @Override + public Object getValueAt(int row, int column) { + return comboBox.getModel().getElementAt(row); + } + + /** + * {@inheritDoc} + * This is implemented to query the table's StringValueRegistry for an appropriate + * StringValue and use that for getting the string representation. + */ + @Override + public String getStringAt(int row, int column) { + StringValue sv = comboBox.getStringValueRegistry().getStringValue(row, column); + + return sv.getString(getValueAt(row, column)); + } + + /** + * {@inheritDoc} + */ + @Override + public Rectangle getCellBounds() { + JList list = getPopupListFor(comboBox); + + if (list == null) { + assert false; + return new Rectangle(comboBox.getSize()); + } + + return list.getCellBounds(row, row); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isCellEditable(int row, int column) { + return row == -1 && comboBox.isEditable(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isEditable() { + return isCellEditable(row, column); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isSelected() { + if (isPopupVisible()) { + JList list = getPopupListFor(comboBox); + + return list != null && row == list.getLeadSelectionIndex(); + } + + return comboBox.isFocusOwner(); + } + } + + class StringValueKeySelectionManager implements KeySelectionManager, Serializable, UIDependent { + private long timeFactor; + private long lastTime = 0L; + private String prefix = ""; + private String typedString = ""; + + public StringValueKeySelectionManager() { + updateUI(); + } + + @Override + public int selectionForKey(char aKey, ComboBoxModel aModel) { + if (lastTime == 0L) { + prefix = ""; + typedString = ""; + } + + int startIndex = getSelectedIndex(); + + if (EventQueue.getMostRecentEventTime() - lastTime < timeFactor) { + typedString += aKey; + if ((prefix.length() == 1) && (aKey == prefix.charAt(0))) { + // Subsequent same key presses move the keyboard focus to the next + // object that starts with the same letter. + startIndex++; + } else { + prefix = typedString; + } + } else { + startIndex++; + typedString = "" + aKey; + prefix = typedString; + } + + lastTime = EventQueue.getMostRecentEventTime(); + + if (startIndex < 0 || startIndex >= aModel.getSize()) { + startIndex = 0; + } + + for (int i = startIndex, c = aModel.getSize(); i < c; i++) { + String v = getStringAt(i).toLowerCase(); + + if (v.length() > 0 && v.charAt(0) == aKey) { + return i; + } + } + + for (int i = startIndex, c = aModel.getSize(); i < c; i++) { + String v = getStringAt(i).toLowerCase(); + + if (v.length() > 0 && v.charAt(0) == aKey) { + return i; + } + } + + for (int i = 0; i < startIndex; i++) { + String v = getStringAt(i).toLowerCase(); + + if (v.length() > 0 && v.charAt(0) == aKey) { + return i; + } + } + + return -1; + } + + @Override + public void updateUI() { + Long l = (Long) UIManager.get("ComboBox.timeFactor"); + timeFactor = l == null ? 1000L : l.longValue(); + } + } + + private ComboBoxAdapter dataAdapter; + + private DelegatingRenderer delegatingRenderer; + + private StringValueRegistry stringValueRegistry; + + private boolean useHighlightersForCurrentValue = true; + + private CompoundHighlighter compoundHighlighter; + + private ChangeListener highlighterChangeListener; + + private List pendingEvents; + + private boolean isDispatching; + + private boolean updatingUI; + + /** + * Creates a JXComboBox with a default data model. The default data model is an + * empty list of objects. Use addItem to add items. By default the first item in + * the data model becomes selected. + * + * @see DefaultComboBoxModel + */ + public JXComboBox() { + super(); + init(); + } + + /** + * Creates a JXComboBox that takes its items from an existing + * ComboBoxModel. Since the ComboBoxModel is provided, a combo box + * created using this constructor does not create a default combo box model and may impact how + * the insert, remove and add methods behave. + * + * @param model + * the ComboBoxModel that provides the displayed list of items + * @see DefaultComboBoxModel + */ + public JXComboBox(ComboBoxModel model) { + super(model); + init(); + } + + /** + * Creates a JXComboBox that contains the elements in the specified array. By + * default the first item in the array (and therefore the data model) becomes selected. + * + * @param items + * an array of objects to insert into the combo box + * @see DefaultComboBoxModel + */ + public JXComboBox(Object[] items) { + super(items); + init(); + } + + /** + * Creates a JXComboBox that contains the elements in the specified Vector. By + * default the first item in the vector (and therefore the data model) becomes selected. + * + * @param items + * an array of vectors to insert into the combo box + * @see DefaultComboBoxModel + */ + public JXComboBox(Vector items) { + super(items); + init(); + } + + private void init() { + pendingEvents = new ArrayList(); + + if (keySelectionManager == null || keySelectionManager instanceof UIResource) { + setKeySelectionManager(createDefaultKeySelectionManager()); + } + } + + protected static JList getPopupListFor(JComboBox comboBox) { + int count = comboBox.getUI().getAccessibleChildrenCount(comboBox); + + for (int i = 0; i < count; i++) { + Accessible a = comboBox.getUI().getAccessibleChild(comboBox, i); + + if (a instanceof ComboPopup) { + return ((ComboPopup) a).getList(); + } + } + + return null; + } + + /** + * {@inheritDoc} + *

+ * This implementation uses the {@code StringValue} representation of the elements to determine + * the selected item. + */ + @Override + protected KeySelectionManager createDefaultKeySelectionManager() { + return new StringValueKeySelectionManager(); + } + + /** + * {@inheritDoc} + */ + @Override + protected boolean processKeyBinding(KeyStroke ks, final KeyEvent e, int condition, + boolean pressed) { + boolean retValue = super.processKeyBinding(ks, e, condition, pressed); + + if (!retValue && editor != null) { + if (isStartingCellEdit(e)) { + pendingEvents.add(e); + } else if (pendingEvents.size() == 2) { + pendingEvents.add(e); + isDispatching = true; + + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + try { + for (KeyEvent event : pendingEvents) { + editor.getEditorComponent().dispatchEvent(event); + } + + pendingEvents.clear(); + } finally { + isDispatching = false; + } + } + }); + } + } + return retValue; + } + + private boolean isStartingCellEdit(KeyEvent e) { + if (isDispatching) { + return false; + } + + JTable table = (JTable) SwingUtilities.getAncestorOfClass(JTable.class, this); + boolean isOwned = table != null + && !Boolean.FALSE.equals(table.getClientProperty("JTable.autoStartsEdit")); + + return isOwned && e.getComponent() == table; + } + + /** + * @return the unconfigured ComponentAdapter. + */ + protected ComponentAdapter getComponentAdapter() { + if (dataAdapter == null) { + dataAdapter = new ComboBoxAdapter(this); + } + return dataAdapter; + } + + /** + * Convenience to access a configured ComponentAdapter. + * Note: the column index of the configured adapter is always 0. + * + * @param index the row index in view coordinates, must be valid. + * @return the configured ComponentAdapter. + */ + protected ComponentAdapter getComponentAdapter(int index) { + ComponentAdapter adapter = getComponentAdapter(); + adapter.column = 0; + adapter.row = index; + return adapter; + } + + /** + * Returns the StringValueRegistry which defines the string representation for + * each cells. This is strictly for internal use by the table, which has the + * responsibility to keep in synch with registered renderers.

+ * + * Currently exposed for testing reasons, client code is recommended to not use nor override. + * + * @return the current string value registry + */ + protected StringValueRegistry getStringValueRegistry() { + if (stringValueRegistry == null) { + stringValueRegistry = createDefaultStringValueRegistry(); + } + return stringValueRegistry; + } + + /** + * Creates and returns the default registry for StringValues.

+ * + * @return the default registry for StringValues. + */ + protected StringValueRegistry createDefaultStringValueRegistry() { + return new StringValueRegistry(); + } + + /** + * Returns the string representation of the cell value at the given position. + * + * @param row the row index of the cell in view coordinates + * @return the string representation of the cell value as it will appear in the + * table. + */ + public String getStringAt(int row) { + // changed implementation to use StringValueRegistry + StringValue stringValue = getStringValueRegistry().getStringValue(row, 0); + + return stringValue.getString(getItemAt(row)); + } + + private DelegatingRenderer getDelegatingRenderer() { + if (delegatingRenderer == null) { + // only called once... to get hold of the default? + delegatingRenderer = new DelegatingRenderer(); + } + return delegatingRenderer; + } + + /** + * Creates and returns the default cell renderer to use. Subclasses + * may override to use a different type. Here: returns a DefaultListRenderer. + * + * @return the default cell renderer to use with this list. + */ + protected ListCellRenderer createDefaultCellRenderer() { + return new DefaultListRenderer(); + } + + /** + * {@inheritDoc}

+ * + * Overridden to return the delegating renderer which is wrapped around the + * original to support highlighting. The returned renderer is of type + * DelegatingRenderer and guaranteed to not-null

+ * + * @see #setRenderer(ListCellRenderer) + * @see DelegatingRenderer + */ + @Override + public ListCellRenderer getRenderer() { + // PENDING JW: something wrong here - why exactly can't we return super? + // not even if we force the initial setting in init? +// return super.getCellRenderer(); + return getDelegatingRenderer(); + } + + /** + * Returns the renderer installed by client code or the default if none has + * been set. + * + * @return the wrapped renderer. + * @see #setRenderer(ListCellRenderer) + */ + public ListCellRenderer getWrappedRenderer() { + return getDelegatingRenderer().getDelegateRenderer(); + } + + /** + * {@inheritDoc}

+ * + * Overridden to wrap the given renderer in a DelegatingRenderer to support + * highlighting.

+ * + * Note: the wrapping implies that the renderer returned from the getCellRenderer + * is not the renderer as given here, but the wrapper. To access the original, + * use getWrappedCellRenderer. + * + * @see #getWrappedRenderer() + * @see #getRenderer() + */ + @Override + public void setRenderer(ListCellRenderer renderer) { + // PENDING: do something against recursive setting + // == multiple delegation... + ListCellRenderer oldValue = super.getRenderer(); + getDelegatingRenderer().setDelegateRenderer(renderer); + getStringValueRegistry().setStringValue( + renderer instanceof StringValue ? (StringValue) renderer : null, 0); + super.setRenderer(delegatingRenderer); + + if (oldValue == delegatingRenderer) { + firePropertyChange("renderer", null, delegatingRenderer); + } + } + + /** + * PENDING JW to KS: review method naming - doesn't sound like valid English to me (no + * native speaker of course :-). Options are to + * change the property name to usingHighlightersForCurrentValue (as we did in JXMonthView + * after some debate) or stick to getXX. Thinking about it: maybe then the property should be + * usesHighlightersXX, that is third person singular instead of imperative, + * like in tracksVerticalViewport of JTable? + * + * @return {@code true} if the combo box decorates the current value with highlighters; {@code false} otherwise + */ + public boolean isUseHighlightersForCurrentValue() { + return useHighlightersForCurrentValue; + } + + public void setUseHighlightersForCurrentValue(boolean useHighlightersForCurrentValue) { + boolean oldValue = isUseHighlightersForCurrentValue(); + this.useHighlightersForCurrentValue = useHighlightersForCurrentValue; + repaint(); + firePropertyChange("useHighlightersForCurrentValue", oldValue, + isUseHighlightersForCurrentValue()); + } + + /** + * Returns the CompoundHighlighter assigned to the table, null if none. PENDING: open up for + * subclasses again?. + * + * @return the CompoundHighlighter assigned to the table. + * @see #setCompoundHighlighter(CompoundHighlighter) + */ + private CompoundHighlighter getCompoundHighlighter() { + return compoundHighlighter; + } + + /** + * Assigns a CompoundHighlighter to the table, maybe null to remove all Highlighters. + *

+ * + * The default value is null. + *

+ * + * PENDING: open up for subclasses again?. + * + * @param pipeline + * the CompoundHighlighter to use for renderer decoration. + * @see #getCompoundHighlighter() + * @see #addHighlighter(Highlighter) + * @see #removeHighlighter(Highlighter) + * + */ + private void setCompoundHighlighter(CompoundHighlighter pipeline) { + CompoundHighlighter old = getCompoundHighlighter(); + if (old != null) { + old.removeChangeListener(getHighlighterChangeListener()); + } + compoundHighlighter = pipeline; + if (compoundHighlighter != null) { + compoundHighlighter.addChangeListener(getHighlighterChangeListener()); + } + // PENDING: wrong event - the property is either "compoundHighlighter" + // or "highlighters" with the old/new array as value + firePropertyChange("highlighters", old, getCompoundHighlighter()); + } + + /** + * Sets the Highlighters to the column, replacing any old settings. None of the + * given Highlighters must be null. + *

+ * + * @param highlighters + * zero or more not null highlighters to use for renderer decoration. + * + * @see #getHighlighters() + * @see #addHighlighter(Highlighter) + * @see #removeHighlighter(Highlighter) + * + */ + public void setHighlighters(Highlighter... highlighters) { + Contract.asNotNull(highlighters, "highlighters cannot be null or contain null"); + + CompoundHighlighter pipeline = null; + if (highlighters.length > 0) { + pipeline = new CompoundHighlighter(highlighters); + } + + setCompoundHighlighter(pipeline); + } + + /** + * Returns the Highlighters used by this column. Maybe empty, but guarantees to be + * never null. + * + * @return the Highlighters used by this column, guaranteed to never null. + * @see #setHighlighters(Highlighter[]) + */ + public Highlighter[] getHighlighters() { + return getCompoundHighlighter() != null ? getCompoundHighlighter().getHighlighters() + : CompoundHighlighter.EMPTY_HIGHLIGHTERS; + } + + /** + * Adds a Highlighter. Appends to the end of the list of used Highlighters. + *

+ * + * @param highlighter + * the Highlighter to add. + * @throws NullPointerException + * if Highlighter is null. + * + * @see #removeHighlighter(Highlighter) + * @see #setHighlighters(Highlighter[]) + */ + public void addHighlighter(Highlighter highlighter) { + CompoundHighlighter pipeline = getCompoundHighlighter(); + if (pipeline == null) { + setCompoundHighlighter(new CompoundHighlighter(highlighter)); + } else { + pipeline.addHighlighter(highlighter); + } + } + + /** + * Removes the given Highlighter. + *

+ * + * Does nothing if the Highlighter is not contained. + * + * @param highlighter + * the Highlighter to remove. + * @see #addHighlighter(Highlighter) + * @see #setHighlighters(Highlighter...) + */ + public void removeHighlighter(Highlighter highlighter) { + if ((getCompoundHighlighter() == null)) { + return; + } + getCompoundHighlighter().removeHighlighter(highlighter); + } + + /** + * Returns the ChangeListener to use with highlighters. Lazily creates the + * listener. + * + * @return the ChangeListener for observing changes of highlighters, guaranteed to be + * not-null + */ + protected ChangeListener getHighlighterChangeListener() { + if (highlighterChangeListener == null) { + highlighterChangeListener = createHighlighterChangeListener(); + } + + return highlighterChangeListener; + } + + /** + * Creates and returns the ChangeListener observing Highlighters. + *

+ * A property change event is create for a state change. + * + * @return the ChangeListener defining the reaction to changes of highlighters. + */ + protected ChangeListener createHighlighterChangeListener() { + return new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + // need to fire change so JXComboBox can update + firePropertyChange("highlighters", null, getHighlighters()); + repaint(); + } + }; + } + + /** + * {@inheritDoc} + *

+ * Overridden to update renderer and highlighters. + */ + @Override + public void updateUI() { + updatingUI = true; + + try { + super.updateUI(); + + if (keySelectionManager instanceof UIDependent) { + ((UIDependent) keySelectionManager).updateUI(); + } + + ListCellRenderer renderer = getRenderer(); + + if (renderer instanceof UIDependent) { + ((UIDependent) renderer).updateUI(); + } else if (renderer instanceof Component) { + SwingUtilities.updateComponentTreeUI((Component) renderer); + } + + if (compoundHighlighter != null) { + compoundHighlighter.updateUI(); + } + } finally { + updatingUI = false; + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/JXDatePicker.java b/src/main/java/org/jdesktop/swingx/JXDatePicker.java new file mode 100644 index 0000000000..55b6be85c4 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXDatePicker.java @@ -0,0 +1,1005 @@ +/* + * $Id: JXDatePicker.java 4147 2012-02-01 17:13:24Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.calendar.DatePickerFormatter; +import org.jdesktop.swingx.event.EventListenerMap; +import org.jdesktop.swingx.painter.MattePainter; +import org.jdesktop.swingx.plaf.DatePickerAddon; +import org.jdesktop.swingx.plaf.DatePickerUI; +import org.jdesktop.swingx.plaf.LookAndFeelAddons; +import org.jdesktop.swingx.plaf.UIManagerExt; +import org.jdesktop.swingx.util.Contract; + +import javax.swing.*; +import javax.swing.JFormattedTextField.AbstractFormatter; +import javax.swing.JFormattedTextField.AbstractFormatterFactory; +import javax.swing.event.PopupMenuListener; +import javax.swing.text.DefaultFormatterFactory; +import java.awt.*; +import java.awt.event.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyVetoException; +import java.text.*; +import java.util.*; +import java.util.logging.Logger; + +/** + * A component for entering dates with a user interaction similar to a + * JComboBox. The dates can be typed into a text field or selected from a + * JXMonthView which opens in a JXPopupMenu on user's request. + *

+ * + * The date selection is controlled by the JXMonthView's DateSelectionModel. + * This allows the use of all its functionality in the JXDatePicker as well. + * F.i. restrict the selection to a date in the current or next week: + *

+ * + *


+ * Appointment appointment = new Appointment(director,
+ *         "Be sure to have polished shoes!");
+ * JXDatePicker picker = new JXDatePicker();
+ * Calendar calendar = picker.getMonthView().getCalendar();
+ * // starting today if we are in a hurry
+ * calendar.setTime(new Date());
+ * picker.getMonthView().setLowerBound(calendar.getTime());
+ * // end of next week
+ * CalendarUtils.endOfWeek(calendar);
+ * calendar.add(Calendar.WEEK_OF_YEAR);
+ * picker.getMonthView().setUpperBound(calendar.getTime());
+ * 
+ * + * Similar to a JXMonthView, the JXDatePicker fires an ActionEvent when the user + * actively commits or cancels a selection. Interested client code can add a + * ActionListener to be notified by the user action. + * + *

+ * JXDatePicker picker = new JXDatePicker(new Date());
+ * ActionListener l = new ActionListener() {
+ *     public void actionPerformed(ActionEvent e) {
+ *         if (JXDatePicker.COMMIT_KEY.equals(e.getActionCommand)) {
+ *             saveDate(picker.getDate());
+ *         }
+ *     }
+ * };
+ * picker.addActionListener(l);
+ * 
+ * + * Note that ActionListener will not be notified if the user + * edits the date text without hitting the Enter key afterwards. To detect both kinds of + * date change, interested client code can add a PropertyChangeListener. + * + *

+ * JXDatePicker picker = new JXDatePicker(new Date());
+ * PropertyChangeListener listener = new PropertyChangeListener() {
+ *     public void propertyChange(PropertyChangeEvent e) {
+ *         if ("date".equals(e.getPropertyName()) {
+ *              saveDate(picker.getDate());
+ *         }
+ *     }
+ * };
+ * picker.addPropertyChangeListener(listener);
+ * 
+ + * + *

+ * The DateFormats used in the JXDatePicker's are initialized to the default + * formats of the DatePickerFormatter, as defined by the picker's resourceBundle + * DatePicker.properties. Application code can overwrite the picker's default + * + *


+ * picker.setDateFormats(myCustomFormat, myAlternativeCustomFormat);
+ * 
+ * + * PENDING JW: explain what the alternatives are for (after understanding it + * myself ;-) + *

+ * + * The selected Date is a bound property of the JXDatePicker. This allows easy + * binding to a property of a custom bean when using a binding framework. + *

+ * + * Keybindings (as installed by the UI-Delegate) + *

    + *
  • ENTER commits the edited or selected value + *
  • ESCAPE reverts the edited or selected value + *
  • alt-DOWN opens the monthView popup + *
  • shift-F5 if monthView is visible, navigates the monthView to today + * (no effect otherwise) + *
  • F5 commits today + *
+ * + * PENDNG JW: support per-OS keybindings to be installed, currently they are + * hardcoded in our (single) BasicDatePickerUI. + * + * @author Joshua Outwater + * @author Jeanette Winzenburg + * + * @see JXMonthView + * @see org.jdesktop.swingx.calendar.DateSelectionModel + * @see DatePickerFormatter + * + */ +@JavaBean +public class JXDatePicker extends JComponent { + @SuppressWarnings("unused") + private static final Logger LOG = Logger.getLogger(JXDatePicker.class + .getName()); + static { + LookAndFeelAddons.contribute(new DatePickerAddon()); + } + + /** + * UI Class ID + */ + public static final String uiClassID = "DatePickerUI"; + + public static final String EDITOR = "editor"; + public static final String MONTH_VIEW = "monthView"; + + public static final String LINK_PANEL = "linkPanel"; + + /** action command used for commit actionEvent. */ + public static final String COMMIT_KEY = "datePickerCommit"; + /** action command used for cancel actionEvent. */ + public static final String CANCEL_KEY = "datePickerCancel"; + /** action key for navigate home action */ + public static final String HOME_NAVIGATE_KEY = "navigateHome"; + /** action key for commit home action */ + public static final String HOME_COMMIT_KEY = "commitHome"; + + private static final DateFormat[] EMPTY_DATE_FORMATS = new DateFormat[0]; + + /** + * The editable date field that displays the date + */ + private JFormattedTextField _dateField; + + /** + * Popup that displays the month view with controls for + * traversing/selecting dates. + */ + private JPanel _linkPanel; + private MessageFormat _linkFormat; + private Date linkDate; + + private JXMonthView _monthView; + private boolean editable = true; + // PENDING JW: remove - duplication, we have access to super's listenerlist + private EventListenerMap listenerMap; + protected boolean lightWeightPopupEnabled = JPopupMenu.getDefaultLightWeightPopupEnabled(); + + private Date date; + + private PropertyChangeListener monthViewListener; + + + /** + * Intantiates a date picker with no selection and the default + * DatePickerFormatter. + *

+ * The date picker is configured with the default time zone and locale + * + * @see #setTimeZone + * @see #getTimeZone + */ + public JXDatePicker() { + this(null, null); + } + + + + /** + * Intantiates a date picker using the specified time as the initial + * selection and the default + * DatePickerFormatter. + *

+ * The date picker is configured with the default time zone and locale + * + * @param selected the initially selected date + * @see #setTimeZone + * @see #getTimeZone + */ + public JXDatePicker(Date selected) { + this(selected, null); + } + + /** + * Intantiates a date picker with no selection and the default + * DatePickerFormatter. + *

+ * The date picker is configured with the default time zone and specified + * locale + * + * @param locale initial Locale + * @see #setTimeZone + * @see #getTimeZone + */ + public JXDatePicker(Locale locale) { + this(null, locale); + } + + /** + * Intantiates a date picker using the specified time as the initial + * selection and the default + * DatePickerFormatter. + *

+ * The date picker is configured with the default time zone and specified locale + * + * @param selection initially selected Date + * @param locale initial Locale + * @see #setTimeZone + * @see #getTimeZone + * + */ + public JXDatePicker(Date selection, Locale locale) { + init(); + if (locale != null) { + setLocale(locale); + } + // install the controller before setting the date + updateUI(); + setDate(selection); + } + + /** + * Sets the date property.

+ * + * Does nothing if the ui vetos the new date - as might happen if + * the code tries to set a date which is unselectable in the + * monthView's context. The actual value of the new Date is controlled + * by the JXMonthView's DateSelectionModel. The default implementation + * normalizes the date to the start of the day in the model's calendar's + * coordinates, that is all time fields are zeroed. To keep the time fields, + * configure the monthView with a SingleDaySelectionModel. + *

+ * + * At all "stable" (= not editing in date input field nor + * in the monthView) times the date is the same in the + * JXMonthView, this JXDatePicker and the editor. If a new Date + * is set, this invariant is enforced by the DatePickerUI. + *

+ * + * This is a bound property. + * + * + * @param date the new date to set. + * @see #getDate() + * @see org.jdesktop.swingx.calendar.DateSelectionModel + * @see org.jdesktop.swingx.calendar.SingleDaySelectionModel + */ + public void setDate(Date date) { + /* + * JW: + * this is a poor woman's constraint property. + * Introduces explicit coupling to the ui. + * Which is unusual at this place in code. + * + * If needed the date can be made a formal + * constraint property and let the ui add a + * VetoablePropertyListener. + */ + try { + date = getUI().getSelectableDate(date); + } catch (PropertyVetoException e) { + return; + } + Date old = getDate(); + this.date = date; + firePropertyChange("date", old, getDate()); + } + + + + /** + * Returns the currently selected date. + * + * @return Date + */ + public Date getDate() { + return date; + } + + /** + * + */ + private void init() { + listenerMap = new EventListenerMap(); + initMonthView(); + + updateLinkFormat(); + linkDate = _monthView.getToday(); + _linkPanel = new TodayPanel(); + } + + private void initMonthView() { + _monthView = new JXMonthView(); +// _monthView.setSelectionModel(new SingleDaySelectionModel()); + _monthView.setTraversable(true); + _monthView.addPropertyChangeListener(getMonthViewListener()); + } + + /** + * Lazily creates and returns the PropertyChangeListener which listens + * for model's calendar properties. + * + * @return a PropertyChangeListener for monthView's property change notification. + */ + private PropertyChangeListener getMonthViewListener() { + if (monthViewListener == null) { + monthViewListener = new PropertyChangeListener() { + + @Override + public void propertyChange(PropertyChangeEvent evt) { + if ("timeZone".equals(evt.getPropertyName())) { + updateTimeZone((TimeZone) evt.getOldValue(), (TimeZone) evt.getNewValue()); + } + + } + + }; + } + return monthViewListener; + } + + /** + * Callback from monthView timezone changes.

+ * + * NOTE: as timeZone is a bound property of this class we need to + * guarantee the propertyChangeNotification. As this class doesn't + * own this property it must listen to the owner (monthView) and + * re-fire the change. + * + * @param oldValue the old timezone. + * @param newValue the new timezone. + */ + protected void updateTimeZone(TimeZone oldValue, TimeZone newValue) { + firePropertyChange("timeZone", oldValue, newValue); + } + + /** + * Returns the look and feel (L&F) object that renders this component. + * + * @return the DatePickerUI object that renders this component + */ + public DatePickerUI getUI() { + return (DatePickerUI) ui; + } + + /** + * Sets the L&F object that renders this component. + * + * @param ui UI to use for this {@code JXDatePicker} + */ + public void setUI(DatePickerUI ui) { + super.setUI(ui); + } + + /** + * Resets the UI property with the value from the current look and feel. + * + * @see UIManager#getUI + */ + @Override + public void updateUI() { + setUI((DatePickerUI) LookAndFeelAddons.getUI(this, DatePickerUI.class)); + // JW: quick hack around #706-swingx - monthView not updated + // is this complete? how about editor (if not uiResource), linkPanel? + SwingUtilities.updateComponentTreeUI(getMonthView()); + invalidate(); + } + + /** + * @inheritDoc + */ + @Override + public String getUIClassID() { + return uiClassID; + } + + /** + * Replaces the currently installed formatter and factory used by the + * editor. These string formats are defined by the + * java.text.SimpleDateFormat class.

+ * + * Note: The given formats are internally synched to the picker's current + * TimeZone. + * + * @param formats zero or more not null string formats to use. Note that a + * null array is allowed and resets the formatter to use the + * localized default formats. + * @throws NullPointerException any array element is null. + * @see SimpleDateFormat + */ + public void setFormats(String... formats) { + DateFormat[] dateFormats = null; + if (formats != null) { + Contract.asNotNull(formats, + "the array of format strings must not " + + "must not contain null elements"); + dateFormats = new DateFormat[formats.length]; + for (int counter = formats.length - 1; counter >= 0; counter--) { + dateFormats[counter] = new SimpleDateFormat(formats[counter], getLocale()); + } + } + setFormats(dateFormats); + } + + /** + * Replaces the currently installed formatter and factory used by the + * editor.

+ * + * Note: The given formats are internally synched to the picker's current + * TimeZone. + * + * @param formats zero or more not null formats to use. Note that a + * null array is allowed and resets the formatter to use the + * localized default formats. + * @throws NullPointerException any of its elements is null. + */ + public void setFormats(DateFormat... formats) { + if (formats != null) { + Contract.asNotNull(formats, "the array of formats " + "must not contain null elements"); + } + + DateFormat[] old = getFormats(); + _dateField.setFormatterFactory(new DefaultFormatterFactory( + new DatePickerFormatter(formats, getLocale()))); + firePropertyChange("formats", old, getFormats()); + } + + /** + * Returns an array of the formats used by the installed formatter + * if it is a subclass of JXDatePickerFormatter. + * javax.swing.JFormattedTextField.AbstractFormatter + * and javax.swing.text.DefaultFormatter do not have + * support for accessing the formats used. + * + * @return array of formats guaranteed to be not null, but might be empty. + */ + public DateFormat[] getFormats() { + // Dig this out from the factory, if possible, otherwise return null. + AbstractFormatterFactory factory = _dateField.getFormatterFactory(); + if (factory != null) { + AbstractFormatter formatter = factory.getFormatter(_dateField); + if (formatter instanceof DatePickerFormatter) { + return ((DatePickerFormatter) formatter).getFormats(); + } + } + return EMPTY_DATE_FORMATS; + } + + /** + * Return the JXMonthView used in the popup to + * select dates from. + * + * @return the month view component + */ + public JXMonthView getMonthView() { + return _monthView; + } + + /** + * Set the component to use the specified JXMonthView. If the new JXMonthView + * is configured to a different time zone it will affect the time zone of this + * component. + * + * @param monthView month view comopnent. + * @throws NullPointerException if view component is null + * + * @see #setTimeZone + * @see #getTimeZone + */ + public void setMonthView(JXMonthView monthView) { + Contract.asNotNull(monthView, "monthView must not be null"); + JXMonthView oldMonthView = getMonthView(); + TimeZone oldTZ = getTimeZone(); + oldMonthView.removePropertyChangeListener(getMonthViewListener()); + _monthView = monthView; + getMonthView().addPropertyChangeListener(getMonthViewListener()); + firePropertyChange(MONTH_VIEW, oldMonthView, getMonthView()); + firePropertyChange("timeZone", oldTZ, getTimeZone()); + } + + /** + * Gets the time zone. This is a convenience method which returns the time zone + * of the JXMonthView being used. + * + * @return The TimeZone used by the JXMonthView. + */ + public TimeZone getTimeZone() { + return _monthView.getTimeZone(); + } + + /** + * Sets the time zone with the given time zone value. This is a convenience + * method which returns the time zone of the JXMonthView being used.

+ * + * PENDING JW: currently this property is the only property of the monthView + * which is exposed in this api. Not sure why it is here at all. + * It's asymetric (to the other properties) and as such should be either removed + * or the others which might be relevant to a datePicker exposed as well (probably + * hiding the monthView itself as an implementation detail of the ui delegate). + * + * @param tz The TimeZone. + */ + public void setTimeZone(TimeZone tz) { + _monthView.setTimeZone(tz); + } + + + /** + * Returns the date shown in the LinkPanel. + *

+ * PENDING JW: the property should be named linkDate - but that's held by the + * deprecated long returning method. Maybe revisit if we actually remove the other. + * + * @return the date shown in the LinkPanel. + */ + public Date getLinkDay() { + return linkDate; + } + + /** + * Set the date the link will use and the string defining a MessageFormat + * to format the link. If no valid date is in the editor when the popup + * is displayed the popup will focus on the month the linkDate is in. Calling + * this method will replace the currently installed linkPanel and install + * a new one with the requested date and format. + * + * + * @param linkDay the Date to set on the LinkPanel + * @param linkFormatString String used to format the link + * @see MessageFormat + */ + public void setLinkDay(Date linkDay, String linkFormatString) { + setLinkFormat(new MessageFormat(linkFormatString)); + setLinkDay(linkDay); + } + + /** + * Sets the date shown in the TodayPanel. + * + * PENDING JW ... quick api hack for testing. Don't recreate the panel if + * it had been used + * + * @param linkDay the date used in the TodayPanel + */ + public void setLinkDay(Date linkDay) { + this.linkDate = linkDay; + Format[] formats = getLinkFormat().getFormatsByArgumentIndex(); + for (Format format : formats) { + if (format instanceof DateFormat) { + ((DateFormat) format).setTimeZone(getTimeZone()); + } + } + setLinkPanel(new TodayPanel()); + } + + + /** + * @param _linkFormat the _linkFormat to set + */ + protected void setLinkFormat(MessageFormat _linkFormat) { + this._linkFormat = _linkFormat; + } + + /** + * @return the _linkFormat + */ + protected MessageFormat getLinkFormat() { + return _linkFormat; + } + + /** + * Update text on the link panel. + * + */ + private void updateLinkFormat() { + // PENDING JW: move to ui + String linkFormat = UIManagerExt.getString( + "JXDatePicker.linkFormat", getLocale()); + + if (linkFormat != null) { + setLinkFormat(new MessageFormat(linkFormat)); + } else { + setLinkFormat(new MessageFormat("{0,date, dd MMMM yyyy}")); + } + } + + /** + * Return the panel that is used at the bottom of the popup. The default + * implementation shows a link that displays the current month. + * + * @return The currently installed link panel + */ + public JPanel getLinkPanel() { + return _linkPanel; + } + + /** + * Set the panel that will be used at the bottom of the popup. + * PENDING JW: why insist on JPanel? JComponent would be enough? + * + * @param linkPanel The new panel to install in the popup + */ + public void setLinkPanel(JPanel linkPanel) { + JPanel oldLinkPanel = _linkPanel; + _linkPanel = linkPanel; + firePropertyChange(LINK_PANEL, oldLinkPanel, _linkPanel); + } + + /** + * Returns the formatted text field used to edit the date selection. + *

+ * Clients should NOT use this method. It is provided to temporarily support + * the PLAF code. + * + * @return the formatted text field + */ +// @Deprecated + public JFormattedTextField getEditor() { + return _dateField; + } + + /** + * Sets the editor. The editor's editable and enabled properties are + * set the corresponding properties of the JXDatePicker.

+ * + * The default is created and set by the UI delegate. + *

+ * Clients should NOT use this method. It is provided to temporarily support + * the PLAF code. + * + * @param editor the formatted input. + * @throws NullPointerException if editor is null. + * + * @see #getEditor() + */ +// @Deprecated + public void setEditor(JFormattedTextField editor) { + Contract.asNotNull(editor, "editor must not be null"); + JFormattedTextField oldEditor = _dateField; + _dateField = editor; + firePropertyChange(EDITOR, oldEditor, _dateField); + } + + @Override + public void setComponentOrientation(ComponentOrientation orientation) { + super.setComponentOrientation(orientation); + _monthView.setComponentOrientation(orientation); + } + + /** + * Returns true if the current value being edited is valid. + * + * @return true if the current value being edited is valid. + */ + public boolean isEditValid() { + return _dateField.isEditValid(); + } + + /** + * Commits the editor's changes and notifies ActionListeners. + * + * Forces the current value to be taken from the AbstractFormatter and + * set as the current value. This has no effect if there is no current + * AbstractFormatter installed. + * + * @throws ParseException Throws parse exception if the date + * can not be parsed. + */ + public void commitEdit() throws ParseException { + try { + _dateField.commitEdit(); + fireActionPerformed(COMMIT_KEY); + } catch (ParseException e) { + // re-throw + throw e; + } + } + + /** + * Cancels the editor's changes and notifies ActionListeners. + * + */ + public void cancelEdit() { + // hmmm... no direct api? + _dateField.setValue(_dateField.getValue()); + fireActionPerformed(CANCEL_KEY); + } + + /** + * Sets the editable property. If false, ...? + * + * The default value is true. + * + * @param value + * @see #isEditable() + */ + public void setEditable(boolean value) { + boolean oldEditable = isEditable(); + editable = value; + firePropertyChange("editable", oldEditable, editable); + if (editable != oldEditable) { + repaint(); + } + } + + /** + * Returns the editable property. + * + * @return {@code true} if the picker is editable; {@code false} otherwise + */ + public boolean isEditable() { + return editable; + } + + /** + * Returns the font that is associated with the editor of this date picker. + */ + @Override + public Font getFont() { + return getEditor().getFont(); + } + + /** + * Set the font for the editor associated with this date picker. + */ + @Override + public void setFont(final Font font) { + getEditor().setFont(font); + } + + /** + * Sets the lightWeightPopupEnabled property, which + * provides a hint as to whether or not a lightweight + * Component should be used to contain the + * JXDatePicker, versus a heavyweight + * Component such as a Panel + * or a Window. The decision of lightweight + * versus heavyweight is ultimately up to the + * JXDatePicker. Lightweight windows are more + * efficient than heavyweight windows, but lightweight + * and heavyweight components do not mix well in a GUI. + * If your application mixes lightweight and heavyweight + * components, you should disable lightweight popups. + * The default value for the lightWeightPopupEnabled + * property is true, unless otherwise specified + * by the look and feel. Some look and feels always use + * heavyweight popups, no matter what the value of this property. + *

+ * See the article Mixing Heavy and Light Components + * on + * The Swing Connection + * This method fires a property changed event. + * + * @param aFlag if true, lightweight popups are desired + * @beaninfo bound: true + * expert: true + * description: Set to false to require heavyweight popups. + */ + public void setLightWeightPopupEnabled(boolean aFlag) { + boolean oldFlag = lightWeightPopupEnabled; + lightWeightPopupEnabled = aFlag; + firePropertyChange("lightWeightPopupEnabled", oldFlag, lightWeightPopupEnabled); + } + + /** + * Gets the value of the lightWeightPopupEnabled + * property. + * + * @return the value of the lightWeightPopupEnabled + * property + * @see #setLightWeightPopupEnabled + */ + public boolean isLightWeightPopupEnabled() { + return lightWeightPopupEnabled; + } + + /** + * Get the baseline for the specified component, or a value less + * than 0 if the baseline can not be determined. The baseline is measured + * from the top of the component. + * + * @param width Width of the component to determine baseline for. + * @param height Height of the component to determine baseline for. + * @return baseline for the specified component + */ + @Override + public int getBaseline(int width, int height) { + return ((DatePickerUI) ui).getBaseline(width, height); + } + + + /** + * Adds an ActionListener. + *

+ * The ActionListener will receive an ActionEvent when a selection has + * been made. + * + * @param l The ActionListener that is to be notified + */ + public void addActionListener(ActionListener l) { + listenerMap.add(ActionListener.class, l); + } + + /** + * Removes an ActionListener. + * + * @param l The action listener to remove. + */ + public void removeActionListener(ActionListener l) { + listenerMap.remove(ActionListener.class, l); + } + + @Override + @SuppressWarnings("unchecked") + public T[] getListeners(Class listenerType) { + java.util.List listeners = listenerMap.getListeners(listenerType); + T[] result; + if (!listeners.isEmpty()) { + //noinspection unchecked + result = (T[]) java.lang.reflect.Array.newInstance(listenerType, listeners.size()); + result = listeners.toArray(result); + } else { + result = super.getListeners(listenerType); + } + return result; + } + + + /** + * Fires an ActionEvent with the given actionCommand + * to all listeners. + */ + protected void fireActionPerformed(String actionCommand) { + ActionListener[] listeners = getListeners(ActionListener.class); + ActionEvent e = null; + + for (ActionListener listener : listeners) { + if (e == null) { + e = new ActionEvent(this, + ActionEvent.ACTION_PERFORMED, + actionCommand); + } + listener.actionPerformed(e); + } + } + + /** + * Adds a PopupMenuListener.

+ * + * PENDING JW: the canceled method is never called due to internal + * interference in BasicDatePickerUI. Probably need to re-visit that. + * + * @param l the PopupMenuListener to add. + */ + public void addPopupMenuListener(PopupMenuListener l) { + listenerMap.add(PopupMenuListener.class, l); + } + + /** + * Removes a PopupMenuListener. + * + * @param l the PopupMenuListener to remove. + */ + public void removePopupMenuListener(PopupMenuListener l) { + listenerMap.remove(PopupMenuListener.class, l); + } + + /** + * Returns an array containing all PopupMenuListeners which are + * registered to this picker. + * + * @return an array containing all PopupMenuListeners which are + * registered to this picker, guaranteed to be never null. + */ + public PopupMenuListener[] getPopupMenuListeners() { + return getListeners(PopupMenuListener.class); + } + + /** + * Pes: added setLocale method to refresh link text on locale changes + */ + private final class TodayPanel extends JXPanel { + private TodayAction todayAction; + private JXHyperlink todayLink; + + TodayPanel() { + super(new FlowLayout()); + setBackgroundPainter(new MattePainter(new GradientPaint(0, 0, new Color(238, 238, 238), 0, 1, Color.WHITE))); + todayAction = new TodayAction(); + todayLink = new JXHyperlink(todayAction); + todayLink.addMouseListener(createDoubleClickListener()); + Color textColor = new Color(16, 66, 104); + todayLink.setUnclickedColor(textColor); + todayLink.setClickedColor(textColor); + add(todayLink); + } + + /** + * @return + */ + private MouseListener createDoubleClickListener() { + MouseAdapter adapter = new MouseAdapter() { + + @Override + public void mousePressed(MouseEvent e) { + if (e.getClickCount() != 2) return; + todayAction.select = true; + } + + }; + return adapter; + } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + g.setColor(new Color(187, 187, 187)); + g.drawLine(0, 0, getWidth(), 0); + g.setColor(new Color(221, 221, 221)); + g.drawLine(0, 1, getWidth(), 1); + } + + /** + * {@inheritDoc}

+ * Overridden to update the link format and hyperlink text. + */ + @Override + public void setLocale(Locale l) { + super.setLocale(l); + updateLinkFormat(); + todayLink.setText(getLinkFormat().format(new Object[]{getLinkDay()})); + } + + private final class TodayAction extends AbstractAction { + boolean select; + TodayAction() { + super(getLinkFormat().format(new Object[]{getLinkDay()})); + Calendar cal = _monthView.getCalendar(); + cal.setTime(getLinkDay()); + putValue(NAME, getLinkFormat().format(new Object[] {cal.getTime()})); + } + + @Override + public void actionPerformed(ActionEvent ae) { + String key = select ? JXDatePicker.HOME_COMMIT_KEY : JXDatePicker.HOME_NAVIGATE_KEY; + select = false; + Action delegate = getActionMap().get(key); + /* + * PatrykRy: Commit today date only when commit action is enabled. + * Home navigate is always enabled. + */ + if (delegate != null && delegate.isEnabled()) { + delegate.actionPerformed(null); + } + + } + } + } + + +} + \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/JXDialog.java b/src/main/java/org/jdesktop/swingx/JXDialog.java new file mode 100644 index 0000000000..0efa520725 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXDialog.java @@ -0,0 +1,403 @@ +/* + * $Id: JXDialog.java 4172 2012-04-17 14:54:00Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.action.BoundAction; +import org.jdesktop.swingx.plaf.LookAndFeelAddons; +import org.jdesktop.swingx.plaf.UIManagerExt; + +import javax.swing.*; +import javax.swing.plaf.basic.BasicOptionPaneUI; +import java.awt.*; +import java.util.Locale; + +/** + * First cut for enhanced Dialog. The idea is to have a pluggable content + * from which the dialog auto-configures all its "dialogueness". + * + *

    + *
  • accepts a content and configures itself from content's properties - + * replaces the execute action from the appropriate action in content's action map (if any) + * and set's its title from the content's name. + *
  • registers stand-in actions for close/execute with the dialog's RootPane + *
  • registers keyStrokes for esc/enter to trigger the close/execute actions + *
  • takes care of building the button panel using the close/execute actions. + *
+ * + *
    + *
  • TODO: add link to forum discussion, wiki summary? + *
  • PENDING: add support for vetoing the close. + *
  • PENDING: add complete set of constructors + *
  • PENDING: add windowListener to delegate to close action + *
+ * + * @author Jeanette Winzenburg + * @author Karl Schaefer + */ +@JavaBean +public class JXDialog extends JDialog { + + static { + // Hack to enforce loading of SwingX framework ResourceBundle + LookAndFeelAddons.getAddon(); + } + + public static final String EXECUTE_ACTION_COMMAND = "execute"; + public static final String CLOSE_ACTION_COMMAND = "close"; + public static final String UIPREFIX = "XDialog."; + + protected JComponent content; + + /** + * Creates a non-modal dialog with the given component as + * content and without specified owner. A shared, hidden frame will be + * set as the owner of the dialog. + *

+ * @param content the component to show and to auto-configure from. + */ + public JXDialog(JComponent content) { + super(); + setContent(content); + } + + + /** + * Creates a non-modal dialog with the given component as content and the + * specified Frame as owner. + *

+ * @param frame the owner + * @param content the component to show and to auto-configure from. + */ + public JXDialog(Frame frame, JComponent content) { + super(frame); + setContent(content); + } + + /** + * Creates a non-modal dialog with the given component as content and the + * specified Dialog as owner. + *

+ * @param dialog the owner + * @param content the component to show and to auto-configure from. + */ + public JXDialog(Dialog dialog, JComponent content) { + super(dialog); + setContent(content); + } + + /** + * Creates a non-modal dialog with the given component as content and the + * specified Window as owner. + *

+ * @param window the owner + * @param content the component to show and to auto-configure from. + */ + public JXDialog(Window window, JComponent content) { + super(window); + setContentPane(content); + } + + /** + * {@inheritDoc} + */ + @Override + protected JXRootPane createRootPane() { + return new JXRootPane(); + } + + /** + * {@inheritDoc} + */ + @Override + public JXRootPane getRootPane() { + return (JXRootPane) super.getRootPane(); + } + + /** + * Sets the status bar property on the underlying {@code JXRootPane}. + * + * @param statusBar + * the {@code JXStatusBar} which is to be the status bar + * @see #getStatusBar() + * @see JXRootPane#setStatusBar(JXStatusBar) + */ + public void setStatusBar(JXStatusBar statusBar) { + getRootPane().setStatusBar(statusBar); + } + + /** + * Returns the value of the status bar property from the underlying + * {@code JXRootPane}. + * + * @return the {@code JXStatusBar} which is the current status bar + * @see #setStatusBar(JXStatusBar) + * @see JXRootPane#getStatusBar() + */ + public JXStatusBar getStatusBar() { + return getRootPane().getStatusBar(); + } + + /** + * Sets the tool bar property on the underlying {@code JXRootPane}. + * + * @param toolBar + * the {@code JToolBar} which is to be the tool bar + * @see #getToolBar() + * @see JXRootPane#setToolBar(JToolBar) + */ + public void setToolBar(JToolBar toolBar) { + getRootPane().setToolBar(toolBar); + } + + /** + * Returns the value of the tool bar property from the underlying + * {@code JXRootPane}. + * + * @return the {@code JToolBar} which is the current tool bar + * @see #setToolBar(JToolBar) + * @see JXRootPane#getToolBar() + */ + public JToolBar getToolBar() { + return getRootPane().getToolBar(); + } + + /** + * PENDING: widen access - this could be public to make the content really + * pluggable? + * + * @param content + */ + private void setContent(JComponent content) { + if (this.content != null) { + throw new IllegalStateException("content must not be set more than once"); + } + initActions(); + Action contentCloseAction = content.getActionMap().get(CLOSE_ACTION_COMMAND); + if (contentCloseAction != null) { + putAction(CLOSE_ACTION_COMMAND, contentCloseAction); + } + Action contentExecuteAction = content.getActionMap().get(EXECUTE_ACTION_COMMAND); + if (contentExecuteAction != null) { + putAction(EXECUTE_ACTION_COMMAND, contentExecuteAction); + } + this.content = content; + build(); + setTitleFromContent(); + } + + /** + * Infers and sets this dialog's title from the the content. + * Does nothing if content is null. + * + * Here: uses the content's name as title. + */ + protected void setTitleFromContent() { + if (content == null) return; + setTitle(content.getName()); + } + + /** + * pre: content != null. + * + */ + private void build() { + JComponent contentBox = new Box(BoxLayout.PAGE_AXIS); + contentBox.add(content); + JComponent buttonPanel = createButtonPanel(); + contentBox.add(buttonPanel); + contentBox.setBorder(BorderFactory.createEmptyBorder(14, 14, 14, 14)); +// content.applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); + +// fieldPanel.setAlignmentX(); +// buttonPanel.setAlignmentX(Component.RIGHT_ALIGNMENT); + add(contentBox); + + } + + /** + * {@inheritDoc} + * + * Overridden to check if content is available.

+ * PENDING: doesn't make sense - the content is immutable and guaranteed + * to be not null. + */ + @Override + public void setVisible(boolean visible) { + if (content == null) throw + new IllegalStateException("content must be built before showing the dialog"); + super.setVisible(visible); + } + +//------------------------ dynamic locale support + + + /** + * {@inheritDoc}

+ * + * Overridden to set the content's Locale and then updated + * this dialog's internal state.

+ * + * + */ + @Override + public void setLocale(Locale l) { + /* + * NOTE: this is called from super's constructor as one of the + * first methods (prior to setting the rootPane!). So back out + * + */ + if (content != null) { + content.setLocale(l); + updateLocaleState(l); + } + super.setLocale(l); + } + + /** + * Updates this dialog's locale-dependent state. + * + * Here: updates title and actions. + *

+ * + * + * @see #setLocale(Locale) + */ + protected void updateLocaleState(Locale locale) { + setTitleFromContent(); + for (Object key : getRootPane().getActionMap().allKeys()) { + if (key instanceof String) { + Action contentAction = content.getActionMap().get(key); + Action rootPaneAction = getAction(key); + if ((!rootPaneAction.equals(contentAction))) { + String keyString = getUIString((String) key, locale); + if (!key.equals(keyString)) { + rootPaneAction.putValue(Action.NAME, keyString); + } + } + } + } + } + + /** + * The callback method executed when closing the dialog.

+ * Here: calls dispose. + * + */ + public void doClose() { + dispose(); + } + + private void initActions() { + Action defaultAction = createCloseAction(); + putAction(CLOSE_ACTION_COMMAND, defaultAction); + putAction(EXECUTE_ACTION_COMMAND, defaultAction); + } + + private Action createCloseAction() { + String actionName = getUIString(CLOSE_ACTION_COMMAND); + BoundAction action = new BoundAction(actionName, + CLOSE_ACTION_COMMAND); + action.registerCallback(this, "doClose"); + return action; + } + + /** + * create the dialog button controls. + * + * + * @return panel containing button controls + */ + protected JComponent createButtonPanel() { + // PENDING: this is a hack until we have a dedicated ButtonPanel! + JPanel panel = new JPanel(new BasicOptionPaneUI.ButtonAreaLayout(true, 6)) + { + @Override + public Dimension getMaximumSize() { + return getPreferredSize(); + } + }; + + panel.setBorder(BorderFactory.createEmptyBorder(9, 0, 0, 0)); + Action executeAction = getAction(EXECUTE_ACTION_COMMAND); + Action closeAction = getAction(CLOSE_ACTION_COMMAND); + + JButton defaultButton = new JButton(executeAction); + panel.add(defaultButton); + getRootPane().setDefaultButton(defaultButton); + + if (executeAction != closeAction) { + JButton b = new JButton(closeAction); + panel.add(b); + getRootPane().setCancelButton(b); + } + + return panel; + } + + /** + * convenience wrapper to access rootPane's actionMap. + * @param key + * @param action + */ + private void putAction(Object key, Action action) { + getRootPane().getActionMap().put(key, action); + } + + /** + * convenience wrapper to access rootPane's actionMap. + * + * @param key + * @return root pane's ActionMap + */ + private Action getAction(Object key) { + return getRootPane().getActionMap().get(key); + } + + /** + * Returns a potentially localized value from the UIManager. The given key + * is prefixed by this component|s UIPREFIX before doing the + * lookup. The lookup respects this table's current locale + * property. Returns the key, if no value is found. + * + * @param key the bare key to look up in the UIManager. + * @return the value mapped to UIPREFIX + key or key if no value is found. + */ + protected String getUIString(String key) { + return getUIString(key, getLocale()); + } + + /** + * Returns a potentially localized value from the UIManager for the + * given locale. The given key + * is prefixed by this component's UIPREFIX before doing the + * lookup. Returns the key, if no value is found. + * + * @param key the bare key to look up in the UIManager. + * @param locale the locale use for lookup + * @return the value mapped to UIPREFIX + key in the given locale, + * or key if no value is found. + */ + protected String getUIString(String key, Locale locale) { + String text = UIManagerExt.getString(UIPREFIX + key, locale); + return text != null ? text : key; + } +} diff --git a/src/main/java/org/jdesktop/swingx/JXEditorPane.java b/src/main/java/org/jdesktop/swingx/JXEditorPane.java new file mode 100644 index 0000000000..564b5a6d4c --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXEditorPane.java @@ -0,0 +1,869 @@ +/* + * $Id: JXEditorPane.java 4147 2012-02-01 17:13:24Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.action.ActionManager; +import org.jdesktop.swingx.action.Targetable; +import org.jdesktop.swingx.action.TargetableSupport; +import org.jdesktop.swingx.plaf.UIAction; +import org.jdesktop.swingx.search.SearchFactory; +import org.jdesktop.swingx.search.Searchable; + +import javax.swing.*; +import javax.swing.event.CaretEvent; +import javax.swing.event.CaretListener; +import javax.swing.event.UndoableEditEvent; +import javax.swing.event.UndoableEditListener; +import javax.swing.text.*; +import javax.swing.text.html.HTML; +import javax.swing.text.html.HTMLDocument; +import javax.swing.text.html.HTMLEditorKit; +import javax.swing.undo.CannotRedoException; +import javax.swing.undo.CannotUndoException; +import javax.swing.undo.UndoManager; +import java.awt.*; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.event.ActionEvent; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.Vector; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.MatchResult; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + *

+ * {@code JXEditorPane} offers enhanced functionality over the standard {@code + * JEditorPane}. Unlike its parent, {@code JXEdtiorPane} {@link + * JEditorPane#HONOR_DISPLAY_PROPERTIES honors display properties} by default. + * Users can revert to the behavior of {@code JEditorPane} by setting the + * property to {@code false}. + *

+ *

Additional Features

+ *
+ *
+ * Improved text editing
+ *
+ * The standard text component commands for cut, copy, and + * paste used enhanced selection methods. The commands will only be + * active if there is text to cut or copy selected or valid text in the + * clipboard to paste.
+ *
+ * Improved HTML editing
+ *
+ * Using the context-sensitive approach for the standard text commands, {@code + * JXEditorPane} provides HTML editing commands that alter functionality + * depending on the document state. Currently, the user can quick-format the + * document with headers (H# tags), paragraphs, and breaks.
+ *
+ * Built-in UndoManager
+ *
+ * Text components provide {@link UndoableEditEvent}s. {@code JXEditorPane} + * places those events in an {@link UndoManager} and provides + * undo/redo commands. Undo and redo are context-sensitive (like + * the text commands) and will only be active if it is possible to perform the + * command.
+ *
+ * Built-in search
+ *
+ * Using SwingX {@linkplain SearchFactory search mechanisms}, {@code + * JXEditorPane} provides search capabilities, allowing the user to find text + * within the document.
+ *
+ *

Example

+ *

+ * Creating a {@code JXEditorPane} is no different than creating a {@code + * JEditorPane}. However, the following example demonstrates the best way to + * access the improved command functionality. + * + *

+ * JXEditorPane editorPane = new JXEditorPane("some URL");
+ * add(editorPane);
+ * JToolBar toolBar = ActionContainerFactory.createToolBar(editorPane.getCommands[]);
+ * toolBar.addSeparator();
+ * toolBar.add(editorPane.getParagraphSelector());
+ * setToolBar(toolBar);
+ * 
+ *

+ * + * @author Mark Davidson + */ +@JavaBean +public class JXEditorPane extends JEditorPane implements /*Searchable, */Targetable { + + private static final Logger LOG = Logger.getLogger(JXEditorPane.class + .getName()); + + private UndoableEditListener undoHandler; + private UndoManager undoManager; + private CaretListener caretHandler; + private JComboBox selector; + + // The ids of supported actions. Perhaps this should be public. + private final static String ACTION_FIND = "find"; + private final static String ACTION_UNDO = "undo"; + private final static String ACTION_REDO = "redo"; + /* + * These next 3 actions are part of a *HACK* to get cut/copy/paste + * support working in the same way as find, undo and redo. in JTextComponent + * the cut/copy/paste actions are _not_ added to the ActionMap. Instead, + * a default "transfer handler" system is used, apparently to get the text + * onto the system clipboard. + * Since there aren't any CUT/COPY/PASTE actions in the JTextComponent's action + * map, they cannot be referenced by the action framework the same way that + * find/undo/redo are. So, I added the actions here. The really hacky part + * is that by defining an Action to go along with the cut/copy/paste keys, + * I loose the default handling in the cut/copy/paste routines. So, I have + * to remove cut/copy/paste from the action map, call the appropriate + * method (cut, copy, or paste) and then add the action back into the + * map. Yuck! + */ + private final static String ACTION_CUT = "cut"; + private final static String ACTION_COPY = "copy"; + private final static String ACTION_PASTE = "paste"; + + private TargetableSupport targetSupport = new TargetableSupport(this); + private Searchable searchable; + + /** + * Creates a new JXEditorPane. + * The document model is set to null. + */ + public JXEditorPane() { + init(); + } + + /** + * Creates a JXEditorPane based on a string containing + * a URL specification. + * + * @param url the URL + * @exception IOException if the URL is null or + * cannot be accessed + */ + public JXEditorPane(String url) throws IOException { + super(url); + init(); + } + + /** + * Creates a JXEditorPane that has been initialized + * to the given text. This is a convenience constructor that calls the + * setContentType and setText methods. + * + * @param type mime type of the given text + * @param text the text to initialize with; may be null + * @exception NullPointerException if the type parameter + * is null + */ + public JXEditorPane(String type, String text) { + super(type, text); + init(); + } + + /** + * Creates a JXEditorPane based on a specified URL for input. + * + * @param initialPage the URL + * @exception IOException if the URL is null + * or cannot be accessed + */ + public JXEditorPane(URL initialPage) throws IOException { + super(initialPage); + init(); + } + + private void init() { + putClientProperty(HONOR_DISPLAY_PROPERTIES, true); + setEditorKitForContentType("text/html", new SloppyHTMLEditorKit()); + addPropertyChangeListener(new PropertyHandler()); + getDocument().addUndoableEditListener(getUndoableEditListener()); + initActions(); + } + + private class PropertyHandler implements PropertyChangeListener { + @Override + public void propertyChange(PropertyChangeEvent evt) { + String name = evt.getPropertyName(); + if (name.equals("document")) { + Document doc = (Document)evt.getOldValue(); + if (doc != null) { + doc.removeUndoableEditListener(getUndoableEditListener()); + } + + doc = (Document)evt.getNewValue(); + if (doc != null) { + doc.addUndoableEditListener(getUndoableEditListener()); + } + } + } + + } + + // pp for testing + CaretListener getCaretListener() { + return caretHandler; + } + + // pp for testing + UndoableEditListener getUndoableEditListener() { + if (undoHandler == null) { + undoHandler = new UndoHandler(); + undoManager = new UndoManager(); + } + return undoHandler; + } + + /** + * Overidden to perform document initialization based on type. + */ + @Override + public void setEditorKit(EditorKit kit) { + super.setEditorKit(kit); + + if (kit instanceof StyledEditorKit) { + if (caretHandler == null) { + caretHandler = new CaretHandler(); + } + addCaretListener(caretHandler); + } + } + + /** + * Register the actions that this class can handle. + */ + protected void initActions() { + ActionMap map = getActionMap(); + map.put(ACTION_FIND, new Actions(ACTION_FIND)); + map.put(ACTION_UNDO, new Actions(ACTION_UNDO)); + map.put(ACTION_REDO, new Actions(ACTION_REDO)); + map.put(ACTION_CUT, new Actions(ACTION_CUT)); + map.put(ACTION_COPY, new Actions(ACTION_COPY)); + map.put(ACTION_PASTE, new Actions(ACTION_PASTE)); + + KeyStroke findStroke = SearchFactory.getInstance().getSearchAccelerator(); + getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(findStroke, "find"); + } + + // undo/redo implementation + + private class UndoHandler implements UndoableEditListener { + @Override + public void undoableEditHappened(UndoableEditEvent evt) { + undoManager.addEdit(evt.getEdit()); + updateActionState(); + } + } + + /** + * Updates the state of the actions in response to an undo/redo operation.

+ * + */ + private void updateActionState() { + // Update the state of the undo and redo actions + // JW: fiddling with actionManager's actions state? I'm pretty sure + // we don't want that: the manager will get nuts with multiple + // components with different state. + // It's up to whatever manager to listen + // to our changes and update itself accordingly. Which is not + // well supported with the current design ... nobody + // really cares about enabled as it should. + // + Runnable doEnabled = new Runnable() { + @Override + public void run() { + ActionManager manager = ActionManager.getInstance(); + manager.setEnabled(ACTION_UNDO, undoManager.canUndo()); + manager.setEnabled(ACTION_REDO, undoManager.canRedo()); + } + }; + SwingUtilities.invokeLater(doEnabled); + } + + /** + * A small class which dispatches actions. + * TODO: Is there a way that we can make this static? + * JW: these if-constructs are totally crazy ... we live in OO world! + * + */ + private class Actions extends UIAction { + Actions(String name) { + super(name); + } + + @Override + public void actionPerformed(ActionEvent evt) { + String name = getName(); + if (ACTION_FIND.equals(name)) { + find(); + } + else if (ACTION_UNDO.equals(name)) { + try { + undoManager.undo(); + } catch (CannotUndoException ex) { + LOG.info("Could not undo"); + } + updateActionState(); + } + else if (ACTION_REDO.equals(name)) { + try { + undoManager.redo(); + } catch (CannotRedoException ex) { + LOG.info("Could not redo"); + } + updateActionState(); + } else if (ACTION_CUT.equals(name)) { + ActionMap map = getActionMap(); + map.remove(ACTION_CUT); + cut(); + map.put(ACTION_CUT, this); + } else if (ACTION_COPY.equals(name)) { + ActionMap map = getActionMap(); + map.remove(ACTION_COPY); + copy(); + map.put(ACTION_COPY, this); + } else if (ACTION_PASTE.equals(name)) { + ActionMap map = getActionMap(); + map.remove(ACTION_PASTE); + paste(); + map.put(ACTION_PASTE, this); + } + else { + LOG.fine("ActionHandled: " + name); + } + + } + + @Override + public boolean isEnabled(Object sender) { + String name = getName(); + if (ACTION_UNDO.equals(name)) { + return isEditable() && undoManager.canUndo(); + } + if (ACTION_REDO.equals(name)) { + return isEditable() && undoManager.canRedo(); + } + if (ACTION_PASTE.equals(name)) { + if (!isEditable()) return false; + // is this always possible? + boolean dataOnClipboard = false; + try { + dataOnClipboard = getToolkit() + .getSystemClipboard().getContents(null) != null; + } catch (Exception e) { + // can't do anything - clipboard unaccessible + } + return dataOnClipboard; + } + boolean selectedText = getSelectionEnd() + - getSelectionStart() > 0; + if (ACTION_CUT.equals(name)) { + return isEditable() && selectedText; + } + if (ACTION_COPY.equals(name)) { + return selectedText; + } + if (ACTION_FIND.equals(name)) { + return getDocument().getLength() > 0; + } + return true; + } + + + } + + /** + * Retrieves a component which will be used as the paragraph selector. + * This can be placed in the toolbar. + *

+ * Note: This is only valid for the HTMLEditorKit + */ + public JComboBox getParagraphSelector() { + if (selector == null) { + selector = new ParagraphSelector(); + } + return selector; + } + + /** + * A control which should be placed in the toolbar to enable + * paragraph selection. + */ + private class ParagraphSelector extends JComboBox implements ItemListener { + + private Map itemMap; + + public ParagraphSelector() { + + // The item map is for rendering + itemMap = new HashMap(); + itemMap.put(HTML.Tag.P, "Paragraph"); + itemMap.put(HTML.Tag.H1, "Heading 1"); + itemMap.put(HTML.Tag.H2, "Heading 2"); + itemMap.put(HTML.Tag.H3, "Heading 3"); + itemMap.put(HTML.Tag.H4, "Heading 4"); + itemMap.put(HTML.Tag.H5, "Heading 5"); + itemMap.put(HTML.Tag.H6, "Heading 6"); + itemMap.put(HTML.Tag.PRE, "Preformatted"); + + // The list of items + Vector items = new Vector(); + items.addElement(HTML.Tag.P); + items.addElement(HTML.Tag.H1); + items.addElement(HTML.Tag.H2); + items.addElement(HTML.Tag.H3); + items.addElement(HTML.Tag.H4); + items.addElement(HTML.Tag.H5); + items.addElement(HTML.Tag.H6); + items.addElement(HTML.Tag.PRE); + + setModel(new DefaultComboBoxModel(items)); + setRenderer(new ParagraphRenderer()); + addItemListener(this); + setFocusable(false); + } + + @Override + public void itemStateChanged(ItemEvent evt) { + if (evt.getStateChange() == ItemEvent.SELECTED) { + applyTag((HTML.Tag)evt.getItem()); + } + } + + private class ParagraphRenderer extends DefaultListCellRenderer { + + public ParagraphRenderer() { + setOpaque(true); + } + + @Override + public Component getListCellRendererComponent(JList list, + Object value, + int index, + boolean isSelected, + boolean cellHasFocus) { + super.getListCellRendererComponent(list, value, index, isSelected, + cellHasFocus); + + setText((String)itemMap.get(value)); + + return this; + } + } + + + // TODO: Should have a rendererer which does stuff like: + // Paragraph, Heading 1, etc... + } + + /** + * Applys the tag to the current selection + */ + protected void applyTag(HTML.Tag tag) { + Document doc = getDocument(); + if (!(doc instanceof HTMLDocument)) { + return; + } + HTMLDocument hdoc = (HTMLDocument)doc; + int start = getSelectionStart(); + int end = getSelectionEnd(); + + Element element = hdoc.getParagraphElement(start); + MutableAttributeSet newAttrs = new SimpleAttributeSet(element.getAttributes()); + newAttrs.addAttribute(StyleConstants.NameAttribute, tag); + + hdoc.setParagraphAttributes(start, end - start, newAttrs, true); + } + + /** + * The paste method has been overloaded to strip off the tags + * This doesn't really work. + */ + @Override + public void paste() { + Clipboard clipboard = getToolkit().getSystemClipboard(); + Transferable content = clipboard.getContents(this); + if (content != null) { + DataFlavor[] flavors = content.getTransferDataFlavors(); + try { + for (int i = 0; i < flavors.length; i++) { + if (String.class.equals(flavors[i].getRepresentationClass())) { + Object data = content.getTransferData(flavors[i]); + + if (flavors[i].isMimeTypeEqual("text/plain")) { + // This works but we lose all the formatting. + replaceSelection(data.toString()); + break; + } + } + } + } catch (Exception ex) { + // TODO change to something meaningful - when can this acutally happen? + LOG.log(Level.FINE, "What can produce a problem with data flavor?", ex); + } + } + } + + private void find() { + SearchFactory.getInstance().showFindInput(this, getSearchable()); + } + + /** + * + * @return a not-null Searchable for this editor. + */ + public Searchable getSearchable() { + if (searchable == null) { + searchable = new DocumentSearchable(); + } + return searchable; + } + + /** + * sets the Searchable for this editor. If null, a default + * searchable will be used. + * + * @param searchable + */ + public void setSearchable(Searchable searchable) { + this.searchable = searchable; + } + + /** + * A {@code Searchable} implementation for {@code Document}s. + */ + public class DocumentSearchable implements Searchable { + @Override + public int search(String searchString) { + return search(searchString, -1); + } + + @Override + public int search(String searchString, int columnIndex) { + return search(searchString, columnIndex, false); + } + + @Override + public int search(String searchString, int columnIndex, boolean backward) { + Pattern pattern = null; + if (!isEmpty(searchString)) { + pattern = Pattern.compile(searchString, 0); + } + return search(pattern, columnIndex, backward); + } + + /** + * checks if the searchString should be interpreted as empty. + * here: returns true if string is null or has zero length. + * + * TODO: This should be in a utility class. + * + * @param searchString + * @return true if string is null or has zero length + */ + protected boolean isEmpty(String searchString) { + return (searchString == null) || searchString.length() == 0; + } + + @Override + public int search(Pattern pattern) { + return search(pattern, -1); + } + + @Override + public int search(Pattern pattern, int startIndex) { + return search(pattern, startIndex, false); + } + + int lastFoundIndex = -1; + + MatchResult lastMatchResult; + String lastRegEx; + /** + * @return start position of matching string or -1 + */ + @Override + public int search(Pattern pattern, final int startIndex, + boolean backwards) { + if ((pattern == null) + || (getDocument().getLength() == 0) + || ((startIndex > -1) && (getDocument().getLength() < startIndex))) { + updateStateAfterNotFound(); + return -1; + } + + int start = startIndex; + if (maybeExtendedMatch(startIndex)) { + if (foundExtendedMatch(pattern, start)) { + return lastFoundIndex; + } + start++; + } + + int length; + if (backwards) { + start = 0; + if (startIndex < 0) { + length = getDocument().getLength() - 1; + } else { + length = -1 + startIndex; + } + } else { + // start = startIndex + 1; + if (start < 0) + start = 0; + length = getDocument().getLength() - start; + } + Segment segment = new Segment(); + + try { + getDocument().getText(start, length, segment); + } catch (BadLocationException ex) { + LOG.log(Level.FINE, + "this should not happen (calculated the valid start/length) " , ex); + } + + Matcher matcher = pattern.matcher(segment.toString()); + MatchResult currentResult = getMatchResult(matcher, !backwards); + if (currentResult != null) { + updateStateAfterFound(currentResult, start); + } else { + updateStateAfterNotFound(); + } + return lastFoundIndex; + + } + + /** + * Search from same startIndex as the previous search. + * Checks if the match is different from the last (either + * extended/reduced) at the same position. Returns true + * if the current match result represents a different match + * than the last, false if no match or the same. + * + * @param pattern + * @param start + * @return true if the current match result represents a different + * match than the last, false if no match or the same. + */ + private boolean foundExtendedMatch(Pattern pattern, int start) { + // JW: logic still needs cleanup... + if (pattern.pattern().equals(lastRegEx)) { + return false; + } + int length = getDocument().getLength() - start; + Segment segment = new Segment(); + + try { + getDocument().getText(start, length, segment); + } catch (BadLocationException ex) { + LOG.log(Level.FINE, + "this should not happen (calculated the valid start/length) " , ex); + } + Matcher matcher = pattern.matcher(segment.toString()); + MatchResult currentResult = getMatchResult(matcher, true); + if (currentResult != null) { + // JW: how to compare match results reliably? + // the group().equals probably isn't the best idea... + // better check pattern? + if ((currentResult.start() == 0) && + (!lastMatchResult.group().equals(currentResult.group()))) { + updateStateAfterFound(currentResult, start); + return true; + } + } + return false; + } + + /** + * Checks if the startIndex is a candidate for trying a re-match. + * + * + * @param startIndex + * @return true if the startIndex should be re-matched, false if not. + */ + private boolean maybeExtendedMatch(final int startIndex) { + return (startIndex >= 0) && (startIndex == lastFoundIndex); + } + + /** + * @param currentResult + * @param offset + * @return the start position of the selected text + */ + private int updateStateAfterFound(MatchResult currentResult, final int offset) { + int end = currentResult.end() + offset; + int found = currentResult.start() + offset; + select(found, end); + getCaret().setSelectionVisible(true); + lastFoundIndex = found; + lastMatchResult = currentResult; + lastRegEx = ((Matcher) lastMatchResult).pattern().pattern(); + return found; + } + + /** + * @param matcher + * @param useFirst whether or not to return after the first match is found. + * @return MatchResult or null + */ + private MatchResult getMatchResult(Matcher matcher, boolean useFirst) { + MatchResult currentResult = null; + while (matcher.find()) { + currentResult = matcher.toMatchResult(); + if (useFirst) break; + } + return currentResult; + } + + /** + */ + private void updateStateAfterNotFound() { + lastFoundIndex = -1; + lastMatchResult = null; + lastRegEx = null; + setCaretPosition(getSelectionEnd()); + } + + } + + @Override + public boolean hasCommand(Object command) { + return targetSupport.hasCommand(command); + } + + @Override + public Object[] getCommands() { + return targetSupport.getCommands(); + } + + @Override + public boolean doCommand(Object command, Object value) { + return targetSupport.doCommand(command, value); + } + + /** + * {@inheritDoc} + */ + @Override + public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { + switch(orientation) { + case SwingConstants.VERTICAL: + return getFontMetrics(getFont()).getHeight(); + case SwingConstants.HORIZONTAL: + return getFontMetrics(getFont()).charWidth('M'); + default: + throw new IllegalArgumentException("Invalid orientation: " + orientation); + } + } + + /** + * Listens to the caret placement and adjusts the editing + * properties as appropriate. + * + * Should add more attributes as required. + */ + private class CaretHandler implements CaretListener { + @Override + public void caretUpdate(CaretEvent evt) { + StyledDocument document = (StyledDocument)getDocument(); + int dot = evt.getDot(); + //SwingX #257--ensure display shows the valid attributes + dot = dot > 0 ? dot - 1 : dot; + + Element elem = document.getCharacterElement(dot); + AttributeSet set = elem.getAttributes(); + + // JW: see comment in updateActionState + ActionManager manager = ActionManager.getInstance(); + manager.setSelected("font-bold", StyleConstants.isBold(set)); + manager.setSelected("font-italic", StyleConstants.isItalic(set)); + manager.setSelected("font-underline", StyleConstants.isUnderline(set)); + + elem = document.getParagraphElement(dot); + set = elem.getAttributes(); + + // Update the paragraph selector if applicable. + if (selector != null) { + selector.setSelectedItem(set.getAttribute(StyleConstants.NameAttribute)); + } + + switch (StyleConstants.getAlignment(set)) { + // XXX There is a bug here. the setSelected method + // should only affect the UI actions rather than propagate + // down into the action map actions. + case StyleConstants.ALIGN_LEFT: + manager.setSelected("left-justify", true); + break; + + case StyleConstants.ALIGN_CENTER: + manager.setSelected("center-justify", true); + break; + + case StyleConstants.ALIGN_RIGHT: + manager.setSelected("right-justify", true); + break; + } + } + } + + /** + * Handles sloppy HTML. This implementation currently only looks for + * tags that have a / at the end (self-closing tags) and fixes them + * to work with the version of HTML supported by HTMLEditorKit + *

TODO: Need to break this functionality out so it can take pluggable + * replacement code blocks, allowing people to write custom replacement + * routines. The idea is that with some simple modifications a lot more + * sloppy HTML can be rendered correctly. + * + * @author rbair + */ + private static final class SloppyHTMLEditorKit extends HTMLEditorKit { + @Override + public void read(Reader in, Document doc, int pos) throws IOException, BadLocationException { + //read the reader into a String + StringBuffer buffer = new StringBuffer(); + int length; + char[] data = new char[1024]; + while ((length = in.read(data)) != -1) { + buffer.append(data, 0, length); + } + //TODO is this regex right? + StringReader reader = new StringReader(buffer.toString().replaceAll("/>", ">")); + super.read(reader, doc, pos); + } + } +} + diff --git a/src/main/java/org/jdesktop/swingx/JXErrorPane.java b/src/main/java/org/jdesktop/swingx/JXErrorPane.java new file mode 100644 index 0000000000..4abde6853f --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXErrorPane.java @@ -0,0 +1,654 @@ +/* + * $Id: JXErrorPane.java 4158 2012-02-03 18:29:40Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.error.ErrorInfo; +import org.jdesktop.swingx.error.ErrorReporter; +import org.jdesktop.swingx.plaf.ErrorPaneAddon; +import org.jdesktop.swingx.plaf.ErrorPaneUI; +import org.jdesktop.swingx.plaf.LookAndFeelAddons; + +import javax.swing.*; +import java.awt.*; +import java.lang.reflect.InvocationTargetException; + +/** + *

JXErrorPane is a common error component suitable for displaying errors, + * warnings, and exceptional application behavior to users.

+ * + *

User interaction with the JXErrorPane includes the ability to + * view details associated with the error. This is the primary feature that differentiates + * JXErrorPane from JOptionPane. In addition, + * JXErrorPane specializes in handling unrecoverable errors. If you + * need an error dialog that allows the user to take some action to recover + * from an error (such as "Repair Disk", "Replace All", etc) then you should + * use JOptionPane.

+ * + *

Data and application state associated with an error are encapsulated + * in the {@link ErrorInfo} class. The + * {@code JXErrorPane} displays the data contained in the {@code ErrorInfo}. + * In addition, {@code ErrorInfo} is passed to the + * {@link ErrorReporter} if the user decides to report + * the incident.

+ * + *

Basic Usage

+ *

Typically, the JXErrorPane + * is not created and displayed directly. Instead, one of the static showXXX methods + * are called that create and display the JXErrorPane in a + * JDialog, JFrame, or JInternalFrame.

+ * + *

These static showXXX methods all follow the same pattern, namely ( + * where XXX could be one of Dialog, Frame, or InternalFrame): + *

    + *
  • showXXX(Throwable e): This usage allows you to show a default error + * window, detailing the error
  • + *
  • showXXX(Component owner, ErrorInfo info): This usage shows an + * error dialog based on the given ErrorInfo. The component + * argument is the component over which the dialog should be centered.
  • + *
  • showXXX(Component owner, JXErrorPane pane): This usage shows + * an error dialog using the given error pane. This allows you to completely + * modify the pane (perhaps installing a custom UI delegate, etc) to present + * to the user
  • + *
  • createXXX(Component owner, JXErrorPane pane): Creates and returns + * a dialog for presenting the given JXErrorPane, but does not + * show it. This allows the developer to modify properties of the dialog + * prior to display
  • + *

+ * + *

Following are some examples and further discussion regarding some of these + * static methods. Example of the most basic usage: + *


+ *      try {
+ *          //do stuff.... something throws an exception in here
+ *      } catch (Exception e) {
+ *          JXErrorPane.showDialog(e);
+ *      }
+ * 
. Alternatively there are showFrame and + * showInternalFrame variants of each of the showDialog + * methods described in this API.

+ * + *

While this is the simplest usage, it is not the recommended approach for + * most errors since it yields the most difficult messages for users to understand. + * Instead it is recommended to provide a more useful message for users. For example: + *


+ *      URL url = null;
+ *      try {
+ *          url = new URL(userSuppliedUrl);
+ *      } catch (MalformedURLException e) {
+ *          String msg = "The web resource you entered is not formatted"
+ *                      + " correctly.";
+ *          String details = "<html>Web resources should begin with \"http://\""
+ *                      + " and cannot contain any spaces. Below are a few"
+ *                      + " more guidelines.<ul>"
+ *                      + getURLGuidelines()
+ *                      + "</ul></html>";
+ *          JXErrorPane.showDialog(myWindow, "Unknown Resource", msg, details, e);
+ *          return false;
+ *      }
+ * 

+ * + *

Before showing the JXErrorPane in a frame or dialog, you may modify + * the appearance and behavior of the JXErrorPane by setting one or more of its bean + * properties. For example, to modify the icon shown with a particular + * instance of a JXErrorPane, you might do the following: + *


+ *      JXErrorPane pane = new JXErrorPane();
+ *      pane.setErrorIcon(myErrorIcon);
+ *      pane.setErrorInfo(new ErrorInfo("Fatal Error", exception));
+ *      JXErrorPane.showDialog(null, pane);
+ * 

+ * + *

JXErrorPane may also be configured with a "Report" button which allows + * the user to send a bug report, typically through email. This is done through + * the pluggable {@link ErrorReporter} class. Simply instantiate + * some custom subclass of ErrorReporter and pass the instance into the + * {@link #setErrorReporter} method.

+ * + *

JXErrorPane can also be used for displaying fatal error messages to + * users. Fatal messages indicate a serious error in the application that cannot + * be corrected and that must result in the termination of the application. + * After the close of a fatal error dialog, the application should + * automatically exit. Fatal messages are identified by the Level + * of the ErrorInfo being + * {@link org.jdesktop.swingx.error.ErrorLevel}.FATAL.

+ * + *

By default, when Fatal error dialogs are closed the application exits with + * a code of "1". In other words, System.exit(1). If you wish to implement + * custom handling, you can replace the default fatal action in the ActionMap + * of the JXErrorPane instance. If you specify a custom fatal + * action, then the default action of calling + * System.exit will not occur. You are therefore responsible for shutting down + * the application.

+ * + *

UI Default Keys

+ *

TODO

+ * JXErrorPane.errorIcon + * or, if not specified, JOptionPane.errorIcon + * JXErrorPane.warningIcon + * or, if not specified, JOptionPane.warningIcon + * JXErrorPane.details_contract_text (ignored on Mac OS X) + * JXErrorPane.details_expand_text (ignored on Mac OS X) + * JXErrorPane.mac.details_contract_text + * JXErrorPane.mac.details_expand_text + * Tree.expandedIcon (on Mac OS X) + * Tree.collapsedIcon (on Mac OS X) + * + *

Customizing the Look and Feel

+ *

TODO

+ * + * + * @status REVIEWED + * + * @author Richard Bair + * @author Alexander Zuev + * @author Shai Almog + * @author rah003 + */ +@JavaBean +public class JXErrorPane extends JComponent { + //---------------------------------------------------- static properties + /** + * Name of the Action used for reporting errors + */ + public static final String REPORT_ACTION_KEY = "report-action"; + /** + * Name of the Action used for fatal errors + */ + public static final String FATAL_ACTION_KEY = "fatal-action"; + /** + * UI Class ID + */ + public final static String uiClassID = "ErrorPaneUI"; + + /** + */ + static { + LookAndFeelAddons.contribute(new ErrorPaneAddon()); + } + + //-------------------------------------------------- instance properties + + /** + * ErrorInfo that contains all the information prepared for + * reporting. + */ + private ErrorInfo errorInfo = new ErrorInfo("Error", "Normally this place contains problem description.\n You see this text because one of the following reasons:\n * Either it is a test\n * Developer have not provided error details\n * This error message was invoked unexpectedly and there are no more details available", null, null, null, null, null); + /** + * The Icon to use, regardless of the error message. The UI delegate is + * responsible for setting this icon, if the developer has not specified + * the icon. + */ + private Icon icon; + /** + * The delegate to use for reporting errors. + */ + private ErrorReporter reporter; + + //--------------------------------------------------------- constructors + + /** + * Create a new JXErrorPane. + */ + public JXErrorPane() { + super(); + updateUI(); + } + + //------------------------------------------------------------- UI Logic + + /** + * Returns the look and feel (L&F) object that renders this component. + * + * @return the {@link ErrorPaneUI} object that renders this component + */ + public ErrorPaneUI getUI() { + return (ErrorPaneUI)ui; + } + + /** + * Sets the look and feel (L&F) object that renders this component. + * + * @param ui + * the ErrorPaneUI L&F object + * @see javax.swing.UIDefaults#getUI + * @beaninfo bound: true hidden: true attribute: visualUpdate true + * description: The UI object that implements the Component's + * LookAndFeel. + */ + public void setUI(ErrorPaneUI ui) { + super.setUI(ui); + } + + /** + * Returns the name of the L&F class that renders this component. + * + * @return the string {@link #uiClassID} + * @see JComponent#getUIClassID + * @see javax.swing.UIDefaults#getUI + */ + @Override + public String getUIClassID() { + return uiClassID; + } + + /** + * Notification from the UIManager that the L&F has changed. + * Replaces the current UI object with the latest version from the + * UIManager. + * + * @see JComponent#updateUI + */ + @Override + public void updateUI() { + setUI((ErrorPaneUI) LookAndFeelAddons + .getUI(this, ErrorPaneUI.class)); + } + + //-------------------------------------------- public methods/properties + + /** + * Sets the ErrorInfo for this dialog. ErrorInfo can't be null. + * + * @param info ErrorInfo that incorporates all the details about the error. Null value is not supported. + */ + public void setErrorInfo(ErrorInfo info) { + if (info == null) { + throw new NullPointerException("ErrorInfo can't be null. Provide valid ErrorInfo object."); + } + ErrorInfo old = this.errorInfo; + this.errorInfo = info; + firePropertyChange("errorInfo", old, this.errorInfo); + } + + /** + * Gets the JXErrorPane's ErrorInfo + * + * @return ErrorInfo assigned to this dialog + */ + public ErrorInfo getErrorInfo() { + return errorInfo; + } + + /** + * Specifies the icon to use + * + * @param icon the Icon to use. May be null. + */ + public void setIcon(Icon icon) { + Icon old = this.icon; + this.icon = icon; + firePropertyChange("icon", old, this.icon); + } + + /** + * Returns the Icon used + * + * @return the Icon + */ + public Icon getIcon() { + return icon; + } + + /** + * Sets the {@link ErrorReporter} delegate to use. This delegate is called + * automatically when the report action is fired. + * + * @param reporter the ErrorReporter to use. If null, the report button will + * not be shown in the error dialog. + */ + public void setErrorReporter(ErrorReporter reporter) { + ErrorReporter old = getErrorReporter(); + this.reporter = reporter; + firePropertyChange("errorReporter", old, getErrorReporter()); + } + + /** + * Gets the {@link ErrorReporter} delegate in use. + * + * @return the ErrorReporter. May be null. + */ + public ErrorReporter getErrorReporter() { + return reporter; + } + + //------------------------------------------------------- static methods + + /** + *

Constructs and shows the error dialog for the given exception. The + * exceptions message will be the errorMessage, and the stacktrace will form + * the details for the error dialog.

+ * + *

This method may be called from any thread. It will actually show the error + * dialog on the AWT event dispatch thread. This method blocks. If called + * on the EDT, the dialog shown will be modal. Otherwise, this thread will + * block until the error dialog has been shown and hidden on the EDT.

+ * + * @param e Exception that contains information about the error cause and stack trace + */ + public static void showDialog(Throwable e) { + ErrorInfo ii = new ErrorInfo(null, null, null, null, e, null, null); + showDialog(null, ii); + } + + /** + *

Constructs and shows the error dialog, using the given + * ErrorInfo to initialize the view.

+ * + *

This method may be called from any thread. It will actually show the error + * dialog on the AWT event dispatch thread. This method blocks. If called + * on the EDT, the dialog shown will be modal. Otherwise, this thread will + * block until the error dialog has been shown and hidden on the EDT.

+ * + * @param owner Owner of this error dialog. Determines the Window in which the dialog + * is displayed; if the owner has + * no Window, a default Frame is used + * @param info ErrorInfo that incorporates all the information about the error + */ + public static void showDialog(Component owner, ErrorInfo info) { + JXErrorPane pane = new JXErrorPane(); + pane.setErrorInfo(info); + showDialog(owner, pane); + } + + /** + *

Constructs and shows the error dialog, using the given + * JXErrorPane for the view portion of the dialog.

+ * + *

This method may be called from any thread. It will actually show the error + * dialog on the AWT event dispatch thread. This method blocks. If called + * on the EDT, the dialog shown will be modal. Otherwise, this thread will + * block until the error dialog has been shown and hidden on the EDT.

+ * + * @param owner Owner of this error dialog. Determines the Window in which the dialog + * is displayed; if the owner has + * no Window, a default Frame is used + * @param pane JXErrorPane which will form the content area + * of the dialog. + */ + public static void showDialog(final Component owner, final JXErrorPane pane) { + Runnable r = new Runnable() { + @Override + public void run() { + JDialog dlg = createDialog(owner, pane); + dlg.setVisible(true); + } + }; + + if (!SwingUtilities.isEventDispatchThread()) { + try { + SwingUtilities.invokeAndWait(r); + } catch (InvocationTargetException ex) { + ex.printStackTrace(); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + } else { + r.run(); + } + } + + /** + *

Constructs and returns an error dialog, using the given + * JXErrorPane for the view portion of the dialog.

+ * + *

This method may be called from any thread. It does not block. The + * caller is responsible for ensuring that the dialog is shown and manipulated + * on the AWT event dispatch thread. A common way to do this is to use + * SwingUtilities.invokeAndWait or + * SwingUtilities.invokeLater().

+ * + * @param owner Owner of this error dialog. Determines the Window in which the dialog + * is displayed; if the owner has + * no Window, a default Frame is used + * @param pane JXErrorPane which will form the content area + * of the dialog. + * @return a JDialog configured to display the error. + */ + public static JDialog createDialog(Component owner, JXErrorPane pane) { + JDialog window = pane.getUI().getErrorDialog(owner); + // If the owner is null applies orientation of the shared + // hidden window used as owner. + if(owner != null) { + pane.applyComponentOrientation(owner.getComponentOrientation()); + } else { + pane.applyComponentOrientation(window.getComponentOrientation()); + } + window.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + window.pack(); + window.setLocationRelativeTo(owner); + return window; + } + + /** + *

Constructs and shows the error frame for the given exception. The + * exceptions message will be the errorMessage, and the stacktrace will form + * the details for the error dialog.

+ * + *

This method may be called from any thread. It will actually show the error + * dialog on the AWT event dispatch thread. This method blocks. If called + * on the EDT, the frame shown will be modal. Otherwise, this thread will + * block until the error frame has been shown and hidden on the EDT.

+ * + * @param e Exception that contains information about the error cause and stack trace + */ + public static void showFrame(Throwable e) { + ErrorInfo ii = new ErrorInfo(null, null, null, null, e, null, null); + showFrame(null, ii); + } + + /** + *

Constructs and shows the error frame, using the given + * ErrorInfo to initialize the view.

+ * + *

This method may be called from any thread. It will actually show the error + * dialog on the AWT event dispatch thread. This method blocks. If called + * on the EDT, the frame shown will be modal. Otherwise, this thread will + * block until the error frame has been shown and hidden on the EDT.

+ * + * @param owner Owner of this error frame. Determines the Window in which the frame + * is displayed; if the owner has + * no Window, a default Frame is used + * @param info ErrorInfo that incorporates all the information about the error + */ + public static void showFrame(Component owner, ErrorInfo info) { + JXErrorPane pane = new JXErrorPane(); + pane.setErrorInfo(info); + showFrame(owner, pane); + } + + /** + *

Constructs and shows the error frame, using the given + * JXErrorPane for the view portion of the frame.

+ * + *

This method may be called from any thread. It will actually show the error + * dialog on the AWT event dispatch thread. This method blocks. If called + * on the EDT, the frame shown will be modal. Otherwise, this thread will + * block until the error frame has been shown and hidden on the EDT.

+ * + * @param owner Owner of this error frame. Determines the Window in which the dialog + * is displayed; if the owner has + * no Window, a default Frame is used + * @param pane JXErrorPane which will form the content area + * of the frame. + */ + public static void showFrame(final Component owner, final JXErrorPane pane) { + Runnable r = new Runnable() { + @Override + public void run() { + JFrame window = createFrame(owner, pane); + window.setVisible(true); + } + }; + + if (!SwingUtilities.isEventDispatchThread()) { + try { + SwingUtilities.invokeAndWait(r); + } catch (InvocationTargetException ex) { + ex.printStackTrace(); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + } else { + r.run(); + } + } + + /** + *

Constructs and returns an error frame, using the given + * JXErrorPane for the view portion of the frame.

+ * + *

This method may be called from any thread. It does not block. The + * caller is responsible for ensuring that the frame is shown and manipulated + * on the AWT event dispatch thread. A common way to do this is to use + * SwingUtilities.invokeAndWait or + * SwingUtilities.invokeLater().

+ * + * @param owner Owner of this error frame. Determines the Window in which the frame + * is displayed; if the owner has + * no Window, a default Frame is used + * @param pane JXErrorPane which will form the content area + * of the frame. + * @return a JFrame configured to display the error. + */ + public static JFrame createFrame(Component owner, JXErrorPane pane) { + JFrame window = pane.getUI().getErrorFrame(owner); + // If the owner is null applies orientation of the shared + // hidden window used as owner. + if(owner != null) { + pane.applyComponentOrientation(owner.getComponentOrientation()); + } else { + pane.applyComponentOrientation(window.getComponentOrientation()); + } + window.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + window.pack(); +// window.setLocationRelativeTo(owner); + return window; + } + + /** + *

Constructs and shows the error frame for the given exception. The + * exceptions message will be the errorMessage, and the stacktrace will form + * the details for the error dialog.

+ * + *

This method may be called from any thread. It will actually show the error + * dialog on the AWT event dispatch thread. This method blocks. If called + * on the EDT, the frame shown will be modal. Otherwise, this thread will + * block until the error frame has been shown and hidden on the EDT.

+ * + * @param e Exception that contains information about the error cause and stack trace + */ + public static void showInternalFrame(Throwable e) { + ErrorInfo ii = new ErrorInfo(null, null, null, null, e, null, null); + showInternalFrame(null, ii); + } + + /** + *

Constructs and shows the error frame, using the given + * ErrorInfo to initialize the view.

+ * + *

This method may be called from any thread. It will actually show the error + * dialog on the AWT event dispatch thread. This method blocks. If called + * on the EDT, the frame shown will be modal. Otherwise, this thread will + * block until the error frame has been shown and hidden on the EDT.

+ * + * @param owner Owner of this error frame. Determines the Window in which the frame + * is displayed; if the owner has + * no Window, a default Frame is used + * @param info ErrorInfo that incorporates all the information about the error + */ + public static void showInternalFrame(Component owner, ErrorInfo info) { + JXErrorPane pane = new JXErrorPane(); + pane.setErrorInfo(info); + showInternalFrame(owner, pane); + } + + /** + *

Constructs and shows the error frame, using the given + * JXErrorPane for the view portion of the frame.

+ * + *

This method may be called from any thread. It will actually show the error + * dialog on the AWT event dispatch thread. This method blocks. If called + * on the EDT, the frame shown will be modal. Otherwise, this thread will + * block until the error frame has been shown and hidden on the EDT.

+ * + * @param owner Owner of this error frame. Determines the Window in which the dialog + * is displayed; if the owner has + * no Window, a default Frame is used + * @param pane JXErrorPane which will form the content area + * of the frame. + */ + public static void showInternalFrame(final Component owner, final JXErrorPane pane) { + Runnable r = new Runnable() { + @Override + public void run() { + JInternalFrame window = createInternalFrame(owner, pane); + window.setVisible(true); + } + }; + + if (!SwingUtilities.isEventDispatchThread()) { + try { + SwingUtilities.invokeAndWait(r); + } catch (InvocationTargetException ex) { + ex.printStackTrace(); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + } else { + r.run(); + } + } + + /** + *

Constructs and returns an error frame, using the given + * JXErrorPane for the view portion of the frame.

+ * + *

This method may be called from any thread. It does not block. The + * caller is responsible for ensuring that the frame is shown and manipulated + * on the AWT event dispatch thread. A common way to do this is to use + * SwingUtilities.invokeAndWait or + * SwingUtilities.invokeLater().

+ * + * @param owner Owner of this error frame. Determines the Window in which the frame + * is displayed; if the owner has + * no Window, a default Frame is used + * @param pane JXErrorPane which will form the content area + * of the frame. + * @return a JInternalFrame configured to display the error. + */ + public static JInternalFrame createInternalFrame(Component owner, JXErrorPane pane) { + JInternalFrame window = pane.getUI().getErrorInternalFrame(owner); + // If the owner is null applies orientation of the shared + // hidden window used as owner. + if(owner != null) { + pane.applyComponentOrientation(owner.getComponentOrientation()); + } else { + pane.applyComponentOrientation(window.getComponentOrientation()); + } + window.setDefaultCloseOperation(JInternalFrame.DISPOSE_ON_CLOSE); + window.pack(); + //TODO! +// window.setLocationRelativeTo(owner); + return window; + } + +} diff --git a/src/main/java/org/jdesktop/swingx/JXFindBar.java b/src/main/java/org/jdesktop/swingx/JXFindBar.java new file mode 100644 index 0000000000..e13f58866e --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXFindBar.java @@ -0,0 +1,178 @@ +/* + * $Id: JXFindBar.java 4147 2012-02-01 17:13:24Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.search.Searchable; + +import javax.swing.*; +import java.awt.*; + +/** + * A simple low-intrusion default widget for incremental search. + * + * Actions registered (in addition to super): + *
    + *
  • {@link JXDialog#CLOSE_ACTION_COMMAND} - an action bound to this + * component's cancel method. The method itself is an empty implementation: + * Subclassing clients can override the method, all clients can register a + * custom action. + *
+ * + * Key bindings: + *
    + *
  • ESCAPE - calls action registered for + * {@link JXDialog#CLOSE_ACTION_COMMAND} + *
+ * + * This implementation uses textfield coloring as not-found visualization. + * + *

+ * PENDING: the coloring needs to be read from the UIManager instead of + * hardcoding. + * + *

+ * PENDING: the state transition of found/non-found coloring needs clean-up - + * there are spurious problems when re-using the same instance (as SearchFactory + * does). + * + * @author Jeanette Winzenburg + * + */ +@JavaBean +public class JXFindBar extends JXFindPanel { + + protected Color previousBackgroundColor; + + protected Color previousForegroundColor; + + // PENDING: need to read from UIManager + protected Color notFoundBackgroundColor = Color.decode("#FF6666"); + + protected Color notFoundForegroundColor = Color.white; + + protected JButton findNext; + + protected JButton findPrevious; + + public JXFindBar() { + this(null); + } + + public JXFindBar(Searchable searchable) { + super(searchable); + getPatternModel().setIncremental(true); + getPatternModel().setWrapping(true); + } + + @Override + public void setSearchable(Searchable searchable) { + super.setSearchable(searchable); + match(); + } + + /** + * here: set textfield colors to not-found colors. + */ + @Override + protected void showNotFoundMessage() { + //JW: quick hack around #487-swingx - NPE in setSearchable + if (searchField == null) return; + searchField.setForeground(notFoundForegroundColor); + searchField.setBackground(notFoundBackgroundColor); + } + + /** + * here: set textfield colors to normal. + */ + @Override + protected void showFoundMessage() { + //JW: quick hack around #487-swingx - NPE in setSearchable + if (searchField == null) return; + searchField.setBackground(previousBackgroundColor); + searchField.setForeground(previousForegroundColor); + } + + @Override + public void addNotify() { + super.addNotify(); + if (previousBackgroundColor == null) { + previousBackgroundColor = searchField.getBackground(); + previousForegroundColor = searchField.getForeground(); + } else { + searchField.setBackground(previousBackgroundColor); + searchField.setForeground(previousForegroundColor); + } + } + + // --------------------------- action call back + /** + * Action callback method for bound action JXDialog.CLOSE_ACTION_COMMAND. + * + * Here: does nothing. Subclasses can override to define custom "closing" + * behaviour. Alternatively, any client can register a custom action with + * the actionMap. + * + * + */ + public void cancel() { + } + + // -------------------- init + + @Override + protected void initExecutables() { + getActionMap().put(JXDialog.CLOSE_ACTION_COMMAND, + createBoundAction(JXDialog.CLOSE_ACTION_COMMAND, "cancel")); + super.initExecutables(); + } + + @Override + protected void bind() { + super.bind(); + searchField + .addActionListener(getAction(JXDialog.EXECUTE_ACTION_COMMAND)); + findNext.setAction(getAction(FIND_NEXT_ACTION_COMMAND)); + findPrevious.setAction(getAction(FIND_PREVIOUS_ACTION_COMMAND)); + KeyStroke stroke = KeyStroke.getKeyStroke("ESCAPE"); + getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(stroke, + JXDialog.CLOSE_ACTION_COMMAND); + } + + @Override + protected void build() { + setLayout(new FlowLayout(SwingConstants.LEADING)); + add(searchLabel); + add(new JLabel(":")); + add(new JLabel(" ")); + add(searchField); + add(findNext); + add(findPrevious); + } + + @Override + protected void initComponents() { + super.initComponents(); + findNext = new JButton(); + findPrevious = new JButton(); + } + +} diff --git a/src/main/java/org/jdesktop/swingx/JXFindPanel.java b/src/main/java/org/jdesktop/swingx/JXFindPanel.java new file mode 100644 index 0000000000..5efc890f61 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXFindPanel.java @@ -0,0 +1,296 @@ +/* + * $Id: JXFindPanel.java 4158 2012-02-03 18:29:40Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.search.PatternModel; +import org.jdesktop.swingx.search.Searchable; + +import javax.swing.*; +import java.awt.*; +import java.util.Locale; +import java.util.regex.Pattern; + +/** + * {@code JXFindPanel} is a basic find panel suitable for use in dialogs. It + * offers case-sensitivity, wrapped searching, and reverse searching. + * + * @author unascribed from JDNC + * @author Jeanette Winzenburg + */ +@JavaBean +public class JXFindPanel extends AbstractPatternPanel { + + public static final String FIND_NEXT_ACTION_COMMAND = "findNext"; + public static final String FIND_PREVIOUS_ACTION_COMMAND = "findPrevious"; + + protected Searchable searchable; + + protected JCheckBox wrapCheck; + protected JCheckBox backCheck; + private boolean initialized; + + /** + * Default constructor for the find panel. Constructs panel not targeted to + * any component. + */ + public JXFindPanel() { + this(null); + } + + /** + * Construct search panel targeted to specific Searchable component. + * + * @param searchable Component where search widget will try to locate and select + * information using methods of the Searchable interface. + */ + public JXFindPanel(Searchable searchable) { + setName(getUIString(SEARCH_TITLE)); + setSearchable(searchable); + initActions(); + } + + /** + * Sets the Searchable targeted of this find widget. + * Triggers a search with null pattern to release the old + * searchable, if any. + * + * @param searchable Component where search widget will try to locate and select + * information using methods of the {@link Searchable Searchable} interface. + */ + public void setSearchable(Searchable searchable) { + if ((this.searchable != null) && this.searchable.equals(searchable)) return; + Searchable old = this.searchable; + if (old != null) { + old.search((Pattern) null); + } + this.searchable = searchable; + getPatternModel().setFoundIndex(-1); + firePropertyChange("searchable", old, this.searchable); + } + + /** + * Notifies this component that it now has a parent component. + * When this method is invoked, the chain of parent components is + * set up with KeyboardAction event listeners. + */ + @Override + public void addNotify() { + init(); + super.addNotify(); + } + + /** + * Initializes component and its listeners and models. + */ + protected void init() { + if (initialized) return; + initialized = true; + initComponents(); + build(); + bind(); + } + + //------------------ support synch the model <--> components + + + /** + * Configure and bind components to/from PatternModel. + */ + @Override + protected void bind() { + super.bind(); + getActionContainerFactory().configureButton(wrapCheck, + getAction(PatternModel.MATCH_WRAP_ACTION_COMMAND), + null); + getActionContainerFactory().configureButton(backCheck, + getAction(PatternModel.MATCH_BACKWARDS_ACTION_COMMAND), + null); + } + + + /** + * called from listening to empty property of PatternModel. + * + * this implementation calls super and additionally synchs the + * enabled state of FIND_NEXT_ACTION_COMMAND, FIND_PREVIOUS_ACTION_COMMAND + * to !empty. + */ + @Override + protected void refreshEmptyFromModel() { + super.refreshEmptyFromModel(); + boolean enabled = !getPatternModel().isEmpty(); + getAction(FIND_NEXT_ACTION_COMMAND).setEnabled(enabled); + getAction(FIND_PREVIOUS_ACTION_COMMAND).setEnabled(enabled); + } + + //--------------------- action callbacks + /** + * Action callback for Find action. + * Find next/previous match using current setting of direction flag. + * + */ + @Override + public void match() { + doFind(); + } + + /** + * Action callback for FindNext action. + * Sets direction flag to forward and calls find. + */ + public void findNext() { + getPatternModel().setBackwards(false); + doFind(); + } + + /** + * Action callback for FindPrevious action. + * Sets direction flag to previous and calls find. + */ + public void findPrevious() { + getPatternModel().setBackwards(true); + doFind(); + } + + /** + * Common standalone method to perform search. Used by the action callback methods + * for Find/FindNext/FindPrevious actions. Finds next/previous match using current + * setting of direction flag. Result is being reporred using showFoundMessage and + * showNotFoundMessage methods respectively. + * + * @see #match + * @see #findNext + * @see #findPrevious + */ + protected void doFind() { + if (searchable == null) + return; + int foundIndex = doSearch(); + boolean notFound = (foundIndex == -1) && !getPatternModel().isEmpty(); + if (notFound) { + if (getPatternModel().isWrapping()) { + notFound = doSearch() == -1; + } + } + if (notFound) { + showNotFoundMessage(); + } else { + showFoundMessage(); + } + } + + /** + * Performs search and returns index of the next match. + * + * @return Index of the next match in document. + */ + protected int doSearch() { + int foundIndex = searchable.search(getPatternModel().getPattern(), + getPatternModel().getFoundIndex(), getPatternModel().isBackwards()); + getPatternModel().setFoundIndex(foundIndex); + return getPatternModel().getFoundIndex(); +// first try on #236-swingx - foundIndex wrong in backwards search. +// re-think: autoIncrement in PatternModel? +// return foundIndex; + } + + /** + * Report that suitable match is found. + */ + protected void showFoundMessage() { + + } + + /** + * Report that no match is found. + */ + protected void showNotFoundMessage() { + JOptionPane.showMessageDialog(this, getUIString("notFound")); + } + + +//-------------- dynamic Locale support + + + + @Override + protected void updateLocaleState(Locale locale) { + super.updateLocaleState(locale); + setName(getUIString(SEARCH_TITLE, locale)); + } + + //-------------------------- initial + + + /** + * creates and registers all "executable" actions. + * Meaning: the actions bound to a callback method on this. + */ + @Override + protected void initExecutables() { + getActionMap().put(FIND_NEXT_ACTION_COMMAND, + createBoundAction(FIND_NEXT_ACTION_COMMAND, "findNext")); + getActionMap().put(FIND_PREVIOUS_ACTION_COMMAND, + createBoundAction(FIND_PREVIOUS_ACTION_COMMAND, "findPrevious")); + super.initExecutables(); + } + + + +//----------------------------- init ui + + /** + * Create and initialize components. + */ + @Override + protected void initComponents() { + super.initComponents(); + wrapCheck = new JCheckBox(); + backCheck = new JCheckBox(); + } + + + + /** + * Compose and layout all the subcomponents. + */ + protected void build() { + Box lBox = new Box(BoxLayout.LINE_AXIS); + lBox.add(searchLabel); + lBox.add(new JLabel(":")); + lBox.add(new JLabel(" ")); + lBox.setAlignmentY(Component.TOP_ALIGNMENT); + Box rBox = new Box(BoxLayout.PAGE_AXIS); + rBox.add(searchField); + rBox.add(matchCheck); + rBox.add(wrapCheck); + rBox.add(backCheck); + rBox.setAlignmentY(Component.TOP_ALIGNMENT); + + setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS)); + + add(lBox); + add(rBox); + } + +} diff --git a/src/main/java/org/jdesktop/swingx/JXFormattedTextField.java b/src/main/java/org/jdesktop/swingx/JXFormattedTextField.java new file mode 100644 index 0000000000..805ef90498 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXFormattedTextField.java @@ -0,0 +1,150 @@ +package org.jdesktop.swingx; + +import org.jdesktop.swingx.prompt.BuddySupport; +import org.jdesktop.swingx.prompt.BuddySupport.Position; +import org.jdesktop.swingx.prompt.PromptSupport; +import org.jdesktop.swingx.prompt.PromptSupport.FocusBehavior; + +import javax.swing.*; +import java.awt.*; +import java.util.List; + + +/** + * {@link JFormattedTextField}, with integrated support for prompts and buddies. + * + * @see PromptSupport + * @see BuddySupport + * @author Peter Weishapl + * + */ +public class JXFormattedTextField extends JFormattedTextField { + public JXFormattedTextField() { + this(null); + } + + public JXFormattedTextField(String promptText) { + this(promptText, null); + } + + public JXFormattedTextField(String promptText, Color promptForeground) { + this(promptText, promptForeground, null); + } + + public JXFormattedTextField(String promptText, Color promptForeground, Color promptBackground) { + PromptSupport.init(promptText, promptForeground, promptBackground, this); + } + + /** + * @see PromptSupport#getFocusBehavior(javax.swing.text.JTextComponent) + */ + public FocusBehavior getFocusBehavior() { + return PromptSupport.getFocusBehavior(this); + } + + /** + * @see PromptSupport#getPrompt(javax.swing.text.JTextComponent) + */ + public String getPrompt() { + return PromptSupport.getPrompt(this); + } + + /** + * @see PromptSupport#getForeground(javax.swing.text.JTextComponent) + */ + public Color getPromptForeground() { + return PromptSupport.getForeground(this); + } + + /** + * @see PromptSupport#getForeground(javax.swing.text.JTextComponent) + */ + public Color getPromptBackground() { + return PromptSupport.getBackground(this); + } + + /** + * @see PromptSupport#getFontStyle(javax.swing.text.JTextComponent) + */ + public Integer getPromptFontStyle() { + return PromptSupport.getFontStyle(this); + } + + /** + * @see PromptSupport#getFocusBehavior(javax.swing.text.JTextComponent) + */ + public void setFocusBehavior(FocusBehavior focusBehavior) { + PromptSupport.setFocusBehavior(focusBehavior, this); + } + + /** + * @see PromptSupport#setPrompt(String, javax.swing.text.JTextComponent) + */ + public void setPrompt(String labelText) { + PromptSupport.setPrompt(labelText, this); + } + + /** + * @see PromptSupport#setForeground(Color, javax.swing.text.JTextComponent) + */ + public void setPromptForeground(Color promptTextColor) { + PromptSupport.setForeground(promptTextColor, this); + } + + /** + * @see PromptSupport#setBackground(Color, javax.swing.text.JTextComponent) + */ + public void setPromptBackround(Color promptTextColor) { + PromptSupport.setBackground(promptTextColor, this); + } + + /** + * @see PromptSupport#setFontStyle(Integer, + * javax.swing.text.JTextComponent) + */ + public void setPromptFontStyle(Integer fontStyle) { + PromptSupport.setFontStyle(fontStyle, this); + } + + /** + * @see BuddySupport#setOuterMargin(JTextField, Insets) + */ + public void setOuterMargin(Insets margin) { + BuddySupport.setOuterMargin(this, margin); + } + + /** + * @see BuddySupport#getOuterMargin(JTextField) + */ + public Insets getOuterMargin() { + return BuddySupport.getOuterMargin(this); + } + + /** + * @see BuddySupport#add(Component, Position, JTextField) + */ + public void addBuddy(Component buddy, Position pos) { + BuddySupport.add(buddy, pos, this); + } + + /** + * @see BuddySupport#addGap(int, Position, JTextField) + */ + public void addGap(int width, Position pos) { + BuddySupport.addGap(width, pos, this); + } + + /** + * @see BuddySupport#getBuddies(Position, JTextField) + */ + public List getBuddies(Position pos) { + return BuddySupport.getBuddies(pos, this); + } + + /** + * @see BuddySupport#removeAll(JTextField) + */ + public void removeAllBuddies() { + BuddySupport.removeAll(this); + } +} diff --git a/src/main/java/org/jdesktop/swingx/JXFrame.java b/src/main/java/org/jdesktop/swingx/JXFrame.java new file mode 100644 index 0000000000..53d340d062 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXFrame.java @@ -0,0 +1,629 @@ +/* + * $Id: JXFrame.java 4158 2012-02-03 18:29:40Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.util.WindowUtils; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; + +/** + *

+ * {@code JXFrame} is an enhanced {@link JFrame}. While {@code JXFrame} can + * replace any {@code JFrame}, it has features that make it particularly useful + * as the "main" frame for an application. + *

+ *

Additional Features

+ *

+ * Root pane: {@code JXFrame} uses {@link JXRootPane} as its default root pane. + * The frame provide several convenience methods to provide easy access to the + * additional features. + *

+ *

+ * Idle: {@code JXFrame} offers an idle timer. Registering a + * {@link java.beans.PropertyChangeListener} for "idle" will notify when the + * user has not interacted with the JVM. A primary use for this type of + * functionality is to secure the application, blocking access and requiring the + * user to login again. + *

+ *

+ * Wait (busy) glass pane: The {@code JXFrame} can be configured with an + * alternate glass pane. Typically, this glass pane is used to notify the user + * that the application is busy, but the glass pane could be for any purpose. + * This secondary glass pane can be quickly enabled or disabled by + * {@linkplain #setWaitPaneVisible(boolean) setting the wait pane visible}. + *

+ * + * @author unascribed from JDNC + */ +@JavaBean +@SuppressWarnings({ "nls", "serial" }) +public class JXFrame extends JFrame { + /** + * An enumeration of {@link JXFrame} starting locations. + * + * @author unascribed from JDNC + */ + public enum StartPosition {CenterInScreen, CenterInParent, Manual} + + private Component waitPane = null; + private Component glassPane = null; + private boolean waitPaneVisible = false; + private Cursor realCursor = null; + private boolean waitCursorVisible = false; + private boolean waiting = false; + private StartPosition startPosition; + private boolean hasBeenVisible = false; //startPosition is only used the first time the window is shown + private AWTEventListener keyEventListener; //for listening to KeyPreview events + private boolean keyPreview = false; + private AWTEventListener idleListener; //for listening to events. If no events happen for a specific amount of time, mark as idle + private Timer idleTimer; + private long idleThreshold = 0; + private boolean idle; + + /** + * Creates a {@code JXFrame} with no title and standard closing behavior. + */ + public JXFrame() { + this(null, false); + } + + /** + * Creates a {@code JXFrame} with the specified title and default closing + * behavior. + * + * @param title + * the frame title + */ + public JXFrame(String title) { + this(title, false); + } + + /** + * Creates a JXFrame in the specified + * GraphicsConfiguration of + * a screen device, a blank title and default closing behaviour. + *

+ * + * @param gc the GraphicsConfiguration that is used + * to construct the new Frame; + * if gc is null, the system + * default GraphicsConfiguration is assumed + * @exception IllegalArgumentException if gc is not from + * a screen device. This exception is always thrown when + * GraphicsEnvironment.isHeadless() returns true. + */ + public JXFrame(GraphicsConfiguration gc) { + this(null, gc, false); + } + + + /** + * Creates a JXFrame with the specified title, the + * specified GraphicsConfiguration of a screen device and + * default closing behaviour. + *

+ * + * @param title the title to be displayed in the + * frame's border. A null value is treated as + * an empty string, "". + * @param gc the GraphicsConfiguration that is used + * to construct the new JFrame with; + * if gc is null, the system + * default GraphicsConfiguration is assumed + * @exception IllegalArgumentException if gc is not from + * a screen device. This exception is always thrown when + * GraphicsEnvironment.isHeadless() returns true. + */ + public JXFrame(String title, GraphicsConfiguration gc) { + this(title, gc, false); + } + + /** + * Creates a {@code JXFrame} with the specified title and closing behavior. + * + * @param title + * the frame title + * @param exitOnClose + * {@code true} to override the default ({@link JFrame}) closing + * behavior and use {@link JFrame#EXIT_ON_CLOSE EXIT_ON_CLOSE} + * instead; {@code false} to use the default behavior + */ + public JXFrame(String title, boolean exitOnClose) { + this(title, null, exitOnClose); + } + + /** + * Creates a {@code JXFrame} with the specified title, GraphicsConfiguration + * and closing behavior. + * + * @param title the frame title + * @param gc the GraphicsConfiguration of the target screen + * device. If gc is null, the system + * default GraphicsConfiguration is assumed. + * @param exitOnClose {@code true} to override the default ({@link JFrame}) + * closing behavior and use {@link JFrame#EXIT_ON_CLOSE + * EXIT_ON_CLOSE} instead; {@code false} to use the default behavior + * @exception IllegalArgumentException if gc is not from a + * screen device. + * + */ + public JXFrame(String title, GraphicsConfiguration gc, boolean exitOnClose) { + super(title, gc); + if (exitOnClose) { + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + } + + //create the event handler for key preview functionality + keyEventListener = new AWTEventListener() { + @Override + public void eventDispatched(AWTEvent aWTEvent) { + if (aWTEvent instanceof KeyEvent) { + KeyEvent evt = (KeyEvent)aWTEvent; + for (KeyListener kl : getKeyListeners()) { + int id = aWTEvent.getID(); + switch (id) { + case KeyEvent.KEY_PRESSED: + kl.keyPressed(evt); + break; + case KeyEvent.KEY_RELEASED: + kl.keyReleased(evt); + break; + case KeyEvent.KEY_TYPED: + kl.keyTyped(evt); + break; + default: + System.err.println("Unhandled Key ID: " + id); + } + } + } + } + }; + + idleTimer = new Timer(100, new ActionListener() { + @Override + public void actionPerformed(ActionEvent actionEvent) { + setIdle(true); + } + }); + + //create the event handler for key preview functionality + idleListener = new AWTEventListener() { + @Override + public void eventDispatched(AWTEvent aWTEvent) { + //reset the timer + idleTimer.stop(); + //if the user is idle, then change to not idle + if (isIdle()) { + setIdle(false); + } + //start the timer + idleTimer.restart(); + } + }; + } + + /** + * Sets the cancel button property on the underlying {@code JXRootPane}. + * + * @param button + * the {@code JButton} which is to be the cancel button + * @see #getCancelButton() + * @see JXRootPane#setCancelButton(JButton) + */ + public void setCancelButton(JButton button) { + getRootPaneExt().setCancelButton(button); + } + + /** + * Returns the value of the cancel button property from the underlying + * {@code JXRootPane}. + * + * @return the {@code JButton} which is the cancel button + * @see #setCancelButton(JButton) + * @see JXRootPane#getCancelButton() + */ + public JButton getCancelButton() { + return getRootPaneExt().getCancelButton(); + } + + /** + * Sets the default button property on the underlying {@code JRootPane}. + * + * @param button + * the {@code JButton} which is to be the default button + * @see #getDefaultButton() + * @see JXRootPane#setDefaultButton(JButton) + */ + public void setDefaultButton(JButton button) { + JButton old = getDefaultButton(); + getRootPane().setDefaultButton(button); + firePropertyChange("defaultButton", old, getDefaultButton()); + } + + /** + * Returns the value of the default button property from the underlying + * {@code JRootPane}. + * + * @return the {@code JButton} which is the default button + * @see #setDefaultButton(JButton) + * @see JXRootPane#getDefaultButton() + */ + public JButton getDefaultButton() { + return getRootPane().getDefaultButton(); + } + + /** + * If enabled the {@code KeyListener}s will receive a preview of the {@code + * KeyEvent} prior to normal viewing. + * + * @param flag {@code true} to enable previewing; {@code false} otherwise + * @see #getKeyPreview() + * @see #addKeyListener(KeyListener) + */ + public void setKeyPreview(boolean flag) { + Toolkit.getDefaultToolkit().removeAWTEventListener(keyEventListener); + if (flag) { + Toolkit.getDefaultToolkit().addAWTEventListener(keyEventListener, AWTEvent.KEY_EVENT_MASK); + } + boolean old = keyPreview; + keyPreview = flag; + firePropertyChange("keyPreview", old, keyPreview); + } + + /** + * Returns the value for the key preview. + * + * @return if {@code true} previewing is enabled; otherwise it is not + * @see #setKeyPreview(boolean) + */ + public final boolean getKeyPreview() { + return keyPreview; + } + + /** + * Sets the start position for this frame. Setting this value only has an + * effect is the frame has never been displayed. + * + * @param position + * the position to display the frame at + * @see #getStartPosition() + * @see #setVisible(boolean) + */ + public void setStartPosition(StartPosition position) { + StartPosition old = getStartPosition(); + this.startPosition = position; + firePropertyChange("startPosition", old, getStartPosition()); + } + + /** + * Returns the start position for this frame. + * + * @return the start position of the frame + * @see #setStartPosition(StartPosition) + */ + public StartPosition getStartPosition() { + return startPosition == null ? StartPosition.Manual : startPosition; + } + + /** + * Switches the display cursor to or from the wait cursor. + * + * @param flag + * {@code true} to enable the wait cursor; {@code false} to + * enable the previous cursor + * @see #isWaitCursorVisible() + * @see Cursor#WAIT_CURSOR + */ + public void setWaitCursorVisible(boolean flag) { + boolean old = isWaitCursorVisible(); + if (flag != old) { + waitCursorVisible = flag; + if (isWaitCursorVisible()) { + realCursor = getCursor(); + super.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + } else { + super.setCursor(realCursor); + } + firePropertyChange("waitCursorVisible", old, isWaitCursorVisible()); + } + } + + /** + * Returns the state of the wait cursor visibility. + * + * @return {@code true} if the current cursor is the wait cursor; {@code + * false} otherwise + */ + public boolean isWaitCursorVisible() { + return waitCursorVisible; + } + + /** + * {@inheritDoc} + */ + @Override + public void setCursor(Cursor c) { + if (!isWaitCursorVisible()) { + super.setCursor(c); + } else { + this.realCursor = c; + } + } + + /** + * Sets the component to use as a wait glass pane. This component is not + * part of the display hierarchy unless {@code isWaitPaneVisible() == true}. + * + * @param c + * the wait glass pane for this frame + * @see #getWaitPane() + * @see #setWaitPaneVisible(boolean) + */ + public void setWaitPane(Component c) { + Component old = getWaitPane(); + this.waitPane = c; + firePropertyChange("waitPane", old, getWaitPane()); + } + + /** + * Returns the current wait pane for this frame. This component may or may + * not be part of the display hierarchy. + * + * @return the current wait pane + * @see #setWaitPane(Component) + */ + public Component getWaitPane() { + return waitPane; + } + + /** + * Enabled or disabled the display of the normal or wait glass pane. If + * {@code true} the wait pane is be displayed. Altering this property alters + * the display hierarchy. + * + * @param flag + * {@code true} to display the wait glass pane; {@code false} to + * display the normal glass pane + * @see #isWaitPaneVisible() + * @see #setWaitPane(Component) + */ + public void setWaitPaneVisible(boolean flag) { + boolean old = isWaitPaneVisible(); + if (flag != old) { + this.waitPaneVisible = flag; + Component wp = getWaitPane(); + if (isWaitPaneVisible()) { + glassPane = getRootPane().getGlassPane(); + if (wp != null) { + getRootPane().setGlassPane(wp); + wp.setVisible(true); + } + } else { + if (wp != null) { + wp.setVisible(false); + } + getRootPane().setGlassPane(glassPane); + } + firePropertyChange("waitPaneVisible", old, isWaitPaneVisible()); + } + } + + /** + * Returns the current visibility of the wait glass pane. + * + * @return {@code true} if the wait glass pane is visible; {@code false} + * otherwise + */ + public boolean isWaitPaneVisible() { + return waitPaneVisible; + } + + /** + * Sets the frame into a wait state or restores the frame from a wait state. + * + * @param waiting + * {@code true} to place the frame in a wait state; {@code false} + * otherwise + * @see #isWaiting() + * @see #setWaitCursorVisible(boolean) + * @see #setWaitPaneVisible(boolean) + */ + public void setWaiting(boolean waiting) { + boolean old = isWaiting(); + this.waiting = waiting; + firePropertyChange("waiting", old, isWaiting()); + setWaitPaneVisible(waiting); + setWaitCursorVisible(waiting); + } + + /** + * Determines if the frame is in a wait state or not. + * + * @return {@code true} if the frame is in the wait state; {@code false} + * otherwise + * @see #setWaiting(boolean) + */ + public boolean isWaiting() { + return waiting; + } + + /** + * {@inheritDoc} + */ + @Override + public void setVisible(boolean visible) { + if (!hasBeenVisible && visible) { + //move to the proper start position + StartPosition pos = getStartPosition(); + switch (pos) { + case CenterInParent: + setLocationRelativeTo(getParent()); + break; + case CenterInScreen: + setLocation(WindowUtils.getPointForCentering(this)); + break; + case Manual: + default: + //nothing to do! + } + } + super.setVisible(visible); + } + + public boolean isIdle() { + return idle; + } + + /** + * Sets the frame into an idle state or restores the frame from an idle state. + * + * @param idle + * {@code true} to place the frame in an idle state; {@code false} + * otherwise + * @see #isIdle() + * @see #setIdleThreshold(long) + */ + public void setIdle(boolean idle) { + boolean old = isIdle(); + this.idle = idle; + firePropertyChange("idle", old, isIdle()); + } + + /** + * Sets a threshold for user interaction before automatically placing the + * frame in an idle state. + * + * @param threshold + * the time (in milliseconds) to elapse before setting the frame + * idle + * @see #getIdleThreshold() + * @see #setIdle(boolean) + */ + public void setIdleThreshold(long threshold) { + long old = getIdleThreshold(); + this.idleThreshold = threshold; + firePropertyChange("idleThreshold", old, getIdleThreshold()); + + threshold = getIdleThreshold(); // in case the getIdleThreshold method has been overridden + + Toolkit.getDefaultToolkit().removeAWTEventListener(idleListener); + if (threshold > 0) { + Toolkit.getDefaultToolkit().addAWTEventListener(idleListener, AWTEvent.KEY_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK | AWTEvent.MOUSE_WHEEL_EVENT_MASK); + } + idleTimer.stop(); + idleTimer.setInitialDelay((int)threshold); + idleTimer.restart(); + } + + /** + * Returns the amount of time that must elapse before the frame + * automatically enters an idle state. + * + * @return the time in milliseconds + */ + public long getIdleThreshold() { + return idleThreshold; + } + + /** + * Sets the status bar property on the underlying {@code JXRootPane}. + * + * @param statusBar + * the {@code JXStatusBar} which is to be the status bar + * @see #getStatusBar() + * @see JXRootPane#setStatusBar(JXStatusBar) + */ + public void setStatusBar(JXStatusBar statusBar) { + getRootPaneExt().setStatusBar(statusBar); + } + + /** + * Returns the value of the status bar property from the underlying + * {@code JXRootPane}. + * + * @return the {@code JXStatusBar} which is the current status bar + * @see #setStatusBar(JXStatusBar) + * @see JXRootPane#getStatusBar() + */ + public JXStatusBar getStatusBar() { + return getRootPaneExt().getStatusBar(); + } + + /** + * Sets the tool bar property on the underlying {@code JXRootPane}. + * + * @param toolBar + * the {@code JToolBar} which is to be the tool bar + * @see #getToolBar() + * @see JXRootPane#setToolBar(JToolBar) + */ + public void setToolBar(JToolBar toolBar) { + getRootPaneExt().setToolBar(toolBar); + } + + /** + * Returns the value of the tool bar property from the underlying + * {@code JXRootPane}. + * + * @return the {@code JToolBar} which is the current tool bar + * @see #setToolBar(JToolBar) + * @see JXRootPane#getToolBar() + */ + public JToolBar getToolBar() { + return getRootPaneExt().getToolBar(); + } + + //---------------------------------------------------- Root Pane Methods + /** + * Overridden to create a JXRootPane. + */ + @Override + protected JRootPane createRootPane() { + return new JXRootPane(); + } + + /** + * Overridden to make this public. + */ + @Override + public void setRootPane(JRootPane root) { + super.setRootPane(root); + } + + /** + * Return the extended root pane. If this frame doesn't contain + * an extended root pane the root pane should be accessed with + * getRootPane(). + * + * @return the extended root pane or null. + */ + public JXRootPane getRootPaneExt() { + if (rootPane instanceof JXRootPane) { + return (JXRootPane)rootPane; + } + return null; + } +} + diff --git a/src/main/java/org/jdesktop/swingx/JXGradientChooser.java b/src/main/java/org/jdesktop/swingx/JXGradientChooser.java new file mode 100644 index 0000000000..3104857076 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXGradientChooser.java @@ -0,0 +1,787 @@ +/* + * $Id: JXGradientChooser.java 4147 2012-02-01 17:13:24Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.action.AbstractActionExt; +import org.jdesktop.swingx.color.GradientPreviewPanel; +import org.jdesktop.swingx.color.GradientThumbRenderer; +import org.jdesktop.swingx.color.GradientTrackRenderer; +import org.jdesktop.swingx.multislider.Thumb; +import org.jdesktop.swingx.multislider.ThumbListener; +import org.jdesktop.swingx.util.PaintUtils; + +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import java.awt.*; +import java.awt.event.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.logging.Logger; + +/** + *

A specialized JXPanel that allows the user to construct and choose a Gradient. + * The returned values will be one of: LinearGradientPaint or RadialGradientPaint.

+ * + *

Dependency: Because this class relies on LinearGradientPaint and + * RadialGradientPaint, it requires the optional MultipleGradientPaint.jar

+ * + * @author joshy + */ +@JavaBean +public class JXGradientChooser extends JXPanel { + private enum GradientStyle { Linear, Radial } + + /** + * The multi-thumb slider to use for the gradient stops + */ + private JXMultiThumbSlider slider; + private JButton deleteThumbButton; + private JButton addThumbButton; + + private JTextField colorField; + private JXColorSelectionButton changeColorButton; + private JSpinner colorLocationSpinner; + private JSpinner alphaSpinner; + private JSlider alphaSlider; + + private JComboBox styleCombo; + private GradientPreviewPanel gradientPreview; + + private JRadioButton noCycleRadio; + private JRadioButton reflectedRadio; + private JRadioButton repeatedRadio; + private JCheckBox reversedCheck; + private MultipleGradientPaint gradient; + + /** + * Creates new JXGradientChooser + */ + public JXGradientChooser() { + initComponents2(); + } + + /** + * Returns the MultipleGradientPaint currently choosen by the user. + * @return the currently selected gradient + */ + public MultipleGradientPaint getGradient() { + return gradient; + } + + private boolean thumbsMoving = false; + private Logger log = Logger.getLogger(JXGradientChooser.class.getName()); + + /** + * Sets the gradient within this panel to the new gradient. This will delete + * the old gradient all of it's settings, resetting the slider, gradient + * type selection, and other gradient configuration options to match the + * new gradient. + * + * @param mgrad The desired gradient. + */ + public void setGradient(MultipleGradientPaint mgrad) { + if(gradient == mgrad) { + return; + } + float[] fracts = mgrad.getFractions(); + Color[] colors = mgrad.getColors(); + + if(!thumbsMoving) { + // update the slider properly + if(slider.getModel().getThumbCount() != + mgrad.getColors().length) { + // removing all thumbs; + while(slider.getModel().getThumbCount() > 0) { + slider.getModel().removeThumb(0); + } + // add them back + for(int i=0; i thumb) { + if(thumb == null) { + updateFromStop(-1,-1,Color.black); + } else { + updateFromStop(1,thumb.getPosition(),thumb.getObject()); + } + } + + private void updateFromStop(int thumb, float position, Color color) { + log.fine("updating: " + thumb + " " + position + " " + color); + if(thumb == -1) { + colorLocationSpinner.setEnabled(false); + alphaSpinner.setEnabled(false); + alphaSlider.setEnabled(false); + colorField.setEnabled(false); + changeColorButton.setEnabled(false); + changeColorButton.setBackground(Color.black); + deleteThumbButton.setEnabled(false); + } else { + colorLocationSpinner.setEnabled(true); + alphaSpinner.setEnabled(true); + alphaSlider.setEnabled(true); + colorField.setEnabled(true); + changeColorButton.setEnabled(true); + colorLocationSpinner.setValue((int)(100*position)); + colorField.setText(Integer.toHexString(color.getRGB()).substring(2)); + alphaSpinner.setValue(color.getAlpha()*100/255); + alphaSlider.setValue(color.getAlpha()*100/255); + changeColorButton.setBackground(color); + deleteThumbButton.setEnabled(true); + } + updateDeleteButtons(); + recalcGradientFromStops(); + } + + private void updateDeleteButtons() { + if(slider.getModel().getThumbCount() <= 2) { + deleteThumbButton.setEnabled(false); + } + } + + private void updateGradientProperty() { + firePropertyChange("gradient",null,getGradient()); + gradientPreview.repaint(); + } + + + /** This method is called from within the constructor to + * initialize the form. + */ + + private JPanel topPanel, previewPanel; + private void initComponents() { + // declarations for anonymous components + JPanel jPanel1, jPanel2, jPanel3, jPanel4; + JLabel jLabel1, jLabel5, jLabel2, jLabel6, jLabel4, jLabel7, jLabel8, jLabel9; + ButtonGroup typeGroup; + // pre-init stuff + slider = new JXMultiThumbSlider(); + gradientPreview = new GradientPreviewPanel(); + gradientPreview.setMultiThumbModel(slider.getModel()); + + GridBagConstraints gridBagConstraints; + + typeGroup = new ButtonGroup(); + jPanel1 = new JPanel(); + topPanel = new JPanel(); + jPanel2 = new JPanel(); + jLabel1 = new JLabel(); + jLabel5 = new JLabel(); + colorField = new JTextField(); + jLabel2 = new JLabel(); + jLabel6 = new JLabel(); + colorLocationSpinner = new JSpinner(); + jLabel4 = new JLabel(); + jLabel7 = new JLabel(); + alphaSpinner = new JSpinner(); + changeColorButton = new JXColorSelectionButton(); + alphaSlider = new JSlider(); + //slider = new javax.swing.JSlider(); + jPanel4 = new JPanel(); + addThumbButton = new JButton(); + deleteThumbButton = new JButton(); + previewPanel = new JPanel(); + jPanel3 = new JPanel(); + jLabel8 = new JLabel(); + styleCombo = new JComboBox(); + jLabel9 = new JLabel(); + noCycleRadio = new JRadioButton(); + reflectedRadio = new JRadioButton(); + repeatedRadio = new JRadioButton(); + reversedCheck = new JCheckBox(); + //gradientPreview = new javax.swing.JPanel(); + + //setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); + jPanel1.setLayout(new GridBagLayout()); + + topPanel.setLayout(new GridBagLayout()); + + topPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Gradient")); + jPanel2.setLayout(new GridBagLayout()); + + jLabel1.setText("Color:"); + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.ipadx = 2; + gridBagConstraints.ipady = 2; + gridBagConstraints.anchor = GridBagConstraints.EAST; + gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4); + jPanel2.add(jLabel1, gridBagConstraints); + + jLabel5.setText("#"); + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 0; + gridBagConstraints.insets = new java.awt.Insets(4, 0, 4, 4); + jPanel2.add(jLabel5, gridBagConstraints); + + colorField.setColumns(6); + colorField.setEnabled(false); + colorField.setPreferredSize(null); + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridy = 0; + gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; + jPanel2.add(colorField, gridBagConstraints); + + jLabel2.setText("Location:"); + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridy = 1; + gridBagConstraints.anchor = GridBagConstraints.EAST; + gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4); + jPanel2.add(jLabel2, gridBagConstraints); + + jLabel6.setText("%"); + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridy = 1; + jPanel2.add(jLabel6, gridBagConstraints); + + colorLocationSpinner.setEnabled(false); + colorLocationSpinner.setPreferredSize(null); + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridy = 1; + gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; + jPanel2.add(colorLocationSpinner, gridBagConstraints); + + jLabel4.setText("Opacity:"); + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridy = 2; + gridBagConstraints.anchor = GridBagConstraints.EAST; + gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4); + jPanel2.add(jLabel4, gridBagConstraints); + + jLabel7.setText("%"); + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridy = 2; + jPanel2.add(jLabel7, gridBagConstraints); + + alphaSpinner.setEnabled(false); + alphaSpinner.setPreferredSize(null); + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridy = 2; + gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; + jPanel2.add(alphaSpinner, gridBagConstraints); + + changeColorButton.setText("00"); + changeColorButton.setEnabled(false); + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.fill = GridBagConstraints.NONE; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.anchor = GridBagConstraints.WEST; + gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 0); + jPanel2.add(changeColorButton, gridBagConstraints); + + alphaSlider.setEnabled(false); + alphaSlider.setPreferredSize(new Dimension(20, 25)); + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridy = 2; + gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; + gridBagConstraints.weightx = 1.0; + jPanel2.add(alphaSlider, gridBagConstraints); + + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 2; + gridBagConstraints.gridwidth = 2; + gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); + topPanel.add(jPanel2, gridBagConstraints); + + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridy = 0; + gridBagConstraints.gridwidth = 2; + gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; + topPanel.add(slider, gridBagConstraints); + + jPanel4.setLayout(new GridLayout(1, 0, 2, 0)); + + addThumbButton.setText("Add"); + jPanel4.add(addThumbButton); + + deleteThumbButton.setText("Delete"); + jPanel4.add(deleteThumbButton); + + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridy = 1; + gridBagConstraints.anchor = GridBagConstraints.EAST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); + topPanel.add(jPanel4, gridBagConstraints); + + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; + gridBagConstraints.weightx = 1.0; + jPanel1.add(topPanel, gridBagConstraints); + + previewPanel.setLayout(new GridBagLayout()); + + previewPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Preview")); + jPanel3.setLayout(new GridBagLayout()); + + jLabel8.setText("Style:"); + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridy = 0; + gridBagConstraints.anchor = GridBagConstraints.EAST; + gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4); + jPanel3.add(jLabel8, gridBagConstraints); + + styleCombo.setModel(new DefaultComboBoxModel(new String[] { "Linear", "Radial" })); + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridy = 0; + gridBagConstraints.anchor = GridBagConstraints.WEST; + gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4); + jPanel3.add(styleCombo, gridBagConstraints); + + jLabel9.setText("Type:"); + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridy = 1; + gridBagConstraints.anchor = GridBagConstraints.EAST; + gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4); + jPanel3.add(jLabel9, gridBagConstraints); + + typeGroup.add(noCycleRadio); + noCycleRadio.setSelected(true); + noCycleRadio.setText("None"); + noCycleRadio.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); + noCycleRadio.setMargin(new java.awt.Insets(0, 0, 0, 0)); + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridy = 1; + gridBagConstraints.anchor = GridBagConstraints.WEST; + gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4); + jPanel3.add(noCycleRadio, gridBagConstraints); + + typeGroup.add(reflectedRadio); + reflectedRadio.setText("Reflect"); + reflectedRadio.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); + reflectedRadio.setMargin(new java.awt.Insets(0, 0, 0, 0)); + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 2; + gridBagConstraints.anchor = GridBagConstraints.WEST; + gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4); + jPanel3.add(reflectedRadio, gridBagConstraints); + + typeGroup.add(repeatedRadio); + repeatedRadio.setText("Repeat"); + repeatedRadio.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); + repeatedRadio.setMargin(new java.awt.Insets(0, 0, 0, 0)); + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 3; + gridBagConstraints.anchor = GridBagConstraints.WEST; + gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4); + jPanel3.add(repeatedRadio, gridBagConstraints); + + reversedCheck.setText("Reverse"); + reversedCheck.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); + reversedCheck.setMargin(new java.awt.Insets(0, 0, 0, 0)); + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 4; + gridBagConstraints.anchor = GridBagConstraints.WEST; + gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4); + jPanel3.add(reversedCheck, gridBagConstraints); + + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; + previewPanel.add(jPanel3, gridBagConstraints); + + gradientPreview.setBorder(javax.swing.BorderFactory.createEtchedBorder()); + gradientPreview.setPreferredSize(new Dimension(130, 130)); + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.fill = GridBagConstraints.BOTH; + gridBagConstraints.weightx = 10.0; + gridBagConstraints.weighty = 10.0; + previewPanel.add(gradientPreview, gridBagConstraints); + + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridy = 1; + gridBagConstraints.fill = GridBagConstraints.BOTH; + gridBagConstraints.anchor = GridBagConstraints.NORTH; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + jPanel1.add(previewPanel, gridBagConstraints); + + }// + private void initComponents2() { + this.initComponents(); + setLayout(new BorderLayout()); + add(topPanel, BorderLayout.NORTH); + add(previewPanel, BorderLayout.CENTER); + + + // do event handling stuff + //create the actions and load them in the action map + AddThumbAction addThumbAction = new AddThumbAction(); + DeleteThumbAction deleteThumbAction = new DeleteThumbAction(); + deleteThumbAction.setEnabled(false); //disabled to begin with + //TODO Add to the action map with proper keys, etc + ActionMap actions = getActionMap(); + actions.put("add-thumb", addThumbAction); + actions.put("delete-thumb", deleteThumbAction); + //actions.put("change-color", changeColorAction); + addThumbButton.setAction(addThumbAction); + deleteThumbButton.setAction(deleteThumbAction); + changeColorButton.addPropertyChangeListener("background", new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + selectColorForThumb(); + updateGradientProperty(); + } + }); + colorLocationSpinner.addChangeListener(new ChangeLocationListener()); + ChangeAlphaListener changeAlphaListener = new ChangeAlphaListener(); + alphaSpinner.addChangeListener(changeAlphaListener); + alphaSlider.addChangeListener(changeAlphaListener); + RepaintOnEventListener repaintListener = new RepaintOnEventListener(); + styleCombo.addItemListener(repaintListener); + styleCombo.setModel(new DefaultComboBoxModel(GradientStyle.values())); + noCycleRadio.addActionListener(repaintListener); + reflectedRadio.addActionListener(repaintListener); + repeatedRadio.addActionListener(repaintListener); + reversedCheck.addActionListener(repaintListener); + gradientPreview.picker = this; //wow, nasty + + + ///To still refactor below:: + SpinnerNumberModel alpha_model = new SpinnerNumberModel(100,0,100,1); + alphaSpinner.setModel(alpha_model); + SpinnerNumberModel location_model = new SpinnerNumberModel(100,0,100,1); + colorLocationSpinner.setModel(location_model); + + slider.setOpaque(false); + slider.setPreferredSize(new Dimension(100,35)); + slider.getModel().setMinimumValue(0f); + slider.getModel().setMaximumValue(1.0f); + + slider.getModel().addThumb(0,Color.black); + slider.getModel().addThumb(0.5f,Color.red); + slider.getModel().addThumb(1.0f,Color.white); + + slider.setThumbRenderer(new GradientThumbRenderer()); + slider.setTrackRenderer(new GradientTrackRenderer()); + slider.addMultiThumbListener(new StopListener()); + + // called when the gradient property of the preview pane changes + gradientPreview.addPropertyChangeListener("gradient", new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent propertyChangeEvent) { + recalcGradientFromStops(); + } + }); + + recalcGradientFromStops(); + + } + + // called whenever the color location spinner is changed + private final class ChangeLocationListener implements ChangeListener { + @Override + public void stateChanged(ChangeEvent evt) { + if(slider.getSelectedIndex() >= 0) { + Thumb thumb = slider.getModel().getThumbAt(slider.getSelectedIndex()); + thumb.setPosition((Integer)colorLocationSpinner.getValue()/100f); + updateFromStop(thumb); + updateGradientProperty(); + } + } + } + + // called when the alpha slider moves + private final class ChangeAlphaListener implements ChangeListener { + @Override + public void stateChanged(ChangeEvent changeEvent) { + if(slider.getSelectedIndex() >= 0 && !thumbsMoving) { + // get the selected thumb + Thumb thumb = slider.getModel().getThumbAt(slider.getSelectedIndex()); + // get the new alpha value + int alpha = changeEvent.getSource() == alphaSpinner ? + (Integer)alphaSpinner.getValue() + : alphaSlider.getValue(); + + + // calc new color and set it on thumb + Color col = thumb.getObject(); + col = PaintUtils.setAlpha(col, alpha*255/100); + thumb.setObject(col); + + // set the new alpha value on the other alpha control + if (changeEvent.getSource() == alphaSpinner) { + alphaSlider.setValue(alpha); + } else { + alphaSpinner.setValue(alpha); + } + + recalcGradientFromStops(); + } + } + } + + + private final class AddThumbAction extends AbstractActionExt { + public AddThumbAction() { + super("Add"); + } + + @Override + public void actionPerformed(ActionEvent actionEvent) { + float pos = 0.2f; + Color color = Color.black; + int num = slider.getModel().addThumb(pos,color); + log.fine("new number = " + num); + /* + for (int i = 0; i < slider.getModel().getThumbCount(); i++) { + float pos2 = slider.getModel().getThumbAt(i).getPosition(); + if (pos2 < pos) { + continue; + } + slider.getModel().insertThumb(pos, color, i); + updateFromStop(i,pos,color); + break; + } + */ + + } + } + + private final class DeleteThumbAction extends AbstractActionExt { + public DeleteThumbAction() { + super("Delete"); + } + + @Override + public void actionPerformed(ActionEvent actionEvent) { + int index = slider.getSelectedIndex(); + if (index >= 0) { + slider.getModel().removeThumb(index); + updateFromStop(-1,-1,null); + } + } + } + + private class StopListener implements ThumbListener { + + public StopListener() { + super(); + } + + @Override + public void thumbMoved(int thumb, float pos) { + log.fine("moved: " + thumb + " " + pos); + Color color = slider.getModel().getThumbAt(thumb).getObject(); + thumbsMoving = true; + updateFromStop(thumb,pos,color); + updateDeleteButtons(); + thumbsMoving = false; + + } + + @Override + public void thumbSelected(int thumb) { + + if(thumb == -1) { + updateFromStop(-1,-1,Color.black); + return; + } + thumbsMoving = true; + float pos = slider.getModel().getThumbAt(thumb).getPosition(); + Color color = slider.getModel().getThumbAt(thumb).getObject(); + log.fine("selected = " + thumb + " " + pos + " " + color); + updateFromStop(thumb,pos,color); + updateDeleteButtons(); + slider.repaint(); + thumbsMoving = false; + + } + + @Override + public void mousePressed(MouseEvent e) { + if(e.getClickCount() > 1) { + selectColorForThumb(); + } + } + } + + private final class RepaintOnEventListener implements ActionListener, ItemListener { + @Override + public void actionPerformed(ActionEvent e) { + gradientPreview.setReflected(reflectedRadio.isSelected()); + gradientPreview.setReversed(reversedCheck.isSelected()); + gradientPreview.setRepeated(repeatedRadio.isSelected()); + //updateGradientProperty(); + recalcGradientFromStops(); + gradientPreview.repaint(); + } + + @Override + public void itemStateChanged(ItemEvent e) { + if(styleCombo.getSelectedItem() == GradientStyle.Radial) { + gradientPreview.setRadial(true); + } else { + gradientPreview.setRadial(false); + } + recalcGradientFromStops(); + } + } + + private void selectColorForThumb() { + int index = slider.getSelectedIndex(); + if (index >= 0) { + Color color = changeColorButton.getBackground(); + slider.getModel().getThumbAt(index).setObject(color); + updateFromStop(index, slider.getModel().getThumbAt(index).getPosition(), color); + } + } + + /** + * This static utility method cannot be called from the + * ETD, or your application will lock up. Call it from a separate + * thread or create a new Thread with a Runnable. + * @param comp The component to use when finding a top level window or frame for + * the dialog. + * @param title The desired title of the gradient chooser dialog. + * @param mgrad The gradient to initialize the chooser too. + * @return The gradient the user chose. + */ + public static MultipleGradientPaint showDialog(Component comp, String title, MultipleGradientPaint mgrad) { + Component root = SwingUtilities.getRoot(comp); + final JDialog dialog = new JDialog((JFrame)root,title,true); + final JXGradientChooser picker = new JXGradientChooser(); + if(mgrad != null) { + picker.setGradient(mgrad); + } + dialog.add(picker); + + + JPanel panel = new JPanel(); + JButton cancel = new JButton("Cancel"); + cancel.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent actionEvent) { + dialog.setVisible(false); + } + }); + JButton okay = new JButton("Ok"); + okay.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent actionEvent) { + dialog.setVisible(false); + } + }); + okay.setDefaultCapable(true); + + + GridLayout gl = new GridLayout(); + gl.setHgap(2); + panel.setLayout(gl); + panel.add(cancel); + panel.add(okay); + + JPanel p2 = new JPanel(); + p2.setLayout(new GridBagLayout()); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.EAST; + gbc.weightx = 1.0; + p2.add(panel,gbc); + dialog.add(p2,"South"); + + dialog.getRootPane().setDefaultButton(okay); + dialog.pack(); + dialog.setResizable(false); + dialog.setVisible(true); + + return picker.getGradient(); + } + + /** + * Creates a string representation of a {@code MultipleGradientPaint}. This + * string is used for debugging purposes. Its contents cannot be guaranteed + * between releases. + * + * @param paint + * the {@code paint} to create a string for + * @return a string representing the supplied {@code paint} + */ + public static String toString(MultipleGradientPaint paint) { + StringBuffer buffer = new StringBuffer(); + buffer.append(paint.getClass().getName()); + Color[] colors = paint.getColors(); + float[] values = paint.getFractions(); + buffer.append("["); + for(int i=0; iJXGraph provides a component which can display one or more + * plots on top of a graduated background (or grid.)

+ * + *

User input

+ * + *

To help analyze the plots, this component allows the user to pan the + * view by left-clicking and dragging the mouse around. Using the mouse wheel, + * the user is also able to zoom in and out. Clicking the middle button resets + * the view to its original position.

+ * + *

All user input can be disabled by calling + * {@link #setInputEnabled(boolean)} and passing false. This does not prevent + * subclasses from registering their own event listeners, such as mouse or key + * listeners.

+ * + *

Initializing the component and setting the view

+ * + *

Whenever a new instance of this component is created, the grid boundaries, + * or view, must be defined. The view is comprised of several elements whose + * descriptions are the following:

+ * + *
    + *
  • minX: Minimum value initially displayed by the component on the + * X axis (horizontally.)
  • + *
  • minY: Minimum value initially displayed by the component on the + * Y axis (vertically.)
  • + *
  • maxX: Maximum value initially displayed by the component on the + * X axis (horizontally.)
  • + *
  • maxY: Maximum value initially displayed by the component on the + * Y axis (vertically.)
  • + *
  • originX: Origin on the X axis of the vertical axis.
  • + *
  • originY: Origin on the Y axis of the horizontal axis.
  • + *
  • majorX: Distance between two major vertical lines of the + * grid.
  • + *
  • majorY: Distance between two major horizontal lines of the + * grid.
  • + *
  • minCountX: Number of minor vertical lines between two major + * vertical lines in the grid.
  • + *
  • minCountY: Number of minor horizontal lines between two major + * horizontal lines in the grid.
  • + *
+ * + *

View and origin

+ * + *

The default constructor defines a view bounds by -1.0 and + * +1.0 on both axis, and centered on an origin at + * (0, 0).

+ * + *

To simplify the API, the origin can be read and written with a + * Point2D instance (see {@link #getOrigin()} and + * {@link #setOrigin(Point2D)}.)

+ * + *

Likewise, the view can be read and written with a + * Rectangle2D instance (see {@link #getView()} and + * {@link #setView(Rectangle2D)}.) In this case, you need not to define the + * maximum boundaries of the view. Instead, you need to set the origin of the + * rectangle as the minimum boundaries. The width and the height of the + * rectangle define the distance between the minimum and maximum boundaries. For + * instance, to set the view to minX=-1.0, maxX=1.0, minY=-1.0 and maxY=1.0 you + * can use the following rectangle:

+ * + *
new Rectangle2D.Double(-1.0d, -1.0d, 2.0d, 2.0d);
+ * + *

You can check the boundaries by calling Rectangle2D.getMaxX() + * and Rectangle2D.getMaxY() once your rectangle has been + * created.

+ * + *

Alternatively, you can set the view and the origin at the same time by + * calling the method {@link #setViewAndOrigin(Rectangle2D)}. Calling this + * method will set the origin so as to center it in the view defined by the + * rectangle.

+ * + *

Grid lines

+ * + *

By default, the component defines a spacing of 0.2 units between two + * major grid lines. It also defines 4 minor grid lines between two major + * grid lines. The spacing between major grid lines and the number of minor + * grid lines can be accessed through the getters {@link #getMajorX()}, + * {@link #getMajorY()}, {@link #getMinorCountX()} and + * {@link #getMinorCountY()}.

+ * + *

You can change the number of grid lines at runtime by calling the setters + * {@link #setMajorX(double)}, {@link #setMajorY(double)}, + * {@link #setMinorCountX(int)} and {@link #setMinorCountY(int)}.

+ * + *

Appearance

+ * + *

Although it provides sensible defaults, this component lets you change + * its appearance in several ways. It is possible to modify the colors of the + * graph by calling the setters {@link #setAxisColor(Color)}, + * {@link #setMajorGridColor(Color)} and {@link #setMinorGridColor(Color)}.

+ * + *

You can also enable or disable given parts of the resulting graph by + * calling the following setters:

+ * + *
    + *
  • {@link #setAxisPainted(boolean)}: Defines whether the main axis (see + * {@link #getOrigin()}) is painted.
  • + *
  • {@link #setBackgroundPainted(boolean)}: Defines whether the background + * is painted (see {@link #setBackground(Color)}.)
  • + *
  • {@link #setGridPainted(boolean)}: Defines whether the grid is + * painted.
  • + *
  • {@link #setTextPainted(boolean)}: Defines whether the axis labels are + * painted.
  • + *
+ * + *

Usage example

+ * + *

The following code snippet creates a new graph centered on + * (0, 0), bound to the view [-1.0 1.0 -1.0 1.0], with + * a major grid line every 0.5 units and a minor grid line count of 5:

+ * + *
+ * Point2D origin = new Point2D.Double(0.0d, 0.0d);
+ * Rectangle2D view = new Rectangle2D.Double(-1.0d, 1.0d, 2.0d, 2.0d);
+ * JXGraph graph = new JXGraph(origin, view, 0.5d, 5, 0.5d, 5);
+ * 
+ * + *

Plots

+ * + *

Definition

+ * + *

A plot is defined by a mathematical transformation that, given a value on + * the graph's X axis, returns a value on the Y axis. The component draws the + * result by plotting a spot of color at the coordinates defined by + * (X, f(X)) where f() is the aforementionned + * mathematical transformation. Given the following transformation:

+ * + *
+ * f(X) = X * 2.0
+ * 
+ * + *

For X=1.0, the component will show a spot of color at the + * coordinates (1.0, 2.0).

+ * + *

Creating a new plot

+ * + *

Every plot drawn by the component must be a subclass of + * {@link Plot}. This abstract public class defines a single method to + * be implemented by its children:

+ * + *
+ * public double compute(double value)
+ * 
+ * + *

The previous example can be defined by a concrete + * JXGraph.Plot as follow:

+ * + *
+ * class TwiceTheValuePlot extends JXGraph.Plot {
+ *     public double compute(double value) {
+ *         return value * 2.0d;
+ *     }
+ * }
+ * 
+ * + *

Most of the time though, a plot requires supplementary parameters. For + * instance, let's define the X axis of your graph as the mass of an object. To + * compute the weight of the object given its mass, you need to use the + * acceleration of gravity (w=m*g where g is the + * acceleration.) To let the user modify this last parameter, to compute his + * weight at the surface of the moon for instance, you need to add a parameter + * to your plot.

+ * + *

While JXGraph.Plot does not give you an API for such a + * purpose, it does define an event dispatching API (see + * {@link JXGraph#firePropertyChange(String, double, double)}.) Whenever a + * plot is added to the graph, the component registers itself as a property + * listener of the plot. If you take care of firing events whenever the user + * changes a parameter of your plot, the graph will automatically update its + * display. While not mandatory, it is highly recommended to leverage this + * API.

+ * + *

Adding and removing plots to and from the graph

+ * + *

To add a plot to the graph, simply call the method + * {@link #addPlots(Color, Plot...)}. You can use it to add one or more + * plots at the same time and associate them with a color. This color is used + * when drawing the plots:

+ * + *
+ * JXGraph.Plot plot = new TwiceTheValuePlot();
+ * graph.addPlots(Color.BLUE, plot);
+ * 
+ * + *

These two lines will display our previously defined plot in blue on + * screen. Removing one or several plots is as simple as calling the method + * {@link #removePlots(Plot...)}. You can also remove all plots at once + * with {@link #removeAllPlots()}.

+ * + *

Painting more information

+ * + *

How to draw on the graph

+ * + *

If you need to add more information on the graph you need to extend + * it and override the method {@link #paintExtra(Graphics2D)}. This + * method has a default empty implementation and is called after everything + * has been drawn. Its sole parameter is a reference to the component's drawing + * surface, as configured by {@link #setupGraphics(Graphics2D)}. By default, the + * setup method activates antialising but it can be overriden to change the + * drawing surface. (Translation, rotation, new rendering hints, etc.)

+ * + *

Getting the right coordinates

+ * + *

To properly draw on the graph you will need to perform a translation + * between the graph's coordinates and the screen's coordinates. The component + * defines 4 methods to assist you in this task:

+ * + *
    + *
  • {@link #xPixelToPosition(double)}: Converts a pixel coordinate on the + * X axis into a world coordinate.
  • + *
  • {@link #xPositionToPixel(double)}: Converts a world coordinate on the + * X axis into a pixel coordinate.
  • + *
  • {@link #yPixelToPosition(double)}: Converts a pixel coordinate on the + * Y axis into a world coordinate.
  • + *
  • {@link #yPositionToPixel(double)}: Converts a world coordinate on the + * Y axis into a pixel coordinate.
  • + *
+ * + *

If you have defined a graph view centered on the origin + * (0, 0), the origin of the graph will be at the exact center of + * the screen. That means the world coordinates (0, 0) are + * equivalent to the pixel coordinates (width / 2, height / 2). + * Thus, calling xPositionToPixel(0.0d) would give you the same + * value as the expression getWidth() / 2.0d.

+ * + *

Converting from world coordinates to pixel coordinates is mostly used to + * draw the result of a mathematical transformation. Converting from pixel + * coordinates to world coordinates is mostly used to get the position in the + * world of a mouse event.

+ * + * @see Plot + * @author Romain Guy + */ +@JavaBean +public class JXGraph extends JXPanel { + // stroke widths used to draw the main axis and the grid + // the main axis is slightly thicker + private static final float STROKE_AXIS = 1.2f; + private static final float STROKE_GRID = 1.0f; + + // defines by how much the view is shrinked or expanded everytime the + // user zooms in or out + private static final float ZOOM_MULTIPLIER = 1.1f; + + //listens to changes to plots and repaints the graph + private PropertyChangeListener plotChangeListener; + + // default color of the graph (does not include plots colors) + private Color majorGridColor = Color.GRAY.brighter(); + private Color minorGridColor = new Color(220, 220, 220); + private Color axisColor = Color.BLACK; + + // the list of plots currently known and displayed by the graph + private List plots; + + // view boundaries as defined by the user + private double minX; + private double maxX; + private double minY; + private double maxY; + + // the default view is set when the view is manually changed by the client + // it is used to reset the view in resetView() + private Rectangle2D defaultView; + + // coordinates of the major axis + private double originX; + private double originY; + + // definition of the grid + // various default values are used when the view is reset + private double majorX; + private double defaultMajorX; + private int minorCountX; + private double majorY; + private double defaultMajorY; + private int minorCountY; + + // enables painting layers + private boolean textPainted = true; + private boolean gridPainted = true; + private boolean axisPainted = true; + private boolean backPainted = true; + + // used by the PanHandler to move the view + private Point dragStart; + + // mainFormatter is used for numbers > 0.01 and < 100 + // secondFormatter uses scientific notation + private NumberFormat mainFormatter; + private NumberFormat secondFormatter; + + // input handlers + private boolean inputEnabled = true; + private ZoomHandler zoomHandler; + private PanMotionHandler panMotionHandler; + private PanHandler panHandler; + private ResetHandler resetHandler; + + /** + *

Creates a new graph display. The following properties are + * automatically set:

+ *
    + *
  • view: -1.0 to +1.0 on both axis
  • + *
  • origin: At (0, 0)
  • + *
  • grid: Spacing of 0.2 between major lines; minor lines + * count is 4
  • + *
+ */ + public JXGraph() { + this(0.0, 0.0, -1.0, 1.0, -1.0, 1.0, 0.2, 4, 0.2, 4); + } + + /** + *

Creates a new graph display with the specified view. The following + * properties are automatically set:

+ *
    + *
  • origin: Center of the specified view
  • + *
  • grid: Spacing of 0.2 between major lines; minor lines + * count is 4
  • + *
+ * + * @param view the rectangle defining the view boundaries + */ + public JXGraph(Rectangle2D view) { + this(new Point2D.Double(view.getCenterX(), view.getCenterY()), + view, 0.2, 4, 0.2, 4); + } + + /** + *

Creates a new graph display with the specified view and grid lines. + * The origin is set at the center of the view.

+ * + * @param view the rectangle defining the view boundaries + * @param majorX the spacing between two major grid lines on the X axis + * @param minorCountX the number of minor grid lines between two major + * grid lines on the X axis + * @param majorY the spacing between two major grid lines on the Y axis + * @param minorCountY the number of minor grid lines between two major + * grid lines on the Y axis + * @throws IllegalArgumentException if minX >= maxX or minY >= maxY or + * minorCountX < 0 or minorCountY < 0 or + * majorX <= 0.0 or majorY <= 0.0 + */ + public JXGraph(Rectangle2D view, + double majorX, int minorCountX, + double majorY, int minorCountY) { + this(new Point2D.Double(view.getCenterX(), view.getCenterY()), + view, majorX, minorCountX, majorY, minorCountY); + } + + /** + *

Creates a new graph display with the specified view and origin. + * The following properties are automatically set:

+ *
    + *
  • grid: Spacing of 0.2 between major lines; minor lines + * count is 4
  • + *
+ * + * @param origin the coordinates of the main axis origin + * @param view the rectangle defining the view boundaries + */ + public JXGraph(Point2D origin, Rectangle2D view) { + this(origin, view, 0.2, 4, 0.2, 4); + } + + /** + *

Creates a new graph display with the specified view, origin and grid + * lines.

+ * + * @param origin the coordinates of the main axis origin + * @param view the rectangle defining the view boundaries + * @param majorX the spacing between two major grid lines on the X axis + * @param minorCountX the number of minor grid lines between two major + * grid lines on the X axis + * @param majorY the spacing between two major grid lines on the Y axis + * @param minorCountY the number of minor grid lines between two major + * grid lines on the Y axis + * @throws IllegalArgumentException if minX >= maxX or minY >= maxY or + * minorCountX < 0 or minorCountY < 0 or + * majorX <= 0.0 or majorY <= 0.0 + */ + public JXGraph(Point2D origin, Rectangle2D view, + double majorX, int minorCountX, + double majorY, int minorCountY) { + this(origin.getX(), origin.getY(), + view.getMinX(), view.getMaxX(), view.getMinY(), view.getMaxY(), + majorX, minorCountX, majorY, minorCountY); + } + + /** + *

Creates a new graph display with the specified view, origin and grid + * lines.

+ * + * @param originX the coordinate of the major X axis + * @param originY the coordinate of the major Y axis + * @param minX the minimum coordinate on the X axis for the view + * @param maxX the maximum coordinate on the X axis for the view + * @param minY the minimum coordinate on the Y axis for the view + * @param maxY the maximum coordinate on the Y axis for the view + * @param majorX the spacing between two major grid lines on the X axis + * @param minorCountX the number of minor grid lines between two major + * grid lines on the X axis + * @param majorY the spacing between two major grid lines on the Y axis + * @param minorCountY the number of minor grid lines between two major + * grid lines on the Y axis + * @throws IllegalArgumentException if minX >= maxX or minY >= maxY or + * minorCountX < 0 or minorCountY < 0 or + * majorX <= 0.0 or majorY <= 0.0 + */ + public JXGraph(double originX, double originY, + double minX, double maxX, + double minY, double maxY, + double majorX, int minorCountX, + double majorY, int minorCountY) { + if (minX >= maxX) { + throw new IllegalArgumentException("minX must be < to maxX"); + } + + if (minY >= maxY) { + throw new IllegalArgumentException("minY must be < to maxY"); + } + + if (minorCountX < 0) { + throw new IllegalArgumentException("minorCountX must be >= 0"); + } + + if (minorCountY < 0) { + throw new IllegalArgumentException("minorCountY must be >= 0"); + } + + if (majorX <= 0.0) { + throw new IllegalArgumentException("majorX must be > 0.0"); + } + + if (majorY <= 0.0) { + throw new IllegalArgumentException("majorY must be > 0.0"); + } + + this.originX = originX; + this.originY = originY; + + this.minX = minX; + this.maxX = maxX; + this.minY = minY; + this.maxY = maxY; + + this.defaultView = new Rectangle2D.Double(minX, minY, + maxX - minX, maxY - minY); + + this.setMajorX(this.defaultMajorX = majorX); + this.setMinorCountX(minorCountX); + this.setMajorY(this.defaultMajorY = majorY); + this.setMinorCountY(minorCountY); + + this.plots = new LinkedList(); + + this.mainFormatter = NumberFormat.getInstance(); + this.mainFormatter.setMaximumFractionDigits(2); + + this.secondFormatter = new DecimalFormat("0.##E0"); + + resetHandler = new ResetHandler(); + addMouseListener(resetHandler); + panHandler = new PanHandler(); + addMouseListener(panHandler); + panMotionHandler = new PanMotionHandler(); + addMouseMotionListener(panMotionHandler); + zoomHandler = new ZoomHandler(); + addMouseWheelListener(zoomHandler); + + setBackground(Color.WHITE); + setForeground(Color.BLACK); + + plotChangeListener = new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + repaint(); + } + }; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isOpaque() { + if (!isBackgroundPainted()) { + return false; + } + return super.isOpaque(); + } + + /** + * {@inheritDoc} + * @see #setInputEnabled(boolean) + */ + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + setInputEnabled(enabled); + } + + /** + *

Enables or disables user input on the component. When user input is + * enabled, panning, zooming and view resetting. Disabling input will + * prevent the user from modifying the currently displayed view.

+ *

Calling {@link #setEnabled(boolean)} disables the component in the + * Swing hierarchy and invokes this method.

+ * + * @param enabled true if user input must be enabled, false otherwise + * @see #setEnabled(boolean) + * @see #isInputEnabled() + */ + public void setInputEnabled(boolean enabled) { + if (inputEnabled != enabled) { + boolean old = isInputEnabled(); + this.inputEnabled = enabled; + + if (enabled) { + addMouseListener(resetHandler); + addMouseListener(panHandler); + addMouseMotionListener(panMotionHandler); + addMouseWheelListener(zoomHandler); + } else { + removeMouseListener(resetHandler); + removeMouseListener(panHandler); + removeMouseMotionListener(panMotionHandler); + removeMouseWheelListener(zoomHandler); + } + + firePropertyChange("inputEnabled", old, isInputEnabled()); + } + } + + /** + *

Defines whether or not user input is accepted and managed by this + * component. The component is always created with user input enabled.

+ * + * @return true if user input is enabled, false otherwise + * @see #setInputEnabled(boolean) + */ + public boolean isInputEnabled() { + return inputEnabled; + } + + /** + *

Defines whether or not axis labels are painted by this component. + * The component is always created with text painting enabled.

+ * + * @return true if axis labels are painted, false otherwise + * @see #setTextPainted(boolean) + * @see #getForeground() + */ + public boolean isTextPainted() { + return textPainted; + } + + /** + *

Enables or disables the painting of axis labels depending on the + * value of the parameter. Text painting is enabled by default.

+ * + * @param textPainted if true, axis labels are painted + * @see #isTextPainted() + * @see #setForeground(Color) + */ + public void setTextPainted(boolean textPainted) { + boolean old = isTextPainted(); + this.textPainted = textPainted; + firePropertyChange("textPainted", old, this.textPainted); + } + + /** + *

Defines whether or not grids lines are painted by this component. + * The component is always created with grid lines painting enabled.

+ * + * @return true if grid lines are painted, false otherwise + * @see #setGridPainted(boolean) + * @see #getMajorGridColor() + * @see #getMinorGridColor() + */ + public boolean isGridPainted() { + return gridPainted; + } + + /** + *

Enables or disables the painting of grid lines depending on the + * value of the parameter. Grid painting is enabled by default.

+ * + * @param gridPainted if true, axis labels are painted + * @see #isGridPainted() + * @see #setMajorGridColor(Color) + * @see #setMinorGridColor(Color) + */ + public void setGridPainted(boolean gridPainted) { + boolean old = isGridPainted(); + this.gridPainted = gridPainted; + firePropertyChange("gridPainted", old, isGridPainted()); + } + + /** + *

Defines whether or not the graph main axis is painted by this + * component. The component is always created with main axis painting + * enabled.

+ * + * @return true if main axis is painted, false otherwise + * @see #setTextPainted(boolean) + * @see #getAxisColor() + */ + public boolean isAxisPainted() { + return axisPainted; + } + + /** + *

Enables or disables the painting of main axis depending on the + * value of the parameter. Axis painting is enabled by default.

+ * + * @param axisPainted if true, axis labels are painted + * @see #isAxisPainted() + * @see #setAxisColor(Color) + */ + public void setAxisPainted(boolean axisPainted) { + boolean old = isAxisPainted(); + this.axisPainted = axisPainted; + firePropertyChange("axisPainted", old, isAxisPainted()); + } + + /** + *

Defines whether or not the background painted by this component. + * The component is always created with background painting enabled. + * When background painting is disabled, background painting is deferred + * to the parent class.

+ * + * @return true if background is painted, false otherwise + * @see #setBackgroundPainted(boolean) + * @see #getBackground() + */ + public boolean isBackgroundPainted() { + return backPainted; + } + + /** + *

Enables or disables the painting of background depending on the + * value of the parameter. Background painting is enabled by default.

+ * + * @param backPainted if true, axis labels are painted + * @see #isBackgroundPainted() + * @see #setBackground(Color) + */ + public void setBackgroundPainted(boolean backPainted) { + boolean old = isBackgroundPainted(); + this.backPainted = backPainted; + firePropertyChange("backgroundPainted", old, isBackgroundPainted()); + } + + /** + *

Gets the major grid lines color of this component.

+ * + * @return this component's major grid lines color + * @see #setMajorGridColor(Color) + * @see #setGridPainted(boolean) + */ + public Color getMajorGridColor() { + return majorGridColor; + } + + /** + *

Sets the color of major grid lines on this component. The color + * can be translucent.

+ * + * @param majorGridColor the color to become this component's major grid + * lines color + * @throws IllegalArgumentException if the specified color is null + * @see #getMajorGridColor() + * @see #isGridPainted() + */ + public void setMajorGridColor(Color majorGridColor) { + if (majorGridColor == null) { + throw new IllegalArgumentException("Color cannot be null."); + } + + Color old = getMajorGridColor(); + this.majorGridColor = majorGridColor; + firePropertyChange("majorGridColor", old, getMajorGridColor()); + } + + /** + *

Gets the minor grid lines color of this component.

+ * + * @return this component's minor grid lines color + * @see #setMinorGridColor(Color) + * @see #setGridPainted(boolean) + */ + public Color getMinorGridColor() { + return minorGridColor; + } + + /** + *

Sets the color of minor grid lines on this component. The color + * can be translucent.

+ * + * @param minorGridColor the color to become this component's minor grid + * lines color + * @throws IllegalArgumentException if the specified color is null + * @see #getMinorGridColor() + * @see #isGridPainted() + */ + public void setMinorGridColor(Color minorGridColor) { + if (minorGridColor == null) { + throw new IllegalArgumentException("Color cannot be null."); + } + + Color old = getMinorGridColor(); + this.minorGridColor = minorGridColor; + firePropertyChange("minorGridColor", old, getMinorGridColor()); + } + + /** + *

Gets the main axis color of this component.

+ * + * @return this component's main axis color + * @see #setAxisColor(Color) + * @see #setGridPainted(boolean) + */ + public Color getAxisColor() { + return axisColor; + } + + /** + *

Sets the color of main axis on this component. The color + * can be translucent.

+ * + * @param axisColor the color to become this component's main axis color + * @throws IllegalArgumentException if the specified color is null + * @see #getAxisColor() + * @see #isAxisPainted() + */ + public void setAxisColor(Color axisColor) { + if (axisColor == null) { + throw new IllegalArgumentException("Color cannot be null."); + } + + Color old = getAxisColor(); + this.axisColor = axisColor; + firePropertyChange("axisColor", old, getAxisColor()); + } + + /** + *

Gets the distance, in graph units, between two major grid lines on + * the X axis.

+ * + * @return the spacing between two major grid lines on the X axis + * @see #setMajorX(double) + * @see #getMajorY() + * @see #setMajorY(double) + * @see #getMinorCountX() + * @see #setMinorCountX(int) + */ + public double getMajorX() { + return majorX; + } + + /** + *

Sets the distance, in graph units, between two major grid lines on + * the X axis.

+ * + * @param majorX the requested spacing between two major grid lines on the + * X axis + * @throws IllegalArgumentException if majorX is <= 0.0d + * @see #getMajorX() + * @see #getMajorY() + * @see #setMajorY(double) + * @see #getMinorCountX() + * @see #setMinorCountX(int) + */ + public void setMajorX(double majorX) { + if (majorX <= 0.0) { + throw new IllegalArgumentException("majorX must be > 0.0"); + } + + double old = getMajorX(); + this.majorX = majorX; + this.defaultMajorX = majorX; + repaint(); + firePropertyChange("majorX", old, getMajorX()); + } + + /** + *

Gets the number of minor grid lines between two major grid lines + * on the X axis.

+ * + * @return the number of minor grid lines between two major grid lines + * @see #setMinorCountX(int) + * @see #getMinorCountY() + * @see #setMinorCountY(int) + * @see #getMajorX() + * @see #setMajorX(double) + */ + public int getMinorCountX() { + return minorCountX; + } + + /** + *

Sets the number of minor grid lines between two major grid lines on + * the X axis.

+ * + * @param minorCountX the number of minor grid lines between two major grid + * lines on the X axis + * @throws IllegalArgumentException if minorCountX is < 0 + * @see #getMinorCountX() + * @see #getMinorCountY() + * @see #setMinorCountY(int) + * @see #getMajorX() + * @see #setMajorX(double) + */ + public void setMinorCountX(int minorCountX) { + if (minorCountX < 0) { + throw new IllegalArgumentException("minorCountX must be >= 0"); + } + + int old = getMinorCountX(); + this.minorCountX = minorCountX; + repaint(); + firePropertyChange("minorCountX", old, getMinorCountX()); + } + + /** + *

Gets the distance, in graph units, between two major grid lines on + * the Y axis.

+ * + * @return the spacing between two major grid lines on the Y axis + * @see #setMajorY(double) + * @see #getMajorX() + * @see #setMajorX(double) + * @see #getMinorCountY() + * @see #setMinorCountY(int) + */ + public double getMajorY() { + return majorY; + } + + /** + *

Sets the distance, in graph units, between two major grid lines on + * the Y axis.

+ * + * @param majorY the requested spacing between two major grid lines on the + * Y axis + * @throws IllegalArgumentException if majorY is <= 0.0d + * @see #getMajorY() + * @see #getMajorX() + * @see #setMajorX(double) + * @see #getMinorCountY() + * @see #setMinorCountY(int) + */ + public void setMajorY(double majorY) { + if (majorY <= 0.0) { + throw new IllegalArgumentException("majorY must be > 0.0"); + } + + double old = getMajorY(); + this.majorY = majorY; + this.defaultMajorY = majorY; + repaint(); + firePropertyChange("majorY", old, getMajorY()); + } + + /** + *

Gets the number of minor grid lines between two major grid lines + * on the Y axis.

+ * + * @return the number of minor grid lines between two major grid lines + * @see #setMinorCountY(int) + * @see #getMinorCountX() + * @see #setMinorCountX(int) + * @see #getMajorY() + * @see #setMajorY(double) + */ + public int getMinorCountY() { + return minorCountY; + } + + /** + *

Sets the number of minor grid lines between two major grid lines on + * the Y axis.

+ * + * @param minorCountY the number of minor grid lines between two major grid + * lines on the Y axis + * @throws IllegalArgumentException if minorCountY is < 0 + * @see #getMinorCountY() + * @see #getMinorCountX() + * @see #setMinorCountX(int) + * @see #getMajorY() + * @see #setMajorY(double) + */ + public void setMinorCountY(int minorCountY) { + if (minorCountY < 0) { + throw new IllegalArgumentException("minorCountY must be >= 0"); + } + + int old = getMinorCountY(); + this.minorCountY = minorCountY; + repaint(); + firePropertyChange("minorCountY", old, getMinorCountY()); + } + + /** + *

Sets the view and the origin of the graph at the same time. The view + * minimum boundaries are defined by the location of the rectangle passed + * as parameter. The width and height of the rectangle define the distance + * between the minimum and maximum boundaries:

+ * + *
    + *
  • minX: bounds.getX()
  • + *
  • minY: bounds.getY()
  • + *
  • maxY: bounds.getMaxX() (minX + bounds.getWidth())
  • + *
  • maxX: bounds.getMaxY() (minY + bounds.getHeight())
  • + *
+ * + *

The origin is located at the center of the view. Its coordinates are + * defined by calling bounds.getCenterX() and bounds.getCenterY().

+ * + * @param bounds the rectangle defining the graph's view and its origin + * @see #getView() + * @see #setView(Rectangle2D) + * @see #getOrigin() + * @see #setOrigin(Point2D) + */ + public void setViewAndOrigin(Rectangle2D bounds) { + setView(bounds); + setOrigin(new Point2D.Double(bounds.getCenterX(), bounds.getCenterY())); + } + + /** + *

Sets the view of the graph. The view minimum boundaries are defined by + * the location of the rectangle passed as parameter. The width and height + * of the rectangle define the distance between the minimum and maximum + * boundaries:

+ * + *
    + *
  • minX: bounds.getX()
  • + *
  • minY: bounds.getY()
  • + *
  • maxY: bounds.getMaxX() (minX + bounds.getWidth())
  • + *
  • maxX: bounds.getMaxY() (minY + bounds.getHeight())
  • + *
+ * + *

If the specified view is null, nothing happens.

+ * + *

Calling this method leaves the origin intact.

+ * + * @param bounds the rectangle defining the graph's view and its origin + * @see #getView() + * @see #setViewAndOrigin(Rectangle2D) + */ + public void setView(Rectangle2D bounds) { + if (bounds == null) { + return; + } + Rectangle2D old = getView(); + defaultView = new Rectangle2D.Double(bounds.getX(), bounds.getY(), + bounds.getWidth(), bounds.getHeight()); + + minX = defaultView.getMinX(); + maxX = defaultView.getMaxX(); + minY = defaultView.getMinY(); + maxY = defaultView.getMaxY(); + + majorX = defaultMajorX; + majorY = defaultMajorY; + firePropertyChange("view", old, getView()); + repaint(); + } + + /** + *

Gets the view of the graph. The returned rectangle defines the bounds + * of the view as follows:

+ * + *
    + *
  • minX: bounds.getX()
  • + *
  • minY: bounds.getY()
  • + *
  • maxY: bounds.getMaxX() (minX + bounds.getWidth())
  • + *
  • maxX: bounds.getMaxY() (minY + bounds.getHeight())
  • + *
+ * + * @return the rectangle corresponding to the current view of the graph + * @see #setView(Rectangle2D) + * @see #setViewAndOrigin(Rectangle2D) + */ + public Rectangle2D getView() { + return new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY); + } + + /** + *

Resets the view to the default view if it has been changed by the user + * by panning and zooming. The default view is defined by the view last + * specified in a constructor call or a call to the methods + * {@link #setView(Rectangle2D)} and + * {@link #setViewAndOrigin(Rectangle2D)}.

+ * + * @see #setView(Rectangle2D) + * @see #setViewAndOrigin(Rectangle2D) + */ + public void resetView() { + setView(defaultView); + } + + /** + *

Sets the origin of the graph. The coordinates of the origin are + * defined by the coordinates of the point passed as parameter.

+ * + *

If the specified view is null, nothing happens.

+ * + *

Calling this method leaves the view intact.

+ * + * @param origin the coordinates of the new origin + * @see #getOrigin() + * @see #setViewAndOrigin(Rectangle2D) + */ + public void setOrigin(Point2D origin) { + if (origin == null) { + return; + } + + Point2D old = getOrigin(); + originX = origin.getX(); + originY = origin.getY(); + firePropertyChange("origin", old, getOrigin()); + repaint(); + } + + /** + *

Gets the origin coordinates of the graph. The coordinates are + * represented as an instance of Point2D and stored in + * double format.

+ + * @return the origin coordinates in double format + * @see #setOrigin(Point2D) + * @see #setViewAndOrigin(Rectangle2D) + */ + public Point2D getOrigin() { + return new Point2D.Double(originX, originY); + } + + /** + *

Adds one or more plots to the graph. These plots are associated to + * a color used to draw them.

+ * + *

If plotList is null or empty, nothing happens.

+ * + *

This method is not thread safe and should be called only from the + * EDT.

+ * + * @param color the color to be usd to draw the plots + * @param plotList the list of plots to add to the graph + * @throws IllegalArgumentException if color is null + * @see #removePlots(Plot...) + * @see #removeAllPlots() + */ + public void addPlots(Color color, Plot... plotList) { + if (color == null) { + throw new IllegalArgumentException("Plots color cannot be null."); + } + + if (plotList == null) { + return; + } + + for (Plot plot : plotList) { + DrawablePlot drawablePlot = + new DrawablePlot(plot, color); + if (plot != null && !plots.contains(drawablePlot)) { + plot.addPropertyChangeListener(plotChangeListener); + plots.add(drawablePlot); + } + } + repaint(); + } + + /** + *

Removes the specified plots from the graph. Plots to be removed + * are identified by identity. This means you cannot remove a plot by + * passing a clone or another instance of the same subclass of + * {@link Plot}.

+ * + *

If plotList is null or empty, nothing happens.

+ * + *

This method is not thread safe and should be called only from the + * EDT.

+ * + * @param plotList the list of plots to be removed from the graph + * @see #removeAllPlots() + * @see #addPlots(Color, Plot...) + */ + public void removePlots(Plot... plotList) { + if (plotList == null) { + return; + } + + for (Plot plot : plotList) { + if (plot != null) { + DrawablePlot toRemove = null; + for (DrawablePlot drawable: plots) { + if (drawable.getEquation() == plot) { + toRemove = drawable; + break; + } + } + + if (toRemove != null) { + plot.removePropertyChangeListener(plotChangeListener); + plots.remove(toRemove); + } + } + } + repaint(); + } + + /** + *

Removes all the plots currently associated with this graph.

+ * + *

This method is not thread safe and should be called only from the + * EDT.

+ * + * @see #removePlots(Plot...) + * @see #addPlots(Color, Plot...) + */ + public void removeAllPlots() { + plots.clear(); + repaint(); + } + + /** + * {@inheritDoc} + */ + @Override + public Dimension getPreferredSize() { + return new Dimension(400, 400); + } + + /** + *

Converts a position, in graph units, from the Y axis into a pixel + * coordinate. For instance, if you defined the origin so it appears at the + * exact center of the view, calling + * yPositionToPixel(getOriginY()) will return a value + * approximately equal to getHeight() / 2.0.

+ * + * @param position the Y position to be converted into pixels + * @return the coordinate in pixels of the specified graph Y position + * @see #xPositionToPixel(double) + * @see #yPixelToPosition(double) + */ + protected double yPositionToPixel(double position) { + double height = getHeight(); + return height - ((position - minY) * height / (maxY - minY)); + } + + /** + *

Converts a position, in graph units, from the X axis into a pixel + * coordinate. For instance, if you defined the origin so it appears at the + * exact center of the view, calling + * xPositionToPixel(getOriginX()) will return a value + * approximately equal to getWidth() / 2.0.

+ * + * @param position the X position to be converted into pixels + * @return the coordinate in pixels of the specified graph X position + * @see #yPositionToPixel(double) + * @see #xPixelToPosition(double) + */ + protected double xPositionToPixel(double position) { + return (position - minX) * getWidth() / (maxX - minX); + } + + /** + *

Converts a pixel coordinate from the X axis into a graph position, in + * graph units. For instance, if you defined the origin so it appears at the + * exact center of the view, calling + * xPixelToPosition(getWidth() / 2.0) will return a value + * approximately equal to getOriginX().

+ * + * @param pixel the X pixel coordinate to be converted into a graph position + * @return the graph X position of the specified pixel coordinate + * @see #yPixelToPosition(double) + * @see #xPositionToPixel(double) + */ + protected double xPixelToPosition(double pixel) { +// double axisV = xPositionToPixel(originX); +// return (pixel - axisV) * (maxX - minX) / (double) getWidth(); + return minX + pixel * (maxX - minX) / getWidth(); + } + + /** + *

Converts a pixel coordinate from the Y axis into a graph position, in + * graph units. For instance, if you defined the origin so it appears at the + * exact center of the view, calling + * yPixelToPosition(getHeight() / 2.0) will return a value + * approximately equal to getOriginY().

+ * + * @param pixel the Y pixel coordinate to be converted into a graph position + * @return the graph Y position of the specified pixel coordinate + * @see #xPixelToPosition(double) + * @see #yPositionToPixel(double) + */ + protected double yPixelToPosition(double pixel) { +// double axisH = yPositionToPixel(originY); +// return (getHeight() - pixel - axisH) * (maxY - minY) / (double) getHeight(); + return minY + (getHeight() - pixel) * (maxY - minY) / getHeight(); + } + + /** + * {@inheritDoc} + */ + @Override + protected void paintComponent(Graphics g) { + if (!isVisible()) { + return; + } + + Graphics2D g2 = (Graphics2D) g; + setupGraphics(g2); + + paintBackground(g2); + drawGrid(g2); + drawAxis(g2); + drawPlots(g2); + drawLabels(g2); + + paintExtra(g2); + } + + /** + *

This painting method is meant to be overridden by subclasses of + * JXGraph. This method is called after all the painting + * is done. By overriding this method, a subclass can display extra + * information on top of the graph.

+ *

The graphics surface passed as parameter is configured by + * {@link #setupGraphics(Graphics2D)}.

+ * + * @param g2 the graphics surface on which the graph is drawn + * @see #setupGraphics(Graphics2D) + * @see #xPixelToPosition(double) + * @see #yPixelToPosition(double) + * @see #xPositionToPixel(double) + * @see #yPositionToPixel(double) + */ + protected void paintExtra(Graphics2D g2) { + } + + // Draw all the registered plots with the appropriate color. + private void drawPlots(Graphics2D g2) { + for (DrawablePlot drawable: plots) { + g2.setColor(drawable.getColor()); + drawPlot(g2, drawable.getEquation()); + } + } + + // Draw a single plot as a GeneralPath made of straight lines. + private void drawPlot(Graphics2D g2, Plot equation) { + float x = 0.0f; + float y = (float) yPositionToPixel(equation.compute(xPixelToPosition(0.0))); + + GeneralPath path = new GeneralPath(); + path.moveTo(x, y); + + float width = getWidth(); + for (x = 0.0f; x < width; x += 1.0f) { + double position = xPixelToPosition(x); + y = (float) yPositionToPixel(equation.compute(position)); + path.lineTo(x, y); + } + + g2.draw(path); + } + + // Draws the grid. First draw the vertical lines, then the horizontal lines. + private void drawGrid(Graphics2D g2) { + Stroke stroke = g2.getStroke(); + + if (isGridPainted()) { + drawVerticalGrid(g2); + drawHorizontalGrid(g2); + } + + g2.setStroke(stroke); + } + + // Draw all labels. First draws labels on the horizontal axis, then labels + // on the vertical axis. If the axis is set not to be painted, this + // method draws the origin as a straight cross. + private void drawLabels(Graphics2D g2) { + if (isTextPainted()) { + double axisH = yPositionToPixel(originY); + double axisV = xPositionToPixel(originX); + + if (isAxisPainted()) { + Stroke stroke = g2.getStroke(); + g2.setStroke(new BasicStroke(STROKE_AXIS)); + g2.setColor(getAxisColor()); + g2.drawLine((int) axisV - 3, (int) axisH, + (int) axisV + 3, (int) axisH); + g2.drawLine((int) axisV, (int) axisH - 3, + (int) axisV, (int) axisH + 3); + g2.setStroke(stroke); + } + + g2.setColor(getForeground()); + FontMetrics metrics = g2.getFontMetrics(); + g2.drawString(format(originX) + "; " + + format(originY), (int) axisV + 5, + (int) axisH + metrics.getHeight()); + + drawHorizontalAxisLabels(g2); + drawVerticalAxisLabels(g2); + } + } + + // Draws labels on the vertical axis. First draws labels below the origin, + // then draw labels on top of the origin. + private void drawVerticalAxisLabels(Graphics2D g2) { + double axisV = xPositionToPixel(originX); + +// double startY = Math.floor((minY - originY) / majorY) * majorY; + double startY = Math.floor(minY / majorY) * majorY; + for (double y = startY; y < maxY + majorY; y += majorY) { + if (((y - majorY / 2.0) < originY) && + ((y + majorY / 2.0) > originY)) { + continue; + } + + int position = (int) yPositionToPixel(y); + g2.drawString(format(y), (int) axisV + 5, position); + } + } + + // Draws the horizontal lines of the grid. Draws both minor and major + // grid lines. + private void drawHorizontalGrid(Graphics2D g2) { + double minorSpacing = majorY / getMinorCountY(); + double axisV = xPositionToPixel(originX); + + Stroke gridStroke = new BasicStroke(STROKE_GRID); + Stroke axisStroke = new BasicStroke(STROKE_AXIS); + + Rectangle clip = g2.getClipBounds(); + + int position; + + if (!isAxisPainted()) { + position = (int) xPositionToPixel(originX); + if (position >= clip.x && position <= clip.x + clip.width) { + g2.setColor(getMajorGridColor()); + g2.drawLine(position, clip.y, position, clip.y + clip.height); + } + } + +// double startY = Math.floor((minY - originY) / majorY) * majorY; + double startY = Math.floor(minY / majorY) * majorY; + for (double y = startY; y < maxY + majorY; y += majorY) { + g2.setStroke(gridStroke); + g2.setColor(getMinorGridColor()); + for (int i = 0; i < getMinorCountY(); i++) { + position = (int) yPositionToPixel(y - i * minorSpacing); + if (position >= clip.y && position <= clip.y + clip.height) { + g2.drawLine(clip.x, position, clip.x + clip.width, position); + } + } + + position = (int) yPositionToPixel(y); + if (position >= clip.y && position <= clip.y + clip.height) { + g2.setColor(getMajorGridColor()); + g2.drawLine(clip.x, position, clip.x + clip.width, position); + + if (isAxisPainted()) { + g2.setStroke(axisStroke); + g2.setColor(getAxisColor()); + g2.drawLine((int) axisV - 3, position, (int) axisV + 3, position); + } + } + } + } + + // Draws labels on the horizontal axis. First draws labels on the right of + // the origin, then on the left. + private void drawHorizontalAxisLabels(Graphics2D g2) { + double axisH = yPositionToPixel(originY); + FontMetrics metrics = g2.getFontMetrics(); + +// double startX = Math.floor((minX - originX) / majorX) * majorX; + double startX = Math.floor(minX / majorX) * majorX; + for (double x = startX; x < maxX + majorX; x += majorX) { + if (((x - majorX / 2.0) < originX) && + ((x + majorX / 2.0) > originX)) { + continue; + } + + int position = (int) xPositionToPixel(x); + g2.drawString(format(x), position, + (int) axisH + metrics.getHeight()); + } + } + + // Draws the vertical lines of the grid. Draws both minor and major + // grid lines. + private void drawVerticalGrid(Graphics2D g2) { + double minorSpacing = majorX / getMinorCountX(); + double axisH = yPositionToPixel(originY); + + Stroke gridStroke = new BasicStroke(STROKE_GRID); + Stroke axisStroke = new BasicStroke(STROKE_AXIS); + + Rectangle clip = g2.getClipBounds(); + + int position; + if (!isAxisPainted()) { + position = (int) yPositionToPixel(originY); + if (position >= clip.y && position <= clip.y + clip.height) { + g2.setColor(getMajorGridColor()); + g2.drawLine(clip.x, position, clip.x + clip.width, position); + } + } + +// double startX = Math.floor((minX - originX) / majorX) * majorX; + double startX = Math.floor(minX / majorX) * majorX; + for (double x = startX; x < maxX + majorX; x += majorX) { + g2.setStroke(gridStroke); + g2.setColor(getMinorGridColor()); + for (int i = 0; i < getMinorCountX(); i++) { + position = (int) xPositionToPixel(x - i * minorSpacing); + if (position >= clip.x && position <= clip.x + clip.width) { + g2.drawLine(position, clip.y, position, clip.y + clip.height); + } + } + + position = (int) xPositionToPixel(x); + if (position >= clip.x && position <= clip.x + clip.width) { + g2.setColor(getMajorGridColor()); + g2.drawLine(position, clip.y, position, clip.y + clip.height); + + if (isAxisPainted()) { + g2.setStroke(axisStroke); + g2.setColor(getAxisColor()); + g2.drawLine(position, (int) axisH - 3, position, (int) axisH + 3); + } + } + } + } + + // Drase the main axis. + private void drawAxis(Graphics2D g2) { + if (!isAxisPainted()) { + return; + } + + double axisH = yPositionToPixel(originY); + double axisV = xPositionToPixel(originX); + + Rectangle clip = g2.getClipBounds(); + + g2.setColor(getAxisColor()); + Stroke stroke = g2.getStroke(); + g2.setStroke(new BasicStroke(STROKE_AXIS)); + + if (axisH >= clip.y && axisH <= clip.y + clip.height) { + g2.drawLine(clip.x, (int) axisH, clip.x + clip.width, (int) axisH); + } + if (axisV >= clip.x && axisV <= clip.x + clip.width) { + g2.drawLine((int) axisV, clip.y, (int) axisV, clip.y + clip.height); + } + + g2.setStroke(stroke); + } + + /** + *

This method is called by the component prior to any drawing operation + * to configure the drawing surface. The default implementation enables + * antialiasing on the graphics.

+ *

This method can be overriden by subclasses to modify the drawing + * surface before any painting happens.

+ * + * @param g2 the graphics surface to set up + * @see #paintExtra(Graphics2D) + * @see #paintBackground(Graphics2D) + */ + protected void setupGraphics(Graphics2D g2) { + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + } + + /** + *

This method is called by the component whenever it needs to paint + * its background. The default implementation fills the background with + * a solid color as defined by {@link #getBackground()}. Background painting + * does not happen when {@link #isBackgroundPainted()} returns false.

+ *

It is recommended to subclasses to honor the contract defined by + * {@link #isBackgroundPainted()} and {@link #setBackgroundPainted(boolean)}. + * + * @param g2 the graphics surface on which the background must be drawn + * @see #setupGraphics(Graphics2D) + * @see #paintExtra(Graphics2D) + * @see #isBackgroundPainted() + * @see #setBackgroundPainted(boolean) + */ + protected void paintBackground(Graphics2D g2) { + if (isBackgroundPainted()) { + Painter p = getBackgroundPainter(); + if (p != null) { + p.paint(g2, this, getWidth(), getHeight()); + } else { + g2.setColor(getBackground()); + g2.fill(g2.getClipBounds()); + } + } + } + + // Format a number with the appropriate number formatter. Numbers >= 0.01 + // and < 100 are formatted with a regular, 2-digits, numbers formatter. + // Other numbers use a scientific notation given by a DecimalFormat instance + private String format(double number) { + boolean farAway = (number != 0.0d && Math.abs(number) < 0.01d) || + Math.abs(number) > 99.0d; + return (farAway ? secondFormatter : mainFormatter).format(number); + } + + /** + *

A plot represents a mathematical transformation used by + * {@link JXGraph}. When a plot belongs to a graph, the graph component + * asks for the transformation of a value along the X axis. The resulting + * value defines the Y coordinates at which the graph must draw a spot of + * color.

+ * + *

Here is a sample implementation of this class that draws a straight line + * once added to a graph (it follows the well-known equation y=a.x+b):

+ * + *
+     * class LinePlot extends JXGraph.Plot {
+     *     public double compute(double value) {
+     *         return 2.0 * value + 1.0;
+     *     }
+     * }
+     * 
+ * + *

When a plot is added to an instance of + * JXGraph, the JXGraph automatically becomes + * a new property change listener of the plot. If property change events are + * fired, the graph will be updated accordingly.

+ * + *

More information about plots usage can be found in {@link JXGraph} in + * the section entitled Plots.

+ * + * @see JXGraph + * @see JXGraph#addPlots(Color, Plot...) + */ + public abstract static class Plot extends AbstractBean { + /** + *

Creates a new, parameter-less plot.

+ */ + protected Plot() { + } + + /** + *

This method must return the result of a mathematical + * transformation of its sole parameter.

+ * + * @param value a value along the X axis of the graph currently + * drawing this plot + * @return the result of the mathematical transformation of value + */ + public abstract double compute(double value); + } + + // Encapsulates a plot and its color. Avoids the use of a full-blown Map. + private static class DrawablePlot { + private final Plot equation; + private final Color color; + + private DrawablePlot(Plot equation, Color color) { + this.equation = equation; + this.color = color; + } + + private Plot getEquation() { + return equation; + } + + private Color getColor() { + return color; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final DrawablePlot that = (DrawablePlot) o; + if (!color.equals(that.color)) { + return false; + } + return equation.equals(that.equation); + } + + @Override + public int hashCode() { + int result; + result = equation.hashCode(); + result = 29 * result + color.hashCode(); + return result; + } + } + + // Shrinks or expand the view depending on the mouse wheel direction. + // When the wheel moves down, the view is expanded. Otherwise it is shrunk. + private class ZoomHandler implements MouseWheelListener { + @Override + public void mouseWheelMoved(MouseWheelEvent e) { + double distanceX = maxX - minX; + double distanceY = maxY - minY; + + double cursorX = minX + distanceX / 2.0; + double cursorY = minY + distanceY / 2.0; + + int rotation = e.getWheelRotation(); + if (rotation < 0) { + distanceX /= ZOOM_MULTIPLIER; + distanceY /= ZOOM_MULTIPLIER; + + majorX /= ZOOM_MULTIPLIER; + majorY /= ZOOM_MULTIPLIER; + } else { + distanceX *= ZOOM_MULTIPLIER; + distanceY *= ZOOM_MULTIPLIER; + + majorX *= ZOOM_MULTIPLIER; + majorY *= ZOOM_MULTIPLIER; + } + + minX = cursorX - distanceX / 2.0; + maxX = cursorX + distanceX / 2.0; + minY = cursorY - distanceY / 2.0; + maxY = cursorY + distanceY / 2.0; + + repaint(); + } + } + + // Listens for a click on the middle button of the mouse and resets the view + private class ResetHandler extends MouseAdapter { + @Override + public void mousePressed(MouseEvent e) { + if (e.getButton() != MouseEvent.BUTTON2) { + return; + } + + resetView(); + } + } + + // Starts and ends drag gestures with mouse left button. + private class PanHandler extends MouseAdapter { + @Override + public void mousePressed(MouseEvent e) { + if (e.getButton() != MouseEvent.BUTTON1) { + return; + } + + dragStart = e.getPoint(); + setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); + } + + @Override + public void mouseReleased(MouseEvent e) { + if (e.getButton() != MouseEvent.BUTTON1) { + return; + } + + setCursor(Cursor.getDefaultCursor()); + } + } + + // Handles drag gesture with the left mouse button and relocates the view + // accordingly. + private class PanMotionHandler extends MouseMotionAdapter { + @Override + public void mouseDragged(MouseEvent e) { + Point dragEnd = e.getPoint(); + + double distance = xPixelToPosition(dragEnd.getX()) - + xPixelToPosition(dragStart.getX()); + minX = minX - distance; + maxX = maxX - distance; + + distance = yPixelToPosition(dragEnd.getY()) - + yPixelToPosition(dragStart.getY()); + minY = minY - distance; + maxY = maxY - distance; + + repaint(); + dragStart = dragEnd; + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/JXHeader.java b/src/main/java/org/jdesktop/swingx/JXHeader.java new file mode 100644 index 0000000000..90c06dc544 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXHeader.java @@ -0,0 +1,358 @@ +/* + * $Id: JXHeader.java 4147 2012-02-01 17:13:24Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.plaf.HeaderAddon; +import org.jdesktop.swingx.plaf.HeaderUI; +import org.jdesktop.swingx.plaf.LookAndFeelAddons; + +import javax.swing.*; +import java.awt.*; + +/** + *

JXHeader is a simple component consisting of a title, a description, + * and an icon. An example of such a component can be seen on + * Romain Guys ProgX website

+ * + *

JXHeader is a simple component that is also sufficiently + * configurable to be usable. The description area + * accepts HTML conforming to version 3.2 of the HTML standard. The icon, title, + * and description are all configurable. JXHeader itself extends + * {@link JXPanel}, providing translucency and painting delegates.

+ * + *

If I were to reconstruct the ui shown in the above screenshot, I might + * do so like this:
+ *


+ *      JXHeader header = new JXHeader();
+ *      header.setTitle("Timing Framework Spline Editor");
+ *      header.setDescription("Drag control points in the display to change the " +
+ *          "shape of the spline\n" +
+ *          "Click the Copy Code button to generate the corresponding Java code.");
+ *      Icon icon = new ImageIcon(getClass().getResource("tools.png"));
+ *      header.setIcon(icon);
+ * 

+ * + * Note: The HTML support doesn't exist yet. The UI delegate needs to discover whether + * the text supplied is HTML or not, and change the content type of the editor pane + * being used. The problem is that if "text/html" is always used, the font is wrong. + * This same situation will be found in other parts of the code (JXErrorPane, for instance), + * so this needs to be dealt with. + * + *

Defaults

+ *

BasicHeaderUI uses the following UI defaults: + *

    + *
  • Header.defaultIcon: The default icon to use when creating a new JXHeader.
  • + *
+ *

+ * + * @status REVIEWED + * @author rbair + * @author rah003 + */ +@JavaBean +public class JXHeader extends JXPanel { + /** + * SerialVersionUID. + */ + private static final long serialVersionUID = 3593838231433068954L; + + /** + * JXHeader pluggable UI key HeaderUI + */ + public final static String uiClassID = "HeaderUI"; + + // ensure at least the default ui is registered + static { + LookAndFeelAddons.contribute(new HeaderAddon()); + } + + /** + * Specifies desired location of the icon relative to the title/description text. + */ + public static enum IconPosition { + /** + * Positions icon left from the text. + */ + LEFT, + /** + * Positions icon right from the text. + */ + RIGHT + } + private String title; + private String description; + private Icon icon; + private Font titleFont; + private Font descriptionFont; + private Color titleForeground; + private Color descriptionForeground; + private IconPosition iconPosition = IconPosition.RIGHT; + + /** Creates a new instance of JXHeader */ + public JXHeader() { + } + + /** + * Creates a new instance of JXHeader. PropertyChangeEvents are fired + * when the title and description properties are set. + * + * @param title specifies the title property for this JXHeader + * @param description specifies the description property for this JXHeader + */ + public JXHeader(String title, String description) { + this(title, description, null); + } + + /** Creates a new instance of JXHeader. PropertyChangeEvents are fired + * when the title and description properties are set. + * + * @param title specifies the title property for this JXHeader + * @param description specifies the description property for this JXHeader + * @param icon specifies the icon property for this JXHeader + */ + public JXHeader(String title, String description, Icon icon) { + setTitle(title); + setDescription(description); + setIcon(icon); + } + + //------------------------------------------------------------- UI Logic + + /** + * {@inheritDoc} + */ + @Override + public HeaderUI getUI() { + return (HeaderUI)super.getUI(); + } + + /** + * Sets the look and feel (L&F) object that renders this component. + * + * @param ui the HeaderUI L&F object + * @see javax.swing.UIDefaults#getUI + */ + public void setUI(HeaderUI ui) { + super.setUI(ui); + } + + /** + * Returns the name of the L&F class that renders this component. + * + * @return the string {@link #uiClassID} + * @see javax.swing.JComponent#getUIClassID + * @see javax.swing.UIDefaults#getUI + */ + @Override + public String getUIClassID() { + return uiClassID; + } + + /** + * Notification from the UIManager that the L&F has changed. + * Replaces the current UI object with the latest version from the + * UIManager. + * + * @see javax.swing.JComponent#updateUI + */ + @Override + public void updateUI() { + setUI((HeaderUI) LookAndFeelAddons + .getUI(this, HeaderUI.class)); + } + + /** + * Sets the title to use. This may be either plain text, or a simplified + * version of HTML, as JLabel would use. + * + * @param title the Title. May be null. + */ + public void setTitle(String title) { + String old = getTitle(); + this.title = title; + firePropertyChange("title", old, getTitle()); + } + + /** + * Gets the title. This may use HTML, such as + * that supported by JLabel (version 3.2 of the HTML spec). + * @return the title. May be null. + */ + public String getTitle() { + return title; + } + + /** + * Sets the description for this header. This may use HTML, such as + * that supported by JLabel (version 3.2 of the HTML spec). + * + * @param description the description. May be null, may be HTML or plain text. + */ + public void setDescription(String description) { + String old = getDescription(); + this.description = description; + firePropertyChange("description", old, getDescription()); + } + + /** + * Gets the description. + * + * @return description + */ + public String getDescription() { + return description; + } + + /** + * Sets the icon to use for the header. It is generally recommended that this + * be an image 64x64 pixels in size, and that the icon have no gaps at the top. + * + * @param icon may be null + */ + public void setIcon(Icon icon) { + Icon old = getIcon(); + this.icon = icon; + firePropertyChange("icon", old, getIcon()); + } + + /** + * Gets the icon. + * + * @return the Icon being used. May be null. + */ + public Icon getIcon() { + return icon; + } + + /** + * Sets new font for both, title and description line of the header. + * @see javax.swing.JComponent#setFont(Font) + */ + @Override + public void setFont(Font font) { + super.setFont(font); + setTitleFont(font); + setDescriptionFont(font); + } + + /** + * Sets new font for title. + * @param font New title font. + */ + public void setTitleFont(Font font) { + Font old = getTitleFont(); + this.titleFont = font; + firePropertyChange("titleFont", old, getTitleFont()); + } + + /** + * Gets title font. + * + * @return the Font being used. May be null. + */ + public Font getTitleFont() { + return titleFont; + } + + /** + * Sets font for the description line of header. + * @param font New description font. + */ + public void setDescriptionFont(Font font) { + Font old = getDescriptionFont(); + this.descriptionFont = font; + firePropertyChange("descriptionFont", old, getDescriptionFont()); + } + + /** + * Gets description font. + * + * @return the Font being used. May be null. + */ + public Font getDescriptionFont() { + return descriptionFont; + } + + /** + * Gets current title foreground color. + * @return the Color used to paint title. May be null. + */ + public Color getTitleForeground() { + return titleForeground; + } + + /** + * Sets title foreground color. + * @param titleForeground the Color to be used to paint title. + */ + public void setTitleForeground(Color titleForeground) { + Color old = getTitleForeground(); + this.titleForeground = titleForeground; + firePropertyChange("titleForeground", old, getTitleForeground()); + } + + /** + * Gets current description foreground color. + * @return the Color used to paint description. May be null. + */ + public Color getDescriptionForeground() { + return descriptionForeground; + } + + /** + * Sets description foreground color. + * @param descriptionForeground the Color to be used to paint description. + */ + public void setDescriptionForeground(Color descriptionForeground) { + Color old = getDescriptionForeground(); + this.descriptionForeground = descriptionForeground; + firePropertyChange("descriptionForeground", old, getDescriptionForeground()); + } + + /** + * Gets current icon position. Default is RIGHT. + * @return Current Icon position. + */ + public IconPosition getIconPosition() { + return iconPosition; + } + + /** + * Sets new Icon position. Position is relative to the text. Default value is RIGHT. + * @see #getIconPosition() + * @param iconPosition new desired icon position + */ + public void setIconPosition(IconPosition iconPosition) { + IconPosition old = getIconPosition(); + this.iconPosition = iconPosition; + firePropertyChange("iconPosition", old, getIconPosition()); + } + + @Override + public Dimension getPreferredSize() { + Dimension s = super.getPreferredSize(); + // TODO: hack for JXLabel issue ... see JXHeaderVisualCheck.interactiveCustomProperties(); + s.width += 5; + return s; + } +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/JXHyperlink.java b/src/main/java/org/jdesktop/swingx/JXHyperlink.java new file mode 100644 index 0000000000..6b16361417 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXHyperlink.java @@ -0,0 +1,347 @@ +/* + * $Id: JXHyperlink.java 4162 2012-02-08 16:21:35Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.hyperlink.AbstractHyperlinkAction; +import org.jdesktop.swingx.hyperlink.HyperlinkAction; +import org.jdesktop.swingx.plaf.HyperlinkAddon; +import org.jdesktop.swingx.plaf.LookAndFeelAddons; + +import javax.swing.*; +import javax.swing.plaf.ButtonUI; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.net.URI; + +/** + * A hyperlink component that derives from JButton to provide compatibility + * mostly for binding actions enabled/disabled behavior accessibility i18n etc... + *

+ * + * This button has visual state related to a notion of "clicked": + * foreground color is unclickedColor or clickedColor depending on + * its boolean bound property clicked being false or true, respectively. + * If the hyperlink has an action, it guarantees to synchronize its + * "clicked" state to an action value with key LinkAction.VISITED_KEY. + * Synchronization happens on setAction() and on propertyChange notification + * from the action. JXHyperlink accepts any type of action - + * {@link AbstractHyperlinkAction} is a convenience implementation to + * simplify clicked control. + *

+ * + *

 
+ *      LinkAction linkAction = new LinkAction("http://swinglabs.org") {
+ *            public void actionPerformed(ActionEvent e) {
+ *                doSomething(getTarget());
+ *                setVisited(true);
+ *            }
+ *      };
+ *      JXHyperlink hyperlink = new JXHyperlink(linkAction);
+ *  
+ * + * The hyperlink can be configured to always update its clicked + * property after firing the actionPerformed: + * + *
 
+ *      JXHyperlink hyperlink = new JXHyperlink(action);
+ *      hyperlink.setOverrulesActionOnClick(true);
+ *  
+ * + * By default, this property is false. The hyperlink will + * auto-click only if it has no action. Developers can change the + * behaviour by overriding {@link JXHyperlink#isAutoSetClicked()}; + * + * @author Richard Bair + * @author Shai Almog + * @author Jeanette Winzenburg + */ +@JavaBean +public class JXHyperlink extends JButton { + + /** + * @see #getUIClassID + * @see #readObject + */ + public static final String uiClassID = "HyperlinkUI"; + + // ensure at least the default ui is registered + static { + LookAndFeelAddons.contribute(new HyperlinkAddon()); + } + + private boolean hasBeenVisited = false; + + /** + * Color for the hyper link if it has not yet been clicked. This color can + * be set both in code, and through the UIManager with the property + * "JXHyperlink.unclickedColor". + */ + private Color unclickedColor; + + /** + * Color for the hyper link if it has already been clicked. This color can + * be set both in code, and through the UIManager with the property + * "JXHyperlink.clickedColor". + */ + private Color clickedColor; + + private boolean overrulesActionOnClick; + + /** + * Creates a new instance of JXHyperlink with default parameters + */ + public JXHyperlink() { + this(null); + } + + /** + * Creates a new instance of JHyperLink and configures it from provided Action. + * + * @param action Action whose parameters will be borrowed to configure newly + * created JXHyperLink + */ + public JXHyperlink(Action action) { + super(); + setAction(action); + init(); + } + + /** + * Convenience method to create and install a HyperlinkAction for the given URI. + * + * @param uri + * to uri to create a HyperlinkAction for, maybe null. + * @throws HeadlessException + * if {@link GraphicsEnvironment#isHeadless()} returns {@code true} + * @throws UnsupportedOperationException + * if the current platform doesn't support Desktop + * + * @see HyperlinkAction#createHyperlinkAction(URI) + */ + public void setURI(URI uri) { + setAction(HyperlinkAction.createHyperlinkAction(uri)); + } + + /** + * Returns the foreground color for unvisited links. + * + * @return Color for the hyper link if it has not yet been clicked. + */ + public Color getUnclickedColor() { + return unclickedColor; + } + + /** + * Sets the color for the previously visited link. This value will override the one + * set by the "JXHyperlink.clickedColor" UIManager property and defaults. + * + * @param color Color for the hyper link if it has already been clicked. + */ + public void setClickedColor(Color color) { + Color old = getClickedColor(); + clickedColor = color; + if (isClicked()) { + setForeground(getClickedColor()); + } + firePropertyChange("clickedColor", old, getClickedColor()); + } + + /** + * Returns the foreground color for visited links. + * + * @return Color for the hyper link if it has already been clicked. + */ + public Color getClickedColor() { + return clickedColor; + } + + /** + * Sets the color for the previously not visited link. This value will override the one + * set by the "JXHyperlink.unclickedColor" UIManager property and defaults. + * + * @param color Color for the hyper link if it has not yet been clicked. + */ + public void setUnclickedColor(Color color) { + Color old = getUnclickedColor(); + unclickedColor = color; + if (!isClicked()) { + setForeground(getUnclickedColor()); + } + firePropertyChange("unclickedColor", old, getUnclickedColor()); + } + + /** + * Sets the clicked property and updates visual state depending on clicked. + * This implementation updated the foreground color. + *

+ * + * NOTE: as with all button's visual properties, this will not update the + * backing action's "visited" state. + * + * @param clicked flag to indicate if the button should be regarded as + * having been clicked or not. + * @see #isClicked() + */ + public void setClicked(boolean clicked) { + boolean old = isClicked(); + hasBeenVisited = clicked; + setForeground(isClicked() ? getClickedColor() : getUnclickedColor()); + firePropertyChange("clicked", old, isClicked()); + } + + /** + * Returns a boolean indicating if this link has already been visited. + * + * @return true if hyper link has already been clicked. + * @see #setClicked(boolean) + */ + public boolean isClicked() { + return hasBeenVisited; + } + + /** + * Sets the overrulesActionOnClick property. It controls whether this + * button should overrule the Action's visited property on actionPerformed.

+ * + * The default value is false. + * + * @param overrule if true, fireActionPerformed will set clicked to true + * independent of action. + * + * @see #getOverrulesActionOnClick() + * @see #setClicked(boolean) + */ + public void setOverrulesActionOnClick(boolean overrule) { + boolean old = getOverrulesActionOnClick(); + this.overrulesActionOnClick = overrule; + firePropertyChange("overrulesActionOnClick", old, getOverrulesActionOnClick()); + } + + /** + * Returns a boolean indicating whether the clicked property should be set + * always on clicked. + * + * @return overrulesActionOnClick false if his button clicked property + * respects the Action's visited property. True if the clicked + * should be updated on every actionPerformed. + * + * @see #setOverrulesActionOnClick(boolean) + * @see #setClicked(boolean) + */ + public boolean getOverrulesActionOnClick() { + return overrulesActionOnClick; + } + + /** + * {@inheritDoc}

+ * Overridden to respect the overrulesActionOnClick property. + */ + @Override + protected void fireActionPerformed(ActionEvent event) { + super.fireActionPerformed(event); + if (isAutoSetClicked()) { + setClicked(true); + } + } + + /** + * Returns a boolean indicating whether the clicked property should be set + * after firing action events. + * Here: true if no action or overrulesAction property is true. + * @return true if fireActionEvent should force a clicked, false if not. + */ + protected boolean isAutoSetClicked() { + return getAction() == null || getOverrulesActionOnClick(); + } + + /** + * Creates and returns a listener that will watch the changes of the + * provided Action and will update JXHyperlink's properties + * accordingly. + */ + @Override + protected PropertyChangeListener createActionPropertyChangeListener( + final Action a) { + final PropertyChangeListener superListener = super + .createActionPropertyChangeListener(a); + // JW: need to do something better - only weak refs allowed! + // no way to hook into super + return new PropertyChangeListener() { + + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (AbstractHyperlinkAction.VISITED_KEY.equals(evt.getPropertyName())) { + configureClickedPropertyFromAction(a); + } else { + superListener.propertyChange(evt); + } + + } + + }; + } + + /** + * Read all the essential properties from the provided Action + * and apply it to the JXHyperlink + */ + @Override + protected void configurePropertiesFromAction(Action a) { + super.configurePropertiesFromAction(a); + configureClickedPropertyFromAction(a); + } + + private void configureClickedPropertyFromAction(Action a) { + boolean clicked = false; + if (a != null) { + clicked = Boolean.TRUE.equals(a.getValue(AbstractHyperlinkAction.VISITED_KEY)); + + } + setClicked(clicked); + } + + private void init() { + setForeground(isClicked() ? getClickedColor() : getUnclickedColor()); + } + + /** + * Returns a string that specifies the name of the L&F class + * that renders this component. + */ + @Override + public String getUIClassID() { + return uiClassID; + } + + /** + * Notification from the UIManager that the L&F has changed. + * Replaces the current UI object with the latest version from the UIManager. + * + * @see javax.swing.JComponent#updateUI + */ + @Override + public void updateUI() { + setUI((ButtonUI)LookAndFeelAddons.getUI(this, ButtonUI.class)); + } +} diff --git a/src/main/java/org/jdesktop/swingx/JXImagePanel.java b/src/main/java/org/jdesktop/swingx/JXImagePanel.java new file mode 100644 index 0000000000..87bcfd1bee --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXImagePanel.java @@ -0,0 +1,419 @@ +/* + * $Id: JXImagePanel.java 4017 2011-05-10 21:00:48Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx; + +import javax.imageio.ImageIO; +import javax.swing.*; +import java.awt.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.File; +import java.lang.ref.SoftReference; +import java.net.URL; +import java.util.concurrent.*; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + *

+ * A panel that draws an image. The standard mode is to draw the specified image + * centered and unscaled. The component&s preferred size is based on the + * image, unless explicitly set by the user. + *

+ *

+ * Images to be displayed can be set based on URL, Image, etc. This is + * accomplished by passing in an image loader. + * + *

+ * public class URLImageLoader extends Callable<Image> {
+ *     private URL url;
+ * 
+ *     public URLImageLoader(URL url) {
+ *         url.getClass(); //null check
+ *         this.url = url;
+ *     }
+ * 
+ *     public Image call() throws Exception {
+ *         return ImageIO.read(url);
+ *     }
+ * }
+ * 
+ * imagePanel.setImageLoader(new URLImageLoader(url));
+ * 
+ * + *

+ *

+ * This component also supports allowing the user to set the image. If the + * JXImagePanel is editable, then when the user clicks on the + * JXImagePanel a FileChooser is shown allowing the user to pick + * some other image to use within the JXImagePanel. + *

+ *

+ * TODO In the future, the JXImagePanel will also support tiling of images, + * scaling, resizing, cropping, segues etc. + *

+ *

+ * TODO other than the image loading this component can be replicated by a + * JXPanel with the appropriate Painter. What's the point? + *

+ * + * @author rbair + * @deprecated (pre-1.6.2) use a JXPanel with an ImagePainter; see Issue 988 + */ +//moved to package-private instead of deleting; needed by JXLoginPane +@Deprecated +class JXImagePanel extends JXPanel { + public static enum Style { + CENTERED, TILED, SCALED, SCALED_KEEP_ASPECT_RATIO + } + + private static final Logger LOG = Logger.getLogger(JXImagePanel.class.getName()); + + /** + * Text informing the user that clicking on this component will allow them + * to set the image + */ + private static final String TEXT = "Click here
to set the image
"; + + /** + * The image to draw + */ + private SoftReference img = new SoftReference(null); + + /** + * If true, then the image can be changed. Perhaps a better name is + * "readOnly", but editable was chosen to be more consistent with + * other Swing components. + */ + private boolean editable = false; + + /** + * The mouse handler that is used if the component is editable + */ + private MouseHandler mhandler = new MouseHandler(); + + /** + * Specifies how to draw the image, i.e. what kind of Style to use when + * drawing + */ + private Style style = Style.CENTERED; + + private Image defaultImage; + + private Callable imageLoader; + + private static final ExecutorService service = Executors.newFixedThreadPool(5); + + public JXImagePanel() { + } + + //TODO remove this constructor; no where else can a URL be used in this class + public JXImagePanel(URL imageUrl) { + try { + setImage(ImageIO.read(imageUrl)); + } catch (Exception e) { + // TODO need convert to something meaningful + LOG.log(Level.WARNING, "", e); + } + } + + /** + * Sets the image to use for the background of this panel. This image is + * painted whether the panel is opaque or translucent. + * + * @param image if null, clears the image. Otherwise, this will set the + * image to be painted. If the preferred size has not been explicitly + * set, then the image dimensions will alter the preferred size of + * the panel. + */ + public void setImage(Image image) { + if (image != img.get()) { + Image oldImage = img.get(); + img = new SoftReference(image); + firePropertyChange("image", oldImage, img); + invalidate(); + repaint(); + } + } + + /** + * @return the image used for painting the background of this panel + */ + public Image getImage() { + Image image = img.get(); + + //TODO perhaps we should have a default image loader? + if (image == null && imageLoader != null) { + try { + image = imageLoader.call(); + img = new SoftReference(image); + } catch (Exception e) { + LOG.log(Level.WARNING, "", e); + } + } + return image; + } + + /** + * @param editable + */ + public void setEditable(boolean editable) { + if (editable != this.editable) { + // if it was editable, remove the mouse handler + if (this.editable) { + removeMouseListener(mhandler); + } + this.editable = editable; + // if it is now editable, add the mouse handler + if (this.editable) { + addMouseListener(mhandler); + } + setToolTipText(editable ? TEXT : ""); + firePropertyChange("editable", !editable, editable); + repaint(); + } + } + + /** + * @return whether the image for this panel can be changed or not via the + * UI. setImage may still be called, even if isEditable + * returns false. + */ + public boolean isEditable() { + return editable; + } + + /** + * Sets what style to use when painting the image + * + * @param s + */ + public void setStyle(Style s) { + if (style != s) { + Style oldStyle = style; + style = s; + firePropertyChange("style", oldStyle, s); + repaint(); + } + } + + /** + * @return the Style used for drawing the image (CENTERED, TILED, etc). + */ + public Style getStyle() { + return style; + } + + /** + * {@inheritDoc} + * The old property value in PCE fired by this method might not be always correct! + */ + @Override + public Dimension getPreferredSize() { + if (!isPreferredSizeSet() && img != null) { + Image img = this.img.get(); + // was img GCed in the mean time? + if (img != null) { + // it has not been explicitly set, so return the width/height of + // the image + int width = img.getWidth(null); + int height = img.getHeight(null); + if (width == -1 || height == -1) { + return super.getPreferredSize(); + } + Insets insets = getInsets(); + width += insets.left + insets.right; + height += insets.top + insets.bottom; + return new Dimension(width, height); + } + } + return super.getPreferredSize(); + } + + /** + * Overridden to paint the image on the panel + * + * @param g + */ + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + Graphics2D g2 = (Graphics2D) g; + Image img = this.img.get(); + if (img == null && imageLoader != null) { + // schedule for loading (will repaint itself once loaded) + // have to use new future task every time as it holds strong + // reference to the object it retrieved and doesn't allow to reset + // it. + service.execute(new FutureTask(imageLoader) { + + @Override + protected void done() { + super.done(); + + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + try { + JXImagePanel.this.setImage(get()); + } catch (InterruptedException e) { + // ignore - canceled image load + } catch (ExecutionException e) { + LOG.log(Level.WARNING, "", e); + } + } + }); + } + + }); + img = defaultImage; + } + if (img != null) { + final int imgWidth = img.getWidth(null); + final int imgHeight = img.getHeight(null); + if (imgWidth == -1 || imgHeight == -1) { + // image hasn't completed loading, return + return; + } + + Insets insets = getInsets(); + final int pw = getWidth() - insets.left - insets.right; + final int ph = getHeight() - insets.top - insets.bottom; + + switch (style) { + case CENTERED: + Rectangle clipRect = g2.getClipBounds(); + int imageX = (pw - imgWidth) / 2 + insets.left; + int imageY = (ph - imgHeight) / 2 + insets.top; + Rectangle r = SwingUtilities.computeIntersection(imageX, imageY, imgWidth, imgHeight, clipRect); + if (r.x == 0 && r.y == 0 && (r.width == 0 || r.height == 0)) { + return; + } + // I have my new clipping rectangle "r" in clipRect space. + // It is therefore the new clipRect. + clipRect = r; + // since I have the intersection, all I need to do is adjust the + // x & y values for the image + int txClipX = clipRect.x - imageX; + int txClipY = clipRect.y - imageY; + int txClipW = clipRect.width; + int txClipH = clipRect.height; + + g2.drawImage(img, clipRect.x, clipRect.y, clipRect.x + clipRect.width, clipRect.y + clipRect.height, txClipX, txClipY, txClipX + txClipW, txClipY + txClipH, null); + break; + case TILED: + g2.translate(insets.left, insets.top); + Rectangle clip = g2.getClipBounds(); + g2.setClip(0, 0, pw, ph); + + int totalH = 0; + + while (totalH < ph) { + int totalW = 0; + + while (totalW < pw) { + g2.drawImage(img, totalW, totalH, null); + totalW += img.getWidth(null); + } + + totalH += img.getHeight(null); + } + + g2.setClip(clip); + g2.translate(-insets.left, -insets.top); + break; + case SCALED: + g2.drawImage(img, insets.left, insets.top, pw, ph, null); + break; + case SCALED_KEEP_ASPECT_RATIO: + int w = pw; + int h = ph; + final float ratioW = ((float) w) / ((float) imgWidth); + final float ratioH = ((float) h) / ((float) imgHeight); + + if (ratioW < ratioH) { + h = (int) (imgHeight * ratioW); + } else { + w = (int) (imgWidth * ratioH); + } + + final int x = (pw - w) / 2 + insets.left; + final int y = (ph - h) / 2 + insets.top; + g2.drawImage(img, x, y, w, h, null); + break; + default: + LOG.fine("unimplemented"); + g2.drawImage(img, insets.left, insets.top, this); + break; + } + } + } + + /** + * Handles click events on the component + */ + private class MouseHandler extends MouseAdapter { + private Cursor oldCursor; + + private JFileChooser chooser; + + @Override + public void mouseClicked(MouseEvent evt) { + if (chooser == null) { + chooser = new JFileChooser(); + } + int retVal = chooser.showOpenDialog(JXImagePanel.this); + if (retVal == JFileChooser.APPROVE_OPTION) { + File file = chooser.getSelectedFile(); + try { + setImage(new ImageIcon(file.toURI().toURL()).getImage()); + } catch (Exception ex) { + } + } + } + + @Override + public void mouseEntered(MouseEvent evt) { + if (oldCursor == null) { + oldCursor = getCursor(); + setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + } + } + + @Override + public void mouseExited(MouseEvent evt) { + if (oldCursor != null) { + setCursor(oldCursor); + oldCursor = null; + } + } + } + + public void setDefaultImage(Image def) { + this.defaultImage = def; + } + + public void setImageLoader(Callable loadImage) { + this.imageLoader = loadImage; + + } +} diff --git a/src/main/java/org/jdesktop/swingx/JXImageView.java b/src/main/java/org/jdesktop/swingx/JXImageView.java new file mode 100644 index 0000000000..6266aec394 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXImageView.java @@ -0,0 +1,771 @@ +/* + * $Id: JXImageView.java 4248 2012-11-13 18:12:08Z kschaefe $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.error.ErrorListener; +import org.jdesktop.swingx.error.ErrorSupport; +import org.jdesktop.swingx.painter.MattePainter; +import org.jdesktop.swingx.util.GraphicsUtilities; +import org.jdesktop.swingx.util.PaintUtils; + +import javax.imageio.ImageIO; +import javax.swing.*; +import javax.swing.event.MouseInputAdapter; +import java.awt.*; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.awt.image.AffineTransformOp; +import java.awt.image.BufferedImage; +import java.awt.image.BufferedImageOp; +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Logger; + +/** + *

A panel which shows an image centered. The user can drag an image into the + * panel from other applications and move the image around within the view. + * The JXImageView has built in actions for scaling, rotating, opening a new + * image, and saving. These actions can be obtained using the relevant get*Action() + * methods. + *

+ * + *

TODO: has dashed rect and text indicating you should drag there.

+ * + * + *

If the user drags more than one photo at a time into the JXImageView only + * the first photo will be loaded and shown. Any errors generated internally, + * such as dragging in a list of files which are not images, will be reported + * to any attached {@link ErrorListener} added by the + * {@link #addErrorListener}() method.

+ * + * @author Joshua Marinacci joshua.marinacci@sun.com + */ +@JavaBean +public class JXImageView extends JXPanel { + + private Logger log = Logger.getLogger(JXImageView.class.getName()); + /* ======= instance variables ========= */ + // the image this view will show + private Image image; + // the url of the image, if available + private URL imageURL; + + // support for error listeners + private ErrorSupport errorSupport = new ErrorSupport(this); + + // location to draw image. if null then draw in the center + private Point2D imageLocation; + // the scale for drawing the image + private double scale = 1.0; + // controls whether the user can move images around + private boolean editable = true; + // the handler for moving the image around within the panel + private MoveHandler moveHandler = new MoveHandler(this); + // controls the drag part of drag and drop + private boolean dragEnabled = false; + // controls the filename of the dropped file + private String exportName = "UntitledImage"; + // controls the format and filename extension of the dropped file + private String exportFormat = "png"; + + /** Creates a new instance of JXImageView */ + public JXImageView() { + // fix for: java.net/jira/browse/SWINGX-1479 + setBackgroundPainter(new MattePainter(PaintUtils.getCheckerPaint(Color.white,new Color(250,250,250),50))); + setEditable(true); + } + + + + /* ========= properties ========= */ + /** + * Gets the current image location. This location can be changed programmatically + * or by the user dragging the image within the JXImageView. + * @return the current image location + */ + public Point2D getImageLocation() { + return imageLocation; + } + + /** + * Set the current image location. + * @param imageLocation The new image location. + */ + public void setImageLocation(Point2D imageLocation) { + Point2D old = getImageLocation(); + this.imageLocation = imageLocation; + firePropertyChange("imageLocation", old, getImageLocation()); + repaint(); + } + + /** + * Gets the currently set image, or null if no image is set. + * @return the currently set image, or null if no image is set. + */ + public Image getImage() { + return image; + } + + /** + * Sets the current image. Can set null if there should be no image show. + * @param image the new image to set, or null. + */ + public void setImage(Image image) { + Image oldImage = getImage(); + this.image = image; + setImageLocation(null); + setScale(1.0); + firePropertyChange("image",oldImage,image); + repaint(); + } + + /** + * Set the current image to an image pointed to by this URL. + * @param url a URL pointing to an image, or null + * @throws IOException thrown if the image cannot be loaded + */ + public void setImage(URL url) throws IOException { + setImageURL(url); + //setImage(ImageIO.read(url)); + } + + /** + * Set the current image to an image pointed to by this File. + * @param file a File pointing to an image + * @throws IOException thrown if the image cannot be loaded + */ + public void setImage(File file) throws IOException { + setImageURL(file.toURI().toURL()); + } + + /** + * Gets the current image scale . When the scale is set to 1.0 + * then one image pixel = one screen pixel. When scale < 1.0 the draw image + * will be smaller than it's real size. When scale > 1.0 the drawn image will + * be larger than it's real size. 1.0 is the default value. + * @return the current image scale + */ + public double getScale() { + return scale; + } + + /** + * Sets the current image scale . When the scale is set to 1.0 + * then one image pixel = one screen pixel. When scale < 1.0 the draw image + * will be smaller than it's real size. When scale > 1.0 the drawn image will + * be larger than it's real size. 1.0 is the default value. + * @param scale the new image scale + */ + public void setScale(double scale) { + double oldScale = this.scale; + this.scale = scale; + this.firePropertyChange("scale",oldScale,scale); + repaint(); + } + + /** + * Returns whether or not the user can drag images. + * @return whether or not the user can drag images + */ + public boolean isEditable() { + return editable; + } + + /** + * Sets whether or not the user can drag images. When set to true the user can + * drag the photo around with their mouse. Also the cursor will be set to the + * 'hand' cursor. When set to false the user cannot drag photos around + * and the cursor will be set to the default. + * @param editable whether or not the user can drag images + */ + public void setEditable(boolean editable) { + boolean old = isEditable(); + this.editable = editable; + if(editable) { + addMouseMotionListener(moveHandler); + addMouseListener(moveHandler); + this.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + try { + this.setTransferHandler(new DnDHandler()); + } catch (ClassNotFoundException ex) { + ex.printStackTrace(); + fireError(ex); + } + } else { + removeMouseMotionListener(moveHandler); + removeMouseListener(moveHandler); + this.setCursor(Cursor.getDefaultCursor()); + setTransferHandler(null); + } + firePropertyChange("editable", old, isEditable()); + } + + /** + * Sets the dragEnabled property, which determines whether or not + * the user can drag images out of the image view and into other components or + * application. Note: setting + * this to true will disable the ability to move the image around within the + * well., though it will not change the editable property directly. + * @param dragEnabled the value to set the dragEnabled property to. + */ + public void setDragEnabled(boolean dragEnabled) { + boolean old = isDragEnabled(); + this.dragEnabled = dragEnabled; + firePropertyChange("dragEnabled", old, isDragEnabled()); + } + + /** + * Gets the current value of the dragEnabled property. + * @return the current value of the dragEnabled property + */ + public boolean isDragEnabled() { + return dragEnabled; + } + + /** + * Adds an ErrorListener to the list of listeners to be notified + * of ErrorEvents + * @param el an ErrorListener to add + */ + public void addErrorListener(ErrorListener el) { + errorSupport.addErrorListener(el); + } + + /** + * Remove an ErrorListener from the list of listeners to be notified of ErrorEvents. + * @param el an ErrorListener to remove + */ + public void removeErrorListener(ErrorListener el) { + errorSupport.removeErrorListener(el); + } + + /** + * Send a new ErrorEvent to all registered ErrorListeners + * @param throwable the Error or Exception which was thrown + */ + protected void fireError(Throwable throwable) { + errorSupport.fireErrorEvent(throwable); + } + + + private static FileDialog getSafeFileDialog(Component comp) { + Window win = SwingUtilities.windowForComponent(comp); + if(win instanceof Dialog) { + return new FileDialog((Dialog)win); + } + if(win instanceof Frame) { + return new FileDialog((Frame)win); + } + return null; + } + + // an action which will open a file chooser and load the selected image + // if any. + /** + * Returns an Action which will open a file chooser, ask the user for an image file + * then load the image into the view. If the load fails an error will be fired + * to all registered ErrorListeners + * @return the action + * @see ErrorListener + * @deprecated see SwingX issue 990 + */ + @Deprecated + public Action getOpenAction() { + Action action = new AbstractAction() { + @Override + public void actionPerformed(ActionEvent actionEvent) { + FileDialog fd = getSafeFileDialog(JXImageView.this); + fd.setMode(FileDialog.LOAD); + fd.setVisible(true); + if(fd.getFile() != null) { + try { + setImage(new File(fd.getDirectory(),fd.getFile())); + } catch (IOException ex) { + fireError(ex); + } + } + /* + JFileChooser chooser = new JFileChooser(); + chooser.showOpenDialog(JXImageView.this); + File file = chooser.getSelectedFile(); + if(file != null) { + try { + setImage(file); + } catch (IOException ex) { + log.fine(ex.getMessage()); + ex.printStackTrace(); + fireError(ex); + } + } + */ + } + }; + action.putValue(Action.NAME,"Open"); + return action; + } + + // an action that will open a file chooser then save the current image to + // the selected file, if any. + /** + * Returns an Action which will open a file chooser, ask the user for an image file + * then save the image from the view. If the save fails an error will be fired + * to all registered ErrorListeners + * @return an Action + * @deprecated see SwingX issue 990 + */ + @Deprecated + public Action getSaveAction() { + Action action = new AbstractAction() { + @Override + public void actionPerformed(ActionEvent evt) { + Image img = getImage(); + BufferedImage dst = new BufferedImage( + img.getWidth(null), + img.getHeight(null), + BufferedImage.TYPE_INT_ARGB); + Graphics2D g = (Graphics2D)dst.getGraphics(); + + try { + // smooth scaling + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BICUBIC); + g.drawImage(img, 0, 0, null); + } finally { + g.dispose(); + } + FileDialog fd = new FileDialog((Frame)SwingUtilities.windowForComponent(JXImageView.this)); + fd.setMode(FileDialog.SAVE); + fd.setVisible(true); + if(fd.getFile() != null) { + try { + ImageIO.write(dst,"png",new File(fd.getDirectory(),fd.getFile())); + } catch (IOException ex) { + fireError(ex); + } + } + /* + JFileChooser chooser = new JFileChooser(); + chooser.showSaveDialog(JXImageView.this); + File file = chooser.getSelectedFile(); + if(file != null) { + try { + ImageIO.write(dst,"png",file); + } catch (IOException ex) { + log.fine(ex.getMessage()); + ex.printStackTrace(); + fireError(ex); + } + } + */ + } + }; + + action.putValue(Action.NAME,"Save"); + return action; + } + + /** + * Get an action which will rotate the currently selected image clockwise. + * @return an action + * @deprecated see SwingX issue 990 + */ + @Deprecated + public Action getRotateClockwiseAction() { + Action action = new AbstractAction() { + @Override + public void actionPerformed(ActionEvent evt) { + Image img = getImage(); + BufferedImage src = new BufferedImage( + img.getWidth(null), + img.getHeight(null), + BufferedImage.TYPE_INT_ARGB); + BufferedImage dst = new BufferedImage( + img.getHeight(null), + img.getWidth(null), + BufferedImage.TYPE_INT_ARGB); + Graphics2D g = (Graphics2D)src.getGraphics(); + + try { + // smooth scaling + g.drawImage(img, 0, 0, null); + } finally { + g.dispose(); + } + + AffineTransform trans = AffineTransform.getRotateInstance(Math.PI/2,0,0); + trans.translate(0,-src.getHeight()); + BufferedImageOp op = new AffineTransformOp(trans, AffineTransformOp.TYPE_NEAREST_NEIGHBOR); + op.filter(src,dst); + setImage(dst); + } + }; + action.putValue(Action.NAME,"Rotate Clockwise"); + return action; + } + + /** + * Gets an action which will rotate the current image counter clockwise. + * @return an Action + * @deprecated see SwingX issue 990 + */ + @Deprecated + public Action getRotateCounterClockwiseAction() { + Action action = new AbstractAction() { + @Override + public void actionPerformed(ActionEvent evt) { + Image img = getImage(); + BufferedImage src = new BufferedImage( + img.getWidth(null), + img.getHeight(null), + BufferedImage.TYPE_INT_ARGB); + BufferedImage dst = new BufferedImage( + img.getHeight(null), + img.getWidth(null), + BufferedImage.TYPE_INT_ARGB); + Graphics2D g = (Graphics2D)src.getGraphics(); + + try { + // smooth scaling + g.drawImage(img, 0, 0, null); + } finally { + g.dispose(); + } + AffineTransform trans = AffineTransform.getRotateInstance(-Math.PI/2,0,0); + trans.translate(-src.getWidth(),0); + BufferedImageOp op = new AffineTransformOp(trans, AffineTransformOp.TYPE_NEAREST_NEIGHBOR); + op.filter(src,dst); + setImage(dst); + } + }; + action.putValue(Action.NAME, "Rotate CounterClockwise"); + return action; + } + + /** + * Gets an action which will zoom the current image out by a factor of 2. + * @return an action + * @deprecated see SwingX issue 990 + */ + @Deprecated + public Action getZoomOutAction() { + Action action = new AbstractAction() { + @Override + public void actionPerformed(ActionEvent actionEvent) { + setScale(getScale()*0.5); + } + }; + action.putValue(Action.NAME,"Zoom Out"); + return action; + } + + /** + * Gets an action which will zoom the current image in by a factor of 2 + * @return an action + * @deprecated see SwingX issue 990 + */ + @Deprecated + public Action getZoomInAction() { + Action action = new AbstractAction() { + @Override + public void actionPerformed(ActionEvent actionEvent) { + setScale(getScale()*2); + } + }; + action.putValue(Action.NAME,"Zoom In"); + return action; + } + /* === overriden methods === */ + + /** + * Implementation detail. + * @param g + */ + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + if(getImage() != null) { + Point2D center = new Point2D.Double(getWidth()/2,getHeight()/2); + if(getImageLocation() != null) { + center = getImageLocation(); + } + Point2D loc = new Point2D.Double(); + double width = getImage().getWidth(null)*getScale(); + double height = getImage().getHeight(null)*getScale(); + loc.setLocation(center.getX()-width/2, center.getY()-height/2); + g.drawImage(getImage(), (int)loc.getX(), (int)loc.getY(), + (int)width,(int)height, + null); + } + } + + + /* === Internal helper classes === */ + + private class MoveHandler extends MouseInputAdapter { + private JXImageView panel; + private Point prev = null; + private Point start = null; + public MoveHandler(JXImageView panel) { + this.panel = panel; + } + + @Override + public void mousePressed(MouseEvent evt) { + prev = evt.getPoint(); + start = prev; + } + + @Override + public void mouseDragged(MouseEvent evt) { + Point curr = evt.getPoint(); + + if(isDragEnabled()) { + //log.fine("testing drag enabled: " + curr + " " + start); + //log.fine("distance = " + curr.distance(start)); + if(curr.distance(start) > 5) { + JXImageView.this.log.fine("starting the drag: "); + panel.getTransferHandler().exportAsDrag((JComponent)evt.getSource(),evt,TransferHandler.COPY); + return; + } + } + + int offx = curr.x - prev.x; + int offy = curr.y - prev.y; + Point2D offset = getImageLocation(); + if (offset == null) { + if (image != null) { + offset = new Point2D.Double(getWidth() / 2, getHeight() / 2); + } else { + offset = new Point2D.Double(0, 0); + } + } + offset = new Point2D.Double(offset.getX() + offx, offset.getY() + offy); + setImageLocation(offset); + prev = curr; + repaint(); + } + + @Override + public void mouseReleased(MouseEvent evt) { + prev = null; + } + } + + private class DnDHandler extends TransferHandler { + DataFlavor urlFlavor; + + public DnDHandler() throws ClassNotFoundException { + urlFlavor = new DataFlavor("application/x-java-url;class=java.net.URL"); + } + + @Override + public void exportAsDrag(JComponent c, InputEvent evt, int action) { + //log.fine("exportting as drag"); + super.exportAsDrag(c,evt,action); + } + @Override + public int getSourceActions(JComponent c) { + //log.fine("get source actions: " + c); + return COPY; + } + @Override + protected void exportDone(JComponent source, Transferable data, int action) { + //log.fine("exportDone: " + source + " " + data + " " +action); + } + + @Override + public boolean canImport(JComponent c, DataFlavor[] flavors) { + //log.fine("canImport:" + c); + for (int i = 0; i < flavors.length; i++) { + //log.fine("testing: "+flavors[i]); + if (DataFlavor.javaFileListFlavor.equals(flavors[i])) { + return true; + } + if (DataFlavor.imageFlavor.equals(flavors[i])) { + return true; + } + if (urlFlavor.match(flavors[i])) { + return true; + } + + } + return false; + } + + @Override + protected Transferable createTransferable(JComponent c) { + JXImageView view = (JXImageView)c; + return new ImageTransferable(view.getImage(), + view.getExportName(), view.getExportFormat()); + } + + @Override + @SuppressWarnings("unchecked") + public boolean importData(JComponent comp, Transferable t) { + if (canImport(comp, t.getTransferDataFlavors())) { + try { + if(t.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) { + List files = (List) t.getTransferData(DataFlavor.javaFileListFlavor); + //log.fine("doing file list flavor"); + if (files.size() > 0) { + File file = files.get(0); + //log.fine("readingt hte image: " + file.getCanonicalPath()); + /*Iterator it = ImageIO.getImageReaders(new FileInputStream(file)); + while(it.hasNext()) { + log.fine("can read: " + it.next()); + }*/ + setImageString(file.toURI().toURL().toString()); + //BufferedImage img = ImageIO.read(file.toURI().toURL()); + //setImage(img); + return true; + } + } + //log.fine("doing a uri list"); + Object obj = t.getTransferData(urlFlavor); + //log.fine("obj = " + obj + " " + obj.getClass().getPackage() + " " + // + obj.getClass().getName()); + if(obj instanceof URL) { + setImageString(((URL)obj).toString()); + } + return true; + } catch (Exception ex) { + log .severe(ex.getMessage()); + ex.printStackTrace(); + fireError(ex); + } + } + return false; + } + + } + + + private static class ImageTransferable implements Transferable { + private Image img; + private List files; + private String exportName, exportFormat; + public ImageTransferable(Image img, String exportName, String exportFormat) { + this.img = img; + this.exportName = exportName; + this.exportFormat = exportFormat; + } + + @Override + public DataFlavor[] getTransferDataFlavors() { + return new DataFlavor[] { DataFlavor.imageFlavor, + DataFlavor.javaFileListFlavor }; + } + + @Override + public boolean isDataFlavorSupported(DataFlavor flavor) { + if(flavor == DataFlavor.imageFlavor) { + return true; + } + return flavor == DataFlavor.javaFileListFlavor; + } + + @Override + public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { + //log.fine("doing get trans data: " + flavor); + if(flavor == DataFlavor.imageFlavor) { + return img; + } + if(flavor == DataFlavor.javaFileListFlavor) { + if(files == null) { + files = new ArrayList(); + File file = File.createTempFile(exportName,"."+exportFormat); + //log.fine("writing to: " + file); + ImageIO.write(GraphicsUtilities.convertToBufferedImage(img),exportFormat,file); + files.add(file); + } + //log.fine("returning: " + files); + return files; + } + return null; + } + } + + public String getExportName() { + return exportName; + } + + public void setExportName(String exportName) { + String old = getExportName(); + this.exportName = exportName; + firePropertyChange("exportName", old, getExportName()); + } + + public String getExportFormat() { + return exportFormat; + } + + public void setExportFormat(String exportFormat) { + String old = getExportFormat(); + this.exportFormat = exportFormat; + firePropertyChange("exportFormat", old, getExportFormat()); + } + + public URL getImageURL() { + return imageURL; + } + + public void setImageURL(URL imageURL) throws IOException { + URL old = getImageURL(); + this.imageURL = imageURL; + firePropertyChange("imageURL", old, getImageURL()); + setImage(ImageIO.read(getImageURL())); + } + + /** Returns the current image's URL (if available) as a string. + * If the image has no URL, or if there is no image, then this + * method will return null. + * @return the url of the image as a string + */ + public String getImageString() { + if(getImageURL() == null) { + return null; + } + return getImageURL().toString(); + } + + /** Sets the current image using a string. This string must + * contain a valid URL. + * @param url string of a URL + * @throws IOException thrown if the URL does not parse + */ + public void setImageString(String url) throws IOException { + String old = getImageString(); + setImageURL(new URL(url)); + firePropertyChange("imageString", old, url); + } + +} diff --git a/src/main/java/org/jdesktop/swingx/JXLabel.java b/src/main/java/org/jdesktop/swingx/JXLabel.java new file mode 100644 index 0000000000..9c4da90c6d --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXLabel.java @@ -0,0 +1,1318 @@ +/* + * $Id: JXLabel.java 4225 2012-08-07 15:37:57Z kschaefe $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.painter.AbstractPainter; +import org.jdesktop.swingx.painter.Painter; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentEvent.ElementChange; +import javax.swing.plaf.basic.BasicHTML; +import javax.swing.text.*; +import java.awt.*; +import java.awt.event.HierarchyBoundsAdapter; +import java.awt.event.HierarchyEvent; +import java.awt.font.TextAttribute; +import java.awt.geom.Point2D; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.Reader; +import java.io.StringReader; + +/** + *

+ * A {@link JLabel} subclass which supports {@link Painter}s, multi-line text, + * and text rotation. + *

+ * + *

+ * Painter support consists of the foregroundPainter and backgroundPainter properties. The + * backgroundPainter refers to a painter responsible for painting beneath the text and icon. This + * painter, if set, will paint regardless of the opaque property. If the background painter does not + * fully paint each pixel, then you should make sure the opaque property is set to false. + *

+ * + *

+ * The foregroundPainter is responsible for painting the icon and the text label. If no foregroundPainter + * is specified, then the look and feel will paint the label. Note that if opaque is set to true and the look and feel + * is rendering the foreground, then the foreground may paint over the background. Most look and feels will + * paint a background when opaque is true. To avoid this behavior, set opaque to false. + *

+ * + *

+ * Since JXLabel is not opaque by default (isOpaque() returns false), neither of these problems + * typically present themselves. + *

+ * + *

+ * Multi-line text is enabled via the lineWrap property. Simply set it to true. By default, line wrapping + * occurs on word boundaries. + *

+ * + *

+ * The text (actually, the entire foreground and background) of the JXLabel may be rotated. Set the + * rotation property to specify what the rotation should be. Specify rotation angle in radian units. + *

+ * + * @author joshua.marinacci@sun.com + * @author rbair + * @author rah + * @author mario_cesar + */ +@JavaBean +public class JXLabel extends JLabel implements BackgroundPaintable { + + /** + * Text alignment enums. Controls alignment of the text when line wrapping is enabled. + */ + public enum TextAlignment implements IValue { + LEFT(StyleConstants.ALIGN_LEFT), CENTER(StyleConstants.ALIGN_CENTER), RIGHT(StyleConstants.ALIGN_RIGHT), JUSTIFY(StyleConstants.ALIGN_JUSTIFIED); + + private int value; + private TextAlignment(int val) { + value = val; + } + + @Override + public int getValue() { + return value; + } + + } + + protected interface IValue { + int getValue(); + } + + // textOrientation value declarations... + public static final double NORMAL = 0; + + public static final double INVERTED = Math.PI; + + public static final double VERTICAL_LEFT = 3 * Math.PI / 2; + + public static final double VERTICAL_RIGHT = Math.PI / 2; + + private double textRotation = NORMAL; + + private boolean painting = false; + + private Painter foregroundPainter; + + private Painter backgroundPainter; + + private boolean multiLine; + + private int pWidth; + + private int pHeight; + + // using reverse logic ... some methods causing re-flow of text are called from super constructor, but private variables are initialized only after call to super so have to rely on default for boolean being false + private boolean dontIgnoreRepaint = false; + + private int occupiedWidth; + + private static final String oldRendererKey = "was" + BasicHTML.propertyKey; + +// private static final Logger log = Logger.getAnonymousLogger(); +// static { +// log.setLevel(Level.FINEST); +// } + + /** + * Create a new JXLabel. This has the same semantics as creating a new JLabel. + */ + public JXLabel() { + super(); + initPainterSupport(); + initLineWrapSupport(); + } + + /** + * Creates new JXLabel with given icon. + * @param image the icon to set. + */ + public JXLabel(Icon image) { + super(image); + initPainterSupport(); + initLineWrapSupport(); + } + + /** + * Creates new JXLabel with given icon and alignment. + * @param image the icon to set. + * @param horizontalAlignment the text alignment. + */ + public JXLabel(Icon image, int horizontalAlignment) { + super(image, horizontalAlignment); + initPainterSupport(); + initLineWrapSupport(); + } + + /** + * Create a new JXLabel with the given text as the text for the label. This is shorthand for: + * + *

+     * JXLabel label = new JXLabel();
+     * label.setText("Some Text");
+     * 
+ * + * @param text the text to set. + */ + public JXLabel(String text) { + super(text); + initPainterSupport(); + initLineWrapSupport(); + } + + /** + * Creates new JXLabel with given text, icon and alignment. + * @param text the test to set. + * @param image the icon to set. + * @param horizontalAlignment the text alignment relative to the icon. + */ + public JXLabel(String text, Icon image, int horizontalAlignment) { + super(text, image, horizontalAlignment); + initPainterSupport(); + initLineWrapSupport(); + } + + /** + * Creates new JXLabel with given text and alignment. + * @param text the test to set. + * @param horizontalAlignment the text alignment. + */ + public JXLabel(String text, int horizontalAlignment) { + super(text, horizontalAlignment); + initPainterSupport(); + initLineWrapSupport(); + } + + private void initPainterSupport() { + foregroundPainter = new AbstractPainter() { + @Override + protected void doPaint(Graphics2D g, JXLabel label, int width, int height) { + Insets i = getInsets(); + g = (Graphics2D) g.create(-i.left, -i.top, width, height); + + try { + label.paint(g); + } finally { + g.dispose(); + } + } + //if any of the state of the JButton that affects the foreground has changed, + //then I must clear the cache. This is really hard to get right, there are + //bound to be bugs. An alternative is to NEVER cache. + @Override + protected boolean shouldUseCache() { + return false; + } + + @Override + public boolean equals(Object obj) { + return obj != null && this.getClass().equals(obj.getClass()); + } + + }; + ((AbstractPainter) foregroundPainter).setAntialiasing(false); + } + + /** + * Helper method for initializing multi line support. + */ + private void initLineWrapSupport() { + addPropertyChangeListener(new MultiLineSupport()); + // FYI: no more listening for componentResized. Those events are delivered out + // of order and without old values are meaningless and forcing us to react when + // not necessary. Instead overriding reshape() ensures we have control over old AND new size. + addHierarchyBoundsListener(new HierarchyBoundsAdapter() { + @Override + public void ancestorResized(HierarchyEvent e) { + // if one of the parents is viewport, resized events will not be propagated down unless viewport is changing visibility of scrollbars. + // To make sure Label is able to re-wrap text when viewport size changes, initiate re-wrapping here by changing size of view + if (e.getChanged() instanceof JViewport) { + Rectangle viewportBounds = e.getChanged().getBounds(); + if (viewportBounds.getWidth() < getWidth()) { + View view = getWrappingView(); + if (view != null) { + view.setSize(viewportBounds.width, viewportBounds.height); + } + } + } + }}); + } + + /** + * Returns the current foregroundPainter. This is a bound property. By default the foregroundPainter will be an + * internal painter which executes the standard painting code (paintComponent()). + * + * @return the current foreground painter. + */ + public final Painter getForegroundPainter() { + return foregroundPainter; + } + + @Override + @SuppressWarnings("deprecation") + public void reshape(int x, int y, int w, int h) { + int oldH = getHeight(); + super.reshape(x, y, w, h); + if (!isLineWrap()) { + return; + } + if (oldH == 0) { + return; + } + if (w > getVisibleRect().width) { + w = getVisibleRect().width; + } + View view = (View) getClientProperty(BasicHTML.propertyKey); + if (view != null && view instanceof Renderer) { + view.setSize(w - occupiedWidth, h); + } + } + + /** + * Sets a new foregroundPainter on the label. This will replace the existing foreground painter. Existing painters + * can be wrapped by using a CompoundPainter. + * + * @param painter + */ + public void setForegroundPainter(Painter painter) { + Painter old = this.getForegroundPainter(); + if (painter == null) { + //restore default painter + initPainterSupport(); + } else { + this.foregroundPainter = painter; + } + firePropertyChange("foregroundPainter", old, getForegroundPainter()); + repaint(); + } + + /** + * Sets a Painter to use to paint the background of this component By default there is already a single painter + * installed which draws the normal background for this component according to the current Look and Feel. Calling + * setBackgroundPainter will replace that existing painter. + * + * @param p the new painter + * @see #getBackgroundPainter() + */ + @Override + public void setBackgroundPainter(Painter p) { + Painter old = getBackgroundPainter(); + backgroundPainter = p; + firePropertyChange("backgroundPainter", old, getBackgroundPainter()); + repaint(); + } + + /** + * Returns the current background painter. The default value of this property is a painter which draws the normal + * JPanel background according to the current look and feel. + * + * @return the current painter + * @see #setBackgroundPainter(Painter) + */ + @Override + public final Painter getBackgroundPainter() { + return backgroundPainter; + } + + /** + * Gets current value of text rotation in rads. + * + * @return a double representing the current rotation of the text + * @see #setTextRotation(double) + */ + public double getTextRotation() { + return textRotation; + } + + @Override + public Dimension getPreferredSize() { + Dimension size = super.getPreferredSize(); + //if (true) return size; + if (isPreferredSizeSet()) { + //log.fine("ret 0"); + return size; + } else if (this.textRotation != NORMAL) { + // #swingx-680 change the preferred size when rotation is set ... ideally this would be solved in the LabelUI rather then here + double theta = getTextRotation(); + size.setSize(rotateWidth(size, theta), rotateHeight(size, + theta)); + } else { + // #swingx-780 preferred size is not set properly when parent container doesn't enforce the width + View view = getWrappingView(); + if (view == null) { + if (isLineWrap() && !MultiLineSupport.isHTML(getText())) { + getMultiLineSupport(); + // view might get lost on LAF change ... + putClientProperty(BasicHTML.propertyKey, + MultiLineSupport.createView(this)); + view = (View) getClientProperty(BasicHTML.propertyKey); + } else { + return size; + } + } + Insets insets = getInsets(); + int dx = insets.left + insets.right; + int dy = insets.top + insets.bottom; + //log.fine("INSETS:" + insets); + //log.fine("BORDER:" + this.getBorder()); + Rectangle textR = new Rectangle(); + Rectangle viewR = new Rectangle(); + textR.x = textR.y = textR.width = textR.height = 0; + viewR.x = dx; + viewR.y = dy; + viewR.width = viewR.height = Short.MAX_VALUE; + // layout label + // 1) icon + Rectangle iconR = calculateIconRect(); + // 2) init textR + boolean textIsEmpty = (getText() == null) || getText().equals(""); + int lsb = 0; + /* Unless both text and icon are non-null, we effectively ignore + * the value of textIconGap. + */ + int gap; + if (textIsEmpty) { + textR.width = textR.height = 0; + gap = 0; + } + else { + int availTextWidth; + gap = (iconR.width == 0) ? 0 : getIconTextGap(); + + occupiedWidth = dx + iconR.width + gap; + Object parent = getParent(); + if (parent != null && (parent instanceof JPanel)) { + JPanel panel = ((JPanel) parent); + Border b = panel.getBorder(); + if (b != null) { + Insets in = b.getBorderInsets(panel); + occupiedWidth += in.left + in.right; + } + } + if (getHorizontalTextPosition() == CENTER) { + availTextWidth = viewR.width; + } + else { + availTextWidth = viewR.width - (iconR.width + gap); + } + float xPrefSpan = view.getPreferredSpan(View.X_AXIS); + //log.fine("atw:" + availTextWidth + ", vps:" + xPrefSpan); + textR.width = Math.min(availTextWidth, (int) xPrefSpan); + if (maxLineSpan > 0) { + textR.width = Math.min(textR.width, maxLineSpan); + if (xPrefSpan > maxLineSpan) { + view.setSize(maxLineSpan, textR.height); + } + } + textR.height = (int) view.getPreferredSpan(View.Y_AXIS); + if (textR.height == 0) { + textR.height = getFont().getSize(); + } + //log.fine("atw:" + availTextWidth + ", vps:" + xPrefSpan + ", h:" + textR.height); + + } + // 3) set text xy based on h/v text pos + if (getVerticalTextPosition() == TOP) { + if (getHorizontalTextPosition() != CENTER) { + textR.y = 0; + } + else { + textR.y = -(textR.height + gap); + } + } + else if (getVerticalTextPosition() == CENTER) { + textR.y = (iconR.height / 2) - (textR.height / 2); + } + else { // (verticalTextPosition == BOTTOM) + if (getVerticalTextPosition() != CENTER) { + textR.y = iconR.height - textR.height; + } + else { + textR.y = (iconR.height + gap); + } + } + + if (getHorizontalTextPosition() == LEFT) { + textR.x = -(textR.width + gap); + } + else if (getHorizontalTextPosition() == CENTER) { + textR.x = (iconR.width / 2) - (textR.width / 2); + } + else { // (horizontalTextPosition == RIGHT) + textR.x = (iconR.width + gap); + } + + // 4) shift label around based on its alignment + int labelR_x = Math.min(iconR.x, textR.x); + int labelR_width = Math.max(iconR.x + iconR.width, + textR.x + textR.width) - labelR_x; + int labelR_y = Math.min(iconR.y, textR.y); + int labelR_height = Math.max(iconR.y + iconR.height, + textR.y + textR.height) - labelR_y; + + int dax, day; + + if (getVerticalAlignment() == TOP) { + day = viewR.y - labelR_y; + } + else if (getVerticalAlignment() == CENTER) { + day = (viewR.y + (viewR.height / 2)) - (labelR_y + (labelR_height / 2)); + } + else { // (verticalAlignment == BOTTOM) + day = (viewR.y + viewR.height) - (labelR_y + labelR_height); + } + + if (getHorizontalAlignment() == LEFT) { + dax = viewR.x - labelR_x; + } + else if (getHorizontalAlignment() == RIGHT) { + dax = (viewR.x + viewR.width) - (labelR_x + labelR_width); + } + else { // (horizontalAlignment == CENTER) + dax = (viewR.x + (viewR.width / 2)) - + (labelR_x + (labelR_width / 2)); + } + + textR.x += dax; + textR.y += day; + + iconR.x += dax; + iconR.y += day; + + if (lsb < 0) { + // lsb is negative. Shift the x location so that the text is + // visually drawn at the right location. + textR.x -= lsb; + } + // EO layout label + + int x1 = Math.min(iconR.x, textR.x); + int x2 = Math.max(iconR.x + iconR.width, textR.x + textR.width); + int y1 = Math.min(iconR.y, textR.y); + int y2 = Math.max(iconR.y + iconR.height, textR.y + textR.height); + Dimension rv = new Dimension(x2 - x1, y2 - y1); + + rv.width += dx; + rv.height += dy; + //log.fine("returning: " + rv); + return rv; + } + //log.fine("ret 3"); + return size; + } + + private View getWrappingView() { + if (super.getTopLevelAncestor() == null) { + return null; + } + View view = (View) getClientProperty(BasicHTML.propertyKey); + if (!(view instanceof Renderer)) { + return null; + } + return view; + } + + private Container getViewport() { + for(Container p = this; p != null; p = p.getParent()) { + if(p instanceof Window || SwingXUtilities.isApplet(p) || p instanceof JViewport) { + return p; + } + } + return null; + } + + private Rectangle calculateIconRect() { + Rectangle iconR = new Rectangle(); + Icon icon = isEnabled() ? getIcon() : getDisabledIcon(); + iconR.x = iconR.y = iconR.width = iconR.height = 0; + if (icon != null) { + iconR.width = icon.getIconWidth(); + iconR.height = icon.getIconHeight(); + } + else { + iconR.width = iconR.height = 0; + } + return iconR; + } + + public int getMaxLineSpan() { + return maxLineSpan ; + } + + public void setMaxLineSpan(int maxLineSpan) { + int old = getMaxLineSpan(); + this.maxLineSpan = maxLineSpan; + firePropertyChange("maxLineSpan", old, getMaxLineSpan()); + } + + private static int rotateWidth(Dimension size, double theta) { + return (int)Math.round(size.width*Math.abs(Math.cos(theta)) + + size.height*Math.abs(Math.sin(theta))); + } + + private static int rotateHeight(Dimension size, double theta) { + return (int)Math.round(size.width*Math.abs(Math.sin(theta)) + + size.height*Math.abs(Math.cos(theta))); + } + + /** + * Sets new value for text rotation. The value can be anything in range <0,2PI>. Note that although property name + * suggests only text rotation, the whole foreground painter is rotated in fact. Due to various reasons it is + * strongly discouraged to access any size related properties of the label from other threads then EDT when this + * property is set. + * + * @param textOrientation Value for text rotation in range <0,2PI> + * @see #getTextRotation() + */ + public void setTextRotation(double textOrientation) { + double old = getTextRotation(); + this.textRotation = textOrientation; + if (old != getTextRotation()) { + firePropertyChange("textRotation", old, getTextRotation()); + } + repaint(); + } + + /** + * Enables line wrapping support for plain text. By default this support is disabled to mimic default of the JLabel. + * Value of this property has no effect on HTML text. + * + * @param b the new value + */ + public void setLineWrap(boolean b) { + boolean old = isLineWrap(); + this.multiLine = b; + if (isLineWrap() != old) { + firePropertyChange("lineWrap", old, isLineWrap()); + if (getForegroundPainter() != null) { + // XXX There is a bug here. In order to make painter work with this, caching has to be disabled + ((AbstractPainter) getForegroundPainter()).setCacheable(!b); + } + //repaint(); + } + } + + /** + * Returns the current status of line wrap support. The default value of this property is false to mimic default + * JLabel behavior. Value of this property has no effect on HTML text. + * + * @return the current multiple line splitting status + */ + public boolean isLineWrap() { + return this.multiLine; + } + + private boolean paintBorderInsets = true; + + private int maxLineSpan = -1; + + public boolean painted; + + private TextAlignment textAlignment = TextAlignment.LEFT; + + /** + * Gets current text wrapping style. + * + * @return the text alignment for this label + */ + public TextAlignment getTextAlignment() { + return textAlignment; + } + + /** + * Sets style of wrapping the text. + * @see TextAlignment for accepted values. + * @param alignment + */ + public void setTextAlignment(TextAlignment alignment) { + TextAlignment old = getTextAlignment(); + this.textAlignment = alignment; + firePropertyChange("textAlignment", old, getTextAlignment()); + } + + /** + * Returns true if the background painter should paint where the border is + * or false if it should only paint inside the border. This property is + * true by default. This property affects the width, height, + * and initial transform passed to the background painter. + * @return current value of the paintBorderInsets property + */ + @Override + public boolean isPaintBorderInsets() { + return paintBorderInsets; + } + + @Override + public boolean isOpaque() { + return painting ? false : super.isOpaque(); + } + + /** + * Sets the paintBorderInsets property. + * Set to true if the background painter should paint where the border is + * or false if it should only paint inside the border. This property is true by default. + * This property affects the width, height, + * and initial transform passed to the background painter. + * + * This is a bound property. + * @param paintBorderInsets new value of the paintBorderInsets property + */ + @Override + public void setPaintBorderInsets(boolean paintBorderInsets) { + boolean old = this.isPaintBorderInsets(); + this.paintBorderInsets = paintBorderInsets; + firePropertyChange("paintBorderInsets", old, isPaintBorderInsets()); + } + + /** + * @param g graphics to paint on + */ + @Override + protected void paintComponent(Graphics g) { + //log.fine("in"); + // resizing the text view causes recursive callback to the paint down the road. In order to prevent such + // computationally intensive series of repaints every call to paint is skipped while top most call is being + // executed. +// if (!dontIgnoreRepaint) { +// return; +// } + painted = true; + if (painting || backgroundPainter == null && foregroundPainter == null) { + super.paintComponent(g); + } else { + pWidth = getWidth(); + pHeight = getHeight(); + if (backgroundPainter != null) { + Graphics2D tmp = (Graphics2D) g.create(); + + try { + SwingXUtilities.paintBackground(this, tmp); + } finally { + tmp.dispose(); + } + } + if (foregroundPainter != null) { + Insets i = getInsets(); + pWidth = getWidth() - i.left - i.right; + pHeight = getHeight() - i.top - i.bottom; + + Point2D tPoint = calculateT(); + double wx = Math.sin(textRotation) * tPoint.getY() + Math.cos(textRotation) * tPoint.getX(); + double wy = Math.sin(textRotation) * tPoint.getX() + Math.cos(textRotation) * tPoint.getY(); + double x = (getWidth() - wx) / 2 + Math.sin(textRotation) * tPoint.getY(); + double y = (getHeight() - wy) / 2; + Graphics2D tmp = (Graphics2D) g.create(); + if (i != null) { + tmp.translate(i.left + x, i.top + y); + } else { + tmp.translate(x, y); + } + tmp.rotate(textRotation); + + painting = true; + // uncomment to highlight text area + // Color c = g2.getColor(); + // g2.setColor(Color.RED); + // g2.fillRect(0, 0, getWidth(), getHeight()); + // g2.setColor(c); + //log.fine("PW:" + pWidth + ", PH:" + pHeight); + foregroundPainter.paint(tmp, this, pWidth, pHeight); + tmp.dispose(); + painting = false; + pWidth = 0; + pHeight = 0; + } + } + } + + private Point2D calculateT() { + double tx = getWidth(); + double ty = getHeight(); + + // orthogonal cases are most likely the most often used ones, so give them preferential treatment. + if ((textRotation > 4.697 && textRotation < 4.727) || (textRotation > 1.555 && textRotation < 1.585)) { + // vertical + int tmp = pHeight; + pHeight = pWidth; + pWidth = tmp; + tx = pWidth; + ty = pHeight; + } else if ((textRotation > -0.015 && textRotation < 0.015) + || (textRotation > 3.140 && textRotation < 3.1430)) { + // normal & inverted + pHeight = getHeight(); + pWidth = getWidth(); + } else { + // the rest of it. Calculate best rectangle that fits the bounds. "Best" is considered one that + // allows whole text to fit in, spanned on preferred axis (X). If that doesn't work, fit the text + // inside square with diagonal equal min(height, width) (Should be the largest rectangular area that + // fits in, math proof available upon request) + + dontIgnoreRepaint = false; + double square = Math.min(getHeight(), getWidth()) * Math.cos(Math.PI / 4d); + + View v = (View) getClientProperty(BasicHTML.propertyKey); + if (v == null) { + // no html and no wrapline enabled means no view + // ... find another way to figure out the heigh + ty = getFontMetrics(getFont()).getHeight(); + double cw = (getWidth() - Math.abs(ty * Math.sin(textRotation))) + / Math.abs(Math.cos(textRotation)); + double ch = (getHeight() - Math.abs(ty * Math.cos(textRotation))) + / Math.abs(Math.sin(textRotation)); + // min of whichever is above 0 (!!! no min of abs values) + tx = cw < 0 ? ch : ch > 0 ? Math.min(cw, ch) : cw; + } else { + float w = v.getPreferredSpan(View.X_AXIS); + float h = v.getPreferredSpan(View.Y_AXIS); + double c = w; + double alpha = textRotation;// % (Math.PI/2d); + boolean ready = false; + while (!ready) { + // shorten the view len until line break is forced + while (h == v.getPreferredSpan(View.Y_AXIS)) { + w -= 10; + v.setSize(w, h); + } + if (w < square || h > square) { + // text is too long to fit no matter what. Revert shape to square since that is the + // best option (1st derivation for area size of rotated rect in rect is equal 0 for + // rotated rect with equal w and h i.e. for square) + w = h = (float) square; + // set view height to something big to prevent recursive resize/repaint requests + v.setSize(w, 100000); + break; + } + // calc avail width with new view height + h = v.getPreferredSpan(View.Y_AXIS); + double cw = (getWidth() - Math.abs(h * Math.sin(alpha))) / Math.abs(Math.cos(alpha)); + double ch = (getHeight() - Math.abs(h * Math.cos(alpha))) / Math.abs(Math.sin(alpha)); + // min of whichever is above 0 (!!! no min of abs values) + c = cw < 0 ? ch : ch > 0 ? Math.min(cw, ch) : cw; + // make it one pix smaller to ensure text is not cut on the left + c--; + if (c > w) { + v.setSize((float) c, 10 * h); + ready = true; + } else { + v.setSize((float) c, 10 * h); + if (v.getPreferredSpan(View.Y_AXIS) > h) { + // set size back to figure out new line break and height after + v.setSize(w, 10 * h); + } else { + w = (float) c; + ready = true; + } + } + } + + tx = Math.floor(w);// xxx: watch out for first letter on each line missing some pixs!!! + ty = h; + } + pWidth = (int) tx; + pHeight = (int) ty; + dontIgnoreRepaint = true; + } + return new Point2D.Double(tx,ty); + } + + @Override + public void repaint() { + if (!dontIgnoreRepaint) { + return; + } + super.repaint(); + } + + @Override + public void repaint(int x, int y, int width, int height) { + if (!dontIgnoreRepaint) { + return; + } + super.repaint(x, y, width, height); + } + + @Override + public void repaint(long tm) { + if (!dontIgnoreRepaint) { + return; + } + super.repaint(tm); + } + + @Override + public void repaint(long tm, int x, int y, int width, int height) { + if (!dontIgnoreRepaint) { + return; + } + super.repaint(tm, x, y, width, height); + } + + // ---------------------------------------------------------- + // textOrientation magic + @Override + public int getHeight() { + int retValue = super.getHeight(); + if (painting) { + retValue = pHeight; + } + return retValue; + } + + @Override + public int getWidth() { + int retValue = super.getWidth(); + if (painting) { + retValue = pWidth; + } + return retValue; + } + + protected MultiLineSupport getMultiLineSupport() { + return new MultiLineSupport(); + } + // ---------------------------------------------------------- + // WARNING: + // Anything below this line is related to lineWrap support and can be safely ignored unless + // in need to mess around with the implementation details. + // ---------------------------------------------------------- + // FYI: This class doesn't reinvent line wrapping. Instead it makes use of existing support + // made for JTextComponent/JEditorPane. + // All the classes below named Alter* are verbatim copy of swing.text.* classes made to + // overcome package visibility of some of the code. All other classes here, when their name + // matches corresponding class from swing.text.* package are copy of the class with removed + // support for highlighting selection. In case this is ever merged back to JDK all of this + // can be safely removed as long as corresponding swing.text.* classes make appropriate checks + // before casting JComponent into JTextComponent to find out selected region since + // JLabel/JXLabel does not support selection of the text. + + public static class MultiLineSupport implements PropertyChangeListener { + + private static final String HTML = ""; + + private static ViewFactory basicViewFactory; + + private static BasicEditorKit basicFactory; + + @Override + public void propertyChange(PropertyChangeEvent evt) { + String name = evt.getPropertyName(); + JXLabel src = (JXLabel) evt.getSource(); + if ("ancestor".equals(name)) { + src.dontIgnoreRepaint = true; + } + if (src.isLineWrap()) { + if ("font".equals(name) || "foreground".equals(name) || "maxLineSpan".equals(name) || "textAlignment".equals(name) || "icon".equals(name) || "iconTextGap".equals(name)) { + if (evt.getOldValue() != null && !isHTML(src.getText())) { + updateRenderer(src); + } + } else if ("text".equals(name)) { + if (isHTML((String) evt.getOldValue()) && evt.getNewValue() != null + && !isHTML((String) evt.getNewValue())) { + // was html , but is not + if (src.getClientProperty(oldRendererKey) == null + && src.getClientProperty(BasicHTML.propertyKey) != null) { + src.putClientProperty(oldRendererKey, src.getClientProperty(BasicHTML.propertyKey)); + } + src.putClientProperty(BasicHTML.propertyKey, createView(src)); + } else if (!isHTML((String) evt.getOldValue()) && evt.getNewValue() != null + && !isHTML((String) evt.getNewValue())) { + // wasn't html and isn't + updateRenderer(src); + } else { + // either was html and is html or wasn't html, but is html + restoreHtmlRenderer(src); + } + } else if ("lineWrap".equals(name) && !isHTML(src.getText())) { + src.putClientProperty(BasicHTML.propertyKey, createView(src)); + } + } else if ("lineWrap".equals(name) && !((Boolean)evt.getNewValue())) { + restoreHtmlRenderer(src); + } + } + + private static void restoreHtmlRenderer(JXLabel src) { + Object current = src.getClientProperty(BasicHTML.propertyKey); + if (current == null || current instanceof Renderer) { + src.putClientProperty(BasicHTML.propertyKey, src.getClientProperty(oldRendererKey)); + } + } + + private static boolean isHTML(String s) { + return s != null && s.toLowerCase().startsWith(HTML); + } + + public static View createView(JXLabel c) { + BasicEditorKit kit = getFactory(); + float rightIndent = 0; + if (c.getIcon() != null && c.getHorizontalTextPosition() != SwingConstants.CENTER) { + rightIndent = c.getIcon().getIconWidth() + c.getIconTextGap(); + } + Document doc = kit.createDefaultDocument(c.getFont(), c.getForeground(), c.getTextAlignment(), rightIndent); + Reader r = new StringReader(c.getText() == null ? "" : c.getText()); + try { + kit.read(r, doc, 0); + } catch (Throwable e) { + } + ViewFactory f = kit.getViewFactory(); + View hview = f.create(doc.getDefaultRootElement()); + View v = new Renderer(c, f, hview, true); + return v; + } + + public static void updateRenderer(JXLabel c) { + View value = null; + View oldValue = (View) c.getClientProperty(BasicHTML.propertyKey); + if (oldValue == null || oldValue instanceof Renderer) { + value = createView(c); + } + if (value != oldValue && oldValue != null) { + for (int i = 0; i < oldValue.getViewCount(); i++) { + oldValue.getView(i).setParent(null); + } + } + c.putClientProperty(BasicHTML.propertyKey, value); + } + + private static BasicEditorKit getFactory() { + if (basicFactory == null) { + basicViewFactory = new BasicViewFactory(); + basicFactory = new BasicEditorKit(); + } + return basicFactory; + } + + private static class BasicEditorKit extends StyledEditorKit { + public Document createDefaultDocument(Font defaultFont, Color foreground, TextAlignment textAlignment, float rightIndent) { + BasicDocument doc = new BasicDocument(defaultFont, foreground, textAlignment, rightIndent); + doc.setAsynchronousLoadPriority(Integer.MAX_VALUE); + return doc; + } + + @Override + public ViewFactory getViewFactory() { + return basicViewFactory; + } + } + } + + private static class BasicViewFactory implements ViewFactory { + @Override + public View create(Element elem) { + + String kind = elem.getName(); + View view = null; + if (kind == null) { + // default to text display + view = new LabelView(elem); + } else if (kind.equals(AbstractDocument.ContentElementName)) { + view = new LabelView(elem); + } else if (kind.equals(AbstractDocument.ParagraphElementName)) { + view = new ParagraphView(elem); + } else if (kind.equals(AbstractDocument.SectionElementName)) { + view = new BoxView(elem, View.Y_AXIS); + } else if (kind.equals(StyleConstants.ComponentElementName)) { + view = new ComponentView(elem); + } else if (kind.equals(StyleConstants.IconElementName)) { + view = new IconView(elem); + } + return view; + } + } + + static class BasicDocument extends DefaultStyledDocument { + BasicDocument(Font defaultFont, Color foreground, TextAlignment textAlignment, float rightIndent) { + setFontAndColor(defaultFont, foreground); + + MutableAttributeSet attr = new SimpleAttributeSet(); + StyleConstants.setAlignment(attr, textAlignment.getValue()); + getStyle("default").addAttributes(attr); + + attr = new SimpleAttributeSet(); + StyleConstants.setRightIndent(attr, rightIndent); + getStyle("default").addAttributes(attr); + } + + private void setFontAndColor(Font font, Color fg) { + if (fg != null) { + + MutableAttributeSet attr = new SimpleAttributeSet(); + StyleConstants.setForeground(attr, fg); + getStyle("default").addAttributes(attr); + } + + if (font != null) { + MutableAttributeSet attr = new SimpleAttributeSet(); + StyleConstants.setFontFamily(attr, font.getFamily()); + getStyle("default").addAttributes(attr); + + attr = new SimpleAttributeSet(); + StyleConstants.setFontSize(attr, font.getSize()); + getStyle("default").addAttributes(attr); + + attr = new SimpleAttributeSet(); + StyleConstants.setBold(attr, font.isBold()); + getStyle("default").addAttributes(attr); + + attr = new SimpleAttributeSet(); + StyleConstants.setItalic(attr, font.isItalic()); + getStyle("default").addAttributes(attr); + + attr = new SimpleAttributeSet(); + Object underline = font.getAttributes().get(TextAttribute.UNDERLINE); + boolean canUnderline = underline instanceof Integer && (Integer) underline != -1; + StyleConstants.setUnderline(attr, canUnderline); + getStyle("default").addAttributes(attr); + } + + MutableAttributeSet attr = new SimpleAttributeSet(); + StyleConstants.setSpaceAbove(attr, 0f); + getStyle("default").addAttributes(attr); + + } + } + + /** + * Root text view that acts as an renderer. + */ + static class Renderer extends WrappedPlainView { + + JXLabel host; + + boolean invalidated = false; + + private float width; + + private float height; + + Renderer(JXLabel c, ViewFactory f, View v, boolean wordWrap) { + super(null, wordWrap); + factory = f; + view = v; + view.setParent(this); + host = c; + //log.fine("vir: " + host.getVisibleRect()); + int w; + if (host.getVisibleRect().width == 0) { + invalidated = true; + return; + } else { + w = host.getVisibleRect().width; + } + //log.fine("w:" + w); + // initially layout to the preferred size + //setSize(c.getMaxLineSpan() > -1 ? c.getMaxLineSpan() : view.getPreferredSpan(X_AXIS), view.getPreferredSpan(Y_AXIS)); + setSize(c.getMaxLineSpan() > -1 ? c.getMaxLineSpan() : w, host.getVisibleRect().height); + } + + @Override + protected void updateLayout(ElementChange ec, DocumentEvent e, Shape a) { + if ( (a != null)) { + // should damage more intelligently + preferenceChanged(null, true, true); + Container host = getContainer(); + if (host != null) { + host.repaint(); + } + } + } + + @Override + public void preferenceChanged(View child, boolean width, boolean height) { + if (host != null && host.painted) { + host.revalidate(); + host.repaint(); + } + } + + + /** + * Fetches the attributes to use when rendering. At the root level there are no attributes. If an attribute is + * resolved up the view hierarchy this is the end of the line. + */ + @Override + public AttributeSet getAttributes() { + return null; + } + + /** + * Renders the view. + * + * @param g the graphics context + * @param allocation the region to render into + */ + @Override + public void paint(Graphics g, Shape allocation) { + Rectangle alloc = allocation.getBounds(); + //log.fine("aloc:" + alloc + "::" + host.getVisibleRect() + "::" + host.getBounds()); + //view.setSize(alloc.width, alloc.height); + //this.width = alloc.width; + //this.height = alloc.height; + if (g.getClipBounds() == null) { + g.setClip(alloc); + view.paint(g, allocation); + g.setClip(null); + } else { + //g.translate(alloc.x, alloc.y); + view.paint(g, allocation); + //g.translate(-alloc.x, -alloc.y); + } + } + + /** + * Sets the view parent. + * + * @param parent the parent view + */ + @Override + public void setParent(View parent) { + throw new Error("Can't set parent on root view"); + } + + /** + * Returns the number of views in this view. Since this view simply wraps the root of the view hierarchy it has + * exactly one child. + * + * @return the number of views + * @see #getView + */ + @Override + public int getViewCount() { + return 1; + } + + /** + * Gets the n-th view in this container. + * + * @param n the number of the view to get + * @return the view + */ + @Override + public View getView(int n) { + return view; + } + + /** + * Returns the document model underlying the view. + * + * @return the model + */ + @Override + public Document getDocument() { + return view == null ? null : view.getDocument(); + } + + /** + * Sets the view size. + * + * @param width the width + * @param height the height + */ + @Override + public void setSize(float width, float height) { + if (host.maxLineSpan > 0) { + width = Math.min(width, host.maxLineSpan); + } + if (width == this.width && height == this.height) { + return; + } + this.width = (int) width; + this.height = (int) height; + view.setSize(width, height == 0 ? Short.MAX_VALUE : height); + if (this.height == 0) { + this.height = view.getPreferredSpan(View.Y_AXIS); + } + } + + @Override + public float getPreferredSpan(int axis) { + if (axis == X_AXIS) { + //log.fine("inv: " + invalidated + ", w:" + width + ", vw:" + host.getVisibleRect()); + // width currently laid out to + if (invalidated) { + int w = host.getVisibleRect().width; + if (w != 0) { + //log.fine("vrh: " + host.getVisibleRect().height); + invalidated = false; + // JXLabelTest4 works + setSize(w - (host.getOccupiedWidth()), host.getVisibleRect().height); + // JXLabelTest3 works; 20 == width of the parent border!!! ... why should this screw with us? + //setSize(w - (host.getOccupiedWidth()+20), host.getVisibleRect().height); + } + } + return width > 0 ? width : view.getPreferredSpan(axis); + } else { + return view.getPreferredSpan(axis); + } + } + + /** + * Fetches the container hosting the view. This is useful for things like scheduling a repaint, finding out the + * host components font, etc. The default implementation of this is to forward the query to the parent view. + * + * @return the container + */ + @Override + public Container getContainer() { + return host; + } + + /** + * Fetches the factory to be used for building the various view fragments that make up the view that represents + * the model. This is what determines how the model will be represented. This is implemented to fetch the + * factory provided by the associated EditorKit. + * + * @return the factory + */ + @Override + public ViewFactory getViewFactory() { + return factory; + } + + private View view; + + private ViewFactory factory; + + @Override + public int getWidth() { + return (int) width; + } + + @Override + public int getHeight() { + return (int) height; + } + + } + + protected int getOccupiedWidth() { + return occupiedWidth; + } +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/JXList.java b/src/main/java/org/jdesktop/swingx/JXList.java new file mode 100644 index 0000000000..e3201d5597 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXList.java @@ -0,0 +1,1568 @@ +/* + * $Id: JXList.java 4158 2012-02-03 18:29:40Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.decorator.ComponentAdapter; +import org.jdesktop.swingx.decorator.CompoundHighlighter; +import org.jdesktop.swingx.decorator.Highlighter; +import org.jdesktop.swingx.plaf.LookAndFeelAddons; +import org.jdesktop.swingx.plaf.UIAction; +import org.jdesktop.swingx.plaf.XListAddon; +import org.jdesktop.swingx.plaf.basic.core.BasicXListUI; +import org.jdesktop.swingx.renderer.AbstractRenderer; +import org.jdesktop.swingx.renderer.DefaultListRenderer; +import org.jdesktop.swingx.renderer.StringValue; +import org.jdesktop.swingx.rollover.ListRolloverController; +import org.jdesktop.swingx.rollover.ListRolloverProducer; +import org.jdesktop.swingx.rollover.RolloverProducer; +import org.jdesktop.swingx.rollover.RolloverRenderer; +import org.jdesktop.swingx.search.ListSearchable; +import org.jdesktop.swingx.search.SearchFactory; +import org.jdesktop.swingx.search.Searchable; +import org.jdesktop.swingx.sort.DefaultSortController; +import org.jdesktop.swingx.sort.ListSortController; +import org.jdesktop.swingx.sort.SortController; +import org.jdesktop.swingx.sort.StringValueRegistry; +import org.jdesktop.swingx.table.TableColumnExt; + +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.plaf.ListUI; +import javax.swing.text.Position.Bias; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Vector; +import java.util.logging.Logger; +import java.util.regex.Pattern; + +/** + * Enhanced List component with support for general SwingX sorting/filtering, + * rendering, highlighting, rollover and search functionality. List specific + * enhancements include ?? PENDING JW ... + * + *

Sorting and Filtering

+ * JXList supports sorting and filtering. + * + * Changed to use core support. Usage is very similar to J/X/Table. + * It provides api to apply a specific sort order, to toggle the sort order and to reset a sort. + * Sort sequence can be configured by setting a custom comparator. + * + *

+ * list.setAutoCreateRowSorter(true);
+ * list.setComparator(myComparator);
+ * list.setSortOrder(SortOrder.DESCENDING);
+ * list.toggleSortOder();
+ * list.resetSortOrder();
+ * 
+ * + *

+ * JXList provides api to access items of the underlying model in view coordinates + * and to convert from/to model coordinates. + * + * Note: JXList needs a specific ui-delegate - BasicXListUI and subclasses - which + * is aware of model vs. view coordiate systems and which controls the synchronization of + * selection/dataModel and sorter state. SwingX comes with a subclass for Synth. + * + *

Rendering and Highlighting

+ * + * As all SwingX collection views, a JXList is a HighlighterClient (PENDING JW: + * formally define and implement, like in AbstractTestHighlighter), that is it + * provides consistent api to add and remove Highlighters which can visually + * decorate the rendering component. + *

+ * + *


+ * 
+ * JXList list = new JXList(new Contributors());
+ * // implement a custom string representation, concated from first-, lastName
+ * StringValue sv = new StringValue() {
+ *     public String getString(Object value) {
+ *        if (value instanceof Contributor) {
+ *           Contributor contributor = (Contributor) value;
+ *           return contributor.lastName() + ", " + contributor.firstName(); 
+ *        }
+ *        return StringValues.TO_STRING(value);
+ *     }
+ * };
+ * list.setCellRenderer(new DefaultListRenderer(sv); 
+ * // highlight condition: gold merits
+ * HighlightPredicate predicate = new HighlightPredicate() {
+ *    public boolean isHighlighted(Component renderer,
+ *                     ComponentAdapter adapter) {
+ *       if (!(value instanceof Contributor)) return false;              
+ *       return ((Contributor) value).hasGold();
+ *    }
+ * };
+ * // highlight with foreground color 
+ * list.addHighlighter(new PainterHighlighter(predicate, goldStarPainter);      
+ * 
+ * 
+ * + * Note: to support the highlighting this implementation wraps the + * ListCellRenderer set by client code with a DelegatingRenderer which applies + * the Highlighter after delegating the default configuration to the wrappee. As + * a side-effect, getCellRenderer does return the wrapper instead of the custom + * renderer. To access the latter, client code must call getWrappedCellRenderer. + *

+ * + *

Rollover

+ * + * As all SwingX collection views, a JXList supports per-cell rollover. If + * enabled, the component fires rollover events on enter/exit of a cell which by + * default is promoted to the renderer if it implements RolloverRenderer, that + * is simulates live behaviour. The rollover events can be used by client code + * as well, f.i. to decorate the rollover row using a Highlighter. + * + *

+ * 
+ * JXList list = new JXList();
+ * list.setRolloverEnabled(true);
+ * list.setCellRenderer(new DefaultListRenderer());
+ * list.addHighlighter(new ColorHighlighter(HighlightPredicate.ROLLOVER_ROW, 
+ *      null, Color.RED);      
+ * 
+ * 
+ * + * + *

Search

+ * + * As all SwingX collection views, a JXList is searchable. A search action is + * registered in its ActionMap under the key "find". The default behaviour is to + * ask the SearchFactory to open a search component on this component. The + * default keybinding is retrieved from the SearchFactory, typically ctrl-f (or + * cmd-f for Mac). Client code can register custom actions and/or bindings as + * appropriate. + *

+ * + * JXList provides api to vend a renderer-controlled String representation of + * cell content. This allows the Searchable and Highlighters to use WYSIWYM + * (What-You-See-Is-What-You-Match), that is pattern matching against the actual + * string as seen by the user. + * + * + * @author Ramesh Gupta + * @author Jeanette Winzenburg + */ +@JavaBean +public class JXList extends JList { + @SuppressWarnings("all") + private static final Logger LOG = Logger.getLogger(JXList.class.getName()); + + /** + * UI Class ID + */ + public final static String uiClassID = "XListUI"; + + /** + * Registers a Addon for JXList. + */ + static { + LookAndFeelAddons.contribute(new XListAddon()); + } + + + + public static final String EXECUTE_BUTTON_ACTIONCOMMAND = "executeButtonAction"; + + /** + * The pipeline holding the highlighters. + */ + protected CompoundHighlighter compoundHighlighter; + + /** listening to changeEvents from compoundHighlighter. */ + private ChangeListener highlighterChangeListener; + + /** The ComponentAdapter for model data access. */ + protected ComponentAdapter dataAdapter; + + /** + * Mouse/Motion/Listener keeping track of mouse moved in cell coordinates. + */ + private RolloverProducer rolloverProducer; + + /** + * RolloverController: listens to cell over events and repaints + * entered/exited rows. + */ + private ListRolloverController linkController; + + /** A wrapper around the default renderer enabling decoration. */ + private transient DelegatingRenderer delegatingRenderer; + + private Searchable searchable; + + private Comparator comparator; + + private boolean autoCreateRowSorter; + + private RowSorter rowSorter; + + private boolean sortable; + + private boolean sortsOnUpdates; + + private StringValueRegistry stringValueRegistry; + + private SortOrder[] sortOrderCycle; + + /** + * Constructs a JXList with an empty model and filters disabled. + * + */ + public JXList() { + this(false); + } + + /** + * Constructs a JXList that displays the elements in the + * specified, non-null model and automatic creation of a RowSorter disabled. + * + * @param dataModel the data model for this list + * @exception IllegalArgumentException if dataModel + * is null + */ + public JXList(ListModel dataModel) { + this(dataModel, false); + } + + /** + * Constructs a JXList that displays the elements in + * the specified array and automatic creation of a RowSorter disabled. + * + * @param listData the array of Objects to be loaded into the data model + * @throws IllegalArgumentException if listData + * is null + */ + public JXList(Object[] listData) { + this(listData, false); + } + + /** + * Constructs a JXList that displays the elements in + * the specified Vector and automatic creation of a RowSorter disabled. + * + * @param listData the Vector to be loaded into the + * data model + * @throws IllegalArgumentException if listData + * is null + */ + public JXList(Vector listData) { + this(listData, false); + } + + + /** + * Constructs a JXList with an empty model and + * automatic creation of a RowSorter as given. + * + * @param autoCreateRowSorter boolean to determine if + * a RowSorter should be created automatically. + */ + public JXList(boolean autoCreateRowSorter) { + init(autoCreateRowSorter); + } + + /** + * Constructs a JXList with the specified model and + * automatic creation of a RowSorter as given. + * + * @param dataModel the data model for this list + * @param autoCreateRowSorter boolean to determine if + * a RowSorter should be created automatically. + * @throws IllegalArgumentException if dataModel + * is null + */ + public JXList(ListModel dataModel, boolean autoCreateRowSorter) { + super(dataModel); + init(autoCreateRowSorter); + } + + /** + * Constructs a JXList that displays the elements in + * the specified array and automatic creation of a RowSorter as given. + * + * @param listData the array of Objects to be loaded into the data model + * @param autoCreateRowSorter boolean to determine if + * a RowSorter should be created automatically. + * @throws IllegalArgumentException if listData + * is null + */ + public JXList(Object[] listData, boolean autoCreateRowSorter) { + super(listData); + if (listData == null) + throw new IllegalArgumentException("listData must not be null"); + init(autoCreateRowSorter); + } + + /** + * Constructs a JXList that displays the elements in + * the specified Vector and filtersEnabled property. + * + * @param listData the Vector to be loaded into the + * data model + * @param autoCreateRowSorter boolean to determine if + * a RowSorter should be created automatically. + * @throws IllegalArgumentException if listData is null + */ + public JXList(Vector listData, boolean autoCreateRowSorter) { + super(listData); + if (listData == null) + throw new IllegalArgumentException("listData must not be null"); + init(autoCreateRowSorter); + } + + + private void init(boolean autoCreateRowSorter) { + sortOrderCycle = DefaultSortController.getDefaultSortOrderCycle(); + setSortable(true); + setSortsOnUpdates(true); + setAutoCreateRowSorter(autoCreateRowSorter); + Action findAction = createFindAction(); + getActionMap().put("find", findAction); + + KeyStroke findStroke = SearchFactory.getInstance().getSearchAccelerator(); + getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(findStroke, "find"); + } + + private Action createFindAction() { + return new UIAction("find") { + @Override + public void actionPerformed(ActionEvent e) { + doFind(); + } + }; + } + + /** + * Starts a search on this List's visible items. This implementation asks the + * SearchFactory to open a find widget on itself. + */ + protected void doFind() { + SearchFactory.getInstance().showFindInput(this, getSearchable()); + } + + /** + * Returns a Searchable for this component, guaranteed to be not null. This + * implementation lazily creates a ListSearchable if necessary. + * + * @return a not-null Searchable for this list. + * + * @see #setSearchable(Searchable) + * @see ListSearchable + */ + public Searchable getSearchable() { + if (searchable == null) { + searchable = new ListSearchable(this); + } + return searchable; + } + + /** + * Sets the Searchable for this component. If null, a default + * Searchable will be created and used. + * + * @param searchable the Searchable to use for this component, may be null to indicate + * using the list's default searchable. + * @see #getSearchable() + */ + public void setSearchable(Searchable searchable) { + this.searchable = searchable; + } + + + /** + * {@inheritDoc}

+ * + * Overridden to cope with sorting/filtering, taking over completely. + */ + @Override + public int getNextMatch(String prefix, int startIndex, Bias bias) { + Pattern pattern = Pattern.compile("^" + prefix, Pattern.CASE_INSENSITIVE); + return getSearchable().search(pattern, startIndex, bias ==Bias.Backward); + } +//--------------------- Rollover support + + /** + * Sets the property to enable/disable rollover support. If enabled, the list + * fires property changes on per-cell mouse rollover state, i.e. + * when the mouse enters/leaves a list cell.

+ * + * This can be enabled to show "live" rollover behaviour, f.i. the cursor over a cell + * rendered by a JXHyperlink.

+ * + * Default value is disabled. + * + * @param rolloverEnabled a boolean indicating whether or not the rollover + * functionality should be enabled. + * + * @see #isRolloverEnabled() + * @see #getLinkController() + * @see #createRolloverProducer() + * @see RolloverRenderer + * + */ + public void setRolloverEnabled(boolean rolloverEnabled) { + boolean old = isRolloverEnabled(); + if (rolloverEnabled == old) + return; + if (rolloverEnabled) { + rolloverProducer = createRolloverProducer(); + rolloverProducer.install(this); + getLinkController().install(this); + } else { + rolloverProducer.release(this); + rolloverProducer = null; + getLinkController().release(); + } + firePropertyChange("rolloverEnabled", old, isRolloverEnabled()); + } + + /** + * Returns a boolean indicating whether or not rollover support is enabled. + * + * @return a boolean indicating whether or not rollover support is enabled. + * + * @see #setRolloverEnabled(boolean) + */ + public boolean isRolloverEnabled() { + return rolloverProducer != null; + } + + /** + * Returns the RolloverController for this component. Lazyly creates the + * controller if necessary, that is the return value is guaranteed to be + * not null.

+ * + * PENDING JW: rename to getRolloverController + * + * @return the RolloverController for this tree, guaranteed to be not null. + * + * @see #setRolloverEnabled(boolean) + * @see #createLinkController() + * @see org.jdesktop.swingx.rollover.RolloverController + */ + protected ListRolloverController getLinkController() { + if (linkController == null) { + linkController = createLinkController(); + } + return linkController; + } + + /** + * Creates and returns a RolloverController appropriate for this component. + * + * @return a RolloverController appropriate for this component. + * + * @see #getLinkController() + * @see org.jdesktop.swingx.rollover.RolloverController + */ + protected ListRolloverController createLinkController() { + return new ListRolloverController(); + } + + + /** + * Creates and returns the RolloverProducer to use with this tree. + *

+ * + * @return RolloverProducer to use with this tree + * + * @see #setRolloverEnabled(boolean) + */ + protected RolloverProducer createRolloverProducer() { + return new ListRolloverProducer(); + } + + //--------------------- public sort api + + /** + * Returns {@code true} if whenever the model changes, a new + * {@code RowSorter} should be created and installed + * as the table's sorter; otherwise, returns {@code false}. + * + * @return true if a {@code RowSorter} should be created when + * the model changes + * @since 1.6 + */ + public boolean getAutoCreateRowSorter() { + return autoCreateRowSorter; + } + + /** + * Specifies whether a {@code RowSorter} should be created for the + * list whenever its model changes. + *

+ * When {@code setAutoCreateRowSorter(true)} is invoked, a {@code + * RowSorter} is immediately created and installed on the + * list. While the {@code autoCreateRowSorter} property remains + * {@code true}, every time the model is changed, a new {@code + * RowSorter} is created and set as the list's row sorter.

+ * + * The default value is false. + * + * @param autoCreateRowSorter whether or not a {@code RowSorter} + * should be automatically created + * @beaninfo + * bound: true + * preferred: true + * description: Whether or not to turn on sorting by default. + */ + public void setAutoCreateRowSorter(boolean autoCreateRowSorter) { + if (getAutoCreateRowSorter() == autoCreateRowSorter) return; + boolean oldValue = getAutoCreateRowSorter(); + this.autoCreateRowSorter = autoCreateRowSorter; + if (autoCreateRowSorter) { + setRowSorter(createDefaultRowSorter()); + } + firePropertyChange("autoCreateRowSorter", oldValue, + getAutoCreateRowSorter()); + } + + /** + * Creates and returns the default RowSorter. Note that this is already + * configured to the current ListModel. + * + * PENDING JW: review method signature - better expose the need for the + * model by adding a parameter? + * + * @return the default RowSorter. + */ + protected RowSorter createDefaultRowSorter() { + return new ListSortController(getModel()); + } + /** + * Returns the object responsible for sorting. + * + * @return the object responsible for sorting + * @since 1.6 + */ + public RowSorter getRowSorter() { + return rowSorter; + } + + /** + * Sets the RowSorter. RowSorter is used + * to provide sorting and filtering to a JXList. + *

+ * This method clears the selection and resets any variable row heights. + *

+ * If the underlying model of the RowSorter differs from + * that of this JXList undefined behavior will result. + * + * @param sorter the RowSorter; null turns + * sorting off + */ + public void setRowSorter(RowSorter sorter) { + RowSorter oldRowSorter = getRowSorter(); + this.rowSorter = sorter; + configureSorterProperties(); + firePropertyChange("rowSorter", oldRowSorter, sorter); + } + + /** + * Propagates sort-related properties from table/columns to the sorter if it + * is of type SortController, does nothing otherwise. + * + */ + protected void configureSorterProperties() { + if (!getControlsSorterProperties()) return; + // configure from table properties + getSortController().setSortable(sortable); + getSortController().setSortsOnUpdates(sortsOnUpdates); + getSortController().setComparator(0, comparator); + getSortController().setSortOrderCycle(getSortOrderCycle()); + getSortController().setStringValueProvider(getStringValueRegistry()); + } + + /** + * Sets "sortable" property indicating whether or not this list + * isSortable. + * + * Note: as of post-1.0 this property is propagated to the SortController. + * Whether or not a change triggers a re-sort is up to either the concrete controller + * implementation (the default doesn't) or client code. This behaviour is + * different from old SwingX style sorting. + * + * @see TableColumnExt#isSortable() + * @param sortable boolean indicating whether or not this table supports + * sortable columns + */ + public void setSortable(boolean sortable) { + boolean old = isSortable(); + this.sortable = sortable; + if (getControlsSorterProperties()) { + getSortController().setSortable(sortable); + } + firePropertyChange("sortable", old, isSortable()); + } + + /** + * Returns the table's sortable property.

+ * + * @return true if the table is sortable. + */ + public boolean isSortable() { + return sortable; + } + + /** + * If true, specifies that a sort should happen when the underlying + * model is updated (rowsUpdated is invoked). For + * example, if this is true and the user edits an entry the + * location of that item in the view may change. The default is + * true. + * + * @param sortsOnUpdates whether or not to sort on update events + */ + public void setSortsOnUpdates(boolean sortsOnUpdates) { + boolean old = getSortsOnUpdates(); + this.sortsOnUpdates = sortsOnUpdates; + if (getControlsSorterProperties()) { + getSortController().setSortsOnUpdates(sortsOnUpdates); + } + firePropertyChange("sortsOnUpdates", old, getSortsOnUpdates()); + } + + /** + * Returns true if a sort should happen when the underlying + * model is updated; otherwise, returns false. + * + * @return whether or not to sort when the model is updated + */ + public boolean getSortsOnUpdates() { + return sortsOnUpdates; + } + + /** + * Sets the sortorder cycle used when toggle sorting this table's columns. + * This property is propagated to the SortController + * if controlsSorterProperties is true. + * + * @param cycle the sequence of zero or more not-null SortOrders to cycle through. + * @throws NullPointerException if the array or any of its elements are null + * + */ + public void setSortOrderCycle(SortOrder... cycle) { + SortOrder[] old = getSortOrderCycle(); + if (getControlsSorterProperties()) { + getSortController().setSortOrderCycle(cycle); + } + this.sortOrderCycle = Arrays.copyOf(cycle, cycle.length); + firePropertyChange("sortOrderCycle", old, getSortOrderCycle()); + } + + /** + * Returns the sortOrder cycle used when toggle sorting this table's columns, guaranteed + * to be not null. + * + * @return the sort order cycle used in toggle sort, not null + */ + public SortOrder[] getSortOrderCycle() { + return Arrays.copyOf(sortOrderCycle, sortOrderCycle.length); + } + + /** + * + * @return the comparator used. + * @see #setComparator(Comparator) + */ + public Comparator getComparator() { + return comparator; + } + + /** + * Sets the comparator to use for sorting.

+ * + * Note: as of post-1.0 the property is propagated to the SortController, + * if available. + * Whether or not a change triggers a re-sort is up to either the concrete controller + * implementation (the default doesn't) or client code. This behaviour is + * different from old SwingX style sorting. + * + * @param comparator the comparator to use. + */ + public void setComparator(Comparator comparator) { + Comparator old = getComparator(); + this.comparator = comparator; + updateSortAfterComparatorChange(); + firePropertyChange("comparator", old, getComparator()); + } + + /** + * Updates the SortController's comparator, if available. Does nothing otherwise. + * + */ + protected void updateSortAfterComparatorChange() { + if (getControlsSorterProperties()) { + getSortController().setComparator(0, getComparator()); + } + } + +//------------------------- sort: do sort/filter + + /** + * Sets the filter to the sorter, if available and of type SortController. + * Does nothing otherwise. + *

+ * + * @param filter the filter used to determine what entries should be + * included + */ + @SuppressWarnings("unchecked") + public void setRowFilter(RowFilter filter) { + if (hasSortController()) { + // all fine, because R is a ListModel (R extends ListModel) + SortController controller = (SortController) getSortController(); + controller.setRowFilter(filter); + } + } + + /** + * Returns the filter of the sorter, if available and of type SortController. + * Returns null otherwise.

+ * + * PENDING JW: generics? had to remove return type from getSortController to + * make this compilable, so probably wrong. + * + * @return the filter used in the sorter. + */ + @SuppressWarnings("unchecked") + public RowFilter getRowFilter() { + return hasSortController() ? getSortController().getRowFilter() : null; + } + + /** + * Resets sorting of all columns. + * Delegates to the SortController if available, or does nothing if not.

+ * + * PENDING JW: method name - consistent in SortController and here. + * + */ + public void resetSortOrder() { + if (hasSortController()) + getSortController().resetSortOrders(); + } + + /** + * + * Toggles the sort order of the list. + * Delegates to the SortController if available, or does nothing if not.

+ * + *

+ * The exact behaviour is defined by the SortController's toggleSortOrder + * implementation. Typically a unsorted list is sorted in ascending order, + * a sorted list's order is reversed. + *

+ * + * + */ + public void toggleSortOrder() { + if (hasSortController()) + getSortController().toggleSortOrder(0); + } + + /** + * Sorts the list using SortOrder. + * Delegates to the SortController if available, or does nothing if not.

+ * + * @param sortOrder the sort order to use. + * + */ + public void setSortOrder(SortOrder sortOrder) { + if (hasSortController()) + getSortController().setSortOrder(0, sortOrder); + } + + + /** + * Returns the SortOrder. + * Delegates to the SortController if available, or returns SortOrder.UNSORTED if not.

+ * + * @return the current SortOrder + */ + public SortOrder getSortOrder() { + if (hasSortController()) + return getSortController().getSortOrder(0); + return SortOrder.UNSORTED; + } + + + /** + * Returns the currently active SortController. May be null if RowSorter + * is null or not of type SortController.

+ * + * PENDING JW: swaying about hiding or not - currently the only way to + * make the view not configure a RowSorter of type SortController is to + * let this return null. + * + * @return the currently active SortController may be null + */ + @SuppressWarnings("unchecked") + protected SortController getSortController() { + if (hasSortController()) { + // JW: the RowSorter is always of type + // so the unchecked cast is safe + return (SortController) getRowSorter(); + } + return null; + } + + /** + * Returns a boolean indicating whether the table has a SortController. + * If true, the call to getSortController is guaranteed to return a not-null + * value. + * + * @return a boolean indicating whether the table has a SortController. + * + * @see #getSortController() + */ + protected boolean hasSortController() { + return getRowSorter() instanceof SortController; + } + + /** + * Returns a boolean indicating whether the table configures the sorter's + * properties. If true, guaranteed that table's and the columns' sort related + * properties are propagated to the sorter. If false, guaranteed to not + * touch the sorter's configuration.

+ * + * This implementation returns true if the sorter is of type SortController. + * + * Note: the synchronization is unidirection from the table to the sorter. + * Changing the sorter under the table's feet might lead to undefined + * behaviour. + * + * @return a boolean indicating whether the table configurers the sorter's + * properties. + */ + protected boolean getControlsSorterProperties() { + return hasSortController() && getAutoCreateRowSorter(); + } + + // ---------------------------- filters + + /** + * Returns the element at the given index. The index is in view coordinates + * which might differ from model coordinates if filtering is enabled and + * filters/sorters are active. + * + * @param viewIndex the index in view coordinates + * @return the element at the index + * @throws IndexOutOfBoundsException if viewIndex < 0 or viewIndex >= + * getElementCount() + */ + public Object getElementAt(int viewIndex) { + return getModel().getElementAt(convertIndexToModel(viewIndex)); + } + + /** + * Returns the value for the smallest selected cell index; + * the selected value when only a single item is selected in the + * list. When multiple items are selected, it is simply the value for the + * smallest selected index. Returns {@code null} if there is no selection. + *

+ * This is a convenience method that simply returns the model value for + * {@code getMinSelectionIndex}, taking into account sorting and filtering. + * + * @return the first selected value + * @see #getMinSelectionIndex + * @see #getModel + * @see #addListSelectionListener + */ + @Override + public Object getSelectedValue() { + int i = getSelectedIndex(); + return (i == -1) ? null : getElementAt(i); + } + + /** + * Selects the specified object from the list, taking into account + * sorting and filtering. + * + * @param anObject the object to select + * @param shouldScroll {@code true} if the list should scroll to display + * the selected object, if one exists; otherwise {@code false} + */ + @Override + public void setSelectedValue(Object anObject,boolean shouldScroll) { + // Note: this method is a copy of JList.setSelectedValue, + // including comments. It simply usues getElementCount() and getElementAt() + // instead of the model. + if(anObject == null) + setSelectedIndex(-1); + else if(!anObject.equals(getSelectedValue())) { + int i,c; + for(i=0,c=getElementCount();i= getElementCount() + */ + public int convertIndexToModel(int viewIndex) { + return getRowSorter() != null ? + getRowSorter().convertRowIndexToModel(viewIndex):viewIndex; + } + + /** + * Convert index from model coordinates to view coordinates accounting + * for the presence of sorters and filters. + * + * @param modelIndex index in model coordinates + * @return index in view coordinates if the model index maps to a view coordinate + * or -1 if not contained in the view. + * + */ + public int convertIndexToView(int modelIndex) { + return getRowSorter() != null + ? getRowSorter().convertRowIndexToView(modelIndex) : modelIndex; + } + + /** + * {@inheritDoc}

+ * + * Sets the underlying data model. Note that if isFilterEnabled you must + * call getWrappedModel to access the model given here. In this case + * getModel returns a wrapper around the data! + * + * @param model the data model for this list. + * + */ + @Override + public void setModel(ListModel model) { + super.setModel(model); + if (getAutoCreateRowSorter()) { + setRowSorter(createDefaultRowSorter()); + } + } + + + // ---------------------------- uniform data model + + /** + * @return the unconfigured ComponentAdapter. + */ + protected ComponentAdapter getComponentAdapter() { + if (dataAdapter == null) { + dataAdapter = new ListAdapter(this); + } + return dataAdapter; + } + + /** + * Convenience to access a configured ComponentAdapter. + * Note: the column index of the configured adapter is always 0. + * + * @param index the row index in view coordinates, must be valid. + * @return the configured ComponentAdapter. + */ + protected ComponentAdapter getComponentAdapter(int index) { + ComponentAdapter adapter = getComponentAdapter(); + adapter.column = 0; + adapter.row = index; + return adapter; + } + + /** + * A component adapter targeted at a JXList. + */ + protected static class ListAdapter extends ComponentAdapter { + private final JXList list; + + /** + * Constructs a ListAdapter for the specified target + * JXList. + * + * @param component the target list. + */ + public ListAdapter(JXList component) { + super(component); + list = component; + } + + /** + * Typesafe accessor for the target component. + * + * @return the target component as a {@link JXList} + */ + public JXList getList() { + return list; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean hasFocus() { + /** TODO: Think through printing implications */ + return list.isFocusOwner() && (row == list.getLeadSelectionIndex()); + } + + /** + * {@inheritDoc} + */ + @Override + public int getRowCount() { + return list.getModel().getSize(); + } + + /** + * {@inheritDoc} + */ + @Override + public Object getValueAt(int row, int column) { + return list.getModel().getElementAt(row); + } + + /** + * {@inheritDoc} + * This is implemented to query the table's StringValueRegistry for an appropriate + * StringValue and use that for getting the string representation. + */ + @Override + public String getStringAt(int row, int column) { + StringValue sv = list.getStringValueRegistry().getStringValue(row, column); + return sv.getString(getValueAt(row, column)); + } + + /** + * {@inheritDoc} + */ + @Override + public Rectangle getCellBounds() { + return list.getCellBounds(row, row); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isCellEditable(int row, int column) { + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isEditable() { + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isSelected() { + /** TODO: Think through printing implications */ + return list.isSelectedIndex(row); + } + + /** + * {@inheritDoc} + */ + @Override + public int convertRowIndexToView(int rowModelIndex) { + return list.convertIndexToView(rowModelIndex); + } + + /** + * {@inheritDoc} + */ + @Override + public int convertRowIndexToModel(int rowViewIndex) { + return list.convertIndexToModel(rowViewIndex); + } + } + + // ------------------------------ renderers + + + + /** + * Sets the Highlighters to the table, replacing any old settings. + * None of the given Highlighters must be null.

+ * + * This is a bound property.

+ * + * Note: as of version #1.257 the null constraint is enforced strictly. To remove + * all highlighters use this method without param. + * + * @param highlighters zero or more not null highlighters to use for renderer decoration. + * @throws NullPointerException if array is null or array contains null values. + * + * @see #getHighlighters() + * @see #addHighlighter(Highlighter) + * @see #removeHighlighter(Highlighter) + * + */ + public void setHighlighters(Highlighter... highlighters) { + Highlighter[] old = getHighlighters(); + getCompoundHighlighter().setHighlighters(highlighters); + firePropertyChange("highlighters", old, getHighlighters()); + } + + /** + * Returns the Highlighters used by this table. + * Maybe empty, but guarantees to be never null. + * + * @return the Highlighters used by this table, guaranteed to never null. + * @see #setHighlighters(Highlighter[]) + */ + public Highlighter[] getHighlighters() { + return getCompoundHighlighter().getHighlighters(); + } + /** + * Appends a Highlighter to the end of the list of used + * Highlighters. The argument must not be null. + *

+ * + * @param highlighter the Highlighter to add, must not be null. + * @throws NullPointerException if Highlighter is null. + * + * @see #removeHighlighter(Highlighter) + * @see #setHighlighters(Highlighter[]) + */ + public void addHighlighter(Highlighter highlighter) { + Highlighter[] old = getHighlighters(); + getCompoundHighlighter().addHighlighter(highlighter); + firePropertyChange("highlighters", old, getHighlighters()); + } + + /** + * Removes the given Highlighter.

+ * + * Does nothing if the Highlighter is not contained. + * + * @param highlighter the Highlighter to remove. + * @see #addHighlighter(Highlighter) + * @see #setHighlighters(Highlighter...) + */ + public void removeHighlighter(Highlighter highlighter) { + Highlighter[] old = getHighlighters(); + getCompoundHighlighter().removeHighlighter(highlighter); + firePropertyChange("highlighters", old, getHighlighters()); + } + + /** + * Returns the CompoundHighlighter assigned to the table, null if none. + * PENDING: open up for subclasses again?. + * + * @return the CompoundHighlighter assigned to the table. + */ + protected CompoundHighlighter getCompoundHighlighter() { + if (compoundHighlighter == null) { + compoundHighlighter = new CompoundHighlighter(); + compoundHighlighter.addChangeListener(getHighlighterChangeListener()); + } + return compoundHighlighter; + } + + /** + * Returns the ChangeListener to use with highlighters. Lazily + * creates the listener. + * + * @return the ChangeListener for observing changes of highlighters, + * guaranteed to be not-null + */ + protected ChangeListener getHighlighterChangeListener() { + if (highlighterChangeListener == null) { + highlighterChangeListener = createHighlighterChangeListener(); + } + return highlighterChangeListener; + } + + /** + * Creates and returns the ChangeListener observing Highlighters. + *

+ * Here: repaints the table on receiving a stateChanged. + * + * @return the ChangeListener defining the reaction to changes of + * highlighters. + */ + protected ChangeListener createHighlighterChangeListener() { + return new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + repaint(); + } + }; + } + + /** + * Returns the StringValueRegistry which defines the string representation for + * each cells. This is strictly for internal use by the table, which has the + * responsibility to keep in synch with registered renderers.

+ * + * Currently exposed for testing reasons, client code is recommended to not use nor override. + * + * @return the current string value registry + */ + protected StringValueRegistry getStringValueRegistry() { + if (stringValueRegistry == null) { + stringValueRegistry = createDefaultStringValueRegistry(); + } + return stringValueRegistry; + } + + /** + * Creates and returns the default registry for StringValues.

+ * + * @return the default registry for StringValues. + */ + protected StringValueRegistry createDefaultStringValueRegistry() { + return new StringValueRegistry(); + } + + + + /** + * Returns the string representation of the cell value at the given position. + * + * @param row the row index of the cell in view coordinates + * @return the string representation of the cell value as it will appear in the + * table. + */ + public String getStringAt(int row) { + // changed implementation to use StringValueRegistry + StringValue stringValue = getStringValueRegistry().getStringValue( + convertIndexToModel(row), 0); + return stringValue.getString(getElementAt(row)); + } + + private DelegatingRenderer getDelegatingRenderer() { + if (delegatingRenderer == null) { + // only called once... to get hold of the default? + delegatingRenderer = new DelegatingRenderer(); + } + return delegatingRenderer; + } + + /** + * Creates and returns the default cell renderer to use. Subclasses + * may override to use a different type. Here: returns a DefaultListRenderer. + * + * @return the default cell renderer to use with this list. + */ + protected ListCellRenderer createDefaultCellRenderer() { + return new DefaultListRenderer(); + } + + /** + * {@inheritDoc}

+ * + * Overridden to return the delegating renderer which is wrapped around the + * original to support highlighting. The returned renderer is of type + * DelegatingRenderer and guaranteed to not-null

+ * + * @see #setCellRenderer(ListCellRenderer) + * @see DelegatingRenderer + */ + @Override + public ListCellRenderer getCellRenderer() { + return getDelegatingRenderer(); + } + + /** + * Returns the renderer installed by client code or the default if none has + * been set. + * + * @return the wrapped renderer. + * @see #setCellRenderer(ListCellRenderer) + */ + public ListCellRenderer getWrappedCellRenderer() { + return getDelegatingRenderer().getDelegateRenderer(); + } + + /** + * {@inheritDoc}

+ * + * Overridden to wrap the given renderer in a DelegatingRenderer to support + * highlighting.

+ * + * Note: the wrapping implies that the renderer returned from the getCellRenderer + * is not the renderer as given here, but the wrapper. To access the original, + * use getWrappedCellRenderer. + * + * @see #getWrappedCellRenderer() + * @see #getCellRenderer() + * + */ + @Override + public void setCellRenderer(ListCellRenderer renderer) { + // PENDING JW: super fires for very first setting + // as defaults are automagically set (by delegatingRenderer + // using this list's factory method) there is no + // easy way to _not_ force, this isn't working + // but then ... it's only the very first time around. + // Safe enough to wait for complaints ;-) + boolean forceFire = (delegatingRenderer != null) ; + // JW: Pending - probably fires propertyChangeEvent with wrong newValue? + // how about fixedCellWidths? + // need to test!! + getDelegatingRenderer().setDelegateRenderer(renderer); + getStringValueRegistry().setStringValue( + renderer instanceof StringValue ? (StringValue) renderer: null, + 0); + super.setCellRenderer(delegatingRenderer); + if (forceFire) + firePropertyChange("cellRenderer", null, delegatingRenderer); + } + + /** + * A decorator for the original ListCellRenderer. Needed to hook highlighters + * after messaging the delegate.

+ * + * PENDING JW: formally implement UIDependent? + */ + public class DelegatingRenderer implements ListCellRenderer, RolloverRenderer { + /** the delegate. */ + private ListCellRenderer delegateRenderer; + + /** + * Instantiates a DelegatingRenderer with list's default renderer as delegate. + */ + public DelegatingRenderer() { + this(null); + } + + /** + * Instantiates a DelegatingRenderer with the given delegate. If the + * delegate is null, the default is created via the list's factory method. + * + * @param delegate the delegate to use, if null the list's default is + * created and used. + */ + public DelegatingRenderer(ListCellRenderer delegate) { + setDelegateRenderer(delegate); + } + + /** + * Sets the delegate. If the + * delegate is null, the default is created via the list's factory method. + * + * @param delegate the delegate to use, if null the list's default is + * created and used. + */ + public void setDelegateRenderer(ListCellRenderer delegate) { + if (delegate == null) { + delegate = createDefaultCellRenderer(); + } + delegateRenderer = delegate; + } + + /** + * Returns the delegate. + * + * @return the delegate renderer used by this renderer, guaranteed to + * not-null. + */ + public ListCellRenderer getDelegateRenderer() { + return delegateRenderer; + } + + /** + * Updates the ui of the delegate. + */ + public void updateUI() { + updateRendererUI(delegateRenderer); + } + + /** + * + * @param renderer the renderer to update the ui of. + */ + private void updateRendererUI(ListCellRenderer renderer) { + if (renderer == null) return; + Component comp = null; + if (renderer instanceof AbstractRenderer) { + comp = ((AbstractRenderer) renderer).getComponentProvider().getRendererComponent(null); + } else if (renderer instanceof Component) { + comp = (Component) renderer; + } else { + try { + comp = renderer.getListCellRendererComponent( + JXList.this, null, -1, false, false); + } catch (Exception e) { + // nothing to do - renderer barked on off-range row + } + } + if (comp != null) { + SwingUtilities.updateComponentTreeUI(comp); + } + + } + + // --------- implement ListCellRenderer + /** + * {@inheritDoc}

+ * + * Overridden to apply the highlighters, if any, after calling the delegate. + * The decorators are not applied if the row is invalid. + */ + @Override + public Component getListCellRendererComponent(JList list, Object value, + int index, boolean isSelected, boolean cellHasFocus) { + Component comp = delegateRenderer.getListCellRendererComponent(list, value, index, + isSelected, cellHasFocus); + if ((compoundHighlighter != null) && (index >= 0) && (index < getElementCount())) { + comp = compoundHighlighter.highlight(comp, getComponentAdapter(index)); + } + return comp; + } + + + // implement RolloverRenderer + + /** + * {@inheritDoc} + * + */ + @Override + public boolean isEnabled() { + return (delegateRenderer instanceof RolloverRenderer) && + ((RolloverRenderer) delegateRenderer).isEnabled(); + } + + /** + * {@inheritDoc} + */ + @Override + public void doClick() { + if (isEnabled()) { + ((RolloverRenderer) delegateRenderer).doClick(); + } + } + + } + + /** + * Invalidates cell size caching in the ui delegate. May do nothing if there's no + * safe (i.e. without reflection) way to message the delegate.

+ * + * This implementation calls the corresponding method on BasicXListUI if available, + * does nothing otherwise. + * + */ + public void invalidateCellSizeCache() { + if (getUI() instanceof BasicXListUI) { + ((BasicXListUI) getUI()).invalidateCellSizeCache(); + } + } + + // --------------------------- updateUI + + + /** + * {@inheritDoc}

+ * + * Overridden to update renderer and Highlighters. + */ + @Override + public void updateUI() { + // PENDING JW: temporary during dev to quickly switch between default and custom ui + if (getUIClassID() == super.getUIClassID()) { + super.updateUI(); + } else { + setUI((ListUI) LookAndFeelAddons.getUI(this, ListUI.class)); + } + updateRendererUI(); + updateHighlighterUI(); + } + + @Override + public String getUIClassID() { + // PENDING JW: temporary during dev to quickly switch between default and custom ui +// return super.getUIClassID(); + return uiClassID; + } + + private void updateRendererUI() { + if (delegatingRenderer != null) { + delegatingRenderer.updateUI(); + } else { + ListCellRenderer renderer = getCellRenderer(); + if (renderer instanceof Component) { + SwingUtilities.updateComponentTreeUI((Component) renderer); + } + } + } + + /** + * Updates highlighter after updateUI changes. + * + * @see org.jdesktop.swingx.plaf.UIDependent + */ + protected void updateHighlighterUI() { + if (compoundHighlighter == null) return; + compoundHighlighter.updateUI(); + } + +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/JXLoginPane.java b/src/main/java/org/jdesktop/swingx/JXLoginPane.java new file mode 100644 index 0000000000..34ca7b0187 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXLoginPane.java @@ -0,0 +1,1737 @@ +/* + * $Id: JXLoginPane.java 4147 2012-02-01 17:13:24Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.action.AbstractActionExt; +import org.jdesktop.swingx.auth.*; +import org.jdesktop.swingx.autocomplete.AutoCompleteDecorator; +import org.jdesktop.swingx.painter.MattePainter; +import org.jdesktop.swingx.plaf.LoginPaneAddon; +import org.jdesktop.swingx.plaf.LoginPaneUI; +import org.jdesktop.swingx.plaf.LookAndFeelAddons; +import org.jdesktop.swingx.plaf.UIManagerExt; +import org.jdesktop.swingx.plaf.basic.CapsLockSupport; +import org.jdesktop.swingx.util.WindowUtils; + +import javax.swing.*; +import javax.swing.border.EmptyBorder; +import javax.swing.plaf.basic.BasicHTML; +import javax.swing.text.View; +import java.awt.*; +import java.awt.event.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + *

JXLoginPane is a specialized JPanel that implements a Login dialog with + * support for saving passwords supplied for future use in a secure + * manner. LoginService is invoked to perform authentication + * and optional PasswordStore can be provided to store the user + * login information.

+ * + *

In order to perform the authentication, JXLoginPane + * calls the authenticate method of the LoginService + * . In order to perform the persistence of the password, + * JXLoginPane calls the put method of the + * PasswordStore object that is supplied. If + * the PasswordStore is null, then the password + * is not saved. Similarly, if a PasswordStore is + * supplied and the password is null, then the PasswordStore + * will be queried for the password using the get method. + * + * Example: + *

+ *         final JXLoginPane panel = new JXLoginPane(new LoginService() {
+ *                      public boolean authenticate(String name, char[] password,
+ *                                      String server) throws Exception {
+ *                              // perform authentication and return true on success.
+ *                              return false;
+ *                      }});
+ *      final JFrame frame = JXLoginPane.showLoginFrame(panel);
+ * 
+ * + * @author Bino George + * @author Shai Almog + * @author rbair + * @author Karl Schaefer + * @author rah003 + * @author Jonathan Giles + */ +@JavaBean +public class JXLoginPane extends JXPanel { + + /** + * The Logger + */ + private static final Logger LOG = Logger.getLogger(JXLoginPane.class.getName()); + /** + * Comment for serialVersionUID + */ + private static final long serialVersionUID = 3544949969896288564L; + /** + * UI Class ID + */ + public final static String uiClassID = "LoginPaneUI"; + /** + * Action key for an Action in the ActionMap that initiates the Login + * procedure + */ + public static final String LOGIN_ACTION_COMMAND = "login"; + /** + * Action key for an Action in the ActionMap that cancels the Login + * procedure + */ + public static final String CANCEL_LOGIN_ACTION_COMMAND = "cancel-login"; + /** + * The JXLoginPane can attempt to save certain user information such as + * the username, password, or both to their respective stores. + * This type specifies what type of save should be performed. + */ + public static enum SaveMode {NONE, USER_NAME, PASSWORD, BOTH} + /** + * Returns the status of the login process + */ + public enum Status {NOT_STARTED, IN_PROGRESS, FAILED, CANCELLED, SUCCEEDED} + /** + * Used as a prefix when pulling data out of UIManager for i18n + */ + private static String CLASS_NAME = JXLoginPane.class.getSimpleName(); + + /** + * The current login status for this panel + */ + private Status status = Status.NOT_STARTED; + /** + * An optional banner at the top of the panel + */ + private JXImagePanel banner; + /** + * Text that should appear on the banner + */ + private String bannerText; + /** + * Custom label allowing the developer to display some message to the user + */ + private JLabel messageLabel; + /** + * Shows an error message such as "user name or password incorrect" or + * "could not contact server" or something like that if something + * goes wrong + */ + private JXLabel errorMessageLabel; + /** + * A Panel containing all of the input fields, check boxes, etc necessary + * for the user to do their job. The items on this panel change whenever + * the SaveMode changes, so this panel must be recreated at runtime if the + * SaveMode changes. Thus, I must maintain this reference so I can remove + * this panel from the content panel at runtime. + */ + private JXPanel loginPanel; + /** + * The panel on which the input fields, messageLabel, and errorMessageLabel + * are placed. While the login thread is running, this panel is removed + * from the dialog and replaced by the progressPanel + */ + private JXPanel contentPanel; + /** + * This is the area in which the name field is placed. That way it can toggle on the fly + * between text field and a combo box depending on the situation, and have a simple + * way to get the user name + */ + private NameComponent namePanel; + /** + * The password field presented allowing the user to enter their password + */ + private JPasswordField passwordField; + /** + * A combo box presenting the user with a list of servers to which they + * may log in. This is an optional feature, which is only enabled if + * the List of servers supplied to the JXLoginPane has a length greater + * than 1. + */ + private JComboBox serverCombo; + /** + * Check box presented if a PasswordStore is used, allowing the user to decide whether to + * save their password + */ + private JCheckBox saveCB; + + /** + * Label displayed whenever caps lock is on. + */ + private JLabel capsOn; + /** + * A special panel that displays a progress bar and cancel button, and + * which notify the user of the login process, and allow them to cancel + * that process. + */ + private JXPanel progressPanel; + /** + * A JLabel on the progressPanel that is used for informing the user + * of the status of the login procedure (logging in..., canceling login...) + */ + private JLabel progressMessageLabel; + /** + * The LoginService to use. This must be specified for the login dialog to operate. + * If no LoginService is defined, a default login service is used that simply + * allows all users access. This is useful for demos or prototypes where a proper login + * server is not available. + */ + private LoginService loginService; + /** + * Optional: a PasswordStore to use for storing and retrieving passwords for a specific + * user. + */ + private PasswordStore passwordStore; + /** + * Optional: a UserNameStore to use for storing user names and retrieving them + */ + private UserNameStore userNameStore; + /** + * A list of servers where each server is represented by a String. If the + * list of Servers is greater than 1, then a combo box will be presented to + * the user to choose from. If any servers are specified, the selected one + * (or the only one if servers.size() == 1) will be passed to the LoginService + */ + private List servers; + /** + * Whether to save password or username or both. + */ + private SaveMode saveMode; + /** + * Tracks the cursor at the time that authentication was started, and restores to that + * cursor after authentication ends, or is canceled; + */ + private Cursor oldCursor; + + private boolean namePanelEnabled = true; + + /** + * The default login listener used by this panel. + */ + private LoginListener defaultLoginListener; + + /** + * Login/cancel control pane; + */ + private JXBtnPanel buttonPanel; + + /** + * Card pane holding user/pwd fields view and the progress view. + */ + private JPanel contentCardPane; + private boolean isErrorMessageSet; + + /** + * Creates a default JXLoginPane instance + */ + static { + LookAndFeelAddons.contribute(new LoginPaneAddon()); + } + + /** + * Populates UIDefaults with the localizable Strings we will use + * in the Login panel. + */ + private void reinitLocales(Locale l) { + // PENDING: JW - use the locale given as parameter + // as this probably (?) should be called before super.setLocale + setBannerText(UIManagerExt.getString(CLASS_NAME + ".bannerString", getLocale())); + banner.setImage(createLoginBanner()); + if (!isErrorMessageSet) { + errorMessageLabel.setText(UIManager.getString(CLASS_NAME + ".errorMessage", getLocale())); + } + progressMessageLabel.setText(UIManagerExt.getString(CLASS_NAME + ".pleaseWait", getLocale())); + recreateLoginPanel(); + Window w = SwingUtilities.getWindowAncestor(this); + if (w instanceof JXLoginFrame) { + JXLoginFrame f = (JXLoginFrame) w; + f.setTitle(UIManagerExt.getString(CLASS_NAME + ".titleString", getLocale())); + if (buttonPanel != null) { + buttonPanel.getOk().setText(UIManagerExt.getString(CLASS_NAME + ".loginString", getLocale())); + buttonPanel.getCancel().setText(UIManagerExt.getString(CLASS_NAME + ".cancelString", getLocale())); + } + } + JLabel lbl = (JLabel) passwordField.getClientProperty("labeledBy"); + if (lbl != null) { + lbl.setText(UIManagerExt.getString(CLASS_NAME + ".passwordString", getLocale())); + } + lbl = (JLabel) namePanel.getComponent().getClientProperty("labeledBy"); + if (lbl != null) { + lbl.setText(UIManagerExt.getString(CLASS_NAME + ".nameString", getLocale())); + } + if (serverCombo != null) { + lbl = (JLabel) serverCombo.getClientProperty("labeledBy"); + if (lbl != null) { + lbl.setText(UIManagerExt.getString(CLASS_NAME + ".serverString", getLocale())); + } + } + saveCB.setText(UIManagerExt.getString(CLASS_NAME + ".rememberPasswordString", getLocale())); + // by default, caps is initialized in off state - i.e. without warning. Setting to + // whitespace preserves formatting of the panel. + capsOn.setText(isCapsLockOn() ? UIManagerExt.getString(CLASS_NAME + ".capsOnWarning", getLocale()) : " "); + + getActionMap().get(LOGIN_ACTION_COMMAND).putValue(Action.NAME, UIManagerExt.getString(CLASS_NAME + ".loginString", getLocale())); + getActionMap().get(CANCEL_LOGIN_ACTION_COMMAND).putValue(Action.NAME, UIManagerExt.getString(CLASS_NAME + ".cancelString", getLocale())); + + } + + //--------------------------------------------------------- Constructors + /** + * Create a {@code JXLoginPane} that always accepts the user, never stores + * passwords or user ids, and has no target servers. + *

+ * This constructor should NOT be used in a real application. It is + * provided for compliance to the bean specification and for use with visual + * editors. + */ + public JXLoginPane() { + this(null); + } + + /** + * Create a {@code JXLoginPane} with the specified {@code LoginService} + * that does not store user ids or passwords and has no target servers. + * + * @param service + * the {@code LoginService} to use for logging in + */ + public JXLoginPane(LoginService service) { + this(service, null, null); + } + + /** + * Create a {@code JXLoginPane} with the specified {@code LoginService}, + * {@code PasswordStore}, and {@code UserNameStore}, but without a server + * list. + *

+ * If you do not want to store passwords or user ids, those parameters can + * be {@code null}. {@code SaveMode} is autoconfigured from passed in store + * parameters. + * + * @param service + * the {@code LoginService} to use for logging in + * @param passwordStore + * the {@code PasswordStore} to use for storing password + * information + * @param userStore + * the {@code UserNameStore} to use for storing user information + */ + public JXLoginPane(LoginService service, PasswordStore passwordStore, UserNameStore userStore) { + this(service, passwordStore, userStore, null); + } + + /** + * Create a {@code JXLoginPane} with the specified {@code LoginService}, + * {@code PasswordStore}, {@code UserNameStore}, and server list. + *

+ * If you do not want to store passwords or user ids, those parameters can + * be {@code null}. {@code SaveMode} is autoconfigured from passed in store + * parameters. + *

+ * Setting the server list to {@code null} will unset all of the servers. + * The server list is guaranteed to be non-{@code null}. + * + * @param service + * the {@code LoginService} to use for logging in + * @param passwordStore + * the {@code PasswordStore} to use for storing password + * information + * @param userStore + * the {@code UserNameStore} to use for storing user information + * @param servers + * a list of servers to authenticate against + */ + public JXLoginPane(LoginService service, PasswordStore passwordStore, UserNameStore userStore, List servers) { + setLoginService(service); + setPasswordStore(passwordStore); + setUserNameStore(userStore); + setServers(servers); + + + //create the login and cancel actions, and add them to the action map + getActionMap().put(LOGIN_ACTION_COMMAND, createLoginAction()); + getActionMap().put(CANCEL_LOGIN_ACTION_COMMAND, createCancelAction()); + + //initialize the save mode + if (passwordStore != null && userStore != null) { + saveMode = SaveMode.BOTH; + } else if (passwordStore != null) { + saveMode = SaveMode.PASSWORD; + } else if (userStore != null) { + saveMode = SaveMode.USER_NAME; + } else { + saveMode = SaveMode.NONE; + } + + // #732 set all internal components opacity to false in order to allow top level (frame's content pane) background painter to have any effect. + setOpaque(false); + CapsLockSupport.getInstance().addPropertyChangeListener("capsLockEnabled", new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (capsOn != null) { + if (Boolean.TRUE.equals(evt.getNewValue())) { + capsOn.setText(UIManagerExt.getString(CLASS_NAME + ".capsOnWarning", getLocale())); + } else { + capsOn.setText(" "); + } + } + } + }); + initComponents(); + } + + /** + * Gets current state of the caps lock as seen by the login panel. The state seen by the login + * panel and therefore returned by this method can be delayed in comparison to the real caps + * lock state and displayed by the keyboard light. This is usually the case when component or + * its text fields are not focused. + * + * @return True when caps lock is on, false otherwise. Returns always false when + * isCapsLockDetectionSupported() returns false. + */ + public boolean isCapsLockOn() { + return CapsLockSupport.getInstance().isCapsLockEnabled(); + } + + //------------------------------------------------------------- UI Logic + + /** + * {@inheritDoc} + */ + @Override + public LoginPaneUI getUI() { + return (LoginPaneUI) super.getUI(); + } + + /** + * Sets the look and feel (L&F) object that renders this component. + * + * @param ui the LoginPaneUI L&F object + * @see javax.swing.UIDefaults#getUI + */ + public void setUI(LoginPaneUI ui) { + // initialized here due to implicit updateUI call from JPanel + if (banner == null) { + banner = new JXImagePanel(); + } + if (errorMessageLabel == null) { + errorMessageLabel = new JXLabel(UIManagerExt.getString(CLASS_NAME + ".errorMessage", getLocale())); + } + super.setUI(ui); + banner.setImage(createLoginBanner()); + } + + /** + * Notification from the UIManager that the L&F has changed. + * Replaces the current UI object with the latest version from the + * UIManager. + * + * @see JComponent#updateUI + */ + @Override + public void updateUI() { + setUI((LoginPaneUI) LookAndFeelAddons.getUI(this, LoginPaneUI.class)); + } + + /** + * Returns the name of the L&F class that renders this component. + * + * @return the string {@link #uiClassID} + * @see JComponent#getUIClassID + * @see javax.swing.UIDefaults#getUI + */ + @Override + public String getUIClassID() { + return uiClassID; + } + + /** + * Recreates the login panel, and replaces the current one with the new one + */ + protected void recreateLoginPanel() { + JXPanel old = loginPanel; + loginPanel = createLoginPanel(); + loginPanel.setBorder(BorderFactory.createEmptyBorder(0, 36, 7, 11)); + contentPanel.remove(old); + contentPanel.add(loginPanel, 1); + } + + /** + * Creates and returns a new LoginPanel, based on the SaveMode state of + * the login panel. Whenever the SaveMode changes, the panel is recreated. + * I do this rather than hiding/showing components, due to a cleaner + * implementation (no invisible components, components are not sharing + * locations in the LayoutManager, etc). + */ + private JXPanel createLoginPanel() { + JXPanel loginPanel = new JXPanel(); + + JPasswordField oldPwd = passwordField; + //create the password component + passwordField = new JPasswordField("", 15); + JLabel passwordLabel = new JLabel(UIManagerExt.getString(CLASS_NAME + ".passwordString", getLocale())); + passwordLabel.setLabelFor(passwordField); + if (oldPwd != null) { + passwordField.setText(new String(oldPwd.getPassword())); + } + + NameComponent oldPanel = namePanel; + //create the NameComponent + if (saveMode == SaveMode.NONE) { + namePanel = new SimpleNamePanel(); + } else { + namePanel = new ComboNamePanel(); + } + if (oldPanel != null) { + // need to reset here otherwise value will get lost during LAF change as panel gets recreated. + namePanel.setUserName(oldPanel.getUserName()); + namePanel.setEnabled(oldPanel.isEnabled()); + namePanel.setEditable(oldPanel.isEditable()); + } else { + namePanel.setEnabled(namePanelEnabled); + namePanel.setEditable(namePanelEnabled); + } + JLabel nameLabel = new JLabel(UIManagerExt.getString(CLASS_NAME + ".nameString", getLocale())); + nameLabel.setLabelFor(namePanel.getComponent()); + + //create the server combo box if necessary + JLabel serverLabel = new JLabel(UIManagerExt.getString(CLASS_NAME + ".serverString", getLocale())); + if (servers.size() > 1) { + serverCombo = new JComboBox(servers.toArray()); + serverLabel.setLabelFor(serverCombo); + } else { + serverCombo = null; + } + + //create the save check box. By default, it is not selected + saveCB = new JCheckBox(UIManagerExt.getString(CLASS_NAME + ".rememberPasswordString", getLocale())); + saveCB.setIconTextGap(10); + //TODO should get this from preferences!!! And, it should be based on the user + saveCB.setSelected(false); + //determine whether to show/hide the save check box based on the SaveMode + saveCB.setVisible(saveMode == SaveMode.PASSWORD || saveMode == SaveMode.BOTH); + saveCB.setOpaque(false); + + capsOn = new JLabel(); + capsOn.setText(isCapsLockOn() ? UIManagerExt.getString(CLASS_NAME + ".capsOnWarning", getLocale()) : " "); + + int lShift = 3;// lShift is used to align all other components with the checkbox + GridLayout grid = new GridLayout(2,1); + grid.setVgap(5); + JPanel fields = new JPanel(grid); + fields.setOpaque(false); + fields.add(namePanel.getComponent()); + fields.add(passwordField); + + loginPanel.setLayout(new GridBagLayout()); + GridBagConstraints gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.anchor = GridBagConstraints.LINE_START; + gridBagConstraints.insets = new Insets(4, lShift, 5, 11); + loginPanel.add(nameLabel, gridBagConstraints); + + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 0; + gridBagConstraints.gridwidth = 1; + gridBagConstraints.gridheight = 2; + gridBagConstraints.anchor = GridBagConstraints.LINE_START; + gridBagConstraints.fill = GridBagConstraints.BOTH; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new Insets(0, 0, 5, 0); + loginPanel.add(fields, gridBagConstraints); + + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 1; + gridBagConstraints.anchor = GridBagConstraints.LINE_START; + gridBagConstraints.insets = new Insets(5, lShift, 5, 11); + loginPanel.add(passwordLabel, gridBagConstraints); + + if (serverCombo != null) { + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 2; + gridBagConstraints.anchor = GridBagConstraints.LINE_START; + gridBagConstraints.insets = new Insets(0, lShift, 5, 11); + loginPanel.add(serverLabel, gridBagConstraints); + + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 2; + gridBagConstraints.gridwidth = 1; + gridBagConstraints.anchor = GridBagConstraints.LINE_START; + gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new Insets(0, 0, 5, 0); + loginPanel.add(serverCombo, gridBagConstraints); + + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 3; + gridBagConstraints.gridwidth = 2; + gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = GridBagConstraints.LINE_START; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new Insets(0, 0, 4, 0); + loginPanel.add(saveCB, gridBagConstraints); + + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 4; + gridBagConstraints.gridwidth = 2; + gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = GridBagConstraints.LINE_START; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new Insets(0, lShift, 0, 11); + loginPanel.add(capsOn, gridBagConstraints); + } else { + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 2; + gridBagConstraints.gridwidth = 2; + gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = GridBagConstraints.LINE_START; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new Insets(0, 0, 4, 0); + loginPanel.add(saveCB, gridBagConstraints); + + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 3; + gridBagConstraints.gridwidth = 2; + gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = GridBagConstraints.LINE_START; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new Insets(0, lShift, 0, 11); + loginPanel.add(capsOn, gridBagConstraints); + } + loginPanel.setOpaque(false); + return loginPanel; + } + + /** + * This method adds functionality to support bidi languages within this + * component + */ + @Override + public void setComponentOrientation(ComponentOrientation orient) { + // this if is used to avoid needless creations of the image + if(orient != super.getComponentOrientation()) { + super.setComponentOrientation(orient); + banner.setImage(createLoginBanner()); + progressPanel.applyComponentOrientation(orient); + } + } + + /** + * Create all of the UI components for the login panel + */ + private void initComponents() { + //create the default banner + banner.setImage(createLoginBanner()); + + //create the default label + messageLabel = new JLabel(" "); + messageLabel.setOpaque(false); + messageLabel.setFont(messageLabel.getFont().deriveFont(Font.BOLD)); + + //create the main components + loginPanel = createLoginPanel(); + + //create the message and hyperlink and hide them + errorMessageLabel.setIcon(UIManager.getIcon(CLASS_NAME + ".errorIcon", getLocale())); + errorMessageLabel.setVerticalTextPosition(SwingConstants.TOP); + errorMessageLabel.setLineWrap(true); + errorMessageLabel.setPaintBorderInsets(false); + errorMessageLabel.setBackgroundPainter(new MattePainter(UIManager.getColor(CLASS_NAME + ".errorBackground", getLocale()), true)); + errorMessageLabel.setMaxLineSpan(320); + errorMessageLabel.setVisible(false); + + //aggregate the optional message label, content, and error label into + //the contentPanel + contentPanel = new JXPanel(new LoginPaneLayout()); + contentPanel.setOpaque(false); + messageLabel.setBorder(BorderFactory.createEmptyBorder(12, 12, 7, 11)); + contentPanel.add(messageLabel); + loginPanel.setBorder(BorderFactory.createEmptyBorder(0, 36, 7, 11)); + contentPanel.add(loginPanel); + errorMessageLabel.setBorder(UIManager.getBorder(CLASS_NAME + ".errorBorder", getLocale())); + contentPanel.add(errorMessageLabel); + + //create the progress panel + progressPanel = new JXPanel(new GridBagLayout()); + progressPanel.setOpaque(false); + progressMessageLabel = new JLabel(UIManagerExt.getString(CLASS_NAME + ".pleaseWait", getLocale())); + progressMessageLabel.setFont(UIManager.getFont(CLASS_NAME +".pleaseWaitFont", getLocale())); + JProgressBar pb = new JProgressBar(); + pb.setIndeterminate(true); + JButton cancelButton = new JButton(getActionMap().get(CANCEL_LOGIN_ACTION_COMMAND)); + progressPanel.add(progressMessageLabel, new GridBagConstraints(0, 0, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(12, 12, 11, 11), 0, 0)); + progressPanel.add(pb, new GridBagConstraints(0, 1, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 24, 11, 7), 0, 0)); + progressPanel.add(cancelButton, new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 11, 11), 0, 0)); + + //layout the panel + setLayout(new BorderLayout()); + add(banner, BorderLayout.NORTH); + contentCardPane = new JPanel(new CardLayout()); + contentCardPane.setOpaque(false); + contentCardPane.add(contentPanel, "0"); + contentCardPane.add(progressPanel, "1"); + add(contentCardPane, BorderLayout.CENTER); + + } + + private final class LoginPaneLayout extends VerticalLayout implements LayoutManager { + @Override + public Dimension preferredLayoutSize(Container parent) { + Insets insets = parent.getInsets(); + Dimension pref = new Dimension(0, 0); + int gap = getGap(); + for (int i = 0, c = parent.getComponentCount(); i < c; i++) { + Component m = parent.getComponent(i); + if (m.isVisible()) { + Dimension componentPreferredSize = m.getPreferredSize(); + // swingx-917 - don't let jlabel to force width due to long text + if (m instanceof JLabel) { + View view = (View) ((JLabel)m).getClientProperty(BasicHTML.propertyKey); + if (view != null) { + view.setSize(pref.width, m.getHeight()); + // get fresh preferred size since we have forced new size on label + componentPreferredSize = m.getPreferredSize(); + } + } else { + pref.width = Math.max(pref.width, componentPreferredSize.width); + } + pref.height += componentPreferredSize.height + gap; + } + } + + pref.width += insets.left + insets.right; + pref.height += insets.top + insets.bottom; + + return pref; + } + } + + /** + * Create and return an image to use for the Banner. This may be overridden + * to return any image you like + */ + protected Image createLoginBanner() { + return getUI() == null ? null : getUI().getBanner(); + } + + /** + * Create and return an Action for logging in + */ + protected Action createLoginAction() { + return new LoginAction(this); + } + + /** + * Create and return an Action for canceling login + */ + protected Action createCancelAction() { + return new CancelAction(this); + } + + //------------------------------------------------------ Bean Properties + //REMEMBER: when adding new methods, they need to fire property change events!!! + /** + * @return Returns the saveMode. + */ + public SaveMode getSaveMode() { + return saveMode; + } + + /** + * The save mode indicates whether the "save" password is checked by default. This method + * makes no difference if the passwordStore is null. + * + * @param saveMode The saveMode to set either SAVE_NONE, SAVE_PASSWORD or SAVE_USERNAME + */ + public void setSaveMode(SaveMode saveMode) { + if (this.saveMode != saveMode) { + SaveMode oldMode = getSaveMode(); + this.saveMode = saveMode; + recreateLoginPanel(); + firePropertyChange("saveMode", oldMode, getSaveMode()); + } + } + + public boolean isRememberPassword() { + return saveCB.isVisible() && saveCB.isSelected(); + } + + /** + * @return the List of servers + */ + public List getServers() { + return Collections.unmodifiableList(servers); + } + + /** + * Sets the list of servers. See the servers field javadoc for more info. + */ + public void setServers(List servers) { + //only at startup + if (this.servers == null) { + this.servers = servers == null ? new ArrayList() : servers; + } else if (this.servers != servers) { + List old = getServers(); + this.servers = servers == null ? new ArrayList() : servers; + recreateLoginPanel(); + firePropertyChange("servers", old, getServers()); + } + } + + private LoginListener getDefaultLoginListener() { + if (defaultLoginListener == null) { + defaultLoginListener = new LoginListenerImpl(); + } + + return defaultLoginListener; + } + + /** + * Sets the {@code LoginService} for this panel. Setting the login service + * to {@code null} will actually set the service to use + * {@code NullLoginService}. + * + * @param service + * the service to set. If {@code service == null}, then a + * {@code NullLoginService} is used. + */ + public void setLoginService(LoginService service) { + LoginService oldService = getLoginService(); + LoginService newService = service == null ? new NullLoginService() : service; + + //newService is guaranteed to be nonnull + if (!newService.equals(oldService)) { + if (oldService != null) { + oldService.removeLoginListener(getDefaultLoginListener()); + } + + loginService = newService; + this.loginService.addLoginListener(getDefaultLoginListener()); + + firePropertyChange("loginService", oldService, getLoginService()); + } + } + + /** + * Gets the LoginService for this panel. + * + * @return service service + */ + public LoginService getLoginService() { + return loginService; + } + + /** + * Sets the PasswordStore for this panel. + * + * @param store PasswordStore + */ + public void setPasswordStore(PasswordStore store) { + PasswordStore oldStore = getPasswordStore(); + PasswordStore newStore = store == null ? new NullPasswordStore() : store; + + //newStore is guaranteed to be nonnull + if (!newStore.equals(oldStore)) { + passwordStore = newStore; + + firePropertyChange("passwordStore", oldStore, getPasswordStore()); + } + } + + /** + * Gets the {@code UserNameStore} for this panel. + * + * @return the {@code UserNameStore} + */ + public UserNameStore getUserNameStore() { + return userNameStore; + } + + /** + * Sets the user name store for this panel. + * @param store + */ + public void setUserNameStore(UserNameStore store) { + UserNameStore oldStore = getUserNameStore(); + UserNameStore newStore = store == null ? new DefaultUserNameStore() : store; + + //newStore is guaranteed to be nonnull + if (!newStore.equals(oldStore)) { + userNameStore = newStore; + + firePropertyChange("userNameStore", oldStore, getUserNameStore()); + } + } + + /** + * Gets the PasswordStore for this panel. + * + * @return store PasswordStore + */ + public PasswordStore getPasswordStore() { + return passwordStore; + } + + /** + * Sets the User name for this panel. + * + * @param username User name + */ + public void setUserName(String username) { + if (namePanel != null) { + String old = getUserName(); + namePanel.setUserName(username); + firePropertyChange("userName", old, getUserName()); + } + } + + /** + * Enables or disables User name for this panel. + * + * @param enabled + */ + public void setUserNameEnabled(boolean enabled) { + boolean old = isUserNameEnabled(); + this.namePanelEnabled = enabled; + if (namePanel != null) { + namePanel.setEnabled(enabled); + namePanel.setEditable(enabled); + } + firePropertyChange("userNameEnabled", old, isUserNameEnabled()); + } + + /** + * Gets current state of the user name field. Field can be either disabled (false) for editing or enabled (true). + * @return True when user name field is enabled and editable, false otherwise. + */ + public boolean isUserNameEnabled() { + return this.namePanelEnabled; + } + + /** + * Gets the User name for this panel. + * @return the user name + */ + public String getUserName() { + return namePanel == null ? null : namePanel.getUserName(); + } + + /** + * Sets the Password for this panel. + * + * @param password Password + */ + public void setPassword(char[] password) { + passwordField.setText(new String(password)); + } + + /** + * Gets the Password for this panel. + * + * @return password Password + */ + public char[] getPassword() { + return passwordField.getPassword(); + } + + /** + * Return the image used as the banner + */ + public Image getBanner() { + return banner.getImage(); + } + + /** + * Set the image to use for the banner. If the {@code img} is {@code null}, + * then no image will be displayed. + * + * @param img + * the image to display + */ + public void setBanner(Image img) { + // we do not expose the ImagePanel, so we will produce property change + // events here + Image oldImage = getBanner(); + + if (oldImage != img) { + banner.setImage(img); + firePropertyChange("banner", oldImage, getBanner()); + } + } + + /** + * Set the text to use when creating the banner. If a custom banner image is + * specified, then this is ignored. If {@code text} is {@code null}, then + * no text is displayed. + * + * @param text + * the text to display + */ + public void setBannerText(String text) { + if (text == null) { + text = ""; + } + + if (!text.equals(this.bannerText)) { + String oldText = this.bannerText; + this.bannerText = text; + //fix the login banner + this.banner.setImage(createLoginBanner()); + firePropertyChange("bannerText", oldText, text); + } + } + + /** + * Returns text used when creating the banner + */ + public String getBannerText() { + return bannerText; + } + + /** + * Returns the custom message for this login panel + */ + public String getMessage() { + return messageLabel.getText(); + } + + /** + * Sets a custom message for this login panel + */ + public void setMessage(String message) { + String old = messageLabel.getText(); + messageLabel.setText(message); + firePropertyChange("message", old, messageLabel.getText()); + } + + /** + * Returns the error message for this login panel + */ + public String getErrorMessage() { + return errorMessageLabel.getText(); + } + + /** + * Sets the error message for this login panel + */ + public void setErrorMessage(String errorMessage) { + isErrorMessageSet = true; + String old = errorMessageLabel.getText(); + errorMessageLabel.setText(errorMessage); + firePropertyChange("errorMessage", old, errorMessageLabel.getText()); + } + + /** + * Returns the panel's status + */ + public Status getStatus() { + return status; + } + + /** + * Change the status + */ + protected void setStatus(Status newStatus) { + if (status != newStatus) { + Status oldStatus = status; + status = newStatus; + firePropertyChange("status", oldStatus, newStatus); + } + } + + @Override + public void setLocale(Locale l) { + super.setLocale(l); + reinitLocales(l); + } + //-------------------------------------------------------------- Methods + + /** + * Initiates the login procedure. This method is called internally by + * the LoginAction. This method handles cursor management, and actually + * calling the LoginService's startAuthentication method. Method will return + * immediately if asynchronous login is enabled or will block until + * authentication finishes if getSynchronous() returns true. + */ + protected void startLogin() { + oldCursor = getCursor(); + try { + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + progressMessageLabel.setText(UIManagerExt.getString(CLASS_NAME + ".pleaseWait", getLocale())); + String name = getUserName(); + char[] password = getPassword(); + String server = servers.size() == 1 ? servers.get(0) : serverCombo == null ? null : (String)serverCombo.getSelectedItem(); + + loginService.startAuthentication(name, password, server); + } catch(Exception ex) { + //The status is set via the loginService listener, so no need to set + //the status here. Just log the error. + LOG.log(Level.WARNING, "Authentication exception while logging in", ex); + } finally { + setCursor(oldCursor); + } + } + + /** + * Cancels the login procedure. Handles cursor management and interfacing + * with the LoginService's cancelAuthentication method. Calling this method + * has an effect only when authentication is still in progress (i.e. after + * previous call to startAuthentications() and only when + * authentication is performed asynchronously (getSynchronous() + * returns false). + */ + protected void cancelLogin() { + progressMessageLabel.setText(UIManagerExt.getString(CLASS_NAME + ".cancelWait", getLocale())); + getActionMap().get(CANCEL_LOGIN_ACTION_COMMAND).setEnabled(false); + loginService.cancelAuthentication(); + setCursor(oldCursor); + } + + /** + * Puts the password into the password store. If password store is not set, method will do + * nothing. + */ + protected void savePassword() { + if (saveCB.isSelected() + && (saveMode == SaveMode.BOTH || saveMode == SaveMode.PASSWORD) + && passwordStore != null) { + passwordStore.set(getUserName(),getLoginService().getServer(),getPassword()); + } + } + + //--------------------------------------------- Listener Implementations + /* + + For Login (initiated in LoginAction): + 0) set the status + 1) Immediately disable the login action + 2) Immediately disable the close action (part of enclosing window) + 3) initialize the progress pane + a) enable the cancel login action + b) set the message text + 4) hide the content pane, show the progress pane + + When cancelling (initiated in CancelAction): + 0) set the status + 1) Disable the cancel login action + 2) Change the message text on the progress pane + + When cancel finishes (handled in LoginListener): + 0) set the status + 1) hide the progress pane, show the content pane + 2) enable the close action (part of enclosing window) + 3) enable the login action + + When login fails (handled in LoginListener): + 0) set the status + 1) hide the progress pane, show the content pane + 2) enable the close action (part of enclosing window) + 3) enable the login action + 4) Show the error message + 5) resize the window (part of enclosing window) + + When login succeeds (handled in LoginListener): + 0) set the status + 1) close the dialog/frame (part of enclosing window) + */ + /** + * Listener class to track state in the LoginService + */ + protected class LoginListenerImpl extends LoginAdapter { + @Override + public void loginSucceeded(LoginEvent source) { + //save the user names and passwords + String userName = namePanel.getUserName(); + if ((getSaveMode() == SaveMode.USER_NAME || getSaveMode() == SaveMode.BOTH) + && userName != null && !userName.trim().equals("")) { + userNameStore.addUserName(userName); + userNameStore.saveUserNames(); + } + + // if the user and/or password store knows of this user, + // and the checkbox is unchecked, we remove them, otherwise + // we save the password + if (saveCB.isSelected()) { + savePassword(); + } else { + // remove the password from the password store + if (passwordStore != null) { + passwordStore.removeUserPassword(userName); + } + } + + setStatus(Status.SUCCEEDED); + } + + @Override + public void loginStarted(LoginEvent source) { + assert EventQueue.isDispatchThread(); + getActionMap().get(LOGIN_ACTION_COMMAND).setEnabled(false); + getActionMap().get(CANCEL_LOGIN_ACTION_COMMAND).setEnabled(true); +// remove(contentPanel); +// add(progressPanel, BorderLayout.CENTER); + ((CardLayout) contentCardPane.getLayout()).last(contentCardPane); + revalidate(); + repaint(); + setStatus(Status.IN_PROGRESS); + } + + @Override + public void loginFailed(LoginEvent source) { + assert EventQueue.isDispatchThread(); +// remove(progressPanel); +// add(contentPanel, BorderLayout.CENTER); + ((CardLayout) contentCardPane.getLayout()).first(contentCardPane); + getActionMap().get(LOGIN_ACTION_COMMAND).setEnabled(true); + errorMessageLabel.setVisible(true); + revalidate(); + repaint(); + setStatus(Status.FAILED); + } + + @Override + public void loginCanceled(LoginEvent source) { + assert EventQueue.isDispatchThread(); +// remove(progressPanel); +// add(contentPanel, BorderLayout.CENTER); + ((CardLayout) contentCardPane.getLayout()).first(contentCardPane); + getActionMap().get(LOGIN_ACTION_COMMAND).setEnabled(true); + errorMessageLabel.setVisible(false); + revalidate(); + repaint(); + setStatus(Status.CANCELLED); + } + } + + //---------------------------------------------- Default Implementations + /** + * Action that initiates a login procedure. Delegates to JXLoginPane.startLogin + */ + private static final class LoginAction extends AbstractActionExt { + private static final long serialVersionUID = 7256761187925982485L; + private JXLoginPane panel; + public LoginAction(JXLoginPane p) { + super(UIManagerExt.getString(CLASS_NAME + ".loginString", p.getLocale()), LOGIN_ACTION_COMMAND); + this.panel = p; + } + @Override + public void actionPerformed(ActionEvent e) { + panel.startLogin(); + } + @Override + public void itemStateChanged(ItemEvent e) {} + } + + /** + * Action that cancels the login procedure. + */ + private static final class CancelAction extends AbstractActionExt { + private static final long serialVersionUID = 4040029973355439229L; + private JXLoginPane panel; + public CancelAction(JXLoginPane p) { + super(UIManagerExt.getString(CLASS_NAME + ".cancelLogin", p.getLocale()), CANCEL_LOGIN_ACTION_COMMAND); + this.panel = p; + this.setEnabled(false); + } + @Override + public void actionPerformed(ActionEvent e) { + panel.cancelLogin(); + } + @Override + public void itemStateChanged(ItemEvent e) {} + } + + /** + * Simple login service that allows everybody to login. This is useful in demos and allows + * us to avoid having to check for LoginService being null + */ + private static final class NullLoginService extends LoginService { + @Override + public boolean authenticate(String name, char[] password, String server) throws Exception { + return true; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof NullLoginService; + } + + @Override + public int hashCode() { + return 7; + } + } + + /** + * Simple PasswordStore that does not remember passwords + */ + private static final class NullPasswordStore extends PasswordStore { + @Override + public boolean set(String username, String server, char[] password) { + //null op + return false; + } + + @Override + public char[] get(String username, String server) { + return new char[0]; + } + + @Override + public void removeUserPassword(String username) { + return; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof NullPasswordStore; + } + + @Override + public int hashCode() { + return 7; + } + } + + //--------------------------------- Default NamePanel Implementations + private static interface NameComponent { + public String getUserName(); + public boolean isEnabled(); + public boolean isEditable(); + public void setEditable(boolean enabled); + public void setEnabled(boolean enabled); + public void setUserName(String userName); + public JComponent getComponent(); + } + + private void updatePassword(final String username) { + String password = ""; + if (username != null) { + char[] pw = passwordStore.get(username, null); + password = pw == null ? "" : new String(pw); + + // if the userstore has this username, we should change the + // 'remember me' checkbox to be selected. Unselecting this will + // result in the user being 'forgotten'. + saveCB.setSelected(userNameStore.containsUserName(username)); + } + + passwordField.setText(password); + } + + /** + * If a UserNameStore is not used, then this text field is presented allowing the user + * to simply enter their user name + */ + private final class SimpleNamePanel extends JTextField implements NameComponent { + private static final long serialVersionUID = 6513437813612641002L; + + public SimpleNamePanel() { + super("", 15); + + // auto-complete based on the users input + // AutoCompleteDecorator.decorate(this, Arrays.asList(userNameStore.getUserNames()), false); + + // listen to text input, and offer password suggestion based on current + // text + if (passwordStore != null && passwordField!=null) { + addKeyListener(new KeyAdapter() { + @Override + public void keyReleased(KeyEvent e) { + updatePassword(getText()); + } + }); + } + } + + @Override + public String getUserName() { + return getText(); + } + @Override + public void setUserName(String userName) { + setText(userName); + } + @Override + public JComponent getComponent() { + return this; + } + } + + /** + * If a UserNameStore is used, then this combo box is presented allowing the user + * to select a previous login name, or type in a new login name + */ + private final class ComboNamePanel extends JComboBox implements NameComponent { + private static final long serialVersionUID = 2511649075486103959L; + + public ComboNamePanel() { + super(); + setModel(new NameComboBoxModel()); + setEditable(true); + + // auto-complete based on the users input + AutoCompleteDecorator.decorate(this); + + // listen to selection or text input, and offer password suggestion based on current + // text + if (passwordStore != null && passwordField!=null) { + final JTextField textfield = (JTextField) getEditor().getEditorComponent(); + textfield.addKeyListener(new KeyAdapter() { + @Override + public void keyReleased(KeyEvent e) { + updatePassword(textfield.getText()); + } + }); + + super.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + updatePassword((String)getSelectedItem()); + } + }); + } + } + + @Override + public String getUserName() { + Object item = getModel().getSelectedItem(); + return item == null ? null : item.toString(); + } + @Override + public void setUserName(String userName) { + getModel().setSelectedItem(userName); + } + public void setUserNames(String[] names) { + setModel(new DefaultComboBoxModel(names)); + } + @Override + public JComponent getComponent() { + return this; + } + + private final class NameComboBoxModel extends AbstractListModel implements ComboBoxModel { + private static final long serialVersionUID = 7097674687536018633L; + private Object selectedItem; + @Override + public void setSelectedItem(Object anItem) { + selectedItem = anItem; + fireContentsChanged(this, -1, -1); + } + @Override + public Object getSelectedItem() { + return selectedItem; + } + @Override + public Object getElementAt(int index) { + if (index == -1) { + return null; + } + + return userNameStore.getUserNames()[index]; + } + @Override + public int getSize() { + return userNameStore.getUserNames().length; + } + } + } + + //------------------------------------------ Static Construction Methods + /** + * Shows a login dialog. This method blocks. + * @return The status of the login operation + */ + public static Status showLoginDialog(Component parent, LoginService svc) { + return showLoginDialog(parent, svc, null, null); + } + + /** + * Shows a login dialog. This method blocks. + * @return The status of the login operation + */ + public static Status showLoginDialog(Component parent, LoginService svc, PasswordStore ps, UserNameStore us) { + return showLoginDialog(parent, svc, ps, us, null); + } + + /** + * Shows a login dialog. This method blocks. + * @return The status of the login operation + */ + public static Status showLoginDialog(Component parent, LoginService svc, PasswordStore ps, UserNameStore us, List servers) { + JXLoginPane panel = new JXLoginPane(svc, ps, us, servers); + return showLoginDialog(parent, panel); + } + + /** + * Shows a login dialog. This method blocks. + * @return The status of the login operation + */ + public static Status showLoginDialog(Component parent, JXLoginPane panel) { + Window w = WindowUtils.findWindow(parent); + JXLoginDialog dlg = null; + if (w == null) { + dlg = new JXLoginDialog((Frame)null, panel); + } else if (w instanceof Dialog) { + dlg = new JXLoginDialog((Dialog)w, panel); + } else if (w instanceof Frame) { + dlg = new JXLoginDialog((Frame)w, panel); + } else { + throw new AssertionError("Shouldn't be able to happen"); + } + dlg.setVisible(true); + return dlg.getStatus(); + } + + /** + * Shows a login frame. A JFrame is not modal, and thus does not block + */ + public static JXLoginFrame showLoginFrame(LoginService svc) { + return showLoginFrame(svc, null, null); + } + + /** + */ + public static JXLoginFrame showLoginFrame(LoginService svc, PasswordStore ps, UserNameStore us) { + return showLoginFrame(svc, ps, us, null); + } + + /** + */ + public static JXLoginFrame showLoginFrame(LoginService svc, PasswordStore ps, UserNameStore us, List servers) { + JXLoginPane panel = new JXLoginPane(svc, ps, us, servers); + return showLoginFrame(panel); + } + + /** + */ + public static JXLoginFrame showLoginFrame(JXLoginPane panel) { + return new JXLoginFrame(panel); + } + + public static final class JXLoginDialog extends JDialog { + private static final long serialVersionUID = -3185639594267828103L; + private JXLoginPane panel; + + public JXLoginDialog(Frame parent, JXLoginPane p) { + super(parent, true); + init(p); + } + + public JXLoginDialog(Dialog parent, JXLoginPane p) { + super(parent, true); + init(p); + } + + protected void init(JXLoginPane p) { + setTitle(UIManagerExt.getString(CLASS_NAME + ".titleString", getLocale())); + this.panel = p; + initWindow(this, panel); + } + + public Status getStatus() { + return panel.getStatus(); + } + } + + public static final class JXLoginFrame extends JXFrame { + private static final long serialVersionUID = -9016407314342050807L; + private JXLoginPane panel; + + public JXLoginFrame(JXLoginPane p) { + super(UIManagerExt.getString(CLASS_NAME + ".titleString", p.getLocale())); + JXPanel cp = new JXPanel(); + cp.setOpaque(true); + setContentPane(cp); + this.panel = p; + initWindow(this, panel); + } + + @Override + public JXPanel getContentPane() { + return (JXPanel) super.getContentPane(); + } + + public Status getStatus() { + return panel.getStatus(); + } + + public JXLoginPane getPanel() { + return panel; + } + } + + /** + * Utility method for initializing a Window for displaying a LoginDialog. + * This is particularly useful because the differences between JFrame and + * JDialog are so minor. + * + * Note: This method is package private for use by JXLoginDialog (proper, + * not JXLoginPane.JXLoginDialog). Change to private if JXLoginDialog is + * removed. + */ + static void initWindow(final Window w, final JXLoginPane panel) { + w.setLayout(new BorderLayout()); + w.add(panel, BorderLayout.CENTER); + JButton okButton = new JButton(panel.getActionMap().get(LOGIN_ACTION_COMMAND)); + final JButton cancelButton = new JButton( + UIManagerExt.getString(CLASS_NAME + ".cancelString", panel.getLocale())); + cancelButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + //change panel status to canceled! + panel.status = Status.CANCELLED; + w.setVisible(false); + w.dispose(); + } + }); + panel.addPropertyChangeListener("status", new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + Status status = (Status)evt.getNewValue(); + switch (status) { + case NOT_STARTED: + break; + case IN_PROGRESS: + cancelButton.setEnabled(false); + break; + case CANCELLED: + cancelButton.setEnabled(true); + w.pack(); + break; + case FAILED: + cancelButton.setEnabled(true); + panel.passwordField.requestFocusInWindow(); + w.pack(); + break; + case SUCCEEDED: + w.setVisible(false); + w.dispose(); + } + for (PropertyChangeListener l : w.getPropertyChangeListeners("status")) { + PropertyChangeEvent pce = new PropertyChangeEvent(w, "status", evt.getOldValue(), evt.getNewValue()); + l.propertyChange(pce); + } + } + }); + // FIX for #663 - commented out two lines below. Not sure why they were here in a first place. + // cancelButton.setText(UIManager.getString(CLASS_NAME + ".cancelString")); + // okButton.setText(UIManager.getString(CLASS_NAME + ".loginString")); + JXBtnPanel buttonPanel = new JXBtnPanel(okButton, cancelButton); + buttonPanel.setOpaque(false); + panel.setButtonPanel(buttonPanel); + JXPanel controls = new JXPanel(new FlowLayout(FlowLayout.RIGHT)); + controls.setOpaque(false); + new BoxLayout(controls, BoxLayout.X_AXIS); + controls.add(Box.createHorizontalGlue()); + controls.add(buttonPanel); + w.add(controls, BorderLayout.SOUTH); + w.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(java.awt.event.WindowEvent e) { + panel.cancelLogin(); + } + }); + + if (w instanceof JFrame) { + final JFrame f = (JFrame)w; + f.getRootPane().setDefaultButton(okButton); + f.setResizable(false); + f.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); + ActionListener closeAction = new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + f.setVisible(false); + f.dispose(); + } + }; + f.getRootPane().registerKeyboardAction(closeAction, ks, JComponent.WHEN_IN_FOCUSED_WINDOW); + } else if (w instanceof JDialog) { + final JDialog d = (JDialog)w; + d.getRootPane().setDefaultButton(okButton); + d.setResizable(false); + KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); + ActionListener closeAction = new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + d.setVisible(false); + } + }; + d.getRootPane().registerKeyboardAction(closeAction, ks, JComponent.WHEN_IN_FOCUSED_WINDOW); + } + w.pack(); + w.setLocation(WindowUtils.getPointForCentering(w)); + } + + private void setButtonPanel(JXBtnPanel buttonPanel) { + this.buttonPanel = buttonPanel; + } + + private static class JXBtnPanel extends JXPanel { + private static final long serialVersionUID = 4136611099721189372L; + private JButton cancel; + private JButton ok; + + public JXBtnPanel(JButton okButton, JButton cancelButton) { + GridLayout layout = new GridLayout(1,2); + layout.setHgap(5); + setLayout(layout); + this.ok = okButton; + this.cancel = cancelButton; + add(okButton); + add(cancelButton); + setBorder(new EmptyBorder(0,0,7,11)); + } + + /** + * @return the cancel button. + */ + public JButton getCancel() { + return cancel; + } + + /** + * @return the ok button. + */ + public JButton getOk() { + return ok; + } + + } +} diff --git a/src/main/java/org/jdesktop/swingx/JXMonthView.java b/src/main/java/org/jdesktop/swingx/JXMonthView.java new file mode 100644 index 0000000000..f81263f9c2 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXMonthView.java @@ -0,0 +1,1889 @@ +/* + * $Id: JXMonthView.java 4147 2012-02-01 17:13:24Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.calendar.CalendarUtils; +import org.jdesktop.swingx.calendar.DateSelectionModel; +import org.jdesktop.swingx.calendar.DateSelectionModel.SelectionMode; +import org.jdesktop.swingx.calendar.DaySelectionModel; +import org.jdesktop.swingx.event.DateSelectionEvent; +import org.jdesktop.swingx.event.DateSelectionEvent.EventType; +import org.jdesktop.swingx.event.DateSelectionListener; +import org.jdesktop.swingx.event.EventListenerMap; +import org.jdesktop.swingx.plaf.LookAndFeelAddons; +import org.jdesktop.swingx.plaf.MonthViewAddon; +import org.jdesktop.swingx.plaf.MonthViewUI; +import org.jdesktop.swingx.util.Contract; + +import javax.swing.Timer; +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.*; +import java.util.logging.Logger; + + +/** + * Component that displays a month calendar which can be used to select a day + * or range of days. By default the JXMonthView will display a + * single calendar using the current month and year, using + * Calendar.SUNDAY as the first day of the week. + *

+ * The JXMonthView can be configured to display more than one + * calendar at a time by calling + * setPreferredCalCols/setPreferredCalRows. These + * methods will set the preferred number of calendars to use in each + * column/row. As these values change, the Dimension returned + * from getMinimumSize and getPreferredSize will + * be updated. The following example shows how to create a 2x2 view which is + * contained within a JFrame: + *

+ *     JXMonthView monthView = new JXMonthView();
+ *     monthView.setPreferredCols(2);
+ *     monthView.setPreferredRows(2);
+ *
+ *     JFrame frame = new JFrame();
+ *     frame.getContentPane().add(monthView);
+ *     frame.pack();
+ *     frame.setVisible(true);
+ * 
+ *

+ * JXMonthView can be further configured to allow any day of the + * week to be considered the first day of the week. Character + * representation of those days may also be set by providing an array of + * strings. + *

+ *    monthView.setFirstDayOfWeek(Calendar.MONDAY);
+ *    monthView.setDaysOfTheWeek(
+ *            new String[]{"S", "M", "T", "W", "Th", "F", "S"});
+ * 
+ *

+ * This component supports flagging days. These flagged days are displayed + * in a bold font. This can be used to inform the user of such things as + * scheduled appointment. + *


+ *    // Create some dates that we want to flag as being important.
+ *    Calendar cal1 = Calendar.getInstance();
+ *    cal1.set(2004, 1, 1);
+ *    Calendar cal2 = Calendar.getInstance();
+ *    cal2.set(2004, 1, 5);
+ *
+ *    monthView.setFlaggedDates(cal1.getTime(), cal2.getTime(), new Date());
+ * 
+ * Applications may have the need to allow users to select different ranges of + * dates. There are three modes of selection that are supported, single, single interval + * and multiple interval selection. Once a selection is made a DateSelectionEvent is + * fired to inform listeners of the change. + *
+ *    // Change the selection mode to select full weeks.
+ *    monthView.setSelectionMode(SelectionMode.SINGLE_INTERVAL_SELECTION);
+ *
+ *    // Register a date selection listener to get notified about
+ *    // any changes in the date selection model.
+ *    monthView.getSelectionModel().addDateSelectionListener(new DateSelectionListener {
+ *        public void valueChanged(DateSelectionEvent e) {
+ *            log.info(e.getSelection());
+ *        }
+ *    });
+ * 
+ * + * NOTE (for users of earlier versions): as of version 1.19 control about selection + * dates is moved completely into the model. The default model used is of type + * DaySelectionModel, which handles dates in the same way the JXMonthView did earlier + * (that is, normalize all to the start of the day, which means zeroing all time + * fields).

+ * + * @author Joshua Outwater + * @author Jeanette Winzenburg + * @version $Revision: 4147 $ + */ +@JavaBean +public class JXMonthView extends JComponent { + @SuppressWarnings("unused") + private static final Logger LOG = Logger.getLogger(JXMonthView.class + .getName()); + /* + * moved from package calendar to swingx at version 1.51 + */ + + /** action command used for commit actionEvent. */ + public static final String COMMIT_KEY = "monthViewCommit"; + /** action command used for cancel actionEvent. */ + public static final String CANCEL_KEY = "monthViewCancel"; + + public static final String BOX_PADDING_X = "boxPaddingX"; + public static final String BOX_PADDING_Y = "boxPaddingY"; + public static final String DAYS_OF_THE_WEEK = "daysOfTheWeek"; + public static final String SELECTION_MODEL = "selectionModel"; + public static final String TRAVERSABLE = "traversable"; + public static final String FLAGGED_DATES = "flaggedDates"; + + static { + LookAndFeelAddons.contribute(new MonthViewAddon()); + } + + /** + * UI Class ID + */ + public static final String uiClassID = "MonthViewUI"; + + public static final int DAYS_IN_WEEK = 7; + public static final int MONTHS_IN_YEAR = 12; + + + /** + * Keeps track of the first date we are displaying. We use this as a + * restore point for the calendar. This is normalized to the start of the + * first day of the month given in setFirstDisplayedDate. + */ + private Date firstDisplayedDay; + /** + * the calendar to base all selections, flagging upon. + * NOTE: the time of this calendar is undefined - before using, internal + * code must explicitly set it. + * PENDING JW: as of version 1.26 all calendar/properties are controlled by the model. + * We keep a clone of the model's calendar here for notification reasons: + * model fires DateSelectionEvent of type CALENDAR_CHANGED which neiter carry the + * oldvalue nor the property name needed to map into propertyChange notification. + */ + private Calendar cal; + /** calendar to store the real input of firstDisplayedDate. */ + private Calendar anchor; + /** + * Start of the day which contains System.millis() in the current calendar. + * Kept in synch via a timer started in addNotify. + */ + private Date today; + /** + * The timer used to keep today in synch with system time. + */ + private Timer todayTimer; + // PENDING JW: why kept apart from cal? Why writable? - shouldn't the calendar have complete + // control? + private int firstDayOfWeek; + //-------------- selection/flagging + /** + * The DateSelectionModel driving this component. This model's calendar + * is the reference for all dates. + */ + private DateSelectionModel model; + /** + * Listener registered with the current model to keep Calendar dependent + * state synched. + */ + private DateSelectionListener modelListener; + /** + * The manager of the flagged dates. Note + * that the type of this is an implementation detail. + */ + private DaySelectionModel flaggedDates; + /** + * Storage of actionListeners registered with the monthView. + */ + private EventListenerMap listenerMap; + + private boolean traversable; + private boolean leadingDays; + private boolean trailingDays; + private boolean showWeekNumber; + private boolean componentInputMapEnabled; + + //------------------- + // PENDING JW: ?? + @SuppressWarnings({"FieldCanBeLocal"}) + protected Date modifiedStartDate; + @SuppressWarnings({"FieldCanBeLocal"}) + protected Date modifiedEndDate; + + //------------- visuals + + /** + * Localizable day column headers. Default typically installed by the uidelegate. + */ + private String[] _daysOfTheWeek; + + protected Insets _monthStringInsets = new Insets(0, 0, 0, 0); + private int boxPaddingX; + private int boxPaddingY; + private int minCalCols = 1; + private int minCalRows = 1; + private Color todayBackgroundColor; + private Color monthStringBackground; + private Color monthStringForeground; + private Color daysOfTheWeekForeground; + private Color selectedBackground; + private Hashtable dayToColorTable = new Hashtable(); + private Color flaggedDayForeground; + + private Color selectedForeground; + private boolean zoomable; + + /** + * Create a new instance of the JXMonthView class using the + * default Locale and the current system time as the first date to + * display. + */ + public JXMonthView() { + this(null, null, null); + } + + /** + * Create a new instance of the JXMonthView class using the + * default Locale and the current system time as the first date to + * display. + * + * @param locale desired locale, if null the system default locale is used + */ + public JXMonthView(final Locale locale) { + this(null, null, locale); + } + + /** + * Create a new instance of the JXMonthView class using the + * default Locale and the given time as the first date to + * display. + * + * @param firstDisplayedDay a day of the first month to display; if null, the current + * System time is used. + */ + public JXMonthView(Date firstDisplayedDay) { + this(firstDisplayedDay, null, null); + } + + /** + * Create a new instance of the JXMonthView class using the + * default Locale, the given time as the first date to + * display and the given selection model. + * + * @param firstDisplayedDay a day of the first month to display; if null, the current + * System time is used. + * @param model the selection model to use, if null a DefaultSelectionModel is + * created. + */ + public JXMonthView(Date firstDisplayedDay, final DateSelectionModel model) { + this(firstDisplayedDay, model, null); + } + + + /** + * Create a new instance of the JXMonthView class using the + * given Locale, the given time as the first date to + * display and the given selection model. + * + * @param firstDisplayedDay a day of the first month to display; if null, the current + * System time is used. + * @param model the selection model to use, if null a DefaultSelectionModel is + * created. + * @param locale desired locale, if null the system default locale is used + */ + public JXMonthView(Date firstDisplayedDay, final DateSelectionModel model, final Locale locale) { + super(); + listenerMap = new EventListenerMap(); + + initModel(model, locale); + superSetLocale(locale); + setFirstDisplayedDay(firstDisplayedDay != null ? firstDisplayedDay : getCurrentDate()); + // Keep track of today + updateTodayFromCurrentTime(); + + // install the controller + updateUI(); + + setFocusable(true); + todayBackgroundColor = getForeground(); + + } + + +//------------------ Calendar related properties + + /** + * Sets locale and resets text and format used to display months and days. + * Also resets firstDayOfWeek.

+ * + * PENDING JW: the following warning should be obsolete (installCalendar + * should take care) - check if it really is! + * + *

+ * Warning: Since this resets any string labels that are cached in UI + * (month and day names) and firstDayofWeek, use setDaysOfTheWeek and/or + * setFirstDayOfWeek after (re)setting locale. + *

+ * + * @param locale new Locale to be used for formatting + * @see #setDaysOfTheWeek(String[]) + * @see #setFirstDayOfWeek(int) + */ + @Override + public void setLocale(Locale locale) { + model.setLocale(locale); + } + + /** + * + * @param locale + */ + private void superSetLocale(Locale locale) { + // PENDING JW: formally, a null value is allowed and must be passed on to super + // I suspect this is not done here to keep the logic out off the constructor? + // + if (locale != null) { + super.setLocale(locale); + repaint(); + } + } + + /** + * Returns a clone of the internal calendar, with it's time set to firstDisplayedDate. + * + * PENDING: firstDisplayed useful as reference time? It's timezone dependent anyway. + * Think: could return with monthView's today instead? + * + * @return a clone of internal calendar, configured to the current firstDisplayedDate + * @throws IllegalStateException if called before instantitation is completed + */ + public Calendar getCalendar() { + // JW: this is to guard against a regression of not-fully understood + // problems in constructor (UI used to call back into this before we were ready) + if (cal == null) throw + new IllegalStateException("must not be called before instantiation is complete"); + Calendar calendar = (Calendar) cal.clone(); + calendar.setTime(firstDisplayedDay); + return calendar; + } + + /** + * Gets the time zone. + * + * @return The TimeZone used by the JXMonthView. + */ + public TimeZone getTimeZone() { + // PENDING JW: looks fishy (left-over?) .. why not ask the model? + return cal.getTimeZone(); + } + + /** + * Sets the time zone with the given time zone value. + * + * This is a bound property. + * + * @param tz The TimeZone. + */ + public void setTimeZone(TimeZone tz) { + model.setTimeZone(tz); + } + + /** + * Gets what the first day of the week is; e.g., + * Calendar.SUNDAY in the U.S., Calendar.MONDAY + * in France. + * + * @return int The first day of the week. + */ + public int getFirstDayOfWeek() { + return firstDayOfWeek; + } + + /** + * Sets what the first day of the week is; e.g., + * Calendar.SUNDAY in US, Calendar.MONDAY + * in France. + * + * @param firstDayOfWeek The first day of the week. + * @see Calendar + */ + public void setFirstDayOfWeek(int firstDayOfWeek) { + getSelectionModel().setFirstDayOfWeek(firstDayOfWeek); + } + + + +//---------------------- synch to model's calendar + + /** + * Initializes selection model related internals. If the Locale is + * null, it falls back to JComponent.defaultLocale. If the model + * is null it creates a default model with the locale. + * + * PENDING JW: leave default locale fallback to model? + * + * @param model the DateSelectionModel which should drive the monthView. + * If null, a default model is created and initialized with the given locale. + * @param locale the Locale to use with the selectionModel. If null, + * JComponent.getDefaultLocale is used. + */ + private void initModel(DateSelectionModel model, Locale locale) { + if (locale == null) { + locale = JComponent.getDefaultLocale(); + } + if (model == null) { + model = new DaySelectionModel(locale); + } + this.model = model; + // PENDING JW: do better to synchronize Calendar related + // properties of flaggedDates to those of the selection model. + // plus: should use the same normalization? + this.flaggedDates = new DaySelectionModel(locale); + flaggedDates.setSelectionMode(SelectionMode.MULTIPLE_INTERVAL_SELECTION); + + installCalendar(); + model.addDateSelectionListener(getDateSelectionListener()); + } + + /** + * Lazily creates and returns the DateSelectionListener which listens + * for model's calendar properties. + * + * @return a DateSelectionListener for model's CALENDAR_CHANGED notification. + */ + private DateSelectionListener getDateSelectionListener() { + if (modelListener == null) { + modelListener = new DateSelectionListener() { + + @Override + public void valueChanged(DateSelectionEvent ev) { + if (EventType.CALENDAR_CHANGED.equals(ev.getEventType())) { + updateCalendar(); + } + + } + + }; + } + return modelListener; + } + + /** + * Installs the internal calendars from the selection model.

+ * + * PENDING JW: in fixing #11433, added update of firstDisplayedDay and + * today here - check if correct place to do so. + * + */ + private void installCalendar() { + cal = model.getCalendar(); + firstDayOfWeek = cal.getFirstDayOfWeek(); + Date anchorDate = getAnchorDate(); + anchor = (Calendar) cal.clone(); + if (anchorDate != null) { + setFirstDisplayedDay(anchorDate); + } + updateTodayFromCurrentTime(); + } + + /** + * Returns the anchor date. Currently, this is the "uncleaned" input date + * of setFirstDisplayedDate. This is a quick hack for Issue #618-swingx, to + * have some invariant for testing. Do not use in client code, may change + * without notice! + * + * @return the "uncleaned" first display date or null if the firstDisplayedDay + * is not yet set. + */ + protected Date getAnchorDate() { + return anchor != null ? anchor.getTime() : null; + } + + /** + * Callback from selection model calendar changes. + */ + private void updateCalendar() { + if (!getLocale().equals(model.getLocale())) { + installCalendar(); + superSetLocale(model.getLocale()); + } else { + if (!model.getTimeZone().equals(getTimeZone())) { + updateTimeZone(); + } + if (cal.getMinimalDaysInFirstWeek() != model.getMinimalDaysInFirstWeek()) { + updateMinimalDaysOfFirstWeek(); + } + if (cal.getFirstDayOfWeek() != model.getFirstDayOfWeek()) { + updateFirstDayOfWeek(); + } + } + } + + + /** + * Callback from changing timezone in model. + */ + private void updateTimeZone() { + TimeZone old = getTimeZone(); + TimeZone tz = model.getTimeZone(); + cal.setTimeZone(tz); + anchor.setTimeZone(tz); + setFirstDisplayedDay(anchor.getTime()); + updateTodayFromCurrentTime(); + updateDatesAfterTimeZoneChange(old); + firePropertyChange("timeZone", old, getTimeZone()); + } + + /** + * All dates are "cleaned" relative to the timezone they had been set. + * After changing the timezone, they need to be updated to the new. + * + * Here: clear everything. + * + * @param oldTimeZone the timezone before the change + */ + protected void updateDatesAfterTimeZoneChange(TimeZone oldTimeZone) { + SortedSet flagged = getFlaggedDates(); + flaggedDates.setTimeZone(getTimeZone()); + firePropertyChange("flaggedDates", flagged, getFlaggedDates()); + } + + /** + * Call back from listening to model firstDayOfWeek change. + */ + private void updateFirstDayOfWeek() { + int oldFirstDayOfWeek = this.firstDayOfWeek; + + firstDayOfWeek = getSelectionModel().getFirstDayOfWeek(); + cal.setFirstDayOfWeek(firstDayOfWeek); + anchor.setFirstDayOfWeek(firstDayOfWeek); + firePropertyChange("firstDayOfWeek", oldFirstDayOfWeek, firstDayOfWeek); + } + + /** + * Call back from listening to model minimalDaysOfFirstWeek change. + *

+ * NOTE: this is not a property as we have no public api to change + * it on JXMonthView. + */ + private void updateMinimalDaysOfFirstWeek() { + cal.setMinimalDaysInFirstWeek(model.getMinimalDaysInFirstWeek()); + anchor.setMinimalDaysInFirstWeek(model.getMinimalDaysInFirstWeek()); + } + + + +//-------------------- scrolling + /** + * Returns the last date able to be displayed. For example, if the last + * visible month was April the time returned would be April 30, 23:59:59. + * + * @return long The last displayed date. + */ + public Date getLastDisplayedDay() { + return getUI().getLastDisplayedDay(); + } + + + /** + * Returns the first displayed date. + * + * @return long The first displayed date. + */ + public Date getFirstDisplayedDay() { + return firstDisplayedDay; + } + + + /** + * Set the first displayed date. We only use the month and year of + * this date. The Calendar.DAY_OF_MONTH field is reset to + * 1 and all other fields, with exception of the year and month, + * are reset to 0. + * + * @param date The first displayed date. + */ + public void setFirstDisplayedDay(Date date) { + anchor.setTime(date); + Date oldDate = getFirstDisplayedDay(); + + cal.setTime(anchor.getTime()); + CalendarUtils.startOfMonth(cal); + firstDisplayedDay = cal.getTime(); + firePropertyChange("firstDisplayedDay", oldDate, getFirstDisplayedDay() ); + } + + /** + * Moves the date into the visible region of the calendar. If + * the date is greater than the last visible date it will become the last + * visible date. While if it is less than the first visible date it will + * become the first visible date.

+ * + * NOTE: this is the recommended method to scroll to a particular date, the + * functionally equivalent method taking a long as parameter will most + * probably be deprecated. + * + * @param date Date to make visible, must not be null. + * @see #ensureDateVisible(Date) + */ + public void ensureDateVisible(Date date) { + if (date.before(firstDisplayedDay)) { + setFirstDisplayedDay(date); + } else { + Date lastDisplayedDate = getLastDisplayedDay(); + if (date.after(lastDisplayedDate)) { + // extract to CalendarUtils! + cal.setTime(date); + int month = cal.get(Calendar.MONTH); + int year = cal.get(Calendar.YEAR); + + cal.setTime(lastDisplayedDate); + int lastMonth = cal.get(Calendar.MONTH); + int lastYear = cal.get(Calendar.YEAR); + + int diffMonths = month - lastMonth + + ((year - lastYear) * MONTHS_IN_YEAR); + + cal.setTime(firstDisplayedDay); + cal.add(Calendar.MONTH, diffMonths); + setFirstDisplayedDay(cal.getTime()); + } + } + } + + + /** + * Returns the Date at the given location. May be null if the + * coordinates don't map to a day in the month which contains the + * coordinates. Specifically: hitting leading/trailing dates returns null. + * + * Mapping pixel to calendar day. + * + * @param x the x position of the location in pixel + * @param y the y position of the location in pixel + * @return the day at the given location or null if the location + * doesn't map to a day in the month which contains the coordinates. + */ + public Date getDayAtLocation(int x, int y) { + return getUI().getDayAtLocation(x, y); + } + +//------------------ today + + /** + * Returns the current Date (whateverthatmeans). Internally always invoked when + * the current default is needed. Introduced mainly for testing, don't override! + * + * This implementation returns a Date instantiated with System.currentTimeInMillis. + * + * @return the date deemed as current. + */ + Date getCurrentDate() { + return new Date(System.currentTimeMillis()); + } + + + /** + * Sets today from the current system time. + * + * temporary widened access for testing. + */ + protected void updateTodayFromCurrentTime() { + setToday(getCurrentDate()); + } + + /** + * Increments today. This is used by the timer. + * + * PENDING: is it safe? doesn't check if we are really tomorrow? + * temporary widened access for testing. + */ + protected void incrementToday() { + cal.setTime(getToday()); + cal.add(Calendar.DAY_OF_MONTH, 1); + setToday(cal.getTime()); + } + + /** + * Sets the date which represents today. Internally + * modified to the start of the day which contains the + * given date in this monthView's calendar coordinates. + * + * temporary widened access for testing. + * + * @param date the date which should be used as today. + */ + protected void setToday(Date date) { + Date oldToday = getToday(); + // PENDING JW: do we really want the start of today? + this.today = startOfDay(date); + firePropertyChange("today", oldToday, getToday()); + } + + /** + * Returns the start of today in this monthviews calendar coordinates. + * + * @return the start of today as Date. + */ + public Date getToday() { + // null only happens in the very first time ... + return today != null ? (Date) today.clone() : null; + } + + + +//---- internal date manipulation ("cleanup" == start of day in monthView's calendar) + + + /** + * Returns the start of the day as Date. + * + * @param date the Date. + * @return start of the given day as Date, relative to this + * monthView's calendar. + * + */ + private Date startOfDay(Date date) { + return CalendarUtils.startOfDay(cal, date); + } + +//------------------- ui delegate + /** + * @inheritDoc + */ + public MonthViewUI getUI() { + return (MonthViewUI)ui; + } + + /** + * Sets the L&F object that renders this component. + * + * @param ui UI to use for this {@code JXMonthView} + */ + public void setUI(MonthViewUI ui) { + super.setUI(ui); + } + + /** + * Resets the UI property with the value from the current look and feel. + * + * @see UIManager#getUI(JComponent) + */ + @Override + public void updateUI() { + setUI((MonthViewUI)LookAndFeelAddons.getUI(this, MonthViewUI.class)); + invalidate(); + } + + /** + * @inheritDoc + */ + @Override + public String getUIClassID() { + return uiClassID; + } + + +//---------------- DateSelectionModel + + /** + * Returns the date selection model which drives this + * JXMonthView. + * + * @return the date selection model + */ + public DateSelectionModel getSelectionModel() { + return model; + } + + /** + * Sets the date selection model to drive this monthView. + * + * @param model the selection model to use, must not be null. + * @throws NullPointerException if model is null + */ + public void setSelectionModel(DateSelectionModel model) { + Contract.asNotNull(model, "date selection model must not be null"); + DateSelectionModel oldModel = getSelectionModel(); + model.removeDateSelectionListener(getDateSelectionListener()); + this.model = model; + installCalendar(); + if (!model.getLocale().equals(getLocale())) { + super.setLocale(model.getLocale()); + } + model.addDateSelectionListener(getDateSelectionListener()); + firePropertyChange(SELECTION_MODEL, oldModel, getSelectionModel()); + } + +//-------------------- delegates to model + + /** + * Clear any selection from the selection model + */ + public void clearSelection() { + getSelectionModel().clearSelection(); + } + + /** + * Return true if the selection is empty, false otherwise + * + * @return true if the selection is empty, false otherwise + */ + public boolean isSelectionEmpty() { + return getSelectionModel().isSelectionEmpty(); + } + + /** + * Get the current selection + * + * @return sorted set of selected dates + */ + public SortedSet getSelection() { + return getSelectionModel().getSelection(); + } + + /** + * Adds the selection interval to the selection model. + * + * @param startDate Start of date range to add to the selection + * @param endDate End of date range to add to the selection + */ + public void addSelectionInterval(Date startDate, Date endDate) { + getSelectionModel().addSelectionInterval(startDate, endDate); + } + + /** + * Sets the selection interval to the selection model. + * + * @param startDate Start of date range to set the selection to + * @param endDate End of date range to set the selection to + */ + public void setSelectionInterval(final Date startDate, final Date endDate) { + getSelectionModel().setSelectionInterval(startDate, endDate); + } + + /** + * Removes the selection interval from the selection model. + * + * @param startDate Start of the date range to remove from the selection + * @param endDate End of the date range to remove from the selection + */ + public void removeSelectionInterval(final Date startDate, final Date endDate) { + getSelectionModel().removeSelectionInterval(startDate, endDate); + } + + /** + * Returns the current selection mode for this JXMonthView. + * + * @return int Selection mode. + */ + public SelectionMode getSelectionMode() { + return getSelectionModel().getSelectionMode(); + } + + /** + * Set the selection mode for this JXMonthView. + + * @param selectionMode The selection mode to use for this {@code JXMonthView} + */ + public void setSelectionMode(final SelectionMode selectionMode) { + getSelectionModel().setSelectionMode(selectionMode); + } + + /** + * Returns the earliest selected date. + * + * + * @return the first Date in the selection or null if empty. + */ + public Date getFirstSelectionDate() { + return getSelectionModel().getFirstSelectionDate(); + } + + + /** + * Returns the earliest selected date. + * + * @return the first Date in the selection or null if empty. + */ + public Date getLastSelectionDate() { + return getSelectionModel().getLastSelectionDate(); + } + + /** + * Returns the earliest selected date. + * + * PENDING JW: keep this? it was introduced before the first/last + * in model. When delegating everything, we duplicate here. + * + * @return the first Date in the selection or null if empty. + */ + public Date getSelectionDate() { + return getFirstSelectionDate(); + } + + /** + * Sets the model's selection to the given date or clears the selection if + * null. + * + * @param newDate the selection date to set + */ + public void setSelectionDate(Date newDate) { + if (newDate == null) { + clearSelection(); + } else { + setSelectionInterval(newDate, newDate); + } + } + + /** + * Returns true if the specified date falls within the _startSelectedDate + * and _endSelectedDate range. + * + * @param date The date to check + * @return true if the date is selected, false otherwise + */ + public boolean isSelected(Date date) { + return getSelectionModel().isSelected(date); + } + + + /** + * Set the lower bound date that is allowed to be selected.

+ * + * + * @param lowerBound the lower bound, null means none. + */ + public void setLowerBound(Date lowerBound) { + getSelectionModel().setLowerBound(lowerBound); + } + + /** + * Set the upper bound date that is allowed to be selected.

+ * + * @param upperBound the upper bound, null means none. + */ + public void setUpperBound(Date upperBound) { + getSelectionModel().setUpperBound(upperBound); + } + + + /** + * Return the lower bound date that is allowed to be selected for this + * model. + * + * @return lower bound date or null if not set + */ + public Date getLowerBound() { + return getSelectionModel().getLowerBound(); + } + + /** + * Return the upper bound date that is allowed to be selected for this + * model. + * + * @return upper bound date or null if not set + */ + public Date getUpperBound() { + return getSelectionModel().getUpperBound(); + } + + /** + * Identifies whether or not the date passed is an unselectable date. + *

+ * + * @param date date which to test for unselectable status + * @return true if the date is unselectable, false otherwise + */ + public boolean isUnselectableDate(Date date) { + return getSelectionModel().isUnselectableDate(date); + } + + /** + * Sets the dates that should be unselectable. This will replace the model's + * current set of unselectable dates. The implication is that calling with + * zero dates will remove all unselectable dates. + *

+ * + * NOTE: neither the given array nor any of its elements must be null. + * + * @param unselectableDates zero or more not-null dates that should be + * unselectable. + * @throws NullPointerException if either the array or any of the elements + * are null + */ + public void setUnselectableDates(Date... unselectableDates) { + Contract.asNotNull(unselectableDates, + "unselectable dates must not be null"); + SortedSet unselectableSet = new TreeSet(); + for (Date unselectableDate : unselectableDates) { + unselectableSet.add(unselectableDate); + } + getSelectionModel().setUnselectableDates(unselectableSet); + // PENDING JW: check that ui does the repaint! + repaint(); + } + + // --------------------- flagged dates + /** + * Identifies whether or not the date passed is a flagged date. + * + * @param date date which to test for flagged status + * @return true if the date is flagged, false otherwise + */ + public boolean isFlaggedDate(Date date) { + if (date == null) + return false; + return flaggedDates.isSelected(date); + } + + /** + * Replace all flags with the given dates.

+ * + * NOTE: neither the given array nor any of its elements should be null. + * Currently, a null array will be tolerated to ease migration. A null + * has the same effect as clearFlaggedDates. + * + * + * @param flagged the dates to be flagged + */ + public void setFlaggedDates(Date... flagged) { +// Contract.asNotNull(flagged, "must not be null"); + SortedSet oldFlagged = getFlaggedDates(); + flaggedDates.clearSelection(); + if (flagged != null) { + for (Date date : flagged) { + flaggedDates.addSelectionInterval(date, date); + } + } + firePropertyChange("flaggedDates", oldFlagged, getFlaggedDates()); + } + /** + * Adds the dates to the flags. + * + * NOTE: neither the given array nor any of its elements should be null. + * Currently, a null array will be tolerated to ease migration. A null + * does nothing. + * + * @param flagged the dates to be flagged + */ + public void addFlaggedDates(Date... flagged) { +// Contract.asNotNull(flagged, "must not be null"); + SortedSet oldFlagged = flaggedDates.getSelection(); + if (flagged != null) { + for (Date date : flagged) { + flaggedDates.addSelectionInterval(date, date); + } + } + firePropertyChange("flaggedDates", oldFlagged, flaggedDates.getSelection()); + } + + /** + * Unflags the given dates. + * + * NOTE: neither the given array nor any of its elements should be null. + * Currently, a null array will be tolerated to ease migration. + * + * @param flagged the dates to be unflagged + */ + public void removeFlaggedDates(Date... flagged) { +// Contract.asNotNull(flagged, "must not be null"); + SortedSet oldFlagged = flaggedDates.getSelection(); + if (flagged != null) { + for (Date date : flagged) { + flaggedDates.removeSelectionInterval(date, date); + } + } + firePropertyChange("flaggedDates", oldFlagged, flaggedDates.getSelection()); + } + /** + * Clears all flagged dates. + * + */ + public void clearFlaggedDates() { + SortedSet oldFlagged = flaggedDates.getSelection(); + flaggedDates.clearSelection(); + firePropertyChange("flaggedDates", oldFlagged, flaggedDates.getSelection()); + } + + /** + * Returns a sorted set of flagged Dates. The returned set is guaranteed to + * be not null, but may be empty. + * + * @return a sorted set of flagged dates. + */ + public SortedSet getFlaggedDates() { + return flaggedDates.getSelection(); + } + + /** + * Returns a boolean indicating if this monthView has flagged dates. + * + * @return a boolean indicating if this monthView has flagged dates. + */ + public boolean hasFlaggedDates() { + return !flaggedDates.isSelectionEmpty(); + } + + +//------------------- visual properties + /** + * Sets a boolean property indicating whether or not to show leading dates + * for a months displayed by this component.

+ * + * The default value is false. + * + * @param value true if leading dates should be displayed, false otherwise. + */ + public void setShowingLeadingDays(boolean value) { + boolean old = isShowingLeadingDays(); + leadingDays = value; + firePropertyChange("showingLeadingDays", old, isShowingLeadingDays()); + } + + /** + * Returns a boolean indicating whether or not we're showing leading dates. + * + * @return true if leading dates are shown, false otherwise. + */ + public boolean isShowingLeadingDays() { + return leadingDays; + } + + /** + * Sets a boolean property indicating whether or not to show + * trailing dates for the months displayed by this component.

+ * + * The default value is false. + * + * @param value true if trailing dates should be displayed, false otherwise. + */ + public void setShowingTrailingDays(boolean value) { + boolean old = isShowingTrailingDays(); + trailingDays = value; + firePropertyChange("showingTrailingDays", old, isShowingTrailingDays()); + } + + /** + * Returns a boolean indicating whether or not we're showing trailing dates. + * + * @return true if trailing dates are shown, false otherwise. + */ + public boolean isShowingTrailingDays() { + return trailingDays; + } + + /** + * Returns whether or not the month view supports traversing months. + * If zoomable is enabled, traversable is enabled as well. Otherwise + * returns the traversable property as set by client code. + * + * @return true if month traversing is enabled. + * @see #setZoomable(boolean) + */ + public boolean isTraversable() { + if (isZoomable()) return true; + return traversable; + } + + /** + * Set whether or not the month view will display buttons to allow the user + * to traverse to previous or next months.

+ * + * The default value is false.

+ * + * PENDING JW: fire the "real" property or the compound with zoomable? + * + * @param traversable set to true to enable month traversing, false + * otherwise. + * @see #isTraversable() + * @see #setZoomable(boolean) + */ + public void setTraversable(boolean traversable) { + boolean old = isTraversable(); + this.traversable = traversable; + firePropertyChange(TRAVERSABLE, old, isTraversable()); + } + + /** + * Returns true if zoomable (through date ranges). + * + * @return true if zoomable is enabled. + * @see #setZoomable(boolean) + */ + public boolean isZoomable() { + return zoomable; + } + + /** + * Sets the zoomable property. If true, the calendar's date range can + * be zoomed. This state implies that the calendar is traversable and + * showing exactly one calendar box, effectively ignoring the properties.

+ * + * Note: The actual zoomable behaviour is not yet implemented. + * + * @param zoomable a boolean indicating whether or not zooming date + * ranges is enabled. + * + * @see #setTraversable(boolean) + */ + public void setZoomable(boolean zoomable) { + boolean old = isZoomable(); + this.zoomable = zoomable; + firePropertyChange("zoomable", old, isZoomable()); + } + + /** + * Returns whether or not this JXMonthView should display + * week number. + * + * @return true if week numbers should be displayed + */ + public boolean isShowingWeekNumber() { + return showWeekNumber; + } + + /** + * Set whether or not this JXMonthView will display week + * numbers or not. + * + * @param showWeekNumber true if week numbers should be displayed, + * false otherwise + */ + public void setShowingWeekNumber(boolean showWeekNumber) { + boolean old = isShowingWeekNumber(); + this.showWeekNumber = showWeekNumber; + firePropertyChange("showingWeekNumber", old, isShowingWeekNumber()); + } + + /** + * Sets the String representation for each day of the week as used + * in the header of the day's grid. For + * this method the first days of the week days[0] is assumed to be + * Calendar.SUNDAY. If null, the representation provided + * by the MonthViewUI is used. + * + * The default value is the representation as + * returned from the MonthViewUI. + * + * @param days Array of characters that represents each day + * @throws IllegalArgumentException if not null and days.length != + * DAYS_IN_WEEK + */ + public void setDaysOfTheWeek(String[] days) { + if ((days != null) && (days.length != DAYS_IN_WEEK)) { + throw new IllegalArgumentException( + "Array of days is not of length " + DAYS_IN_WEEK + + " as expected."); + } + + String[] oldValue = getDaysOfTheWeek(); + _daysOfTheWeek = days; + firePropertyChange(DAYS_OF_THE_WEEK, oldValue, days); + } + + /** + * Returns the String representation for each day of the + * week. + * + * @return String representation for the days of the week, guaranteed to + * never be null. + * + * @see #setDaysOfTheWeek(String[]) + * @see MonthViewUI + */ + public String[] getDaysOfTheWeek() { + if (_daysOfTheWeek != null) { + String[] days = new String[DAYS_IN_WEEK]; + System.arraycopy(_daysOfTheWeek, 0, days, 0, DAYS_IN_WEEK); + return days; + } + return getUI().getDaysOfTheWeek(); + } + + /** + * + * @param dayOfWeek + * @return String representation of day of week. + */ + public String getDayOfTheWeek(int dayOfWeek) { + return getDaysOfTheWeek()[dayOfWeek - 1]; + } + + + /** + * Returns the padding used between days in the calendar. + * + * @return Padding used between days in the calendar + */ + public int getBoxPaddingX() { + return boxPaddingX; + } + + /** + * Sets the number of pixels used to pad the left and right side of a day. + * The padding is applied to both sides of the days. Therefore, if you + * used the padding value of 3, the number of pixels between any two days + * would be 6. + * + * @param boxPaddingX Number of pixels applied to both sides of a day + */ + public void setBoxPaddingX(int boxPaddingX) { + int oldBoxPadding = getBoxPaddingX(); + this.boxPaddingX = boxPaddingX; + firePropertyChange(BOX_PADDING_X, oldBoxPadding, getBoxPaddingX()); + } + + /** + * Returns the padding used above and below days in the calendar. + * + * @return Padding used between dats in the calendar + */ + public int getBoxPaddingY() { + return boxPaddingY; + } + + /** + * Sets the number of pixels used to pad the top and bottom of a day. + * The padding is applied to both the top and bottom of a day. Therefore, + * if you used the padding value of 3, the number of pixels between any + * two days would be 6. + * + * @param boxPaddingY Number of pixels applied to top and bottom of a day + */ + public void setBoxPaddingY(int boxPaddingY) { + int oldBoxPadding = getBoxPaddingY(); + this.boxPaddingY = boxPaddingY; + firePropertyChange(BOX_PADDING_Y, oldBoxPadding, getBoxPaddingY()); + } + + + /** + * Returns the selected background color. + * + * @return the selected background color. + */ + public Color getSelectionBackground() { + return selectedBackground; + } + + /** + * Sets the selected background color to c. The default color + * is installed by the ui. + * + * @param c Selected background. + */ + public void setSelectionBackground(Color c) { + Color old = getSelectionBackground(); + selectedBackground = c; + firePropertyChange("selectionBackground", old, getSelectionBackground()); + } + + /** + * Returns the selected foreground color. + * + * @return the selected foreground color. + */ + public Color getSelectionForeground() { + return selectedForeground; + } + + /** + * Sets the selected foreground color to c. The default color + * is installed by the ui. + * + * @param c Selected foreground. + */ + public void setSelectionForeground(Color c) { + Color old = getSelectionForeground(); + selectedForeground = c; + firePropertyChange("selectionForeground", old, getSelectionForeground()); + } + + + /** + * Returns the color used when painting the today background. + * + * @return Color Color + */ + public Color getTodayBackground() { + return todayBackgroundColor; + } + + /** + * Sets the color used to draw the bounding box around today. The default + * is the background of the JXMonthView component. + * + * @param c color to set + */ + public void setTodayBackground(Color c) { + Color oldValue = getTodayBackground(); + todayBackgroundColor = c; + firePropertyChange("todayBackground", oldValue, getTodayBackground()); + // PENDING JW: remove repaint, ui must take care of it + repaint(); + } + + /** + * Returns the color used to paint the month string background. + * + * @return Color Color. + */ + public Color getMonthStringBackground() { + return monthStringBackground; + } + + /** + * Sets the color used to draw the background of the month string. The + * default is 138, 173, 209 (Blue-ish). + * + * @param c color to set + */ + public void setMonthStringBackground(Color c) { + Color old = getMonthStringBackground(); + monthStringBackground = c; + firePropertyChange("monthStringBackground", old, getMonthStringBackground()); + // PENDING JW: remove repaint, ui must take care of it + repaint(); + } + + /** + * Returns the color used to paint the month string foreground. + * + * @return Color Color. + */ + public Color getMonthStringForeground() { + return monthStringForeground; + } + + /** + * Sets the color used to draw the foreground of the month string. The + * default is Color.WHITE. + * + * @param c color to set + */ + public void setMonthStringForeground(Color c) { + Color old = getMonthStringForeground(); + monthStringForeground = c; + firePropertyChange("monthStringForeground", old, getMonthStringForeground()); + // PENDING JW: remove repaint, ui must take care of it + repaint(); + } + + /** + * Sets the color used to draw the foreground of each day of the week. These + * are the titles + * + * @param c color to set + */ + public void setDaysOfTheWeekForeground(Color c) { + Color old = getDaysOfTheWeekForeground(); + daysOfTheWeekForeground = c; + firePropertyChange("daysOfTheWeekForeground", old, getDaysOfTheWeekForeground()); + } + + /** + * @return Color Color + */ + public Color getDaysOfTheWeekForeground() { + return daysOfTheWeekForeground; + } + + /** + * Set the color to be used for painting the specified day of the week. + * Acceptable values are Calendar.SUNDAY - Calendar.SATURDAY.

+ * + * PENDING JW: this is not a property - should it be and + * fire a change notification? If so, how? + * + * + * @param dayOfWeek constant value defining the day of the week. + * @param c The color to be used for painting the numeric day of the week. + */ + public void setDayForeground(int dayOfWeek, Color c) { + if ((dayOfWeek < Calendar.SUNDAY) || (dayOfWeek > Calendar.SATURDAY)) { + throw new IllegalArgumentException("dayOfWeek must be in [Calendar.SUNDAY ... " + + "Calendar.SATURDAY] but was " + dayOfWeek); + } + dayToColorTable.put(dayOfWeek, c); + repaint(); + } + + /** + * Return the color that should be used for painting the numerical day of the week. + * + * @param dayOfWeek The day of week to get the color for. + * @return The color to be used for painting the numeric day of the week. + * If this was no color has yet been defined the component foreground color + * will be returned. + */ + public Color getDayForeground(int dayOfWeek) { + Color c; + c = dayToColorTable.get(dayOfWeek); + if (c == null) { + c = getForeground(); + } + return c; + } + + /** + * Return the color that should be used for painting the numerical day of the week. + * + * @param dayOfWeek The day of week to get the color for. + * @return The color to be used for painting the numeric day of the week or null + * If no color has yet been defined. + */ + public Color getPerDayOfWeekForeground(int dayOfWeek) { + return dayToColorTable.get(dayOfWeek); + } + /** + * Set the color to be used for painting the foreground of a flagged day. + * + * @param c The color to be used for painting. + */ + public void setFlaggedDayForeground(Color c) { + Color old = getFlaggedDayForeground(); + flaggedDayForeground = c; + firePropertyChange("flaggedDayForeground", old, getFlaggedDayForeground()); + } + + /** + * Return the color that should be used for painting the foreground of the flagged day. + * + * @return The color to be used for painting + */ + public Color getFlaggedDayForeground() { + return flaggedDayForeground; + } + + /** + * Returns a copy of the insets used to paint the month string background. + * + * @return Insets Month string insets. + */ + public Insets getMonthStringInsets() { + return (Insets) _monthStringInsets.clone(); + } + + /** + * Insets used to modify the width/height when painting the background + * of the month string area. + * + * @param insets Insets + */ + public void setMonthStringInsets(Insets insets) { + Insets old = getMonthStringInsets(); + if (insets == null) { + _monthStringInsets.top = 0; + _monthStringInsets.left = 0; + _monthStringInsets.bottom = 0; + _monthStringInsets.right = 0; + } else { + _monthStringInsets.top = insets.top; + _monthStringInsets.left = insets.left; + _monthStringInsets.bottom = insets.bottom; + _monthStringInsets.right = insets.right; + } + firePropertyChange("monthStringInsets", old, getMonthStringInsets()); + // PENDING JW: remove repaint, ui must take care of it + repaint(); + } + + /** + * Returns the preferred number of columns to paint calendars in. + *

+ * @return int preferred number of columns of calendars. + * + * @see #setPreferredColumnCount(int) + */ + public int getPreferredColumnCount() { + return minCalCols; + } + + /** + * Sets the preferred number of columns of calendars. Does nothing if cols + * <= 0. The default value is 1. + *

+ * @param cols The number of columns of calendars. + * + * @see #getPreferredColumnCount() + */ + public void setPreferredColumnCount(int cols) { + if (cols <= 0) { + return; + } + int old = getPreferredColumnCount(); + minCalCols = cols; + firePropertyChange("preferredColumnCount", old, getPreferredColumnCount()); + // PENDING JW: remove revalidate/repaint, ui must take care of it + revalidate(); + repaint(); + } + + + /** + * Returns the preferred number of rows to paint calendars in. + *

+ * @return int Rows of calendars. + * + * @see #setPreferredRowCount(int) + */ + public int getPreferredRowCount() { + return minCalRows; + } + + /** + * Sets the preferred number of rows to paint calendars.Does nothing if rows + * <= 0. The default value is 1. + *

+ * + * @param rows The number of rows of calendars. + * + * @see #getPreferredRowCount() + */ + public void setPreferredRowCount(int rows) { + if (rows <= 0) { + return; + } + int old = getPreferredRowCount(); + minCalRows = rows; + firePropertyChange("preferredRowCount", old, getPreferredRowCount()); + // PENDING JW: remove revalidate/repaint, ui must take care of it + revalidate(); + repaint(); + } + + + /** + * {@inheritDoc} + */ + @Override + public void removeNotify() { + if (todayTimer != null) { + todayTimer.stop(); + } + super.removeNotify(); + } + + /** + * {@inheritDoc} + */ + @Override + public void addNotify() { + super.addNotify(); + // partial fix for #1125: today updated in addNotify + // partial, because still not in synch if not shown + updateTodayFromCurrentTime(); + // Setup timer to update the value of today. + int secondsTillTomorrow = 86400; + + if (todayTimer == null) { + todayTimer = new Timer(secondsTillTomorrow * 1000, + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + incrementToday(); + } + }); + } + + // Modify the initial delay by the current time. +// cal.setTimeInMillis(System.currentTimeMillis()); + cal.setTime(getCurrentDate()); + secondsTillTomorrow = secondsTillTomorrow - + (cal.get(Calendar.HOUR_OF_DAY) * 3600) - + (cal.get(Calendar.MINUTE) * 60) - + cal.get(Calendar.SECOND); + todayTimer.setInitialDelay(secondsTillTomorrow * 1000); + todayTimer.start(); + } + +//-------------------- action and listener + + + /** + * Commits the current selection.

+ * + * Resets the model's adjusting property to false + * and fires an ActionEvent + * with the COMMIT_KEY action command. + * + * + * @see #cancelSelection() + * @see DateSelectionModel#setAdjusting(boolean) + */ + public void commitSelection() { + getSelectionModel().setAdjusting(false); + fireActionPerformed(COMMIT_KEY); + } + + /** + * Cancels the selection.

+ * + * Resets the model's adjusting property to + * false and fires an ActionEvent with the CANCEL_KEY action command. + * + * @see #commitSelection + * @see DateSelectionModel#setAdjusting(boolean) + */ + public void cancelSelection() { + getSelectionModel().setAdjusting(false); + fireActionPerformed(CANCEL_KEY); + } + + /** + * Sets the component input map enablement property.

+ * + * If enabled, the keybinding for WHEN_IN_FOCUSED_WINDOW are + * installed, otherwise not. Changing this property will + * install/clear the corresponding key bindings. Typically, clients + * which want to use the monthview in a popup, should enable these.

+ * + * The default value is false. + * + * @param enabled boolean to indicate whether the component + * input map should be enabled. + * @see #isComponentInputMapEnabled() + */ + public void setComponentInputMapEnabled(boolean enabled) { + boolean old = isComponentInputMapEnabled(); + this.componentInputMapEnabled = enabled; + firePropertyChange("componentInputMapEnabled", old, isComponentInputMapEnabled()); + } + + /** + * Returns the componentInputMapEnabled property. + * + * @return a boolean indicating whether the component input map is + * enabled. + * @see #setComponentInputMapEnabled(boolean) + * + */ + public boolean isComponentInputMapEnabled() { + return componentInputMapEnabled; + } + + /** + * Adds an ActionListener. + *

+ * The ActionListener will receive an ActionEvent with its actionCommand + * set to COMMIT_KEY or CANCEL_KEY after the selection has been committed + * or canceled, respectively. + *

+ * + * Note that actionEvents are typically fired after a dedicated user gesture + * to end an ongoing selectin (like ENTER, ESCAPE) or after explicit programmatic + * commits/cancels. It is usually not fired after each change to the selection state. + * Client code which wants to be notified about all selection changes should + * register a DateSelectionListener to the DateSelectionModel. + * + * @param l The ActionListener that is to be notified + * + * @see #commitSelection() + * @see #cancelSelection() + * @see #getSelectionModel() + */ + public void addActionListener(ActionListener l) { + listenerMap.add(ActionListener.class, l); + } + + /** + * Removes an ActionListener. + * + * @param l The ActionListener to remove. + */ + public void removeActionListener(ActionListener l) { + listenerMap.remove(ActionListener.class, l); + } + + @Override + @SuppressWarnings("unchecked") + public T[] getListeners(Class listenerType) { + java.util.List listeners = listenerMap.getListeners(listenerType); + T[] result; + if (!listeners.isEmpty()) { + //noinspection unchecked + result = (T[]) java.lang.reflect.Array.newInstance(listenerType, listeners.size()); + result = listeners.toArray(result); + } else { + result = super.getListeners(listenerType); + } + return result; + } + + /** + * Creates and fires an ActionEvent with the given action + * command to all listeners. + * + * @param actionCommand the command for the created. + */ + protected void fireActionPerformed(String actionCommand) { + ActionListener[] listeners = getListeners(ActionListener.class); + ActionEvent e = null; + + for (ActionListener listener : listeners) { + if (e == null) { + e = new ActionEvent(JXMonthView.this, + ActionEvent.ACTION_PERFORMED, + actionCommand); + } + listener.actionPerformed(e); + } + } + + +//--- deprecated code - NOTE: these methods will be removed soon! + + /** + * @deprecated pre-0.9.5 - this is kept as a reminder only, don't + * use! we can make this private or comment it out after + * next version + */ + @Deprecated + protected void cleanupWeekSelectionDates(Date startDate, Date endDate) { + int count = 1; + cal.setTime(startDate); + while (cal.getTimeInMillis() < endDate.getTime()) { + cal.add(Calendar.DAY_OF_MONTH, 1); + count++; + } + + if (count > JXMonthView.DAYS_IN_WEEK) { + // Move the start date to the first day of the week. + cal.setTime(startDate); + int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK); + int firstDayOfWeek = getFirstDayOfWeek(); + int daysFromStart = dayOfWeek - firstDayOfWeek; + if (daysFromStart < 0) { + daysFromStart += JXMonthView.DAYS_IN_WEEK; + } + cal.add(Calendar.DAY_OF_MONTH, -daysFromStart); + + modifiedStartDate = cal.getTime(); + + // Move the end date to the last day of the week. + cal.setTime(endDate); + dayOfWeek = cal.get(Calendar.DAY_OF_WEEK); + int lastDayOfWeek = firstDayOfWeek - 1; + if (lastDayOfWeek == 0) { + lastDayOfWeek = Calendar.SATURDAY; + } + int daysTillEnd = lastDayOfWeek - dayOfWeek; + if (daysTillEnd < 0) { + daysTillEnd += JXMonthView.DAYS_IN_WEEK; + } + cal.add(Calendar.DAY_OF_MONTH, daysTillEnd); + modifiedEndDate = cal.getTime(); + } + } + + + + + +} diff --git a/src/main/java/org/jdesktop/swingx/JXMultiSplitPane.java b/src/main/java/org/jdesktop/swingx/JXMultiSplitPane.java new file mode 100644 index 0000000000..cf07cb11f4 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXMultiSplitPane.java @@ -0,0 +1,590 @@ +/* + * $Id: JXMultiSplitPane.java 4147 2012-02-01 17:13:24Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.MultiSplitLayout.Divider; +import org.jdesktop.swingx.MultiSplitLayout.Node; +import org.jdesktop.swingx.painter.AbstractPainter; +import org.jdesktop.swingx.painter.Painter; + +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; +import javax.swing.*; +import javax.swing.event.MouseInputAdapter; +import java.awt.*; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.event.MouseEvent; +import java.io.Serializable; + +/** + * + *

+ * All properties in this class are bound: when a properties value + * is changed, all PropertyChangeListeners are fired. + * + * @author Hans Muller + * @author Luan O'Carroll + */ +@JavaBean +public class JXMultiSplitPane extends JPanel implements BackgroundPaintable { + private AccessibleContext accessibleContext = null; + private boolean continuousLayout = true; + private DividerPainter dividerPainter = new DefaultDividerPainter(); + private Painter backgroundPainter; + private boolean paintBorderInsets; + + /** + * Creates a MultiSplitPane with it's LayoutManager set to + * to an empty MultiSplitLayout. + */ + public JXMultiSplitPane() { + this(new MultiSplitLayout()); + } + + /** + * Creates a MultiSplitPane. + * @param layout the new split pane's layout + */ + public JXMultiSplitPane( MultiSplitLayout layout ) { + super(layout); + InputHandler inputHandler = new InputHandler(); + addMouseListener(inputHandler); + addMouseMotionListener(inputHandler); + addKeyListener(inputHandler); + setFocusable(true); + } + + /** + * A convenience method that returns the layout manager cast + * to MutliSplitLayout. + * + * @return this MultiSplitPane's layout manager + * @see java.awt.Container#getLayout + * @see #setModel + */ + public final MultiSplitLayout getMultiSplitLayout() { + return (MultiSplitLayout)getLayout(); + } + + /** + * A convenience method that sets the MultiSplitLayout model. + * Equivalent to getMultiSplitLayout.setModel(model) + * + * @param model the root of the MultiSplitLayout model + * @see #getMultiSplitLayout + * @see MultiSplitLayout#setModel + */ + public final void setModel(Node model) { + getMultiSplitLayout().setModel(model); + } + + /** + * A convenience method that sets the MultiSplitLayout dividerSize + * property. Equivalent to + * getMultiSplitLayout().setDividerSize(newDividerSize). + * + * @param dividerSize the value of the dividerSize property + * @see #getMultiSplitLayout + * @see MultiSplitLayout#setDividerSize + */ + public final void setDividerSize(int dividerSize) { + getMultiSplitLayout().setDividerSize(dividerSize); + } + + /** + * A convenience method that returns the MultiSplitLayout dividerSize + * property. Equivalent to + * getMultiSplitLayout().getDividerSize(). + * + * @see #getMultiSplitLayout + * @see MultiSplitLayout#getDividerSize + */ + public final int getDividerSize() { + return getMultiSplitLayout().getDividerSize(); + } + + /** + * Sets the value of the continuousLayout property. + * If true, then the layout is revalidated continuously while + * a divider is being moved. The default value of this property + * is true. + * + * @param continuousLayout value of the continuousLayout property + * @see #isContinuousLayout + */ + public void setContinuousLayout(boolean continuousLayout) { + boolean oldContinuousLayout = isContinuousLayout(); + this.continuousLayout = continuousLayout; + firePropertyChange("continuousLayout", oldContinuousLayout, isContinuousLayout()); + } + + /** + * Returns true if dragging a divider only updates + * the layout when the drag gesture ends (typically, when the + * mouse button is released). + * + * @return the value of the continuousLayout property + * @see #setContinuousLayout + */ + public boolean isContinuousLayout() { + return continuousLayout; + } + + /** + * Returns the Divider that's currently being moved, typically + * because the user is dragging it, or null. + * + * @return the Divider that's being moved or null. + */ + public Divider activeDivider() { + return dragDivider; + } + + /** + * Draws a single Divider. Typically used to specialize the + * way the active Divider is painted. + * + * @see #getDividerPainter + * @see #setDividerPainter + */ + public static abstract class DividerPainter extends AbstractPainter { + } + + private class DefaultDividerPainter extends DividerPainter implements Serializable { + @Override + protected void doPaint(Graphics2D g, Divider divider, int width, int height) { + if ((divider == activeDivider()) && !isContinuousLayout()) { + g.setColor(Color.black); + g.fillRect(0, 0, width, height); + } + } + } + + /** + * The DividerPainter that's used to paint Dividers on this MultiSplitPane. + * This property may be null. + * + * @return the value of the dividerPainter Property + * @see #setDividerPainter + */ + public DividerPainter getDividerPainter() { + return dividerPainter; + } + + /** + * Sets the DividerPainter that's used to paint Dividers on this + * MultiSplitPane. The default DividerPainter only draws + * the activeDivider (if there is one) and then, only if + * continuousLayout is false. The value of this property is + * used by the paintChildren method: Dividers are painted after + * the MultiSplitPane's children have been rendered so that + * the activeDivider can appear "on top of" the children. + * + * @param dividerPainter the value of the dividerPainter property, can be null + * @see #paintChildren + * @see #activeDivider + */ + public void setDividerPainter(DividerPainter dividerPainter) { + DividerPainter old = getDividerPainter(); + this.dividerPainter = dividerPainter; + firePropertyChange("dividerPainter", old, getDividerPainter()); + } + + /** + * Calls the UI delegate's paint method, if the UI delegate + * is non-null. We pass the delegate a copy of the + * Graphics object to protect the rest of the + * paint code from irrevocable changes + * (for example, Graphics.translate). + *

+ * If you override this in a subclass you should not make permanent + * changes to the passed in Graphics. For example, you + * should not alter the clip Rectangle or modify the + * transform. If you need to do these operations you may find it + * easier to create a new Graphics from the passed in + * Graphics and manipulate it. Further, if you do not + * invoker super's implementation you must honor the opaque property, + * that is + * if this component is opaque, you must completely fill in the background + * in a non-opaque color. If you do not honor the opaque property you + * will likely see visual artifacts. + *

+ * The passed in Graphics object might + * have a transform other than the identify transform + * installed on it. In this case, you might get + * unexpected results if you cumulatively apply + * another transform. + * + * @param g the Graphics object to protect + * @see #paint(Graphics) + * @see javax.swing.plaf.ComponentUI + */ + @Override + protected void paintComponent(Graphics g) + { + if (backgroundPainter == null) { + super.paintComponent(g); + } else { + if (isOpaque()) { + super.paintComponent(g); + } + + Graphics2D g2 = (Graphics2D) g.create(); + + try { + SwingXUtilities.paintBackground(this, g2); + } finally { + g2.dispose(); + } + + getUI().paint(g, this); + } + } + + /** + * Specifies a Painter to use to paint the background of this JXPanel. + * If p is not null, then setOpaque(false) will be called + * as a side effect. A component should not be opaque if painters are + * being used, because Painters may paint transparent pixels or not + * paint certain pixels, such as around the border insets. + */ + @Override + public void setBackgroundPainter(Painter p) + { + Painter old = getBackgroundPainter(); + this.backgroundPainter = p; + + if (p != null) { + setOpaque(false); + } + + firePropertyChange("backgroundPainter", old, getBackgroundPainter()); + repaint(); + } + + @Override + public Painter getBackgroundPainter() { + return backgroundPainter; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isPaintBorderInsets() { + return paintBorderInsets; + } + + /** + * {@inheritDoc} + */ + @Override + public void setPaintBorderInsets(boolean paintBorderInsets) { + boolean oldValue = isPaintBorderInsets(); + this.paintBorderInsets = paintBorderInsets; + firePropertyChange("paintBorderInsets", oldValue, isPaintBorderInsets()); + } + + /** + * Uses the DividerPainter (if any) to paint each Divider that + * overlaps the clip Rectangle. This is done after the call to + * super.paintChildren() so that Dividers can be + * rendered "on top of" the children. + *

+ * {@inheritDoc} + */ + @Override + protected void paintChildren(Graphics g) { + super.paintChildren(g); + DividerPainter dp = getDividerPainter(); + Rectangle clipR = g.getClipBounds(); + if ((dp != null) && (clipR != null)) { + MultiSplitLayout msl = getMultiSplitLayout(); + if ( msl.hasModel()) { + for(Divider divider : msl.dividersThatOverlap(clipR)) { + Rectangle bounds = divider.getBounds(); + Graphics cg = g.create( bounds.x, bounds.y, bounds.width, bounds.height ); + try { + dp.paint((Graphics2D)cg, divider, bounds.width, bounds.height ); + } finally { + cg.dispose(); + } + } + } + } + } + + private boolean dragUnderway = false; + private Divider dragDivider = null; + private Rectangle initialDividerBounds = null; + private boolean oldFloatingDividers = true; + private int dragOffsetX = 0; + private int dragOffsetY = 0; + private int dragMin = -1; + private int dragMax = -1; + + private void startDrag(int mx, int my) { + requestFocusInWindow(); + MultiSplitLayout msl = getMultiSplitLayout(); + Divider divider = msl.dividerAt(mx, my); + if (divider != null) { + Node prevNode = divider.previousSibling(); + Node nextNode = divider.nextSibling(); + if ((prevNode == null) || (nextNode == null)) { + dragUnderway = false; + } + else { + initialDividerBounds = divider.getBounds(); + dragOffsetX = mx - initialDividerBounds.x; + dragOffsetY = my - initialDividerBounds.y; + dragDivider = divider; + + Rectangle prevNodeBounds = prevNode.getBounds(); + Rectangle nextNodeBounds = nextNode.getBounds(); + if (dragDivider.isVertical()) { + dragMin = prevNodeBounds.x; + dragMax = nextNodeBounds.x + nextNodeBounds.width; + dragMax -= dragDivider.getBounds().width; + if ( msl.getLayoutMode() == MultiSplitLayout.USER_MIN_SIZE_LAYOUT ) + dragMax -= msl.getUserMinSize(); + } + else { + dragMin = prevNodeBounds.y; + dragMax = nextNodeBounds.y + nextNodeBounds.height; + dragMax -= dragDivider.getBounds().height; + if ( msl.getLayoutMode() == MultiSplitLayout.USER_MIN_SIZE_LAYOUT ) + dragMax -= msl.getUserMinSize(); + } + + if ( msl.getLayoutMode() == MultiSplitLayout.USER_MIN_SIZE_LAYOUT ) { + dragMin = dragMin + msl.getUserMinSize(); + } + else { + if (dragDivider.isVertical()) { + dragMin = Math.max( dragMin, dragMin + getMinNodeSize(msl,prevNode).width ); + dragMax = Math.min( dragMax, dragMax - getMinNodeSize(msl,nextNode).width ); + + Dimension maxDim = getMaxNodeSize(msl,prevNode); + if ( maxDim != null ) + dragMax = Math.min( dragMax, prevNodeBounds.x + maxDim.width ); + } + else { + dragMin = Math.max( dragMin, dragMin + getMinNodeSize(msl,prevNode).height ); + dragMax = Math.min( dragMax, dragMax - getMinNodeSize(msl,nextNode).height ); + + Dimension maxDim = getMaxNodeSize(msl,prevNode); + if ( maxDim != null ) + dragMax = Math.min( dragMax, prevNodeBounds.y + maxDim.height ); + } + } + + oldFloatingDividers = getMultiSplitLayout().getFloatingDividers(); + getMultiSplitLayout().setFloatingDividers(false); + dragUnderway = true; + } + } + else { + dragUnderway = false; + } + } + + /** + * Set the maximum node size. This method can be overridden to limit the + * size of a node during a drag operation on a divider. When implementing + * this method in a subclass the node instance should be checked, for + * example: + * + * class MyMultiSplitPane extends JXMultiSplitPane + * { + * protected Dimension getMaxNodeSize( MultiSplitLayout msl, Node n ) + * { + * if (( n instanceof Leaf ) && ((Leaf)n).getName().equals( "top" )) + * return msl.maximumNodeSize( n ); + * return null; + * } + * } + * + * @param msl the MultiSplitLayout used by this pane + * @param n the node being resized + * @return the maximum size or null (by default) to ignore the maximum size. + */ + protected Dimension getMaxNodeSize( MultiSplitLayout msl, Node n ) { + return null; + } + + /** + * Set the minimum node size. This method can be overridden to limit the + * size of a node during a drag operation on a divider. + * @param msl the MultiSplitLayout used by this pane + * @param n the node being resized + * @return the maximum size or null (by default) to ignore the maximum size. + */ + protected Dimension getMinNodeSize( MultiSplitLayout msl, Node n ) { + return msl.minimumNodeSize(n); + } + + private void repaintDragLimits() { + Rectangle damageR = dragDivider.getBounds(); + if (dragDivider.isVertical()) { + damageR.x = dragMin; + damageR.width = dragMax - dragMin; + } + else { + damageR.y = dragMin; + damageR.height = dragMax - dragMin; + } + repaint(damageR); + } + + private void updateDrag(int mx, int my) { + if (!dragUnderway) { + return; + } + Rectangle oldBounds = dragDivider.getBounds(); + Rectangle bounds = new Rectangle(oldBounds); + if (dragDivider.isVertical()) { + bounds.x = mx - dragOffsetX; + bounds.x = Math.max(bounds.x, dragMin ); + bounds.x = Math.min(bounds.x, dragMax); + } + else { + bounds.y = my - dragOffsetY; + bounds.y = Math.max(bounds.y, dragMin ); + bounds.y = Math.min(bounds.y, dragMax); + } + dragDivider.setBounds(bounds); + if (isContinuousLayout()) { + revalidate(); + repaintDragLimits(); + } + else { + repaint(oldBounds.union(bounds)); + } + } + + private void clearDragState() { + dragDivider = null; + initialDividerBounds = null; + oldFloatingDividers = true; + dragOffsetX = dragOffsetY = 0; + dragMin = dragMax = -1; + dragUnderway = false; + } + + private void finishDrag(int x, int y) { + if (dragUnderway) { + clearDragState(); + if (!isContinuousLayout()) { + revalidate(); + repaint(); + } + } + setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + + private void cancelDrag() { + if (dragUnderway) { + dragDivider.setBounds(initialDividerBounds); + getMultiSplitLayout().setFloatingDividers(oldFloatingDividers); + setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + repaint(); + revalidate(); + clearDragState(); + } + } + + private void updateCursor(int x, int y, boolean show) { + if (dragUnderway) { + return; + } + int cursorID = Cursor.DEFAULT_CURSOR; + if (show) { + Divider divider = getMultiSplitLayout().dividerAt(x, y); + if (divider != null) { + cursorID = (divider.isVertical()) ? + Cursor.E_RESIZE_CURSOR : + Cursor.N_RESIZE_CURSOR; + } + } + setCursor(Cursor.getPredefinedCursor(cursorID)); + } + + + private class InputHandler extends MouseInputAdapter implements KeyListener { + + @Override + public void mouseEntered(MouseEvent e) { + updateCursor(e.getX(), e.getY(), true); + } + + @Override + public void mouseMoved(MouseEvent e) { + updateCursor(e.getX(), e.getY(), true); + } + + @Override + public void mouseExited(MouseEvent e) { + updateCursor(e.getX(), e.getY(), false); + } + + @Override + public void mousePressed(MouseEvent e) { + startDrag(e.getX(), e.getY()); + } + @Override + public void mouseReleased(MouseEvent e) { + finishDrag(e.getX(), e.getY()); + } + @Override + public void mouseDragged(MouseEvent e) { + updateDrag(e.getX(), e.getY()); + } + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { + cancelDrag(); + } + } + @Override + public void keyReleased(KeyEvent e) { } + + @Override + public void keyTyped(KeyEvent e) { } + } + + @Override + public AccessibleContext getAccessibleContext() { + if( accessibleContext == null ) { + accessibleContext = new AccessibleMultiSplitPane(); + } + return accessibleContext; + } + + protected class AccessibleMultiSplitPane extends AccessibleJPanel { + @Override + public AccessibleRole getAccessibleRole() { + return AccessibleRole.SPLIT_PANE; + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/JXMultiThumbSlider.java b/src/main/java/org/jdesktop/swingx/JXMultiThumbSlider.java new file mode 100644 index 0000000000..4146dc2205 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXMultiThumbSlider.java @@ -0,0 +1,404 @@ +/* + * $Id: JXMultiThumbSlider.java 4147 2012-02-01 17:13:24Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.multislider.*; +import org.jdesktop.swingx.plaf.LookAndFeelAddons; +import org.jdesktop.swingx.plaf.MultiThumbSliderAddon; +import org.jdesktop.swingx.plaf.MultiThumbSliderUI; + +import javax.swing.*; +import javax.swing.event.MouseInputAdapter; +import java.awt.*; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.List; + +/** + *

A slider which can have multiple control points or Thumbs

+ *

The thumbs each represent a value between the minimum and maximum values + * of the slider. Thumbs can pass each other when being dragged. Thumbs have + * no default visual representation. To customize the look of the thumbs and the + * track behind the thumbs you must provide a ThumbRenderer and a TrackRenderer + * implementation. To listen for changes to the thumbs you must provide an + * implementation of ThumbDataListener. + * + * TODOs: + * add min/maxvalue convenience methods to jxmultithumbslider + * add plafs for windows, mac, and basic (if necessary) + * make way to properly control the height. + * hide the inner thumb component + * + * @author joshy + */ +@JavaBean +public class JXMultiThumbSlider extends JComponent { + public static final String uiClassID = "MultiThumbSliderUI"; + + private ThumbDataListener tdl; + + private List thumbs; + + private ThumbRenderer thumbRenderer; + + private TrackRenderer trackRenderer; + + private MultiThumbModel model; + + private List listeners = new ArrayList(); + + private ThumbComp selected; + + static { + LookAndFeelAddons.contribute(new MultiThumbSliderAddon()); + } + + /** Creates a new instance of JMultiThumbSlider */ + public JXMultiThumbSlider() { + thumbs = new ArrayList(); + setLayout(null); + + tdl = new ThumbHandler(); + + setModel(new DefaultMultiThumbModel()); + MultiThumbMouseListener mia = new MultiThumbMouseListener(); + addMouseListener(mia); + addMouseMotionListener(mia); + + Dimension dim = new Dimension(60,16); + setPreferredSize(dim); + setSize(dim); + setMinimumSize(new Dimension(30,16)); + updateUI(); + } + + /** + * {@inheritDoc} + */ + @Override + public String getUIClassID() { + return uiClassID; + } + + public MultiThumbSliderUI getUI() { + return (MultiThumbSliderUI)ui; + } + + public void setUI(MultiThumbSliderUI ui) { + super.setUI(ui); + } + + @Override + public void updateUI() { + setUI((MultiThumbSliderUI)LookAndFeelAddons.getUI(this, MultiThumbSliderUI.class)); + invalidate(); + } + + @Override + protected void paintComponent(Graphics g) { + if(isVisible()) { + if(trackRenderer != null) { + JComponent comp = trackRenderer.getRendererComponent(this); + add(comp); + comp.paint(g); + remove(comp); + } else { + paintRange((Graphics2D)g); + } + } + } + + private void paintRange(Graphics2D g) { + g.setColor(Color.blue); + g.fillRect(0,0,getWidth(),getHeight()); + } + + private float getThumbValue(int thumbIndex) { + return getModel().getThumbAt(thumbIndex).getPosition(); + } + + private float getThumbValue(ThumbComp thumb) { + return getThumbValue(thumbs.indexOf(thumb)); + } + + private int getThumbIndex(ThumbComp thumb) { + return thumbs.indexOf(thumb); + } + + private void clipThumbPosition(ThumbComp thumb) { + if(getThumbValue(thumb) < getModel().getMinimumValue()) { + getModel().getThumbAt(getThumbIndex(thumb)).setPosition( + getModel().getMinimumValue()); + } + if(getThumbValue(thumb) > getModel().getMaximumValue()) { + getModel().getThumbAt(getThumbIndex(thumb)).setPosition( + getModel().getMaximumValue()); + } + } + + public ThumbRenderer getThumbRenderer() { + return thumbRenderer; + } + + public void setThumbRenderer(ThumbRenderer thumbRenderer) { + this.thumbRenderer = thumbRenderer; + } + + public TrackRenderer getTrackRenderer() { + return trackRenderer; + } + + public void setTrackRenderer(TrackRenderer trackRenderer) { + this.trackRenderer = trackRenderer; + } + + public float getMinimumValue() { + return getModel().getMinimumValue(); + } + + public void setMinimumValue(float minimumValue) { + getModel().setMinimumValue(minimumValue); + } + + public float getMaximumValue() { + return getModel().getMaximumValue(); + } + + public void setMaximumValue(float maximumValue) { + getModel().setMaximumValue(maximumValue); + } + + private void setThumbPositionByX(ThumbComp selected) { + float range = getModel().getMaximumValue()-getModel().getMinimumValue(); + int x = selected.getX(); + // adjust to the center of the thumb + x += selected.getWidth()/2; + // adjust for the leading space on the slider + x -= selected.getWidth()/2; + + int w = getWidth(); + // adjust for the leading and trailing space on the slider + w -= selected.getWidth(); + float delta = ((float)x)/((float)w); + int thumb_index = getThumbIndex(selected); + float value = delta*range; + getModel().getThumbAt(thumb_index).setPosition(value); + //getModel().setPositionAt(thumb_index,value); + clipThumbPosition(selected); + } + + private void setThumbXByPosition(ThumbComp thumb, float pos) { + float lp = getWidth()-thumb.getWidth(); + float lu = getModel().getMaximumValue()-getModel().getMinimumValue(); + float tp = (pos*lp)/lu; + thumb.setLocation((int)tp-thumb.getWidth()/2 + thumb.getWidth()/2, thumb.getY()); + } + + private void recalc() { + for(ThumbComp th : thumbs) { + setThumbXByPosition(th,getModel().getThumbAt(getThumbIndex(th)).getPosition()); + //getPositionAt(getThumbIndex(th))); + } + } + + @Override + public void setBounds(int x, int y, int w, int h) { + super.setBounds(x,y,w,h); + recalc(); + } + + public JComponent getSelectedThumb() { + return selected; + } + + public int getSelectedIndex() { + return getThumbIndex(selected); + } + + public MultiThumbModel getModel() { + return model; + } + + public void setModel(MultiThumbModel model) { + if(this.model != null) { + this.model.removeThumbDataListener(tdl); + } + this.model = model; + this.model.addThumbDataListener(tdl); + } + + public void addMultiThumbListener(ThumbListener listener) { + listeners.add(listener); + } + + private class MultiThumbMouseListener extends MouseInputAdapter { + @Override + public void mousePressed(MouseEvent evt) { + ThumbComp handle = findHandle(evt); + if(handle != null) { + selected = handle; + selected.setSelected(true); + int thumb_index = getThumbIndex(selected); + for(ThumbListener tl : listeners) { + tl.thumbSelected(thumb_index); + } + repaint(); + } else { + selected = null; + for(ThumbListener tl : listeners) { + tl.thumbSelected(-1); + } + repaint(); + } + for(ThumbListener tl : listeners) { + tl.mousePressed(evt); + } + } + + @Override + public void mouseReleased(MouseEvent evt) { + if(selected != null) { + selected.setSelected(false); + } + } + + @Override + public void mouseDragged(MouseEvent evt) { + if(selected != null) { + int nx = (int)evt.getPoint().getX()- selected.getWidth()/2; + if(nx < 0) { + nx = 0; + } + if(nx > getWidth()-selected.getWidth()) { + nx = getWidth()-selected.getWidth(); + } + selected.setLocation(nx,(int)selected.getLocation().getY()); + setThumbPositionByX(selected); + int thumb_index = getThumbIndex(selected); + //log.fine("still dragging: " + thumb_index); + for(ThumbListener mtl : listeners) { + mtl.thumbMoved(thumb_index,getModel().getThumbAt(thumb_index).getPosition()); + //getPositionAt(thumb_index)); + } + repaint(); + } + } + + + private ThumbComp findHandle(MouseEvent evt) { + for(ThumbComp hand : thumbs) { + Point p2 = new Point(); + p2.setLocation(evt.getPoint().getX() - hand.getX(), + evt.getPoint().getY() - hand.getY()); + if(hand.contains(p2)) { + return hand; + } + } + return null; + } + } + + private static class ThumbComp extends JComponent { + + private JXMultiThumbSlider slider; + + public ThumbComp(JXMultiThumbSlider slider) { + this.slider = slider; + Dimension dim = new Dimension(10,10);//slider.getHeight()); + /*if(slider.getThumbRenderer() != null) { + JComponent comp = getRenderer(); + dim = comp.getPreferredSize(); + }*/ + setSize(dim); + setMinimumSize(dim); + setPreferredSize(dim); + setMaximumSize(dim); + setBackground(Color.white); + } + + @Override + public void paintComponent(Graphics g) { + if(slider.getThumbRenderer() != null) { + JComponent comp = getRenderer(); + comp.setSize(this.getSize()); + comp.paint(g); + } else { + g.setColor(getBackground()); + g.fillRect(0,0,getWidth(),getHeight()); + if(isSelected()) { + g.setColor(Color.black); + g.drawRect(0,0,getWidth()-1,getHeight()-1); + } + } + } + + private JComponent getRenderer() { + return slider.getThumbRenderer(). + getThumbRendererComponent(slider,slider.getThumbIndex(this),isSelected()); + } + + private boolean selected; + + public boolean isSelected() { + return selected; + } + + public void setSelected(boolean selected) { + this.selected = selected; + } + } + + private class ThumbHandler implements ThumbDataListener { + @Override + public void positionChanged(ThumbDataEvent e) { + ThumbComp comp = thumbs.get(e.getIndex()); + clipThumbPosition(comp); + setThumbXByPosition(comp, e.getThumb().getPosition()); + repaint(); + } + + @Override + public void thumbAdded(ThumbDataEvent evt) { + ThumbComp thumb = new ThumbComp(JXMultiThumbSlider.this); + thumb.setLocation(0, 0); + add(thumb); + thumbs.add(evt.getIndex(), thumb); + clipThumbPosition(thumb); + setThumbXByPosition(thumb, evt.getThumb().getPosition()); + repaint(); + } + + @Override + public void thumbRemoved(ThumbDataEvent evt) { + ThumbComp thumb = thumbs.get(evt.getIndex()); + remove(thumb); + thumbs.remove(thumb); + repaint(); + } + + @Override + public void valueChanged(ThumbDataEvent e) { + repaint(); + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/JXPanel.java b/src/main/java/org/jdesktop/swingx/JXPanel.java new file mode 100644 index 0000000000..8225483337 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXPanel.java @@ -0,0 +1,693 @@ +/* + * $Id: JXPanel.java 4280 2013-02-21 17:35:03Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.painter.AbstractPainter; +import org.jdesktop.swingx.painter.Painter; +import org.jdesktop.swingx.util.Contract; + +import javax.swing.*; +import javax.swing.plaf.synth.SynthLookAndFeel; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.logging.Logger; + +import static org.jdesktop.swingx.util.GraphicsUtilities.createCompatibleTranslucentImage; + +/** + *

+ * An extended {@code JPanel} that provides additional features. + *

+ *

Scrollable

+ *

+ * {@code JXPanel} is {@link Scrollable} by default. It provides reasonable implementations of all + * of the interface methods. In addition, it supports the setting of common scrolling approaches + * defined in {@link ScrollableSizeHint}. + *

+ *

Alpha Support

+ *

+ * {@code JXPanel} has full alpha-channel support. This means that the {@code JXPanel} can be made + * fully or partially transparent. This means that the JXPanel and all of its children will behave + * as a single paint at the specified alpha value. Cauton: best practice is to use + * either alpha support or opacity support, but not both. See the documentation on the methods for + * further information. + *

+ *

+ * A transparency example, this following code will show the black background of the parent: + * + *

+ * JXPanel panel = new JXPanel();
+ * panel.add(new JButton("Push Me"));
+ * panel.setAlpha(.5f);
+ * 
+ * container.setBackground(Color.BLACK);
+ * container.add(panel);
+ * 
+ * + *

+ *

Painter Support

+ *

+ * {@code JXPanel} has support for {@linkplain Painter}s. + *

+ *

+ * A painter example, this following code will show how to add a simple painter: + * + *

+ * JXPanel panel = new JXPanel();
+ * panel.setBackgroundPainter(new PinstripePainter());
+ * 
+ * + *

+ * + * @author rbair + * @see Scrollable + * @see Painter + */ +@JavaBean +@SuppressWarnings("nls") +public class JXPanel extends JPanel implements AlphaPaintable, BackgroundPaintable, Scrollable { +// private boolean scrollableTracksViewportHeight = true; +// private boolean scrollableTracksViewportWidth = true; + + private ScrollableSizeHint scrollableWidthHint = ScrollableSizeHint.FIT; + private ScrollableSizeHint scrollableHeightHint = ScrollableSizeHint.FIT; + + /** + * The alpha level for this component. + */ + private volatile float alpha = 1.0f; + /** + * If the old alpha value was 1.0, I keep track of the opaque setting because + * a translucent component is not opaque, but I want to be able to restore + * opacity to its default setting if the alpha is 1.0. Honestly, I don't know + * if this is necessary or not, but it sounded good on paper :) + *

TODO: Check whether this variable is necessary or not

+ */ + private boolean oldOpaque; + + private float oldAlpha = 1f; + + /** + * Indicates whether this component should inherit its parent alpha value + */ + private boolean inheritAlpha = true; + /** + * Specifies the Painter to use for painting the background of this panel. + * If no painter is specified, the normal painting routine for JPanel + * is called. Old behavior is also honored for the time being if no + * backgroundPainter is specified + */ + @SuppressWarnings("rawtypes") + private Painter backgroundPainter; + + private boolean paintBorderInsets = true; + + /** + * The listener installed on the current backgroundPainter, if any. + */ + private PropertyChangeListener painterChangeListener; + + /** + * Creates a new JXPanel with a double buffer + * and a flow layout. + */ + public JXPanel() { + } + + /** + * Creates a new JXPanel with FlowLayout + * and the specified buffering strategy. + * If isDoubleBuffered is true, the JXPanel + * will use a double buffer. + * + * @param isDoubleBuffered a boolean, true for double-buffering, which + * uses additional memory space to achieve fast, flicker-free + * updates + */ + public JXPanel(boolean isDoubleBuffered) { + super(isDoubleBuffered); + } + + /** + * Create a new buffered JXPanel with the specified layout manager + * + * @param layout the LayoutManager to use + */ + public JXPanel(LayoutManager layout) { + super(layout); + } + + /** + * Creates a new JXPanel with the specified layout manager and buffering + * strategy. + * + * @param layout the LayoutManager to use + * @param isDoubleBuffered a boolean, true for double-buffering, which + * uses additional memory space to achieve fast, flicker-free + * updates + */ + public JXPanel(LayoutManager layout, boolean isDoubleBuffered) { + super(layout, isDoubleBuffered); + } + + /** + * {@inheritDoc} + *

+ * Setting the component to be opaque will reset the alpha setting to {@code 1f} (full + * opaqueness). Setting the component to be non-opaque will restore the previous alpha + * transparency. If the component is non-opaque with a fully-opaque alpha value ({@code 1f}), + * the behavior should be the same as as a {@code JPanel} that is non-opaque. + */ + @Override + public void setOpaque(boolean opaque) { + if (isPatch()) { + setOpaquePatch(opaque); + return; + } + if (opaque) { + oldAlpha = getAlpha(); + + if (oldAlpha < 1f) { + setAlpha(1f); + } else { + super.setOpaque(true); + repaint(); + } + } else if (getAlpha() == 1f) { + if (oldAlpha == 1f) { + super.setOpaque(false); + repaint(); + } else { + setAlpha(oldAlpha); + } + } + } + + @Override + public boolean isOpaque() { + if (isPatch()) { + return isOpaquePatch(); + } + return super.isOpaque(); + } + + /** + * {@inheritDoc} + */ + @Override + public float getAlpha() { + return alpha; + } + + /** + * {@inheritDoc} + */ + @Override + public void setAlpha(float alpha) { + if (isPatch()) { + setAlphaPatch(alpha); + return; + } + if (alpha < 0f || alpha > 1f) { + throw new IllegalArgumentException("invalid alpha value " + alpha); + } + + float oldValue = getAlpha(); + this.alpha = alpha; + + if (getAlpha() < 1f) { + if (oldValue == 1) { + //it used to be 1, but now is not. Save the oldOpaque + oldOpaque = isOpaque(); + super.setOpaque(false); + } + } else { + //restore the oldOpaque if it was true (since opaque is false now) + if (oldOpaque) { + super.setOpaque(true); + } + } + + firePropertyChange("alpha", oldValue, getAlpha()); + repaint(); + } + + /** + * experimental version: doesn't tweak opaque + * + * called if isPatch + * @param alpha + */ + private void setAlphaPatch(float alpha) { + if (alpha < 0f || alpha > 1f) { + throw new IllegalArgumentException("invalid alpha value " + alpha); + } + + float oldValue = getAlpha(); + this.alpha = alpha; + + if (getAlpha() < 1f) { + if (oldValue == 1) { + //it used to be 1, but now is not. Save the oldOpaque + oldOpaque = isOpaque(); +// super.setOpaque(false); + } + + } else { + //restore the oldOpaque if it was true (since opaque is false now) + if (oldOpaque) { +// super.setOpaque(true); + } + } + + firePropertyChange("alpha", oldValue, getAlpha()); + repaint(); + } + + /** + * {@inheritDoc} + */ + @Override + public float getEffectiveAlpha() { + float a = getAlpha(); + + if (isInheritAlpha()) { + for (Component c = getParent(); c != null; c = c.getParent()) { + if (c instanceof AlphaPaintable) { + a = Math.min(((AlphaPaintable) c).getEffectiveAlpha(), a); + break; + } + } + } + + return a; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isInheritAlpha() { + return inheritAlpha; + } + + /** + * {@inheritDoc} + */ + @Override + public void setInheritAlpha(boolean val) { + boolean oldValue = isInheritAlpha(); + inheritAlpha = val; + firePropertyChange("inheritAlpha", oldValue, isInheritAlpha()); + } + + /** + * Sets the horizontal sizing hint. The hint is used by the Scrollable implementation + * to service the getScrollableTracksWidth. + * + * @param hint the horizontal sizing hint, must not be null + * and must be vertical. + * + * @throws NullPointerException if null + * + * @see #setScrollableHeightHint(ScrollableSizeHint) + * @see ScrollableSizeHint + */ + public final void setScrollableWidthHint(ScrollableSizeHint hint) { + Contract.asNotNull(hint, "hint cannot be null"); + ScrollableSizeHint oldValue = getScrollableWidthHint(); + if (oldValue == hint) return; + this.scrollableWidthHint = hint; + revalidate(); + firePropertyChange("scrollableWidthHint", oldValue, getScrollableWidthHint()); + } + + + /** + * Sets the vertical sizing hint. The hint is used by the Scrollable implementation + * to service the getScrollableTracksHeight. + * + * @param hint the vertical sizing hint, must not be null + * and must be vertical. + * + * @throws NullPointerException if null + * + * @see #setScrollableWidthHint(ScrollableSizeHint) + * @see ScrollableSizeHint + */ + public final void setScrollableHeightHint(ScrollableSizeHint hint) { + Contract.asNotNull(hint, "hint cannot be null"); + ScrollableSizeHint oldValue = getScrollableHeightHint(); + if (oldValue == hint) return; + this.scrollableHeightHint = hint; + revalidate(); + firePropertyChange("scrollableHeightHint", oldValue, getScrollableHeightHint()); + } + + protected ScrollableSizeHint getScrollableWidthHint() { + return scrollableWidthHint; + } + + protected ScrollableSizeHint getScrollableHeightHint() { + return scrollableHeightHint; + + } + + /** + * {@inheritDoc} + */ + @Override + public boolean getScrollableTracksViewportHeight() { + return scrollableHeightHint.getTracksParentSize(this, SwingConstants.VERTICAL); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean getScrollableTracksViewportWidth() { + return scrollableWidthHint.getTracksParentSize(this, SwingConstants.HORIZONTAL); + } + + /** + * {@inheritDoc} + */ + @Override + public Dimension getPreferredScrollableViewportSize() { + return getPreferredSize(); + } + + /** + * {@inheritDoc} + */ + @Override + public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { + if (orientation == SwingConstants.VERTICAL) { + return visibleRect.height; + } else if (orientation == SwingConstants.HORIZONTAL) { + return visibleRect.width; + } else { + throw new IllegalArgumentException("invalid orientation"); //$NON-NLS-1$ + } + } + + /** + * {@inheritDoc} + */ + @Override + public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { + return getScrollableBlockIncrement(visibleRect, orientation, direction) / 10; + } + + /** + * + * Sets the vertical size tracking to either ScrollableSizeTrack.FIT or NONE, if the + * boolean parameter is true or false, respectively.

+ * + * NOTE: this method is kept for backward compatibility only, for full + * control use setScrollableHeightHint. + * + * @param scrollableTracksViewportHeight The scrollableTracksViewportHeight to set. + * + * @see #setScrollableHeightHint(ScrollableSizeHint) + */ + public void setScrollableTracksViewportHeight(boolean scrollableTracksViewportHeight) { + setScrollableHeightHint(scrollableTracksViewportHeight ? + ScrollableSizeHint.FIT : ScrollableSizeHint.NONE); + } + /** + * Sets the horizontal size tracking to either ScrollableSizeTrack.FIT or NONE, if the + * boolean parameter is true or false, respectively.

+ * + * NOTE: this method is kept for backward compatibility only, for full + * control use setScrollableWidthHint. + * + * + * @param scrollableTracksViewportWidth The scrollableTracksViewportWidth to set. + * + * @see #setScrollableWidthHint(ScrollableSizeHint) + */ + public void setScrollableTracksViewportWidth(boolean scrollableTracksViewportWidth) { + setScrollableWidthHint(scrollableTracksViewportWidth ? + ScrollableSizeHint.FIT : ScrollableSizeHint.NONE); + } + + /** + * Sets a Painter to use to paint the background of this JXPanel. + * + * @param p the new painter + * @see #getBackgroundPainter() + */ + @Override + public void setBackgroundPainter(Painter p) { + Painter old = getBackgroundPainter(); + if (old instanceof AbstractPainter) { + ((AbstractPainter) old).removePropertyChangeListener(painterChangeListener); + } + backgroundPainter = p; + if (backgroundPainter instanceof AbstractPainter) { + ((AbstractPainter) backgroundPainter).addPropertyChangeListener(getPainterChangeListener()); + } + firePropertyChange("backgroundPainter", old, getBackgroundPainter()); + repaint(); + } + + /** + * @return a listener for painter change events + */ + protected PropertyChangeListener getPainterChangeListener() { + if (painterChangeListener == null) { + painterChangeListener = new PropertyChangeListener() { + + @Override + public void propertyChange(PropertyChangeEvent evt) { + repaint(); + } + }; + } + return painterChangeListener; + } + + /** + * Returns the current background painter. The default value of this property + * is a painter which draws the normal JPanel background according to the current look and feel. + * @return the current painter + * @see #setBackgroundPainter(Painter) + * @see #isPaintBorderInsets() + */ + @Override + public Painter getBackgroundPainter() { + return backgroundPainter; + } + + /** + * Returns true if the background painter should paint where the border is + * or false if it should only paint inside the border. This property is + * true by default. This property affects the width, height, + * and initial transform passed to the background painter. + */ + @Override + public boolean isPaintBorderInsets() { + return paintBorderInsets; + } + + /** + * Sets the paintBorderInsets property. + * Set to true if the background painter should paint where the border is + * or false if it should only paint inside the border. This property is true by default. + * This property affects the width, height, + * and initial transform passed to the background painter. + * + * This is a bound property. + */ + @Override + public void setPaintBorderInsets(boolean paintBorderInsets) { + boolean old = this.isPaintBorderInsets(); + this.paintBorderInsets = paintBorderInsets; + firePropertyChange("paintBorderInsets", old, isPaintBorderInsets()); + } + + //support for Java 7 painting improvements + protected boolean isPaintingOrigin() { + return getAlpha() < 1f; + } + + /** + * Overridden paint method to take into account the alpha setting. + * + * @param g + * the Graphics context in which to paint + */ + @Override + public void paint(Graphics g) { + //short circuit painting if no transparency + if (getAlpha() == 1f) { + super.paint(g); + } else { + //the component is translucent, so we need to render to + //an intermediate image before painting + // TODO should we cache this image? repaint to same image unless size changes? + BufferedImage img = createCompatibleTranslucentImage(getWidth(), getHeight()); + Graphics2D gfx = img.createGraphics(); + + try { + super.paint(gfx); + } finally { + gfx.dispose(); + } + + Graphics2D g2d = (Graphics2D) g; + Composite oldComp = g2d.getComposite(); + + try { + Composite alphaComp = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, getEffectiveAlpha()); + g2d.setComposite(alphaComp); + //TODO should we cache the image? + g2d.drawImage(img, null, 0, 0); + } finally { + g2d.setComposite(oldComp); + } + } + } + + /** + * Overridden to provide Painter support. It will call backgroundPainter.paint() + * if it is not null, else it will call super.paintComponent(). + * + * @param g + * the Graphics context in which to paint + */ + @Override + @SuppressWarnings("unchecked") + protected void paintComponent(Graphics g) { + if (isPatch()) { + paintComponentPatch(g); + return; + } + Graphics2D g2 = (Graphics2D) g.create(); + + try { + // we should be painting the background behind the painter if we have one + // this prevents issues with buffer reuse where visual artifacts sneak in + if (isOpaque() || UIManager.getLookAndFeel() instanceof SynthLookAndFeel) { + //this will paint the foreground if a JXPanel subclass is + //unfortunate enough to have one + super.paintComponent(g2); + } else if (getAlpha() < 1f) { + g.setColor(getBackground()); + g.fillRect(0, 0, getWidth(), getHeight()); + } + + if (getBackgroundPainter() != null) { + if (isPaintBorderInsets()) { + getBackgroundPainter().paint(g2, this, getWidth(), getHeight()); + } else { + Insets insets = getInsets(); + g.translate(insets.left, insets.top); + getBackgroundPainter().paint(g2, this, getWidth() - insets.left - insets.right, + getHeight() - insets.top - insets.bottom); + g.translate(-insets.left, -insets.top); + } + } + + //force the foreground to paint again...workaround for folks that + //incorrectly extend JXPanel instead of JComponent + getUI().paint(g2, this); + } finally { + g2.dispose(); + } + } + +//--------------------- experimental patch + + protected boolean isPatch() { + return Boolean.TRUE.equals(UIManager.get("JXPanel.patch")); + } + + boolean fakeTransparent; + + protected void paintComponentPatch(Graphics g) { + Graphics2D g2 = (Graphics2D) g.create(); + + try { + if (isPaintingBackground()) { + g2.setColor(getBackground()); + g2.fillRect(0, 0, getWidth(), getHeight()); + } + if (getBackgroundPainter() != null) { + getBackgroundPainter().paint(g2, this, getWidth(), getHeight()); + } + fakeTransparent = true; + getUI().update(g2, this); + } finally { + g2.dispose(); + fakeTransparent = false; + } + + } + + protected boolean isOpaquePatch() { + if (fakeTransparent) return false; + if (isPaintingBackground()) { + return !isTransparentBackground() && !isAlpha(); + } + return false; + } + + protected void setOpaquePatch(boolean opaque) { + super.setOpaque(opaque); + } + /** + * Returns whether or not the container hierarchy below is + * transparent. + * + * @return + */ + protected boolean isAlpha() { + // PENDING JW: use effective alpha? + return getAlpha() < 1.0f; + } + + /** + * Returns whether or not the background is transparent. + * + * @return + */ + protected boolean isTransparentBackground() { + return getBackground().getAlpha() < 255; + } + + /** + * Returns whether or not the background should be painted. + * + * @return + */ + protected boolean isPaintingBackground() { + return super.isOpaque(); + } + + @SuppressWarnings("unused") + private static final Logger LOG = Logger.getLogger(JXPanel.class.getName()); +} diff --git a/src/main/java/org/jdesktop/swingx/JXRadioGroup.java b/src/main/java/org/jdesktop/swingx/JXRadioGroup.java new file mode 100644 index 0000000000..9e5a27b0cb --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXRadioGroup.java @@ -0,0 +1,338 @@ +/* + * $Id: JXRadioGroup.java 4158 2012-02-03 18:29:40Z kschaefe $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; + +import javax.swing.*; +import javax.swing.event.EventListenerList; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; + +/** + *

+ * {@code JXRadioGroup} is a group of radio buttons that functions as a unit. It + * is similar in concept to a {@link JComboBox} in functionality, but can offer + * a better presentation for a small number of choices. {@code JXRadioGroup} + * should be used in preference to {@code JComboBox} when the number of choices + * is small (less than six) or the choices are verbose. + *

+ *

+ * Notes: + *

    + *
  1. Enabling and disabling the JXRadioGroup will enable/disable all of the + * child buttons inside the JXRadioGroup.
  2. + *
  3. + * If the generic type parameter of JXRadioGroup is a subclass of + * {@link AbstractButton}, then the buttons will be added "as is" to the + * container. If the generic type is anything else, buttons will be created as + * {@link JRadioButton} objects, and the button text will be set by calling + * toString() on the value object.
  4. + *
  5. + * Alternatively, if you want to configure the buttons individually, construct + * the JXRadioGroup normally, and then call {@link #getChildButton(int)} or + * {@link #getChildButton(Object)} and configure the buttons.
  6. + *
+ *

+ *

+ * TODO back with a model (possibly reuse of extend {@link ComboBoxModel} + *

+ * + * @author Amy Fowler + * @author Noel Grandin + * @version 1.0 + */ +@JavaBean +public class JXRadioGroup extends JPanel { + + private static final long serialVersionUID = 3257285842266567986L; + + private ButtonGroup buttonGroup; + + private final List values = new ArrayList(); + + private ActionSelectionListener actionHandler; + + /** + * Create a default JXRadioGroup with a default layout axis of {@link BoxLayout#X_AXIS}. + */ + public JXRadioGroup() { + setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); + buttonGroup = new ButtonGroup(); + } + + /** + * Create a default JXRadioGroup with a default layout axis of {@link BoxLayout#X_AXIS}. + * + * @param radioValues the list of values used to create the group. + */ + public JXRadioGroup(T[] radioValues) { + this(); + for (int i = 0; i < radioValues.length; i++) { + add(radioValues[i]); + } + } + + /** + * Convenience factory method. + * Reduces code clutter when dealing with generics. + * + * @param radioValues the list of values used to create the group. + */ + public static JXRadioGroup create(T[] radioValues) + { + return new JXRadioGroup(radioValues); + } + + /** + * Set the layout axis of the radio group. + * + * @param axis values from {@link BoxLayout}. + */ + public void setLayoutAxis(int axis) + { + setLayout(new BoxLayout(this, axis)); + } + + /** + * Sets the values backing this group. This replaces the current set of + * values with the new set. + * + * @param radioValues + * the new backing values for this group + */ + public void setValues(T[] radioValues) { + clearAll(); + for (int i = 0; i < radioValues.length; i++) { + add(radioValues[i]); + } + } + + private void clearAll() { + values.clear(); + buttonGroup = new ButtonGroup(); + // remove all the child components + removeAll(); + } + + /** + * You can use this method to manually add your own AbstractButton objects, provided you declared + * the class as JXRadioGroup<JRadioButton>. + */ + public void add(T radioValue) { + if (values.contains(radioValue)) + { + throw new IllegalArgumentException("cannot add the same value twice " + radioValue); + } + if (radioValue instanceof AbstractButton) { + values.add(radioValue); + addButton((AbstractButton) radioValue); + } else { + values.add(radioValue); + // Note: the "quote + object" trick here allows null values + addButton(new JRadioButton(""+radioValue)); + } + } + + private void addButton(AbstractButton button) { + buttonGroup.add(button); + super.add(button); + if (actionHandler == null) { + actionHandler = new ActionSelectionListener(); + } + button.addActionListener(actionHandler); + button.addItemListener(actionHandler); + } + + private class ActionSelectionListener implements ActionListener, ItemListener + { + @Override + public void actionPerformed(ActionEvent e) { + fireActionEvent(e); + } + + @Override + public void itemStateChanged(ItemEvent e) { + fireActionEvent(null); + } + } + + /** + * Gets the currently selected button. + * + * @return the currently selected button + * @see #getSelectedValue() + */ + public AbstractButton getSelectedButton() { + final ButtonModel selectedModel = buttonGroup.getSelection(); + final AbstractButton children[] = getButtonComponents(); + for (int i = 0; i < children.length; i++) { + AbstractButton button = children[i]; + if (button.getModel() == selectedModel) { + return button; + } + } + return null; + } + + private AbstractButton[] getButtonComponents() { + final Component[] children = getComponents(); + final List buttons = new ArrayList(); + for (int i = 0; i < children.length; i++) { + if (children[i] instanceof AbstractButton) { + buttons.add((AbstractButton) children[i]); + } + } + return buttons.toArray(new AbstractButton[buttons.size()]); + } + + private int getSelectedIndex() { + final ButtonModel selectedModel = buttonGroup.getSelection(); + final Component children[] = getButtonComponents(); + for (int i = 0; i < children.length; i++) { + AbstractButton button = (AbstractButton) children[i]; + if (button.getModel() == selectedModel) { + return i; + } + } + return -1; + } + + /** + * The currently selected value. + * + * @return the current value + */ + public T getSelectedValue() { + final int index = getSelectedIndex(); + return (index < 0 || index >= values.size()) ? null : values.get(index); + } + + /** + * Selects the supplied value. + * + * @param value + * the value to select + */ + public void setSelectedValue(T value) { + final int index = values.indexOf(value); + AbstractButton button = getButtonComponents()[index]; + button.setSelected(true); + } + + /** + * Retrieve the child button by index. + */ + public AbstractButton getChildButton(int index) { + return getButtonComponents()[index]; + } + + /** + * Retrieve the child button that represents this value. + */ + public AbstractButton getChildButton(T value) { + final int index = values.indexOf(value); + return getButtonComponents()[index]; + } + + /** + * Get the number of child buttons. + */ + public int getChildButtonCount() { + return getButtonComponents().length; + } + + /** + * Adds an ActionListener. + *

+ * The ActionListener will receive an ActionEvent + * when a selection has been made. + * + * @param l the ActionListener that is to be notified + * @see #setSelectedValue(Object) + */ + public void addActionListener(ActionListener l) { + listenerList.add(ActionListener.class, l); + } + + /** + * Removes an ActionListener. + * + * @param l + * the ActionListener to remove + */ + public void removeActionListener(ActionListener l) { + listenerList.remove(ActionListener.class, l); + } + + /** + * Returns an array of all the ActionListeners added + * to this JRadioGroup with addActionListener(). + * + * @return all of the ActionListeners added or an empty + * array if no listeners have been added + */ + public ActionListener[] getActionListeners() { + return listenerList.getListeners(ActionListener.class); + } + + /** + * Notifies all listeners that have registered interest for notification on + * this event type. + * + * @param e + * the event to pass to the listeners + * @see EventListenerList + */ + protected void fireActionEvent(ActionEvent e) { + for (ActionListener l : getActionListeners()) { + l.actionPerformed(e); + } + } + + /** + * Enable/disable all of the child buttons + * + * @see JComponent#setEnabled(boolean) + */ + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + for (Enumeration en = buttonGroup.getElements(); en.hasMoreElements();) { + final AbstractButton button = en.nextElement(); + /* We don't want to enable a button where the action does not + * permit it. */ + if (enabled && button.getAction() != null + && !button.getAction().isEnabled()) { + // do nothing + } else { + button.setEnabled(enabled); + } + } + } + +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/JXRootPane.java b/src/main/java/org/jdesktop/swingx/JXRootPane.java new file mode 100644 index 0000000000..f7bd932e4c --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXRootPane.java @@ -0,0 +1,458 @@ +/* + * $Id: JXRootPane.java 4147 2012-02-01 17:13:24Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; + +/** + * Extends the JRootPane by supporting specific placements for a toolbar and a + * status bar. If a status bar exists, then toolbars, menus will be registered + * with the status bar. + * + * @see JXStatusBar + * @author Mark Davidson + */ +@JavaBean +public class JXRootPane extends JRootPane { + /** + * An extended {@code RootLayout} offering support for managing the status + * bar. + * + * @author Karl George Schaefer + * @author Jeanette Winzenberg + */ + protected class XRootLayout extends RootLayout { + + LayoutManager2 delegate; + + /** + * The layout manager backing this manager. The delegate is used to + * calculate the size when the UI handles the window decorations. + * + * @param delegate + * the backing manager + */ + public void setLayoutManager(LayoutManager2 delegate) { + this.delegate = delegate; + } + + private Dimension delegatePreferredLayoutSize(Container parent) { + if (delegate == null) + return super.preferredLayoutSize(parent); + return delegate.preferredLayoutSize(parent); + } + + /** + * {@inheritDoc} + */ + @Override + public Dimension preferredLayoutSize(Container parent) { + Dimension pref = delegatePreferredLayoutSize(parent); + if (statusBar != null && statusBar.isVisible()) { + Dimension statusPref = statusBar.getPreferredSize(); + pref.width = Math.max(pref.width, statusPref.width); + pref.height += statusPref.height; + } + return pref; + } + + private Dimension delegateMinimumLayoutSize(Container parent) { + if (delegate == null) + return super.minimumLayoutSize(parent); + return delegate.minimumLayoutSize(parent); + } + + /** + * {@inheritDoc} + */ + @Override + public Dimension minimumLayoutSize(Container parent) { + Dimension pref = delegateMinimumLayoutSize(parent); + if (statusBar != null && statusBar.isVisible()) { + Dimension statusPref = statusBar.getMinimumSize(); + pref.width = Math.max(pref.width, statusPref.width); + pref.height += statusPref.height; + } + return pref; + + } + + private Dimension delegateMaximumLayoutSize(Container parent) { + if (delegate == null) + + return super.maximumLayoutSize(parent); + return delegate.maximumLayoutSize(parent); + } + + /** + * {@inheritDoc} + */ + @Override + public Dimension maximumLayoutSize(Container target) { + Dimension pref = delegateMaximumLayoutSize(target); + if (statusBar != null && statusBar.isVisible()) { + Dimension statusPref = statusBar.getMaximumSize(); + pref.width = Math.max(pref.width, statusPref.width); + // PENDING JW: overflow? + pref.height += statusPref.height; + } + return pref; + } + + private void delegateLayoutContainer(Container parent) { + if (delegate == null) { + super.layoutContainer(parent); + } else { + delegate.layoutContainer(parent); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void layoutContainer(Container parent) { + delegateLayoutContainer(parent); + if (statusBar == null || !statusBar.isVisible()) + return; + Rectangle b = parent.getBounds(); + Insets i = getInsets(); + int w = b.width - i.right - i.left; +// int h = b.height - i.top - i.bottom; + Dimension statusPref = statusBar.getPreferredSize(); + statusBar.setBounds(i.right, b.height - i.bottom + - statusPref.height, w, statusPref.height); + if (contentPane != null) { + Rectangle bounds = contentPane.getBounds(); + contentPane.setBounds(bounds.x, bounds.y, bounds.width, + bounds.height - statusPref.height); + } + + } + } + + /** + * The current status bar for this root pane. + */ + protected JXStatusBar statusBar; + + private JToolBar toolBar; + + /** + * The button that gets activated when the pane has the focus and + * a UI-specific action like pressing the ESC key occurs. + */ + private JButton cancelButton; + + /** + * Creates an extended root pane. + */ + public JXRootPane() { + installKeyboardActions(); + } + + /** + * {@inheritDoc} + */ + @Override + protected Container createContentPane() { + JComponent c = new JXPanel() { + /** + * {@inheritDoc} + */ + @Override + protected void addImpl(Component comp, Object constraints, int index) { + synchronized (getTreeLock()) { + super.addImpl(comp, constraints, index); + registerStatusBar(comp); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void remove(int index) { + synchronized (getTreeLock()) { + unregisterStatusBar(getComponent(index)); + super.remove(index); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void removeAll() { + synchronized (getTreeLock()) { + for (Component c : getComponents()) { + unregisterStatusBar(c); + } + + super.removeAll(); + } + } + }; + c.setName(this.getName()+".contentPane"); + c.setLayout(new BorderLayout() { + /* This BorderLayout subclass maps a null constraint to CENTER. + * Although the reference BorderLayout also does this, some VMs + * throw an IllegalArgumentException. + */ + @Override + public void addLayoutComponent(Component comp, Object constraints) { + if (constraints == null) { + constraints = BorderLayout.CENTER; + } + super.addLayoutComponent(comp, constraints); + } + }); + return c; + } + + + /** + * {@inheritDoc} + */ + @Override + public void setLayout(LayoutManager layout) { + if (layout instanceof XRootLayout) { + // happens if decoration is uninstalled by ui + if ((layout != null) && (layout == getLayout())) { + ((XRootLayout) layout).setLayoutManager(null); + } + super.setLayout(layout); + } else { + if (layout instanceof LayoutManager2) { + ((XRootLayout) getLayout()).setLayoutManager((LayoutManager2) layout); + if (!isValid()) { + invalidate(); + } + } + } + } + + /** + * {@inheritDoc} + */ + @Override + protected LayoutManager createRootLayout() { + return new XRootLayout(); + } + + /** + * PENDING: move to UI + * + */ + private void installKeyboardActions() { + Action escAction = new AbstractAction() { + @Override + public void actionPerformed(ActionEvent evt) { + JButton cancelButton = getCancelButton(); + if (cancelButton != null) { + cancelButton.doClick(20); + } + } + + /** + * Overridden to hack around #566-swing: + * JXRootPane eats escape keystrokes from datepicker popup. + * Disable action if there is no cancel button.

+ * + * That's basically what RootPaneUI does - only not in + * the parameterless isEnabled, but in the one that passes + * in the sender (available in UIAction only). We can't test + * nor compare against core behaviour, UIAction has + * sun package scope.

+ * + * Cont'd (Issue #1358-swingx: popup menus not closed) + * The extended hack is inspired by Rob Camick's + * Blog + * and consists in checking if the the rootpane has a popup's actionMap "inserted". + * NOTE: this does not work if the popup or any of its children is focusOwner. + */ + @Override + public boolean isEnabled() { + Component component = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); + if (component instanceof JComponent) { + Action cancelPopup = ((JComponent)component).getActionMap().get("cancel"); + if (cancelPopup != null) return false; + } + return (cancelButton != null) && (cancelButton.isEnabled()); + } + }; + getActionMap().put("esc-action", escAction); + InputMap im = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); + KeyStroke key = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); + im.put(key, "esc-action"); + } + + private void registerStatusBar(Component comp) { + if (statusBar == null || comp == null) { + return; + } + if (comp instanceof Container) { + Component[] comps = ((Container) comp).getComponents(); + for (int i = 0; i < comps.length; i++) { + registerStatusBar(comps[i]); + } + } + } + + private void unregisterStatusBar(Component comp) { + if (statusBar == null || comp == null) { + return; + } + if (comp instanceof Container) { + Component[] comps = ((Container) comp).getComponents(); + for (int i = 0; i < comps.length; i++) { + unregisterStatusBar(comps[i]); + } + } + } + + /** + * Set the status bar for this root pane. Any components held by this root + * pane will be registered. If this is replacing an existing status bar then + * the existing component will be unregistered from the old status bar. + * + * @param statusBar + * the status bar to use + */ + public void setStatusBar(JXStatusBar statusBar) { + JXStatusBar oldStatusBar = this.statusBar; + this.statusBar = statusBar; + + Component[] comps = getContentPane().getComponents(); + for (int i = 0; i < comps.length; i++) { + // Unregister the old status bar. + unregisterStatusBar(comps[i]); + + // register the new status bar. + registerStatusBar(comps[i]); + } + if (oldStatusBar != null) { + remove(oldStatusBar); + } + if (statusBar != null) { + add(statusBar); + } + firePropertyChange("statusBar", oldStatusBar, getStatusBar()); + } + + /** + * Gets the currently installed status bar. + * + * @return the current status bar + */ + public JXStatusBar getStatusBar() { + return statusBar; + } + + /** + * Set the toolbar bar for this root pane. If a tool bar is currently registered with this + * {@code JXRootPane}, then it is removed prior to setting the new tool + * bar. If an implementation needs to handle more than one tool bar, a + * subclass will need to override the singleton logic used here or manually + * add toolbars with {@code getContentPane().add}. + * + * @param toolBar + * the toolbar to register + */ + public void setToolBar(JToolBar toolBar) { + JToolBar oldToolBar = getToolBar(); + this.toolBar = toolBar; + + if (oldToolBar != null) { + getContentPane().remove(oldToolBar); + } + + getContentPane().add(BorderLayout.NORTH, this.toolBar); + + //ensure the new toolbar is correctly sized and displayed + getContentPane().validate(); + + firePropertyChange("toolBar", oldToolBar, getToolBar()); + } + + /** + * The currently installed tool bar. + * + * @return the current tool bar + */ + public JToolBar getToolBar() { + return toolBar; + } + + + /** + * Sets the cancelButton property, + * which determines the current default cancel button for this JRootPane. + * The cancel button is the button which will be activated + * when a UI-defined activation event (typically the ESC key) + * occurs in the root pane regardless of whether or not the button + * has keyboard focus (unless there is another component within + * the root pane which consumes the activation event, + * such as a JTextPane). + * For default activation to work, the button must be an enabled + * descendant of the root pane when activation occurs. + * To remove a cancel button from this root pane, set this + * property to null. + * + * @param cancelButton the JButton which is to be the cancel button + * @see #getCancelButton() + * + * @beaninfo + * description: The button activated by default for cancel actions in this root pane + */ + public void setCancelButton(JButton cancelButton) { + JButton old = this.cancelButton; + + if (old != cancelButton) { + this.cancelButton = cancelButton; + + if (old != null) { + old.repaint(); + } + if (cancelButton != null) { + cancelButton.repaint(); + } + } + + firePropertyChange("cancelButton", old, cancelButton); + } + + /** + * Returns the value of the cancelButton property. + * @return the JButton which is currently the default cancel button + * @see #setCancelButton + */ + public JButton getCancelButton() { + return cancelButton; + } + +} diff --git a/src/main/java/org/jdesktop/swingx/JXSearchField.java b/src/main/java/org/jdesktop/swingx/JXSearchField.java new file mode 100644 index 0000000000..64b9d50243 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXSearchField.java @@ -0,0 +1,841 @@ +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.plaf.LookAndFeelAddons; +import org.jdesktop.swingx.plaf.SearchFieldAddon; +import org.jdesktop.swingx.plaf.TextUIWrapper; +import org.jdesktop.swingx.plaf.UIManagerExt; +import org.jdesktop.swingx.prompt.BuddyButton; +import org.jdesktop.swingx.search.NativeSearchFieldSupport; +import org.jdesktop.swingx.search.RecentSearches; + +import javax.swing.*; +import javax.swing.text.Document; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +/** + * A text field with a find icon in which the user enters text that identifies + * items to search for. + * + * JXSearchField almost looks and behaves like a native Windows Vista search + * box, a Mac OS X search field, or a search field like the one used in Mozilla + * Thunderbird 2.0 - depending on the current look and feel. + * + * JXSearchField is a text field that contains a find button and a cancel + * button. The find button normally displays a lens icon appropriate for the + * current look and feel. The cancel button is used to clear the text and + * therefore only visible when text is present. It normally displays a 'x' like + * icon. Text can also be cleared, using the 'Esc' key. + * + * The position of the find and cancel buttons can be customized by either + * changing the search fields (text) margin or button margin, or by changing the + * {@link LayoutStyle}. + * + * JXSearchField supports two different search modes: {@link SearchMode#INSTANT} + * and {@link SearchMode#REGULAR}. + * + * A search can be performed by registering an {@link ActionListener}. The + * {@link ActionEvent}s command property contains the text to search for. The + * search should be cancelled, when the command text is empty or null. + * + * @see RecentSearches + * @author Peter Weishapl + * + */ +@JavaBean +public class JXSearchField extends JXTextField { + /** + * The default instant search delay. + */ + private static final int DEFAULT_INSTANT_SEARCH_DELAY = 180; + /** + * The key used to invoke the cancel action. + */ + private static final KeyStroke CANCEL_KEY = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); + + /** + * Defines, how the find and cancel button are layouted. + */ + public enum LayoutStyle { + /** + *

+ * In VISTA layout style, the find button is placed on the right side of + * the search field. If text is entered, the find button is replaced by + * the cancel button when the actual search mode is + * {@link SearchMode#INSTANT}. When the search mode is + * {@link SearchMode#REGULAR} the find button will always stay visible + * and the cancel button will never be shown. However, 'Escape' can + * still be pressed to clear the text. + *

+ */ + VISTA, + /** + *

+ * In MAC layout style, the find button is placed on the left side of + * the search field and the cancel button on the right side. The cancel + * button is only visible when text is present. + *

+ */ + MAC + }; + + /** + * Defines when action events are posted. + */ + public enum SearchMode { + /** + *

+ * In REGULAR search mode, an action event is fired, when the user + * presses enter or clicks the find button. + *

+ *

+ * However, if a find popup menu is set and layout style is + * {@link LayoutStyle#MAC}, no action will be fired, when the find + * button is clicked, because instead the popup menu is shown. A search + * can therefore only be triggered, by pressing the enter key. + *

+ *

+ * The find button can have a rollover and a pressed icon, defined by + * the "SearchField.rolloverIcon" and "SearchField.pressedIcon" UI + * properties. When a find popup menu is set, + * "SearchField.popupRolloverIcon" and "SearchField.popupPressedIcon" + * are used. + *

+ * + */ + REGULAR, + /** + * In INSTANT search mode, an action event is fired, when the user + * presses enter or changes the search text. + * + * The action event is delayed about the number of milliseconds + * specified by {@link JXSearchField#getInstantSearchDelay()}. + * + * No rollover and pressed icon is used for the find button. + */ + INSTANT + } + + // ensure at least the default ui is registered + static { + LookAndFeelAddons.contribute(new SearchFieldAddon()); + } + + private JButton findButton; + + private JButton cancelButton; + + private JButton popupButton; + + private LayoutStyle layoutStyle; + + private SearchMode searchMode = SearchMode.INSTANT; + + private boolean useSeperatePopupButton; + + private boolean useSeperatePopupButtonSet; + + private boolean layoutStyleSet; + + private int instantSearchDelay = DEFAULT_INSTANT_SEARCH_DELAY; + + private boolean promptFontStyleSet; + + private Timer instantSearchTimer; + + private String recentSearchesSaveKey; + + private RecentSearches recentSearches; + + /** + * Creates a new search field with a default prompt. + */ + public JXSearchField() { + this(UIManagerExt.getString("SearchField.prompt")); + } + + /** + * Creates a new search field with the given prompt and + * {@link SearchMode#INSTANT}. + * + * @param prompt + */ + public JXSearchField(String prompt) { + super(prompt); + // use the native search field if possible. + setUseNativeSearchFieldIfPossible(true); + // install default actions + setCancelAction(new ClearAction()); + setFindAction(new FindAction()); + + // We cannot register the ClearAction through the Input- and + // ActionMap because ToolTipManager registers the escape key with an + // action that hides the tooltip every time the tooltip is changed and + // then the ClearAction will never be called. + addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + if (CANCEL_KEY.equals(KeyStroke.getKeyStroke(e.getKeyCode(), e.getModifiers()))) { + getCancelAction().actionPerformed( + new ActionEvent(JXSearchField.this, e.getID(), KeyEvent.getKeyText(e.getKeyCode()))); + } + } + }); + + // Map specific native properties to general JXSearchField properties. + addPropertyChangeListener(NativeSearchFieldSupport.FIND_POPUP_PROPERTY, new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + JPopupMenu oldPopup = (JPopupMenu) evt.getOldValue(); + firePropertyChange("findPopupMenu", oldPopup, evt.getNewValue()); + } + }); + addPropertyChangeListener(NativeSearchFieldSupport.CANCEL_ACTION_PROPERTY, new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + ActionListener oldAction = (ActionListener) evt.getOldValue(); + firePropertyChange("cancelAction", oldAction, evt.getNewValue()); + } + }); + addPropertyChangeListener(NativeSearchFieldSupport.FIND_ACTION_PROPERTY, new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + ActionListener oldAction = (ActionListener) evt.getOldValue(); + firePropertyChange("findAction", oldAction, evt.getNewValue()); + } + }); + } + + /** + * Returns the current {@link SearchMode}. + * + * @return the current {@link SearchMode}. + */ + public SearchMode getSearchMode() { + return searchMode; + } + + /** + * Returns true if the current {@link SearchMode} is + * {@link SearchMode#INSTANT}. + * + * @return true if the current {@link SearchMode} is + * {@link SearchMode#INSTANT} + */ + public boolean isInstantSearchMode() { + return SearchMode.INSTANT.equals(getSearchMode()); + } + + /** + * Returns true if the current {@link SearchMode} is + * {@link SearchMode#REGULAR}. + * + * @return true if the current {@link SearchMode} is + * {@link SearchMode#REGULAR} + */ + public boolean isRegularSearchMode() { + return SearchMode.REGULAR.equals(getSearchMode()); + } + + /** + * Sets the current search mode. See {@link SearchMode} for a description of + * the different search modes. + * + * @param searchMode + * {@link SearchMode#INSTANT} or {@link SearchMode#REGULAR} + */ + public void setSearchMode(SearchMode searchMode) { + firePropertyChange("searchMode", this.searchMode, this.searchMode = searchMode); + } + + /** + * Get the instant search delay in milliseconds. The default delay is 50 + * Milliseconds. + * + * @see {@link #setInstantSearchDelay(int)} + * @return the instant search delay in milliseconds + */ + public int getInstantSearchDelay() { + return instantSearchDelay; + } + + /** + * Set the instant search delay in milliseconds. In + * {@link SearchMode#INSTANT}, when the user changes the text, an action + * event will be fired after the specified instant search delay. + * + * It is recommended to use a instant search delay to avoid the firing of + * unnecessary events. For example when the user replaces the whole text + * with a different text the search fields underlying {@link Document} + * typically fires 2 document events. The first one, because the old text is + * removed and the second one because the new text is inserted. If the + * instant search delay is 0, this would result in 2 action events being + * fired. When a instant search delay is used, the first document event + * typically is ignored, because the second one is fired before the delay is + * over, which results in a correct behavior because only the last and only + * relevant event will be delivered. + * + * @param instantSearchDelay + */ + public void setInstantSearchDelay(int instantSearchDelay) { + firePropertyChange("instantSearchDelay", this.instantSearchDelay, this.instantSearchDelay = instantSearchDelay); + } + + /** + * Get the current {@link LayoutStyle}. + * + * @return + */ + public LayoutStyle getLayoutStyle() { + return layoutStyle; + } + + /** + * Returns true if the current {@link LayoutStyle} is + * {@link LayoutStyle#VISTA}. + * + * @return + */ + public boolean isVistaLayoutStyle() { + return LayoutStyle.VISTA.equals(getLayoutStyle()); + } + + /** + * Returns true if the current {@link LayoutStyle} is + * {@link LayoutStyle#MAC}. + * + * @return + */ + public boolean isMacLayoutStyle() { + return LayoutStyle.MAC.equals(getLayoutStyle()); + } + + /** + * Set the current {@link LayoutStyle}. See {@link LayoutStyle} for a + * description of how this affects layout and behavior of the search field. + * + * @param layoutStyle + * {@link LayoutStyle#MAC} or {@link LayoutStyle#VISTA} + */ + public void setLayoutStyle(LayoutStyle layoutStyle) { + layoutStyleSet = true; + firePropertyChange("layoutStyle", this.layoutStyle, this.layoutStyle = layoutStyle); + } + + /** + * Set the margin space around the search field's text. + * + * @see javax.swing.text.JTextComponent#setMargin(Insets) + */ + @Override + public void setMargin(Insets m) { + super.setMargin(m); + } + + /** + * Returns the cancel action, or an instance of {@link ClearAction}, if + * none has been set. + * + * @return the cancel action + */ + public final ActionListener getCancelAction() { + ActionListener a = NativeSearchFieldSupport.getCancelAction(this); + if (a == null) { + a = new ClearAction(); + } + return a; + } + + /** + * Sets the action that is invoked, when the user presses the 'Esc' key or + * clicks the cancel button. + * + * @param cancelAction + */ + public final void setCancelAction(ActionListener cancelAction) { + NativeSearchFieldSupport.setCancelAction(this, cancelAction); + } + + /** + * Returns the cancel button. + * + * Calls {@link #createCancelButton()} to create the cancel button and + * registers an {@link ActionListener} that delegates actions to the + * {@link ActionListener} returned by {@link #getCancelAction()}, if + * needed. + * + * @return the cancel button + */ + public final JButton getCancelButton() { + if (cancelButton == null) { + cancelButton = createCancelButton(); + cancelButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + getCancelAction().actionPerformed(e); + } + }); + } + return cancelButton; + } + + /** + * Creates and returns the cancel button. + * + * Override to use a custom cancel button. + * + * @see #getCancelButton() + * @return the cancel button + */ + protected JButton createCancelButton() { + BuddyButton btn = new BuddyButton(); + + return btn; + } + + /** + * Returns the action that is invoked when the enter key is pressed or the + * find button is clicked. If no action has been set, a new instance of + * {@link FindAction} will be returned. + * + * @return the find action + */ + public final ActionListener getFindAction() { + ActionListener a = NativeSearchFieldSupport.getFindAction(this); + if (a == null) { + a = new FindAction(); + } + return a; + } + + /** + * Sets the action that is invoked when the enter key is pressed or the find + * button is clicked. + * + * @return the find action + */ + public final void setFindAction(ActionListener findAction) { + NativeSearchFieldSupport.setFindAction(this, findAction); + } + + /** + * Returns the find button. + * + * Calls {@link #createFindButton()} to create the find button and registers + * an {@link ActionListener} that delegates actions to the + * {@link ActionListener} returned by {@link #getFindAction()}, if needed. + * + * @return the find button + */ + public final JButton getFindButton() { + if (findButton == null) { + findButton = createFindButton(); + findButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + getFindAction().actionPerformed(e); + } + }); + } + return findButton; + } + + /** + * Creates and returns the find button. The buttons action is set to the + * action returned by {@link #getSearchAction()}. + * + * Override to use a custom find button. + * + * @see #getFindButton() + * @return the find button + */ + protected JButton createFindButton() { + BuddyButton btn = new BuddyButton(); + + return btn; + } + + /** + * Returns the popup button. If a find popup menu is set, it will be + * displayed when this button is clicked. + * + * This button will only be visible, if {@link #isUseSeperatePopupButton()} + * returns true. Otherwise the popup menu will be displayed + * when the find button is clicked. + * + * @return the popup button + */ + public final JButton getPopupButton() { + if (popupButton == null) { + popupButton = createPopupButton(); + } + return popupButton; + } + + /** + * Creates and returns the popup button. Override to use a custom popup + * button. + * + * @see #getPopupButton() + * @return the popup button + */ + protected JButton createPopupButton() { + return new BuddyButton(); + } + + /** + * Returns true if the popup button should be visible and + * used for displaying the find popup menu. Otherwise, the find popup menu + * will be displayed when the find button is clicked. + * + * @return true if the popup button should be used + */ + public boolean isUseSeperatePopupButton() { + return useSeperatePopupButton; + } + + /** + * Set if the popup button should be used for displaying the find popup + * menu. + * + * @param useSeperatePopupButton + */ + public void setUseSeperatePopupButton(boolean useSeperatePopupButton) { + useSeperatePopupButtonSet = true; + firePropertyChange("useSeperatePopupButton", this.useSeperatePopupButton, + this.useSeperatePopupButton = useSeperatePopupButton); + } + + public boolean isUseNativeSearchFieldIfPossible() { + return NativeSearchFieldSupport.isSearchField(this); + } + + public void setUseNativeSearchFieldIfPossible(boolean useNativeSearchFieldIfPossible) { + TextUIWrapper.getDefaultWrapper().uninstall(this); + NativeSearchFieldSupport.setSearchField(this, useNativeSearchFieldIfPossible); + TextUIWrapper.getDefaultWrapper().install(this, true); + updateUI(); + } + + /** + * Updates the cancel, find and popup buttons enabled state in addition to + * setting the search fields editable state. + * + * @see #updateButtonState() + * @see javax.swing.text.JTextComponent#setEditable(boolean) + */ + @Override + public void setEditable(boolean b) { + super.setEditable(b); + updateButtonState(); + } + + /** + * Updates the cancel, find and popup buttons enabled state in addition to + * setting the search fields enabled state. + * + * @see #updateButtonState() + * @see javax.swing.text.JTextComponent#setEnabled(boolean) + */ + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + updateButtonState(); + } + + /** + * Enables the cancel action if this search field is editable and enabled, + * otherwise it will be disabled. Enabled the search action and popup button + * if this search field is enabled, otherwise it will be disabled. + */ + protected void updateButtonState() { + getCancelButton().setEnabled(isEditable() & isEnabled()); + getFindButton().setEnabled(isEnabled()); + getPopupButton().setEnabled(isEnabled()); + } + + /** + * Sets the popup menu that will be displayed when the popup button is + * clicked. If a find popup menu is set and + * {@link #isUseSeperatePopupButton()} returns false, the + * popup button will be displayed instead of the find button. Otherwise the + * popup button will be displayed in addition to the find button. + * + * The find popup menu is managed using {@link NativeSearchFieldSupport} to + * achieve compatibility with the native search field support provided by + * the Mac Look And Feel since Mac OS 10.5. + * + * If a recent searches save key has been set and therefore a recent + * searches popup menu is installed, this method does nothing. You must + * first remove the recent searches save key, by calling + * {@link #setRecentSearchesSaveKey(String)} with a null + * parameter. + * + * @see #setRecentSearchesSaveKey(String) + * @see RecentSearches + * @param findPopupMenu + * the popup menu, which will be displayed when the popup button + * is clicked + */ + public void setFindPopupMenu(JPopupMenu findPopupMenu) { + if (isManagingRecentSearches()) { + return; + } + + NativeSearchFieldSupport.setFindPopupMenu(this, findPopupMenu); + } + + /** + * Returns the find popup menu. + * + * @see #setFindPopupMenu(JPopupMenu) + * @return the find popup menu + */ + public JPopupMenu getFindPopupMenu() { + return NativeSearchFieldSupport.getFindPopupMenu(this); + } + + /** + * TODO + * + * @return + */ + public final boolean isManagingRecentSearches() { + return recentSearches != null; + } + + private boolean isValidRecentSearchesKey(String key) { + return key != null && key.length() > 0; + } + + /** + * Returns the key used to persist recent searches. + * + * @see #setRecentSearchesSaveKey(String) + * @return + */ + public String getRecentSearchesSaveKey() { + return recentSearchesSaveKey; + } + + /** + * Installs and manages a recent searches popup menu as the find popup menu, + * if recentSearchesSaveKey is not null. Otherwise, removes + * the popup menu and stops managing recent searches. + * + * @see #setFindAction(ActionListener) + * @see #isManagingRecentSearches() + * @see RecentSearches + * + * @param recentSearchesSaveKey + * this key is used to persist the recent searches. + */ + public void setRecentSearchesSaveKey(String recentSearchesSaveKey) { + String oldName = getRecentSearchesSaveKey(); + this.recentSearchesSaveKey = recentSearchesSaveKey; + + if (recentSearches != null) { + // set null before uninstalling. otherwise the popup menu is not + // allowed to be changed. + RecentSearches rs = recentSearches; + recentSearches = null; + rs.uninstall(this); + } + + if (isValidRecentSearchesKey(recentSearchesSaveKey)) { + recentSearches = new RecentSearches(recentSearchesSaveKey); + recentSearches.install(this); + } + + firePropertyChange("recentSearchesSaveKey", oldName, this.recentSearchesSaveKey); + } + + /** + * TODO + * + * @return + */ + public RecentSearches getRecentSearches() { + return recentSearches; + } + + /** + * Returns the {@link Timer} used to delay the firing of action events in + * instant search mode when the user enters text. + * + * This timer calls {@link #postActionEvent()}. + * + * @return the {@link Timer} used to delay the firing of action events + */ + public Timer getInstantSearchTimer() { + if (instantSearchTimer == null) { + instantSearchTimer = new Timer(0, new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + postActionEvent(); + } + }); + instantSearchTimer.setRepeats(false); + } + return instantSearchTimer; + } + + /** + * Returns true if this search field is the focus owner or + * the find popup menu is visible. + * + * This is a hack to make the search field paint the focus indicator in Mac + * OS X Aqua when the find popup menu is visible. + * + * @return true if this search field is the focus owner or + * the find popup menu is visible + */ + @Override + public boolean hasFocus() { + if (getFindPopupMenu() != null && getFindPopupMenu().isVisible()) { + return true; + } + return super.hasFocus(); + } + + /** + * Overriden to also update the find popup menu if set. + */ + @Override + public void updateUI() { + super.updateUI(); + if (getFindPopupMenu() != null) { + SwingUtilities.updateComponentTreeUI(getFindPopupMenu()); + } + } + + /** + * Hack to enable the UI delegate to set default values depending on the + * current Look and Feel, without overriding custom values. + */ + @Override + public void setPromptFontStyle(Integer fontStyle) { + super.setPromptFontStyle(fontStyle); + promptFontStyleSet = true; + } + + /** + * Hack to enable the UI delegate to set default values depending on the + * current Look and Feel, without overriding custom values. + * + * @param propertyName + * the name of the property to change + * @param value + * the new value of the property + */ + public void customSetUIProperty(String propertyName, Object value) { + customSetUIProperty(propertyName, value, false); + } + + /** + * Hack to enable the UI delegate to set default values depending on the + * current Look and Feel, without overriding custom values. + * + * @param propertyName + * the name of the property to change + * @param value + * the new value of the property + * @param override + * override custom values + */ + public void customSetUIProperty(String propertyName, Object value, boolean override) { + if (propertyName == "useSeperatePopupButton") { + if (!useSeperatePopupButtonSet || override) { + setUseSeperatePopupButton(((Boolean) value).booleanValue()); + useSeperatePopupButtonSet = false; + } + } else if (propertyName == "layoutStyle") { + if (!layoutStyleSet || override) { + setLayoutStyle(LayoutStyle.valueOf(value.toString())); + layoutStyleSet = false; + } + } else if (propertyName == "promptFontStyle") { + if (!promptFontStyleSet || override) { + setPromptFontStyle((Integer) value); + promptFontStyleSet = false; + } + } else { + throw new IllegalArgumentException(); + } + } + + /** + * Overriden to prevent any delayed {@link ActionEvent}s from being sent + * after posting this action. + * + * For example, if the current {@link SearchMode} is + * {@link SearchMode#INSTANT} and the instant search delay is greater 0. The + * user enters some text and presses enter. This method will be invoked + * immediately because the users presses enter. However, this method would + * be invoked after the instant search delay, if we would not prevent it + * here. + */ + @Override + public void postActionEvent() { + getInstantSearchTimer().stop(); + super.postActionEvent(); + } + + /** + * Invoked when the the cancel button or the 'Esc' key is pressed. Sets the + * text in the search field to null. + * + */ + class ClearAction extends AbstractAction { + public ClearAction() { + putValue(SHORT_DESCRIPTION, "Clear Search Text"); + } + + /** + * Calls {@link #clear()}. + */ + @Override + public void actionPerformed(ActionEvent e) { + clear(); + } + + /** + * Sets the search field's text to null and requests the + * focus for the search field. + */ + public void clear() { + setText(null); + requestFocusInWindow(); + } + } + + /** + * Invoked when the find button is pressed. + */ + public class FindAction extends AbstractAction { + public FindAction() { + } + + /** + * In regular search mode posts an action event if the search field is + * the focus owner. + * + * Also requests the focus for the search field and selects the whole + * text. + */ + @Override + public void actionPerformed(ActionEvent e) { + if (isFocusOwner() && isRegularSearchMode()) { + postActionEvent(); + } + requestFocusInWindow(); + selectAll(); + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/JXSearchPanel.java b/src/main/java/org/jdesktop/swingx/JXSearchPanel.java new file mode 100644 index 0000000000..49e92d8b3d --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXSearchPanel.java @@ -0,0 +1,275 @@ +/* + * $Id: JXSearchPanel.java 4158 2012-02-03 18:29:40Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.decorator.Highlighter; +import org.jdesktop.swingx.renderer.DefaultListRenderer; +import org.jdesktop.swingx.renderer.LocalizableStringValue; +import org.jdesktop.swingx.renderer.StringValue; +import org.jdesktop.swingx.search.PatternMatcher; +import org.jdesktop.swingx.search.PatternModel; + +import javax.swing.*; +import java.util.*; +import java.util.regex.Pattern; +/** + *

+ * {@code JXSearchPanel} provides complex searching features. Users are able to + * specify searching rules, enter searching text (including regular + * expressions), and toggle case-sensitivity. + *

+ *

+ * One of the main features that {@code JXSearchPanel} provides is the ability + * to update {@link PatternMatcher}s. To highlight text with a + * {@link Highlighter}, you need to update the highlighter via a pattern + * matcher. + *

+ *
+ * public class PatternHandler implements PatternMatcher {
+ * 
+ *     private Highlighter highlighter;
+ * 
+ *     private Pattern pattern;
+ * 
+ *     public void setPattern(Pattern pattern) {
+ *         this.pattern = pattern;
+ *         highlighter.setHighlightPredicate(new PatternPredicate(pattern));
+ *     }
+ * 
+ * }
+ * 
+ *

+ * TODO: allow custom PatternModel and/or access to configuration of bound + * PatternModel. + *

+ *

+ * TODO: fully support control of multiple PatternMatchers. + *

+ * + * @author Ramesh Gupta + * @author Jeanette Winzenburg + */ +@JavaBean +public class JXSearchPanel extends AbstractPatternPanel { + /** + * The action command key. + */ + public static final String MATCH_RULE_ACTION_COMMAND = "selectMatchRule"; + + private JXComboBox searchCriteria; + + private List patternMatchers; + + + /** + * Creates a search panel. + */ + public JXSearchPanel() { + initComponents(); + build(); + initActions(); + bind(); + getPatternModel().setIncremental(true); + } + +//----------------- accessing public properties + + /** + * Adds a pattern matcher. + * + * @param matcher + * the matcher to add. + */ + public void addPatternMatcher(PatternMatcher matcher) { + getPatternMatchers().add(matcher); + updateFieldName(matcher); + } + + /** + * sets the PatternFilter control. + * + * PENDING: change to do a addPatternMatcher to enable multiple control. + * + */ +// public void setPatternFilter(PatternFilter filter) { +// getPatternMatchers().add(filter); +// updateFieldName(filter); +// } + + /** + * set the label of the search combo. + * + * @param name + * the label + */ + public void setFieldName(String name) { + String old = searchLabel.getText(); + searchLabel.setText(name); + firePropertyChange("fieldName", old, searchLabel.getText()); + } + + /** + * returns the label of the search combo. + * + */ + public String getFieldName() { + return searchLabel.getText(); + } + + /** + * returns the current compiled Pattern. + * + * @return the current compiled Pattern + */ + public Pattern getPattern() { + return patternModel.getPattern(); + } + + /** + * @param matcher + */ + protected void updateFieldName(PatternMatcher matcher) { + +// if (matcher instanceof PatternFilter) { +// PatternFilter filter = (PatternFilter) matcher; +// searchLabel.setText(filter.getColumnName()); +// } else { + if (searchLabel.getText().length() == 0) { // ugly hack + searchLabel.setText("Field"); + /** TODO: Remove this hack!!! */ +// } + } + } + + // ---------------- action callbacks + + /** + * Updates the pattern matchers. + */ + @Override + public void match() { + for (Iterator iter = getPatternMatchers().iterator(); iter.hasNext();) { + iter.next().setPattern(getPattern()); + + } + } + + /** + * set's the PatternModel's MatchRule to the selected in combo. + * + * NOTE: this + * is public as an implementation side-effect! + * No need to ever call directly. + */ + public void updateMatchRule() { + getPatternModel().setMatchRule( + (String) searchCriteria.getSelectedItem()); + } + + private List getPatternMatchers() { + if (patternMatchers == null) { + patternMatchers = new ArrayList(); + } + return patternMatchers; + } + + //---------------- init actions and model + + @Override + protected void initExecutables() { + super.initExecutables(); + getActionMap().put(MATCH_RULE_ACTION_COMMAND, + createBoundAction(MATCH_RULE_ACTION_COMMAND, "updateMatchRule")); + } + + + //--------------------- binding support + + + + /** + * bind the components to the patternModel/actions. + */ + @Override + protected void bind() { + super.bind(); + List matchRules = getPatternModel().getMatchRules(); + // PENDING: map rules to localized strings + ComboBoxModel model = new DefaultComboBoxModel(matchRules.toArray()); + model.setSelectedItem(getPatternModel().getMatchRule()); + searchCriteria.setModel(model); + searchCriteria.setAction(getAction(MATCH_RULE_ACTION_COMMAND)); + searchCriteria.setRenderer(new DefaultListRenderer(createStringValue(getLocale()))); + + } + + + private StringValue createStringValue(Locale locale) { + // TODO Auto-generated method stub + Map keys = new HashMap(); + keys.put(PatternModel.MATCH_RULE_CONTAINS, + PatternModel.MATCH_RULE_CONTAINS); + keys.put(PatternModel.MATCH_RULE_ENDSWITH, + PatternModel.MATCH_RULE_ENDSWITH); + keys.put(PatternModel.MATCH_RULE_EQUALS, + PatternModel.MATCH_RULE_EQUALS); + keys.put(PatternModel.MATCH_RULE_STARTSWITH, + PatternModel.MATCH_RULE_STARTSWITH); + return new LocalizableStringValue(keys, PatternModel.SEARCH_PREFIX, locale); + } + + /** + * {@inheritDoc} + */ + @Override + protected void updateLocaleState(Locale locale) { + // TODO Auto-generated method stub + super.updateLocaleState(locale); + searchCriteria.setRenderer(new DefaultListRenderer(createStringValue(locale))); + } + + //------------------------ init ui + /** + * build container by adding all components. + * PRE: all components created. + */ + private void build() { + add(searchLabel); + add(searchCriteria); + add(searchField); + add(matchCheck); + } + + /** + * create contained components. + * + * + */ + @Override + protected void initComponents() { + super.initComponents(); + searchCriteria = new JXComboBox(); + } + + +} diff --git a/src/main/java/org/jdesktop/swingx/JXStatusBar.java b/src/main/java/org/jdesktop/swingx/JXStatusBar.java new file mode 100644 index 0000000000..3ff00f0f0b --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXStatusBar.java @@ -0,0 +1,318 @@ +/* + * $Id: JXStatusBar.java 4147 2012-02-01 17:13:24Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx; + + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.plaf.LookAndFeelAddons; +import org.jdesktop.swingx.plaf.StatusBarAddon; +import org.jdesktop.swingx.plaf.StatusBarUI; + +import javax.swing.*; +import java.awt.*; + +/** + *

A container for JComponents that is typically placed at + * the bottom of a form and runs the entire width of the form. There are 3 + * important functions that JXStatusBar provides. + * First, JXStatusBar provides a hook for a pluggable look. + * There is a definite look associated with status bars on windows, for instance. + * By implementing a subclass of {@link JComponent}, we provide a way for the + * pluggable look and feel system to modify the look of the status bar.

+ * + *

Second, JXStatusBar comes with its own layout manager. Each item is added to + * the JXStatusBar with a JXStatusBar.Constraint + * as the constraint argument. The JXStatusBar.Constraint contains + * an Insets object, as well as a ResizeBehavior, + * which can be FIXED or FILL. The resize behaviour applies to the width of + * components. All components added will maintain there preferred height, and the + * height of the JXStatusBar will be the height of the highest + * component plus insets.

+ * + *

A constraint with JXStatusBar.Constraint.ResizeBehavior.FIXED + * will cause the component to occupy a fixed area on the JXStatusBar. + * The size of the area remains constant when the JXStatusBar is resized. + * A constraint with this behavior may also take a width value, see + * {@link Constraint#setFixedWidth(int)}. The width is a preferred + * minimum width. If the component preferred width is greater than the constraint + * width, the component width will apply.

+ * + *

All components with constraint JXStatusBar.Constraint.ResizeBehavior.FILL + * will share equally any spare space in the JXStatusBar. Spare space + * is that left over after allowing for all FIXED component and the preferred + * width of FILL components, plus insets + * + *

Constructing a JXStatusBar is very straightforward: + *


+ *      JXStatusBar bar = new JXStatusBar();
+ *      JLabel statusLabel = new JLabel("Ready");
+ *      JXStatusBar.Constraint c1 = new JXStatusBar.Constraint() 
+ *      c1.setFixedWidth(100);
+ *      bar.add(statusLabel, c1);     // Fixed width of 100 with no inserts
+ *      JXStatusBar.Constraint c2 = new JXStatusBarConstraint(
+ *              JXStatusBar.Constraint.ResizeBehavior.FILL) // Fill with no inserts
+ *      JProgressBar pbar = new JProgressBar();
+ *      bar.add(pbar, c2);            // Fill with no inserts - will use remaining space
+ * 

+ * + *

Two common use cases for status bars include tracking application status and + * progress. JXStatusBar does not manage these tasks, but instead special components + * exist or can be created that do manage these tasks. For example, if your application + * has a TaskManager or some other repository of currently running jobs, you could + * easily create a TaskManagerProgressBar that tracks those jobs. This component + * could then be added to the JXStatusBar like any other component.

+ * + *

Client Properties

+ *

The BasicStatusBarUI.AUTO_ADD_SEPARATOR client property can be specified, which + * will disable the auto-adding of separators. In this case, you must add your own + * JSeparator components. To use: + *


+ *      JXStatusBar sbar = new JXStatusBar();
+ *      sbar.putClientProperty(BasicStatusBarUI.AUTO_ADD_SEPARATOR, false);
+ *      sbar.add(comp1);
+ *      sbar.add(new JSeparator(JSeparator.VERTICAL));
+ *      sbar.add(comp2);
+ *      sbar.add(comp3);
+ *  

+ * + * @status REVIEWED + * + * @author pdoubleya + * @author rbair + * @author Karl George Schaefer + */ +@JavaBean +public class JXStatusBar extends JComponent { + /** + * @see #getUIClassID + * @see #readObject + */ + public static final String uiClassID = "StatusBarUI"; + + //TODO how to handle UI delegate setting of primitive? + private boolean resizeHandleEnabled; + + /** + * Initialization that would ideally be moved into various look and feel + * classes. + */ + static { + LookAndFeelAddons.contribute(new StatusBarAddon()); + } + + /** + * Creates a new JXStatusBar + */ + public JXStatusBar() { + super(); + updateUI(); + } + + /** + * @param resizeHandleEnabled the resizeHandleEnabled to set + */ + public void setResizeHandleEnabled(boolean resizeHandleEnabled) { + boolean oldValue = isResizeHandleEnabled(); + this.resizeHandleEnabled = resizeHandleEnabled; + firePropertyChange("resizeHandleEnabled", oldValue, isResizeHandleEnabled()); + } + + /** + * @return the resizeHandleEnabled + */ + public boolean isResizeHandleEnabled() { + return resizeHandleEnabled; + } + + /** + * Returns the look and feel (L&F) object that renders this component. + * + * @return the StatusBarUI object that renders this component + */ + public StatusBarUI getUI() { + return (StatusBarUI) ui; + } + + /** + * Sets the look and feel (L&F) object that renders this component. + * + * @param ui + * the StatusBarUI L&F object + * @see javax.swing.UIDefaults#getUI + * @beaninfo + * bound: true + * hidden: true + * attribute: visualUpdate true + * description: The component's look and feel delegate. + */ + public void setUI(StatusBarUI ui) { + super.setUI(ui); + } + + /** + * Returns a string that specifies the name of the L&F class that renders + * this component. + * + * @return "StatusBarUI" + * @see JComponent#getUIClassID + * @see javax.swing.UIDefaults#getUI + * @beaninfo expert: true description: A string that specifies the name of + * the L&F class. + */ + @Override + public String getUIClassID() { + return uiClassID; + } + + /** + * Notification from the UIManager that the L&F has changed. + * Replaces the current UI object with the latest version from the + * UIManager. + * + * @see JComponent#updateUI + */ + @Override + public void updateUI() { + setUI((StatusBarUI) LookAndFeelAddons + .getUI(this, StatusBarUI.class)); + } + + /** + * The constraint object to be used with the JXStatusBar. It takes + * a ResizeBehaviour, Insets and a Width. Width is only applicable for + * ResizeBehavior.FIXED. @see JXStatusBar class documentation. + */ + public static class Constraint { + public enum ResizeBehavior {FILL, FIXED} + + private final Insets insets; + private final ResizeBehavior resizeBehavior; + private int fixedWidth = 0; + + /** + * Creates a new Constraint with default FIXED behaviour and no insets. + */ + public Constraint() { + this(ResizeBehavior.FIXED, null); + } + + /** + * Creates a new Constraint with default FIXED behaviour and the given insets + * + * @param insets may be null. If null, an Insets with 0 values will be used. + */ + public Constraint(Insets insets) { + this(ResizeBehavior.FIXED, insets); + } + + /** + * Creates a new Constraint with default FIXED behaviour and the given fixed + * width. + * + * @param fixedWidth must be >= 0 + */ + public Constraint(int fixedWidth) { + this(fixedWidth, null); + } + + /** + * Creates a new Constraint with default FIXED behaviour and the given fixed + * width, and using the given Insets. + * + * @param fixedWidth must be >= 0 + * @param insets may be null. If null, an Insets with 0 values will be used. + */ + public Constraint(int fixedWidth, Insets insets) { + if (fixedWidth < 0) { + throw new IllegalArgumentException("fixedWidth must be >= 0"); + } + this.fixedWidth = fixedWidth; + this.insets = insets == null ? new Insets(0, 0, 0, 0) : (Insets)insets.clone(); + this.resizeBehavior = ResizeBehavior.FIXED; + } + + /** + * Creates a new Constraint with the specified resize behaviour and no insets + * + * @param resizeBehavior - either JXStatusBar.Constraint.ResizeBehavior.FIXED + * or JXStatusBar.Constraint.ResizeBehavior.FILL. + */ + public Constraint(ResizeBehavior resizeBehavior) { + this(resizeBehavior, null); + } + + /** + * Creates a new Constraint with the specified resize behavior and insets. + * + * @param resizeBehavior - either JXStatusBar.Constraint.ResizeBehavior.FIXED + * or JXStatusBar.Constraints.ResizeBehavior.FILL. + * @param insets may be null. If null, an Insets with 0 values will be used. + */ + public Constraint(ResizeBehavior resizeBehavior, Insets insets) { + this.resizeBehavior = resizeBehavior; + this.insets = insets == null ? new Insets(0, 0, 0, 0) : (Insets)insets.clone(); + } + + /** + * Set the fixed width the component added with this + * constraint will occupy on the JXStatusBar. Only applies + * to ResizeBehavior.FIXED. Will be ignored for ResizeBehavior.FILL. + * + * @param width - minimum width component will occupy. If 0, the preferred + * width of the component will be used. + * The width specified must be >= 0 + */ + public void setFixedWidth(int width) { + if (width < 0) { + throw new IllegalArgumentException("width must be >= 0"); + } + fixedWidth = resizeBehavior == ResizeBehavior.FIXED ? width : 0; + } + + /** + * Returns the ResizeBehavior. + * + * @return ResizeBehavior + */ + public ResizeBehavior getResizeBehavior() { + return resizeBehavior; + } + + /** + * Returns the insets. + * + * @return insets + */ + public Insets getInsets() { + return (Insets)insets.clone(); + } + + /** + * Get fixed width. Width is zero for resize behavior FILLED + * @return the width of this constraint + */ + public int getFixedWidth() { + return fixedWidth; + } + } + +} diff --git a/src/main/java/org/jdesktop/swingx/JXTable.java b/src/main/java/org/jdesktop/swingx/JXTable.java new file mode 100644 index 0000000000..419ffb5bff --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXTable.java @@ -0,0 +1,4399 @@ +/* + * $Id: JXTable.java 4266 2012-12-05 16:34:37Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.action.AbstractActionExt; +import org.jdesktop.swingx.action.BoundAction; +import org.jdesktop.swingx.decorator.ComponentAdapter; +import org.jdesktop.swingx.decorator.CompoundHighlighter; +import org.jdesktop.swingx.decorator.Highlighter; +import org.jdesktop.swingx.decorator.ResetDTCRColorHighlighter; +import org.jdesktop.swingx.event.TableColumnModelExtListener; +import org.jdesktop.swingx.hyperlink.HyperlinkAction; +import org.jdesktop.swingx.plaf.*; +import org.jdesktop.swingx.renderer.*; +import org.jdesktop.swingx.rollover.RolloverProducer; +import org.jdesktop.swingx.rollover.TableRolloverController; +import org.jdesktop.swingx.rollover.TableRolloverProducer; +import org.jdesktop.swingx.search.AbstractSearchable; +import org.jdesktop.swingx.search.SearchFactory; +import org.jdesktop.swingx.search.Searchable; +import org.jdesktop.swingx.search.TableSearchable; +import org.jdesktop.swingx.sort.*; +import org.jdesktop.swingx.table.*; + +import javax.swing.*; +import javax.swing.RowSorter.SortKey; +import javax.swing.border.LineBorder; +import javax.swing.event.*; +import javax.swing.table.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.print.PrinterException; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.net.URI; +import java.util.List; +import java.util.*; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Enhanced Table component with support for general SwingX sorting/filtering, + * rendering, highlighting, rollover and search functionality. Table specific + * enhancements include runtime configuration options like toggle column + * visibility, column sizing, PENDING JW ... + * + *

Sorting and Filtering

+ * + * JXTable supports sorting and filtering of rows (switched to core sorting). + * + * Additionally, it provides api to apply + * a specific sort order, to toggle the sort order of columns identified + * by view index or column identifier and to reset all sorts. F.i: + * + *

+ * table.setSortOrder("PERSON_ID", SortOrder.DESCENDING);
+ * table.toggleSortOder(4);
+ * table.resetSortOrder();
+ * 
+ * + * Sorting sequence can be configured per column by setting the TableColumnExt's + * comparator property. Sorting can be disabled per column - setting the TableColumnExt's + * sortable or per table by {@link #setSortable(boolean)}. + * The table takes responsibility to propagate these + * properties to the current sorter, if available

+ * + * Note that the enhanced sorting controls are effective only if the RowSorter is + * of type SortController, which it is by default. Different from core JTable, the + * autoCreateRowSorter property is enabled by default. If on, the JXTable creates and + * uses a default row sorter as returned by the createDefaultRowSorter method. + * + *

+ * Typically, a JXTable is sortable by left clicking on column headers. By default, each + * subsequent click on a header reverses the order of the sort, and a sort arrow + * icon is automatically drawn on the header. + * + *

+ * + *

Rendering and Highlighting

+ * + * As all SwingX collection views, a JXTable is a HighlighterClient (PENDING JW: + * formally define and implement, like in AbstractTestHighlighter), that is it + * provides consistent api to add and remove Highlighters which can visually + * decorate the rendering component. + * + *

+ * An example multiple highlighting (default striping as appropriate for the + * current LookAndFeel, cell foreground on matching pattern, and shading a + * column): + * + *


+ * 
+ * Highlighter simpleStriping = HighlighterFactory.createSimpleStriping();
+ * PatternPredicate patternPredicate = new PatternPredicate("ˆM", 1);
+ * ColorHighlighter magenta = new ColorHighlighter(patternPredicate, null,
+ *       Color.MAGENTA, null, Color.MAGENTA);
+ * Highlighter shading = new ShadingColorHighlighter(
+ *       new HighlightPredicate.ColumnHighlightPredicate(1));
+ * 
+ * table.setHighlighters(simpleStriping,
+ *        magenta,
+ *        shading);
+ * 
+ * + *

+ * To fully support, JXTable registers SwingX default table renderers instead of + * core defaults (see {@link DefaultTableRenderer}) The recommended approach for + * customizing rendered content it to intall a DefaultTableRenderer configured + * with a custom String- and/or IconValue. F.i. assuming the cell value is a + * File and should be rendered by showing its name followed and date of last + * change: + * + *


+ * StringValue sv = new StringValue() {
+ *      public String getString(Object value) {
+ *        if (!(value instanceof File)) return StringValues.TO_STRING.getString(value);
+ *        return StringValues.FILE_NAME.getString(value) + ", " 
+ *           + StringValues.DATE_TO_STRING.getString(((File) value).lastModified());
+ * }};
+ * table.setCellRenderer(File.class, new DefaultTableRenderer(sv));
+ * 
+ * + * In addition to super default per-class registration, JXTable registers a default + * renderer for URIs which opens the default application to view the related + * document as supported by Desktop. Note: this action is triggered only if + * rolloverEnabled is true (default value) and the cell is not editable. + * + *

+ * Note: DefaultTableCellRenderer and subclasses require a hack to play + * nicely with Highlighters because it has an internal "color memory" in + * setForeground/setBackground. The hack is applied by default which might lead + * to unexpected side-effects in custom renderers subclassing DTCR. See + * {@link #resetDefaultTableCellRendererHighlighter} for details. + *

+ * + * Note: by default JXTable disables the alternate row striping provided + * by Nimbus, instead it does use the color provided by Nimbus to configure the + * UIColorHighlighter. Like in any other LAF without striping support, + * client code has to explicitly turn on striping by + * setting a Highlighter like: + * + *


+ * table.addHighlighter(HighlighterFactory.createSimpleStriping());
+ * 
+ * + * Alternatively, if client code wants to rely on the LAF provided striping + * support, it can set a property in the UIManager ("early" in the application + * lifetime to prevent JXTable to disable Nimbus handling it. In this case it is + * recommended to not any of the ui-dependent Highlighters provided by the + * HighlighterFactory. + * + *

+ * UIManager.put("Nimbus.keepAlternateRowColor", Boolean.TRUE);
+ * 
+ * + *

Rollover

+ * + * As all SwingX collection views, a JXTable supports per-cell rollover which is + * enabled by default. If enabled, the component fires rollover events on + * enter/exit of a cell which by default is promoted to the renderer if it + * implements RolloverRenderer, that is simulates live behaviour. The rollover + * events can be used by client code as well, f.i. to decorate the rollover row + * using a Highlighter. + * + *

+ * JXTable table = new JXTable();
+ * table.addHighlighter(new ColorHighlighter(HighlightPredicate.ROLLOVER_ROW, 
+ *      null, Color.RED);      
+ * 
+ * + *

Search

+ * + * As all SwingX collection views, a JXTable is searchable. A search action is + * registered in its ActionMap under the key "find". The default behaviour is to + * ask the SearchFactory to open a search component on this component. The + * default keybinding is retrieved from the SearchFactory, typically ctrl-f (or + * cmd-f for Mac). Client code can register custom actions and/or bindings as + * appropriate. + *

+ * + * JXTable provides api to vend a renderer-controlled String representation of + * cell content. This allows the Searchable and Highlighters to use WYSIWYM + * (What-You-See-Is-What-You-Match), that is pattern matching against the actual + * string as seen by the user. + * + *

Column Configuration

+ * + * JXTable's default column model + * is of type TableColumnModelExt which allows management of hidden columns. + * Furthermore, it guarantees to delegate creation and configuration of table columns + * to its ColumnFactory. The factory is meant as the central place to + * customize column configuration. + * + *

+ * Columns can be hidden or shown by setting the visible property on the + * TableColumnExt using {@link TableColumnExt#setVisible(boolean)}. Columns can + * also be shown or hidden from the column control popup. + * + *

+ * The column control popup is triggered by an icon drawn to the far right of + * the column headers, above the table's scrollbar (when installed in a + * JScrollPane). The popup allows the user to select which columns should be + * shown or hidden, as well as to pack columns and turn on horizontal scrolling. + * To show or hide the column control, use the + * {@link #setColumnControlVisible(boolean show)}method. + * + *

+ * You can resize all columns, selected columns, or a single column using the + * methods like {@link #packAll()}. Packing combines several other aspects of a + * JXTable. If horizontal scrolling is enabled using + * {@link #setHorizontalScrollEnabled(boolean)}, then the scrollpane will allow + * the table to scroll right-left, and columns will be sized to their preferred + * size. To control the preferred sizing of a column, you can provide a + * prototype value for the column in the TableColumnExt using + * {@link TableColumnExt#setPrototypeValue(Object)}. The prototype is used as an + * indicator of the preferred size of the column. This can be useful if some + * data in a given column is very long, but where the resize algorithm would + * normally not pick this up. + * + *

+ * + * + *

+ * Keys/Actions registered with this component: + * + *

    + *
  • "find" - open an appropriate search widget for searching cell content. + * The default action registeres itself with the SearchFactory as search target. + *
  • "print" - print the table + *
  • {@link JXTable#HORIZONTALSCROLL_ACTION_COMMAND} - toggle the horizontal + * scrollbar + *
  • {@link JXTable#PACKSELECTED_ACTION_COMMAND} - resize the selected column + * to fit the widest cell content + *
  • {@link JXTable#PACKALL_ACTION_COMMAND} - resize all columns to fit the + * widest cell content in each column + * + *
+ * + *

+ * Key bindings. + * + *

    + *
  • "control F" - bound to actionKey "find". + *
+ * + *

+ * Client Properties. + * + *

    + *
  • {@link JXTable#MATCH_HIGHLIGHTER} - set to Boolean.TRUE to use a + * SearchHighlighter to mark a cell as matching. + *
+ * + * @author Ramesh Gupta + * @author Amy Fowler + * @author Mark Davidson + * @author Jeanette Winzenburg + * + */ +@JavaBean +public class JXTable extends JTable implements TableColumnModelExtListener { + + /** + * + */ + public static final String FOCUS_PREVIOUS_COMPONENT = "focusPreviousComponent"; + + /** + * + */ + public static final String FOCUS_NEXT_COMPONENT = "focusNextComponent"; + + private static final Logger LOG = Logger.getLogger(JXTable.class.getName()); + + /** + * Identifier of show horizontal scroll action, used in JXTable's + * ActionMap. + * + */ + public static final String HORIZONTALSCROLL_ACTION_COMMAND = ColumnControlButton.COLUMN_CONTROL_MARKER + + "horizontalScroll"; + + /** + * Identifier of pack table action, used in JXTable's ActionMap + * . + */ + public static final String PACKALL_ACTION_COMMAND = ColumnControlButton.COLUMN_CONTROL_MARKER + + "packAll"; + + /** + * Identifier of pack selected column action, used in JXTable's + * ActionMap. + */ + public static final String PACKSELECTED_ACTION_COMMAND = ColumnControlButton.COLUMN_CONTROL_MARKER + + "packSelected"; + + /** + * The prefix marker to find table related properties in the + * ResourceBundle. + */ + public static final String UIPREFIX = "JXTable."; + + /** key for client property to use SearchHighlighter as match marker. */ + public static final String MATCH_HIGHLIGHTER = AbstractSearchable.MATCH_HIGHLIGHTER; + + static { + // Hack: make sure the resource bundle is loaded + LookAndFeelAddons.getAddon(); + LookAndFeelAddons.contribute(new TableAddon()); + } + + /** The CompoundHighlighter for the table. */ + protected CompoundHighlighter compoundHighlighter; + + /** + * The key for the client property deciding about whether the color memory + * hack for DefaultTableCellRenderer should be used. + * + * @see #resetDefaultTableCellRendererHighlighter + */ + public static final String USE_DTCR_COLORMEMORY_HACK = "useDTCRColorMemoryHack"; + + /** + * The Highlighter used to hack around DefaultTableCellRenderer's color + * memory. + */ + protected Highlighter resetDefaultTableCellRendererHighlighter; + + /** The ComponentAdapter for model data access. */ + protected ComponentAdapter dataAdapter; + + + + /** Listens for changes from the highlighters. */ + private ChangeListener highlighterChangeListener; + + /** the factory to use for column creation and configuration. */ + private ColumnFactory columnFactory; + + /** The default number of visible rows (in a ScrollPane). */ + private int visibleRowCount = 20; + + /** The default number of visible columns (in a ScrollPane). */ + private int visibleColumnCount = -1; + + + /** + * Flag to indicate if the column control is visible. + */ + private boolean columnControlVisible; + + /** + * ScrollPane's original vertical scroll policy. If the column control is + * visible the policy is set to ALWAYS. + */ + private int verticalScrollPolicy; + + /** + * The component used a column control in the upper trailing corner of an + * enclosing JScrollPane. + */ + private JComponent columnControlButton; + + /** + * Mouse/Motion/Listener keeping track of mouse moved in cell coordinates. + */ + private transient RolloverProducer rolloverProducer; + + /** + * RolloverController: listens to cell over events and repaints + * entered/exited rows. + */ + private transient TableRolloverController linkController; + + /** + * field to store the autoResizeMode while interactively setting horizontal + * scrollbar to visible. + */ + private int oldAutoResizeMode; + + /** + * flag to indicate enhanced auto-resize-off behaviour is on. This is + * set/reset in setHorizontalScrollEnabled. + */ + private boolean intelliMode; + + /** + * internal flag indicating that we are in super.doLayout(). (used in + * columnMarginChanged to not update the resizingCol's prefWidth). + */ + private boolean inLayout; + + /** + * Flag to distinguish internal settings of row height from client code + * settings. The rowHeight will be internally adjusted to font size on + * instantiation and in updateUI if the height has not been set explicitly + * by the application. + * + * @see #adminSetRowHeight(int) + * @see #setRowHeight(int) + */ + protected boolean isXTableRowHeightSet; + + /** property to control search behaviour. */ + protected Searchable searchable; + + /** property to control table's editability as a whole. */ + private boolean editable; + + private Dimension calculatedPrefScrollableViewportSize; + + /** flag to indicate whether the rowSorter is auto-created. */ + private boolean autoCreateRowSorter; + /** flag to indicate if table is interactively sortable. */ + private boolean sortable; + /** flag to indicate whether model update events should trigger resorts. */ + private boolean sortsOnUpdates; + /** flag to indicate that it's unsafe to update sortable-related sorter properties. */ + private boolean ignoreAddColumn; + /** Registry of per-cell string representation. */ + private transient StringValueRegistry stringValueRegistry; + + private SortOrder[] sortOrderCycle; + + + /** Instantiates a JXTable with a default table model, no data. */ + public JXTable() { + init(); + } + + /** + * Instantiates a JXTable with a specific table model. + * + * @param dm The model to use. + */ + public JXTable(TableModel dm) { + super(dm); + init(); + } + + /** + * Instantiates a JXTable with a specific table model. + * + * @param dm The model to use. + */ + public JXTable(TableModel dm, TableColumnModel cm) { + super(dm, cm); + init(); + } + + /** + * Instantiates a JXTable with a specific table model, column model, and + * selection model. + * + * @param dm The table model to use. + * @param cm The column model to use. + * @param sm The list selection model to use. + */ + public JXTable(TableModel dm, TableColumnModel cm, ListSelectionModel sm) { + super(dm, cm, sm); + init(); + } + + /** + * Instantiates a JXTable for a given number of columns and rows. + * + * @param numRows Count of rows to accommodate. + * @param numColumns Count of columns to accommodate. + */ + public JXTable(int numRows, int numColumns) { + super(numRows, numColumns); + init(); + } + + /** + * Instantiates a JXTable with data in a vector or rows and column names. + * + * @param rowData Row data, as a Vector of Objects. + * @param columnNames Column names, as a Vector of Strings. + */ + public JXTable(Vector rowData, Vector columnNames) { + super(rowData, columnNames); + init(); + } + + /** + * Instantiates a JXTable with data in a array or rows and column names. + * + * @param rowData Row data, as a two-dimensional Array of Objects (by row, + * for column). + * @param columnNames Column names, as a Array of Strings. + */ + public JXTable(Object[][] rowData, Object[] columnNames) { + super(rowData, columnNames); + init(); + } + + /** + * Initializes the table for use. + * + */ + private void init() { + putClientProperty(USE_DTCR_COLORMEMORY_HACK, Boolean.TRUE); + initDefaultStringValues(); + sortOrderCycle = DefaultSortController.getDefaultSortOrderCycle(); + setSortsOnUpdates(true); + setSortable(true); + setAutoCreateRowSorter(true); + setRolloverEnabled(true); + setEditable(true); + setTerminateEditOnFocusLost(true); + initActionsAndBindings(); + initFocusBindings(); + // instantiate row height depending ui setting or font size. + updateRowHeightUI(false); + // set to null - don't want hard-coded pixel sizes. + setPreferredScrollableViewportSize(null); + // PENDING: need to duplicate here.. + // why doesn't the call in tableChanged work? + initializeColumnWidths(); + setFillsViewportHeight(true); + updateLocaleState(getLocale()); + } + +//--------------- Rollover support + /** + * Sets the property to enable/disable rollover support. If enabled, this component + * fires property changes on per-cell mouse rollover state, i.e. + * when the mouse enters/leaves a list cell.

+ * + * This can be enabled to show "live" rollover behaviour, f.i. the cursor over a cell + * rendered by a JXHyperlink.

+ * + * The default value is true. + * + * @param rolloverEnabled a boolean indicating whether or not the rollover + * functionality should be enabled. + * + * @see #isRolloverEnabled() + * @see #getLinkController() + * @see #createRolloverProducer() + * @see org.jdesktop.swingx.rollover.RolloverRenderer + */ + public void setRolloverEnabled(boolean rolloverEnabled) { + boolean old = isRolloverEnabled(); + if (rolloverEnabled == old) + return; + if (rolloverEnabled) { + rolloverProducer = createRolloverProducer(); + rolloverProducer.install(this); + getLinkController().install(this); + + } else { + rolloverProducer.release(this); + rolloverProducer = null; + getLinkController().release(); + } + firePropertyChange("rolloverEnabled", old, isRolloverEnabled()); + } + + /** + * Returns a boolean indicating whether or not rollover support is enabled. + * + * @return a boolean indicating whether or not rollover support is enabled. + * + * @see #setRolloverEnabled(boolean) + */ + public boolean isRolloverEnabled() { + return rolloverProducer != null; + } + + /** + * Returns the RolloverController for this component. Lazyly creates the + * controller if necessary, that is the return value is guaranteed to be + * not null.

+ * + * PENDING JW: rename to getRolloverController + * + * @return the RolloverController for this tree, guaranteed to be not null. + * + * @see #setRolloverEnabled(boolean) + * @see #createLinkController() + * @see org.jdesktop.swingx.rollover.RolloverController + */ + protected TableRolloverController getLinkController() { + if (linkController == null) { + linkController = createLinkController(); + } + return linkController; + } + + /** + * Creates and returns a RolloverController appropriate for this component. + * + * @return a RolloverController appropriate for this component. + * + * @see #getLinkController() + * @see org.jdesktop.swingx.rollover.RolloverController + */ + protected TableRolloverController createLinkController() { + return new TableRolloverController(); + } + + /** + * Creates and returns the RolloverProducer to use with this component. + *

+ * + * @return RolloverProducer to use with this component + * + * @see #setRolloverEnabled(boolean) + */ + protected RolloverProducer createRolloverProducer() { + return new TableRolloverProducer(); + } + + + /** + * Returns the column control visible property. + *

+ * + * @return boolean to indicate whether the column control is visible. + * @see #setColumnControlVisible(boolean) + * @see #setColumnControl(JComponent) + */ + public boolean isColumnControlVisible() { + return columnControlVisible; + } + + /** + * Sets the column control visible property. If true and + * JXTable is contained in a JScrollPane, the + * table adds the column control to the trailing corner of the scroll pane. + *

+ * + * Note: if the table is not inside a JScrollPane the column + * control is not shown even if this returns true. In this case it's the + * responsibility of the client code to actually show it. + *

+ * + * The default value is false. + * + * @param visible boolean to indicate if the column control should be shown + * @see #isColumnControlVisible() + * @see #setColumnControl(JComponent) + * + */ + public void setColumnControlVisible(boolean visible) { + if (isColumnControlVisible() == visible) + return; + boolean old = isColumnControlVisible(); + if (old) { + unconfigureColumnControl(); + } + this.columnControlVisible = visible; + if (isColumnControlVisible()) { + configureColumnControl(); + } + firePropertyChange("columnControlVisible", old, !old); + + } + + /** + * Returns the component used as column control. Lazily creates the control + * to the default if it is null. + * + * @return component for column control, guaranteed to be != null. + * @see #setColumnControl(JComponent) + * @see #createDefaultColumnControl() + */ + public JComponent getColumnControl() { + if (columnControlButton == null) { + columnControlButton = createDefaultColumnControl(); + } + return columnControlButton; + } + + /** + * Sets the component used as column control. Updates the enclosing + * JScrollPane if appropriate. Passing a null + * parameter restores the column control to the default. + *

+ * The component is automatically visible only if the + * columnControlVisible property is true and the + * table is contained in a JScrollPane. + * + *

+ * NOTE: from the table's perspective, the column control is simply a + * JComponent to add to and keep in the trailing corner of the + * scrollpane. (if any). It's up the concrete control to configure itself + * from and keep synchronized to the columns' states. + *

+ * + * @param columnControl the JComponent to use as columnControl. + * @see #getColumnControl() + * @see #createDefaultColumnControl() + * @see #setColumnControlVisible(boolean) + * + */ + public void setColumnControl(JComponent columnControl) { + // PENDING JW: release old column control? who's responsible? + // Could implement CCB.autoRelease()? + JComponent old = columnControlButton; + this.columnControlButton = columnControl; + configureColumnControl(); + firePropertyChange("columnControl", old, getColumnControl()); + } + + /** + * Creates the default column control used by this table. This + * implementation returns a ColumnControlButton configured with + * default ColumnControlIcon. + * + * @return the default component used as column control. + * @see #setColumnControl(JComponent) + * @see ColumnControlButton + * @see org.jdesktop.swingx.icon.ColumnControlIcon + */ + protected JComponent createDefaultColumnControl() { + return new ColumnControlButton(this); + } + + /** + * Sets the language-sensitive orientation that is to be used to order the + * elements or text within this component. + *

+ * + * Overridden to work around a core bug: JScrollPane can't cope + * with corners when changing component orientation at runtime. This method + * explicitly re-configures the column control. + *

+ * + * @param o the ComponentOrientation for this table. + * @see Component#setComponentOrientation(ComponentOrientation) + */ + @Override + public void setComponentOrientation(ComponentOrientation o) { + removeColumnControlFromCorners(); + super.setComponentOrientation(o); + configureColumnControl(); + } + + /** + * Sets upper corners in JScrollPane to null if same as getColumnControl(). + * This is a hack around core not coping correctly with component orientation. + * + * @see #setComponentOrientation(ComponentOrientation) + */ + protected void removeColumnControlFromCorners() { + JScrollPane scrollPane = getEnclosingScrollPane(); + if ((scrollPane == null) || !isColumnControlVisible()) return; + removeColumnControlFromCorners(scrollPane, + JScrollPane.UPPER_LEFT_CORNER, JScrollPane.UPPER_RIGHT_CORNER); + } + + private void removeColumnControlFromCorners(JScrollPane scrollPane, String... corners) { + for (String corner : corners) { + if (scrollPane.getCorner(corner) == getColumnControl()) { + scrollPane.setCorner(corner, null); + } + } + } + + /** + * Configures the enclosing JScrollPane. + *

+ * + * Overridden to addionally configure the upper trailing corner with the + * column control. + * + * @see #configureColumnControl() + * + */ + @Override + protected void configureEnclosingScrollPane() { + super.configureEnclosingScrollPane(); + configureColumnControl(); + } + + /** + * Unconfigures the enclosing JScrollPane. + *

+ * + * Overridden to addionally unconfigure the upper trailing corner with the + * column control. + * + * @see #unconfigureColumnControl() + * + */ + @Override + protected void unconfigureEnclosingScrollPane() { + unconfigureColumnControl(); + super.unconfigureEnclosingScrollPane(); + } + + /** + * /** Unconfigures the upper trailing corner of an enclosing + * JScrollPane. + * + * Here: removes the upper trailing corner and resets. + * + * @see #setColumnControlVisible(boolean) + * @see #setColumnControl(JComponent) + */ + protected void unconfigureColumnControl() { + JScrollPane scrollPane = getEnclosingScrollPane(); + if (scrollPane == null) return; + if (verticalScrollPolicy != 0) { + // Fix #155-swingx: reset only if we had forced always before + // PENDING: JW - doesn't cope with dynamically changing the + // policy + // shouldn't be much of a problem because doesn't happen too + // often?? + scrollPane.setVerticalScrollBarPolicy(verticalScrollPolicy); + verticalScrollPolicy = 0; + } + if (isColumnControlVisible()) { + scrollPane.setCorner(JScrollPane.UPPER_TRAILING_CORNER, null); + } + } + + /** + * Configures the upper trailing corner of an enclosing + * JScrollPane. + * + * Adds the ColumnControl if the + * columnControlVisible property is true. + *

+ * + * @see #setColumnControlVisible(boolean) + * @see #setColumnControl(JComponent) + */ + protected void configureColumnControl() { + if (!isColumnControlVisible()) + return; + JScrollPane scrollPane = getEnclosingScrollPane(); + if (scrollPane == null) return; + if (verticalScrollPolicy == 0) { + verticalScrollPolicy = scrollPane.getVerticalScrollBarPolicy(); + } + scrollPane.setCorner(JScrollPane.UPPER_TRAILING_CORNER, + getColumnControl()); + scrollPane + .setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); + } + + /** + * Returns the enclosing JScrollPane of this table, or null if not + * contained in a JScrollPane or not the main view of the scrollPane. + * + * @return the enclosing JScrollPane if this table is the main view or + * null if not. + */ + protected JScrollPane getEnclosingScrollPane() { + Container p = getParent(); + if (p instanceof JViewport) { + Container gp = p.getParent(); + if (gp instanceof JScrollPane) { + JScrollPane scrollPane = (JScrollPane) gp; + // Make certain we are the viewPort's view and not, for + // example, the rowHeaderView of the scrollPane - + // an implementor of fixed columns might do this. + JViewport viewport = scrollPane.getViewport(); + if (viewport == null || viewport.getView() != this) { + return null; + } + return scrollPane; + } + } + return null; + } + + + // --------------------- actions + /** + * Take over ctrl-tab. + * + */ + private void initFocusBindings() { + setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, + new TreeSet()); + setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, + new TreeSet()); + getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put( + KeyStroke.getKeyStroke("ctrl TAB"), FOCUS_NEXT_COMPONENT); + getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put( + KeyStroke.getKeyStroke("shift ctrl TAB"), + FOCUS_PREVIOUS_COMPONENT); + getActionMap().put(FOCUS_NEXT_COMPONENT, + createFocusTransferAction(true)); + getActionMap().put(FOCUS_PREVIOUS_COMPONENT, + createFocusTransferAction(false)); + } + + /** + * Creates and returns an action for forward/backward focus transfer, + * depending on the given flag. + * + * @param forward a boolean indicating the direction of the required focus + * transfer + * @return the action bound to focusTraversal. + */ + private Action createFocusTransferAction(final boolean forward) { + BoundAction action = new BoundAction(null, + forward ? FOCUS_NEXT_COMPONENT : FOCUS_PREVIOUS_COMPONENT); + action.registerCallback(this, forward ? "transferFocus" + : "transferFocusBackward"); + return action; + } + + /** + * A small class which dispatches actions. + *

+ * TODO (?): Is there a way that we can make this static? + *

+ * + * PENDING JW: don't use UIAction ... we are in OO-land! + */ + private class Actions extends UIAction { + Actions(String name) { + super(name); + } + + @Override + public void actionPerformed(ActionEvent evt) { + if ("print".equals(getName())) { + try { + print(); + } catch (PrinterException ex) { + // REMIND(aim): should invoke pluggable application error + // handler + LOG.log(Level.WARNING, "", ex); + } + } else if ("find".equals(getName())) { + doFind(); + } + } + + } + + /** + * Registers additional, per-instance Actions to the this + * table's ActionMap. Binds the search accelerator (as returned by the + * SearchFactory) to the find action. + * + * + */ + private void initActionsAndBindings() { + // Register the actions that this class can handle. + ActionMap map = getActionMap(); + map.put("print", new Actions("print")); + map.put("find", new Actions("find")); + // hack around core bug: cancel editing doesn't fire + // reported against SwingX as of #610-swingx + map.put("cancel", createCancelAction()); + map.put(PACKALL_ACTION_COMMAND, createPackAllAction()); + map.put(PACKSELECTED_ACTION_COMMAND, createPackSelectedAction()); + map + .put(HORIZONTALSCROLL_ACTION_COMMAND, + createHorizontalScrollAction()); + + KeyStroke findStroke = SearchFactory.getInstance() + .getSearchAccelerator(); + getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put( + findStroke, "find"); + } + + /** + * Creates and returns an Action which cancels an ongoing edit correctly. + * Note: the correct thing to do is to call the editor's cancelEditing, the + * wrong thing to do is to call table removeEditor (as core JTable does...). + * So this is a quick hack around a core bug, reported against SwingX in + * #610-swingx. + * + * @return an Action which cancels an edit. + */ + private Action createCancelAction() { + Action action = new AbstractActionExt() { + + @Override + public void actionPerformed(ActionEvent e) { + if (!isEditing()) + return; + getCellEditor().cancelCellEditing(); + } + + @Override + public boolean isEnabled() { + return isEditing(); + } + + }; + return action; + } + + /** + * Creates and returns the default Action for toggling the + * horizontal scrollBar. + */ + private Action createHorizontalScrollAction() { + BoundAction action = new BoundAction(null, + HORIZONTALSCROLL_ACTION_COMMAND); + action.setStateAction(); + action.registerCallback(this, "setHorizontalScrollEnabled"); + action.setSelected(isHorizontalScrollEnabled()); + return action; + } + + /** + * Returns a potentially localized value from the UIManager. The given key + * is prefixed by this table's UIPREFIX before doing the + * lookup. The lookup respects this table's current locale + * property. Returns the key, if no value is found. + * + * @param key the bare key to look up in the UIManager. + * @return the value mapped to UIPREFIX + key or key if no value is found. + */ + protected String getUIString(String key) { + return getUIString(key, getLocale()); + } + + /** + * Returns a potentially localized value from the UIManager for the given + * locale. The given key is prefixed by this table's UIPREFIX + * before doing the lookup. Returns the key, if no value is found. + * + * @param key the bare key to look up in the UIManager. + * @param locale the locale use for lookup + * @return the value mapped to UIPREFIX + key in the given locale, or key if + * no value is found. + */ + protected String getUIString(String key, Locale locale) { + String text = UIManagerExt.getString(UIPREFIX + key, locale); + return text != null ? text : key; + } + + /** + * Creates and returns the default Action for packing the + * selected column. + */ + private Action createPackSelectedAction() { + BoundAction action = new BoundAction(null, PACKSELECTED_ACTION_COMMAND); + action.registerCallback(this, "packSelected"); + action.setEnabled(getSelectedColumnCount() > 0); + return action; + } + + /** + * Creates and returns the default Action for packing all columns. + */ + private Action createPackAllAction() { + BoundAction action = new BoundAction(null, PACKALL_ACTION_COMMAND); + action.registerCallback(this, "packAll"); + return action; + } + + /** + * {@inheritDoc} + *

+ * Overridden to update locale-dependent properties. + * + * @see #updateLocaleState(Locale) + */ + @Override + public void setLocale(Locale locale) { + updateLocaleState(locale); + super.setLocale(locale); + } + + /** + * Updates locale-dependent state to the given Locale. + * + * Here: updates registered column actions' locale-dependent state. + *

+ * + * PENDING: Try better to find all column actions including custom + * additions? Or move to columnControl? + * + * @param locale the Locale to use for value lookup + * @see #setLocale(Locale) + * @see #updateLocaleActionState(String, Locale) + */ + protected void updateLocaleState(Locale locale) { + updateLocaleActionState(HORIZONTALSCROLL_ACTION_COMMAND, locale); + updateLocaleActionState(PACKALL_ACTION_COMMAND, locale); + updateLocaleActionState(PACKSELECTED_ACTION_COMMAND, locale); + } + + /** + * Updates locale-dependent state of action registered with key in + * ActionMap. Does nothing if no action with key is found. + *

+ * + * Here: updates the Action's name property. + * + * @param key the string for lookup in this table's ActionMap + * @see #updateLocaleState(Locale) + */ + protected void updateLocaleActionState(String key, Locale locale) { + Action action = getActionMap().get(key); + if (action == null) + return; + action.putValue(Action.NAME, getUIString(key, locale)); + } + + // ------------------ bound action callback methods + + /** + * Resizes all columns to fit their content. + *

+ * + * By default this method is bound to the pack all columns + * Action and registered in the table's ActionMap. + * + */ + public void packAll() { + packTable(-1); + } + + /** + * Resizes the lead column to fit its content. + *

+ * + * By default this method is bound to the pack selected column + * Action and registered in the table's ActionMap. + */ + public void packSelected() { + int selected = getColumnModel().getSelectionModel() + .getLeadSelectionIndex(); + if (selected >= 0) { + packColumn(selected, -1); + } + } + + /** + * {@inheritDoc} + *

+ * + * Overridden to update the enabled state of the pack selected column + * Action. + */ + @Override + public void columnSelectionChanged(ListSelectionEvent e) { + super.columnSelectionChanged(e); + if (e.getValueIsAdjusting()) + return; + Action packSelected = getActionMap().get(PACKSELECTED_ACTION_COMMAND); + if ((packSelected != null)) { + packSelected.setEnabled(!((ListSelectionModel) e.getSource()) + .isSelectionEmpty()); + } + } + + // ----------------------- scrollable control + + /** + * Sets the enablement of enhanced horizontal scrolling. If enabled, it + * toggles an auto-resize mode which always fills the JViewport + * horizontally and shows the horizontal scrollbar if necessary. + *

+ * + * The default value is false. + *

+ * + * Note: this is not a bound property, though it follows + * bean naming conventions. + * + * PENDING: Probably should be... If so, could be taken by a listening + * Action as in the app-framework. + *

+ * PENDING JW: the name is mis-leading? + * + * @param enabled a boolean indicating whether enhanced auto-resize mode is + * enabled. + * @see #isHorizontalScrollEnabled() + */ + public void setHorizontalScrollEnabled(boolean enabled) { + /* + * PENDING JW: add a "real" mode? Problematic because there are several + * places in core which check for #AUTO_RESIZE_OFF, can't use different + * value without unwanted side-effects. The current solution with + * tagging the #AUTO_RESIZE_OFF by a boolean flag #intelliMode is + * brittle - need to be very careful to turn off again ... Another + * problem is to keep the horizontalScrollEnabled toggling action in + * synch with this property. Yet another problem is the change + * notification: currently this is _not_ a bound property. + */ + if (enabled == (isHorizontalScrollEnabled())) { + return; + } + boolean old = isHorizontalScrollEnabled(); + if (enabled) { + // remember the resizeOn mode if any + if (getAutoResizeMode() != AUTO_RESIZE_OFF) { + oldAutoResizeMode = getAutoResizeMode(); + } + setAutoResizeMode(AUTO_RESIZE_OFF); + // setAutoResizeModel always disables the intelliMode + // must set after calling and update the action again + intelliMode = true; + updateHorizontalAction(); + } else { + setAutoResizeMode(oldAutoResizeMode); + } + firePropertyChange("horizontalScrollEnabled", old, + isHorizontalScrollEnabled()); + } + + /** + * Returns the current setting for horizontal scrolling. + * + * @return the enablement of enhanced horizontal scrolling. + * @see #setHorizontalScrollEnabled(boolean) + */ + public boolean isHorizontalScrollEnabled() { + return intelliMode && getAutoResizeMode() == AUTO_RESIZE_OFF; + } + + /** + * {@inheritDoc} + *

+ * + * Overridden for internal bookkeeping related to the enhanced auto-resize + * behaviour. + *

+ * + * Note: to enable/disable the enhanced auto-resize mode use exclusively + * setHorizontalScrollEnabled, this method can't cope with it. + * + * @see #setHorizontalScrollEnabled(boolean) + * + */ + @Override + public void setAutoResizeMode(int mode) { + if (mode != AUTO_RESIZE_OFF) { + oldAutoResizeMode = mode; + } + intelliMode = false; + super.setAutoResizeMode(mode); + updateHorizontalAction(); + } + + /** + * Synchs selected state of horizontal scrolling Action to + * enablement of enhanced auto-resize behaviour. + */ + protected void updateHorizontalAction() { + Action showHorizontal = getActionMap().get( + HORIZONTALSCROLL_ACTION_COMMAND); + if (showHorizontal instanceof BoundAction) { + ((BoundAction) showHorizontal) + .setSelected(isHorizontalScrollEnabled()); + } + } + + /** + *{@inheritDoc} + *

+ * + * Overridden to support enhanced auto-resize behaviour enabled and + * necessary. + * + * @see #setHorizontalScrollEnabled(boolean) + */ + @Override + public boolean getScrollableTracksViewportWidth() { + boolean shouldTrack = super.getScrollableTracksViewportWidth(); + if (isHorizontalScrollEnabled()) { + return hasExcessWidth(); + } + return shouldTrack; + } + + /** + * Layouts column width. The exact behaviour depends on the + * autoResizeMode property. + *

+ * Overridden to support enhanced auto-resize behaviour enabled and + * necessary. + * + * @see #setAutoResizeMode(int) + * @see #setHorizontalScrollEnabled(boolean) + */ + @Override + public void doLayout() { + int resizeMode = getAutoResizeMode(); + // fool super... + if (isHorizontalScrollEnabled() && hasRealizedParent() + && hasExcessWidth()) { + autoResizeMode = oldAutoResizeMode; + } + inLayout = true; + super.doLayout(); + inLayout = false; + autoResizeMode = resizeMode; + } + + /** + * + * @return boolean to indicate whether the table has a realized parent. + */ + private boolean hasRealizedParent() { + return (getWidth() > 0) && (getParent() != null) + && (getParent().getWidth() > 0); + } + + /** + * PRE: hasRealizedParent() + * + * @return boolean to indicate whether the table has widths excessing + * parent's width + */ + private boolean hasExcessWidth() { + return getPreferredSize().width < getParent().getWidth(); + } + + /** + * {@inheritDoc} + *

+ * + * Overridden to support enhanced auto-resize behaviour enabled and + * necessary. + * + * @see #setHorizontalScrollEnabled(boolean) + */ + @Override + public void columnMarginChanged(ChangeEvent e) { + if (isEditing()) { + removeEditor(); + } + TableColumn resizingColumn = getResizingColumn(); + // Need to do this here, before the parent's + // layout manager calls getPreferredSize(). + if (resizingColumn != null && autoResizeMode == AUTO_RESIZE_OFF + && !inLayout) { + resizingColumn.setPreferredWidth(resizingColumn.getWidth()); + } + resizeAndRepaint(); + } + + /** + * Returns the column which is interactively resized. The return value is + * null if the header is null or has no resizing column. + * + * @return the resizing column. + */ + private TableColumn getResizingColumn() { + return (tableHeader == null) ? null : tableHeader.getResizingColumn(); + } + + /** + * {@inheritDoc}

+ * + * Overridden for documentation reasons only: same behaviour but different default value. + *

+ * + * The default value is true. + *

+ */ + @Override + public void setFillsViewportHeight(boolean fillsViewportHeight) { + if (fillsViewportHeight == getFillsViewportHeight()) + return; + super.setFillsViewportHeight(fillsViewportHeight); + } + + // ------------------------ override super because of filter-awareness + + + /** + * {@inheritDoc}

+ * Overridden to respect the cell's editability, that is it has no effect if + * !isCellEditable(row, column). + * + * + * @see #isCellEditable(int, int) + */ + @Override + public void setValueAt(Object aValue, int row, int column) { + if (!isCellEditable(row, column)) + return; + super.setValueAt(aValue, row, column); + } + + /** + * Returns true if the cell at row and column is + * editable. Otherwise, invoking setValueAt on the cell will + * have no effect. + *

+ * Overridden to account for row index mapping and to support a layered + * editability control: + *

    + *
  • per-table: JXTable.isEditable() + *
  • per-column: TableColumnExt.isEditable() + *
  • per-cell: controlled by the model + * TableModel.isCellEditable() + *
+ * The view cell is considered editable only if all three layers are + * enabled. + * + * @param row the row index in view coordinates + * @param column the column index in view coordinates + * @return true if the cell is editable + * + * @see #setValueAt(Object, int, int) + * @see #isEditable() + * @see TableColumnExt#isEditable + * @see TableModel#isCellEditable + */ + @Override + public boolean isCellEditable(int row, int column) { + if (!isEditable()) + return false; + boolean editable = super.isCellEditable(row, column); + if (editable) { + TableColumnExt tableColumn = getColumnExt(column); + if (tableColumn != null) { + editable = tableColumn.isEditable(); + } + } + return editable; + } + + + + /** + * {@inheritDoc} + *

+ * + * Overridden for documentation clarification. The property has the same + * meaning as super, that is if true to re-create all table columns on + * either setting a new TableModel or receiving a structureChanged from the + * existing. The most obvious visual effect is that custom column properties + * appear to be "lost". + *

+ * + * JXTable does support additonal custom configuration (via a custom + * ColumnFactory) which can (and incorrectly was) called independently from + * the creation. Setting this property to false guarantees that no column + * configuration is applied. + * + * @see #tableChanged(TableModelEvent) + * @see ColumnFactory + * + */ + @Override + public boolean getAutoCreateColumnsFromModel() { + return super.getAutoCreateColumnsFromModel(); + } + + /** + * {@inheritDoc} + *

+ * + * Overridden to update internal state related to enhanced functionality and + * hack around core bugs. + *

    + *
  • re-calculate intialize column width and preferred + * scrollable size after a structureChanged if autocreateColumnsFromModel is + * true. + *
  • update string representation control after structureChanged + *
  • core bug #6791934 logic to force revalidate if appropriate + *
+ *

+ * + */ + @Override + public void tableChanged(TableModelEvent e) { + preprocessModelChange(e); + super.tableChanged(e); + if (isStructureChanged(e) && getAutoCreateColumnsFromModel()) { + initializeColumnWidths(); + resetCalculatedScrollableSize(true); + } + if ((isStructureChanged(e))) { + updateStringValueRegistryColumnClasses(); + } + postprocessModelChange(e); + } + +//----> start hack around core issue 6791934: +// table not updated correctly after updating model +// while having a sorter with filter. + + /** + * Overridden to hack around core bug + * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6791934 + * + */ + @Override + public void sorterChanged(RowSorterEvent e) { + super.sorterChanged(e); + postprocessSorterChanged(e); + } + + + /** flag to indicate if forced revalidate is needed. */ + protected boolean forceRevalidate; + /** flag to indicate if a sortOrderChanged has happened between pre- and postProcessModelChange. */ + protected boolean filteredRowCountChanged; + + /** + * Hack around core issue 6791934: sets flags to force revalidate if appropriate. + * Called before processing the event. + * @param e the TableModelEvent received from the model + */ + protected void preprocessModelChange(TableModelEvent e) { + forceRevalidate = getSortsOnUpdates() && getRowFilter() != null && isUpdate(e) ; + } + + /** + * Hack around core issue 6791934: forces a revalidate if appropriate and resets + * internal flags. + * Called after processing the event. + * @param e the TableModelEvent received from the model + */ + protected void postprocessModelChange(TableModelEvent e) { + if (forceRevalidate && filteredRowCountChanged) { + resizeAndRepaint(); + } + filteredRowCountChanged = false; + forceRevalidate = false; + } + + /** + * Hack around core issue 6791934: sets the sorter changed flag if appropriate. + * Called after processing the event. + * @param e the sorter event received from the sorter + */ + protected void postprocessSorterChanged(RowSorterEvent e) { + filteredRowCountChanged = false; + if (forceRevalidate && e.getType() == RowSorterEvent.Type.SORTED) { + filteredRowCountChanged = e.getPreviousRowCount() != getRowCount(); + } + } + +//----> end hack around core issue 6791934: + + /** + * {@inheritDoc}

+ * + * Overridden to prevent super from creating RowSorter. + */ + @Override + public void setModel(TableModel dataModel) { + boolean old = getAutoCreateRowSorter(); + try { + this.autoCreateRowSorter = false; + this.ignoreAddColumn = true; + super.setModel(dataModel); + } finally { + this.autoCreateRowSorter = old; + this.ignoreAddColumn = false; + } + if (getAutoCreateRowSorter()) { + setRowSorter(createDefaultRowSorter()); + } + + } + + /** + * {@inheritDoc}

+ * + * Overridden to synch sorter state from columns. + */ + @Override + public void setColumnModel(TableColumnModel columnModel) { + super.setColumnModel(columnModel); + configureSorterProperties(); + initPerColumnStringValues(); + } + + /** + * {@inheritDoc}

+ * + * Overridden to + *

    + *
  • fix core bug: replaces sorter even if flag doesn't change. + *
  • use xflag (need because super's RowSorter creation is hard-coded. + *
+ */ + @Override + public void setAutoCreateRowSorter(boolean autoCreateRowSorter) { + if (getAutoCreateRowSorter() == autoCreateRowSorter) return; + boolean oldValue = getAutoCreateRowSorter(); + this.autoCreateRowSorter = autoCreateRowSorter; + if (autoCreateRowSorter) { + setRowSorter(createDefaultRowSorter()); + } + firePropertyChange("autoCreateRowSorter", oldValue, + getAutoCreateRowSorter()); + } + + /** + * {@inheritDoc}

+ * + * Overridden to return xflag + */ + @Override + public boolean getAutoCreateRowSorter() { + return autoCreateRowSorter; + } + + /** + * {@inheritDoc}

+ * + * Overridden propagate sort-related properties to the sorter after calling super, + * if the given RowSorter is of type SortController. Does nothing additional otherwise. + */ + @Override + public void setRowSorter(RowSorter sorter) { + super.setRowSorter(sorter); + configureSorterProperties(); + } + + /** + * Propagates sort-related properties from table/columns to the sorter if it + * is of type SortController, does nothing otherwise. + * + */ + protected void configureSorterProperties() { + // need to hack: if a structureChange is the result of a setModel + // the rowsorter is not yet updated + if (ignoreAddColumn || (!getControlsSorterProperties())) return; + getSortController().setStringValueProvider(getStringValueRegistry()); + // configure from table properties + getSortController().setSortable(sortable); + getSortController().setSortsOnUpdates(sortsOnUpdates); + getSortController().setSortOrderCycle(getSortOrderCycle()); + // configure from column properties + List columns = getColumns(true); + for (TableColumn tableColumn : columns) { + int modelIndex = tableColumn.getModelIndex(); + getSortController().setSortable(modelIndex, + tableColumn instanceof TableColumnExt ? + ((TableColumnExt) tableColumn).isSortable() : true); + getSortController().setComparator(modelIndex, + tableColumn instanceof TableColumnExt ? + ((TableColumnExt) tableColumn).getComparator() : null); + } + } + + /** + * Creates and returns the default RowSorter. Note that this is already + * configured to the current TableModel - no api in the base class to set + * the model?

+ * + * PENDING JW: review method signature - better expose the need for the + * model by adding a parameter? + * + * @return the default RowSorter. + */ + protected RowSorter createDefaultRowSorter() { +// return new TableRowSorter(getModel()); + return new TableSortController(getModel()); + } + + + + /** + * Convenience method to detect dataChanged table event type. + * + * @param e the event to examine. + * @return true if the event is of type dataChanged, false else. + */ + protected boolean isDataChanged(TableModelEvent e) { + if (e == null) + return false; + return e.getType() == TableModelEvent.UPDATE && e.getFirstRow() == 0 + && e.getLastRow() == Integer.MAX_VALUE; + } + + /** + * Convenience method to detect update table event type. + * + * @param e the event to examine. + * @return true if the event is of type update and not dataChanged, false + * else. + */ + protected boolean isUpdate(TableModelEvent e) { + if (isStructureChanged(e)) + return false; + return e.getType() == TableModelEvent.UPDATE + && e.getLastRow() < Integer.MAX_VALUE; + } + + /** + * Convenience method to detect a structureChanged table event type. + * + * @param e the event to examine. + * @return true if the event is of type structureChanged or null, false + * else. + */ + protected boolean isStructureChanged(TableModelEvent e) { + return e == null || e.getFirstRow() == TableModelEvent.HEADER_ROW; + } + + + // -------------------------------- sorting: configure sorter + + + /** + * Sets "sortable" property indicating whether or not this table + * supports sortable columns. If sortable is true + * then sorting will be enabled on all columns whose sortable + * property is true. If sortable is + * false then sorting will be disabled for all columns, + * regardless of each column's individual sorting property. The + * default is true.

+ * + * Note: as of post-1.0 this property is propagated to the SortController + * if controlsSorterProperties is true. + * Whether or not a change triggers a re-sort is up to either the concrete controller + * implementation (the default doesn't) or client code. This behaviour is + * different from old SwingX style sorting. + * + * @param sortable boolean indicating whether or not this table supports + * sortable columns + * @see #getControlsSorterProperties() + */ + public void setSortable(boolean sortable) { + boolean old = isSortable(); + this.sortable = sortable; + if (getControlsSorterProperties()) { + getSortController().setSortable(sortable); + } + firePropertyChange("sortable", old, isSortable()); + } + + /** + * Returns the table's sortable property.

+ * + * @return true if the table is sortable. + * @see #setSortable(boolean) + */ + public boolean isSortable() { + return sortable; + } + + /** + * If true, specifies that a sort should happen when the underlying + * model is updated (rowsUpdated is invoked). For + * example, if this is true and the user edits an entry the + * location of that item in the view may change. + * This property is propagated to the SortController + * if controlsSorterProperties is true. + *

+ * + * The default value is true.

+ * + * @param sortsOnUpdates whether or not to sort on update events + * @see #getSortsOnUpdates() + * @see #getControlsSorterProperties() + * + */ + public void setSortsOnUpdates(boolean sortsOnUpdates) { + boolean old = getSortsOnUpdates(); + this.sortsOnUpdates = sortsOnUpdates; + if (getControlsSorterProperties()) { + getSortController().setSortsOnUpdates(sortsOnUpdates); + } + firePropertyChange("sortsOnUpdates", old, getSortsOnUpdates()); + } + + /** + * Returns true if a sort should happen when the underlying + * model is updated; otherwise, returns false. + * + * @return whether or not to sort when the model is updated + */ + public boolean getSortsOnUpdates() { + return sortsOnUpdates; + } + + /** + * Sets the sortorder cycle used when toggle sorting this table's columns. + * This property is propagated to the SortController + * if controlsSorterProperties is true. + * + * @param cycle the sequence of zero or more not-null SortOrders to cycle through. + * @throws NullPointerException if the array or any of its elements are null + * + */ + public void setSortOrderCycle(SortOrder... cycle) { + SortOrder[] old = getSortOrderCycle(); + if (getControlsSorterProperties()) { + getSortController().setSortOrderCycle(cycle); + } + this.sortOrderCycle = Arrays.copyOf(cycle, cycle.length); + firePropertyChange("sortOrderCycle", old, getSortOrderCycle()); + } + + /** + * Returns the sortOrder cycle used when toggle sorting this table's columns, guaranteed + * to be not null. + * + * @return the sort order cycle used in toggle sort, not null + */ + public SortOrder[] getSortOrderCycle() { + return Arrays.copyOf(sortOrderCycle, sortOrderCycle.length); + } + + +//------------------------- sorting: sort/filter + + /** + * Sets the filter to the sorter, if available and of type SortController. + * Does nothing otherwise. + *

+ * + * @param filter the filter used to determine what entries should be + * included + */ + @SuppressWarnings("unchecked") + public void setRowFilter(RowFilter filter) { + if (hasSortController()) { + // all fine, because R is a TableModel (R extends TableModel) + SortController controller = (SortController) getSortController(); + controller.setRowFilter(filter); + } + } + + /** + * Returns the filter of the sorter, if available and of type SortController. + * Returns null otherwise.

+ * + * PENDING JW: generics? had to remove return type from getSortController to + * make this compilable, so probably wrong. + * + * @return the filter used in the sorter. + */ + @SuppressWarnings("unchecked") + public RowFilter getRowFilter() { + return hasSortController() ? getSortController().getRowFilter() : null; + } + + /** + * Resets sorting of all columns. + * Delegates to the SortController if available, or does nothing if not.

+ * + * PENDING JW: method name - consistent in SortController and here. + * + */ + public void resetSortOrder() { + if (!hasSortController()) return; + getSortController().resetSortOrders(); + // JW PENDING: think about notification instead of manual repaint. + if (getTableHeader() != null) { + getTableHeader().repaint(); + } + } + + /** + * + * Toggles the sort order of the column at columnIndex. + * Delegates to the SortController if available, or does nothing if not.

+ * + *

+ * The exact behaviour is defined by the SortController's toggleSortOrder + * implementation. Typically a unsorted column is sorted in ascending order, + * a sorted column's order is reversed. + *

+ * + * PRE: 0 <= columnIndex < getColumnCount() + * + * @param columnIndex the columnIndex in view coordinates. + * + */ + public void toggleSortOrder(int columnIndex) { + if (hasSortController()){ + getSortController().toggleSortOrder(convertColumnIndexToModel(columnIndex)); + } + } + + /** + * Sorts the table by the given column using SortOrder. + * Delegates to the SortController if available, or does nothing if not.

+ * + * PRE: 0 <= columnIndex < getColumnCount() + *

+ * + * + * @param columnIndex the column index in view coordinates. + * @param sortOrder the sort order to use. + * + */ + public void setSortOrder(int columnIndex, SortOrder sortOrder) { + if (hasSortController()) { + getSortController().setSortOrder( + convertColumnIndexToModel(columnIndex), sortOrder); + } + } + + /** + * Returns the SortOrder of the given column. + * Delegates to the SortController if available, or returns SortOrder.UNSORTED if not.

+ * + * @param columnIndex the column index in view coordinates. + * @return the interactive sorter's SortOrder if matches the column or + * SortOrder.UNSORTED + */ + public SortOrder getSortOrder(int columnIndex) { + if (hasSortController()) { + return getSortController().getSortOrder(convertColumnIndexToModel(columnIndex)); + } + return SortOrder.UNSORTED; + } + + /** + * + * Toggles the sort order of the column with identifier. + * Delegates to the SortController if available, or does nothing if not.

+ * + * The exact behaviour of a toggle is defined by the SortController's toggleSortOrder + * implementation. Typically a unsorted column is sorted in ascending order, + * a sorted column's order is reversed. + *

+ * + * PENDING: JW - define the behaviour if the identifier is not found. This + * can happen if either there's no column at all with the identifier or if + * there's no column of type TableColumnExt. Currently does nothing, that is + * does not change sort state. + * + * @param identifier the column identifier. + * + */ + public void toggleSortOrder(Object identifier) { + if (!hasSortController()) + return; + TableColumn columnExt = getColumnByIdentifier(identifier); + if (columnExt == null) + return; + getSortController().toggleSortOrder(columnExt.getModelIndex()); + } + + /** + * Sorts the table by the given column using the SortOrder. + * Delegates to the SortController, if available or does nothing if not. + *

+ * + * PENDING: JW - define the behaviour if the identifier is not found. This + * can happen if either there's no column at all with the identifier or if + * there's no column of type TableColumnExt. Currently does nothing, that is + * does not change sort state. + * + * @param identifier the column's identifier. + * @param sortOrder the sort order to use. If null or SortOrder.UNSORTED, + * this method has the same effect as resetSortOrder(); + * + */ + public void setSortOrder(Object identifier, SortOrder sortOrder) { + if (!hasSortController()) + return; + TableColumn columnExt = getColumnByIdentifier(identifier); + if (columnExt == null) + return; + getSortController().setSortOrder(columnExt.getModelIndex(), sortOrder); + } + + /** + * Returns the SortOrder of the given column. + * Delegates to the SortController if available, or returns SortOrder.UNSORTED if not.

+ * + * PENDING: JW - define the behaviour if the identifier is not found. This + * can happen if either there's no column at all with the identifier or if + * there's no column of type TableColumnExt. Currently returns + * SortOrder.UNSORTED. + * + * @param identifier the column's identifier. + * @return the interactive sorter's SortOrder if matches the column or + * SortOrder.UNSORTED + */ + public SortOrder getSortOrder(Object identifier) { + if (!hasSortController()) + return SortOrder.UNSORTED; + TableColumn columnExt = getColumnByIdentifier(identifier); + if (columnExt == null) + return SortOrder.UNSORTED; + int modelIndex = columnExt.getModelIndex(); + return getSortController().getSortOrder(modelIndex); + } + + /** + * Returns a contained TableColumn with the given identifier. + * + * Note that this is a hack around weird columnModel.getColumn(Object) contract in + * core TableColumnModel (throws exception if not found). + * + * @param identifier the column identifier + * @return a TableColumn with the identifier if found, or null if not found. + */ + private TableColumn getColumnByIdentifier(Object identifier) { + TableColumn columnExt; + try { + columnExt = getColumn(identifier); + } catch (IllegalArgumentException e) { + // hacking around weird getColumn(Object) behaviour - + // PENDING JW: revisit and override + columnExt = getColumnExt(identifier); + } + return columnExt; + } + + /** + * Returns the currently active SortController. May be null, if the current RowSorter + * is not an instance of SortController.

+ * + * PENDING JW: generics - can't get the + * RowFilter getter signature correct with having controller typed here.

+ * + * PENDING JW: swaying about hiding or not - currently the only way to + * make the view not configure a RowSorter of type SortController is to + * let this return null. + * + * @return the currently active SortController may be null + */ + @SuppressWarnings("unchecked") + protected SortController getSortController() { + if (hasSortController()) { + // JW: the RowSorter is always of type + // so the unchecked cast is safe + return (SortController) getRowSorter(); + } + return null; + } + + /** + * Returns a boolean indicating whether the table has a SortController. + * If true, the call to getSortController is guaranteed to return a not-null + * value. + * + * @return a boolean indicating whether the table has a SortController. + * + * @see #getSortController() + */ + protected boolean hasSortController() { + return getRowSorter() instanceof SortController; + } + + /** + * Returns a boolean indicating whether the table configures the sorter's + * properties. If true, guaranteed that table's and the columns' sort related + * properties are propagated to the sorter. If false, guaranteed to not + * touch the sorter's configuration.

+ * + * This implementation returns true if the sorter is of type SortController. + * + * Note: the synchronization is unidirection from the table to the sorter. + * Changing the sorter under the table's feet might lead to undefined + * behaviour. + * + * @return a boolean indicating whether the table configurers the sorter's + * properties. + */ + protected boolean getControlsSorterProperties() { + return hasSortController() && getAutoCreateRowSorter(); + } + + /** + * Returns the primary sort column, or null if nothing sorted or no sortKey + * corresponds to a TableColumn currently contained in the TableColumnModel. + * + * @return the currently interactively sorted TableColumn or null if there + * is not sorter active or if the sorted column index does not + * correspond to any column in the TableColumnModel. + */ + public TableColumn getSortedColumn() { + // bloody hack: get primary SortKey and + // check if there's a column with it available + RowSorter controller = getRowSorter(); + if (controller != null) { + // PENDING JW: must use RowSorter? + SortKey sortKey = SortUtils.getFirstSortingKey(controller + .getSortKeys()); + if (sortKey != null) { + int sorterColumn = sortKey.getColumn(); + List columns = getColumns(true); + for (Iterator iter = columns.iterator(); iter + .hasNext();) { + TableColumn column = iter.next(); + if (column.getModelIndex() == sorterColumn) { + return column; + } + } + + } + } + return null; + } + + /** + * Returns the view column index of the primary sort column. + * + * @return the view column index of the primary sort column or -1 if nothing + * sorted or the primary sort column not visible. + */ + public int getSortedColumnIndex() { + RowSorter controller = getRowSorter(); + if (controller != null) { + SortKey sortKey = SortUtils.getFirstSortingKey(controller.getSortKeys()); + if (sortKey != null) { + return convertColumnIndexToView(sortKey.getColumn()); + } + } + return -1; + } + /** + * {@inheritDoc}

+ * + * Overridden to propagate sort-related column properties to the SortController and + * to update string representation of column.

+ * + * PENDING JW: check correct update on visibility change!

+ * PENDING JW: need cleanup of string rep after column removed (if it's a real remove) + */ + @Override + public void columnAdded(TableColumnModelEvent e) { + super.columnAdded(e); + // PENDING JW: check for visibility event? + TableColumn column = getColumn(e.getToIndex()); + updateStringValueForColumn(column, column.getCellRenderer()); + if (ignoreAddColumn) return; + updateSortableAfterColumnChanged(column, column instanceof TableColumnExt ? ((TableColumnExt) column).isSortable() : true); + updateComparatorAfterColumnChanged(column, column instanceof TableColumnExt ? ((TableColumnExt) column).getComparator() : null); + } + + + + // ----------------- enhanced column support: delegation to TableColumnModel + /** + * Returns the TableColumn at view position + * columnIndex. The return value is not null. + * + *

+ * NOTE: This delegate method is added to protect developer's from + * unexpected exceptions in jdk1.5+. Super does not expose the + * TableColumn access by index which may lead to unexpected + * IllegalArgumentException: If client code assumes the + * delegate method is available, autoboxing will convert the given int to an + * Integer which will call the getColumn(Object) method. + * + * + * @param viewColumnIndex index of the column with the object in question + * + * @return the TableColumn object that matches the column index + * @throws ArrayIndexOutOfBoundsException if viewColumnIndex out of allowed + * range. + * + * @see #getColumn(Object) + * @see #getColumnExt(int) + * @see TableColumnModel#getColumn(int) + */ + public TableColumn getColumn(int viewColumnIndex) { + return getColumnModel().getColumn(viewColumnIndex); + } + + /** + * Returns a List of visible TableColumns. + * + * @return a List of visible columns. + * @see #getColumns(boolean) + */ + public List getColumns() { + return Collections.list(getColumnModel().getColumns()); + } + + /** + * Returns the margin between columns. + *

+ * + * Convenience to expose column model properties through + * JXTable api. + * + * @return the margin between columns + * + * @see #setColumnMargin(int) + * @see TableColumnModel#getColumnMargin() + */ + public int getColumnMargin() { + return getColumnModel().getColumnMargin(); + } + + /** + * Sets the margin between columns. + * + * Convenience to expose column model properties through + * JXTable api. + * + * @param value margin between columns; must be greater than or equal to + * zero. + * @see #getColumnMargin() + * @see TableColumnModel#setColumnMargin(int) + */ + public void setColumnMargin(int value) { + getColumnModel().setColumnMargin(value); + } + + // ----------------- enhanced column support: delegation to + // TableColumnModelExt + + /** + * Returns the number of contained columns. The count includes or excludes + * invisible columns, depending on whether the includeHidden is + * true or false, respectively. If false, this method returns the same count + * as getColumnCount(). If the columnModel is not of type + * TableColumnModelExt, the parameter value has no effect. + * + * @param includeHidden a boolean to indicate whether invisible columns + * should be included + * @return the number of contained columns, including or excluding the + * invisible as specified. + * @see #getColumnCount() + * @see TableColumnModelExt#getColumnCount(boolean) + */ + public int getColumnCount(boolean includeHidden) { + if (getColumnModel() instanceof TableColumnModelExt) { + return ((TableColumnModelExt) getColumnModel()) + .getColumnCount(includeHidden); + } + return getColumnCount(); + } + + /** + * Returns a List of contained TableColumns. + * Includes or excludes invisible columns, depending on whether the + * includeHidden is true or false, respectively. If false, an + * Iterator over the List is equivalent to the + * Enumeration returned by getColumns(). If the + * columnModel is not of type TableColumnModelExt, the + * parameter value has no effect. + *

+ * + * NOTE: the order of columns in the List depends on whether or not the + * invisible columns are included, in the former case it's the insertion + * order in the latter it's the current order of the visible columns. + * + * @param includeHidden a boolean to indicate whether invisible columns + * should be included + * @return a List of contained columns. + * + * @see #getColumns() + * @see TableColumnModelExt#getColumns(boolean) + */ + public List getColumns(boolean includeHidden) { + if (getColumnModel() instanceof TableColumnModelExt) { + return ((TableColumnModelExt) getColumnModel()) + .getColumns(includeHidden); + } + return getColumns(); + } + + /** + * Returns the first TableColumnExt with the given + * identifier. The return value is null if there is no + * contained column with identifier or if the column with + * identifier is not of type TableColumnExt. The + * returned column may be visible or hidden. + * + * @param identifier the object used as column identifier + * @return first TableColumnExt with the given identifier or + * null if none is found + * + * @see #getColumnExt(int) + * @see #getColumn(Object) + * @see TableColumnModelExt#getColumnExt(Object) + */ + public TableColumnExt getColumnExt(Object identifier) { + if (getColumnModel() instanceof TableColumnModelExt) { + return ((TableColumnModelExt) getColumnModel()) + .getColumnExt(identifier); + } else { + // PENDING: not tested! + try { + TableColumn column = getColumn(identifier); + if (column instanceof TableColumnExt) { + return (TableColumnExt) column; + } + } catch (Exception e) { + // TODO: handle exception + } + } + return null; + } + + /** + * Returns the TableColumnExt at view position + * columnIndex. The return value is null, if the column at + * position columnIndex is not of type + * TableColumnExt. The returned column is visible. + * + * @param viewColumnIndex the index of the column desired + * @return the TableColumnExt object that matches the column + * index + * @throws ArrayIndexOutOfBoundsException if columnIndex out of allowed + * range, that is if + * (columnIndex < 0) || (columnIndex >= getColumnCount()) + * . + * + * @see #getColumnExt(Object) + * @see #getColumn(int) + * @see TableColumnModelExt#getColumnExt(int) + */ + public TableColumnExt getColumnExt(int viewColumnIndex) { + TableColumn column = getColumn(viewColumnIndex); + if (column instanceof TableColumnExt) { + return (TableColumnExt) column; + } + return null; + } + + // ---------------------- enhanced TableColumn/Model support: convenience + + /** + * Reorders the columns in the sequence given array. Logical names that do + * not correspond to any column in the model will be ignored. Columns with + * logical names not contained are added at the end. + * + * PENDING JW - do we want this? It's used by JNTable. + * + * @param identifiers array of logical column names + * + * @see #getColumns(boolean) + */ + public void setColumnSequence(Object[] identifiers) { + /* + * JW: not properly tested (not in all in fact) ... + */ + List columns = getColumns(true); + Map map = new HashMap(); + for (Iterator iter = columns.iterator(); iter.hasNext();) { + // PENDING: handle duplicate identifiers ... + TableColumn column = iter.next(); + map.put(column.getIdentifier(), column); + getColumnModel().removeColumn(column); + } + for (int i = 0; i < identifiers.length; i++) { + TableColumn column = map.get(identifiers[i]); + if (column != null) { + getColumnModel().addColumn(column); + columns.remove(column); + } + } + for (Iterator iter = columns.iterator(); iter.hasNext();) { + TableColumn column = (TableColumn) iter.next(); + getColumnModel().addColumn(column); + } + } + + // --------------- implement TableColumnModelExtListener + + /** + * {@inheritDoc} + * + * Listens to column property changes. + * + */ + @Override + public void columnPropertyChange(PropertyChangeEvent event) { + if (event.getPropertyName().equals("editable")) { + updateEditingAfterColumnChanged((TableColumn) event.getSource(), + (Boolean) event.getNewValue()); + } else if (event.getPropertyName().equals("sortable")) { + updateSortableAfterColumnChanged((TableColumn) event.getSource(), + (Boolean) event.getNewValue()); + } else if (event.getPropertyName().equals("comparator")) { + updateComparatorAfterColumnChanged((TableColumn) event.getSource(), + (Comparator) event.getNewValue()); + } else if (event.getPropertyName().equals("cellRenderer")) { + updateStringValueForColumn((TableColumn) event.getSource(), + (TableCellRenderer) event.getNewValue()); + } else if (event.getPropertyName().startsWith("highlighter")) { + if (event.getSource() instanceof TableColumnExt + && getRowCount() > 0) { + TableColumnExt column = (TableColumnExt) event.getSource(); + + Rectangle r = getCellRect(0, convertColumnIndexToView(column + .getModelIndex()), true); + r.height = getHeight(); + repaint(r); + } else { + repaint(); + } + } + + } + + + /** + * Adjusts editing state after column's property change. Cancels ongoing + * editing if the sending column is the editingColumn and the column's + * editable changed to false, otherwise does nothing. + * + * @param column the TableColumn which sent the change + * notifcation + * @param editable the new value of the column's editable property + */ + private void updateEditingAfterColumnChanged(TableColumn column, + boolean editable) { + if (!isEditing()) + return; + int viewIndex = convertColumnIndexToView(column.getModelIndex()); + if ((viewIndex < 0) || (viewIndex != getEditingColumn())) + return; + getCellEditor().cancelCellEditing(); + } + + /** + * Synch's the SortController column sortable property to the new value, if + * controlsSorterProperties. Does nothing otherwise. This method is + * called on sortable property change notification from the ext column model.

+ * + * @param column the TableColumn which sent the change + * notifcation + * @param sortable the new value of the column's sortable property + */ + private void updateSortableAfterColumnChanged(TableColumn column, + boolean sortable) { + if (getControlsSorterProperties()) { + getSortController().setSortable(column.getModelIndex(), sortable); + } + } + /** + * Synch's the SortController column comparator property to the new value, if + * controlsSorterProperties. Does nothing otherwise. This method is + * called on comparator property change notification from the ext column model.

+ * + * @param column the TableColumn which sent the change + * notifcation + * @param comparator the new value of the column's sortable property + */ + private void updateComparatorAfterColumnChanged(TableColumn column, + Comparator comparator) { + if (getControlsSorterProperties()) { + getSortController().setComparator(column.getModelIndex(), comparator); + } + } + + // -------------------------- ColumnFactory + + /** + * Creates, configures and adds default TableColumns for + * columns in this table's TableModel. Removes all currently + * contained TableColumns. The exact type and configuration of + * the columns is controlled completely by the ColumnFactory. + * Client code can use {@link #setColumnFactory(ColumnFactory)} to plug-in a + * custom ColumnFactory implementing their own default column creation and + * behaviour. + *

+ * + * Note: this method will probably become final (Issue #961-SwingX) + * so it's strongly recommended to not override now (and replace existing + * overrides by a custom ColumnFactory)! + * + * @see #setColumnFactory(ColumnFactory) + * @see ColumnFactory + * + */ + @Override + public final void createDefaultColumnsFromModel() { + // JW: when could this happen? + if (getModel() == null) + return; + // Remove any current columns + removeColumns(); + createAndAddColumns(); + } + + /** + * Creates and adds TableColumns for each column of the table + * model. + *

+ * + * + */ + private void createAndAddColumns() { + /* + * PENDING: go the whole distance and let the factory decide which model + * columns to map to view columns? That would introduce an collection + * managing operation into the factory, sprawling? Can't (and probably + * don't want to) move all collection related operations over - the + * ColumnFactory relies on TableColumnExt type columns, while the + * JXTable has to cope with all the base types. + */ + for (int i = 0; i < getModel().getColumnCount(); i++) { + // add directly to columnModel - don't go through this.addColumn + // to guarantee full control of ColumnFactory + // addColumn has the side-effect to set the header! + TableColumnExt tableColumn = getColumnFactory() + .createAndConfigureTableColumn(getModel(), i); + if (tableColumn != null) { + getColumnModel().addColumn(tableColumn); + } + } + } + + /** + * Remove all columns, make sure to include hidden. + *

+ */ + private void removeColumns() { + /* + * TODO: promote this method to superclass, and change + * createDefaultColumnsFromModel() to call this method + */ + List columns = getColumns(true); + for (Iterator iter = columns.iterator(); iter.hasNext();) { + getColumnModel().removeColumn(iter.next()); + + } + } + + /** + * Returns the ColumnFactory. + *

+ * + * @return the columnFactory to use for column creation and configuration, + * guaranteed to not be null. + * + * @see #setColumnFactory(ColumnFactory) + * @see ColumnFactory + */ + public ColumnFactory getColumnFactory() { + /* + * TODO JW: think about implications of not/ copying the reference to + * the shared instance into the table's field? Better access the + * getInstance() on each call? We are on single thread anyway... + * Furthermore, we don't expect the instance to change often, typically + * it is configured on startup. So we don't really have to worry about + * changes which would destabilize column state? + */ + if (columnFactory == null) { + return ColumnFactory.getInstance(); + // columnFactory = ColumnFactory.getInstance(); + } + return columnFactory; + } + + /** + * Sets the ColumnFactory to use for column creation and + * configuration. The default value is the shared application ColumnFactory. + *

+ * + * Note: this method has no side-effect, that is existing columns are + * not re-created automatically, client code must trigger it + * manually. + * + * @param columnFactory the factory to use, null indicates to + * use the shared application factory. + * + * @see #getColumnFactory() + * @see ColumnFactory + */ + public void setColumnFactory(ColumnFactory columnFactory) { + /* + * + * TODO auto-configure columns on set? or add public table api to do so? + * Mostly, this is meant to be done once in the lifetime of the table, + * preferably before a model is set ... overshoot? + */ + ColumnFactory old = getColumnFactory(); + this.columnFactory = columnFactory; + firePropertyChange("columnFactory", old, getColumnFactory()); + } + + // -------------------------------- enhanced sizing support + + /** + * Packs all the columns to their optimal size. Works best with auto + * resizing turned off. + * + * @param margin the margin to apply to each column. + * + * @see #packColumn(int, int) + * @see #packColumn(int, int, int) + */ + public void packTable(int margin) { + for (int c = 0; c < getColumnCount(); c++) + packColumn(c, margin, -1); + } + + /** + * Packs an indivudal column in the table. + * + * @param column The Column index to pack in View Coordinates + * @param margin The Margin to apply to the column width. + * + * @see #packColumn(int, int, int) + * @see #packTable(int) + */ + public void packColumn(int column, int margin) { + packColumn(column, margin, -1); + } + + /** + * Packs an indivual column in the table to less than or equal to the + * maximum witdth. If maximum is -1 then the column is made as wide as it + * needs. + * + * @param column the column index to pack in view coordinates + * @param margin the margin to apply to the column + * @param max the maximum width the column can be resized to, -1 means no + * limit + * + * @see #packColumn(int, int) + * @see #packTable(int) + * @see ColumnFactory#packColumn(JXTable, TableColumnExt, int, int) + */ + public void packColumn(int column, int margin, int max) { + getColumnFactory().packColumn(this, getColumnExt(column), margin, max); + } + + /** + * Returns the preferred number of rows to show in a + * JScrollPane. + * + * @return the number of rows to show in a JScrollPane + * @see #setVisibleRowCount(int) + */ + public int getVisibleRowCount() { + return visibleRowCount; + } + + /** + * Sets the preferred number of rows to show in a JScrollPane. + *

+ * + * This is a bound property. The default value is 20. + *

+ * + * PENDING: allow negative for use-all? Analogous to visColumnCount. + * + * @param visibleRowCount number of rows to show in a + * JScrollPane + * @throws IllegalArgumentException if given count is negative. + * + * @see #getVisibleRowCount() + */ + public void setVisibleRowCount(int visibleRowCount) { + if (visibleRowCount < 0) + throw new IllegalArgumentException( + "visible row count must not be negative " + visibleRowCount); + if (getVisibleRowCount() == visibleRowCount) + return; + int old = getVisibleRowCount(); + this.visibleRowCount = visibleRowCount; + resetCalculatedScrollableSize(false); + firePropertyChange("visibleRowCount", old, getVisibleRowCount()); + } + + /** + * Returns the preferred number of columns to show in the + * JScrollPane. + * + * @return the number of columns to show in the scroll pane. + * + * @see #setVisibleColumnCount + */ + public int getVisibleColumnCount() { + return visibleColumnCount; + } + + /** + * Sets the preferred number of Columns to show in a + * JScrollPane. A negative number is interpreted as use-all + * available visible columns. + *

+ * + * This is a bound property. The default value is -1 (effectively the same + * as before the introduction of this property). + * + * @param visibleColumnCount number of rows to show in a + * JScrollPane + * @see #getVisibleColumnCount() + */ + public void setVisibleColumnCount(int visibleColumnCount) { + if (getVisibleColumnCount() == visibleColumnCount) + return; + int old = getVisibleColumnCount(); + this.visibleColumnCount = visibleColumnCount; + resetCalculatedScrollableSize(true); + firePropertyChange("visibleColumnCount", old, getVisibleColumnCount()); + } + + /** + * Resets the calculated scrollable size in one dimension, if appropriate. + * + * @param isColumn flag to denote which dimension to reset, true for width, + * false for height + * + */ + private void resetCalculatedScrollableSize(boolean isColumn) { + if (calculatedPrefScrollableViewportSize != null) { + if (isColumn) { + calculatedPrefScrollableViewportSize.width = -1; + } else { + calculatedPrefScrollableViewportSize.height = -1; + } + } + } + + /** + * {@inheritDoc} + *

+ * + * If the given dimension is null, the auto-calculation of the pref + * scrollable size is enabled, otherwise the behaviour is the same as super. + * + * The default is auto-calc enabled on. + * + * @see #getPreferredScrollableViewportSize() + */ + @Override + public void setPreferredScrollableViewportSize(Dimension size) { + // TODO: figure out why firing the event screws the + // JXTableUnitTest.testPrefScrollableUpdatedOnStructureChanged + // Dimension old = getPreferredScrollableViewportSize(); + super.setPreferredScrollableViewportSize(size); + // firePropertyChange("preferredScrollableViewportSize", old, + // getPreferredScrollableViewportSize()); + } + + /** + * {@inheritDoc} + *

+ * Overridden to support auto-calculation of pref scrollable size, dependent + * on the visible row/column count properties. The auto-calc is on if + * there's no explicit pref scrollable size set. Otherwise the fixed size is + * returned + *

+ * + * The calculation of the preferred scrollable width is delegated to the + * ColumnFactory to allow configuration with custom strategies implemented + * in custom factories. + * + * @see #setPreferredScrollableViewportSize(Dimension) + * @see ColumnFactory#getPreferredScrollableViewportWidth(JXTable) + */ + @Override + public Dimension getPreferredScrollableViewportSize() { + // client code has set this - takes precedence. + Dimension prefSize = super.getPreferredScrollableViewportSize(); + if (prefSize != null) { + return new Dimension(prefSize); + } + if (calculatedPrefScrollableViewportSize == null) { + calculatedPrefScrollableViewportSize = new Dimension(); + // JW: hmm... fishy ... shouldn't be necessary here? + // maybe its the "early init" in super's tableChanged(); + // moved to init which looks okay so far + // initializeColumnPreferredWidths(); + } + // the width is reset to -1 in setVisibleColumnCount + if (calculatedPrefScrollableViewportSize.width <= 0) { + calculatedPrefScrollableViewportSize.width = getColumnFactory() + .getPreferredScrollableViewportWidth(this); + } + // the heigth is reset in setVisualRowCount + if (calculatedPrefScrollableViewportSize.height <= 0) { + calculatedPrefScrollableViewportSize.height = getVisibleRowCount() + * getRowHeight(); + } + return new Dimension(calculatedPrefScrollableViewportSize); + } + + /** + * Initialize the width related properties of all contained TableColumns, + * both visible and hidden. + *

+ *

    + *
  • PENDING: move into ColumnFactory? + *
  • PENDING: what to do if autoCreateColumn off? + *
  • PENDING: public? to allow manual setting of column properties which + * might effect their default sizing. Needed in testing - but real-world? + * the factory is meant to do the property setting, based on tableModel and + * meta-data (from where?). But leads to funny call sequence for per-table + * factory (new JXTable(), table.setColumnFactory(..), table.setModel(...)) + *
+ * + * @see #initializeColumnPreferredWidth(TableColumn) + */ + protected void initializeColumnWidths() { + for (TableColumn column : getColumns(true)) { + initializeColumnPreferredWidth(column); + } + } + + /** + * Initialize the width related properties of the specified column. The + * details are specified by the current ColumnFactory if the + * column is of type TableColumnExt. Otherwise nothing is + * changed. + *

+ * + * TODO JW - need to cleanup getScrollablePreferred (refactor and inline) + * + * @param column TableColumn object representing view column + * @see ColumnFactory#configureColumnWidths + */ + protected void initializeColumnPreferredWidth(TableColumn column) { + if (column instanceof TableColumnExt) { + getColumnFactory().configureColumnWidths(this, + (TableColumnExt) column); + } + } + + // ----------------- scrolling support + /** + * Scrolls vertically to make the given row visible. This might not have any + * effect if the table isn't contained in a JViewport. + *

+ * + * Note: this method has no precondition as it internally uses + * getCellRect which is lenient to off-range coordinates. + * + * @param row the view row index of the cell + * + * @see #scrollColumnToVisible(int) + * @see #scrollCellToVisible(int, int) + * @see #scrollRectToVisible(Rectangle) + */ + public void scrollRowToVisible(int row) { + Rectangle cellRect = getCellRect(row, 0, false); + Rectangle visibleRect = getVisibleRect(); + cellRect.x = visibleRect.x; + cellRect.width = visibleRect.width; + scrollRectToVisible(cellRect); + } + + /** + * Scrolls horizontally to make the given column visible. This might not + * have any effect if the table isn't contained in a JViewport. + *

+ * + * Note: this method has no precondition as it internally uses + * getCellRect which is lenient to off-range coordinates. + * + * @param column the view column index of the cell + * + * @see #scrollRowToVisible(int) + * @see #scrollCellToVisible(int, int) + * @see #scrollRectToVisible(Rectangle) + */ + public void scrollColumnToVisible(int column) { + Rectangle cellRect = getCellRect(0, column, false); + Rectangle visibleRect = getVisibleRect(); + cellRect.y = visibleRect.y; + cellRect.height = visibleRect.height; + scrollRectToVisible(cellRect); + } + + /** + * Scrolls to make the cell at row and column visible. This might not have + * any effect if the table isn't contained in a JViewport. + *

+ * + * Note: this method has no precondition as it internally uses + * getCellRect which is lenient to off-range coordinates. + * + * @param row the view row index of the cell + * @param column the view column index of the cell + * + * @see #scrollColumnToVisible(int) + * @see #scrollRowToVisible(int) + * @see #scrollRectToVisible(Rectangle) + */ + public void scrollCellToVisible(int row, int column) { + Rectangle cellRect = getCellRect(row, column, false); + scrollRectToVisible(cellRect); + } + + // ----------------------- delegating methods?? from super + /** + * Returns the selection mode used by this table's selection model. + *

+ * PENDING JW - setter? + * + * @return the selection mode used by this table's selection model + * @see ListSelectionModel#getSelectionMode() + */ + public int getSelectionMode() { + return getSelectionModel().getSelectionMode(); + } + + // ----------------------- Search support + + /** + * Starts a search on this List's visible items. This implementation asks the + * SearchFactory to open a find widget on itself. + */ + protected void doFind() { + SearchFactory.getInstance().showFindInput(this, getSearchable()); + } + + /** + * Returns a Searchable for this component, guaranteed to be not null. This + * implementation lazily creates a TableSearchable if necessary. + * + * + * @return a not-null Searchable for this component. + * + * @see #setSearchable(Searchable) + * @see TableSearchable + */ + public Searchable getSearchable() { + if (searchable == null) { + searchable = new TableSearchable(this); + } + return searchable; + } + + /** + * Sets the Searchable for this table. If null, a default + * searchable will be used. + * + * @param searchable the Searchable to use for this table, may be null to indicate + * using the table's default searchable. + */ + public void setSearchable(Searchable searchable) { + this.searchable = searchable; + } + + // ----------------------------------- uniform data model access + /** + * @return the unconfigured ComponentAdapter. + */ + protected ComponentAdapter getComponentAdapter() { + if (dataAdapter == null) { + dataAdapter = new TableAdapter(this); + } + return dataAdapter; + } + + /** + * Convenience to access a configured ComponentAdapter. + * + * @param row the row index in view coordinates. + * @param column the column index in view coordinates. + * @return the configured ComponentAdapter. + */ + protected ComponentAdapter getComponentAdapter(int row, int column) { + ComponentAdapter adapter = getComponentAdapter(); + adapter.row = row; + adapter.column = column; + return adapter; + } + + protected static class TableAdapter extends ComponentAdapter { + private final JXTable table; + + /** + * Constructs a TableDataAdapter for the specified target + * component. + * + * @param component the target component + */ + public TableAdapter(JXTable component) { + super(component); + table = component; + } + + /** + * Typesafe accessor for the target component. + * + * @return the target component as a {@link JTable} + */ + public JXTable getTable() { + return table; + } + +//----------------- column meta data + /** + * {@inheritDoc} + */ + @Override + public String getColumnName(int columnIndex) { + TableColumn column = getColumnByModelIndex(columnIndex); + return column == null ? "" : column.getHeaderValue().toString(); + } + + /** + * Returns the first contained TableColumn with the given model index, or + * null if none is found. + * + * @param modelColumn the column index in model coordinates, must be valid + * @return the first contained TableColumn with the given model index, or + * null if none is found + * @throws IllegalArgumentException if model index invalid + */ + protected TableColumn getColumnByModelIndex(int modelColumn) { + if ((modelColumn < 0) || (modelColumn >= getColumnCount())) { + throw new IllegalArgumentException("invalid column index, must be positive and less than " + + getColumnCount() + " was: " + modelColumn); + } + List columns = table.getColumns(true); + for (Iterator iter = columns.iterator(); iter + .hasNext();) { + TableColumn column = iter.next(); + if (column.getModelIndex() == modelColumn) { + return column; + } + } + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public Object getColumnIdentifierAt(int columnIndex) { + if ((columnIndex < 0) || (columnIndex >= getColumnCount())) { + throw new ArrayIndexOutOfBoundsException( + "invalid column index: " + columnIndex); + } + TableColumn column = getColumnByModelIndex(columnIndex); + Object identifier = column != null ? column.getIdentifier() : null; + return identifier; + } + + /** + * {@inheritDoc} + */ + @Override + public int getColumnIndex(Object identifier) { + TableColumn column = table.getColumnExt(identifier); + return column != null ? column.getModelIndex() : -1; + } + + /** + * @inherited

+ */ + @Override + public Class getColumnClass(int column) { + return table.getModel().getColumnClass(column); + } + + //--------------------- model meta data + /** + * {@inheritDoc} + */ + @Override + public int getColumnCount() { + return table.getModel().getColumnCount(); + } + + /** + * {@inheritDoc} + */ + @Override + public int getRowCount() { + return table.getModel().getRowCount(); + } + + /** + * {@inheritDoc} + */ + @Override + public Object getValueAt(int row, int column) { + return table.getModel().getValueAt(row, column); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isCellEditable(int row, int column) { + return table.getModel().isCellEditable(row, column); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isTestable(int column) { + return getColumnByModelIndex(column) != null; + } + + // -------------------------- accessing view state/values + + /** + * {@inheritDoc} + * + * This is implemented to query the table's StringValueRegistry for an appropriate + * StringValue and use that for getting the string representation. + */ + @Override + public String getStringAt(int row, int column) { + StringValue sv = table.getStringValueRegistry().getStringValue(row, column); + return sv.getString(getValueAt(row, column)); + } + + /** + * {@inheritDoc} + */ + @Override + public Rectangle getCellBounds() { + return table.getCellRect(row, column, false); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isEditable() { + return table.isCellEditable(row, column); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isSelected() { + return table.isCellSelected(row, column); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean hasFocus() { + boolean rowIsLead = (table.getSelectionModel() + .getLeadSelectionIndex() == row); + boolean colIsLead = (table.getColumnModel().getSelectionModel() + .getLeadSelectionIndex() == column); + return table.isFocusOwner() && (rowIsLead && colIsLead); + } + + /** + * {@inheritDoc} + */ + @Override + public int convertColumnIndexToView(int columnIndex) { + return table.convertColumnIndexToView(columnIndex); + } + + /** + * {@inheritDoc} + */ + @Override + public int convertColumnIndexToModel(int columnIndex) { + return table.convertColumnIndexToModel(columnIndex); + } + + /** + * {@inheritDoc} + */ + @Override + public int convertRowIndexToView(int rowModelIndex) { + return table.convertRowIndexToView(rowModelIndex); + } + + /** + * {@inheritDoc} + */ + @Override + public int convertRowIndexToModel(int rowViewIndex) { + return table.convertRowIndexToModel(rowViewIndex); + } + + } + + // --------------------- managing renderers/editors + + /** + * Sets the Highlighters to the table, replacing any old + * settings. None of the given Highlighters must be null. + *

+ * + * This is a bound property. + *

+ * + * Note: as of version #1.257 the null constraint is enforced strictly. To + * remove all highlighters use this method without param. + * + * @param highlighters zero or more not null highlighters to use for + * renderer decoration. + * @throws NullPointerException if array is null or array contains null + * values. + * + * @see #getHighlighters() + * @see #addHighlighter(Highlighter) + * @see #removeHighlighter(Highlighter) + * + */ + public void setHighlighters(Highlighter... highlighters) { + Highlighter[] old = getHighlighters(); + getCompoundHighlighter().setHighlighters(highlighters); + firePropertyChange("highlighters", old, getHighlighters()); + } + + /** + * Returns the Highlighters used by this table. Maybe empty, + * but guarantees to be never null. + * + * @return the Highlighters used by this table, guaranteed to never null. + * @see #setHighlighters(Highlighter[]) + */ + public Highlighter[] getHighlighters() { + return getCompoundHighlighter().getHighlighters(); + } + + /** + * Appends a Highlighter to the end of the list of used + * Highlighters. The argument must not be null. + *

+ * + * @param highlighter the Highlighter to add, must not be null. + * @throws NullPointerException if Highlighter is null. + * + * @see #removeHighlighter(Highlighter) + * @see #setHighlighters(Highlighter[]) + */ + public void addHighlighter(Highlighter highlighter) { + Highlighter[] old = getHighlighters(); + getCompoundHighlighter().addHighlighter(highlighter); + firePropertyChange("highlighters", old, getHighlighters()); + } + + /** + * Removes the given Highlighter. + *

+ * + * Does nothing if the Highlighter is not contained. + * + * @param highlighter the Highlighter to remove. + * @see #addHighlighter(Highlighter) + * @see #setHighlighters(Highlighter...) + */ + public void removeHighlighter(Highlighter highlighter) { + Highlighter[] old = getHighlighters(); + getCompoundHighlighter().removeHighlighter(highlighter); + firePropertyChange("highlighters", old, getHighlighters()); + } + + /** + * Returns the CompoundHighlighter assigned to the table, null if none. + * PENDING: open up for subclasses again?. + * + * @return the CompoundHighlighter assigned to the table. + */ + protected CompoundHighlighter getCompoundHighlighter() { + if (compoundHighlighter == null) { + compoundHighlighter = new CompoundHighlighter(); + compoundHighlighter + .addChangeListener(getHighlighterChangeListener()); + } + return compoundHighlighter; + } + + /** + * Returns the ChangeListener to use with highlighters. Lazily + * creates the listener. + * + * @return the ChangeListener for observing changes of highlighters, + * guaranteed to be not-null + */ + protected ChangeListener getHighlighterChangeListener() { + if (highlighterChangeListener == null) { + highlighterChangeListener = createHighlighterChangeListener(); + } + return highlighterChangeListener; + } + + /** + * Creates and returns the ChangeListener observing Highlighters. + *

+ * Here: repaints the table on receiving a stateChanged. + * + * @return the ChangeListener defining the reaction to changes of + * highlighters. + */ + protected ChangeListener createHighlighterChangeListener() { + return new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + repaint(); + } + }; + } + + /** + * Returns the StringValueRegistry which defines the string representation for + * each cells. This is strictly for internal use by the table, which has the + * responsibility to keep in synch with registered renderers.

+ * + * Currently exposed for testing reasons, client code is recommended to not use nor override. + * + * @return the current string value registry + */ + protected StringValueRegistry getStringValueRegistry() { + if (stringValueRegistry == null) { + stringValueRegistry = createDefaultStringValueRegistry(); + } + return stringValueRegistry; + } + + /** + * Creates and returns the default registry for StringValues.

+ * + * @return the default registry for StringValues. + */ + protected StringValueRegistry createDefaultStringValueRegistry() { + return new StringValueRegistry(); + } + + + /** + * Updates per-column class in StringValueRegistry. This is called after + * structureChanged. + */ + private void updateStringValueRegistryColumnClasses() { + getStringValueRegistry().setColumnClasses(null); + for (int i = 0; i < getModel().getColumnCount(); i++) { + getStringValueRegistry().setColumnClass(getModel().getColumnClass(i), i); + } + } + + /** + * Updates per-column StringValue in StringValueRegistry based on given tableColumn. + * This is called after the column's renderer property changed or after the column + * is added. + * + * @param tableColumn the column to update from + * @param renderer the renderer potentially useful as StringValue. + */ + private void updateStringValueForColumn(TableColumn tableColumn, + TableCellRenderer renderer) { + getStringValueRegistry().setStringValue( + renderer instanceof StringValue ? (StringValue) renderer : null, + tableColumn.getModelIndex()); + } + /** + * Called in init to synch the StringValueProvider with default renderers per class + */ + private void initDefaultStringValues() { + for (Object clazz : defaultRenderersByColumnClass.keySet()) { + Object renderer = defaultRenderersByColumnClass.get(clazz); + if (renderer instanceof StringValue) { + getStringValueRegistry().setStringValue((StringValue) renderer, (Class) clazz); + } + } + } + + /** + * Inits per column string values from TableColumns + */ + private void initPerColumnStringValues() { + getStringValueRegistry().clearColumnStringValues(); + for (TableColumn tableColumn : getColumns(true)) { + updateStringValueForColumn(tableColumn, tableColumn.getCellRenderer()); + } + } + /** + * {@inheritDoc}

+ * + * Overridden to synchronize the string representation. If the renderer is of type + * StringValue a mapping it will be used as converter for the class type. If not, + * the mapping is reset to default. + */ + @Override + public void setDefaultRenderer(Class columnClass, + TableCellRenderer renderer) { + super.setDefaultRenderer(columnClass, renderer); + getStringValueRegistry().setStringValue( + (renderer instanceof StringValue) ? (StringValue) renderer : null, + columnClass); + } + + /** + * Returns the string representation of the cell value at the given + * position. + * + * @param row the row index of the cell in view coordinates + * @param column the column index of the cell in view coordinates. + * @return the string representation of the cell value as it will appear in + * the table. + */ + public String getStringAt(int row, int column) { + // changed implementation to use StringValueRegistry + StringValue stringValue = getStringValueRegistry().getStringValue( + convertRowIndexToModel(row), convertColumnIndexToModel(column)); + return stringValue.getString(getValueAt(row, column)); + } + + /** + * {@inheritDoc} + *

+ * + * Overridden to fix core bug #4614616 (NPE if TableModel's + * Class for the column is an interface). This method + * guarantees to always return a not null value. Returns the + * default renderer for Object if super returns + * null.

+ * + * Note: The lookup strategy for is unchanged compared to super. Subclasses + * which override this with a different lookup strategy are strongly advised to + * implement a custom StringValueRegistry with that lookup strategy to keep + * the WYSIWYM in SwingX. + * + */ + @Override + public TableCellRenderer getCellRenderer(int row, int column) { + TableCellRenderer renderer = super.getCellRenderer(row, column); + if (renderer == null) { + renderer = getDefaultRenderer(Object.class); + } + return renderer; + } + + /** + * Returns the decorated Component used as a stamp to render + * the specified cell. Overrides superclass version to provide support for + * cell decorators. + *

+ * + * Adjusts component orientation (guaranteed to happen before applying + * Highlighters). + *

+ * + * Per-column highlighters contained in + * {@link TableColumnExt#getHighlighters()} are applied to the renderer + * after the table highlighters. + *

+ * + * TODO kgs: interaction of search highlighter and column highlighters + *

+ * + * Note: DefaultTableCellRenderer and subclasses require a hack to play + * nicely with Highlighters because it has an internal "color memory" in + * setForeground/setBackground. The hack is applied in + * resetDefaultTableCellRendererColors which is called after + * super.prepareRenderer and before applying the Highlighters. The method is + * called always and for all renderers. + * + * @param renderer the TableCellRenderer to prepare + * @param row the row of the cell to render, where 0 is the first row + * @param column the column of the cell to render, where 0 is the first + * column + * @return the decorated Component used as a stamp to render + * the specified cell + * @see #resetDefaultTableCellRendererColors(Component, int, int) + * @see Highlighter + */ + @Override + public Component prepareRenderer(TableCellRenderer renderer, int row, + int column) { + Component stamp = super.prepareRenderer(renderer, row, column); + // #145-swingx: default renderers don't respect componentOrientation. + adjustComponentOrientation(stamp); + // #258-swingx: hacking around DefaultTableCellRenderer color memory. + resetDefaultTableCellRendererColors(stamp, row, column); + + ComponentAdapter adapter = getComponentAdapter(row, column); + // a very slight optimization: if this instance never had a highlighter + // added then don't create a compound here. + if (compoundHighlighter != null) { + stamp = compoundHighlighter.highlight(stamp, adapter); + } + + TableColumnExt columnExt = getColumnExt(column); + + if (columnExt != null) { + // JW: fix for #838 - artificial compound installs listener + // PENDING JW: instead of doing the looping ourselves, how + // about adding a method prepareRenderer to the TableColumnExt + for (Highlighter highlighter : columnExt.getHighlighters()) { + stamp = highlighter.highlight(stamp, adapter); + + } + // CompoundHighlighter columnHighlighters + // = new CompoundHighlighter(columnExt.getHighlighters()); + + } + + return stamp; + } + + /** + * Convenience method to get the rendering component for the given cell. + * + * @param row the row of the cell to render, where 0 is the first row + * @param col the column of the cell to render, where 0 is the first + * column + * @return the decorated Component used as a stamp to render + * the specified cell + */ + public Component prepareRenderer(int row, int col) { + return prepareRenderer(getCellRenderer(row, col), row, col); + } + + /** + * + * Method to apply a hack around DefaultTableCellRenderer "color memory" + * (Issue #258-swingx). Applies the hack if the client property + * USE_DTCR_COLORMEMORY_HACK having the value of + * Boolean.TRUE, does nothing otherwise. The property is true + * by default. + *

+ * + * The hack consists of applying a specialized Highlighter to + * force reset the color "memory" of DefaultTableCellRenderer. + * Note that the hack is applied always, that is even if there are no custom + * Highlighters. + *

+ * + * Client code which solves the problem at the core (that is in a + * well-behaved DefaultTableCellRenderer) can disable the hack + * by removing the client property or by subclassing and override this to do + * nothing. + * + * @param renderer the TableCellRenderer to hack + * @param row the row of the cell to render + * @param column the column index of the cell to render + * + * @see #prepareRenderer(TableCellRenderer, int, int) + * @see #USE_DTCR_COLORMEMORY_HACK + * @see ResetDTCRColorHighlighter + */ + protected void resetDefaultTableCellRendererColors(Component renderer, + int row, int column) { + if (!Boolean.TRUE.equals(getClientProperty(USE_DTCR_COLORMEMORY_HACK))) + return; + ComponentAdapter adapter = getComponentAdapter(row, column); + if (resetDefaultTableCellRendererHighlighter == null) { + resetDefaultTableCellRendererHighlighter = new ResetDTCRColorHighlighter(); + } + // hacking around DefaultTableCellRenderer color memory. + resetDefaultTableCellRendererHighlighter.highlight(renderer, adapter); + } + + /** + * {@inheritDoc} + *

+ * + * Overridden to adjust the editor's component orientation. + */ + @Override + public Component prepareEditor(TableCellEditor editor, int row, int column) { + Component comp = super.prepareEditor(editor, row, column); + // JW: might be null if generic editor barks about constructor + // super silently backs out - we do the same here + if (comp != null) { + adjustComponentOrientation(comp); + } + return comp; + } + + /** + * Adjusts the Component's orientation to this + * JXTable's CO if appropriate. The parameter must not be + * null. + *

+ * + * This implementation synchs the CO always. + * + * @param stamp the Component who's CO may need to be synched, + * must not be null. + */ + protected void adjustComponentOrientation(Component stamp) { + if (stamp.getComponentOrientation().equals(getComponentOrientation())) + return; + stamp.applyComponentOrientation(getComponentOrientation()); + } + + /** + * Creates default cell renderers for Objects, + * Numbers, Dates, Booleans, + * Icon/Image/s and URIs. + *

+ * Overridden to replace all super default renderers with SwingX variants and + * additionally register a default for URI types. Note: the latter + * registration will fail silently in headless environments or when the runtime + * context doesn't support Desktop. + * + *

+ * {@inheritDoc} + * + * @see DefaultTableRenderer + * @see org.jdesktop.swingx.renderer.ComponentProvider + */ + @Override + protected void createDefaultRenderers() { + defaultRenderersByColumnClass = new UIDefaults(8, 0.75f); + // configured default table renderer (internally LabelProvider) + setDefaultRenderer(Object.class, new DefaultTableRenderer()); + setDefaultRenderer(Number.class, new DefaultTableRenderer( + StringValues.NUMBER_TO_STRING, JLabel.RIGHT)); + setDefaultRenderer(Date.class, new DefaultTableRenderer( + StringValues.DATE_TO_STRING)); + // use the same center aligned default for Image/Icon + TableCellRenderer renderer = new DefaultTableRenderer(new MappedValue( + StringValues.EMPTY, IconValues.ICON), JLabel.CENTER); + setDefaultRenderer(Icon.class, renderer); + setDefaultRenderer(ImageIcon.class, renderer); + // use a ButtonProvider for booleans + setDefaultRenderer(Boolean.class, new DefaultTableRenderer( + new CheckBoxProvider())); + + try { + setDefaultRenderer(URI.class, new DefaultTableRenderer( + new HyperlinkProvider(new HyperlinkAction()) + )); + } catch (Exception e) { + // nothing to do - either headless or Desktop not supported + } + } + + /** + * Creates default cell editors for objects, numbers, and boolean values. + *

+ * Overridden to hook enhanced editors (f.i. NumberEditorExt + * + * @see DefaultCellEditor + */ + @SuppressWarnings("unchecked") + @Override + protected void createDefaultEditors() { + defaultEditorsByColumnClass = new UIDefaults(3, 0.75f); + defaultEditorsByColumnClass.put(Object.class, new GenericEditor()); + // Numbers + // JW: fix for + // Issue #1183-swingx: NumberEditorExt throws in getCellEditorValue if + // Integer (short, byte..) below/above min/max. + // Issue #1236-swingx: NumberEditorExt cannot be used in columns with Object type + defaultEditorsByColumnClass.put(Number.class, new NumberEditorExt(true)); + // Booleans + defaultEditorsByColumnClass.put(Boolean.class, new BooleanEditor()); + + } + + /** + * Default editor registered for Object. The editor tries to + * create a new instance of the column's class by reflection. It assumes + * that the class has a constructor taking a single String + * parameter. + *

+ * + * The editor can be configured with a custom JTextField. + * + */ + public static class GenericEditor extends DefaultCellEditor { + + Class[] argTypes = new Class[] { String.class }; + + java.lang.reflect.Constructor constructor; + + Object value; + + public GenericEditor() { + this(new JTextField()); + } + + public GenericEditor(JTextField textField) { + super(textField); + getComponent().setName("Table.editor"); + } + + @Override + public boolean stopCellEditing() { + String s = (String) super.getCellEditorValue(); + // JW: changed logic to hack around (core!) Issue #1535 + // don't special case empty, but string contructor: + // if so, by-pass relection altogether + if (constructor.getDeclaringClass() == String.class) { + value = s; + } else { // try instantiating a new Object with the string + try { + value = constructor.newInstance(new Object[] { s }); + } catch (Exception e) { + ((JComponent) getComponent()).setBorder(new LineBorder( + Color.red)); + return false; + } + } + return super.stopCellEditing(); + } + + @Override + public Component getTableCellEditorComponent(JTable table, + Object value, boolean isSelected, int row, int column) { + this.value = null; + ((JComponent) getComponent()) + .setBorder(new LineBorder(Color.black)); + try { + Class type = table.getColumnClass(column); + // Since our obligation is to produce a value which is + // assignable for the required type it is OK to use the + // String constructor for columns which are declared + // to contain Objects. A String is an Object. + if (type == Object.class) { + type = String.class; + } + constructor = type.getConstructor(argTypes); + } catch (Exception e) { + return null; + } + return super.getTableCellEditorComponent(table, value, isSelected, + row, column); + } + + @Override + public Object getCellEditorValue() { + return value; + } + } + + /** + * + * Editor for Numbers. + *

+ * Note: this is no longer registered by default. The current default is + * NumberEditorExt which differs from this in being + * locale-aware. + * + */ + public static class NumberEditor extends GenericEditor { + + public NumberEditor() { + ((JTextField) getComponent()) + .setHorizontalAlignment(JTextField.RIGHT); + } + } + + /** + * The default editor for Boolean types. + */ + public static class BooleanEditor extends DefaultCellEditor { + public BooleanEditor() { + super(new JCheckBox()); + JCheckBox checkBox = (JCheckBox) getComponent(); + checkBox.setHorizontalAlignment(JCheckBox.CENTER); + } + + @Override + public Component getTableCellEditorComponent(JTable table, + Object value, boolean isSelected, int row, int column) { + // fix #1521-swingx: consistent selection behaviour + Component comp = super.getTableCellEditorComponent(table, + value, isSelected || shouldSelectCell(null), row, column); + return comp; + } + + + } + + // ----------------------------- enhanced editing support + + /** + * Returns the editable property of the JXTable as a whole. + * + * @return boolean to indicate if the table is editable. + * @see #setEditable + */ + public boolean isEditable() { + return editable; + } + + /** + * Sets the editable property. This property allows to mark all cells in a + * table as read-only, independent of their per-column editability as + * returned by TableColumnExt.isEditable and their per-cell + * editability as returned by the TableModel.isCellEditable. If + * a cell is read-only in its column or model layer, this property has no + * effect. + *

+ * + * The default value is true. + * + * @param editable the flag to indicate if the table is editable. + * @see #isEditable + * @see #isCellEditable(int, int) + */ + public void setEditable(boolean editable) { + boolean old = isEditable(); + this.editable = editable; + firePropertyChange("editable", old, isEditable()); + } + + /** + * Returns the property which determines the edit termination behaviour on + * focus lost. + * + * @return boolean to indicate whether an ongoing edit should be terminated + * if the focus is moved to somewhere outside of the table. + * @see #setTerminateEditOnFocusLost(boolean) + */ + public boolean isTerminateEditOnFocusLost() { + return Boolean.TRUE + .equals(getClientProperty("terminateEditOnFocusLost")); + } + + /** + * Sets the property to determine whether an ongoing edit should be + * terminated if the focus is moved to somewhere outside of the table. If + * true, terminates the edit, does nothing otherwise. The exact behaviour is + * implemented in JTable.CellEditorRemover: "outside" is + * interpreted to be on a component which is not under the table hierarchy + * but inside the same toplevel window, "terminate" does so in any case, + * first tries to stop the edit, if that's unsuccessful it cancels the edit. + *

+ * The default value is true. + * + * @param terminate the flag to determine whether or not to terminate the + * edit + * @see #isTerminateEditOnFocusLost() + */ + public void setTerminateEditOnFocusLost(boolean terminate) { + // JW: we can leave the propertyChange notification to the + // putClientProperty - the key and method name are the same + putClientProperty("terminateEditOnFocusLost", terminate); + } + + /** + * Returns the autoStartsEdit property. + * + * @return boolean to indicate whether a keyStroke should try to start + * editing. + * @see #setAutoStartEditOnKeyStroke(boolean) + */ + public boolean isAutoStartEditOnKeyStroke() { + return !Boolean.FALSE + .equals(getClientProperty("JTable.autoStartsEdit")); + } + + /** + * Sets the autoStartsEdit property. If true, keystrokes are passed-on to + * the cellEditor of the lead cell to let it decide whether to start an + * edit. + *

+ * The default value is true. + *

+ * + * @param autoStart boolean to determine whether a keyStroke should try to + * start editing. + * @see #isAutoStartEditOnKeyStroke() + */ + public void setAutoStartEditOnKeyStroke(boolean autoStart) { + boolean old = isAutoStartEditOnKeyStroke(); + // JW: we have to take over propertyChange notification + // because the key and method name are different. + // As a consequence, there are two events fired: one for + // the client prop and one for this method. + putClientProperty("JTable.autoStartsEdit", autoStart); + firePropertyChange("autoStartEditOnKeyStroke", old, + isAutoStartEditOnKeyStroke()); + } + + /** + * {@inheritDoc} + *

+ * + * overridden to install a custom editor remover. + */ + @Override + public boolean editCellAt(int row, int column, EventObject e) { + boolean started = super.editCellAt(row, column, e); + if (started) { + hackEditorRemover(); + } + return started; + } + + /** + * Overridden with backport from Mustang fix for #4684090, #4887999. + */ + @Override + public void removeEditor() { + boolean isFocusOwnerInTheTable = isFocusOwnerDescending(); + // let super do its stuff + super.removeEditor(); + if (isFocusOwnerInTheTable) { + requestFocusInWindow(); + } + } + + /** + * Returns a boolean to indicate if the current focus owner is descending + * from this table. Returns false if not editing, otherwise walks the + * focusOwner hierarchy, taking popups into account. + * + * @return a boolean to indicate if the current focus owner is contained. + */ + private boolean isFocusOwnerDescending() { + if (!isEditing()) + return false; + Component focusOwner = KeyboardFocusManager + .getCurrentKeyboardFocusManager().getFocusOwner(); + // PENDING JW: special casing to not fall through ... really wanted? + if (focusOwner == null) + return false; + if (SwingXUtilities.isDescendingFrom(focusOwner, this)) + return true; + // same with permanent focus owner + Component permanent = KeyboardFocusManager + .getCurrentKeyboardFocusManager().getPermanentFocusOwner(); + return SwingXUtilities.isDescendingFrom(permanent, this); + } + + protected transient CellEditorRemover editorRemover; + + /** + * removes the standard editor remover and adds the custom remover. + * + */ + private void hackEditorRemover() { + KeyboardFocusManager manager = KeyboardFocusManager + .getCurrentKeyboardFocusManager(); + PropertyChangeListener[] listeners = manager + .getPropertyChangeListeners("permanentFocusOwner"); + for (int i = listeners.length - 1; i >= 0; i--) { + if (listeners[i].getClass().getName().startsWith( + "javax.swing.JTable")) { + manager.removePropertyChangeListener("permanentFocusOwner", + listeners[i]); + break; + } + } + if (editorRemover == null) { + editorRemover = new CellEditorRemover(); + } + } + + /** + * {@inheritDoc} + *

+ * + * Overridden to uninstall the custom editor remover. + */ + @Override + public void removeNotify() { + if (editorRemover != null) { + editorRemover.uninstall(); + editorRemover = null; + } + super.removeNotify(); + } + + /** + * {@inheritDoc} + *

+ * + * Overridden to prevent spurious focus loss to outside of table while + * removing the editor. This is essentially a hack around core bug #6210779. + * + * PENDING: add link to wiki! + */ + @Override + public boolean isFocusCycleRoot() { + if (isEditingFocusCycleRoot()) { + return true; + } + return super.isFocusCycleRoot(); + } + + /** + * {@inheritDoc} + *

+ * Overridden to try to stop the edit, if appropriate. Calls super if + * succeeded, does not yield otherwise. + * + */ + @Override + public void transferFocus() { + if (isEditingFocusCycleRoot() && !getCellEditor().stopCellEditing()) + return; + super.transferFocus(); + } + + /** + * {@inheritDoc} + *

+ * Overridden to try to stop the edit, if appropiate. Calls super if + * succeeded, does not yield otherwise. + * + */ + @Override + public void transferFocusBackward() { + if (isEditingFocusCycleRoot() && !getCellEditor().stopCellEditing()) + return; + super.transferFocusBackward(); + } + + /** + * + * @return a boolean to indicate whether the table needs to fake being focus + * cycle root. + */ + private boolean isEditingFocusCycleRoot() { + return isEditing() && isTerminateEditOnFocusLost(); + } + + /** + * This class tracks changes in the keyboard focus state. It is used when + * the JTable is editing to determine when to cancel the edit. If focus + * switches to a component outside of the jtable, but in the same window, + * this will cancel editing. + */ + class CellEditorRemover implements PropertyChangeListener { + KeyboardFocusManager focusManager; + + public CellEditorRemover() { + install(); + } + + private void install() { + focusManager = KeyboardFocusManager + .getCurrentKeyboardFocusManager(); + focusManager.addPropertyChangeListener("permanentFocusOwner", this); + focusManager.addPropertyChangeListener("managingFocus", this); + } + + /** + * remove all listener registrations. + * + */ + public void uninstall() { + focusManager.removePropertyChangeListener("permanentFocusOwner", + this); + focusManager.removePropertyChangeListener("managingFocus", this); + focusManager = null; + } + + @Override + public void propertyChange(PropertyChangeEvent ev) { + if (ev == null) + return; + if ("permanentFocusOwner".equals(ev.getPropertyName())) { + permanentFocusOwnerChange(); + } else if ("managingFocus".equals(ev.getPropertyName())) { + // TODO uninstall/install after manager changed. + } + } + + /** + * + */ + private void permanentFocusOwnerChange() { + if (!isEditing() || !isTerminateEditOnFocusLost()) { + return; + } + + Component c = focusManager.getPermanentFocusOwner(); + while (c != null) { + // PENDING JW: logic untested! + if (c instanceof JPopupMenu) { + c = ((JPopupMenu) c).getInvoker(); + } else { + if (c == JXTable.this) { + // focus remains inside the table + return; + } else if (c instanceof JPopupMenu) { + // PENDING JW: left-over? we should never reach this ... + // need to switch the hierarchy to a popups invoker + } else if ((c instanceof Window) + || (SwingXUtilities.isApplet(c) && c.getParent() == null)) { + if (c == SwingUtilities.getRoot(JXTable.this)) { + if (!getCellEditor().stopCellEditing()) { + getCellEditor().cancelCellEditing(); + } + } + break; + } + c = c.getParent(); + } + } + } + } + + // ---------------------------- updateUI support + + /** + * {@inheritDoc} + *

+ * Additionally updates auto-adjusted row height and highlighters. + *

+ * Another of the override motivation is to fix core issue (?? ID): super + * fails to update all renderers/editors. + */ + @Override + public void updateUI() { + super.updateUI(); + updateColumnControlUI(); + for (Enumeration defaultEditors = defaultEditorsByColumnClass + .elements(); defaultEditors.hasMoreElements();) { + updateEditorUI(defaultEditors.nextElement()); + } + + for (Enumeration defaultRenderers = defaultRenderersByColumnClass + .elements(); defaultRenderers.hasMoreElements();) { + updateRendererUI(defaultRenderers.nextElement()); + } + for (TableColumn column : getColumns(true)) { + updateColumnUI(column); + } + updateRowHeightUI(true); + updateHighlighterUI(); + } + + /** + * Updates the ui of the columnControl if appropriate. + */ + protected void updateColumnControlUI() { + if ((columnControlButton != null) + && (columnControlButton.getParent() == null)) { + SwingUtilities.updateComponentTreeUI(columnControlButton); + } + } + + /** + * Tries its best to updateUI of the potential + * TableCellEditor. + * + * @param maybeEditor the potential editor. + */ + private void updateEditorUI(Object maybeEditor) { + // maybe null or proxyValue + if (!(maybeEditor instanceof TableCellEditor)) + return; + // super handled this + if ((maybeEditor instanceof JComponent) + || (maybeEditor instanceof DefaultCellEditor)) + return; + // custom editors might balk about fake rows/columns + try { + Component comp = ((TableCellEditor) maybeEditor) + .getTableCellEditorComponent(this, null, false, -1, -1); + if (comp != null) { + SwingUtilities.updateComponentTreeUI(comp); + } + } catch (Exception e) { + // ignore - can't do anything + } + } + + /** + * Tries its best to updateUI of the potential + * TableCellRenderer. + * + * @param maybeRenderer the potential renderer. + */ + private void updateRendererUI(Object maybeRenderer) { + // maybe null or proxyValue + if (!(maybeRenderer instanceof TableCellRenderer)) + return; + // super handled this + if (maybeRenderer instanceof JComponent) + return; + Component comp = null; + if (maybeRenderer instanceof AbstractRenderer) { + comp = ((AbstractRenderer) maybeRenderer).getComponentProvider() + .getRendererComponent(null); + } else { + try { + // custom editors might balk about fake rows/columns + comp = ((TableCellRenderer) maybeRenderer) + .getTableCellRendererComponent(this, null, false, + false, -1, -1); + + } catch (Exception e) { + // can't do anything - renderer can't cope with off-range cells + } + } + if (comp != null) { + SwingUtilities.updateComponentTreeUI(comp); + } + } + + /** + * Updates TableColumn after updateUI changes. This implementation delegates + * to the column if it is of type UIDependent, takes over to try an update + * of the column's cellEditor, Cell-/HeaderRenderer otherwise. + * + * @param column the tableColumn to update. + */ + protected void updateColumnUI(TableColumn column) { + if (column instanceof UIDependent) { + ((UIDependent) column).updateUI(); + } else { + updateEditorUI(column.getCellEditor()); + updateRendererUI(column.getCellRenderer()); + updateRendererUI(column.getHeaderRenderer()); + } + } + + /** + * Updates highlighter after updateUI changes. + * + * @see UIDependent + */ + protected void updateHighlighterUI() { + if (compoundHighlighter == null) + return; + compoundHighlighter.updateUI(); + } + + /** + * Auto-adjusts rowHeight to something more pleasing then the default. This + * method is called after instantiation and after updating the UI. Does + * nothing if the given parameter is true and the rowHeight had + * been already set by client code. The underlying problem is that raw types + * can't implement UIResource. + *

+ * This implementation asks the UIManager for a default value (stored with + * key "JXTable.rowHeight"). If none is available, calculates a "reasonable" + * height from the table's fontMetrics, assuming that most renderers/editors + * will have a border with top/bottom of 1. + *

+ * + * @param respectRowSetFlag a boolean to indicate whether client-code flag + * should be respected. + * @see #isXTableRowHeightSet + */ + protected void updateRowHeightUI(boolean respectRowSetFlag) { + if (respectRowSetFlag && isXTableRowHeightSet) + return; + int uiHeight = UIManager.getInt(UIPREFIX + "rowHeight"); + if (uiHeight > 0) { + setRowHeight(uiHeight); + } else { + int fontBasedHeight = getFontMetrics(getFont()).getHeight() + 2; + int magicMinimum = 18; + setRowHeight(Math.max(fontBasedHeight, magicMinimum)); + } + isXTableRowHeightSet = false; + } + + /** + * Convenience to set both grid line visibility and default margin for + * horizontal/vertical lines. The margin defaults to 1 or 0 if the grid + * lines are drawn or not drawn. + *

+ * + * @param showHorizontalLines boolean to decide whether to draw horizontal + * grid lines. + * @param showVerticalLines boolean to decide whether to draw vertical grid + * lines. + * @see JTable#setShowGrid(boolean) + * @see JTable#setIntercellSpacing(Dimension) + */ + public void setShowGrid(boolean showHorizontalLines, + boolean showVerticalLines) { + int defaultRowMargin = showHorizontalLines ? 1 : 0; + setRowMargin(defaultRowMargin); + setShowHorizontalLines(showHorizontalLines); + int defaultColumnMargin = showVerticalLines ? 1 : 0; + setColumnMargin(defaultColumnMargin); + setShowVerticalLines(showVerticalLines); + } + + /** + * {@inheritDoc} + *

+ * Behaves exactly like super. + *

+ * It's overridden to warn against a frequent programming error: this method + * toggles only the visibility of the grid lines, it does not + * update the row/column margins - which may lead to visual artefacts, as + * f.i. not showing the lines at all or showing normal table background in + * selected state where the lines should have been. + * + * @see #setShowGrid(boolean, boolean) + */ + @Override + public void setShowGrid(boolean showGrid) { + super.setShowGrid(showGrid); + } + + /** + * {@inheritDoc} + *

+ * Overriden to mark the request as client-code induced. + * + * @see #isXTableRowHeightSet + */ + @Override + public void setRowHeight(int rowHeight) { + super.setRowHeight(rowHeight); + if (rowHeight > 0) { + isXTableRowHeightSet = true; + } + } + + /** + * Sets the rowHeight for all rows to the given value. Keeps the flag + * isXTableRowHeight unchanged. This enables the distinction + * between setting the height for internal reasons from doing so by client + * code. + * + * @param rowHeight new height in pixel. + * @see #setRowHeight(int) + * @see #isXTableRowHeightSet + */ + protected void adminSetRowHeight(int rowHeight) { + boolean heightSet = isXTableRowHeightSet; + setRowHeight(rowHeight); + isXTableRowHeightSet = heightSet; + } + + // ---------------------------- overriding super factory methods and buggy + /** + * {@inheritDoc} + *

+ * Overridden to work around core Bug (ID #6291631): negative y is mapped to + * row 0). + * + */ + @Override + public int rowAtPoint(Point point) { + if (point.y < 0) + return -1; + return super.rowAtPoint(point); + } + + /** + * + * {@inheritDoc} + *

+ * + * Overridden to return a JXTableHeader. + * + * @see JXTableHeader + */ + @Override + protected JTableHeader createDefaultTableHeader() { + return new JXTableHeader(columnModel); + } + + /** + * + * {@inheritDoc} + *

+ * + * Overridden to return a DefaultTableColumnModelExt. + * + * @see DefaultTableColumnModelExt + */ + @Override + protected TableColumnModel createDefaultColumnModel() { + return new DefaultTableColumnModelExt(); + } + + /** + * {@inheritDoc} + *

+ * Overridden because super throws NPE on null param. + */ + @Override + public void setSelectionBackground(Color selectionBackground) { + Color old = getSelectionBackground(); + this.selectionBackground = selectionBackground; + firePropertyChange("selectionBackground", old, getSelectionBackground()); + repaint(); + } + + /** + * {@inheritDoc} + *

+ * Overridden because super throws NPE on null param. + */ + @Override + public void setSelectionForeground(Color selectionForeground) { + Color old = getSelectionForeground(); + this.selectionForeground = selectionForeground; + firePropertyChange("selectionForeground", old, getSelectionForeground()); + repaint(); + } + + /** + * {@inheritDoc} + *

+ * Overridden because super throws NPE on null param. + */ + @Override + public void setGridColor(Color gridColor) { + Color old = getGridColor(); + this.gridColor = gridColor; + firePropertyChange("gridColor", old, getGridColor()); + repaint(); + } + +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/JXTableHeader.java b/src/main/java/org/jdesktop/swingx/JXTableHeader.java new file mode 100644 index 0000000000..6a9586e06b --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXTableHeader.java @@ -0,0 +1,752 @@ +/* + * $Id: JXTableHeader.java 3960 2011-03-15 19:36:53Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx; + +import org.jdesktop.swingx.event.TableColumnModelExtListener; +import org.jdesktop.swingx.plaf.LookAndFeelAddons; +import org.jdesktop.swingx.plaf.TableHeaderAddon; +import org.jdesktop.swingx.sort.SortController; +import org.jdesktop.swingx.table.TableColumnExt; + +import javax.swing.*; +import javax.swing.event.MouseInputListener; +import javax.swing.table.JTableHeader; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableColumn; +import javax.swing.table.TableColumnModel; +import java.awt.*; +import java.awt.event.MouseEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.Serializable; +import java.util.logging.Logger; + +/** + * TableHeader with extended functionality if associated Table is of + * type JXTable.

+ * + *

Extended user interaction

+ * + *
    + *
  • Supports column pack (== auto-resize to exactly fit the contents) + * on double-click in resize region. + *
  • Configurable to resort a column on the second click of a mouseClicked event + * (feature request #271-swingx) + *
  • Does its best to not sort if the mouse click happens in the resize region. + *
  • Supports horizontal auto-scroll if a column is dragged outside visible rectangle. + * This feature is enabled if the autoscrolls property is true. The default is false + * (because of Issue #788-swingx which still isn't fixed for jdk1.6). + *
+ * + * Note: extended sort and resize related functionality is fully effective only if the header's + * table is of type JXTable and has control over the row sorter, that is the row sorter + * is of type SortController. + * + *

Extended functionality

+ * + *
    + *
  • Listens to TableColumn propertyChanges to update itself accordingly. + *
  • Supports per-column header ToolTips. + *
  • Guarantees reasonable minimal height > 0 for header preferred height. +*
+ * + * + * @author Jeanette Winzenburg + * + * @see JXTable#toggleSortOrder(int) + * @see JXTable#resetSortOrder() + * @see SortGestureRecognizer + */ +public class JXTableHeader extends JTableHeader + implements TableColumnModelExtListener { + + @SuppressWarnings("unused") + private static final Logger LOG = Logger.getLogger(JXTableHeader.class + .getName()); + + static { + LookAndFeelAddons.contribute(new TableHeaderAddon()); + } + + private transient PropertyChangeListener tablePropertyChangeListener; + private boolean resortsOnDoubleClick; + + /** + * Constructs a JTableHeader with a default + * TableColumnModel. + * + * @see #createDefaultColumnModel + */ + public JXTableHeader() { + super(); + } + + /** + * Constructs a JTableHeader which is initialized with + * cm as the column model. If cm is + * null this method will initialize the table header with a + * default TableColumnModel. + * + * @param columnModel the column model for the table + * @see #createDefaultColumnModel + */ + public JXTableHeader(TableColumnModel columnModel) { + super(columnModel); + } + + + /** + * {@inheritDoc}

+ * Sets the associated JTable. Enables enhanced header + * features if table is of type JXTable.

+ * + * PENDING: who is responsible for synching the columnModel? + */ + @Override + public void setTable(JTable table) { + uninstallTable(); + super.setTable(table); + installTable(); +// setColumnModel(table.getColumnModel()); + // the additional listening option makes sense only if the table + // actually is a JXTable + if (getXTable() != null) { + installHeaderListener(); + } else { + uninstallHeaderListener(); + } + } + + /** + * Installs the table.

+ * This implemenation synchs enabled state and installs the PropertyChangeListener. + */ + protected void installTable() { + updateEnabledFromTable(); + if (getTable() == null) return; + getTable().addPropertyChangeListener(getTablePropertyChangeListener()); + } + + /** + * Synchs the header's enabled with the table's enabled property. + */ + protected void updateEnabledFromTable() { + setEnabled(getTable() != null ? getTable().isEnabled() : true); + } + + /** + * Uninstalls the table.

+ * This implementation uninstalls the PropertyChangeListener. + */ + protected void uninstallTable() { + if (getTable() == null) return; + getTable().removePropertyChangeListener(getTablePropertyChangeListener()); + } + + + /** + * Implements TableColumnModelExt to allow internal update after + * column property changes.

+ * + * This implementation triggers a resizeAndRepaint on every propertyChange which + * doesn't already fire a "normal" columnModelEvent. + * + * @param event change notification from a contained TableColumn. + * @see #isColumnEvent(PropertyChangeEvent) + * @see TableColumnModelExtListener + * + * + */ + @Override + public void columnPropertyChange(PropertyChangeEvent event) { + if (isColumnEvent(event)) return; + resizeAndRepaint(); + } + + + /** + * Returns a boolean indicating if a property change event received + * from column changes is expected to be already broadcasted by the + * core TableColumnModel.

+ * + * This implementation returns true for notification of width, preferredWidth + * and visible properties, false otherwise. + * + * @param event the PropertyChangeEvent received as TableColumnModelExtListener. + * @return a boolean to decide whether the same event triggers a + * base columnModelEvent. + */ + protected boolean isColumnEvent(PropertyChangeEvent event) { + return "width".equals(event.getPropertyName()) || + "preferredWidth".equals(event.getPropertyName()) + || "visible".equals(event.getPropertyName()); + } + + /** + * {@inheritDoc}

+ * + * Overridden to respect the column tooltip, if available. + * + * @return the column tooltip of the column at the mouse position + * if not null or super if not available. + */ + @Override + public String getToolTipText(MouseEvent event) { + String columnToolTipText = getColumnToolTipText(event); + return columnToolTipText != null ? columnToolTipText : super.getToolTipText(event); + } + + /** + * Returns the column tooltip of the column at the position + * of the MouseEvent, if a tooltip is available. + * + * @param event the mouseEvent representing the mouse location. + * @return the column tooltip of the column below the mouse location, + * or null if not available. + */ + protected String getColumnToolTipText(MouseEvent event) { + if (getXTable() == null) return null; + int column = columnAtPoint(event.getPoint()); + if (column < 0) return null; + TableColumnExt columnExt = getXTable().getColumnExt(column); + return columnExt != null ? columnExt.getToolTipText() : null; + } + + /** + * Returns the associated table if it is of type JXTable, or null if not. + * + * @return the associated table if of type JXTable or null if not. + */ + public JXTable getXTable() { + if (!(getTable() instanceof JXTable)) + return null; + return (JXTable) getTable(); + } + + /** + * Returns the resortsOnDoubleClick property. + * + * @return a flag indicating whether or not the second click in a mouseClicked + * event should toggle the sort order again. + * + * @see #setResortsOnDoubleClick(boolean) + */ + public boolean getResortsOnDoubleClick() { + return getXTable() != null && resortsOnDoubleClick; + } + + /** + * Sets the resortsOnDoubleClick property. If enabled, the second click + * of a mouseClicked event will toggle the sort order again if the + * column has been unsorted before. This is introduced to support + * feature request #271-swingx. It is effective only if the coupled table + * is of type JXTable and has full control about its RowSorter's properties. + * + * The default value is false. + * + * @param resortsOnDoubleClick a boolean indicating whether or not the + * second click in a mouseClicked event should resort the column. + * + * @see #getResortsOnDoubleClick() + */ + public void setResortsOnDoubleClick(boolean resortsOnDoubleClick) { + boolean old = getResortsOnDoubleClick(); + this.resortsOnDoubleClick = resortsOnDoubleClick; + firePropertyChange("resortsOnDoubleClick", old, getResortsOnDoubleClick()); + } + + /** + * Returns the TableCellRenderer to use for the column with the given index. This + * implementation returns the column's header renderer if available or this header's + * default renderer if not. + * + * @param columnIndex the index in view coordinates of the column + * @return the renderer to use for the column, guaranteed to be not null. + */ + public TableCellRenderer getCellRenderer(int columnIndex) { + TableCellRenderer renderer = getColumnModel().getColumn(columnIndex).getHeaderRenderer(); + return renderer != null ? renderer : getDefaultRenderer(); + } + + /** + * {@inheritDoc}

+ * + * Overridden to adjust for a reasonable minimum height. Done to fix Issue 334-swingx, + * which actually is a core issue misbehaving in returning a zero height + * if the first column has no text. + * + * @see #getPreferredSize(Dimension) + * @see #getMinimumHeight(int). + * + */ + @Override + public Dimension getPreferredSize() { + Dimension pref = super.getPreferredSize(); + pref = getPreferredSize(pref); + pref.height = getMinimumHeight(pref.height); + return pref; + } + + /** + * Returns a preferred size which is adjusted to the maximum of all + * header renderers' height requirement. + * + * @param pref an initial preferred size + * @return the initial preferred size with its height property adjusted + * to the maximum of all renderers preferred height requirement. + * + * @see #getPreferredSize() + * @see #getMinimumHeight(int) + */ + protected Dimension getPreferredSize(Dimension pref) { + int height = pref.height; + for (int i = 0; i < getColumnModel().getColumnCount(); i++) { + TableCellRenderer renderer = getCellRenderer(i); + Component comp = renderer.getTableCellRendererComponent(table, + getColumnModel().getColumn(i).getHeaderValue(), false, false, -1, i); + height = Math.max(height, comp.getPreferredSize().height); + } + pref.height = height; + return pref; + + } + + /** + * Returns a reasonable minimal preferred height for the header. This is + * meant as a last straw if all header values are null, renderers report 0 as + * their preferred height.

+ * + * This implementation returns the default header renderer's preferred height as measured + * with a dummy value if the input height is 0, otherwise returns the height + * unchanged. + * + * @param height the initial height. + * @return a reasonable minimal preferred height. + * + * @see #getPreferredSize() + * @see #getPreferredSize(Dimension) + */ + protected int getMinimumHeight(int height) { + if ((height == 0)) { +// && (getXTable() != null) +// && getXTable().isColumnControlVisible()){ + TableCellRenderer renderer = getDefaultRenderer(); + Component comp = renderer.getTableCellRendererComponent(getTable(), + "dummy", false, false, -1, -1); + height = comp.getPreferredSize().height; + } + return height; + } + + + /** + * @inherited

+ * + * Overridden to fire a propertyChange for draggedColumn. + */ + @Override + public void setDraggedColumn(TableColumn column) { + if (getDraggedColumn() == column) return; + TableColumn old = getDraggedColumn(); + super.setDraggedColumn(column); + firePropertyChange("draggedColumn", old, getDraggedColumn()); + } + + + /** + * @inherited

+ * + * Overridden to fire a propertyChange for resizingColumn. + */ + @Override + public void setResizingColumn(TableColumn aColumn) { + if (getResizingColumn() == aColumn) return; + TableColumn old = getResizingColumn(); + super.setResizingColumn(aColumn); + firePropertyChange("resizingColumn", old, getResizingColumn()); + } + + + + /** + * {@inheritDoc}

+ * + * Overridden to scroll the table to keep the dragged column visible. + * This side-effect is enabled only if the header's autoscroll property is + * true and the associated table is of type JXTable.

+ * + * The autoscrolls is disabled by default. With or without - core + * issue #6503981 has weird effects (for jdk 1.6 - 1.6u3) on a plain + * JTable as well as a JXTable, fixed in 1.6u4. + * + */ + @Override + public void setDraggedDistance(int distance) { + int old = getDraggedDistance(); + super.setDraggedDistance(distance); + // fire because super doesn't + firePropertyChange("draggedDistance", old, getDraggedDistance()); + if (!getAutoscrolls() || (getXTable() == null)) return; + TableColumn column = getDraggedColumn(); + // fix for #788-swingx: don't try to scroll if we have no dragged column + // as doing will confuse the horizontalScrollEnabled on the JXTable. + if (column != null) { + getXTable().scrollColumnToVisible(getViewIndexForColumn(column)); + } + } + + /** + * Returns the the dragged column if and only if, a drag is in process and + * the column is visible, otherwise returns null. + * + * @return the dragged column, if a drag is in process and the column is + * visible, otherwise returns null + * @see #getDraggedDistance + */ + @Override + public TableColumn getDraggedColumn() { + return isVisible(draggedColumn) ? draggedColumn : null; + } + + /** + * Checks and returns the column's visibility. + * + * @param column the TableColumn to check + * @return a boolean indicating if the column is visible + */ + private boolean isVisible(TableColumn column) { + return getViewIndexForColumn(column) >= 0; + } + + /** + * Returns the (visible) view index for the table column + * or -1 if not visible or not contained in this header's + * columnModel. + * + * + * @param aColumn the TableColumn to find the view index for + * @return the view index of the given table column or -1 if not visible + * or not contained in the column model. + */ + private int getViewIndexForColumn(TableColumn aColumn) { + if (aColumn == null) + return -1; + TableColumnModel cm = getColumnModel(); + for (int column = 0; column < cm.getColumnCount(); column++) { + if (cm.getColumn(column) == aColumn) { + return column; + } + } + return -1; + } + + /** + * Returns the PropertyChangeListener to register on the owning table, + * lazily created. + * + * @return the PropertyChangeListener to use on the owning table. + */ + protected PropertyChangeListener getTablePropertyChangeListener() { + if (tablePropertyChangeListener == null) { + tablePropertyChangeListener = createTablePropertyChangeListener(); + } + return tablePropertyChangeListener; + } + + /** + * Creates and returns the PropertyChangeListener to register on the + * owning table.

+ * + * This implementation synchs the header's enabled properties with the + * table's enabled. + * + * @return the PropertyChangeListener to register on the owning table. + */ + protected PropertyChangeListener createTablePropertyChangeListener() { + PropertyChangeListener l = new PropertyChangeListener() { + + @Override + public void propertyChange(PropertyChangeEvent evt) { + if ("enabled".equals(evt.getPropertyName())) { + updateEnabledFromTable(); + } + } + }; + return l; + } + + + /** + * Creates and installs header listeners to service the extended functionality. + * This implementation creates and installs a custom mouse input listener. + */ + protected void installHeaderListener() { + if (headerListener == null) { + headerListener = new HeaderListener(); + addMouseListener(headerListener); + addMouseMotionListener(headerListener); + } + } + + /** + * Uninstalls header listeners to service the extended functionality. + * This implementation uninstalls a custom mouse input listener. + */ + protected void uninstallHeaderListener() { + if (headerListener != null) { + removeMouseListener(headerListener); + removeMouseMotionListener(headerListener); + headerListener = null; + } + } + + private MouseInputListener headerListener; + + /** + * A MouseListener implementation to support enhanced tableHeader functionality. + * + * Supports column "packing" by double click in resize region. Works around + * core issue #6862170 (must not sort column by click into resize region). + *

+ * + * Note that the logic is critical, mostly because it must be independent of + * sequence of listener notification. So we check whether or not a pressed + * happens in the resizing region in both pressed and released, taking the + * header's resizingColumn property as a marker. The inResize flag can only + * be turned on in those. At the end of the released, we check if we are + * in resize and disable core sorting - which happens in clicked - if appropriate. + * In our clicked we hook the pack action (happens only on double click) + * and reset the resizing region flag always. Pressed (and all other methods) + * restore sorting enablement. + *

+ * + * Supports resort on double click if enabled in the JXTableHeader (Issue #271-swingx). + * + * Is fully effective only if JXTable has control over the row sorter, that is + * if the row sorter is of type SortController. + * + */ + private class HeaderListener implements MouseInputListener, Serializable { + private TableColumn cachedResizingColumn; + private SortOrder[] cachedSortOrderCycle; + private int sortColumn = -1; + + /** + * Packs column on double click in resize region. Resorts + * column on double click if enabled and not in resize region. + */ + @Override + public void mouseClicked(MouseEvent e) { + if (shouldIgnore(e)) { + return; + } + doResize(e); + doDoubleSort(e); + uncacheResizingColumn(); + } + + private void doDoubleSort(MouseEvent e) { + if (!hasCachedSortColumn() || e.getClickCount() % 2 == 1) return; + getXTable().toggleSortOrder(sortColumn); + uncacheSortColumn(); + } + + private boolean hasCachedSortColumn() { + return sortColumn >= 0; + } + + /** + * Resets sort enablement always, set resizing marker if available. + */ + @Override + public void mousePressed(MouseEvent e) { + resetToggleSortOrder(e); + if (shouldIgnore(e)) { + return; + } + cacheResizingColumn(e); + } + + /** + * Sets resizing marker if available, disables table sorting if in + * resize region and sort gesture (aka: single click). + */ + @Override + public void mouseReleased(MouseEvent e) { + if (shouldIgnore(e)) { + return; + } + cacheResizingColumn(e); + cacheSortColumn(e); + if (isInResizeRegion(e) && e.getClickCount() % 2 == 1) { + disableToggleSortOrder(e); + } + } + + private void cacheSortColumn(MouseEvent e) { + if (!canCacheSortColumn(e)) uncacheSortColumn(); + if (e.getClickCount() % 2 == 1) { + int column = columnAtPoint(e.getPoint()); + if (column >= 0) { + int primarySortIndex = getXTable().getSortedColumnIndex(); + if (primarySortIndex == column) { + column = -1; + } + } + sortColumn = column; + } + + } + + private void uncacheSortColumn() { + sortColumn = -1; + } + + private boolean canCacheSortColumn(MouseEvent e) { + if (hasSortController() && !isInResizeRegion(e) && getResortsOnDoubleClick()) { + return true; + } + return false; + } + + /** + * Returns a boolean indication if the mouse event should be ignored. + * Here: returns true if table not enabled or not an event from the left mouse + * button. + * + * @param e + * @return + */ + private boolean shouldIgnore(MouseEvent e) { + return !SwingUtilities.isLeftMouseButton(e) + || !table.isEnabled(); + } + + /** + * Packs caches resizing column on double click, if available. Does nothing + * otherwise. + * + * @param e + */ + private void doResize(MouseEvent e) { + if (e.getClickCount() != 2) + return; + int column = getViewIndexForColumn(cachedResizingColumn); + if (column >= 0) { + (getXTable()).packColumn(column, 5); + } + } + + + /** + * + * @param e + */ + private void disableToggleSortOrder(MouseEvent e) { + if (!hasSortController()) return; + SortController controller = (SortController) getXTable().getRowSorter(); + cachedSortOrderCycle = controller.getSortOrderCycle(); + controller.setSortOrderCycle(); + } + + /** + * @return + */ + private boolean hasSortController() { + return (getXTable().getRowSorter() instanceof SortController); + } + + /** + * + */ + private void resetToggleSortOrder(MouseEvent e) { + if (cachedSortOrderCycle == null) return; + ((SortController) getXTable().getRowSorter()).setSortOrderCycle(cachedSortOrderCycle); + cachedSortOrderCycle = null; + } + + + /** + * Caches the resizing column if set. Does nothing if null. + * + * @param e + */ + private void cacheResizingColumn(MouseEvent e) { + TableColumn column = getResizingColumn(); + if (column != null) { + cachedResizingColumn = column; + } + } + + /** + * Sets the cached resizing column to null. + */ + private void uncacheResizingColumn() { + cachedResizingColumn = null; + } + + /** + * Returns true if the mouseEvent happened in the resizing region. + * + * @param e + * @return + */ + private boolean isInResizeRegion(MouseEvent e) { + return cachedResizingColumn != null; // inResize; + } + + @Override + public void mouseEntered(MouseEvent e) { + } + + /** + * Resets all cached state. + */ + @Override + public void mouseExited(MouseEvent e) { + uncacheSortColumn(); + uncacheResizingColumn(); + resetToggleSortOrder(e); + } + + /** + * Resets all cached state. + */ + @Override + public void mouseDragged(MouseEvent e) { + uncacheSortColumn(); + uncacheResizingColumn(); + resetToggleSortOrder(e); + } + + /** + * Resets all cached state. + */ + @Override + public void mouseMoved(MouseEvent e) { + uncacheSortColumn(); + resetToggleSortOrder(e); + } + } + + + +} diff --git a/src/main/java/org/jdesktop/swingx/JXTaskPane.java b/src/main/java/org/jdesktop/swingx/JXTaskPane.java new file mode 100644 index 0000000000..94b4de71bb --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXTaskPane.java @@ -0,0 +1,625 @@ +/* + * $Id: JXTaskPane.java 4260 2012-11-14 20:59:08Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.plaf.LookAndFeelAddons; +import org.jdesktop.swingx.plaf.TaskPaneAddon; +import org.jdesktop.swingx.plaf.TaskPaneUI; + +import javax.swing.*; +import java.awt.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +/** + * JXTaskPane is a container for tasks and other + * arbitrary components. + * + *

+ * Several JXTaskPanes are usually grouped together within a + * {@link JXTaskPaneContainer}. However it is not mandatory + * to use a JXTaskPaneContainer as the parent for JXTaskPane. The JXTaskPane can + * be added to any other container. See + * {@link JXTaskPaneContainer} to understand the benefits of + * using it as the parent container. + * + *

+ * JXTaskPane provides control to expand and + * collapse the content area in order to show or hide the task list. It can have an + * icon, a title and can be marked as + * special. Marking a JXTaskPane as + * special ({@link #setSpecial(boolean)} is only a hint for + * the pluggable UI which will usually paint it differently (by example by + * using another color for the border of the pane). + * + *

+ * When the JXTaskPane is expanded or collapsed, it will be + * animated with a fade effect. The animated can be disabled on a per + * component basis through {@link #setAnimated(boolean)}. + * + * To disable the animation for all newly created JXTaskPane, + * use the UIManager property: + * UIManager.put("TaskPane.animate", Boolean.FALSE);. + * + *

+ * Example: + *

+ * 
+ * JXFrame frame = new JXFrame();
+ * 
+ * // a container to put all JXTaskPane together
+ * JXTaskPaneContainer taskPaneContainer = new JXTaskPaneContainer();
+ * 
+ * // create a first taskPane with common actions
+ * JXTaskPane actionPane = new JXTaskPane();
+ * actionPane.setTitle("Files and Folders");
+ * actionPane.setSpecial(true);
+ * 
+ * // actions can be added, a hyperlink will be created
+ * Action renameSelectedFile = createRenameFileAction();
+ * actionPane.add(renameSelectedFile);
+ * actionPane.add(createDeleteFileAction());
+ * 
+ * // add this taskPane to the taskPaneContainer
+ * taskPaneContainer.add(actionPane);
+ * 
+ * // create another taskPane, it will show details of the selected file
+ * JXTaskPane details = new JXTaskPane();
+ * details.setTitle("Details");
+ *  
+ * // add standard components to the details taskPane
+ * JLabel searchLabel = new JLabel("Search:");
+ * JTextField searchField = new JTextField("");
+ * details.add(searchLabel);
+ * details.add(searchField);
+ * 
+ * taskPaneContainer.add(details);
+ * 
+ * // put the action list on the left 
+ * frame.add(taskPaneContainer, BorderLayout.EAST);
+ * 
+ * // and a file browser in the middle
+ * frame.add(fileBrowser, BorderLayout.CENTER);
+ * 
+ * frame.pack();
+ * frame.setVisible(true);
+ * 
+ * 
+ * + * @see JXTaskPaneContainer + * @see JXCollapsiblePane + * @author Frederic Lavigne + * @author Karl George Schaefer + * + * @javabean.attribute + * name="isContainer" + * value="Boolean.TRUE" + * rtexpr="true" + * + * @javabean.attribute + * name="containerDelegate" + * value="getContentPane" + * + * @javabean.class + * name="JXTaskPane" + * shortDescription="JXTaskPane is a container for tasks and other arbitrary components." + * stopClass="java.awt.Component" + * + * @javabean.icons + * mono16="JXTaskPane16-mono.gif" + * color16="JXTaskPane16.gif" + * mono32="JXTaskPane32-mono.gif" + * color32="JXTaskPane32.gif" + */ +@JavaBean +@SuppressWarnings("nls") +public class JXTaskPane extends JPanel implements + JXCollapsiblePane.CollapsiblePaneContainer, Mnemonicable { + + /** + * JXTaskPane pluggable UI key swingx/TaskPaneUI + */ + public final static String uiClassID = "swingx/TaskPaneUI"; + + // ensure at least the default ui is registered + static { + LookAndFeelAddons.contribute(new TaskPaneAddon()); + } + + /** + * Used when generating PropertyChangeEvents for the "scrollOnExpand" property + */ + public static final String SCROLL_ON_EXPAND_CHANGED_KEY = "scrollOnExpand"; + + /** + * Used when generating PropertyChangeEvents for the "title" property + */ + public static final String TITLE_CHANGED_KEY = "title"; + + /** + * Used when generating PropertyChangeEvents for the "icon" property + */ + public static final String ICON_CHANGED_KEY = "icon"; + + /** + * Used when generating PropertyChangeEvents for the "special" property + */ + public static final String SPECIAL_CHANGED_KEY = "special"; + + /** + * Used when generating PropertyChangeEvents for the "animated" property + */ + public static final String ANIMATED_CHANGED_KEY = "animated"; + + private String title; + private Icon icon; + private boolean special; + private boolean scrollOnExpand; + + private int mnemonic; + private int mnemonicIndex = -1; + + private JXCollapsiblePane collapsePane; + + /** + * Creates a new empty JXTaskPane. + */ + public JXTaskPane() { + this((String) null); + } + + /** + * Creates a new task pane with the specified title. + * + * @param title + * the title to use + */ + public JXTaskPane(String title) { + this(title, null); + } + + /** + * Creates a new task pane with the specified icon. + * + * @param icon + * the icon to use + */ + public JXTaskPane(Icon icon) { + this(null, icon); + } + + /** + * Creates a new task pane with the specified title and icon. + * + * @param title + * the title to use + * @param icon + * the icon to use + */ + public JXTaskPane(String title, Icon icon) { + collapsePane = new JXCollapsiblePane(); + collapsePane.setOpaque(false); + super.setLayout(new BorderLayout(0, 0)); + super.addImpl(collapsePane, BorderLayout.CENTER, -1); + + setTitle(title); + setIcon(icon); + + updateUI(); + setFocusable(true); + + // disable animation if specified in UIManager + setAnimated(!Boolean.FALSE.equals(UIManager.get("TaskPane.animate"))); + + // listen for animation events and forward them to registered listeners + collapsePane.addPropertyChangeListener("collapsed", new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + JXTaskPane.this.firePropertyChange(evt.getPropertyName(), evt.getOldValue(), + evt.getNewValue()); + } + }); + } + + /** + * Returns the contentPane object for this JXTaskPane. + * @return the contentPane property + */ + public Container getContentPane() { + return collapsePane.getContentPane(); + } + + /** + * Notification from the UIManager that the L&F has changed. + * Replaces the current UI object with the latest version from the UIManager. + * + * @see JComponent#updateUI + */ + @Override + public void updateUI() { + // collapsePane is null when updateUI() is called by the "super()" + // constructor + if (collapsePane == null) { + return; + } + setUI((TaskPaneUI)LookAndFeelAddons.getUI(this, TaskPaneUI.class)); + } + + /** + * Sets the L&F object that renders this component. + * + * @param ui the TaskPaneUI L&F object + * @see javax.swing.UIDefaults#getUI + * + * @beaninfo bound: true hidden: true description: The UI object that + * implements the taskpane group's LookAndFeel. + */ + public void setUI(TaskPaneUI ui) { + super.setUI(ui); + } + + /** + * Returns the name of the L&F class that renders this component. + * + * @return the string {@link #uiClassID} + * @see JComponent#getUIClassID + * @see javax.swing.UIDefaults#getUI + */ + @Override + public String getUIClassID() { + return uiClassID; + } + + /** + * Returns the title currently displayed in the border of this pane. + * + * @return the title currently displayed in the border of this pane + */ + public String getTitle() { + return title; + } + + /** + * Sets the title to be displayed in the border of this pane. + * + * @param title the title to be displayed in the border of this pane + * @javabean.property + * bound="true" + * preferred="true" + */ + public void setTitle(String title) { + String old = this.title; + this.title = title; + firePropertyChange(TITLE_CHANGED_KEY, old, title); + } + + /** + * Returns the icon currently displayed in the border of this pane. + * + * @return the icon currently displayed in the border of this pane + */ + public Icon getIcon() { + return icon; + } + + /** + * Sets the icon to be displayed in the border of this pane. Some pluggable + * UIs may impose size constraints for the icon. A size of 16x16 pixels is + * the recommended icon size. + * + * @param icon the icon to be displayed in the border of this pane + * @javabean.property + * bound="true" + * preferred="true" + */ + public void setIcon(Icon icon) { + Icon old = this.icon; + this.icon = icon; + firePropertyChange(ICON_CHANGED_KEY, old, icon); + } + + /** + * Returns true if this pane is "special". + * + * @return true if this pane is "special" + * @see #setSpecial(boolean) + */ + public boolean isSpecial() { + return special; + } + + /** + * Sets this pane to be "special" or not. Marking a JXTaskPane + * as special is only a hint for the pluggable UI which will + * usually paint it differently (by example by using another color for the + * border of the pane). + * + *

+ * Usually the first JXTaskPane in a JXTaskPaneContainer is marked as special + * because it contains the default set of actions which can be executed given + * the current context. + * + * @param special + * true if this pane is "special", false otherwise + * @javabean.property bound="true" preferred="true" + */ + public void setSpecial(boolean special) { + boolean oldValue = isSpecial(); + this.special = special; + firePropertyChange(SPECIAL_CHANGED_KEY, oldValue, isSpecial()); + } + + /** + * Should this group be scrolled to be visible on expand. + * + * @param scrollOnExpand true to scroll this group to be + * visible if this group is expanded. + * + * @see #setCollapsed(boolean) + * + * @javabean.property + * bound="true" + * preferred="true" + */ + public void setScrollOnExpand(boolean scrollOnExpand) { + boolean oldValue = isScrollOnExpand(); + this.scrollOnExpand = scrollOnExpand; + firePropertyChange(SCROLL_ON_EXPAND_CHANGED_KEY, + oldValue, isScrollOnExpand()); + } + + /** + * Should this group scroll to be visible after + * this group was expanded. + * + * @return true if we should scroll false if nothing + * should be done. + */ + public boolean isScrollOnExpand() { + return scrollOnExpand; + } + + /** + * Expands or collapses this group. + *

+ * As of SwingX 1.6.3, the property change event only fires when the + * state is accurate. As such, animated task panes fire once the + * animation is complete. + * + * @param collapsed + * true to collapse the group, false to expand it + * @javabean.property + * bound="true" + * preferred="false" + */ + public void setCollapsed(boolean collapsed) { + collapsePane.setCollapsed(collapsed); + } + + /** + * Returns the collapsed state of this task pane. + * + * @return {@code true} if the task pane is collapsed; {@code false} + * otherwise + */ + public boolean isCollapsed() { + return collapsePane.isCollapsed(); + } + + /** + * Enables or disables animation during expand/collapse transition. + * + * @param animated + * @javabean.property + * bound="true" + * preferred="true" + */ + public void setAnimated(boolean animated) { + boolean oldValue = isAnimated(); + collapsePane.setAnimated(animated); + firePropertyChange(ANIMATED_CHANGED_KEY, oldValue, isAnimated()); + } + + /** + * Returns true if this task pane is animated during expand/collapse + * transition. + * + * @return true if this task pane is animated during expand/collapse + * transition. + */ + public boolean isAnimated() { + return collapsePane.isAnimated(); + } + + /** + * {@inheritDoc} + *

+ * If the character defined by the mnemonic is found within the task pane's + * text string, the first occurrence of it will be underlined to indicate + * the mnemonic to the user. + */ + @Override + public int getMnemonic() { + return mnemonic; + } + + /** + * {@inheritDoc} + */ + @Override + public void setMnemonic(int mnemonic) { + int oldValue = getMnemonic(); + this.mnemonic = mnemonic; + + firePropertyChange("mnemonic", oldValue, getMnemonic()); + + updateDisplayedMnemonicIndex(getTitle(), mnemonic); + revalidate(); + repaint(); + } + + /** + * Update the displayedMnemonicIndex property. This method + * is called when either text or mnemonic changes. The new + * value of the displayedMnemonicIndex property is the index + * of the first occurrence of mnemonic in text. + */ + private void updateDisplayedMnemonicIndex(String text, int mnemonic) { + if (text == null || mnemonic == '\0') { + mnemonicIndex = -1; + + return; + } + + char uc = Character.toUpperCase((char)mnemonic); + char lc = Character.toLowerCase((char)mnemonic); + + int uci = text.indexOf(uc); + int lci = text.indexOf(lc); + + if (uci == -1) { + mnemonicIndex = lci; + } else if(lci == -1) { + mnemonicIndex = uci; + } else { + mnemonicIndex = (lci < uci) ? lci : uci; + } + } + + /** + * {@inheritDoc} + */ + @Override + public int getDisplayedMnemonicIndex() { + return mnemonicIndex; + } + + /** + * {@inheritDoc} + */ + @Override + public void setDisplayedMnemonicIndex(int index) + throws IllegalArgumentException { + int oldValue = mnemonicIndex; + if (index == -1) { + mnemonicIndex = -1; + } else { + String text = getTitle(); + int textLength = (text == null) ? 0 : text.length(); + if (index < -1 || index >= textLength) { // index out of range + throw new IllegalArgumentException("index == " + index); + } + } + mnemonicIndex = index; + firePropertyChange("displayedMnemonicIndex", oldValue, index); + if (index != oldValue) { + revalidate(); + repaint(); + } + } + + /** + * Adds an action to this JXTaskPane. Returns a + * component built from the action. The returned component has been + * added to the JXTaskPane. + * + * @param action + * @return a component built from the action + */ + public Component add(Action action) { + Component c = ((TaskPaneUI)ui).createAction(action); + add(c); + return c; + } + + /** + * @see JXCollapsiblePane.CollapsiblePaneContainer + */ + @Override +public Container getValidatingContainer() { + return getParent(); + } + + /** + * Overridden to redirect call to the content pane. + */ + @Override + protected void addImpl(Component comp, Object constraints, int index) { + getContentPane().add(comp, constraints, index); + //Fixes SwingX #364; adding to internal component we need to revalidate ourself + revalidate(); + } + + /** + * Overridden to redirect call to the content pane. + */ + @Override + public void setLayout(LayoutManager mgr) { + if (collapsePane != null) { + getContentPane().setLayout(mgr); + } + } + + /** + * Overridden to redirect call to the content pane + */ + @Override + public void remove(Component comp) { + getContentPane().remove(comp); + } + + /** + * Overridden to redirect call to the content pane. + */ + @Override + public void remove(int index) { + getContentPane().remove(index); + } + + /** + * Overridden to redirect call to the content pane. + */ + @Override + public void removeAll() { + getContentPane().removeAll(); + } + + /** + * @see JComponent#paramString() + */ + @Override + protected String paramString() { + return super.paramString() + + ",title=" + + getTitle() + + ",icon=" + + getIcon() + + ",collapsed=" + + String.valueOf(isCollapsed()) + + ",special=" + + String.valueOf(isSpecial()) + + ",scrollOnExpand=" + + String.valueOf(isScrollOnExpand()) + + ",ui=" + getUI(); + } + +} diff --git a/src/main/java/org/jdesktop/swingx/JXTaskPaneContainer.java b/src/main/java/org/jdesktop/swingx/JXTaskPaneContainer.java new file mode 100644 index 0000000000..2f9b017b07 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXTaskPaneContainer.java @@ -0,0 +1,169 @@ +/* + * $Id: JXTaskPaneContainer.java 4226 2012-08-07 16:09:19Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.plaf.LookAndFeelAddons; +import org.jdesktop.swingx.plaf.TaskPaneContainerAddon; +import org.jdesktop.swingx.plaf.TaskPaneContainerUI; + +import java.awt.event.ContainerAdapter; +import java.awt.event.ContainerEvent; + +/** + * JXTaskPaneContainer provides an elegant view + * to display a list of tasks ordered by groups ({@link JXTaskPane}s). + * + *

+ * Although {@link JXTaskPane} can be added to any other + * container, the JXTaskPaneContainer will provide better + * fidelity when it comes to matching the look and feel of the host operating + * system than any other panel. As example, when using on a Windows platform, + * the JXTaskPaneContainer will be painted with a light gradient + * background. Also JXTaskPaneContainer takes care of using the + * right {@link java.awt.LayoutManager} (as required by + * {@link JXCollapsiblePane}) so that + * {@link JXTaskPane} behaves correctly when collapsing and + * expanding its content. + * + *

+ * JXTaskPaneContainer can be added to a JScrollPane. + * + *

+ * Example: + *

+ * 
+ * JXFrame frame = new JXFrame();
+ * 
+ * // a container to put all JXTaskPane together
+ * JXTaskPaneContainer taskPaneContainer = new JXTaskPaneContainer();
+ * 
+ * // add JXTaskPanes to the container
+ * JXTaskPane actionPane = createActionPane();
+ * JXTaskPane miscActionPane = createMiscActionPane();
+ * JXTaskPane detailsPane = createDetailsPane();
+ * taskPaneContainer.add(actionPane);
+ * taskPaneContainer.add(miscActionPane);
+ * taskPaneContainer.add(detailsPane);
+ *
+ * // put the action list on the left in a JScrollPane
+ * // as we have several taskPane and we want to make sure they
+ * // all get visible.   
+ * frame.add(new JScrollPane(taskPaneContainer), BorderLayout.EAST);
+ * 
+ * // and a file browser in the middle
+ * frame.add(fileBrowser, BorderLayout.CENTER);
+ * 
+ * frame.pack().
+ * frame.setVisible(true);
+ * 
+ * 
+ * + * @author Frederic Lavigne + * + * @javabean.attribute + * name="isContainer" + * value="Boolean.TRUE" + * rtexpr="true" + * + * @javabean.class + * name="JXTaskPaneContainer" + * shortDescription="A component that contains JTaskPaneGroups." + * stopClass="java.awt.Component" + * + * @javabean.icons + * mono16="JXTaskPaneContainer16-mono.gif" + * color16="JXTaskPaneContainer16.gif" + * mono32="JXTaskPaneContainer32-mono.gif" + * color32="JXTaskPaneContainer32.gif" + */ +@JavaBean +public class JXTaskPaneContainer extends JXPanel { + + public final static String uiClassID = "swingx/TaskPaneContainerUI"; + + // ensure at least the default ui is registered + static { + LookAndFeelAddons.contribute(new TaskPaneContainerAddon()); + } + + /** + * Creates a new empty task pane. + */ + public JXTaskPaneContainer() { + super(null); + updateUI(); + + addContainerListener(new ContainerAdapter() { + @Override + public void componentRemoved(ContainerEvent e) { + repaint(); + } + }); + setScrollableHeightHint(ScrollableSizeHint.PREFERRED_STRETCH); + } + + /** + * {@inheritDoc} + */ + @Override + public TaskPaneContainerUI getUI() { + return (TaskPaneContainerUI) super.getUI(); + } + + /** + * Notification from the UIManager that the L&F has changed. + * Replaces the current UI object with the latest version from the + * UIManager. + * + * @see javax.swing.JComponent#updateUI + */ + @Override + public void updateUI() { + setUI((TaskPaneContainerUI) LookAndFeelAddons.getUI(this, + TaskPaneContainerUI.class)); + } + + /** + * Sets the L&F object that renders this component. + * + * @param ui the TaskPaneContainerUI L&F object + * @see javax.swing.UIDefaults#getUI + * + * @beaninfo bound: true hidden: true description: The UI object that + * implements the taskpane's LookAndFeel. + */ + public void setUI(TaskPaneContainerUI ui) { + super.setUI(ui); + } + + /** + * Returns the name of the L&F class that renders this component. + * + * @return the string {@link #uiClassID} + * @see javax.swing.JComponent#getUIClassID + * @see javax.swing.UIDefaults#getUI + */ + @Override + public String getUIClassID() { + return uiClassID; + } +} diff --git a/src/main/java/org/jdesktop/swingx/JXTextArea.java b/src/main/java/org/jdesktop/swingx/JXTextArea.java new file mode 100644 index 0000000000..ec697b9749 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXTextArea.java @@ -0,0 +1,108 @@ +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.prompt.BuddySupport; +import org.jdesktop.swingx.prompt.PromptSupport; +import org.jdesktop.swingx.prompt.PromptSupport.FocusBehavior; + +import javax.swing.*; +import java.awt.*; + +/** + * {@link JTextArea}, with integrated support for prompts. + * + * @see PromptSupport + * @see BuddySupport + * @author Peter Weishapl + * + */ +@JavaBean +public class JXTextArea extends JTextArea { + public JXTextArea() { + this(null); + } + + public JXTextArea(String promptText) { + this(promptText, null); + } + + public JXTextArea(String promptText, Color promptForeground) { + this(promptText, promptForeground, null); + } + + public JXTextArea(String promptText, Color promptForeground, + Color promptBackground) { + PromptSupport.init(promptText, promptForeground, promptBackground, + this); + } + + /** + * @see PromptSupport#getFocusBehavior(javax.swing.text.JTextComponent) + */ + public FocusBehavior getFocusBehavior() { + return PromptSupport.getFocusBehavior(this); + } + + /** + * @see PromptSupport#getPrompt(javax.swing.text.JTextComponent) + */ + public String getPrompt() { + return PromptSupport.getPrompt(this); + } + + /** + * @see PromptSupport#getForeground(javax.swing.text.JTextComponent) + */ + public Color getPromptForeground() { + return PromptSupport.getForeground(this); + } + + /** + * @see PromptSupport#getForeground(javax.swing.text.JTextComponent) + */ + public Color getPromptBackground() { + return PromptSupport.getBackground(this); + } + + /** + * @see PromptSupport#getFontStyle(javax.swing.text.JTextComponent) + */ + public Integer getPromptFontStyle() { + return PromptSupport.getFontStyle(this); + } + + /** + * @see PromptSupport#getFocusBehavior(javax.swing.text.JTextComponent) + */ + public void setFocusBehavior(FocusBehavior focusBehavior) { + PromptSupport.setFocusBehavior(focusBehavior, this); + } + + /** + * @see PromptSupport#setPrompt(String, javax.swing.text.JTextComponent) + */ + public void setPrompt(String labelText) { + PromptSupport.setPrompt(labelText, this); + } + + /** + * @see PromptSupport#setForeground(Color, javax.swing.text.JTextComponent) + */ + public void setPromptForeground(Color promptTextColor) { + PromptSupport.setForeground(promptTextColor, this); + } + + /** + * @see PromptSupport#setBackground(Color, javax.swing.text.JTextComponent) + */ + public void setPromptBackround(Color promptTextColor) { + PromptSupport.setBackground(promptTextColor, this); + } + + /** + * @see PromptSupport#setFontStyle(Integer, javax.swing.text.JTextComponent) + */ + public void setPromptFontStyle(Integer fontStyle) { + PromptSupport.setFontStyle(fontStyle, this); + } +} diff --git a/src/main/java/org/jdesktop/swingx/JXTextField.java b/src/main/java/org/jdesktop/swingx/JXTextField.java new file mode 100644 index 0000000000..09de52cc10 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXTextField.java @@ -0,0 +1,152 @@ +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.prompt.BuddySupport; +import org.jdesktop.swingx.prompt.BuddySupport.Position; +import org.jdesktop.swingx.prompt.PromptSupport; +import org.jdesktop.swingx.prompt.PromptSupport.FocusBehavior; + +import javax.swing.*; +import java.awt.*; +import java.util.List; + +/** + * {@link JTextField}, with integrated support for prompts and buddies. + * + * @see PromptSupport + * @see BuddySupport + * @author Peter Weishapl + * + */ +@JavaBean +public class JXTextField extends JTextField { + public JXTextField() { + this(null); + } + + public JXTextField(String promptText) { + this(promptText, null); + } + + public JXTextField(String promptText, Color promptForeground) { + this(promptText, promptForeground, null); + } + + public JXTextField(String promptText, Color promptForeground, + Color promptBackground) { + PromptSupport.init(promptText, promptForeground, promptBackground, + this); + } + + /** + * @see PromptSupport#getFocusBehavior(javax.swing.text.JTextComponent) + */ + public FocusBehavior getFocusBehavior() { + return PromptSupport.getFocusBehavior(this); + } + + /** + * @see PromptSupport#getPrompt(javax.swing.text.JTextComponent) + */ + public String getPrompt() { + return PromptSupport.getPrompt(this); + } + + /** + * @see PromptSupport#getForeground(javax.swing.text.JTextComponent) + */ + public Color getPromptForeground() { + return PromptSupport.getForeground(this); + } + + /** + * @see PromptSupport#getForeground(javax.swing.text.JTextComponent) + */ + public Color getPromptBackground() { + return PromptSupport.getBackground(this); + } + + /** + * @see PromptSupport#getFontStyle(javax.swing.text.JTextComponent) + */ + public Integer getPromptFontStyle() { + return PromptSupport.getFontStyle(this); + } + + /** + * @see PromptSupport#getFocusBehavior(javax.swing.text.JTextComponent) + */ + public void setFocusBehavior(FocusBehavior focusBehavior) { + PromptSupport.setFocusBehavior(focusBehavior, this); + } + + /** + * @see PromptSupport#setPrompt(String, javax.swing.text.JTextComponent) + */ + public void setPrompt(String labelText) { + PromptSupport.setPrompt(labelText, this); + } + + /** + * @see PromptSupport#setForeground(Color, javax.swing.text.JTextComponent) + */ + public void setPromptForeground(Color promptTextColor) { + PromptSupport.setForeground(promptTextColor, this); + } + + /** + * @see PromptSupport#setBackground(Color, javax.swing.text.JTextComponent) + */ + public void setPromptBackround(Color promptTextColor) { + PromptSupport.setBackground(promptTextColor, this); + } + + /** + * @see PromptSupport#setFontStyle(Integer, javax.swing.text.JTextComponent) + */ + public void setPromptFontStyle(Integer fontStyle) { + PromptSupport.setFontStyle(fontStyle, this); + } + + /** + * @see BuddySupport#setOuterMargin(JTextField, Insets) + */ + public void setOuterMargin(Insets margin) { + BuddySupport.setOuterMargin(this, margin); + } + + /** + * @see BuddySupport#getOuterMargin(JTextField) + */ + public Insets getOuterMargin() { + return BuddySupport.getOuterMargin(this); + } + + /** + * @see BuddySupport#add(Component, Position, JTextField) + */ + public void addBuddy(Component buddy, Position pos) { + BuddySupport.add(buddy, pos, this); + } + + /** + * @see BuddySupport#addGap(int, Position, JTextField) + */ + public void addGap(int width, Position pos) { + BuddySupport.addGap(width, pos, this); + } + + /** + * @see BuddySupport#getBuddies(Position, JTextField) + */ + public List getBuddies(Position pos) { + return BuddySupport.getBuddies(pos, this); + } + + /** + * @see BuddySupport#removeAll(JTextField) + */ + public void removeAllBuddies() { + BuddySupport.removeAll(this); + } +} diff --git a/src/main/java/org/jdesktop/swingx/JXTipOfTheDay.java b/src/main/java/org/jdesktop/swingx/JXTipOfTheDay.java new file mode 100644 index 0000000000..4d4ede31de --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXTipOfTheDay.java @@ -0,0 +1,460 @@ +/* + * $Id: JXTipOfTheDay.java 4147 2012-02-01 17:13:24Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.plaf.LookAndFeelAddons; +import org.jdesktop.swingx.plaf.TipOfTheDayAddon; +import org.jdesktop.swingx.plaf.TipOfTheDayUI; +import org.jdesktop.swingx.tips.DefaultTipOfTheDayModel; +import org.jdesktop.swingx.tips.TipOfTheDayModel; +import org.jdesktop.swingx.tips.TipOfTheDayModel.Tip; + +import javax.swing.*; +import java.awt.*; +import java.util.prefs.Preferences; + +/** + * Provides the "Tip of The Day" pane and dialog.
+ * + *

+ * Tips are retrieved from the {@link TipOfTheDayModel}. + * In the most common usage, a tip (as returned by + * {@link Tip#getTip()}) is just a + * String. However, the return type of this method is actually + * Object. Its interpretation depends on its type: + *

+ *
Component + *
The Component is displayed in the dialog. + *
Icon + *
The Icon is wrapped in a JLabel and + * displayed in the dialog. + *
others + *
The object is converted to a String by calling its + * toString method. The result is wrapped in a + * JEditorPane or JTextArea and displayed. + *
+ * + *

+ * JXTipOfTheDay finds its tips in its {@link TipOfTheDayModel}. + * Such model can be programmatically built using {@link DefaultTipOfTheDayModel} + * and {@link org.jdesktop.swingx.tips.DefaultTip} but + * the {@link org.jdesktop.swingx.tips.TipLoader} provides a convenient method to + * build a model and its tips from a {@link java.util.Properties} object. + * + *

+ * Example: + *

+ * Let's consider a file tips.properties with the following content: + *

+ * 
+ * tip.1.description=This is the first time! Plain text.
+ * tip.2.description=<html>This is <b>another tip</b>, it uses HTML!
+ * tip.3.description=A third one
+ * 
+ * 
+ * + * To load and display the tips: + * + *
+ * 
+ * Properties tips = new Properties();
+ * tips.load(new FileInputStream("tips.properties"));
+ * 
+ * TipOfTheDayModel model = TipLoader.load(tips);
+ * JXTipOfTheDay totd = new JXTipOfTheDay(model);
+ * 
+ * totd.showDialog(someParentComponent);
+ * 
+ * 
+ * + *

+ * Additionally, JXTipOfTheDay features an option enabling the end-user + * to choose to not display the "Tip Of The Day" dialog. This user choice can be stored + * in the user {@link Preferences} but JXTipOfTheDay also + * supports custom storage through the {@link ShowOnStartupChoice} interface. + * + *

+ * 
+ * Preferences userPreferences = Preferences.userRoot().node("myApp");
+ * totd.showDialog(someParentComponent, userPreferences);
+ * 
+ * 
+ * In this code, the first time showDialog is called, the dialog will be made + * visible and the user will have the choice to not display it again in the future + * (usually this is controlled by a checkbox "Show tips on startup"). If the user + * unchecks the option, subsequent calls to showDialog will not display the dialog. + * As the choice is saved in the user Preferences, it will persist when the application is relaunched. + * + * @see org.jdesktop.swingx.tips.TipLoader + * @see TipOfTheDayModel + * @see Tip + * @see #showDialog(Component, Preferences) + * @see #showDialog(Component, ShowOnStartupChoice) + * + * @author Frederic Lavigne + */ +@JavaBean +public class JXTipOfTheDay extends JXPanel { + + /** + * JXTipOfTheDay pluggable UI key swingx/TipOfTheDayUI + */ + public final static String uiClassID = "swingx/TipOfTheDayUI"; + + // ensure at least the default ui is registered + static { + LookAndFeelAddons.contribute(new TipOfTheDayAddon()); + } + + /** + * Key used to store the status of the "Show tip on startup" checkbox" + */ + public static final String PREFERENCE_KEY = "ShowTipOnStartup"; + + /** + * Used when generating PropertyChangeEvents for the "currentTip" property + */ + public static final String CURRENT_TIP_CHANGED_KEY = "currentTip"; + + private TipOfTheDayModel model; + private int currentTip = 0; + + /** + * Constructs a new JXTipOfTheDay with an empty + * TipOfTheDayModel + */ + public JXTipOfTheDay() { + this(new DefaultTipOfTheDayModel(new Tip[0])); + } + + /** + * Constructs a new JXTipOfTheDay showing tips from the given + * TipOfTheDayModel. + * + * @param model + */ + public JXTipOfTheDay(TipOfTheDayModel model) { + this.model = model; + updateUI(); + } + + /** + * Notification from the UIManager that the L&F has changed. + * Replaces the current UI object with the latest version from the + * UIManager. + * + * @see javax.swing.JComponent#updateUI + */ + @Override +public void updateUI() { + setUI((TipOfTheDayUI)LookAndFeelAddons.getUI(this, TipOfTheDayUI.class)); + } + + /** + * Sets the L&F object that renders this component. + * + * @param ui + * the TipOfTheDayUI L&F object + * @see javax.swing.UIDefaults#getUI + * + * @beaninfo bound: true hidden: true description: The UI object that + * implements the taskpane group's LookAndFeel. + */ + public void setUI(TipOfTheDayUI ui) { + super.setUI(ui); + } + + /** + * Gets the UI object which implements the L&F for this component. + * + * @return the TipOfTheDayUI object that implements the TipOfTheDayUI L&F + */ + @Override + public TipOfTheDayUI getUI() { + return (TipOfTheDayUI)ui; + } + + /** + * Returns the name of the L&F class that renders this component. + * + * @return the string {@link #uiClassID} + * @see javax.swing.JComponent#getUIClassID + * @see javax.swing.UIDefaults#getUI + */ + @Override + public String getUIClassID() { + return uiClassID; + } + + public TipOfTheDayModel getModel() { + return model; + } + + public void setModel(TipOfTheDayModel model) { + if (model == null) { + throw new IllegalArgumentException("model can not be null"); + } + TipOfTheDayModel old = this.model; + this.model = model; + firePropertyChange("model", old, model); + } + + public int getCurrentTip() { + return currentTip; + } + + /** + * Sets the index of the tip to show + * + * @param currentTip + * @throws IllegalArgumentException if currentTip is not within the bounds [0, + * getModel().getTipCount()[. + */ + public void setCurrentTip(int currentTip) { + if (currentTip < 0 || currentTip >= getModel().getTipCount()) { + throw new IllegalArgumentException( + "Current tip must be within the bounds [0, " + getModel().getTipCount() + + "]"); + } + + int oldTip = this.currentTip; + this.currentTip = currentTip; + firePropertyChange(CURRENT_TIP_CHANGED_KEY, oldTip, currentTip); + } + + /** + * Shows the next tip in the list. It cycles the tip list. + */ + public void nextTip() { + int count = getModel().getTipCount(); + if (count == 0) { return; } + + int nextTip = currentTip + 1; + if (nextTip >= count) { + nextTip = 0; + } + setCurrentTip(nextTip); + } + + /** + * Shows the previous tip in the list. It cycles the tip list. + */ + public void previousTip() { + int count = getModel().getTipCount(); + if (count == 0) { return; } + + int previousTip = currentTip - 1; + if (previousTip < 0) { + previousTip = count - 1; + } + setCurrentTip(previousTip); + } + + /** + * Pops up a "Tip of the day" dialog. + * + * @param parentComponent + * @exception HeadlessException + * if GraphicsEnvironment.isHeadless() returns true. + * @see java.awt.GraphicsEnvironment#isHeadless + */ + public void showDialog(Component parentComponent) throws HeadlessException { + showDialog(parentComponent, (ShowOnStartupChoice)null); + } + + /** + * Pops up a "Tip of the day" dialog. Additionally, it saves the state of the + * "Show tips on startup" checkbox in a key named "ShowTipOnStartup" in the + * given Preferences. + * + * @param parentComponent + * @param showOnStartupPref + * @exception HeadlessException + * if GraphicsEnvironment.isHeadless() returns true. + * @throws IllegalArgumentException + * if showOnStartupPref is null + * @see java.awt.GraphicsEnvironment#isHeadless + * @return true if the user chooses to see the tips again, false otherwise. + */ + public boolean showDialog(Component parentComponent, + Preferences showOnStartupPref) throws HeadlessException { + return showDialog(parentComponent, showOnStartupPref, false); + } + + /** + * Pops up a "Tip of the day" dialog. Additionally, it saves the state of the + * "Show tips on startup" checkbox in a key named "ShowTipOnStartup" in the + * given Preferences. + * + * @param parentComponent + * @param showOnStartupPref + * @param force + * if true, the dialog is displayed even if the Preferences is set to + * hide the dialog + * @exception HeadlessException + * if GraphicsEnvironment.isHeadless() returns true. + * @throws IllegalArgumentException + * if showOnStartupPref is null + * @see java.awt.GraphicsEnvironment#isHeadless + * @return true if the user chooses to see the tips again, false + * otherwise. + */ + public boolean showDialog(Component parentComponent, + final Preferences showOnStartupPref, boolean force) throws HeadlessException { + if (showOnStartupPref == null) { throw new IllegalArgumentException( + "Preferences can not be null"); } + + ShowOnStartupChoice store = new ShowOnStartupChoice() { + @Override + public boolean isShowingOnStartup() { + return showOnStartupPref.getBoolean(PREFERENCE_KEY, true); + } + @Override + public void setShowingOnStartup(boolean showOnStartup) { + if (showOnStartup && !showOnStartupPref.getBoolean(PREFERENCE_KEY, true)) { + // if the choice was previously not enable and now we re-enable it, we + // must remove the key + showOnStartupPref.remove(PREFERENCE_KEY); + } else if (!showOnStartup) { + // user does not want to see the tip + showOnStartupPref.putBoolean(PREFERENCE_KEY, showOnStartup); + } + } + }; + return showDialog(parentComponent, store, force); + } + + /** + * Pops up a "Tip of the day" dialog. + * + * If choice is not null, the method first checks if + * {@link ShowOnStartupChoice#isShowingOnStartup()} is true before showing the + * dialog. + * + * Additionally, it saves the state of the "Show tips on startup" checkbox + * using the given {@link ShowOnStartupChoice} object. + * + * @param parentComponent + * @param choice + * @exception HeadlessException + * if GraphicsEnvironment.isHeadless() returns true. + * @see java.awt.GraphicsEnvironment#isHeadless + * @return true if the user chooses to see the tips again, false otherwise. + */ + public boolean showDialog(Component parentComponent, + ShowOnStartupChoice choice) { + return showDialog(parentComponent, choice, false); + } + + /** + * Pops up a "Tip of the day" dialog. + * + * If choice is not null, the method first checks if + * force is true or if + * {@link ShowOnStartupChoice#isShowingOnStartup()} is true before showing the + * dialog. + * + * Additionally, it saves the state of the "Show tips on startup" checkbox + * using the given {@link ShowOnStartupChoice} object. + * + * @param parentComponent + * @param choice + * @param force + * if true, the dialog is displayed even if + * {@link ShowOnStartupChoice#isShowingOnStartup()} is false + * @exception HeadlessException + * if GraphicsEnvironment.isHeadless() returns true. + * @see java.awt.GraphicsEnvironment#isHeadless + * @return true if the user chooses to see the tips again, false otherwise. + */ + public boolean showDialog(Component parentComponent, + ShowOnStartupChoice choice, boolean force) { + if (choice == null) { + JDialog dialog = createDialog(parentComponent, choice); + dialog.setVisible(true); + dialog.dispose(); + return true; + } else if (force || choice.isShowingOnStartup()) { + JDialog dialog = createDialog(parentComponent, choice); + dialog.setVisible(true); + dialog.dispose(); + return choice.isShowingOnStartup(); + } else { + return false; + } + } + + /** + * @param showOnStartupPref + * @return true if the key named "ShowTipOnStartup" is not set to false + */ + public static boolean isShowingOnStartup(Preferences showOnStartupPref) { + return showOnStartupPref.getBoolean(PREFERENCE_KEY, true); + } + + /** + * Removes the value set for "ShowTipOnStartup" in the given Preferences to + * ensure the dialog shown by a later call to + * {@link #showDialog(Component, Preferences)} will be visible to the user. + * + * @param showOnStartupPref + */ + public static void forceShowOnStartup(Preferences showOnStartupPref) { + showOnStartupPref.remove(PREFERENCE_KEY); + } + + /** + * Calls + * {@link TipOfTheDayUI#createDialog(Component, ShowOnStartupChoice)}. + * + * This method can be overriden in order to control things such as the + * placement of the dialog or its title. + * + * @param parentComponent + * @param choice + * @return a JDialog to show this TipOfTheDay pane + */ + protected JDialog createDialog(Component parentComponent, + ShowOnStartupChoice choice) { + return getUI().createDialog(parentComponent, choice); + } + + /** + * Used in conjunction with the + * {@link JXTipOfTheDay#showDialog(Component, ShowOnStartupChoice)} to save the + * "Show tips on startup" choice. + */ + public static interface ShowOnStartupChoice { + + /** + * Persists the user choice + * @param showOnStartup the user choice + */ + void setShowingOnStartup(boolean showOnStartup); + + /** + * @return the previously stored user choice + */ + boolean isShowingOnStartup(); + } + +} diff --git a/src/main/java/org/jdesktop/swingx/JXTitledPanel.java b/src/main/java/org/jdesktop/swingx/JXTitledPanel.java new file mode 100644 index 0000000000..05f850c62f --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXTitledPanel.java @@ -0,0 +1,301 @@ +/* + * $Id: JXTitledPanel.java 4260 2012-11-14 20:59:08Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.painter.Painter; +import org.jdesktop.swingx.plaf.LookAndFeelAddons; +import org.jdesktop.swingx.plaf.TitledPanelAddon; +import org.jdesktop.swingx.plaf.TitledPanelUI; + +import javax.swing.*; +import java.awt.*; + +/** + * A special type of Panel that has a Title section and a Content section.
+ * The following properties can be set with the UIManager to change the look + * and feel of the JXTitledPanel: + *
    + *
  • JXTitledPanel.titleForeground
  • + *
  • JXTitledPanel.titleBackground
  • + *
  • JXTitledPanel.titleFont
  • + *
  • JXTitledPanel.titlePainter
  • + *
  • JXTitledPanel.captionInsets
  • + *
  • JXTitledPanel.rightDecorationInsets
  • + *
  • JXTitledPanel.leftDecorationInsets
  • + *
+ * + * @author Richard Bair + * @author Nicola Ken Barozzi + * @author Jeanette Winzenburg + */ +@JavaBean +public class JXTitledPanel extends JXPanel { + + /** + * @see #getUIClassID // * + * @see #readObject + */ + static public final String uiClassID = "TitledPanelUI"; + + public static final String LEFT_DECORATION = "JXTitledPanel.leftDecoration"; + + public static final String RIGHT_DECORATION = "JXTitledPanel.rightDecoration"; + + /** + * Initialization that would ideally be moved into various look and feel + * classes. + */ + static { + LookAndFeelAddons.contribute(new TitledPanelAddon()); + } + + /** + * The text to use for the title + */ + private String title; + + /** + * The Font to use for the Title + */ + private Font titleFont; + + /** + * The foreground color to use for the Title (particularly for the text) + */ + private Color titleForeground; + + /** + * The ContentPanel. Whatever this container is will be displayed in the + * Content section + */ + private Container contentPanel; + + /** + * The Painter to use for painting the title section of the JXTitledPanel + */ + private Painter titlePainter; + + /** + * Create a new JTitledPanel with an empty string for the title. + */ + public JXTitledPanel() { + this(null); + } + + /** + * Create a new JTitledPanel with the given title as the title for the + * panel. + * + * @param title + */ + public JXTitledPanel(String title) { + this(title, createDefaultContainer()); + } + + /** + * Create a new JTitledPanel with the given String as the title, and the + * given Container as the content panel. + * + * @param title + * @param content + */ + public JXTitledPanel(String title, Container content) { + setTitle(title); + setContentContainer(content); + } + + /** + * Returns the look and feel (L&F) object that renders this component. + * + * @return the TitledPanelUI object that renders this component + */ + @Override + public TitledPanelUI getUI() { + return (TitledPanelUI) ui; + } + + /** + * Sets the look and feel (L&F) object that renders this component. + * + * @param ui + * the TitledPanelUI L&F object + * @see javax.swing.UIDefaults#getUI + * @beaninfo bound: true + * hidden: true attribute: visualUpdate true + * description: The UI object that implements the Component's LookAndFeel. + */ + public void setUI(TitledPanelUI ui) { + super.setUI(ui); + } + + /** + * Returns a string that specifies the name of the L&F class that renders + * this component. + * + * @return "TitledPanelUI" + * @see JComponent#getUIClassID + * @see javax.swing.UIDefaults#getUI + * @beaninfo expert: true + * description: A string that specifies the name of the L&F class. + */ + @Override + public String getUIClassID() { + return uiClassID; + } + + /** + * Notification from the UIManager that the L&F has changed. + * Replaces the current UI object with the latest version from the + * UIManager. + * + * @see JComponent#updateUI + */ + @Override + public void updateUI() { + setUI((TitledPanelUI) LookAndFeelAddons + .getUI(this, TitledPanelUI.class)); + } + + /** + * Gets the title for this titled panel. + * + * @return the currently displayed title + */ + public String getTitle() { + return title; + } + + /** + * Sets the title for this title panel. + * + * @param title + * the title to display + */ + public void setTitle(String title) { + String oldTitle = this.title; + this.title = (title == null ? "" : title); + // JW: fix swingx #9 - missing/incorrect notification + // let standard notification handle + // NOTE - "getting" the new property in the fire method is + // intentional: there's no way of missing any transformations + // on the parameter to set (like above: setting a + // value depending on whether the input is null). + firePropertyChange("title", oldTitle, getTitle()); + } + + public Container getContentContainer() { + if (contentPanel == null) { + contentPanel = new JXPanel(); + ((JXPanel) contentPanel).setBorder(BorderFactory + .createEmptyBorder()); + this.add(contentPanel, BorderLayout.CENTER); + } + return contentPanel; + } + + public void setContentContainer(Container contentPanel) { + if (this.contentPanel != null) { + remove(this.contentPanel); + } + add(contentPanel, BorderLayout.CENTER); + this.contentPanel = contentPanel; + } + + /** + * Adds the given JComponent as a decoration on the right of the title + * + * @param decoration + */ + public void setRightDecoration(JComponent decoration) { + JComponent old = getRightDecoration(); + getUI().setRightDecoration(decoration); + firePropertyChange("rightDecoration", old, getRightDecoration()); + } + + public JComponent getRightDecoration() { + return getUI().getRightDecoration(); + } + + /** + * Adds the given JComponent as a decoration on the left of the title + * + * @param decoration + */ + public void setLeftDecoration(JComponent decoration) { + JComponent old = getLeftDecoration(); + getUI().setLeftDecoration(decoration); + firePropertyChange("leftDecoration", old, getLeftDecoration()); + } + + public JComponent getLeftDecoration() { + return getUI().getLeftDecoration(); + } + + public Font getTitleFont() { + return titleFont; + } + + public void setTitleFont(Font titleFont) { + Font old = getTitleFont(); + this.titleFont = titleFont; + firePropertyChange("titleFont", old, getTitleFont()); + } + + /** + * Set the Painter to use for painting the title section of the JXTitledPanel. + * This value may be null, which will cause the current look and feel to paint + * an appropriate look + * + * @param p The Painter to use. May be null + */ + public void setTitlePainter(Painter p) { + Painter old = getTitlePainter(); + this.titlePainter = p; + firePropertyChange("titlePainter", old, getTitlePainter()); + } + + /** + * @return the Painter to use for painting the background of the title section + */ + public Painter getTitlePainter() { + return titlePainter; + } + + public Color getTitleForeground() { + return titleForeground; + } + + public void setTitleForeground(Color titleForeground) { + Color old = getTitleForeground(); + this.titleForeground = titleForeground; + firePropertyChange("titleForeground", old, getTitleForeground()); + } + + private static Container createDefaultContainer() { + //TODO: All this default container creation stuff should be in the UI + //delegate. Not enough time at the moment for me to do this right. + JXPanel p = new JXPanel(); + return p; + } + +} diff --git a/src/main/java/org/jdesktop/swingx/JXTitledSeparator.java b/src/main/java/org/jdesktop/swingx/JXTitledSeparator.java new file mode 100644 index 0000000000..987c85522c --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXTitledSeparator.java @@ -0,0 +1,410 @@ +/* + * $Id: JXTitledSeparator.java 4147 2012-02-01 17:13:24Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; + +import javax.swing.*; +import javax.swing.plaf.ColorUIResource; +import javax.swing.plaf.FontUIResource; +import java.awt.*; + +/** + *

A simple horizontal separator that contains a title.
+ * + *

JXTitledSeparator allows you to specify the title via the {@link #setTitle} method. + * The title alignment may be specified by using the {@link #setHorizontalAlignment} + * method, and accepts all the same arguments as the {@link JLabel#setHorizontalAlignment} + * method.

+ * + *

In addition, you may specify an Icon to use with this separator. The icon + * will appear "leading" the title (on the left in left-to-right languages, + * on the right in right-to-left languages). To change the position of the + * title with respect to the icon, call {@link #setHorizontalTextPosition}.

+ * + *

The default font and color of the title comes from the LookAndFeel, mimicking + * the font and color of the {@link javax.swing.border.TitledBorder}

+ * + *

Here are a few example code snippets: + *


+ *  //create a plain separator
+ *  JXTitledSeparator sep = new JXTitledSeparator();
+ *  sep.setTitle("Customer Info");
+ *
+ *  //create a separator with an icon
+ *  sep = new JXTitledSeparator();
+ *  sep.setTitle("Customer Info");
+ *  sep.setIcon(new ImageIcon("myimage.png"));
+ *
+ *  //create a separator with an icon to the right of the title,
+ *  //center justified
+ *  sep = new JXTitledSeparator();
+ *  sep.setTitle("Customer Info");
+ *  sep.setIcon(new ImageIcon("myimage.png"));
+ *  sep.setHorizontalAlignment(SwingConstants.CENTER);
+ *  sep.setHorizontalTextPosition(SwingConstants.TRAILING);
+ * 
+ * + * @status REVIEWED + * @author rbair + */ +@JavaBean +public class JXTitledSeparator extends JXPanel { + /** + * Implementation detail: the label used to display the title + */ + private JLabel label; + /** + * Implementation detail: a separator to use on the left of the + * title if alignment is centered or right justified + */ + private JSeparator leftSeparator; + /** + * Implementation detail: a separator to use on the right of the + * title if alignment is centered or left justified + */ + private JSeparator rightSeparator; + + private int iconTextGap = 5; + + /** + * Creates a new instance of JXTitledSeparator. The default title is simply + * an empty string. Default justification is LEADING, and the default + * horizontal text position is TRAILING (title follows icon) + */ + public JXTitledSeparator() { + this("Untitled"); + } + + /** + * Creates a new instance of JXTitledSeparator with the specified + * title. Default horizontal alignment is LEADING, and the default + * horizontal text position is TRAILING (title follows icon) + */ + public JXTitledSeparator(String title) { + this(title, SwingConstants.LEADING, null); + } + + /** + * Creates a new instance of JXTitledSeparator with the specified + * title and horizontal alignment. The default + * horizontal text position is TRAILING (title follows icon) + */ + public JXTitledSeparator(String title, int horizontalAlignment) { + this(title, horizontalAlignment, null); + } + + /** + * Creates a new instance of JXTitledSeparator with the specified + * title, icon, and horizontal alignment. The default + * horizontal text position is TRAILING (title follows icon) + */ + public JXTitledSeparator(String title, int horizontalAlignment, Icon icon) { + setLayout(new GridBagLayout()); + + label = new JLabel(title) { + @Override + public void updateUI(){ + super.updateUI(); + updateTitle(); + } + }; + label.setIcon(icon); + label.setHorizontalAlignment(horizontalAlignment); + leftSeparator = new JSeparator(); + rightSeparator = new JSeparator(); + + layoutSeparator(); + + updateTitle(); + setOpaque(false); + } + + /** + * Implementation detail. Handles updates of title color and font on LAF change. For more + * details see swingx#451. + */ + //TODO remove this method in favor of UI delegate -- kgs + protected void updateTitle() + { + if (label == null) return; + + Color c = label.getForeground(); + if (c == null || c instanceof ColorUIResource) + setForeground(UIManager.getColor("TitledBorder.titleColor")); + + Font f = label.getFont(); + if (f == null || f instanceof FontUIResource) + setFont(UIManager.getFont("TitledBorder.font")); + } + + /** + * Implementation detail. lays out this component, showing/hiding components + * as necessary. Actually changes the containment (removes and adds components). + * JXTitledSeparator is treated as a single component rather than + * a container. + */ + private void layoutSeparator() { + removeAll(); + + //SwingX #304 fix alignment issues + //this is really a hacky fix, but a fix nonetheless + //we need a better layout approach for this class + int alignment = getHorizontalAlignment(); + + if (!getComponentOrientation().isLeftToRight()) { + switch (alignment) { + case SwingConstants.LEFT: + alignment = SwingConstants.RIGHT; + break; + case SwingConstants.RIGHT: + alignment = SwingConstants.LEFT; + break; + case SwingConstants.EAST: + alignment = SwingConstants.WEST; + break; + case SwingConstants.WEST: + alignment = SwingConstants.EAST; + break; + default: + break; + } + } + + switch (alignment) { + case SwingConstants.LEFT: + case SwingConstants.LEADING: + case SwingConstants.WEST: + add(label, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0,0,0,0), 0, 0)); + add(Box.createHorizontalStrut(iconTextGap), new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0,0,0,0), 0, 0)); + add(rightSeparator, new GridBagConstraints(2, 0, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0,0,0,0), 0, 0)); + break; + case SwingConstants.RIGHT: + case SwingConstants.TRAILING: + case SwingConstants.EAST: + add(rightSeparator, new GridBagConstraints(0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0,0,0,0), 0, 0)); + add(Box.createHorizontalStrut(iconTextGap), new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0,0,0,0), 0, 0)); + add(label, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0,0,0,0), 0, 0)); + break; + case SwingConstants.CENTER: + default: + add(leftSeparator, new GridBagConstraints(0, 0, 1, 1, 0.5, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0,0,0,0), 0, 0)); + add(Box.createHorizontalStrut(iconTextGap), new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0,0,0,0), 0, 0)); + add(label, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0,0,0,0), 0, 0)); + add(Box.createHorizontalStrut(iconTextGap), new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0,0,0,0), 0, 0)); + add(rightSeparator, new GridBagConstraints(4, 0, 1, 1, 0.5, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0,0,0,0), 0, 0)); + } + } + + /** + * Sets the title for the separator. This may be simple html, or plain + * text. + * + * @param title the new title. Any string input is acceptable + */ + public void setTitle(String title) { + String old = getTitle(); + label.setText(title); + firePropertyChange("title", old, getTitle()); + } + + /** + * Gets the title. + * + * @return the title being used for this JXTitledSeparator. + * This will be the raw title text, and so may include html tags etc + * if they were so specified in #setTitle. + */ + public String getTitle() { + return label.getText(); + } + + /** + *

Sets the alignment of the title along the X axis. If leading, then + * the title will lead the separator (in left-to-right languages, + * the title will be to the left and the separator to the right). If centered, + * then a separator will be to the left, followed by the title (centered), + * followed by a separator to the right. Trailing will have the title + * on the right with a separator to its left, in left-to-right languages.

+ * + *

LEFT and RIGHT always position the text left or right of the separator, + * respectively, regardless of the language orientation.

+ * + * @param alignment One of the following constants + * defined in SwingConstants: + * LEFT, + * CENTER, + * RIGHT, + * LEADING (the default) or + * TRAILING. + * + * @throws IllegalArgumentException if the alignment does not match one of + * the accepted inputs. + * @see SwingConstants + * @see #getHorizontalAlignment + */ + public void setHorizontalAlignment(int alignment) { + int old = getHorizontalAlignment(); + label.setHorizontalAlignment(alignment); + if (old != getHorizontalAlignment()) { + layoutSeparator(); + } + firePropertyChange("horizontalAlignment", old, getHorizontalAlignment()); + } + + /** + * Returns the alignment of the title contents along the X axis. + * + * @return The value of the horizontalAlignment property, one of the + * following constants defined in SwingConstants: + * LEFT, + * CENTER, + * RIGHT, + * LEADING or + * TRAILING. + * + * @see #setHorizontalAlignment + * @see SwingConstants + */ + public int getHorizontalAlignment() { + return label.getHorizontalAlignment(); + } + + /** + * Sets the horizontal position of the title's text, + * relative to the icon. + * + * @param position One of the following constants + * defined in SwingConstants: + * LEFT, + * CENTER, + * RIGHT, + * LEADING, or + * TRAILING (the default). + * @throws IllegalArgumentException if the position does not match one of + * the accepted inputs. + */ + public void setHorizontalTextPosition(int position) { + int old = getHorizontalTextPosition(); + label.setHorizontalTextPosition(position); + firePropertyChange("horizontalTextPosition", old, getHorizontalTextPosition()); + } + + /** + * Returns the horizontal position of the title's text, + * relative to the icon. + * + * @return One of the following constants + * defined in SwingConstants: + * LEFT, + * CENTER, + * RIGHT, + * LEADING or + * TRAILING. + * + * @see SwingConstants + */ + public int getHorizontalTextPosition() { + return label.getHorizontalTextPosition(); + } + + /** + * {@inheritDoc} + */ + @Override + public ComponentOrientation getComponentOrientation() { + return label.getComponentOrientation(); + } + + /** + * {@inheritDoc} + */ + @Override + public void setComponentOrientation(ComponentOrientation o) { + ComponentOrientation old = label.getComponentOrientation(); + label.setComponentOrientation(o); + firePropertyChange("componentOrientation", old, label.getComponentOrientation()); + } + + /** + * Defines the icon this component will display. If + * the value of icon is null, nothing is displayed. + *

+ * The default value of this property is null. + * + * @see #setHorizontalTextPosition + * @see #getIcon + */ + public void setIcon(Icon icon) { + Icon old = getIcon(); + label.setIcon(icon); + firePropertyChange("icon", old, getIcon()); + } + + /** + * Returns the graphic image (glyph, icon) that the + * JXTitledSeparator displays. + * + * @return an Icon + * @see #setIcon + */ + public Icon getIcon() { + return label.getIcon(); + } + + /** + * {@inheritDoc} + */ + @Override + public void setForeground(Color foreground) { + if (label != null) { + label.setForeground(foreground); + } + super.setForeground(foreground); + } + + /** + * {@inheritDoc} + */ + @Override + public void setFont(Font font) { + if (label != null) { + label.setFont(font); + } + super.setFont(font); + } + + public void setIconTextGap(int iconTextGap) { + int oldValue = this.iconTextGap; + this.iconTextGap = iconTextGap; + + if (iconTextGap != oldValue) { + layoutSeparator(); + revalidate(); + repaint(); + } + } + + public int getIconTextGap() { + return iconTextGap; + } +} diff --git a/src/main/java/org/jdesktop/swingx/JXTree.java b/src/main/java/org/jdesktop/swingx/JXTree.java new file mode 100644 index 0000000000..ee7a4c956e --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXTree.java @@ -0,0 +1,1651 @@ +/* + * $Id: JXTree.java 4166 2012-02-15 15:21:04Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.decorator.ComponentAdapter; +import org.jdesktop.swingx.decorator.CompoundHighlighter; +import org.jdesktop.swingx.decorator.Highlighter; +import org.jdesktop.swingx.plaf.UIAction; +import org.jdesktop.swingx.plaf.UIDependent; +import org.jdesktop.swingx.renderer.StringValue; +import org.jdesktop.swingx.renderer.StringValues; +import org.jdesktop.swingx.rollover.RolloverProducer; +import org.jdesktop.swingx.rollover.RolloverRenderer; +import org.jdesktop.swingx.rollover.TreeRolloverController; +import org.jdesktop.swingx.rollover.TreeRolloverProducer; +import org.jdesktop.swingx.search.SearchFactory; +import org.jdesktop.swingx.search.Searchable; +import org.jdesktop.swingx.search.TreeSearchable; +import org.jdesktop.swingx.tree.DefaultXTreeCellEditor; +import org.jdesktop.swingx.tree.DefaultXTreeCellRenderer; + +import javax.swing.*; +import javax.swing.event.*; +import javax.swing.plaf.basic.BasicTreeUI; +import javax.swing.text.Position.Bias; +import javax.swing.tree.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.Hashtable; +import java.util.Vector; +import java.util.logging.Logger; +import java.util.regex.Pattern; + + +/** + * Enhanced Tree component with support for SwingX rendering, highlighting, + * rollover and search functionality. + *

+ * + *

Rendering and Highlighting

+ * + * As all SwingX collection views, a JXTree is a HighlighterClient (PENDING JW: + * formally define and implement, like in AbstractTestHighlighter), that is it + * provides consistent api to add and remove Highlighters which can visually + * decorate the rendering component. + *

+ * + *


+ * 
+ * JXTree tree = new JXTree(new FileSystemModel());
+ * // use system file icons and name to render
+ * tree.setCellRenderer(new DefaultTreeRenderer(IconValues.FILE_ICON, 
+ *      StringValues.FILE_NAME));
+ * // highlight condition: file modified after a date     
+ * HighlightPredicate predicate = new HighlightPredicate() {
+ *    public boolean isHighlighted(Component renderer,
+ *                     ComponentAdapter adapter) {
+ *       File file = getUserObject(adapter.getValue());
+ *       return file != null ? lastWeek < file.lastModified : false;
+ *    }
+ * };
+ * // highlight with foreground color 
+ * tree.addHighlighter(new ColorHighlighter(predicate, null, Color.RED);      
+ * 
+ * 
+ * + * Note: for full functionality, a DefaultTreeRenderer must be installed + * as TreeCellRenderer. This is not done by default, because there are + * unresolved issues when editing. PENDING JW: still? Check! + * + * Note: to support the highlighting this implementation wraps the + * TreeCellRenderer set by client code with a DelegatingRenderer which applies + * the Highlighter after delegating the default configuration to the wrappee. As + * a side-effect, getCellRenderer does return the wrapper instead of the custom + * renderer. To access the latter, client code must call getWrappedCellRenderer. + *

+ *

Rollover

+ * + * As all SwingX collection views, a JXTree supports per-cell rollover. If + * enabled, the component fires rollover events on enter/exit of a cell which by + * default is promoted to the renderer if it implements RolloverRenderer, that + * is simulates live behaviour. The rollover events can be used by client code + * as well, f.i. to decorate the rollover row using a Highlighter. + * + *

+ * 
+ * JXTree tree = new JXTree();
+ * tree.setRolloverEnabled(true);
+ * tree.setCellRenderer(new DefaultTreeRenderer());
+ * tree.addHighlighter(new ColorHighlighter(HighlightPredicate.ROLLOVER_ROW, 
+ *      null, Color.RED);      
+ * 
+ * 
+ * + * + *

Search

+ * + * As all SwingX collection views, a JXTree is searchable. A search action is + * registered in its ActionMap under the key "find". The default behaviour is to + * ask the SearchFactory to open a search component on this component. The + * default keybinding is retrieved from the SearchFactory, typically ctrl-f (or + * cmd-f for Mac). Client code can register custom actions and/or bindings as + * appropriate. + *

+ * + * JXTree provides api to vend a renderer-controlled String representation of + * cell content. This allows the Searchable and Highlighters to use WYSIWYM + * (What-You-See-Is-What-You-Match), that is pattern matching against the actual + * string as seen by the user. + * + *

Miscellaneous

+ * + *
    + *
  • Improved usability for editing: guarantees that the tree is the + * focusOwner if editing terminated by user gesture and guards against data + * corruption if focusLost while editing + *
  • Access methods for selection colors, for consistency with JXTable, + * JXList + *
  • Convenience methods and actions to expand, collapse all nodes + *
+ * + * @author Ramesh Gupta + * @author Jeanette Winzenburg + * + * @see org.jdesktop.swingx.renderer.DefaultTreeRenderer + * @see org.jdesktop.swingx.renderer.ComponentProvider + * @see Highlighter + * @see org.jdesktop.swingx.decorator.HighlightPredicate + * @see SearchFactory + * @see Searchable + * + */ +@JavaBean +public class JXTree extends JTree { + @SuppressWarnings("unused") + private static final Logger LOG = Logger.getLogger(JXTree.class.getName()); + + + /** Empty int array used in getSelectedRows(). */ + private static final int[] EMPTY_INT_ARRAY = new int[0]; + /** Empty TreePath used in getSelectedPath() if selection empty. */ + private static final TreePath[] EMPTY_TREEPATH_ARRAY = new TreePath[0]; + + /** Collection of active Highlighters. */ + protected CompoundHighlighter compoundHighlighter; + /** Listener to changes of Highlighters in collection. */ + private ChangeListener highlighterChangeListener; + + /** Wrapper around the installed renderer, needed to support Highlighters. */ + private DelegatingRenderer delegatingRenderer; + + /** + * The RolloverProducer used if rollover is enabled. + */ + private RolloverProducer rolloverProducer; + + /** + * The RolloverController used if rollover is enabled. + */ + private TreeRolloverController linkController; + + private boolean overwriteIcons; + private Searchable searchable; + + // hacks around core focus issues around editing. + /** + * The propertyChangeListener responsible for terminating + * edits if focus lost. + */ + private CellEditorRemover editorRemover; + /** + * The CellEditorListener responsible to force the + * focus back to the tree after terminating edits. + */ + private CellEditorListener editorListener; + + /** Color of selected foreground. Added for consistent api across collection components. */ + private Color selectionForeground; + /** Color of selected background. Added for consistent api across collection components. */ + private Color selectionBackground; + + + + /** + * Constructs a JXTree with a sample model. The default model + * used by this tree defines a leaf node as any node without children. + */ + public JXTree() { + init(); + } + + /** + * Constructs a JXTree with each element of the specified array + * as the child of a new root node which is not displayed. By default, this + * tree defines a leaf node as any node without children. + * + * This version of the constructor simply invokes the super class version + * with the same arguments. + * + * @param value an array of objects that are children of the root. + */ + public JXTree(Object[] value) { + super(value); + init(); + } + + /** + * Constructs a JXTree with each element of the specified + * Vector as the child of a new root node which is not displayed. + * By default, this tree defines a leaf node as any node without children. + * + * This version of the constructor simply invokes the super class version + * with the same arguments. + * + * @param value an Vector of objects that are children of the root. + */ + public JXTree(Vector value) { + super(value); + init(); + } + + /** + * Constructs a JXTree created from a Hashtable which does not + * display with root. Each value-half of the key/value pairs in the HashTable + * becomes a child of the new root node. By default, the tree defines a leaf + * node as any node without children. + * + * This version of the constructor simply invokes the super class version + * with the same arguments. + * + * @param value a Hashtable containing objects that are children of the root. + */ + public JXTree(Hashtable value) { + super(value); + init(); + } + + /** + * Constructs a JXTree with the specified TreeNode as its root, + * which displays the root node. By default, the tree defines a leaf node as + * any node without children. + * + * This version of the constructor simply invokes the super class version + * with the same arguments. + * + * @param root root node of this tree + */ + public JXTree(TreeNode root) { + super(root, false); + init(); + } + + /** + * Constructs a JXTree with the specified TreeNode as its root, + * which displays the root node and which decides whether a node is a leaf + * node in the specified manner. + * + * This version of the constructor simply invokes the super class version + * with the same arguments. + * + * @param root root node of this tree + * @param asksAllowsChildren if true, only nodes that do not allow children + * are leaf nodes; otherwise, any node without children is a leaf node; + * @see javax.swing.tree.DefaultTreeModel#asksAllowsChildren + */ + public JXTree(TreeNode root, boolean asksAllowsChildren) { + super(root, asksAllowsChildren); + init(); + } + + /** + * Constructs an instance of JXTree which displays the root + * node -- the tree is created using the specified data model. + * + * This version of the constructor simply invokes the super class version + * with the same arguments. + * + * @param newModel + * the TreeModel to use as the data model + */ + public JXTree(TreeModel newModel) { + super(newModel); + init(); + } + + /** + * Instantiates JXTree state which is new compared to super. Installs the + * Delegating renderer and editor, registers actions and keybindings. + * + * This must be called from each constructor. + */ + private void init() { + // Issue #1061-swingx: renderer inconsistencies + // force setting of renderer + setCellRenderer(createDefaultCellRenderer()); + // Issue #233-swingx: default editor not bidi-compliant + // manually install an enhanced TreeCellEditor which + // behaves slightly better in RtoL orientation. + // Issue #231-swingx: icons lost + // Anyway, need to install the editor manually because + // the default install in BasicTreeUI doesn't know about + // the DelegatingRenderer and therefore can't see + // the DefaultTreeCellRenderer type to delegate to. + // As a consequence, the icons are lost in the default + // setup. + // JW PENDING need to mimic ui-delegate default re-set? + // JW PENDING alternatively, cleanup and use DefaultXXTreeCellEditor in incubator + if (getWrappedCellRenderer() instanceof DefaultTreeCellRenderer) { + setCellEditor(new DefaultXTreeCellEditor(this, (DefaultTreeCellRenderer) getWrappedCellRenderer())); + } + // Register the actions that this class can handle. + ActionMap map = getActionMap(); + map.put("expand-all", new Actions("expand-all")); + map.put("collapse-all", new Actions("collapse-all")); + map.put("find", createFindAction()); + + KeyStroke findStroke = SearchFactory.getInstance().getSearchAccelerator(); + getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(findStroke, "find"); + } + + /** + * Listens to the model and updates the {@code expandedState} accordingly + * when nodes are removed, or changed. + *

+ * This class will expand an invisible root when a child has been added to + * it. + * + * @author Karl George Schaefer + */ + protected class XTreeModelHandler extends TreeModelHandler { + /** + * {@inheritDoc} + */ + @Override + public void treeNodesInserted(TreeModelEvent e) { + TreePath path = e.getTreePath(); + + //fixes SwingX bug #612 + if (path.getParentPath() == null && !isRootVisible() && isCollapsed(path)) { + //should this be wrapped in SwingUtilities.invokeLater? + expandPath(path); + } + + super.treeNodesInserted(e); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected TreeModelListener createTreeModelListener() { + return new XTreeModelHandler(); + } + + /** + * A small class which dispatches actions. + * TODO: Is there a way that we can make this static? + */ + private class Actions extends UIAction { + Actions(String name) { + super(name); + } + + @Override + public void actionPerformed(ActionEvent evt) { + if ("expand-all".equals(getName())) { + expandAll(); + } + else if ("collapse-all".equals(getName())) { + collapseAll(); + } + } + } + + +//-------------------- search support + + /** + * Creates and returns the action to invoke on a find request. + * + * @return the action to invoke on a find request. + */ + private Action createFindAction() { + return new UIAction("find") { + @Override + public void actionPerformed(ActionEvent e) { + doFind(); + } + }; + } + + /** + * Starts a search on this Tree's visible nodes. This implementation asks the + * SearchFactory to open a find widget on itself. + */ + protected void doFind() { + SearchFactory.getInstance().showFindInput(this, getSearchable()); + } + + /** + * Returns a Searchable for this component, guaranteed to be not null. This + * implementation lazily creates a TreeSearchable if necessary. + * + * + * @return a not-null Searchable for this component. + * + * @see #setSearchable(Searchable) + * @see TreeSearchable + */ + public Searchable getSearchable() { + if (searchable == null) { + searchable = new TreeSearchable(this); + } + return searchable; + } + + /** + * Sets the Searchable for this component. If null, a default + * Searchable will be created and used. + * + * @param searchable the Searchable to use for this component, may be null to + * indicate using the default. + * + * @see #getSearchable() + */ + public void setSearchable(Searchable searchable) { + this.searchable = searchable; + } + + /** + * Returns the string representation of the cell value at the given position. + * + * @param row the row index of the cell in view coordinates + * @return the string representation of the cell value as it will appear in the + * table. + */ + public String getStringAt(int row) { + return getStringAt(getPathForRow(row)); + } + + /** + * Returns the string representation of the cell value at the given position. + * + * @param path the TreePath representing the node. + * @return the string representation of the cell value as it will appear in the + * table, or null if the path is not visible. + */ + public String getStringAt(TreePath path) { + if (path == null) return null; + TreeCellRenderer renderer = getDelegatingRenderer().getDelegateRenderer(); + if (renderer instanceof StringValue) { + return ((StringValue) renderer).getString(path.getLastPathComponent()); + } + return StringValues.TO_STRING.getString(path.getLastPathComponent()); + } + + + /** + * Overridden to respect the string representation, if any. This takes over + * completely (as compared to super), internally messaging the Searchable. + *

+ * + * PENDING JW: re-visit once we support deep node search. + * + */ + @Override + public TreePath getNextMatch(String prefix, int startingRow, Bias bias) { + Pattern pattern = Pattern.compile("^" + prefix, Pattern.CASE_INSENSITIVE); + int row = getSearchable().search(pattern, startingRow, bias ==Bias.Backward); + return getPathForRow(row); + } + +//--------------------- misc. new api and super overrides + /** + * Collapses all nodes in this tree. + */ + public void collapseAll() { + for (int i = getRowCount() - 1; i >= 0 ; i--) { + collapseRow(i); + } + } + + /** + * Expands all nodes in this tree.

+ * + * Note: it's not recommended to use this method on the EDT for large/deep trees + * because expansion can take a considerable amount of time. + */ + public void expandAll() { + if (getRowCount() == 0) { + expandRoot(); + } + for (int i = 0; i < getRowCount(); i++) { + expandRow(i); + } + } + + /** + * Expands the root path if a TreeModel has been set, does nothing if not. + * + */ + private void expandRoot() { + TreeModel model = getModel(); + if (model != null && model.getRoot() != null) { + expandPath(new TreePath(model.getRoot())); + } + } + + /** + * {@inheritDoc} + *

+ * + * Overridden to always return a not-null array (following SwingX + * convention). + */ + @Override + public int[] getSelectionRows() { + int[] rows = super.getSelectionRows(); + return rows != null ? rows : EMPTY_INT_ARRAY; + } + + /** + * {@inheritDoc} + *

+ * + * Overridden to always return a not-null array (following SwingX + * convention). + */ + @Override + public TreePath[] getSelectionPaths() { + TreePath[] paths = super.getSelectionPaths(); + return paths != null ? paths : EMPTY_TREEPATH_ARRAY; + } + + /** + * Returns the background color for selected cells. + * + * @return the Color used for the background of + * selected list items + * @see #setSelectionBackground + * @see #setSelectionForeground + */ + public Color getSelectionBackground() { + return selectionBackground; + } + + /** + * Returns the selection foreground color. + * + * @return the Color object for the foreground property + * @see #setSelectionForeground + * @see #setSelectionBackground + */ + public Color getSelectionForeground() { + return selectionForeground; + } + + /** + * Sets the foreground color for selected cells. Cell renderers + * can use this color to render text and graphics for selected + * cells. + *

+ * The default value of this property is defined by the look + * and feel implementation. + *

+ * This is a JavaBeans bound property. + * + * @param selectionForeground the Color to use in the foreground + * for selected list items + * @see #getSelectionForeground + * @see #setSelectionBackground + * @see #setForeground + * @see #setBackground + * @see #setFont + * @beaninfo + * bound: true + * attribute: visualUpdate true + * description: The foreground color of selected cells. + */ + public void setSelectionForeground(Color selectionForeground) { + Object oldValue = getSelectionForeground(); + this.selectionForeground = selectionForeground; + firePropertyChange("selectionForeground", oldValue, getSelectionForeground()); + repaint(); + } + + /** + * Sets the background color for selected cells. Cell renderers + * can use this color to the fill selected cells. + *

+ * The default value of this property is defined by the look + * and feel implementation. + *

+ * This is a JavaBeans bound property. + * + * @param selectionBackground the Color to use for the + * background of selected cells + * @see #getSelectionBackground + * @see #setSelectionForeground + * @see #setForeground + * @see #setBackground + * @see #setFont + * @beaninfo + * bound: true + * attribute: visualUpdate true + * description: The background color of selected cells. + */ + public void setSelectionBackground(Color selectionBackground) { + Object oldValue = getSelectionBackground(); + this.selectionBackground = selectionBackground; + firePropertyChange("selectionBackground", oldValue, getSelectionBackground()); + repaint(); + } + + +//------------------------- update ui + + /** + * {@inheritDoc}

+ * + * Overridden to update selection background/foreground. Mimicking behaviour of + * ui-delegates for JTable, JList. + */ + @Override + public void updateUI() { + uninstallSelectionColors(); + super.updateUI(); + installSelectionColors(); + updateHighlighterUI(); + updateRendererEditorUI(); + invalidateCellSizeCache(); + } + + + /** + * Quick fix for #1060-swingx: icons lost on toggling LAF + */ + protected void updateRendererEditorUI() { + if (getCellEditor() instanceof UIDependent) { + ((UIDependent) getCellEditor()).updateUI(); + } + // PENDING JW: here we get the DelegationRenderer which is not (yet) UIDependent + // need to think about how to handle the per-tree icons + // anyway, the "real" renderer usually is updated accidentally + // don't know exactly why, added to the comp hierarchy? +// if (getCellRenderer() instanceof UIDependent) { +// ((UIDependent) getCellRenderer()).updateUI(); +// } + } + + /** + * Installs selection colors from UIManager.

+ * + * Note: this should be done in the UI delegate. + */ + private void installSelectionColors() { + if (SwingXUtilities.isUIInstallable(getSelectionBackground())) { + setSelectionBackground(UIManager.getColor("Tree.selectionBackground")); + } + if (SwingXUtilities.isUIInstallable(getSelectionForeground())) { + setSelectionForeground(UIManager.getColor("Tree.selectionForeground")); + } + + } + + /** + * Uninstalls selection colors.

+ * + * Note: this should be done in the UI delegate. + */ + private void uninstallSelectionColors() { + if (SwingXUtilities.isUIInstallable(getSelectionBackground())) { + setSelectionBackground(null); + } + if (SwingXUtilities.isUIInstallable(getSelectionForeground())) { + setSelectionForeground(null); + } + } + + /** + * Updates highlighter after updateUI changes. + * + * @see UIDependent + */ + protected void updateHighlighterUI() { + if (compoundHighlighter == null) return; + compoundHighlighter.updateUI(); + } + + + +//------------------------ Rollover support + + /** + * Sets the property to enable/disable rollover support. If enabled, the list + * fires property changes on per-cell mouse rollover state, i.e. + * when the mouse enters/leaves a list cell.

+ * + * This can be enabled to show "live" rollover behaviour, f.i. the cursor over a cell + * rendered by a JXHyperlink.

+ * + * The default value is false. + * + * @param rolloverEnabled a boolean indicating whether or not the rollover + * functionality should be enabled. + * + * @see #isRolloverEnabled() + * @see #getLinkController() + * @see #createRolloverProducer() + * @see RolloverRenderer + */ + public void setRolloverEnabled(boolean rolloverEnabled) { + boolean old = isRolloverEnabled(); + if (rolloverEnabled == old) return; + if (rolloverEnabled) { + rolloverProducer = createRolloverProducer(); + rolloverProducer.install(this); + getLinkController().install(this); + } else { + rolloverProducer.release(this); + rolloverProducer = null; + getLinkController().release(); + } + firePropertyChange("rolloverEnabled", old, isRolloverEnabled()); + } + + /** + * Returns a boolean indicating whether or not rollover support is enabled. + * + * @return a boolean indicating whether or not rollover support is enabled. + * + * @see #setRolloverEnabled(boolean) + */ + public boolean isRolloverEnabled() { + return rolloverProducer != null; + } + + /** + * Returns the RolloverController for this component. Lazyly creates the + * controller if necessary, that is the return value is guaranteed to be + * not null.

+ * + * PENDING JW: rename to getRolloverController + * + * @return the RolloverController for this tree, guaranteed to be not null. + * + * @see #setRolloverEnabled(boolean) + * @see #createLinkController() + * @see org.jdesktop.swingx.rollover.RolloverController + */ + protected TreeRolloverController getLinkController() { + if (linkController == null) { + linkController = createLinkController(); + } + return linkController; + } + + /** + * Creates and returns a RolloverController appropriate for this tree. + * + * @return a RolloverController appropriate for this tree. + * + * @see #getLinkController() + * @see org.jdesktop.swingx.rollover.RolloverController + */ + protected TreeRolloverController createLinkController() { + return new TreeRolloverController(); + } + + /** + * Creates and returns the RolloverProducer to use with this tree. + *

+ * + * @return RolloverProducer to use with this tree + * + * @see #setRolloverEnabled(boolean) + */ + protected RolloverProducer createRolloverProducer() { + return new TreeRolloverProducer(); + } + + +//----------------------- Highlighter api + + /** + * Sets the Highlighters to the table, replacing any old settings. + * None of the given Highlighters must be null.

+ * + * This is a bound property.

+ * + * Note: as of version #1.257 the null constraint is enforced strictly. To remove + * all highlighters use this method without param. + * + * @param highlighters zero or more not null highlighters to use for renderer decoration. + * @throws NullPointerException if array is null or array contains null values. + * + * @see #getHighlighters() + * @see #addHighlighter(Highlighter) + * @see #removeHighlighter(Highlighter) + * + */ + public void setHighlighters(Highlighter... highlighters) { + Highlighter[] old = getHighlighters(); + getCompoundHighlighter().setHighlighters(highlighters); + firePropertyChange("highlighters", old, getHighlighters()); + } + + /** + * Returns the Highlighters used by this table. + * Maybe empty, but guarantees to be never null. + * + * @return the Highlighters used by this table, guaranteed to never null. + * @see #setHighlighters(Highlighter[]) + */ + public Highlighter[] getHighlighters() { + return getCompoundHighlighter().getHighlighters(); + } + + /** + * Appends a Highlighter to the end of the list of used + * Highlighters. The argument must not be null. + *

+ * + * @param highlighter the Highlighter to add, must not be null. + * @throws NullPointerException if Highlighter is null. + * + * @see #removeHighlighter(Highlighter) + * @see #setHighlighters(Highlighter[]) + */ + public void addHighlighter(Highlighter highlighter) { + Highlighter[] old = getHighlighters(); + getCompoundHighlighter().addHighlighter(highlighter); + firePropertyChange("highlighters", old, getHighlighters()); + } + + /** + * Removes the given Highlighter.

+ * + * Does nothing if the Highlighter is not contained. + * + * @param highlighter the Highlighter to remove. + * @see #addHighlighter(Highlighter) + * @see #setHighlighters(Highlighter...) + */ + public void removeHighlighter(Highlighter highlighter) { + Highlighter[] old = getHighlighters(); + getCompoundHighlighter().removeHighlighter(highlighter); + firePropertyChange("highlighters", old, getHighlighters()); + } + + /** + * Returns the CompoundHighlighter assigned to the table, null if none. + * PENDING: open up for subclasses again?. + * + * @return the CompoundHighlighter assigned to the table. + */ + protected CompoundHighlighter getCompoundHighlighter() { + if (compoundHighlighter == null) { + compoundHighlighter = new CompoundHighlighter(); + compoundHighlighter.addChangeListener(getHighlighterChangeListener()); + } + return compoundHighlighter; + } + + /** + * Returns the ChangeListener to use with highlighters. Lazily + * creates the listener. + * + * @return the ChangeListener for observing changes of highlighters, + * guaranteed to be not-null + */ + protected ChangeListener getHighlighterChangeListener() { + if (highlighterChangeListener == null) { + highlighterChangeListener = createHighlighterChangeListener(); + } + return highlighterChangeListener; + } + + /** + * Creates and returns the ChangeListener observing Highlighters. + *

+ * Here: repaints the table on receiving a stateChanged. + * + * @return the ChangeListener defining the reaction to changes of + * highlighters. + */ + protected ChangeListener createHighlighterChangeListener() { + return new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + repaint(); + } + }; + } + + /** + * Sets the Icon to use for the handle of an expanded node.

+ * + * Note: this will only succeed if the current ui delegate is + * a BasicTreeUI otherwise it will do nothing.

+ * + * PENDING JW: incomplete api (no getter) and not a bound property. + * + * @param expandedIcon the Icon to use for the handle of an expanded node. + */ + public void setExpandedIcon(Icon expandedIcon) { + if (getUI() instanceof BasicTreeUI) { + ((BasicTreeUI) getUI()).setExpandedIcon(expandedIcon); + } + } + + /** + * Sets the Icon to use for the handle of a collapsed node. + * + * Note: this will only succeed if the current ui delegate is + * a BasicTreeUI otherwise it will do nothing. + * + * PENDING JW: incomplete api (no getter) and not a bound property. + * + * @param collapsedIcon the Icon to use for the handle of a collapsed node. + */ + public void setCollapsedIcon(Icon collapsedIcon) { + if (getUI() instanceof BasicTreeUI) { + ((BasicTreeUI) getUI()).setCollapsedIcon(collapsedIcon); + } + } + + /** + * Sets the Icon to use for a leaf node.

+ * + * Note: this will only succeed if current renderer is a + * DefaultTreeCellRenderer.

+ * + * PENDING JW: this (all setXXIcon) is old api pulled up from the JXTreeTable. + * Need to review if we really want it - problematic if sharing the same + * renderer instance across different trees. + * + * PENDING JW: incomplete api (no getter) and not a bound property.

+ * + * @param leafIcon the Icon to use for a leaf node. + */ + public void setLeafIcon(Icon leafIcon) { + getDelegatingRenderer().setLeafIcon(leafIcon); + } + + /** + * Sets the Icon to use for an open folder node. + * + * Note: this will only succeed if current renderer is a + * DefaultTreeCellRenderer. + * + * PENDING JW: incomplete api (no getter) and not a bound property. + * + * @param openIcon the Icon to use for an open folder node. + */ + public void setOpenIcon(Icon openIcon) { + getDelegatingRenderer().setOpenIcon(openIcon); + } + + /** + * Sets the Icon to use for a closed folder node. + * + * Note: this will only succeed if current renderer is a + * DefaultTreeCellRenderer. + * + * PENDING JW: incomplete api (no getter) and not a bound property. + * + * @param closedIcon the Icon to use for a closed folder node. + */ + public void setClosedIcon(Icon closedIcon) { + getDelegatingRenderer().setClosedIcon(closedIcon); + } + + /** + * Property to control whether per-tree icons should be + * copied to the renderer on setCellRenderer.

+ * + * The default value is false. + * + * PENDING: should update the current renderer's icons when + * setting to true? + * + * @param overwrite a boolean to indicate if the per-tree Icons should + * be copied to the new renderer on setCellRenderer. + * + * @see #isOverwriteRendererIcons() + * @see #setLeafIcon(Icon) + * @see #setOpenIcon(Icon) + * @see #setClosedIcon(Icon) + */ + public void setOverwriteRendererIcons(boolean overwrite) { + if (overwriteIcons == overwrite) return; + boolean old = overwriteIcons; + this.overwriteIcons = overwrite; + firePropertyChange("overwriteRendererIcons", old, overwrite); + } + + /** + * Returns a boolean indicating whether the per-tree icons should be + * copied to the renderer on setCellRenderer. + * + * @return true if a TreeCellRenderer's icons will be overwritten with the + * tree's Icons, false if the renderer's icons will be unchanged. + * + * @see #setOverwriteRendererIcons(boolean) + * @see #setLeafIcon(Icon) + * @see #setOpenIcon(Icon) + * @see #setClosedIcon(Icon) + * + */ + public boolean isOverwriteRendererIcons() { + return overwriteIcons; + } + + private DelegatingRenderer getDelegatingRenderer() { + if (delegatingRenderer == null) { + // only called once... to get hold of the default? + delegatingRenderer = new DelegatingRenderer(); + } + return delegatingRenderer; + } + + /** + * Creates and returns the default cell renderer to use. Subclasses may + * override to use a different type. + *

+ * + * This implementation returns a renderer of type + * DefaultTreeCellRenderer. Note: Will be changed to + * return a renderer of type DefaultTreeRenderer, + * once WrappingProvider is reasonably stable. + * + * @return the default cell renderer to use with this tree. + */ + protected TreeCellRenderer createDefaultCellRenderer() { +// return new DefaultTreeCellRenderer(); + return new DefaultXTreeCellRenderer(); + } + + /** + * {@inheritDoc}

+ * + * Overridden to return the delegating renderer which is wrapped around the + * original to support highlighting. The returned renderer is of type + * DelegatingRenderer and guaranteed to not-null

+ * + * @see #setCellRenderer(TreeCellRenderer) + * @see DelegatingRenderer + */ + @Override + public TreeCellRenderer getCellRenderer() { + // PENDING JW: something wrong here - why exactly can't we return super? + // not even if we force the initial setting in init? +// return super.getCellRenderer(); + return getDelegatingRenderer(); + } + + /** + * Returns the renderer installed by client code or the default if none has + * been set. + * + * @return the wrapped renderer. + * @see #setCellRenderer(TreeCellRenderer) + */ + public TreeCellRenderer getWrappedCellRenderer() { + return getDelegatingRenderer().getDelegateRenderer(); + } + + /** + * {@inheritDoc}

+ * + * Overridden to wrap the given renderer in a DelegatingRenderer to support + * highlighting.

+ * + * Note: the wrapping implies that the renderer returned from the getCellRenderer + * is not the renderer as given here, but the wrapper. To access the original, + * use getWrappedCellRenderer. + * + * @see #getWrappedCellRenderer() + * @see #getCellRenderer() + */ + @Override + public void setCellRenderer(TreeCellRenderer renderer) { + // PENDING: do something against recursive setting + // == multiple delegation... + getDelegatingRenderer().setDelegateRenderer(renderer); + super.setCellRenderer(delegatingRenderer); + // quick hack for #1061: renderer/editor inconsistent + if ((renderer instanceof DefaultTreeCellRenderer) && + (getCellEditor() instanceof DefaultXTreeCellEditor)) { + ((DefaultXTreeCellEditor) getCellEditor()).setRenderer((DefaultTreeCellRenderer) renderer); + } + firePropertyChange("cellRenderer", null, delegatingRenderer); + } + + + /** + * A decorator for the original TreeCellRenderer. Needed to hook highlighters + * after messaging the delegate.

+ * + * PENDING JW: formally implement UIDependent? + * PENDING JW: missing updateUI anyway (got lost when c&p from JXList ;-) + * PENDING JW: missing override of updateUI in xtree ... + */ + public class DelegatingRenderer implements TreeCellRenderer, RolloverRenderer { + private Icon closedIcon = null; + private Icon openIcon = null; + private Icon leafIcon = null; + + private TreeCellRenderer delegate; + + /** + * Instantiates a DelegatingRenderer with tree's default renderer as delegate. + */ + public DelegatingRenderer() { + this(null); + initIcons(new DefaultTreeCellRenderer()); + } + + /** + * Instantiates a DelegatingRenderer with the given delegate. If the + * delegate is null, the default is created via the list's factory method. + * + * @param delegate the delegate to use, if null the tree's default is + * created and used. + */ + public DelegatingRenderer(TreeCellRenderer delegate) { + initIcons((DefaultTreeCellRenderer) (delegate instanceof DefaultTreeCellRenderer ? + delegate : new DefaultTreeCellRenderer())); + setDelegateRenderer(delegate); + } + + /** + * initially sets the icons to the defaults as given + * by a DefaultTreeCellRenderer. + * + * @param renderer + */ + private void initIcons(DefaultTreeCellRenderer renderer) { + closedIcon = renderer.getDefaultClosedIcon(); + openIcon = renderer.getDefaultOpenIcon(); + leafIcon = renderer.getDefaultLeafIcon(); + } + + /** + * Sets the delegate. If the + * delegate is null, the default is created via the list's factory method. + * Updates the folder/leaf icons. + * + * THINK: how to update? always override with this.icons, only + * if renderer's icons are null, update this icons if they are not, + * update all if only one is != null.... ?? + * + * @param delegate the delegate to use, if null the list's default is + * created and used. + */ + public void setDelegateRenderer(TreeCellRenderer delegate) { + if (delegate == null) { + delegate = createDefaultCellRenderer(); + } + this.delegate = delegate; + updateIcons(); + } + + /** + * tries to set the renderers icons. Can succeed only if the + * delegate is a DefaultTreeCellRenderer. + * THINK: how to update? always override with this.icons, only + * if renderer's icons are null, update this icons if they are not, + * update all if only one is != null.... ?? + * + */ + private void updateIcons() { + if (!isOverwriteRendererIcons()) return; + setClosedIcon(closedIcon); + setOpenIcon(openIcon); + setLeafIcon(leafIcon); + } + + public void setClosedIcon(Icon closedIcon) { + if (delegate instanceof DefaultTreeCellRenderer) { + ((DefaultTreeCellRenderer) delegate).setClosedIcon(closedIcon); + } + this.closedIcon = closedIcon; + } + + public void setOpenIcon(Icon openIcon) { + if (delegate instanceof DefaultTreeCellRenderer) { + ((DefaultTreeCellRenderer) delegate).setOpenIcon(openIcon); + } + this.openIcon = openIcon; + } + + public void setLeafIcon(Icon leafIcon) { + if (delegate instanceof DefaultTreeCellRenderer) { + ((DefaultTreeCellRenderer) delegate).setLeafIcon(leafIcon); + } + this.leafIcon = leafIcon; + } + + //--------------- TreeCellRenderer + + /** + * Returns the delegate. + * + * @return the delegate renderer used by this renderer, guaranteed to + * not-null. + */ + public TreeCellRenderer getDelegateRenderer() { + return delegate; + } + + /** + * {@inheritDoc}

+ * + * Overridden to apply the highlighters, if any, after calling the delegate. + * The decorators are not applied if the row is invalid. + */ + @Override + public Component getTreeCellRendererComponent(JTree tree, Object value, + boolean selected, boolean expanded, boolean leaf, int row, + boolean hasFocus) { + Component result = delegate.getTreeCellRendererComponent(tree, + value, selected, expanded, leaf, row, hasFocus); + + if ((compoundHighlighter != null) && (row < getRowCount()) + && (row >= 0)) { + result = compoundHighlighter.highlight(result, + getComponentAdapter(row)); + } + + return result; + } + + // ------------------ RolloverRenderer + + @Override + public boolean isEnabled() { + return (delegate instanceof RolloverRenderer) + && ((RolloverRenderer) delegate).isEnabled(); + } + + @Override + public void doClick() { + if (isEnabled()) { + ((RolloverRenderer) delegate).doClick(); + } + } + + } + + /** + * Invalidates cell size caching in the ui delegate. May do nothing if there's no + * safe (i.e. without reflection) way to message the delegate.

+ * + * This implementation calls BasicTreeUI setLeftChildIndent with the old indent if available. + * Beware: clearing the cache is an undocumented implementation side-effect of the + * method. Revisit if we ever should have a custom ui delegate. + * + * + */ + public void invalidateCellSizeCache() { + if (getUI() instanceof BasicTreeUI) { + BasicTreeUI ui = (BasicTreeUI) getUI(); + ui.setLeftChildIndent(ui.getLeftChildIndent()); + } + } + +//----------------------- edit + + /** + * {@inheritDoc}

+ * Overridden to fix focus issues with editors. + * This method installs and updates the internal CellEditorRemover which + * terminates ongoing edits if appropriate. Additionally, it + * registers a CellEditorListener with the cell editor to grab the + * focus back to tree, if appropriate. + * + * @see #updateEditorRemover() + */ + @Override + public void startEditingAtPath(TreePath path) { + super.startEditingAtPath(path); + if (isEditing()) { + updateEditorListener(); + updateEditorRemover(); + } + } + + + /** + * Hack to grab focus after editing. + */ + private void updateEditorListener() { + if (editorListener == null) { + editorListener = new CellEditorListener() { + + @Override + public void editingCanceled(ChangeEvent e) { + terminated(e); + } + + /** + * @param e + */ + private void terminated(ChangeEvent e) { + analyseFocus(); + ((CellEditor) e.getSource()).removeCellEditorListener(editorListener); + } + + @Override + public void editingStopped(ChangeEvent e) { + terminated(e); + } + + }; + } + getCellEditor().addCellEditorListener(editorListener); + + } + + /** + * This is called from cell editor listener if edit terminated. + * Trying to analyse if we should grab the focus back to the + * tree after. Brittle ... we assume we are the first to + * get the event, so we can analyse the hierarchy before the + * editing component is removed. + */ + protected void analyseFocus() { + if (isFocusOwnerDescending()) { + requestFocusInWindow(); + } + } + + + /** + * Returns a boolean to indicate if the current focus owner + * is descending from this table. + * Returns false if not editing, otherwise walks the focusOwner + * hierarchy, taking popups into account.

+ * + * PENDING: copied from JXTable ... should be somewhere in a utility + * class? + * + * @return a boolean to indicate if the current focus + * owner is contained. + */ + private boolean isFocusOwnerDescending() { + if (!isEditing()) return false; + Component focusOwner = + KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); + // PENDING JW: special casing to not fall through ... really wanted? + if (focusOwner == null) return false; + if (SwingXUtilities.isDescendingFrom(focusOwner, this)) return true; + // same with permanent focus owner + Component permanent = + KeyboardFocusManager.getCurrentKeyboardFocusManager().getPermanentFocusOwner(); + return SwingXUtilities.isDescendingFrom(permanent, this); + } + + + + /** + * Overridden to release the CellEditorRemover, if any. + */ + @Override + public void removeNotify() { + if (editorRemover != null) { + editorRemover.release(); + editorRemover = null; + } + super.removeNotify(); + } + + /** + * Lazily creates and updates the internal CellEditorRemover. + * + * + */ + private void updateEditorRemover() { + if (editorRemover == null) { + editorRemover = new CellEditorRemover(); + } + editorRemover.updateKeyboardFocusManager(); + } + + /** This class tracks changes in the keyboard focus state. It is used + * when the JXTree is editing to determine when to terminate the edit. + * If focus switches to a component outside of the JXTree, but in the + * same window, this will terminate editing. The exact terminate + * behaviour is controlled by the invokeStopEditing property. + * + * @see JTree#setInvokesStopCellEditing(boolean) + * + */ + public class CellEditorRemover implements PropertyChangeListener { + /** the focusManager this is listening to. */ + KeyboardFocusManager focusManager; + + public CellEditorRemover() { + updateKeyboardFocusManager(); + } + + /** + * Updates itself to listen to the current KeyboardFocusManager. + * + */ + public void updateKeyboardFocusManager() { + KeyboardFocusManager current = KeyboardFocusManager.getCurrentKeyboardFocusManager(); + setKeyboardFocusManager(current); + } + + /** + * stops listening. + * + */ + public void release() { + setKeyboardFocusManager(null); + } + + /** + * Sets the focusManager this is listening to. + * Unregisters/registers itself from/to the old/new manager, + * respectively. + * + * @param current the KeyboardFocusManager to listen too. + */ + private void setKeyboardFocusManager(KeyboardFocusManager current) { + if (focusManager == current) + return; + KeyboardFocusManager old = focusManager; + if (old != null) { + old.removePropertyChangeListener("permanentFocusOwner", this); + } + focusManager = current; + if (focusManager != null) { + focusManager.addPropertyChangeListener("permanentFocusOwner", + this); + } + + } + @Override + public void propertyChange(PropertyChangeEvent ev) { + if (!isEditing()) { + return; + } + + Component c = focusManager.getPermanentFocusOwner(); + JXTree tree = JXTree.this; + while (c != null) { + if (c instanceof JPopupMenu) { + c = ((JPopupMenu) c).getInvoker(); + } else { + + if (c == tree) { + // focus remains inside the table + return; + } else if ((c instanceof Window) || + (SwingXUtilities.isApplet(c) && c.getParent() == null)) { + if (c == SwingUtilities.getRoot(tree)) { + if (tree.getInvokesStopCellEditing()) { + tree.stopEditing(); + } + if (tree.isEditing()) { + tree.cancelEditing(); + } + } + break; + } + c = c.getParent(); + } + } + } + } + +// ------------------ oldish String conversion api, no longer recommended + + /** + * {@inheritDoc}

+ * + * Overridden to initialize the String conversion method of the model, if any.

+ * PENDING JW: remove - that is an outdated approach? + */ + @Override + public void setModel(TreeModel newModel) { + super.setModel(newModel); + } + + + +//------------------------------- ComponentAdapter + /** + * @return the unconfigured ComponentAdapter. + */ + protected ComponentAdapter getComponentAdapter() { + if (dataAdapter == null) { + dataAdapter = new TreeAdapter(this); + } + return dataAdapter; + } + + /** + * Convenience to access a configured ComponentAdapter. + * Note: the column index of the configured adapter is always 0. + * + * @param index the row index in view coordinates, must be valid. + * @return the configured ComponentAdapter. + */ + protected ComponentAdapter getComponentAdapter(int index) { + ComponentAdapter adapter = getComponentAdapter(); + adapter.column = 0; + adapter.row = index; + return adapter; + } + + protected ComponentAdapter dataAdapter; + + protected static class TreeAdapter extends ComponentAdapter { + private final JXTree tree; + + /** + * Constructs a TableCellRenderContext for the specified + * target component. + * + * @param component the target component + */ + public TreeAdapter(JXTree component) { + super(component); + tree = component; + } + + public JXTree getTree() { + return tree; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean hasFocus() { + return tree.isFocusOwner() && (tree.getLeadSelectionRow() == row); + } + + /** + * {@inheritDoc} + */ + @Override + public Object getValueAt(int row, int column) { + TreePath path = tree.getPathForRow(row); + return path.getLastPathComponent(); + } + + /** + * {@inheritDoc} + */ + @Override + public String getStringAt(int row, int column) { + return tree.getStringAt(row); + } + + /** + * {@inheritDoc} + */ + @Override + public Rectangle getCellBounds() { + return tree.getRowBounds(row); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isEditable() { + //this is not as robust as JXTable; should it be? -- kgs + return tree.isPathEditable(tree.getPathForRow(row)); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isSelected() { + return tree.isRowSelected(row); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isExpanded() { + return tree.isExpanded(row); + } + + /** + * {@inheritDoc} + */ + @Override + public int getDepth() { + return tree.getPathForRow(row).getPathCount() - 1; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isHierarchical() { + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isLeaf() { + return tree.getModel().isLeaf(getValue()); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isCellEditable(int row, int column) { + return false; /** TODO: */ + } + } + + +} diff --git a/src/main/java/org/jdesktop/swingx/JXTreeTable.java b/src/main/java/org/jdesktop/swingx/JXTreeTable.java new file mode 100644 index 0000000000..f79f3024f2 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/JXTreeTable.java @@ -0,0 +1,3336 @@ +/* + * $Id: JXTreeTable.java 4268 2012-12-07 11:55:23Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.decorator.ComponentAdapter; +import org.jdesktop.swingx.event.TreeExpansionBroadcaster; +import org.jdesktop.swingx.plaf.UIAction; +import org.jdesktop.swingx.renderer.StringValue; +import org.jdesktop.swingx.renderer.StringValues; +import org.jdesktop.swingx.rollover.RolloverProducer; +import org.jdesktop.swingx.rollover.RolloverRenderer; +import org.jdesktop.swingx.tree.DefaultXTreeCellRenderer; +import org.jdesktop.swingx.treetable.DefaultTreeTableModel; +import org.jdesktop.swingx.treetable.TreeTableCellEditor; +import org.jdesktop.swingx.treetable.TreeTableModel; +import org.jdesktop.swingx.treetable.TreeTableModelProvider; +import org.jdesktop.swingx.util.Contract; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.event.*; +import javax.swing.plaf.basic.BasicTreeUI; +import javax.swing.table.*; +import javax.swing.tree.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.EventObject; +import java.util.List; +import java.util.logging.Logger; + +/** + *

JXTreeTable is a specialized {@link JTable table} + * consisting of a single column in which to display hierarchical data, and any + * number of other columns in which to display regular data. The interface for + * the data model used by a JXTreeTable is + * {@link TreeTableModel}. It extends the + * {@link javax.swing.tree.TreeModel} interface to allow access to cell data by + * column indices within each node of the tree hierarchy.

+ * + *

The most straightforward way create and use a JXTreeTable, is to + * first create a suitable data model for it, and pass that to a + * JXTreeTable constructor, as shown below: + *

+ *  TreeTableModel  treeTableModel = new FileSystemModel(); // any TreeTableModel
+ *  JXTreeTable     treeTable = new JXTreeTable(treeTableModel);
+ *  JScrollPane     scrollpane = new JScrollPane(treeTable);
+ * 
+ * See {@link JTable} for an explanation of why putting the treetable + * inside a scroll pane is necessary.

+ * + *

A single treetable model instance may be shared among more than one + * JXTreeTable instances. To access the treetable model, always call + * {@link #getTreeTableModel() getTreeTableModel} and + * {@link #setTreeTableModel(TreeTableModel) setTreeTableModel}. + * JXTreeTable wraps the supplied treetable model inside a private + * adapter class to adapt it to a {@link TableModel}. Although + * the model adapter is accessible through the {@link #getModel() getModel} method, you + * should avoid accessing and manipulating it in any way. In particular, each + * model adapter instance is tightly bound to a single table instance, and any + * attempt to share it with another table (for example, by calling + * {@link #setModel(TableModel) setModel}) + * will throw an IllegalArgumentException! + * + * Note:

+ * This implementation is basically as hacky as the very first version + * more than a decaded ago: the renderer of the hierarchical column is a + * JXTree which is trickst into painting a single row at the position of + * the table cell. TreeModel changes must be adapted to TableModel changes + * after the tree received them, that is the TableModel events are asynchronous + * as compared to their base trigger. As a consequence, the adapted TableModel + * doesn't play nicely when shared in other J/X/Tables (f.i. used as rowHeader - + * see http://java.net/jira/browse/SWINGX-1529) + * + * @author Philip Milne + * @author Scott Violet + * @author Ramesh Gupta + * + */ +@JavaBean +public class JXTreeTable extends JXTable { + @SuppressWarnings("unused") + private static final Logger LOG = Logger.getLogger(JXTreeTable.class + .getName()); + /** + * Key for clientProperty to decide whether to apply hack around #168-jdnc. + */ + public static final String DRAG_HACK_FLAG_KEY = "treeTable.dragHackFlag"; + /** + * Key for clientProperty to decide whether to apply hack around #766-swingx. + */ + public static final String DROP_HACK_FLAG_KEY = "treeTable.dropHackFlag"; + /** + * Renderer used to render cells within the + * {@link #isHierarchical(int) hierarchical} column. + * renderer extends JXTree and implements TableCellRenderer + */ + private TreeTableCellRenderer renderer; + + /** + * Editor used to edit cells within the + * {@link #isHierarchical(int) hierarchical} column. + */ + private TreeTableCellEditor hierarchicalEditor; + + private TreeTableHacker treeTableHacker; + private boolean consumedOnPress; + private TreeExpansionBroadcaster treeExpansionBroadcaster; + + /** + * Constructs a JXTreeTable using a + * {@link DefaultTreeTableModel}. + */ + public JXTreeTable() { + this(new DefaultTreeTableModel()); + } + + /** + * Constructs a JXTreeTable using the specified + * {@link TreeTableModel}. + * + * @param treeModel model for the JXTreeTable + */ + public JXTreeTable(TreeTableModel treeModel) { + this(new TreeTableCellRenderer(treeModel)); + } + + /** + * Constructs a JXTreeTable using the specified + * {@link TreeTableCellRenderer}. + * + * @param renderer + * cell renderer for the tree portion of this JXTreeTable + * instance. + */ + private JXTreeTable(TreeTableCellRenderer renderer) { + // To avoid unnecessary object creation, such as the construction of a + // DefaultTableModel, it is better to invoke + // super(TreeTableModelAdapter) directly, instead of first invoking + // super() followed by a call to setTreeTableModel(TreeTableModel). + + // Adapt tree model to table model before invoking super() + super(new TreeTableModelAdapter(renderer)); + + // renderer-related initialization + init(renderer); // private method + initActions(); + // disable sorting + super.setSortable(false); + super.setAutoCreateRowSorter(false); + super.setRowSorter(null); + // no grid + setShowGrid(false, false); + + hierarchicalEditor = new TreeTableCellEditor(renderer); + +// // No grid. +// setShowGrid(false); // superclass default is "true" +// +// // Default intercell spacing +// setIntercellSpacing(spacing); // for both row margin and column margin + + } + + /** + * Initializes this JXTreeTable and permanently binds the specified renderer + * to it. + * + * @param renderer private tree/renderer permanently and exclusively bound + * to this JXTreeTable. + */ + private void init(TreeTableCellRenderer renderer) { + this.renderer = renderer; + assert ((TreeTableModelAdapter) getModel()).tree == this.renderer; + + // Force the JTable and JTree to share their row selection models. + ListToTreeSelectionModelWrapper selectionWrapper = + new ListToTreeSelectionModelWrapper(); + + // JW: when would that happen? + if (renderer != null) { + renderer.bind(this); // IMPORTANT: link back! + renderer.setSelectionModel(selectionWrapper); + } + // adjust the tree's rowHeight to this.rowHeight + adjustTreeRowHeight(getRowHeight()); + adjustTreeBounds(); + setSelectionModel(selectionWrapper.getListSelectionModel()); + + // propagate the lineStyle property to the renderer + PropertyChangeListener l = new PropertyChangeListener() { + + @Override + public void propertyChange(PropertyChangeEvent evt) { + JXTreeTable.this.renderer.putClientProperty(evt.getPropertyName(), evt.getNewValue()); + + } + + }; + addPropertyChangeListener("JTree.lineStyle", l); + + } + + + private void initActions() { + // Register the actions that this class can handle. + ActionMap map = getActionMap(); + map.put("expand-all", new Actions("expand-all")); + map.put("collapse-all", new Actions("collapse-all")); + } + + /** + * A small class which dispatches actions. + * TODO: Is there a way that we can make this static? + */ + private class Actions extends UIAction { + Actions(String name) { + super(name); + } + + @Override + public void actionPerformed(ActionEvent evt) { + if ("expand-all".equals(getName())) { + expandAll(); + } + else if ("collapse-all".equals(getName())) { + collapseAll(); + } + } + } + + + /** + * {@inheritDoc}

+ * Overridden to do nothing. + * + * TreeTable is not sortable because there is no equivalent to + * RowSorter (which is targeted to linear structures) for + * hierarchical data. + * + */ + @Override + public void setSortable(boolean sortable) { + // no-op + } + + /** + * {@inheritDoc}

+ * Overridden to do nothing. + * + * TreeTable is not sortable because there is no equivalent to + * RowSorter (which is targeted to linear structures) for + * hierarchical data. + * + */ + @Override + public void setAutoCreateRowSorter(boolean autoCreateRowSorter) { + } + + /** + * {@inheritDoc}

+ * Overridden to do nothing. + * + * TreeTable is not sortable because there is no equivalent to + * RowSorter (which is targeted to linear structures) for + * hierarchical data. + * + */ + @Override + public void setRowSorter(RowSorter sorter) { + } + + /** + * Hook into super's setAutoCreateRowSorter for use in sub-classes which want to experiment + * with tree table sorting/filtering.

+ * + * NOTE: While subclasses may use this method to allow access to + * super that usage alone will not magically turn sorting/filtering on! They have + * to implement an appropriate RowSorter/SortController + * as well. This is merely a hook to hang themselves, as requested in Issue #479-swingx + * + * + * @param autoCreateRowSorter + */ + protected void superSetAutoCreateRowSorter(boolean autoCreateRowSorter) { + super.setAutoCreateRowSorter(autoCreateRowSorter); + } + + /** + * Hook into super's setSortable for use in sub-classes which want to experiment + * with tree table sorting/filtering.

+ * + * NOTE: While subclasses may use this method to allow access to + * super that usage alone will not magically turn sorting/filtering on! They have + * to implement an appropriate RowSorter/SortController + * as well. This is merely a hook to hang themselves, as requested in Issue #479-swingx + * + * + * @param sortable + */ + protected void superSetSortable(boolean sortable) { + super.setSortable(sortable); + } + + /** + * Hook into super's setRowSorter for use in sub-classes which want to experiment + * with tree table sorting/filtering.

+ * + * NOTE: While subclasses may use this method to allow access to + * super that usage alone will not magically turn sorting/filtering on! They have + * to implement an appropriate RowSorter/SortController + * as well. This is merely a hook to hang themselves, as requested in Issue #479-swingx + * + * + * @param sorter + */ + protected void superSetRowSorter(RowSorter sorter) { + super.setRowSorter(sorter); + } + + /** + * {@inheritDoc}

+ * + * Overridden to keep the tree's enabled in synch. + */ + @Override + public void setEnabled(boolean enabled) { + renderer.setEnabled(enabled); + super.setEnabled(enabled); + } + + /** + * {@inheritDoc}

+ * + * Overridden to keep the tree's selectionBackground in synch. + */ + @Override + public void setSelectionBackground(Color selectionBackground) { + // happens on instantiation, updateUI is called before the renderer is installed + if (renderer != null) + renderer.setSelectionBackground(selectionBackground); + super.setSelectionBackground(selectionBackground); + } + + /** + * {@inheritDoc}

+ * + * Overridden to keep the tree's selectionForeground in synch. + */ + @Override + public void setSelectionForeground(Color selectionForeground) { + // happens on instantiation, updateUI is called before the renderer is installed + if (renderer != null) + renderer.setSelectionForeground(selectionForeground); + super.setSelectionForeground(selectionForeground); + } + + /** + * Overriden to invoke repaint for the particular location if + * the column contains the tree. This is done as the tree editor does + * not fill the bounds of the cell, we need the renderer to paint + * the tree in the background, and then draw the editor over it. + * You should not need to call this method directly.

+ * + * Additionally, there is tricksery involved to expand/collapse + * the nodes. + * + * {@inheritDoc} + */ + @Override + public boolean editCellAt(int row, int column, EventObject e) { + getTreeTableHacker().hitHandleDetectionFromEditCell(column, e); // RG: Fix Issue 49! + boolean canEdit = super.editCellAt(row, column, e); + if (canEdit && isHierarchical(column)) { + repaint(getCellRect(row, column, false)); + } + return canEdit; + } + + /** + * Overridden to enable hit handle detection a mouseEvent which triggered + * a expand/collapse. + */ + @Override + protected void processMouseEvent(MouseEvent e) { + // BasicTableUI selects on released if the pressed had been + // consumed. So we try to fish for the accompanying released + // here and consume it as wll. + if ((e.getID() == MouseEvent.MOUSE_RELEASED) && consumedOnPress) { + consumedOnPress = false; + e.consume(); + return; + } + if (getTreeTableHacker().hitHandleDetectionFromProcessMouse(e)) { + // Issue #332-swing: hacking around selection loss. + // prevent the + // _table_ selection by consuming the mouseEvent + // if it resulted in a expand/collapse + consumedOnPress = true; + e.consume(); + return; + } + consumedOnPress = false; + super.processMouseEvent(e); + } + + + protected TreeTableHacker getTreeTableHacker() { + if (treeTableHacker == null) { + treeTableHacker = createTreeTableHacker(); + } + return treeTableHacker; + } + + /** + * Hacking around various issues. Subclass and let it return + * your favourite. The current default is TreeTableHackerExt5 (latest + * evolution to work around #1230), the old long-standing default was + * TreeTableHackerExt3. If you experience problems with the latest, please + * let us know. + * + * @return + */ + protected TreeTableHacker createTreeTableHacker() { +// return new TreeTableHacker(); +// return new TreeTableHackerExt(); +// return new TreeTableHackerExt2(); +// return new TreeTableHackerExt3(); +// return new TreeTableHackerExt4(); + return new TreeTableHackerExt5(); + } + + private boolean processMouseMotion = true; + + @Override + protected void processMouseMotionEvent(MouseEvent e) { + if (processMouseMotion) + super.processMouseMotionEvent(e); + } + + /** + * This class extends TreeTableHackerExt instead of TreeTableHackerExt3 so + * as to serve as a clue that it is a complete overhaul and looking in + * TreeTableHackerExt2 and TreeTableHackerExt3 for methods to change the + * behavior will do you no good. + *

+ * The methods previously used are abandoned as they would be misnomers to + * the behavior as implemented in this class. + *

+ * Changes: + *

    + *
  1. + * According to TreeTableHackerExt3, clickCounts > 1 are not sent to the + * JTree so that double clicks will start edits (Issue #474). Well, mouse + * events are only sent to the JTree if they occur within the tree handle + * space - so that is not the behavior desired. Double clicks on the + * text/margin opposite the tree handle already started edits without that + * modification (I checked). The only thing that modification does is + * introduce bugs when one actually double clicks on a tree handle... so + * that idea was abandoned.
  2. + *
  3. + * There is no longer any discrimination between events that cause an + * expansion/collapse. Since the event location is check to see if it is in + * the tree handle margin area, this doesn't seem necessary. Plus it is more + * user friendly: if someone missed the tree handle by 1 pixel, then it + * caused a selection change instead of a node expansion/ collapse.
  4. + *
  5. + * The consumption of events are handled within this class itself because + * the behavior associated with the way that processMouseEvent(MouseEvent) + * consumed events was incompatible with the way this + * class does things. As a consequence, + * hitHandleDetectionFromProcessMouse(MouseEvent) + * always returns false so that processMoueEvent(MouseEvent) will not + * doing anything other than call its super + * method.
  6. + *
  7. + * All events of type MOUSE_PRESSED, MOUSE_RELEASED, and MOUSE_CLICKED, but + * excluding when isPopupTrigger() returns true, are sent to + * the JTree. This has the added benefit of not having to piggy back a mouse + * released event as we can just use the real mouse released event. This + * keeps the look and feel consistent for the user of UI's that + * expand/collapse nodes on the release of the mouse.
  8. + *
  9. + * The previous implementations have a spiel about avoiding events with + * modifiers because the UI might try to change the selection. Well that + * didn't occur in any of the look and feels I tested. Perhaps that was the + * case if events that landed within the content area of a node were sent to + * the JTree. If that behavior is actually necessary, then it can be added + * to the isTreeHandleEventType(MouseEvent) method. This + * implementation sends all events regardless of the modifiers.
  10. + *
  11. + * This implementation toggles the processing of mouse motion events. When + * events are sent to the tree, it is turned off and turned back on when an + * event is not sent to the tree. This fixes selection changes that occur + * when one drags the mouse after pressing on a tree handle.
  12. + *
+ * + * contributed by member aephyr@dev.java.net + */ + public class TreeTableHackerExt4 extends TreeTableHackerExt { + + /** + * Filter to find mouse events that are candidates for node expansion/ + * collapse. MOUSE_PRESSED and MOUSE_RELEASED are used by default UIs. + * MOUSE_CLICKED is included as it may be used by a custom UI. + * + * @param e the currently dispatching mouse event + * @return true if the event is a candidate for sending to the JTree + */ + protected boolean isTreeHandleEventType(MouseEvent e) { + switch (e.getID()) { + case MouseEvent.MOUSE_CLICKED: + case MouseEvent.MOUSE_PRESSED: + case MouseEvent.MOUSE_RELEASED: + return !e.isPopupTrigger(); + } + return false; + } + + /** + * This method checks if the location of the event is in the tree handle + * margin and translates the coordinates for the JTree. + * + * @param e the currently dispatching mouse event + * @return the mouse event to dispatch to the JTree or null if nothing + * should be dispatched + */ + protected MouseEvent getEventForTreeRenderer(MouseEvent e) { + Point pt = e.getPoint(); + int col = columnAtPoint(pt); + if (col >= 0 && isHierarchical(col)) { + int row = rowAtPoint(pt); + if (row >= 0) { + // There will not be a check to see if the y coordinate is in range + // because the use of row = rowAtPoint(pt) will only return + // a row that has the y coordinates in the range of our point. + Rectangle cellBounds = getCellRect(row, col, false); + int x = e.getX() - cellBounds.x; + Rectangle nodeBounds = renderer.getRowBounds(row); + // The renderer's component orientation is checked because that + // is the one that really matters. Though it seems to always be + // in sync with the JXTreeTable's component orientation, maybe + // someone wants them to be different for some reason. + if (renderer.getComponentOrientation().isLeftToRight() ? x < nodeBounds.x + : x > nodeBounds.x + nodeBounds.width) { + return new MouseEvent(renderer, e.getID(), e.getWhen(), + e.getModifiers(), x, e.getY(), + e.getXOnScreen(), e.getYOnScreen(), e + .getClickCount(), false, e.getButton()); + } + } + } + return null; + } + + /** + * + * @return this method always returns false, so that processMouseEvent + * always just simply calls its super method + */ + @Override + public boolean hitHandleDetectionFromProcessMouse(MouseEvent e) { + if (!isHitDetectionFromProcessMouse()) + return false; + if (isTreeHandleEventType(e)) { + MouseEvent newE = getEventForTreeRenderer(e); + if (newE != null) { + renderer.dispatchEvent(newE); + if (shouldDisableMouseMotionOnTable(e)) { + // This fixes the issue of drags on tree handles + // (often unintentional) from selecting all nodes from the + // anchor to the node of said tree handle. + processMouseMotion = false; + // part of 561-swingx: if focus elsewhere and dispatching the + // mouseEvent the focus doesn't move from elsewhere + // still doesn't help in very first click after startup + // probably lead of row selection event not correctly + // updated on synch from treeSelectionModel + requestFocusInWindow(); + } else { + processMouseMotion = true; + } + e.consume(); + // Return false to prevent JXTreeTable.processMouseEvent(MouseEvent) + // from stopping the processing of the event. This allows the + // listeners to see the event even though it is consumed + // (perhaps useful for a user supplied listener). A proper UI + // listener will ignore consumed events. + return false; + // alternatively, you would have to use: return e.getID() == MouseEvent.MOUSE_PRESSED; + // because JXTreeTable.processMouseEvent(MouseEvent) assumes true + // will only be returned for MOUSE_PRESSED events. Also, if true + // were to be returned, then you'd have to piggy back a released + // event as the previous implementation does, because the actual + // released event would never reach this method. + } + } + processMouseMotion = true; + return false; + } + + /** + * Returns a boolean indicating whether mouseMotionEvents to the + * table should be disabled. This is called from hitHandleDetectionFromMouseEvent + * if the event was passed to the rendering tree and consumed. Returning + * true has the side-effect of requesting focus to the table.

+ * + * NOTE JW: this was extracted to from the calling method to fix + * Issue #1527-swingx (no tooltips on JXTreeTable after expand/collapse) + * and at the same time allow subclasses to further hack around ...

+ * + * @param e the mouseEvent that was routed to the renderer. + * @return true if disabling mouseMotionEvents to table, false if enabling them + */ + protected boolean shouldDisableMouseMotionOnTable(MouseEvent e) { + return processMouseMotion && e.getID() == MouseEvent.MOUSE_PRESSED; + } + } + + /* + * Changed to calculate the area of the tree handle and only forward mouse + * events to the tree if the event lands within that area. This keeps the + * selection behavior consistent with TreeTableHackerExt3. + * + * contributed by member aephyr@dev.java.net + */ + public class TreeTableHackerExt5 extends TreeTableHackerExt4 { + + /** + * If a negative number is returned, then all events that occur in the + * leading margin will be forwarded to the tree and consumed. + * + * @return the width of the tree handle if it can be determined, else -1 + */ + protected int getTreeHandleWidth() { + if (renderer.getUI() instanceof BasicTreeUI) { + BasicTreeUI ui = (BasicTreeUI) renderer.getUI(); + return ui.getLeftChildIndent() + ui.getRightChildIndent(); + } else { + return -1; + } + } + + @Override + protected MouseEvent getEventForTreeRenderer(MouseEvent e) { + Point pt = e.getPoint(); + int col = columnAtPoint(pt); + if (col >= 0 && isHierarchical(col)) { + int row = rowAtPoint(pt); + // There will not be a check to see if the y coordinate is in + // range + // because the use of row = rowAtPoint(pt) will only return a + // row + // that has the y coordinates in the range of our point. + if (row >= 0) { + TreePath path = getPathForRow(row); + Object node = path.getLastPathComponent(); + // Check if the node has a tree handle and if so, check + // if the event location falls over the tree handle. + if (!getTreeTableModel().isLeaf(node) + && (getTreeTableModel().getChildCount(node) > 0 || !renderer + .hasBeenExpanded(path))) { + Rectangle cellBounds = getCellRect(row, col, false); + int x = e.getX() - cellBounds.x; + Rectangle nb = renderer.getRowBounds(row); + int thw = getTreeHandleWidth(); + // The renderer's component orientation is checked + // because that + // is the one that really matters. Though it seems to + // always be + // in sync with the JXTreeTable's component orientation, + // maybe + // someone wants them to be different for some reason. + if (renderer.getComponentOrientation().isLeftToRight() ? x < nb.x + && (thw < 0 || x > nb.x - thw) + : x > nb.x + nb.width + && (thw < 0 || x < nb.x + nb.width + + thw)) { + return new MouseEvent(renderer, e.getID(), e + .getWhen(), e.getModifiers(), x, e.getY(), + e.getXOnScreen(), e.getYOnScreen(), e + .getClickCount(), false, e + .getButton()); + } + } + } + } + return null; + } + + } + + + + /** + * Temporary class to have all the hacking at one place. Naturally, it will + * change a lot. The base class has the "stable" behaviour as of around + * jun2006 (before starting the fix for 332-swingx).

+ * + * specifically: + * + *

    + *
  1. hitHandleDetection triggeredn in editCellAt + *
+ * + */ + public class TreeTableHacker { + + protected boolean expansionChangedFlag; + + /** + * Decision whether the handle hit detection + * should be done in processMouseEvent or editCellAt. + * Here: returns false. + * + * @return true for handle hit detection in processMouse, false + * for editCellAt. + */ + protected boolean isHitDetectionFromProcessMouse() { + return false; + } + + /** + * Entry point for hit handle detection called from editCellAt, + * does nothing if isHitDetectionFromProcessMouse is true; + * + * @see #isHitDetectionFromProcessMouse() + */ + public void hitHandleDetectionFromEditCell(int column, EventObject e) { + if (!isHitDetectionFromProcessMouse()) { + expandOrCollapseNode(column, e); + } + } + + /** + * Entry point for hit handle detection called from processMouse. + * Does nothing if isHitDetectionFromProcessMouse is false. + * + * @return true if the mouseEvent triggered an expand/collapse in + * the renderer, false otherwise. + * + * @see #isHitDetectionFromProcessMouse() + */ + public boolean hitHandleDetectionFromProcessMouse(MouseEvent e) { + if (!isHitDetectionFromProcessMouse()) + return false; + int col = columnAtPoint(e.getPoint()); + return ((col >= 0) && expandOrCollapseNode(columnAtPoint(e + .getPoint()), e)); + } + + /** + * Complete editing if collapsed/expanded. + *

+ * + * Is: first try to stop editing before falling back to cancel. + *

+ * This is part of fix for #730-swingx - editingStopped not always + * called. The other part is to call this from the renderer before + * expansion related state has changed. + *

+ * + * Was: any editing is always cancelled. + *

+ * This is a rude fix to #120-jdnc: data corruption on collapse/expand + * if editing. This is called from the renderer after expansion related + * state has changed. + * + */ + protected void completeEditing() { + // JW: fix for 1126 - ignore complete if not editing hierarchical + // reverted - introduced regression .... for details please see the bug report + if (isEditing()) { // && isHierarchical(getEditingColumn())) { + boolean success = getCellEditor().stopCellEditing(); + if (!success) { + getCellEditor().cancelCellEditing(); + } + } + } + + /** + * Tricksery to make the tree expand/collapse. + *

+ * + * This might be - indirectly - called from one of two places: + *

    + *
  1. editCellAt: original, stable but buggy (#332, #222) the table's + * own selection had been changed due to the click before even entering + * into editCellAt so all tree selection state is lost. + * + *
  2. processMouseEvent: the idea is to catch the mouseEvent, check + * if it triggered an expanded/collapsed, consume and return if so or + * pass to super if not. + *
+ * + *

+ * widened access for testing ... + * + * + * @param column the column index under the event, if any. + * @param e the event which might trigger a expand/collapse. + * + * @return this methods evaluation as to whether the event triggered a + * expand/collaps + */ + protected boolean expandOrCollapseNode(int column, EventObject e) { + if (!isHierarchical(column)) + return false; + if (!mightBeExpansionTrigger(e)) + return false; + boolean changedExpansion = false; + MouseEvent me = (MouseEvent) e; + if (hackAroundDragEnabled(me)) { + /* + * Hack around #168-jdnc: dirty little hack mentioned in the + * forum discussion about the issue: fake a mousePressed if drag + * enabled. The usability is slightly impaired because the + * expand/collapse is effectively triggered on released only + * (drag system intercepts and consumes all other). + */ + me = new MouseEvent((Component) me.getSource(), + MouseEvent.MOUSE_PRESSED, me.getWhen(), me + .getModifiers(), me.getX(), me.getY(), me + .getClickCount(), me.isPopupTrigger()); + + } + // If the modifiers are not 0 (or the left mouse button), + // tree may try and toggle the selection, and table + // will then try and toggle, resulting in the + // selection remaining the same. To avoid this, we + // only dispatch when the modifiers are 0 (or the left mouse + // button). + if (me.getModifiers() == 0 + || me.getModifiers() == InputEvent.BUTTON1_MASK) { + MouseEvent pressed = new MouseEvent(renderer, me.getID(), me + .getWhen(), me.getModifiers(), me.getX() + - getCellRect(0, column, false).x, me.getY(), me + .getClickCount(), me.isPopupTrigger()); + renderer.dispatchEvent(pressed); + // For Mac OS X, we need to dispatch a MOUSE_RELEASED as well + MouseEvent released = new MouseEvent(renderer, + MouseEvent.MOUSE_RELEASED, pressed + .getWhen(), pressed.getModifiers(), pressed + .getX(), pressed.getY(), pressed + .getClickCount(), pressed.isPopupTrigger()); + renderer.dispatchEvent(released); + if (expansionChangedFlag) { + changedExpansion = true; + } + } + expansionChangedFlag = false; + return changedExpansion; + } + + protected boolean mightBeExpansionTrigger(EventObject e) { + if (!(e instanceof MouseEvent)) return false; + MouseEvent me = (MouseEvent) e; + if (!SwingUtilities.isLeftMouseButton(me)) return false; + return me.getID() == MouseEvent.MOUSE_PRESSED; + } + + /** + * called from the renderer's setExpandedPath after + * all expansion-related updates happend. + * + */ + protected void expansionChanged() { + expansionChangedFlag = true; + } + + } + + /** + * + * Note: currently this class looks a bit funny (only overriding + * the hit decision method). That's because the "experimental" code + * as of the last round moved to stable. But I expect that there's more + * to come, so I leave it here. + * + *

    + *
  1. hit handle detection in processMouse + *
+ */ + public class TreeTableHackerExt extends TreeTableHacker { + + + /** + * Here: returns true. + * @inheritDoc + */ + @Override + protected boolean isHitDetectionFromProcessMouse() { + return true; + } + + } + + /** + * Patch for #471-swingx: no selection on click in hierarchical column + * if outside of node-text. Mar 2007. + *

+ * + * Note: with 1.6 the expansion control was broken even with the "normal extended" + * TreeTableHackerExt. When fixing that (renderer must have correct width for + * BasicTreeUI since 1.6) took a look into why this didn't work and made it work. + * So, now this is bidi-compliant. + * + * @author tiberiu@dev.java.net + */ + public class TreeTableHackerExt2 extends TreeTableHackerExt { + @Override + protected boolean expandOrCollapseNode(int column, EventObject e) { + if (!isHierarchical(column)) + return false; + if (!mightBeExpansionTrigger(e)) + return false; + boolean changedExpansion = false; + MouseEvent me = (MouseEvent) e; + if (hackAroundDragEnabled(me)) { + /* + * Hack around #168-jdnc: dirty little hack mentioned in the + * forum discussion about the issue: fake a mousePressed if drag + * enabled. The usability is slightly impaired because the + * expand/collapse is effectively triggered on released only + * (drag system intercepts and consumes all other). + */ + me = new MouseEvent((Component) me.getSource(), + MouseEvent.MOUSE_PRESSED, me.getWhen(), me + .getModifiers(), me.getX(), me.getY(), me + .getClickCount(), me.isPopupTrigger()); + } + // If the modifiers are not 0 (or the left mouse button), + // tree may try and toggle the selection, and table + // will then try and toggle, resulting in the + // selection remaining the same. To avoid this, we + // only dispatch when the modifiers are 0 (or the left mouse + // button). + if (me.getModifiers() == 0 + || me.getModifiers() == InputEvent.BUTTON1_MASK) { + // compute where the mouse point is relative to the tree + // as renderer, that the x coordinate translated to be relative + // to the column x-position + Point treeMousePoint = getTreeMousePoint(column, me); + int treeRow = renderer.getRowForLocation(treeMousePoint.x, + treeMousePoint.y); + int row = 0; + // mouse location not inside the node content + if (treeRow < 0) { + // get the row for mouse location + row = renderer.getClosestRowForLocation(treeMousePoint.x, + treeMousePoint.y); + // check against actual bounds of the row + Rectangle bounds = renderer.getRowBounds(row); + if (bounds == null) { + row = -1; + } else { + // check if the mouse location is "leading" + // relative to the content box + // JW: fix issue 1168-swingx: expansion control broken in + if (getComponentOrientation().isLeftToRight()) { + // this is LToR only + if ((bounds.y + bounds.height < treeMousePoint.y) + || bounds.x > treeMousePoint.x) { + row = -1; + } + } else { + if ((bounds.y + bounds.height < treeMousePoint.y) + || bounds.x + bounds.width < treeMousePoint.x) { + row = -1; + } + + } + } + // make sure the expansionChangedFlag is set to false for + // the case that up in the tree nothing happens + expansionChangedFlag = false; + } + + if ((treeRow >= 0) // if in content box + || ((treeRow < 0) && (row < 0))) {// or outside but leading + if (treeRow >= 0) { //Issue 561-swingx: in content box, update column lead to focus + getColumnModel().getSelectionModel().setLeadSelectionIndex(column); + } + // dispatch the translated event to the tree + // which either triggers a tree selection + // or expands/collapses a node + MouseEvent pressed = new MouseEvent(renderer, me.getID(), + me.getWhen(), me.getModifiers(), treeMousePoint.x, + treeMousePoint.y, me.getClickCount(), me + .isPopupTrigger()); + renderer.dispatchEvent(pressed); + // For Mac OS X, we need to dispatch a MOUSE_RELEASED as + // well + MouseEvent released = new MouseEvent(renderer, + MouseEvent.MOUSE_RELEASED, pressed + .getWhen(), pressed.getModifiers(), pressed + .getX(), pressed.getY(), pressed + .getClickCount(), pressed.isPopupTrigger()); + renderer.dispatchEvent(released); + // part of 561-swingx: if focus elsewhere and dispatching the + // mouseEvent the focus doesn't move from elsewhere + // still doesn't help in very first click after startup + // probably lead of row selection event not correctly updated + // on synch from treeSelectionModel + requestFocusInWindow(); + } + if (expansionChangedFlag) { + changedExpansion = true; + } else { + } + } + expansionChangedFlag = false; + return changedExpansion; + } + + /** + * This is a patch provided for Issue #980-swingx which should + * improve the bidi-compliance. Still doesn't work in our + * visual tests...

+ * + * Problem was not in the translation to renderer coordinate system, + * it was in the method itself: the check whether we are "beyond" the + * cell content box is bidi-dependent. Plus (since 1.6), width of + * renderer must be > 0. + * + * + * @param column the column index under the event, if any. + * @param e the event which might trigger a expand/collapse. + * @return the Point adjusted for bidi + */ + protected Point getTreeMousePoint(int column, MouseEvent me) { + // could inline as it wasn't the place to fix for broken RToL + return new Point(me.getX() + - getCellRect(0, column, false).x, me.getY()); + } + } + /** + * A more (or less, depending in pov :-) aggressiv hacker. Compared + * to super, it dispatches less events to address open issues.

+ * + * Issue #474-swingx: double click should start edit (not expand/collapse) + * changed mightBeExpansionTrigger to filter out clickCounts > 1 + *

+ * Issue #875-swingx: cell selection mode + * changed the dispatch to do so only if mouse event outside content + * box and leading + *

+ * Issue #1169-swingx: remove 1.5 dnd hack + * removed the additional dispatch here and + * changed in the implementation of hackAroundDragEnabled + * to no longer look for the system property (it's useless even if set) + * + * @author tiberiu@dev.java.net + */ + public class TreeTableHackerExt3 extends TreeTableHackerExt2 { + @Override + protected boolean expandOrCollapseNode(int column, EventObject e) { + if (!isHierarchical(column)) + return false; + if (!mightBeExpansionTrigger(e)) + return false; + boolean changedExpansion = false; + MouseEvent me = (MouseEvent) e; + // If the modifiers are not 0 (or the left mouse button), + // tree may try and toggle the selection, and table + // will then try and toggle, resulting in the + // selection remaining the same. To avoid this, we + // only dispatch when the modifiers are 0 (or the left mouse + // button). + if (me.getModifiers() == 0 + || me.getModifiers() == InputEvent.BUTTON1_MASK) { + // compute where the mouse point is relative to the tree + // as renderer, that the x coordinate translated to be relative + // to the column x-position + Point treeMousePoint = getTreeMousePoint(column, me); + int treeRow = renderer.getRowForLocation(treeMousePoint.x, + treeMousePoint.y); + int row = 0; + // mouse location not inside the node content + if (treeRow < 0) { + // get the row for mouse location + row = renderer.getClosestRowForLocation(treeMousePoint.x, + treeMousePoint.y); + // check against actual bounds of the row + Rectangle bounds = renderer.getRowBounds(row); + if (bounds == null) { + row = -1; + } else { + // check if the mouse location is "leading" + // relative to the content box + // JW: fix issue 1168-swingx: expansion control broken in + if (getComponentOrientation().isLeftToRight()) { + // this is LToR only + if ((bounds.y + bounds.height < treeMousePoint.y) + || bounds.x > treeMousePoint.x) { + row = -1; + } + } else { + if ((bounds.y + bounds.height < treeMousePoint.y) + || bounds.x + bounds.width < treeMousePoint.x) { + row = -1; + } + + } + } + } + // make sure the expansionChangedFlag is set to false for + // the case that up in the tree nothing happens + expansionChangedFlag = false; + + if ((treeRow < 0) && (row < 0)) {// outside and leading + // dispatch the translated event to the tree + // which either triggers a tree selection + // or expands/collapses a node + MouseEvent pressed = new MouseEvent(renderer, me.getID(), + me.getWhen(), me.getModifiers(), treeMousePoint.x, + treeMousePoint.y, me.getClickCount(), me + .isPopupTrigger()); + renderer.dispatchEvent(pressed); + // For Mac OS X, we need to dispatch a MOUSE_RELEASED as + // well + MouseEvent released = new MouseEvent(renderer, + MouseEvent.MOUSE_RELEASED, pressed + .getWhen(), pressed.getModifiers(), pressed + .getX(), pressed.getY(), pressed + .getClickCount(), pressed.isPopupTrigger()); + renderer.dispatchEvent(released); + // part of 561-swingx: if focus elsewhere and dispatching the + // mouseEvent the focus doesn't move from elsewhere + // still doesn't help in very first click after startup + // probably lead of row selection event not correctly updated + // on synch from treeSelectionModel + requestFocusInWindow(); + } + if (expansionChangedFlag) { + changedExpansion = true; + } else { + } + } + expansionChangedFlag = false; + return changedExpansion; + } + /** + * Overridden to exclude clickcounts > 1. + */ + @Override + protected boolean mightBeExpansionTrigger(EventObject e) { + if (!(e instanceof MouseEvent)) return false; + MouseEvent me = (MouseEvent) e; + if (!SwingUtilities.isLeftMouseButton(me)) return false; + if (me.getClickCount() > 1) return false; + return me.getID() == MouseEvent.MOUSE_PRESSED; + } + + } + + /** + * Decides whether we want to apply the hack for #168-jdnc. here: returns + * true if dragEnabled() and a client property with key DRAG_HACK_FLAG_KEY + * has a value of boolean true.

+ * + * Note: this is updated for 1.6, as the intermediate system property + * for enabled drag support is useless now (it's the default) + * + * @param me the mouseEvent that triggered a editCellAt + * @return true if the hack should be applied. + */ + protected boolean hackAroundDragEnabled(MouseEvent me) { + Boolean dragHackFlag = (Boolean) getClientProperty(DRAG_HACK_FLAG_KEY); + return getDragEnabled() && Boolean.TRUE.equals(dragHackFlag); + } + + /** + * Overridden to provide a workaround for BasicTableUI anomaly. Make sure + * the UI never tries to resize the editor. The UI currently uses different + * techniques to paint the renderers and editors. So, overriding setBounds() + * is not the right thing to do for an editor. Returning -1 for the + * editing row in this case, ensures the editor is never painted. + * + * {@inheritDoc} + */ + @Override + public int getEditingRow() { + if (editingRow == -1) return -1; + return isHierarchical(editingColumn) ? -1 : editingRow; + } + + /** + * Returns the actual row that is editing as getEditingRow + * will always return -1. + */ + private int realEditingRow() { + return editingRow; + } + + /** + * Sets the data model for this JXTreeTable to the specified + * {@link TreeTableModel}. The same data model + * may be shared by any number of JXTreeTable instances. + * + * @param treeModel data model for this JXTreeTable + */ + public void setTreeTableModel(TreeTableModel treeModel) { + TreeTableModel old = getTreeTableModel(); +// boolean rootVisible = isRootVisible(); +// setRootVisible(false); + renderer.setModel(treeModel); +// setRootVisible(rootVisible); + + firePropertyChange("treeTableModel", old, getTreeTableModel()); + } + + /** + * Returns the underlying TreeTableModel for this JXTreeTable. + * + * @return the underlying TreeTableModel for this JXTreeTable + */ + public TreeTableModel getTreeTableModel() { + return (TreeTableModel) renderer.getModel(); + } + + /** + *

Overrides superclass version to make sure that the specified + * {@link TableModel} is compatible with JXTreeTable before + * invoking the inherited version.

+ * + *

Because JXTreeTable internally adapts an + * {@link TreeTableModel} to make it a compatible + * TableModel, this method should never be called directly. Use + * {@link #setTreeTableModel(TreeTableModel) setTreeTableModel} instead.

+ * + *

While it is possible to obtain a reference to this adapted + * version of the TableModel by calling {@link JTable#getModel()}, + * any attempt to call setModel() with that adapter will fail because + * the adapter might have been bound to a different JXTreeTable instance. If + * you want to extract the underlying TreeTableModel, which, by the way, + * can be shared, use {@link #getTreeTableModel() getTreeTableModel} + * instead

. + * + * @param tableModel must be a TreeTableModelAdapter + * @throws IllegalArgumentException if the specified tableModel is not an + * instance of TreeTableModelAdapter + */ + @Override + public final void setModel(TableModel tableModel) { // note final keyword + if (tableModel instanceof TreeTableModelAdapter) { + if (((TreeTableModelAdapter) tableModel).getTreeTable() == null) { + // Passing the above test ensures that this method is being + // invoked either from JXTreeTable/JTable constructor or from + // setTreeTableModel(TreeTableModel) + super.setModel(tableModel); // invoke superclass version + + ((TreeTableModelAdapter) tableModel).bind(this); // permanently bound + // Once a TreeTableModelAdapter is bound to any JXTreeTable instance, + // invoking JXTreeTable.setModel() with that adapter will throw an + // IllegalArgumentException, because we really want to make sure + // that a TreeTableModelAdapter is NOT shared by another JXTreeTable. + } + else { + throw new IllegalArgumentException("model already bound"); + } + } + else { + throw new IllegalArgumentException("unsupported model type"); + } + } + + + + @Override + public void tableChanged(TableModelEvent e) { + if (isStructureChanged(e) || isUpdate(e)) { + super.tableChanged(e); + } else { + resizeAndRepaint(); + } + } + + /** + * Throws UnsupportedOperationException because variable height rows are + * not supported. + * + * @param row ignored + * @param rowHeight ignored + * @throws UnsupportedOperationException because variable height rows are + * not supported + */ + @Override + public final void setRowHeight(int row, int rowHeight) { + throw new UnsupportedOperationException("variable height rows not supported"); + } + + /** + * Sets the row height for this JXTreeTable and forwards the + * row height to the renderering tree. + * + * @param rowHeight height of a row. + */ + @Override + public void setRowHeight(int rowHeight) { + super.setRowHeight(rowHeight); + adjustTreeRowHeight(getRowHeight()); + } + + /** + * Forwards tableRowHeight to tree. + * + * @param tableRowHeight height of a row. + */ + protected void adjustTreeRowHeight(int tableRowHeight) { + if (renderer != null && renderer.getRowHeight() != tableRowHeight) { + renderer.setRowHeight(tableRowHeight); + } + } + + /** + * Forwards treeRowHeight to table. This is for completeness only: the + * rendering tree is under our total control, so we don't expect + * any external call to tree.setRowHeight. + * + * @param treeRowHeight height of a row. + */ + protected void adjustTableRowHeight(int treeRowHeight) { + if (getRowHeight() != treeRowHeight) { + adminSetRowHeight(treeRowHeight); + } + } + + /** + * {@inheritDoc}

+ * + * Overridden to adjust the renderer's size. + */ + @Override + public void columnMarginChanged(ChangeEvent e) { + super.columnMarginChanged(e); + adjustTreeBounds(); + } + + /** + * Forces the renderer to resize for fitting into hierarchical column. + */ + private void adjustTreeBounds() { + if (renderer != null) { + renderer.setBounds(0, 0, 0, 0); + } + } + + /** + *

Overridden to ensure that private renderer state is kept in sync with the + * state of the component. Calls the inherited version after performing the + * necessary synchronization. If you override this method, make sure you call + * this version from your version of this method.

+ * + *

This version maps the selection mode used by the renderer to match the + * selection mode specified for the table. Specifically, the modes are mapped + * as follows: + *

+     *  ListSelectionModel.SINGLE_INTERVAL_SELECTION: TreeSelectionModel.CONTIGUOUS_TREE_SELECTION;
+     *  ListSelectionModel.MULTIPLE_INTERVAL_SELECTION: TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION;
+     *  any other (default): TreeSelectionModel.SINGLE_TREE_SELECTION;
+     * 
+ * + * {@inheritDoc} + * + * @param mode any of the table selection modes + */ + @Override + public void setSelectionMode(int mode) { + if (renderer != null) { + switch (mode) { + case ListSelectionModel.SINGLE_INTERVAL_SELECTION: { + renderer.getSelectionModel().setSelectionMode( + TreeSelectionModel.CONTIGUOUS_TREE_SELECTION); + break; + } + case ListSelectionModel.MULTIPLE_INTERVAL_SELECTION: { + renderer.getSelectionModel().setSelectionMode( + TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION); + break; + } + default: { + renderer.getSelectionModel().setSelectionMode( + TreeSelectionModel.SINGLE_TREE_SELECTION); + break; + } + } + } + super.setSelectionMode(mode); + } + + /** + * {@inheritDoc}

+ * + * Overridden to decorate the tree's renderer after calling super. + * At that point, it is only the tree itself that has been decorated. + * + * @param renderer the TableCellRenderer to prepare + * @param row the row of the cell to render, where 0 is the first row + * @param column the column of the cell to render, where 0 is the first column + * @return the Component used as a stamp to render the specified cell + * + * @see #applyRenderer(Component, ComponentAdapter) + */ + @Override + public Component prepareRenderer(TableCellRenderer renderer, int row, + int column) { + Component component = super.prepareRenderer(renderer, row, column); + return applyRenderer(component, getComponentAdapter(row, column)); + } + + /** + * Performs configuration of the tree's renderer if the adapter's column is + * the hierarchical column, does nothing otherwise. + *

+ * + * Note: this is legacy glue if the treeCellRenderer is of type + * DefaultTreeCellRenderer. In that case the renderer's + * background/foreground/Non/Selection colors are set to the tree's + * background/foreground depending on the adapter's selection state. Does + * nothing if the treeCellRenderer is backed by a ComponentProvider. + * + * @param component the rendering component + * @param adapter component data adapter + * @throws NullPointerException if the specified component or adapter is + * null + */ + protected Component applyRenderer(Component component, + ComponentAdapter adapter) { + if (component == null) { + throw new IllegalArgumentException("null component"); + } + if (adapter == null) { + throw new IllegalArgumentException("null component data adapter"); + } + + if (isHierarchical(adapter.column)) { + // After all decorators have been applied, make sure that relevant + // attributes of the table cell renderer are applied to the + // tree cell renderer before the hierarchical column is rendered! + TreeCellRenderer tcr = renderer.getCellRenderer(); + if (tcr instanceof JXTree.DelegatingRenderer) { + tcr = ((JXTree.DelegatingRenderer) tcr).getDelegateRenderer(); + + } + if (tcr instanceof DefaultTreeCellRenderer) { + + DefaultTreeCellRenderer dtcr = ((DefaultTreeCellRenderer) tcr); + // this effectively overwrites the dtcr settings + if (adapter.isSelected()) { + dtcr.setTextSelectionColor(component.getForeground()); + dtcr.setBackgroundSelectionColor(component.getBackground()); + } else { + dtcr.setTextNonSelectionColor(component.getForeground()); + dtcr.setBackgroundNonSelectionColor(component + .getBackground()); + } + } + } + return component; + } + + /** + * Sets the specified TreeCellRenderer as the Tree cell renderer. + * + * @param cellRenderer to use for rendering tree cells. + */ + public void setTreeCellRenderer(TreeCellRenderer cellRenderer) { + if (renderer != null) { + renderer.setCellRenderer(cellRenderer); + } + } + + public TreeCellRenderer getTreeCellRenderer() { + return renderer.getCellRenderer(); + } + + /** + * {@inheritDoc}

+ * + * Overridden to special-case the hierarchical column. + */ + @Override + public String getToolTipText(MouseEvent event) { + int column = columnAtPoint(event.getPoint()); + if (column >= 0 && isHierarchical(column)) { + int row = rowAtPoint(event.getPoint()); + return renderer.getToolTipText(event, row, column); + } + return super.getToolTipText(event); + } + + /** + * {@inheritDoc}

+ * + * Overridden to set the fixed tooltip text to the tree that is rendering the + * hierarchical column. + */ + @Override + public void setToolTipText(String text) { + super.setToolTipText(text); + renderer.setToolTipText(text); + } + + /** + * Sets the specified icon as the icon to use for rendering collapsed nodes. + * + * @param icon to use for rendering collapsed nodes + * + * @see JXTree#setCollapsedIcon(Icon) + */ + public void setCollapsedIcon(Icon icon) { + renderer.setCollapsedIcon(icon); + } + + /** + * Sets the specified icon as the icon to use for rendering expanded nodes. + * + * @param icon to use for rendering expanded nodes + * + * @see JXTree#setExpandedIcon(Icon) + */ + public void setExpandedIcon(Icon icon) { + renderer.setExpandedIcon(icon); + } + + /** + * Sets the specified icon as the icon to use for rendering open container nodes. + * + * @param icon to use for rendering open nodes + * + * @see JXTree#setOpenIcon(Icon) + */ + public void setOpenIcon(Icon icon) { + renderer.setOpenIcon(icon); + } + + /** + * Sets the specified icon as the icon to use for rendering closed container nodes. + * + * @param icon to use for rendering closed nodes + * + * @see JXTree#setClosedIcon(Icon) + */ + public void setClosedIcon(Icon icon) { + renderer.setClosedIcon(icon); + } + + /** + * Sets the specified icon as the icon to use for rendering leaf nodes. + * + * @param icon to use for rendering leaf nodes + * + * @see JXTree#setLeafIcon(Icon) + */ + public void setLeafIcon(Icon icon) { + renderer.setLeafIcon(icon); + } + + /** + * Property to control whether per-tree icons should be + * copied to the renderer on setTreeCellRenderer.

+ * + * The default value is false. + * + * @param overwrite a boolean to indicate if the per-tree Icons should + * be copied to the new renderer on setTreeCellRenderer. + * + * @see #isOverwriteRendererIcons() + * @see #setLeafIcon(Icon) + * @see #setOpenIcon(Icon) + * @see #setClosedIcon(Icon) + * @see JXTree#setOverwriteRendererIcons(boolean) + */ + public void setOverwriteRendererIcons(boolean overwrite) { + renderer.setOverwriteRendererIcons(overwrite); + } + + + /** + * Returns a boolean indicating whether the per-tree icons should be + * copied to the renderer on setTreeCellRenderer. + * + * @return true if a TreeCellRenderer's icons will be overwritten with the + * tree's Icons, false if the renderer's icons will be unchanged. + * + * @see #setOverwriteRendererIcons(boolean) + * @see #setLeafIcon(Icon) + * @see #setOpenIcon(Icon) + * @see #setClosedIcon(Icon) + * @see JXTree#isOverwriteRendererIcons() + * + */ + public boolean isOverwriteRendererIcons() { + return renderer.isOverwriteRendererIcons(); + } + + /** + * Overridden to ensure that private renderer state is kept in sync with the + * state of the component. Calls the inherited version after performing the + * necessary synchronization. If you override this method, make sure you call + * this version from your version of this method. + */ + @Override + public void clearSelection() { + if (renderer != null) { + renderer.clearSelection(); + } + super.clearSelection(); + } + + /** + * Collapses all nodes in the treetable. + */ + public void collapseAll() { + renderer.collapseAll(); + } + + /** + * Expands all nodes in the treetable. + */ + public void expandAll() { + renderer.expandAll(); + } + + /** + * Collapses the node at the specified path in the treetable. + * + * @param path path of the node to collapse + */ + public void collapsePath(TreePath path) { + renderer.collapsePath(path); + } + + /** + * Expands the the node at the specified path in the treetable. + * + * @param path path of the node to expand + */ + public void expandPath(TreePath path) { + renderer.expandPath(path); + } + + /** + * Makes sure all the path components in path are expanded (except + * for the last path component) and scrolls so that the + * node identified by the path is displayed. Only works when this + * JTree is contained in a JScrollPane. + * + * (doc copied from JTree) + * + * PENDING: JW - where exactly do we want to scroll? Here: the scroll + * is in vertical direction only. Might need to show the tree column? + * + * @param path the TreePath identifying the node to + * bring into view + */ + public void scrollPathToVisible(TreePath path) { + renderer.scrollPathToVisible(path); +// if (path == null) return; +// renderer.makeVisible(path); +// int row = getRowForPath(path); +// scrollRowToVisible(row); + } + + + /** + * Collapses the row in the treetable. If the specified row index is + * not valid, this method will have no effect. + */ + public void collapseRow(int row) { + renderer.collapseRow(row); + } + + /** + * Expands the specified row in the treetable. If the specified row index is + * not valid, this method will have no effect. + */ + public void expandRow(int row) { + renderer.expandRow(row); + } + + + /** + * Returns true if the value identified by path is currently viewable, which + * means it is either the root or all of its parents are expanded. Otherwise, + * this method returns false. + * + * @return true, if the value identified by path is currently viewable; + * false, otherwise + */ + public boolean isVisible(TreePath path) { + return renderer.isVisible(path); + } + + /** + * Returns true if the node identified by path is currently expanded. + * Otherwise, this method returns false. + * + * @param path path + * @return true, if the value identified by path is currently expanded; + * false, otherwise + */ + public boolean isExpanded(TreePath path) { + return renderer.isExpanded(path); + } + + /** + * Returns true if the node at the specified display row is currently expanded. + * Otherwise, this method returns false. + * + * @param row row + * @return true, if the node at the specified display row is currently expanded. + * false, otherwise + */ + public boolean isExpanded(int row) { + return renderer.isExpanded(row); + } + + /** + * Returns true if the node identified by path is currently collapsed, + * this will return false if any of the values in path are currently not + * being displayed. + * + * @param path path + * @return true, if the value identified by path is currently collapsed; + * false, otherwise + */ + public boolean isCollapsed(TreePath path) { + return renderer.isCollapsed(path); + } + + /** + * Returns true if the node at the specified display row is collapsed. + * + * @param row row + * @return true, if the node at the specified display row is currently collapsed. + * false, otherwise + */ + public boolean isCollapsed(int row) { + return renderer.isCollapsed(row); + } + + + /** + * Returns an Enumeration of the descendants of the + * path parent that + * are currently expanded. If parent is not currently + * expanded, this will return null. + * If you expand/collapse nodes while + * iterating over the returned Enumeration + * this may not return all + * the expanded paths, or may return paths that are no longer expanded. + * + * @param parent the path which is to be examined + * @return an Enumeration of the descendents of + * parent, or null if + * parent is not currently expanded + */ + + public Enumeration getExpandedDescendants(TreePath parent) { + return renderer.getExpandedDescendants(parent); + } + + + /** + * Returns the TreePath for a given x,y location. + * + * @param x x value + * @param y y value + * + * @return the TreePath for the givern location. + */ + public TreePath getPathForLocation(int x, int y) { + int row = rowAtPoint(new Point(x,y)); + if (row == -1) { + return null; + } + return renderer.getPathForRow(row); + } + + /** + * Returns the TreePath for a given row. + * + * @param row + * + * @return the TreePath for the given row. + */ + public TreePath getPathForRow(int row) { + return renderer.getPathForRow(row); + } + + /** + * Returns the row for a given TreePath. + * + * @param path + * @return the row for the given TreePath. + */ + public int getRowForPath(TreePath path) { + return renderer.getRowForPath(path); + } + +//------------------------------ exposed Tree properties + + /** + * Determines whether or not the root node from the TreeModel is visible. + * + * @param visible true, if the root node is visible; false, otherwise + */ + public void setRootVisible(boolean visible) { + renderer.setRootVisible(visible); + // JW: the revalidate forces the root to appear after a + // toggling a visible from an initially invisible root. + // JTree fires a propertyChange on the ROOT_VISIBLE_PROPERTY + // BasicTreeUI reacts by (ultimately) calling JTree.treeDidChange + // which revalidate the tree part. + // Might consider to listen for the propertyChange (fired only if there + // actually was a change) instead of revalidating unconditionally. + revalidate(); + repaint(); + } + + /** + * Returns true if the root node of the tree is displayed. + * + * @return true if the root node of the tree is displayed + */ + public boolean isRootVisible() { + return renderer.isRootVisible(); + } + + + /** + * Sets the value of the scrollsOnExpand property for the tree + * part. This property specifies whether the expanded paths should be scrolled + * into view. In a look and feel in which a tree might not need to scroll + * when expanded, this property may be ignored. + * + * @param scroll true, if expanded paths should be scrolled into view; + * false, otherwise + */ + public void setScrollsOnExpand(boolean scroll) { + renderer.setScrollsOnExpand(scroll); + } + + /** + * Returns the value of the scrollsOnExpand property. + * + * @return the value of the scrollsOnExpand property + */ + public boolean getScrollsOnExpand() { + return renderer.getScrollsOnExpand(); + } + + /** + * Sets the value of the showsRootHandles property for the tree + * part. This property specifies whether the node handles should be displayed. + * If handles are not supported by a particular look and feel, this property + * may be ignored. + * + * @param visible true, if root handles should be shown; false, otherwise + */ + public void setShowsRootHandles(boolean visible) { + renderer.setShowsRootHandles(visible); + repaint(); + } + + /** + * Returns the value of the showsRootHandles property. + * + * @return the value of the showsRootHandles property + */ + public boolean getShowsRootHandles() { + return renderer.getShowsRootHandles(); + } + + /** + * Sets the value of the expandsSelectedPaths property for the tree + * part. This property specifies whether the selected paths should be expanded. + * + * @param expand true, if selected paths should be expanded; false, otherwise + */ + public void setExpandsSelectedPaths(boolean expand) { + renderer.setExpandsSelectedPaths(expand); + } + + /** + * Returns the value of the expandsSelectedPaths property. + * + * @return the value of the expandsSelectedPaths property + */ + public boolean getExpandsSelectedPaths() { + return renderer.getExpandsSelectedPaths(); + } + + + /** + * Returns the number of mouse clicks needed to expand or close a node. + * + * @return number of mouse clicks before node is expanded + */ + public int getToggleClickCount() { + return renderer.getToggleClickCount(); + } + + /** + * Sets the number of mouse clicks before a node will expand or close. + * The default is two. + * + * @param clickCount the number of clicks required to expand/collapse a node. + */ + public void setToggleClickCount(int clickCount) { + renderer.setToggleClickCount(clickCount); + } + + /** + * Returns true if the tree is configured for a large model. + * The default value is false. + * + * @return true if a large model is suggested + * @see #setLargeModel + */ + public boolean isLargeModel() { + return renderer.isLargeModel(); + } + + /** + * Specifies whether the UI should use a large model. + * (Not all UIs will implement this.)

+ * + * NOTE: this method is exposed for completeness - + * currently it's not recommended + * to use a large model because there are some issues + * (not yet fully understood), namely + * issue #25-swingx, and probably #270-swingx. + * + * @param newValue true to suggest a large model to the UI + */ + public void setLargeModel(boolean newValue) { + renderer.setLargeModel(newValue); + // JW: random method calling ... doesn't help +// renderer.treeDidChange(); +// revalidate(); +// repaint(); + } + +//------------------------------ exposed tree listeners + + /** + * Adds a listener for TreeExpansion events. + * + * @param tel a TreeExpansionListener that will be notified + * when a tree node is expanded or collapsed + */ + public void addTreeExpansionListener(TreeExpansionListener tel) { + getTreeExpansionBroadcaster().addTreeExpansionListener(tel); + } + + /** + * @return + */ + private TreeExpansionBroadcaster getTreeExpansionBroadcaster() { + if (treeExpansionBroadcaster == null) { + treeExpansionBroadcaster = new TreeExpansionBroadcaster(this); + renderer.addTreeExpansionListener(treeExpansionBroadcaster); + } + return treeExpansionBroadcaster; + } + + /** + * Removes a listener for TreeExpansion events. + * @param tel the TreeExpansionListener to remove + */ + public void removeTreeExpansionListener(TreeExpansionListener tel) { + if (treeExpansionBroadcaster == null) return; + treeExpansionBroadcaster.removeTreeExpansionListener(tel); + } + + /** + * Adds a listener for TreeSelection events. + * TODO (JW): redirect event source to this. + * + * @param tsl a TreeSelectionListener that will be notified + * when a tree node is selected or deselected + */ + public void addTreeSelectionListener(TreeSelectionListener tsl) { + renderer.addTreeSelectionListener(tsl); + } + + /** + * Removes a listener for TreeSelection events. + * @param tsl the TreeSelectionListener to remove + */ + public void removeTreeSelectionListener(TreeSelectionListener tsl) { + renderer.removeTreeSelectionListener(tsl); + } + + /** + * Adds a listener for TreeWillExpand events. + * TODO (JW): redirect event source to this. + * + * @param tel a TreeWillExpandListener that will be notified + * when a tree node will be expanded or collapsed + */ + public void addTreeWillExpandListener(TreeWillExpandListener tel) { + renderer.addTreeWillExpandListener(tel); + } + + /** + * Removes a listener for TreeWillExpand events. + * @param tel the TreeWillExpandListener to remove + */ + public void removeTreeWillExpandListener(TreeWillExpandListener tel) { + renderer.removeTreeWillExpandListener(tel); + } + + + /** + * Returns the selection model for the tree portion of the this treetable. + * + * @return selection model for the tree portion of the this treetable + */ + public TreeSelectionModel getTreeSelectionModel() { + return renderer.getSelectionModel(); // RG: Fix JDNC issue 41 + } + + /** + * Overriden to invoke supers implementation, and then, + * if the receiver is editing a Tree column, the editors bounds is + * reset. The reason we have to do this is because JTable doesn't + * think the table is being edited, as getEditingRow returns + * -1, and therefore doesn't automaticly resize the editor for us. + */ + @Override + public void sizeColumnsToFit(int resizingColumn) { + /** TODO: Review wrt doLayout() */ + super.sizeColumnsToFit(resizingColumn); + // rg:changed + if (getEditingColumn() != -1 && isHierarchical(editingColumn)) { + Rectangle cellRect = getCellRect(realEditingRow(), + getEditingColumn(), false); + Component component = getEditorComponent(); + component.setBounds(cellRect); + component.validate(); + } + } + + + /** + * Determines if the specified column is defined as the hierarchical column. + * + * @param column + * zero-based index of the column in view coordinates + * @return true if the column is the hierarchical column; false otherwise. + * @throws IllegalArgumentException + * if the column is less than 0 or greater than or equal to the + * column count + */ + public boolean isHierarchical(int column) { + if (column < 0 || column >= getColumnCount()) { + throw new IllegalArgumentException("column must be valid, was" + column); + } + + return (getHierarchicalColumn() == column); + } + + /** + * Returns the index of the hierarchical column. This is the column that is + * displayed as the tree. + * + * @return the index of the hierarchical column, -1 if there is + * no hierarchical column + * + */ + public int getHierarchicalColumn() { + return convertColumnIndexToView(((TreeTableModel) renderer.getModel()).getHierarchicalColumn()); + } + + /** + * {@inheritDoc} + */ + @Override + public TableCellRenderer getCellRenderer(int row, int column) { + if (isHierarchical(column)) { + return renderer; + } + + return super.getCellRenderer(row, column); + } + + /** + * {@inheritDoc} + */ + @Override + public TableCellEditor getCellEditor(int row, int column) { + if (isHierarchical(column)) { + return hierarchicalEditor; + } + + return super.getCellEditor(row, column); + } + + @Override + public void updateUI() { + super.updateUI(); + updateHierarchicalRendererEditor(); + } + + /** + * Updates Ui of renderer/editor for the hierarchical column. Need to do so + * manually, as not accessible by the default lookup. + */ + protected void updateHierarchicalRendererEditor() { + if (renderer != null) { + SwingUtilities.updateComponentTreeUI(renderer); + } + } + + /** + * {@inheritDoc}

+ * + * Overridden to message the tree directly if the column is the view index of + * the hierarchical column.

+ * + * PENDING JW: revisit once we switch to really using a table renderer. As is, it's + * a quick fix for #821-swingx: string rep for hierarchical column incorrect. + */ + @Override + public String getStringAt(int row, int column) { + if (isHierarchical(column)) { + return getHierarchicalStringAt(row); + } + return super.getStringAt(row, column); + } + + /** + * Returns the String representation of the hierarchical column at the given + * row.

+ * + * @param row the row index in view coordinates + * @return the string representation of the hierarchical column at the given row. + * + * @see #getStringAt(int, int) + */ + private String getHierarchicalStringAt(int row) { + return renderer.getStringAt(row); + } + + /** + * ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel + * to listen for changes in the ListSelectionModel it maintains. Once + * a change in the ListSelectionModel happens, the paths are updated + * in the DefaultTreeSelectionModel. + */ + class ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel { + /** Set to true when we are updating the ListSelectionModel. */ + protected boolean updatingListSelectionModel; + + public ListToTreeSelectionModelWrapper() { + super(); + getListSelectionModel().addListSelectionListener + (createListSelectionListener()); + } + + /** + * Returns the list selection model. ListToTreeSelectionModelWrapper + * listens for changes to this model and updates the selected paths + * accordingly. + */ + ListSelectionModel getListSelectionModel() { + return listSelectionModel; + } + + /** + * This is overridden to set updatingListSelectionModel + * and message super. This is the only place DefaultTreeSelectionModel + * alters the ListSelectionModel. + */ + @Override + public void resetRowSelection() { + if (!updatingListSelectionModel) { + updatingListSelectionModel = true; + try { + super.resetRowSelection(); + } + finally { + updatingListSelectionModel = false; + } + } + // Notice how we don't message super if + // updatingListSelectionModel is true. If + // updatingListSelectionModel is true, it implies the + // ListSelectionModel has already been updated and the + // paths are the only thing that needs to be updated. + } + + /** + * Creates and returns an instance of ListSelectionHandler. + */ + protected ListSelectionListener createListSelectionListener() { + return new ListSelectionHandler(); + } + + /** + * If updatingListSelectionModel is false, this will + * reset the selected paths from the selected rows in the list + * selection model. + */ + protected void updateSelectedPathsFromSelectedRows() { + if (!updatingListSelectionModel) { + updatingListSelectionModel = true; + try { + if (listSelectionModel.isSelectionEmpty()) { + clearSelection(); + } else { + // This is way expensive, ListSelectionModel needs an + // enumerator for iterating. + int min = listSelectionModel.getMinSelectionIndex(); + int max = listSelectionModel.getMaxSelectionIndex(); + + List paths = new ArrayList(); + for (int counter = min; counter <= max; counter++) { + if (listSelectionModel.isSelectedIndex(counter)) { + TreePath selPath = renderer.getPathForRow( + counter); + + if (selPath != null) { + paths.add(selPath); + } + } + } + setSelectionPaths(paths.toArray(new TreePath[paths.size()])); + // need to force here: usually the leadRow is adjusted + // in resetRowSelection which is disabled during this method + leadRow = leadIndex; + } + } + finally { + updatingListSelectionModel = false; + } + } + } + + /** + * Class responsible for calling updateSelectedPathsFromSelectedRows + * when the selection of the list changse. + */ + class ListSelectionHandler implements ListSelectionListener { + @Override + public void valueChanged(ListSelectionEvent e) { + if (!e.getValueIsAdjusting()) { + updateSelectedPathsFromSelectedRows(); + } + } + } + } + + /** + * + */ + protected static class TreeTableModelAdapter extends AbstractTableModel + implements TreeTableModelProvider { + private TreeModelListener treeModelListener; + private final JTree tree; // immutable + private JXTreeTable treeTable; // logically immutable + + /** + * Maintains a TreeTableModel and a JTree as purely implementation details. + * Developers can plug in any type of custom TreeTableModel through a + * JXTreeTable constructor or through setTreeTableModel(). + * + * @param tree TreeTableCellRenderer instantiated with the same model as + * the driving JXTreeTable's TreeTableModel. + * @throws IllegalArgumentException if a null tree argument is passed + */ + TreeTableModelAdapter(JTree tree) { + Contract.asNotNull(tree, "tree must not be null"); + + this.tree = tree; // need tree to implement getRowCount() + tree.getModel().addTreeModelListener(getTreeModelListener()); + tree.addTreeExpansionListener(new TreeExpansionListener() { + // Don't use fireTableRowsInserted() here; the selection model + // would get updated twice. + @Override + public void treeExpanded(TreeExpansionEvent event) { + updateAfterExpansionEvent(event); + } + + @Override + public void treeCollapsed(TreeExpansionEvent event) { + updateAfterExpansionEvent(event); + } + }); + tree.addPropertyChangeListener("model", new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + TreeTableModel model = (TreeTableModel) evt.getOldValue(); + model.removeTreeModelListener(getTreeModelListener()); + + model = (TreeTableModel) evt.getNewValue(); + model.addTreeModelListener(getTreeModelListener()); + + fireTableStructureChanged(); + } + }); + } + + /** + * updates the table after having received an TreeExpansionEvent.

+ * + * @param event the TreeExpansionEvent which triggered the method call. + */ + protected void updateAfterExpansionEvent(TreeExpansionEvent event) { + // moved to let the renderer handle directly +// treeTable.getTreeTableHacker().setExpansionChangedFlag(); + // JW: delayed fire leads to a certain sluggishness occasionally? + fireTableDataChanged(); + } + + /** + * Returns the JXTreeTable instance to which this TreeTableModelAdapter is + * permanently and exclusively bound. For use by + * {@link JXTreeTable#setModel(TableModel)}. + * + * @return JXTreeTable to which this TreeTableModelAdapter is permanently bound + */ + protected JXTreeTable getTreeTable() { + return treeTable; + } + + /** + * Immutably binds this TreeTableModelAdapter to the specified JXTreeTable. + * + * @param treeTable the JXTreeTable instance that this adapter is bound to. + */ + protected final void bind(JXTreeTable treeTable) { + // Suppress potentially subversive invocation! + // Prevent clearing out the deck for possible hijack attempt later! + if (treeTable == null) { + throw new IllegalArgumentException("null treeTable"); + } + + if (this.treeTable == null) { + this.treeTable = treeTable; + } + else { + throw new IllegalArgumentException("adapter already bound"); + } + } + + /** + * + * @inherited

+ * + * Implemented to return the the underlying TreeTableModel. + */ + @Override + public TreeTableModel getTreeTableModel() { + return (TreeTableModel) tree.getModel(); + } + + // Wrappers, implementing TableModel interface. + // TableModelListener management provided by AbstractTableModel superclass. + + @Override + public Class getColumnClass(int column) { + return getTreeTableModel().getColumnClass(column); + } + + @Override + public int getColumnCount() { + return getTreeTableModel().getColumnCount(); + } + + @Override + public String getColumnName(int column) { + return getTreeTableModel().getColumnName(column); + } + + @Override + public int getRowCount() { + return tree.getRowCount(); + } + + @Override + public Object getValueAt(int row, int column) { + // Issue #270-swingx: guard against invisible row + Object node = nodeForRow(row); + return node != null ? getTreeTableModel().getValueAt(node, column) : null; + } + + @Override + public boolean isCellEditable(int row, int column) { + // Issue #270-swingx: guard against invisible row + Object node = nodeForRow(row); + return node != null ? getTreeTableModel().isCellEditable(node, column) : false; + } + + @Override + public void setValueAt(Object value, int row, int column) { + // Issue #270-swingx: guard against invisible row + Object node = nodeForRow(row); + if (node != null) { + getTreeTableModel().setValueAt(value, node, column); + } + } + + protected Object nodeForRow(int row) { + // Issue #270-swingx: guard against invisible row + TreePath path = tree.getPathForRow(row); + return path != null ? path.getLastPathComponent() : null; + } + + /** + * @return TreeModelListener + */ + private TreeModelListener getTreeModelListener() { + if (treeModelListener == null) { + treeModelListener = new TreeModelListener() { + + @Override + public void treeNodesChanged(TreeModelEvent e) { +// LOG.info("got tree event: changed " + e); + delayedFireTableDataUpdated(e); + } + + // We use delayedFireTableDataChanged as we can + // not be guaranteed the tree will have finished processing + // the event before us. + @Override + public void treeNodesInserted(TreeModelEvent e) { + delayedFireTableDataChanged(e, 1); + } + + @Override + public void treeNodesRemoved(TreeModelEvent e) { +// LOG.info("got tree event: removed " + e); + delayedFireTableDataChanged(e, 2); + } + + @Override + public void treeStructureChanged(TreeModelEvent e) { + // ?? should be mapped to structureChanged -- JW + if (isTableStructureChanged(e)) { + delayedFireTableStructureChanged(); + } else { + delayedFireTableDataChanged(); + } + } + }; + } + + return treeModelListener; + } + + /** + * Decides if the given treeModel structureChanged should + * trigger a table structureChanged. Returns true if the + * source path is the root or null, false otherwise.

+ * + * PENDING: need to refine? "Marker" in Event-Object? + * + * @param e the TreeModelEvent received in the treeModelListener's + * treeStructureChanged + * @return a boolean indicating whether the given TreeModelEvent + * should trigger a structureChanged. + */ + private boolean isTableStructureChanged(TreeModelEvent e) { + if ((e.getTreePath() == null) || + (e.getTreePath().getParentPath() == null)) return true; + return false; + } + + /** + * Invokes fireTableDataChanged after all the pending events have been + * processed. SwingUtilities.invokeLater is used to handle this. + */ + private void delayedFireTableStructureChanged() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + fireTableStructureChanged(); + } + }); + } + + /** + * Invokes fireTableDataChanged after all the pending events have been + * processed. SwingUtilities.invokeLater is used to handle this. + */ + private void delayedFireTableDataChanged() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + fireTableDataChanged(); + } + }); + } + + /** + * Invokes fireTableDataChanged after all the pending events have been + * processed. SwingUtilities.invokeLater is used to handle this. + * Allowed event types: 1 for insert, 2 for delete + */ + private void delayedFireTableDataChanged(final TreeModelEvent tme, final int typeChange) { + if ((typeChange < 1 ) || (typeChange > 2)) + throw new IllegalArgumentException("Event type must be 1 or 2, was " + typeChange); + // expansion state before invoke may be different + // from expansion state in invoke + final boolean expanded = tree.isExpanded(tme.getTreePath()); + // quick test if tree throws for unrelated path. Seems like not. +// tree.getRowForPath(new TreePath("dummy")); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + int indices[] = tme.getChildIndices(); + TreePath path = tme.getTreePath(); + // quick test to see if bailing out is an option +// if (false) { + if (indices != null) { + if (expanded) { // Dont bother to update if the parent + // node is collapsed + // indices must in ascending order, as per TreeEvent/Listener doc + int min = indices[0]; + int max = indices[indices.length - 1]; + int startingRow = tree.getRowForPath(path) + 1; + min = startingRow + min; + max = startingRow + max; + switch (typeChange) { + case 1: +// LOG.info("rows inserted: path " + path + "/" + min + "/" +// + max); + fireTableRowsInserted(min, max); + break; + case 2: +// LOG.info("rows deleted path " + path + "/" + min + "/" +// + max); + fireTableRowsDeleted(min, max); + break; + } + } else { + // not expanded - but change might effect appearance + // of parent + // Issue #82-swingx + int row = tree.getRowForPath(path); + // fix Issue #247-swingx: prevent accidental + // structureChanged + // for collapsed path + // in this case row == -1, which == + // TableEvent.HEADER_ROW + if (row >= 0) + fireTableRowsUpdated(row, row); + } + } else { // case where the event is fired to identify + // root. + fireTableDataChanged(); + } + } + }); + } + + /** + * This is used for updated only. PENDING: not necessary to delay? + * Updates are never structural changes which are the critical. + * + * @param tme + */ + protected void delayedFireTableDataUpdated(final TreeModelEvent tme) { + final boolean expanded = tree.isExpanded(tme.getTreePath()); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + int indices[] = tme.getChildIndices(); + TreePath path = tme.getTreePath(); + if (indices != null) { + if (expanded) { // Dont bother to update if the parent + // node is collapsed + Object children[] = tme.getChildren(); + // can we be sure that children.length > 0? + // int min = tree.getRowForPath(path.pathByAddingChild(children[0])); + // int max = tree.getRowForPath(path.pathByAddingChild(children[children.length -1])); + int min = Integer.MAX_VALUE; + int max = Integer.MIN_VALUE; + for (int i = 0; i < indices.length; i++) { + Object child = children[i]; + TreePath childPath = path + .pathByAddingChild(child); + int index = tree.getRowForPath(childPath); + if (index < min) { + min = index; + } + if (index > max) { + max = index; + } + } +// LOG.info("Updated: parentPath/min/max" + path + "/" + min + "/" + max); + // JW: the index is occasionally - 1 - need further digging + fireTableRowsUpdated(Math.max(0, min), Math.max(0, max)); + } else { + // not expanded - but change might effect appearance + // of parent Issue #82-swingx + int row = tree.getRowForPath(path); + // fix Issue #247-swingx: prevent accidental structureChanged + // for collapsed path in this case row == -1, + // which == TableEvent.HEADER_ROW + if (row >= 0) + fireTableRowsUpdated(row, row); + } + } else { // case where the event is fired to identify + // root. + fireTableDataChanged(); + } + } + }); + + } + + } + + static class TreeTableCellRenderer extends JXTree implements + TableCellRenderer + // need to implement RolloverRenderer + // PENDING JW: method name clash rolloverRenderer.isEnabled and + // component.isEnabled .. don't extend, use? And change + // the method name in rolloverRenderer? + // commented - so doesn't show the rollover cursor. + // +// , RolloverRenderer + { + private PropertyChangeListener rolloverListener; + private Border cellBorder; + + // Force user to specify TreeTableModel instead of more general + // TreeModel + public TreeTableCellRenderer(TreeTableModel model) { + super(model); + putClientProperty("JTree.lineStyle", "None"); + setRootVisible(false); // superclass default is "true" + setShowsRootHandles(true); // superclass default is "false" + /** + * TODO: Support truncated text directly in + * DefaultTreeCellRenderer. + */ + // removed as fix for #769-swingx: defaults for treetable should be same as tree +// setOverwriteRendererIcons(true); +// setCellRenderer(new DefaultTreeRenderer()); + setCellRenderer(new ClippedTreeCellRenderer()); + } + + + /** + * {@inheritDoc}

+ * + * Overridden to hack around #766-swingx: cursor flickering in DnD + * when dragging over tree column. This is a core bug (#6700748) related + * to painting the rendering component on a CellRendererPane. A trick + * around is to let this return false.

+ * + * This implementation applies the trick, that is returns false always. + * The hack can be disabled by setting the treeTable's client property + * DROP_HACK_FLAG_KEY to Boolean.FALSE. + * + */ + @Override + public boolean isVisible() { + return shouldApplyDropHack() ? false : super.isVisible(); + } + + + /** + * Returns a boolean indicating whether the drop hack should be applied. + * + * @return a boolean indicating whether the drop hack should be applied. + */ + protected boolean shouldApplyDropHack() { + return !Boolean.FALSE.equals(treeTable.getClientProperty(DROP_HACK_FLAG_KEY)); + } + + + /** + * Hack around #297-swingx: tooltips shown at wrong row. + * + * The problem is that - due to much tricksery when rendering the tree - + * the given coordinates are rather useless. As a consequence, super + * maps to wrong coordinates. This takes over completely. + * + * PENDING: bidi? + * + * @param event the mouseEvent in treetable coordinates + * @param row the view row index + * @param column the view column index + * @return the tooltip as appropriate for the given row + */ + private String getToolTipText(MouseEvent event, int row, int column) { + if (row < 0) return null; + String toolTip = null; + TreeCellRenderer renderer = getCellRenderer(); + TreePath path = getPathForRow(row); + Object lastPath = path.getLastPathComponent(); + Component rComponent = renderer.getTreeCellRendererComponent + (this, lastPath, isRowSelected(row), + isExpanded(row), getModel().isLeaf(lastPath), row, + true); + + if(rComponent instanceof JComponent) { + Rectangle pathBounds = getPathBounds(path); + Rectangle cellRect = treeTable.getCellRect(row, column, false); + // JW: what we are after + // is the offset into the hierarchical column + // then intersect this with the pathbounds + Point mousePoint = event.getPoint(); + // translate to coordinates relative to cell + mousePoint.translate(-cellRect.x, -cellRect.y); + // translate horizontally to + mousePoint.translate(-pathBounds.x, 0); + // show tooltip only if over renderer? +// if (mousePoint.x < 0) return null; +// p.translate(-pathBounds.x, -pathBounds.y); + MouseEvent newEvent = new MouseEvent(rComponent, event.getID(), + event.getWhen(), + event.getModifiers(), + mousePoint.x, + mousePoint.y, +// p.x, p.y, + event.getClickCount(), + event.isPopupTrigger()); + + toolTip = ((JComponent)rComponent).getToolTipText(newEvent); + } + if (toolTip != null) { + return toolTip; + } + return getToolTipText(); + } + + /** + * {@inheritDoc}

+ * + * Overridden to not automatically de/register itself from/to the ToolTipManager. + * As rendering component it is not considered to be active in any way, so the + * manager must not listen. + */ + @Override + public void setToolTipText(String text) { + putClientProperty(TOOL_TIP_TEXT_KEY, text); + } + + /** + * Immutably binds this TreeTableModelAdapter to the specified JXTreeTable. + * For internal use by JXTreeTable only. + * + * @param treeTable the JXTreeTable instance that this renderer is bound to + */ + public final void bind(JXTreeTable treeTable) { + // Suppress potentially subversive invocation! + // Prevent clearing out the deck for possible hijack attempt later! + if (treeTable == null) { + throw new IllegalArgumentException("null treeTable"); + } + + if (this.treeTable == null) { + this.treeTable = treeTable; + // commented because still has issus +// bindRollover(); + } + else { + throw new IllegalArgumentException("renderer already bound"); + } + } + + /** + * Install rollover support. + * Not used - still has issues. + * - not bidi-compliant + * - no coordinate transformation for hierarchical column != 0 + * - method name clash enabled + * - keyboard triggered click unreliable (triggers the treetable) + * ... + */ + @SuppressWarnings("unused") + private void bindRollover() { + setRolloverEnabled(treeTable.isRolloverEnabled()); + treeTable.addPropertyChangeListener(getRolloverListener()); + } + + + /** + * @return + */ + private PropertyChangeListener getRolloverListener() { + if (rolloverListener == null) { + rolloverListener = createRolloverListener(); + } + return rolloverListener; + } + + /** + * Creates and returns a property change listener for + * table's rollover related properties. + * + * This implementation + * - Synchs the tree's rolloverEnabled + * - maps rollover cell from the table to the cell + * (still incomplete: first column only) + * + * @return + */ + protected PropertyChangeListener createRolloverListener() { + PropertyChangeListener l = new PropertyChangeListener() { + + @Override + public void propertyChange(PropertyChangeEvent evt) { + if ((treeTable == null) || (treeTable != evt.getSource())) + return; + if ("rolloverEnabled".equals(evt.getPropertyName())) { + setRolloverEnabled(((Boolean) evt.getNewValue()).booleanValue()); + } + if (RolloverProducer.ROLLOVER_KEY.equals(evt.getPropertyName())){ + rollover(evt); + } + } + + private void rollover(PropertyChangeEvent evt) { + boolean isHierarchical = isHierarchical((Point)evt.getNewValue()); + putClientProperty(evt.getPropertyName(), isHierarchical ? + new Point((Point) evt.getNewValue()) : null); + } + + private boolean isHierarchical(Point point) { + if (point != null) { + int column = point.x; + if (column >= 0) { + return treeTable.isHierarchical(column); + } + } + return false; + } + @SuppressWarnings("unused") + Point rollover = new Point(-1, -1); + }; + return l; + } + + /** + * {@inheritDoc}

+ * + * Overridden to produce clicked client props only. The + * rollover are produced by a propertyChangeListener to + * the table's corresponding prop. + * + */ + @Override + protected RolloverProducer createRolloverProducer() { + return new RolloverProducer() { + + /** + * Overridden to do nothing. + * + * @param e + * @param property + */ + @Override + protected void updateRollover(MouseEvent e, String property, boolean fireAlways) { + if (CLICKED_KEY.equals(property)) { + super.updateRollover(e, property, fireAlways); + } + } + @Override + protected void updateRolloverPoint(JComponent component, + Point mousePoint) { + JXTree tree = (JXTree) component; + int row = tree.getClosestRowForLocation(mousePoint.x, mousePoint.y); + Rectangle bounds = tree.getRowBounds(row); + if (bounds == null) { + row = -1; + } else { + if ((bounds.y + bounds.height < mousePoint.y) || + bounds.x > mousePoint.x) { + row = -1; + } + } + int col = row < 0 ? -1 : 0; + rollover.x = col; + rollover.y = row; + } + + }; + } + + + @Override + public void scrollRectToVisible(Rectangle aRect) { + treeTable.scrollRectToVisible(aRect); + } + + @Override + protected void setExpandedState(TreePath path, boolean state) { + // JW: fix for #1126 - CellEditors are removed immediately after starting an + // edit if they involve a change of selection and the + // expandsOnSelection property is true + // back out if the selection change does not cause a change in + // expansion state + if (isExpanded(path) == state) return; + // on change of expansion state, the editor's row might be changed + // for simplicity, it's stopped always (even if the row is not changed) + treeTable.getTreeTableHacker().completeEditing(); + super.setExpandedState(path, state); + treeTable.getTreeTableHacker().expansionChanged(); + + } + + /** + * updateUI is overridden to set the colors of the Tree's renderer + * to match that of the table. + */ + @Override + public void updateUI() { + super.updateUI(); + // Make the tree's cell renderer use the table's cell selection + // colors. + // TODO JW: need to revisit... + // a) the "real" of a JXTree is always wrapped into a DelegatingRenderer + // consequently the if-block never executes + // b) even if it does it probably (?) should not + // unconditionally overwrite custom selection colors. + // Check for UIResources instead. + TreeCellRenderer tcr = getCellRenderer(); + if (tcr instanceof DefaultTreeCellRenderer) { + DefaultTreeCellRenderer dtcr = ((DefaultTreeCellRenderer) tcr); + // For 1.1 uncomment this, 1.2 has a bug that will cause an + // exception to be thrown if the border selection color is null. + dtcr.setBorderSelectionColor(null); + dtcr.setTextSelectionColor( + UIManager.getColor("Table.selectionForeground")); + dtcr.setBackgroundSelectionColor( + UIManager.getColor("Table.selectionBackground")); + } + } + + /** + * Sets the row height of the tree, and forwards the row height to + * the table. + * + * + */ + @Override + public void setRowHeight(int rowHeight) { + // JW: can't ... updateUI invoked with rowHeight = 0 + // hmmm... looks fishy ... +// if (rowHeight <= 0) throw +// new IllegalArgumentException("the rendering tree must have a fixed rowHeight > 0"); + super.setRowHeight(rowHeight); + if (rowHeight > 0) { + if (treeTable != null) { + treeTable.adjustTableRowHeight(rowHeight); + } + } + } + + + /** + * This is overridden to set the location to (0, 0) and set + * the dimension to exactly fill the bounds of the hierarchical + * column.

+ */ + @Override + public void setBounds(int x, int y, int w, int h) { + // location is relative to the hierarchical column + y = 0; + x = 0; + if (treeTable != null) { + // adjust height to table height + // It is not enough to set the height to treeTable.getHeight() + // JW: why not? + h = treeTable.getRowCount() * this.getRowHeight(); + int hierarchicalC = treeTable.getHierarchicalColumn(); + // JW: re-introduced to fix Issue 1168-swingx + if (hierarchicalC >= 0) { + TableColumn column = treeTable.getColumn(hierarchicalC); + // adjust width to width of hierarchical column + w = column.getWidth(); + } + } + super.setBounds(x, y, w, h); + } + + /** + * Sublcassed to translate the graphics such that the last visible row + * will be drawn at 0,0. + */ + @Override + public void paint(Graphics g) { + Rectangle cellRect = treeTable.getCellRect(visibleRow, 0, false); + g.translate(0, -cellRect.y); + + hierarchicalColumnWidth = getWidth(); + super.paint(g); + + Border border = cellBorder; + if (highlightBorder != null) { + border = highlightBorder; + } + // Draw the Table border if we have focus. + if (border != null) { + // #170: border not drawn correctly + // JW: position the border to be drawn in translated area + // still not satifying in all cases... + // RG: Now it satisfies (at least for the row margins) + // Still need to make similar adjustments for column margins... + border.paintBorder(this, g, 0, cellRect.y, + getWidth(), cellRect.height); + } + } + + /** + * {@inheritDoc}

+ * + * Overridden to fix #swingx-1525: BorderHighlighter fills tree column.

+ * + * Basically, the reason was that the border is set on the tree as a whole + * instead of on the cell level. The fix is to bypass super completely, keep + * a reference to the cell border and manually paint it around the cell + * in the overridden paint.

+ * + * Note: in the paint we need to paint either the focus border or the + * cellBorder, the former taking precedence. + * + */ + @Override + public void setBorder(Border border) { + cellBorder = border; + } + + + public void doClick() { + if ((getCellRenderer() instanceof RolloverRenderer) + && ((RolloverRenderer) getCellRenderer()).isEnabled()) { + ((RolloverRenderer) getCellRenderer()).doClick(); + } + + } + + + @Override + public boolean isRowSelected(int row) { + if ((treeTable == null) || (treeTable.getHierarchicalColumn() <0)) return false; + return treeTable.isCellSelected(row, treeTable.getHierarchicalColumn()); + } + + + @Override + public Component getTableCellRendererComponent(JTable table, + Object value, + boolean isSelected, boolean hasFocus, int row, int column) { + assert table == treeTable; + // JW: quick fix for the tooltip part of #794-swingx: + // visual properties must be reset in each cycle. + // reverted - otherwise tooltip per Highlighter doesn't work + // +// setToolTipText(null); + + if (isSelected) { + setBackground(table.getSelectionBackground()); + setForeground(table.getSelectionForeground()); + } + else { + setBackground(table.getBackground()); + setForeground(table.getForeground()); + } + + highlightBorder = null; + if (treeTable != null) { + if (treeTable.realEditingRow() == row && + treeTable.getEditingColumn() == column) { + } + else if (hasFocus) { + highlightBorder = UIManager.getBorder( + "Table.focusCellHighlightBorder"); + } + } + + visibleRow = row; + + return this; + } + + private class ClippedTreeCellRenderer extends DefaultXTreeCellRenderer + implements StringValue + { + @SuppressWarnings("unused") + private boolean inpainting; + private String shortText; + @Override + public void paint(Graphics g) { + String fullText = super.getText(); + + shortText = SwingUtilities.layoutCompoundLabel( + this, g.getFontMetrics(), fullText, getIcon(), + getVerticalAlignment(), getHorizontalAlignment(), + getVerticalTextPosition(), getHorizontalTextPosition(), + getItemRect(itemRect), iconRect, textRect, + getIconTextGap()); + + /** TODO: setText is more heavyweight than we want in this + * situation. Make JLabel.text protected instead of private. + */ + + try { + inpainting = true; + // TODO JW: don't - override getText to return the short version + // during painting + setText(shortText); // temporarily truncate text + super.paint(g); + } finally { + inpainting = false; + setText(fullText); // restore full text + } + } + + + private Rectangle getItemRect(Rectangle itemRect) { + getBounds(itemRect); +// LOG.info("rect" + itemRect); + itemRect.width = hierarchicalColumnWidth - itemRect.x; + return itemRect; + } + + @Override + public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { + return super.getTreeCellRendererComponent(tree, getHierarchicalTableValue(value), sel, expanded, leaf, + row, hasFocus); + } + + + /** + * + * @param node the node in the treeModel as passed into the TreeCellRenderer + * @return the corresponding value of the hierarchical cell in the TreeTableModel + */ + private Object getHierarchicalTableValue(Object node) { + Object val = node; + + if (treeTable != null) { + int treeColumn = treeTable.getTreeTableModel().getHierarchicalColumn(); + Object o = null; + if (treeColumn >= 0) { + // following is unreliable during a paint cycle + // somehow interferes with BasicTreeUIs painting cache +// o = treeTable.getValueAt(row, treeColumn); + // ask the model - that's always okay + // might blow if the TreeTableModel is strict in + // checking the containment of the value and + // this renderer is called for sizing with a prototype + o = treeTable.getTreeTableModel().getValueAt(node, treeColumn); + } + val = o; + } + return val; + } + + /** + * {@inheritDoc}

+ */ + @Override + public String getString(Object node) { +// int treeColumn = treeTable.getTreeTableModel().getHierarchicalColumn(); +// if (treeColumn >= 0) { +// return StringValues.TO_STRING.getString(treeTable.getTreeTableModel().getValueAt(value, treeColumn)); +// } + return StringValues.TO_STRING.getString(getHierarchicalTableValue(node)); + } + + // Rectangles filled in by SwingUtilities.layoutCompoundLabel(); + private final Rectangle iconRect = new Rectangle(); + private final Rectangle textRect = new Rectangle(); + // Rectangle filled in by this.getItemRect(); + private final Rectangle itemRect = new Rectangle(); + } + + /** Border to draw around the tree, if this is non-null, it will + * be painted. */ + protected Border highlightBorder = null; + protected JXTreeTable treeTable = null; + protected int visibleRow = 0; + + // A JXTreeTable may not have more than one hierarchical column + private int hierarchicalColumnWidth = 0; + + } + + /** + * Returns the adapter that knows how to access the component data model. + * The component data adapter is used by filters, sorters, and highlighters. + * + * @return the adapter that knows how to access the component data model + */ + @Override + protected ComponentAdapter getComponentAdapter() { + if (dataAdapter == null) { + dataAdapter = new TreeTableDataAdapter(this); + } + return dataAdapter; + } + + + protected static class TreeTableDataAdapter extends TableAdapter { + private final JXTreeTable table; + + /** + * Constructs a TreeTableDataAdapter for the specified + * target component. + * + * @param component the target component + */ + public TreeTableDataAdapter(JXTreeTable component) { + super(component); + table = component; + } + + public JXTreeTable getTreeTable() { + return table; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isExpanded() { + return table.isExpanded(row); + } + + /** + * {@inheritDoc} + */ + @Override + public int getDepth() { + return table.getPathForRow(row).getPathCount() - 1; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isLeaf() { + // Issue #270-swingx: guard against invisible row + TreePath path = table.getPathForRow(row); + if (path != null) { + return table.getTreeTableModel().isLeaf(path.getLastPathComponent()); + } + // JW: this is the same as BasicTreeUI.isLeaf. + // Shouldn't happen anyway because must be called for visible rows only. + return true; + } + /** + * + * @return true if the cell identified by this adapter displays hierarchical + * nodes; false otherwise + */ + @Override + public boolean isHierarchical() { + return table.isHierarchical(column); + } + + /** + * {@inheritDoc}

+ * + * Overridden to fix #821-swingx: string rep of hierarchical column incorrect. + * In this case we must delegate to the tree directly (via treetable.getHierarchicalString). + * + * PENDING JW: revisit once we switch to really using a table renderer. + */ + @Override + public String getFilteredStringAt(int row, int column) { + if (table.getTreeTableModel().getHierarchicalColumn() == column) { + if (convertColumnIndexToView(column) < 0) { + // hidden hierarchical column, access directly + // PENDING JW: after introducing and wiring StringValueRegistry, + // had to change to query the hierarchicalString always + // could probably be done more elegantly, but ... + } + return table.getHierarchicalStringAt(row); + } + return super.getFilteredStringAt(row, column); + } + + /** + * {@inheritDoc}

+ * + * Overridden to fix #821-swingx: string rep of hierarchical column incorrect. + * In this case we must delegate to the tree directly (via treetable.getHierarchicalString). + * + * PENDING JW: revisit once we switch to really using a table renderer. + */ + @Override + public String getStringAt(int row, int column) { + if (table.getTreeTableModel().getHierarchicalColumn() == column) { + if (convertColumnIndexToView(column) < 0) { + // hidden hierarchical column, access directly + // PENDING JW: after introducing and wiring StringValueRegistry, + // had to change to query the hierarchicalString always + // could probably be done more elegantly, but ... + } + return table.getHierarchicalStringAt(row); + } + return super.getStringAt(row, column); + } + + } + +} diff --git a/src/main/java/org/jdesktop/swingx/Mnemonicable.java b/src/main/java/org/jdesktop/swingx/Mnemonicable.java new file mode 100644 index 0000000000..1dbf53affd --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/Mnemonicable.java @@ -0,0 +1,79 @@ +package org.jdesktop.swingx; + +/** + * An interface that describes an object that is capable of being accessed/used via a mnemonic + * keystroke. + * + * @author Karl George Schaefer + */ +// TODO this describes the mnemonic feature but not what is used, +// ie. what String returning method is called +interface Mnemonicable { + /** + * Returns the keyboard mnemonic for this component. + * + * @return the keyboard mnemonic + */ + int getMnemonic(); + + /** + * Sets the keyboard mnemonic on this component. The mnemonic is the key + * which when combined with the look and feel's mouseless modifier (usually + * Alt) will activate this component. + *

+ * A mnemonic must correspond to a single key on the keyboard and should be + * specified using one of the VK_XXX keycodes defined in + * java.awt.event.KeyEvent. Mnemonics are case-insensitive, + * therefore a key event with the corresponding keycode would cause the + * button to be activated whether or not the Shift modifier was pressed. + * + * @param mnemonic + * the key code which represents the mnemonic + * @see java.awt.event.KeyEvent + * @see #setDisplayedMnemonicIndex + * + * @beaninfo bound: true attribute: visualUpdate true description: the + * keyboard character mnemonic + */ + void setMnemonic(int mnemonic); + + /** + * Returns the character, as an index, that the look and feel should + * provide decoration for as representing the mnemonic character. + * + * @since 1.4 + * @return index representing mnemonic character + * @see #setDisplayedMnemonicIndex + */ + int getDisplayedMnemonicIndex(); + + /** + * Provides a hint to the look and feel as to which character in the + * text should be decorated to represent the mnemonic. Not all look and + * feels may support this. A value of -1 indicates either there is no + * mnemonic, the mnemonic character is not contained in the string, or + * the developer does not wish the mnemonic to be displayed. + *

+ * The value of this is updated as the properties relating to the + * mnemonic change (such as the mnemonic itself, the text...). + * You should only ever have to call this if + * you do not wish the default character to be underlined. For example, if + * the text was 'Save As', with a mnemonic of 'a', and you wanted the 'A' + * to be decorated, as 'Save As', you would have to invoke + * setDisplayedMnemonicIndex(5) after invoking + * setMnemonic(KeyEvent.VK_A). + * + * @since 1.4 + * @param index Index into the String to underline + * @exception IllegalArgumentException will be thrown if index + * is >= length of the text, or < -1 + * @see #getDisplayedMnemonicIndex + * + * @beaninfo + * bound: true + * attribute: visualUpdate true + * description: the index into the String to draw the keyboard character + * mnemonic at + */ + void setDisplayedMnemonicIndex(int index) throws IllegalArgumentException; +} diff --git a/src/main/java/org/jdesktop/swingx/MultiSplitLayout.java b/src/main/java/org/jdesktop/swingx/MultiSplitLayout.java new file mode 100644 index 0000000000..6ac3618505 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/MultiSplitLayout.java @@ -0,0 +1,2211 @@ +/* + * $Id: MultiSplitLayout.java 4238 2012-08-28 17:56:36Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx; + +import javax.swing.*; +import java.awt.*; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.io.*; +import java.util.List; +import java.util.*; + + +/** + * The MultiSplitLayout layout manager recursively arranges its + * components in row and column groups called "Splits". Elements of + * the layout are separated by gaps called "Dividers". The overall + * layout is defined with a simple tree model whose nodes are + * instances of MultiSplitLayout.Split, MultiSplitLayout.Divider, + * and MultiSplitLayout.Leaf. Named Leaf nodes represent the space + * allocated to a component that was added with a constraint that + * matches the Leaf's name. Extra space is distributed + * among row/column siblings according to their 0.0 to 1.0 weight. + * If no weights are specified then the last sibling always gets + * all of the extra space, or space reduction. + * + *

+ * Although MultiSplitLayout can be used with any Container, it's + * the default layout manager for MultiSplitPane. MultiSplitPane + * supports interactively dragging the Dividers, accessibility, + * and other features associated with split panes. + * + *

+ * All properties in this class are bound: when a properties value + * is changed, all PropertyChangeListeners are fired. + * + * + * @author Hans Muller + * @author Luan O'Carroll + * @see JXMultiSplitPane + */ + +/* + * Changes by Luan O'Carroll + * 1 Support for visibility added. + */ +public class MultiSplitLayout implements LayoutManager, Serializable +{ + public static final int DEFAULT_LAYOUT = 0; + public static final int NO_MIN_SIZE_LAYOUT = 1; + public static final int USER_MIN_SIZE_LAYOUT = 2; + + private final Map childMap = new HashMap(); + private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); + private Node model; + private int dividerSize; + private boolean floatingDividers = true; + + private boolean removeDividers = true; + private boolean layoutByWeight = false; + + private int layoutMode; + private int userMinSize = 20; + + /** + * Create a MultiSplitLayout with a default model with a single + * Leaf node named "default". + * + * #see setModel + */ + public MultiSplitLayout() + { + this(new Leaf("default")); + } + + /** + * Create a MultiSplitLayout with a default model with a single + * Leaf node named "default". + * + * @param layoutByWeight if true the layout is initialized in proportion to + * the node weights rather than the component preferred sizes. + * #see setModel + */ + public MultiSplitLayout(boolean layoutByWeight) + { + this(new Leaf("default")); + this.layoutByWeight = layoutByWeight; + } + + /** + * Set the size of the child components to match the weights of the children. + * If the components to not all specify a weight then the available layout + * space is divided equally between the components. + */ + public void layoutByWeight( Container parent ) + { + doLayoutByWeight( parent ); + + layoutContainer( parent ); + } + + /** + * Set the size of the child components to match the weights of the children. + * If the components to not all specify a weight then the available layout + * space is divided equally between the components. + */ + private void doLayoutByWeight( Container parent ) + { + Dimension size = parent.getSize(); + Insets insets = parent.getInsets(); + int width = size.width - (insets.left + insets.right); + int height = size.height - (insets.top + insets.bottom); + Rectangle bounds = new Rectangle(insets.left, insets.top, width, height); + + if (model instanceof Leaf) + model.setBounds(bounds); + else if (model instanceof Split) + doLayoutByWeight( model, bounds ); + } + + private void doLayoutByWeight( Node node, Rectangle bounds ) + { + int width = bounds.width; + int height = bounds.height; + Split split = (Split)node; + List splitChildren = split.getChildren(); + double distributableWeight = 1.0; + int unweightedComponents = 0; + int dividerSpace = 0; + for( Node splitChild : splitChildren ) { + if ( !splitChild.isVisible()) + continue; + else if ( splitChild instanceof Divider ) { + dividerSpace += dividerSize; + continue; + } + + double weight = splitChild.getWeight(); + if ( weight > 0.0 ) + distributableWeight -= weight; + else + unweightedComponents++; + } + + if ( split.isRowLayout()) { + width -= dividerSpace; + double distributableWidth = width * distributableWeight; + for( Node splitChild : splitChildren ) { + if ( !splitChild.isVisible() || ( splitChild instanceof Divider )) + continue; + + double weight = splitChild.getWeight(); + Rectangle splitChildBounds = splitChild.getBounds(); + if ( weight >= 0 ) + splitChildBounds = new Rectangle( splitChildBounds.x, splitChildBounds.y, (int)( width * weight ), height ); + else + splitChildBounds = new Rectangle( splitChildBounds.x, splitChildBounds.y, (int)( distributableWidth / unweightedComponents ), height ); + + if ( layoutMode == USER_MIN_SIZE_LAYOUT ) { + splitChildBounds.setSize( Math.max( splitChildBounds.width, userMinSize ), splitChildBounds.height ); + } + + splitChild.setBounds( splitChildBounds ); + + if ( splitChild instanceof Split ) + doLayoutByWeight( splitChild, splitChildBounds ); + else { + Component comp = getComponentForNode( splitChild ); + if ( comp != null ) + comp.setPreferredSize( splitChildBounds.getSize()); + } + } + } + else { + height -= dividerSpace; + double distributableHeight = height * distributableWeight; + for( Node splitChild : splitChildren ) { + if ( !splitChild.isVisible() || ( splitChild instanceof Divider )) + continue; + + double weight = splitChild.getWeight(); + Rectangle splitChildBounds = splitChild.getBounds(); + if ( weight >= 0 ) + splitChildBounds = new Rectangle( splitChildBounds.x, splitChildBounds.y, width, (int)( height * weight )); + else + splitChildBounds = new Rectangle( splitChildBounds.x, splitChildBounds.y, width, (int)( distributableHeight / unweightedComponents )); + + if ( layoutMode == USER_MIN_SIZE_LAYOUT ) { + splitChildBounds.setSize( splitChildBounds.width, Math.max( splitChildBounds.height, userMinSize ) ); + } + + splitChild.setBounds( splitChildBounds ); + + if ( splitChild instanceof Split ) + doLayoutByWeight( splitChild, splitChildBounds ); + else { + Component comp = getComponentForNode( splitChild ); + if ( comp != null ) + comp.setPreferredSize( splitChildBounds.getSize()); + } + } + } + } + + /** + * Get the component associated with a MultiSplitLayout.Node + * @param n the layout node + * @return the component handled by the layout or null if not found + */ + public Component getComponentForNode( Node n ) + { + String name = ((Leaf)n).getName(); + return (name != null) ? (Component)childMap.get(name) : null; + } + + /** + * Get the MultiSplitLayout.Node associated with a component + * @param comp the component being positioned by the layout + * @return the node associated with the component + */ + public Node getNodeForComponent( Component comp ) + { + return getNodeForName( getNameForComponent( comp )); + } + + /** + * Get the MultiSplitLayout.Node associated with a component + * @param name the name used to associate a component with the layout + * @return the node associated with the component + */ + public Node getNodeForName( String name ) + { + if ( model instanceof Split ) { + Split split = ((Split)model); + return getNodeForName( split, name ); + } else if (model instanceof Leaf) { + if (((Leaf) model).getName().equals(name)) { + return model; + } else { + return null; + } + } else { + return null; + } + } + + /** + * Get the name used to map a component + * @param child the component + * @return the name used to map the component or null if no mapping is found + */ + public String getNameForComponent( Component child ) + { + String name = null; + for(Map.Entry kv : childMap.entrySet()) { + if (kv.getValue() == child) { + name = kv.getKey(); + break; + } + } + + return name; + } + + /** + * Get the MultiSplitLayout.Node associated with a component + * @param split the layout split that owns the requested node + * @param comp the component being positioned by the layout + * @return the node associated with the component + */ + public Node getNodeForComponent( Split split, Component comp ) + { + return getNodeForName( split, getNameForComponent( comp )); + } + + /** + * Get the MultiSplitLayout.Node associated with a component + * @param split the layout split that owns the requested node + * @param name the name used to associate a component with the layout + * @return the node associated with the component + */ + public Node getNodeForName( Split split, String name ) + { + for(Node n : split.getChildren()) { + if ( n instanceof Leaf ) { + if ( ((Leaf)n).getName().equals( name )) + return n; + } + else if ( n instanceof Split ) { + Node n1 = getNodeForName( (Split)n, name ); + if ( n1 != null ) + return n1; + } + } + return null; + } + + /** + * Is there a valid model for the layout? + * @return true if there is a model + */ + public boolean hasModel() + { + return model != null; + } + + /** + * Create a MultiSplitLayout with the specified model. + * + * #see setModel + */ + public MultiSplitLayout(Node model) { + this.model = model; + this.dividerSize = UIManager.getInt("SplitPane.dividerSize"); + if (this.dividerSize == 0) { + this.dividerSize = 7; + } + } + + public void addPropertyChangeListener(PropertyChangeListener listener) { + if (listener != null) { + pcs.addPropertyChangeListener(listener); + } + } + public void removePropertyChangeListener(PropertyChangeListener listener) { + if (listener != null) { + pcs.removePropertyChangeListener(listener); + } + } + public PropertyChangeListener[] getPropertyChangeListeners() { + return pcs.getPropertyChangeListeners(); + } + + private void firePCS(String propertyName, Object oldValue, Object newValue) { + if (!(oldValue != null && newValue != null && oldValue.equals(newValue))) { + pcs.firePropertyChange(propertyName, oldValue, newValue); + } + } + + /** + * Return the root of the tree of Split, Leaf, and Divider nodes + * that define this layout. + * + * @return the value of the model property + * @see #setModel + */ + public Node getModel() { return model; } + + /** + * Set the root of the tree of Split, Leaf, and Divider nodes + * that define this layout. The model can be a Split node + * (the typical case) or a Leaf. The default value of this + * property is a Leaf named "default". + * + * @param model the root of the tree of Split, Leaf, and Divider node + * @throws IllegalArgumentException if model is a Divider or null + * @see #getModel + */ + public void setModel(Node model) { + if ((model == null) || (model instanceof Divider)) { + throw new IllegalArgumentException("invalid model"); + } + Node oldModel = getModel(); + this.model = model; + firePCS("model", oldModel, getModel()); + } + + /** + * Returns the width of Dividers in Split rows, and the height of + * Dividers in Split columns. + * + * @return the value of the dividerSize property + * @see #setDividerSize + */ + public int getDividerSize() { return dividerSize; } + + /** + * Sets the width of Dividers in Split rows, and the height of + * Dividers in Split columns. The default value of this property + * is the same as for JSplitPane Dividers. + * + * @param dividerSize the size of dividers (pixels) + * @throws IllegalArgumentException if dividerSize < 0 + * @see #getDividerSize + */ + public void setDividerSize(int dividerSize) { + if (dividerSize < 0) { + throw new IllegalArgumentException("invalid dividerSize"); + } + int oldDividerSize = this.dividerSize; + this.dividerSize = dividerSize; + firePCS("dividerSize", oldDividerSize, dividerSize); + } + + /** + * @return the value of the floatingDividers property + * @see #setFloatingDividers + */ + public boolean getFloatingDividers() { return floatingDividers; } + + + /** + * If true, Leaf node bounds match the corresponding component's + * preferred size and Splits/Dividers are resized accordingly. + * If false then the Dividers define the bounds of the adjacent + * Split and Leaf nodes. Typically this property is set to false + * after the (MultiSplitPane) user has dragged a Divider. + * + * @see #getFloatingDividers + */ + public void setFloatingDividers(boolean floatingDividers) + { + boolean oldFloatingDividers = this.floatingDividers; + this.floatingDividers = floatingDividers; + firePCS("floatingDividers", oldFloatingDividers, floatingDividers); + } + + /** + * @return the value of the removeDividers property + * @see #setRemoveDividers + */ + public boolean getRemoveDividers() { return removeDividers; } + + /** + * If true, the next divider is removed when a component is removed from the + * layout. If false, only the node itself is removed. Normally the next + * divider should be removed from the layout when a component is removed. + * @param removeDividers true to removed the next divider whena component is + * removed from teh layout + */ + public void setRemoveDividers( boolean removeDividers ) + { + boolean oldRemoveDividers = this.removeDividers; + this.removeDividers = removeDividers; + firePCS("removeDividers", oldRemoveDividers, removeDividers); + } + + /** + * Add a component to this MultiSplitLayout. The + * name should match the name property of the Leaf + * node that represents the bounds of child. After + * layoutContainer() recomputes the bounds of all of the nodes in + * the model, it will set this child's bounds to the bounds of the + * Leaf node with name. Note: if a component was already + * added with the same name, this method does not remove it from + * its parent. + * + * @param name identifies the Leaf node that defines the child's bounds + * @param child the component to be added + * @see #removeLayoutComponent + */ + @Override +public void addLayoutComponent(String name, Component child) { + if (name == null) { + throw new IllegalArgumentException("name not specified"); + } + childMap.put(name, child); + } + + /** + * Removes the specified component from the layout. + * + * @param child the component to be removed + * @see #addLayoutComponent + */ + @Override +public void removeLayoutComponent(Component child) { + String name = getNameForComponent( child ); + + if ( name != null ) { + childMap.remove( name ); + } + } + + /** + * Removes the specified node from the layout. + * + * @param name the name of the component to be removed + * @see #addLayoutComponent + */ + public void removeLayoutNode(String name) { + + if ( name != null ) { + Node n; + if ( !( model instanceof Split )) + n = model; + else + n = getNodeForName( name ); + + childMap.remove(name); + + if ( n != null ) { + Split s = n.getParent(); + s.remove( n ); + if (removeDividers) { + while ( s.getChildren().size() < 2 ) { + Split p = s.getParent(); + if ( p == null ) { + if ( s.getChildren().size() > 0 ) + model = s.getChildren().get( 0 ); + else + model = null; + return; + } + if ( s.getChildren().size() == 1 ) { + Node next = s.getChildren().get( 0 ); + p.replace( s, next ); + next.setParent( p ); + } + else + p.remove( s ); + s = p; + } + } + } + else { + childMap.remove( name ); + } + } + } + + /** + * Show/Hide nodes. Any dividers that are no longer required due to one of the + * nodes being made visible/invisible are also shown/hidden. The visibility of + * the component managed by the node is also changed by this method + * @param name the node name + * @param visible the new node visible state + */ + public void displayNode( String name, boolean visible ) + { + Node node = getNodeForName( name ); + if ( node != null ) { + Component comp = getComponentForNode( node ); + comp.setVisible( visible ); + node.setVisible( visible ); + + Split p = node.getParent(); + if ( !visible ) { + p.hide( node ); + if ( !p.isVisible()) + p.getParent().hide( p ); + + p.checkDividers( p ); + // If the split has become invisible then the parent may also have a + // divider that needs to be hidden. + while ( !p.isVisible()) { + p = p.getParent(); + if ( p != null ) + p.checkDividers( p ); + else + break; + } + } + else + p.restoreDividers( p ); + } + setFloatingDividers( false ); + } + + private Component childForNode(Node node) { + if (node instanceof Leaf) { + Leaf leaf = (Leaf)node; + String name = leaf.getName(); + return (name != null) ? childMap.get(name) : null; + } + return null; + } + + + private Dimension preferredComponentSize(Node node) { + if ( layoutMode == NO_MIN_SIZE_LAYOUT ) + return new Dimension(0, 0); + + Component child = childForNode(node); + return ((child != null) && child.isVisible() ) ? child.getPreferredSize() : new Dimension(0, 0); + } + + private Dimension minimumComponentSize(Node node) { + if ( layoutMode == NO_MIN_SIZE_LAYOUT ) + return new Dimension(0, 0); + + Component child = childForNode(node); + return ((child != null) && child.isVisible() ) ? child.getMinimumSize() : new Dimension(0, 0); + } + + private Dimension preferredNodeSize(Node root) { + if (root instanceof Leaf) { + return preferredComponentSize(root); + } + else if (root instanceof Divider) { + if ( !((Divider)root).isVisible()) + return new Dimension(0,0); + int divSize = getDividerSize(); + return new Dimension(divSize, divSize); + } + else { + Split split = (Split)root; + List splitChildren = split.getChildren(); + int width = 0; + int height = 0; + if (split.isRowLayout()) { + for(Node splitChild : splitChildren) { + if ( !splitChild.isVisible()) + continue; + Dimension size = preferredNodeSize(splitChild); + width += size.width; + height = Math.max(height, size.height); + } + } + else { + for(Node splitChild : splitChildren) { + if ( !splitChild.isVisible()) + continue; + Dimension size = preferredNodeSize(splitChild); + width = Math.max(width, size.width); + height += size.height; + } + } + return new Dimension(width, height); + } + } + + /** + * Get the minimum size of this node. Sums the minumum sizes of rows or + * columns to get the overall minimum size for the layout node, including the + * dividers. + * @param root the node whose size is required. + * @return the minimum size. + */ + public Dimension minimumNodeSize(Node root) { + assert( root.isVisible ); + if (root instanceof Leaf) { + if ( layoutMode == NO_MIN_SIZE_LAYOUT ) + return new Dimension(0, 0); + + Component child = childForNode(root); + return ((child != null) && child.isVisible() ) ? child.getMinimumSize() : new Dimension(0, 0); + } + else if (root instanceof Divider) { + if ( !((Divider)root).isVisible() ) + return new Dimension(0,0); + int divSize = getDividerSize(); + return new Dimension(divSize, divSize); + } + else { + Split split = (Split)root; + List splitChildren = split.getChildren(); + int width = 0; + int height = 0; + if (split.isRowLayout()) { + for(Node splitChild : splitChildren) { + if ( !splitChild.isVisible()) + continue; + Dimension size = minimumNodeSize(splitChild); + width += size.width; + height = Math.max(height, size.height); + } + } + else { + for(Node splitChild : splitChildren) { + if ( !splitChild.isVisible()) + continue; + Dimension size = minimumNodeSize(splitChild); + width = Math.max(width, size.width); + height += size.height; + } + } + return new Dimension(width, height); + } + } + + /** + * Get the maximum size of this node. Sums the minumum sizes of rows or + * columns to get the overall maximum size for the layout node, including the + * dividers. + * @param root the node whose size is required. + * @return the minimum size. + */ + public Dimension maximumNodeSize(Node root) { + assert( root.isVisible ); + if (root instanceof Leaf) { + Component child = childForNode(root); + return ((child != null) && child.isVisible() ) ? child.getMaximumSize() : new Dimension(0, 0); + } + else if (root instanceof Divider) { + if ( !((Divider)root).isVisible() ) + return new Dimension(0,0); + int divSize = getDividerSize(); + return new Dimension(divSize, divSize); + } + else { + Split split = (Split)root; + List splitChildren = split.getChildren(); + int width = Integer.MAX_VALUE; + int height = Integer.MAX_VALUE; + if (split.isRowLayout()) { + for(Node splitChild : splitChildren) { + if ( !splitChild.isVisible()) + continue; + Dimension size = maximumNodeSize(splitChild); + width += size.width; + height = Math.min(height, size.height); + } + } + else { + for(Node splitChild : splitChildren) { + if ( !splitChild.isVisible()) + continue; + Dimension size = maximumNodeSize(splitChild); + width = Math.min(width, size.width); + height += size.height; + } + } + return new Dimension(width, height); + } + } + + private Dimension sizeWithInsets(Container parent, Dimension size) { + Insets insets = parent.getInsets(); + int width = size.width + insets.left + insets.right; + int height = size.height + insets.top + insets.bottom; + return new Dimension(width, height); + } + + @Override +public Dimension preferredLayoutSize(Container parent) { + Dimension size = preferredNodeSize(getModel()); + return sizeWithInsets(parent, size); + } + + @Override +public Dimension minimumLayoutSize(Container parent) { + Dimension size = minimumNodeSize(getModel()); + return sizeWithInsets(parent, size); + } + + + private Rectangle boundsWithYandHeight(Rectangle bounds, double y, double height) { + Rectangle r = new Rectangle(); + r.setBounds((int)(bounds.getX()), (int)y, (int)(bounds.getWidth()), (int)height); + return r; + } + + private Rectangle boundsWithXandWidth(Rectangle bounds, double x, double width) { + Rectangle r = new Rectangle(); + r.setBounds((int)x, (int)(bounds.getY()), (int)width, (int)(bounds.getHeight())); + return r; + } + + + private void minimizeSplitBounds(Split split, Rectangle bounds) { + assert ( split.isVisible()); + Rectangle splitBounds = new Rectangle(bounds.x, bounds.y, 0, 0); + List splitChildren = split.getChildren(); + Node lastChild = null; + int lastVisibleChildIdx = splitChildren.size(); + do { + lastVisibleChildIdx--; + lastChild = splitChildren.get( lastVisibleChildIdx ); + } while (( lastVisibleChildIdx > 0 ) && !lastChild.isVisible()); + + if ( !lastChild.isVisible()) + return; + if ( lastVisibleChildIdx >= 0 ) { + Rectangle lastChildBounds = lastChild.getBounds(); + if (split.isRowLayout()) { + int lastChildMaxX = lastChildBounds.x + lastChildBounds.width; + splitBounds.add(lastChildMaxX, bounds.y + bounds.height); + } + else { + int lastChildMaxY = lastChildBounds.y + lastChildBounds.height; + splitBounds.add(bounds.x + bounds.width, lastChildMaxY); + } + } + split.setBounds(splitBounds); + } + + + private void layoutShrink(Split split, Rectangle bounds) { + Rectangle splitBounds = split.getBounds(); + ListIterator splitChildren = split.getChildren().listIterator(); + Node lastWeightedChild = split.lastWeightedChild(); + + if (split.isRowLayout()) { + int totalWidth = 0; // sum of the children's widths + int minWeightedWidth = 0; // sum of the weighted childrens' min widths + int totalWeightedWidth = 0; // sum of the weighted childrens' widths + for(Node splitChild : split.getChildren()) { + if ( !splitChild.isVisible()) + continue; + int nodeWidth = splitChild.getBounds().width; + int nodeMinWidth = 0; + if (( layoutMode == USER_MIN_SIZE_LAYOUT ) && !( splitChild instanceof Divider )) + nodeMinWidth = userMinSize; + else if ( layoutMode == DEFAULT_LAYOUT ) + nodeMinWidth = Math.min(nodeWidth, minimumNodeSize(splitChild).width); + totalWidth += nodeWidth; + if (splitChild.getWeight() > 0.0) { + minWeightedWidth += nodeMinWidth; + totalWeightedWidth += nodeWidth; + } + } + + double x = bounds.getX(); + double extraWidth = splitBounds.getWidth() - bounds.getWidth(); + double availableWidth = extraWidth; + boolean onlyShrinkWeightedComponents = + (totalWeightedWidth - minWeightedWidth) > extraWidth; + + while(splitChildren.hasNext()) { + Node splitChild = splitChildren.next(); + if ( !splitChild.isVisible()) { + if ( splitChildren.hasNext()) + splitChildren.next(); + continue; + } + Rectangle splitChildBounds = splitChild.getBounds(); + double minSplitChildWidth = 0.0; + if (( layoutMode == USER_MIN_SIZE_LAYOUT ) && !( splitChild instanceof Divider )) + minSplitChildWidth = userMinSize; + else if ( layoutMode == DEFAULT_LAYOUT ) + minSplitChildWidth = minimumNodeSize(splitChild).getWidth(); + double splitChildWeight = (onlyShrinkWeightedComponents) + ? splitChild.getWeight() + : (splitChildBounds.getWidth() / totalWidth); + + if (!splitChildren.hasNext()) { + double newWidth = Math.max(minSplitChildWidth, bounds.getMaxX() - x); + Rectangle newSplitChildBounds = boundsWithXandWidth(bounds, x, newWidth); + layout2(splitChild, newSplitChildBounds); + } + if ( splitChild.isVisible()) { + if ((availableWidth > 0.0) && (splitChildWeight > 0.0)) { + double oldWidth = splitChildBounds.getWidth(); + double newWidth; + if ( splitChild instanceof Divider ) { + newWidth = dividerSize; + } + else { + double allocatedWidth = Math.rint(splitChildWeight * extraWidth); + newWidth = Math.max(minSplitChildWidth, oldWidth - allocatedWidth); + } + Rectangle newSplitChildBounds = boundsWithXandWidth(bounds, x, newWidth); + layout2(splitChild, newSplitChildBounds); + availableWidth -= (oldWidth - splitChild.getBounds().getWidth()); + } + else { + double existingWidth = splitChildBounds.getWidth(); + Rectangle newSplitChildBounds = boundsWithXandWidth(bounds, x, existingWidth); + layout2(splitChild, newSplitChildBounds); + } + x = splitChild.getBounds().getMaxX(); + } + } + } + + else { + int totalHeight = 0; // sum of the children's heights + int minWeightedHeight = 0; // sum of the weighted childrens' min heights + int totalWeightedHeight = 0; // sum of the weighted childrens' heights + for(Node splitChild : split.getChildren()) { + if ( !splitChild.isVisible()) + continue; + int nodeHeight = splitChild.getBounds().height; + int nodeMinHeight = 0; + if (( layoutMode == USER_MIN_SIZE_LAYOUT ) && !( splitChild instanceof Divider )) + nodeMinHeight = userMinSize; + else if ( layoutMode == DEFAULT_LAYOUT ) + nodeMinHeight = Math.min(nodeHeight, minimumNodeSize(splitChild).height); + totalHeight += nodeHeight; + if (splitChild.getWeight() > 0.0) { + minWeightedHeight += nodeMinHeight; + totalWeightedHeight += nodeHeight; + } + } + + double y = bounds.getY(); + double extraHeight = splitBounds.getHeight() - bounds.getHeight(); + double availableHeight = extraHeight; + boolean onlyShrinkWeightedComponents = + (totalWeightedHeight - minWeightedHeight) > extraHeight; + + while(splitChildren.hasNext()) { + Node splitChild = splitChildren.next(); + if ( !splitChild.isVisible()) { + if ( splitChildren.hasNext()) + splitChildren.next(); + continue; + } + Rectangle splitChildBounds = splitChild.getBounds(); + double minSplitChildHeight = 0.0; + if (( layoutMode == USER_MIN_SIZE_LAYOUT ) && !( splitChild instanceof Divider )) + minSplitChildHeight = userMinSize; + else if ( layoutMode == DEFAULT_LAYOUT ) + minSplitChildHeight = minimumNodeSize(splitChild).getHeight(); + double splitChildWeight = (onlyShrinkWeightedComponents) + ? splitChild.getWeight() + : (splitChildBounds.getHeight() / totalHeight); + + // If this split child is the last visible node it should all the + // remaining space + if ( !hasMoreVisibleSiblings( splitChild )) { + double oldHeight = splitChildBounds.getHeight(); + double newHeight; + if ( splitChild instanceof Divider ) { + newHeight = dividerSize; + } + else { + newHeight = Math.max(minSplitChildHeight, bounds.getMaxY() - y); + } + Rectangle newSplitChildBounds = boundsWithYandHeight(bounds, y, newHeight); + layout2(splitChild, newSplitChildBounds); + availableHeight -= (oldHeight - splitChild.getBounds().getHeight()); + } + else /*if ( splitChild.isVisible()) {*/ + if ((availableHeight > 0.0) && (splitChildWeight > 0.0)) { + double newHeight; + double oldHeight = splitChildBounds.getHeight(); + // Prevent the divider from shrinking + if ( splitChild instanceof Divider ) { + newHeight = dividerSize; + } + else { + double allocatedHeight = Math.rint(splitChildWeight * extraHeight); + newHeight = Math.max(minSplitChildHeight, oldHeight - allocatedHeight); + } + Rectangle newSplitChildBounds = boundsWithYandHeight(bounds, y, newHeight); + layout2(splitChild, newSplitChildBounds); + availableHeight -= (oldHeight - splitChild.getBounds().getHeight()); + } + else { + double existingHeight = splitChildBounds.getHeight(); + Rectangle newSplitChildBounds = boundsWithYandHeight(bounds, y, existingHeight); + layout2(splitChild, newSplitChildBounds); + } + y = splitChild.getBounds().getMaxY(); + } + } + + /* The bounds of the Split node root are set to be + * big enough to contain all of its children. Since + * Leaf children can't be reduced below their + * (corresponding java.awt.Component) minimum sizes, + * the size of the Split's bounds maybe be larger than + * the bounds we were asked to fit within. + */ + minimizeSplitBounds(split, bounds); + } + + /** + * Check if the specified node has any following visible siblings + * @param splitChild the node to check + * @param true if there are visible children following + */ + private boolean hasMoreVisibleSiblings( Node splitChild ) { + Node next = splitChild.nextSibling(); + if ( next == null ) + return false; + + do { + if ( next.isVisible()) + return true; + next = next.nextSibling(); + } while ( next != null ); + + return false; + } + + private void layoutGrow(Split split, Rectangle bounds) { + Rectangle splitBounds = split.getBounds(); + ListIterator splitChildren = split.getChildren().listIterator(); + Node lastWeightedChild = split.lastWeightedChild(); + + /* Layout the Split's child Nodes' along the X axis. The bounds + * of each child will have the same y coordinate and height as the + * layoutGrow() bounds argument. Extra width is allocated to the + * to each child with a non-zero weight: + * newWidth = currentWidth + (extraWidth * splitChild.getWeight()) + * Any extraWidth "left over" (that's availableWidth in the loop + * below) is given to the last child. Note that Dividers always + * have a weight of zero, and they're never the last child. + */ + if (split.isRowLayout()) { + double x = bounds.getX(); + double extraWidth = bounds.getWidth() - splitBounds.getWidth(); + double availableWidth = extraWidth; + + while(splitChildren.hasNext()) { + Node splitChild = splitChildren.next(); + if ( !splitChild.isVisible()) { + continue; + } + Rectangle splitChildBounds = splitChild.getBounds(); + double splitChildWeight = splitChild.getWeight(); + + if ( !hasMoreVisibleSiblings( splitChild )) { + double newWidth = bounds.getMaxX() - x; + Rectangle newSplitChildBounds = boundsWithXandWidth(bounds, x, newWidth); + layout2(splitChild, newSplitChildBounds); + } + else if ((availableWidth > 0.0) && (splitChildWeight > 0.0)) { + double allocatedWidth = (splitChild.equals(lastWeightedChild)) + ? availableWidth + : Math.rint(splitChildWeight * extraWidth); + double newWidth = splitChildBounds.getWidth() + allocatedWidth; + Rectangle newSplitChildBounds = boundsWithXandWidth(bounds, x, newWidth); + layout2(splitChild, newSplitChildBounds); + availableWidth -= allocatedWidth; + } + else { + double existingWidth = splitChildBounds.getWidth(); + Rectangle newSplitChildBounds = boundsWithXandWidth(bounds, x, existingWidth); + layout2(splitChild, newSplitChildBounds); + } + x = splitChild.getBounds().getMaxX(); + } + } + + /* Layout the Split's child Nodes' along the Y axis. The bounds + * of each child will have the same x coordinate and width as the + * layoutGrow() bounds argument. Extra height is allocated to the + * to each child with a non-zero weight: + * newHeight = currentHeight + (extraHeight * splitChild.getWeight()) + * Any extraHeight "left over" (that's availableHeight in the loop + * below) is given to the last child. Note that Dividers always + * have a weight of zero, and they're never the last child. + */ + else { + double y = bounds.getY(); + double extraHeight = bounds.getHeight() - splitBounds.getHeight(); + double availableHeight = extraHeight; + + while(splitChildren.hasNext()) { + Node splitChild = splitChildren.next(); + if ( !splitChild.isVisible()) { + continue; + } + Rectangle splitChildBounds = splitChild.getBounds(); + double splitChildWeight = splitChild.getWeight(); + + if (!splitChildren.hasNext()) { + double newHeight = bounds.getMaxY() - y; + Rectangle newSplitChildBounds = boundsWithYandHeight(bounds, y, newHeight); + layout2(splitChild, newSplitChildBounds); + } + else if ((availableHeight > 0.0) && (splitChildWeight > 0.0)) { + double allocatedHeight = (splitChild.equals(lastWeightedChild)) + ? availableHeight + : Math.rint(splitChildWeight * extraHeight); + double newHeight = splitChildBounds.getHeight() + allocatedHeight; + Rectangle newSplitChildBounds = boundsWithYandHeight(bounds, y, newHeight); + layout2(splitChild, newSplitChildBounds); + availableHeight -= allocatedHeight; + } + else { + double existingHeight = splitChildBounds.getHeight(); + Rectangle newSplitChildBounds = boundsWithYandHeight(bounds, y, existingHeight); + layout2(splitChild, newSplitChildBounds); + } + y = splitChild.getBounds().getMaxY(); + } + } + } + + + /* Second pass of the layout algorithm: branch to layoutGrow/Shrink + * as needed. + */ + private void layout2(Node root, Rectangle bounds) { + if (root instanceof Leaf) { + Component child = childForNode(root); + if (child != null) { + child.setBounds(bounds); + } + root.setBounds(bounds); + } + else if (root instanceof Divider) { + root.setBounds(bounds); + } + else if (root instanceof Split) { + Split split = (Split)root; + boolean grow = split.isRowLayout() + ? (split.getBounds().width <= bounds.width) + : (split.getBounds().height <= bounds.height); + if (grow) { + layoutGrow(split, bounds); + root.setBounds(bounds); + } + else { + layoutShrink(split, bounds); + // split.setBounds() called in layoutShrink() + } + } + } + + + /* First pass of the layout algorithm. + * + * If the Dividers are "floating" then set the bounds of each + * node to accomodate the preferred size of all of the + * Leaf's java.awt.Components. Otherwise, just set the bounds + * of each Leaf/Split node so that it's to the left of (for + * Split.isRowLayout() Split children) or directly above + * the Divider that follows. + * + * This pass sets the bounds of each Node in the layout model. It + * does not resize any of the parent Container's + * (java.awt.Component) children. That's done in the second pass, + * see layoutGrow() and layoutShrink(). + */ + private void layout1(Node root, Rectangle bounds) { + if (root instanceof Leaf) { + root.setBounds(bounds); + } + else if (root instanceof Split) { + Split split = (Split)root; + Iterator splitChildren = split.getChildren().iterator(); + Rectangle childBounds = null; + int divSize = getDividerSize(); + boolean initSplit = false; + + + /* Layout the Split's child Nodes' along the X axis. The bounds + * of each child will have the same y coordinate and height as the + * layout1() bounds argument. + * + * Note: the column layout code - that's the "else" clause below + * this if, is identical to the X axis (rowLayout) code below. + */ + if (split.isRowLayout()) { + double x = bounds.getX(); + while(splitChildren.hasNext()) { + Node splitChild = splitChildren.next(); + if ( !splitChild.isVisible()) { + if ( splitChildren.hasNext()) + splitChildren.next(); + continue; + } + Divider dividerChild = + (splitChildren.hasNext()) ? (Divider)(splitChildren.next()) : null; + + double childWidth = 0.0; + if (getFloatingDividers()) { + childWidth = preferredNodeSize(splitChild).getWidth(); + } + else { + if ((dividerChild != null) && dividerChild.isVisible()) { + double cw = dividerChild.getBounds().getX() - x; + if ( cw > 0.0 ) + childWidth = cw; + else { + childWidth = preferredNodeSize(splitChild).getWidth(); + initSplit = true; + } + } + else { + childWidth = split.getBounds().getMaxX() - x; + } + } + childBounds = boundsWithXandWidth(bounds, x, childWidth); + layout1(splitChild, childBounds); + + if (( initSplit || getFloatingDividers()) && (dividerChild != null) && dividerChild.isVisible()) { + double dividerX = childBounds.getMaxX(); + Rectangle dividerBounds; + dividerBounds = boundsWithXandWidth(bounds, dividerX, divSize); + dividerChild.setBounds(dividerBounds); + } + if ((dividerChild != null) && dividerChild.isVisible()) { + x = dividerChild.getBounds().getMaxX(); + } + } + } + + /* Layout the Split's child Nodes' along the Y axis. The bounds + * of each child will have the same x coordinate and width as the + * layout1() bounds argument. The algorithm is identical to what's + * explained above, for the X axis case. + */ + else { + double y = bounds.getY(); + while(splitChildren.hasNext()) { + Node splitChild = splitChildren.next(); + if ( !splitChild.isVisible()) { + if ( splitChildren.hasNext()) + splitChildren.next(); + continue; + } + Divider dividerChild = + (splitChildren.hasNext()) ? (Divider)(splitChildren.next()) : null; + + double childHeight = 0.0; + if (getFloatingDividers()) { + childHeight = preferredNodeSize(splitChild).getHeight(); + } + else { + if ((dividerChild != null) && dividerChild.isVisible()) { + double cy = dividerChild.getBounds().getY() - y; + if ( cy > 0.0 ) + childHeight = cy; + else { + childHeight = preferredNodeSize(splitChild).getHeight(); + initSplit = true; + } + } + else { + childHeight = split.getBounds().getMaxY() - y; + } + } + childBounds = boundsWithYandHeight(bounds, y, childHeight); + layout1(splitChild, childBounds); + + if (( initSplit || getFloatingDividers()) && (dividerChild != null) && dividerChild.isVisible()) { + double dividerY = childBounds.getMaxY(); + Rectangle dividerBounds = boundsWithYandHeight(bounds, dividerY, divSize); + dividerChild.setBounds(dividerBounds); + } + if ((dividerChild != null) && dividerChild.isVisible()) { + y = dividerChild.getBounds().getMaxY(); + } + } + } + /* The bounds of the Split node root are set to be just + * big enough to contain all of its children, but only + * along the axis it's allocating space on. That's + * X for rows, Y for columns. The second pass of the + * layout algorithm - see layoutShrink()/layoutGrow() + * allocates extra space. + */ + minimizeSplitBounds(split, bounds); + } + } + + /** + * Get the layout mode + * @return current layout mode + */ + public int getLayoutMode() + { + return layoutMode; + } + + /** + * Set the layout mode. By default this layout uses the preferred and minimum + * sizes of the child components. To ignore the minimum size set the layout + * mode to MultiSplitLayout.LAYOUT_NO_MIN_SIZE. + * @param layoutMode the layout mode + *

    + *
  • DEFAULT_LAYOUT - use the preferred and minimum sizes when sizing the children
  • + *
  • LAYOUT_NO_MIN_SIZE - ignore the minimum size when sizing the children
  • + * + */ + public void setLayoutMode( int layoutMode ) + { + this.layoutMode = layoutMode; + } + + /** + * Get the minimum node size + * @return the minimum size + */ + public int getUserMinSize() + { + return userMinSize; + } + + /** + * Set the user defined minimum size support in the USER_MIN_SIZE_LAYOUT + * layout mode. + * @param minSize the new minimum size + */ + public void setUserMinSize( int minSize ) + { + userMinSize = minSize; + } + + /** + * Get the layoutByWeight falg. If the flag is true the layout initializes + * itself using the model weights + * @return the layoutByWeight + */ + public boolean getLayoutByWeight() + { + return layoutByWeight; + } + + /** + * Sset the layoutByWeight falg. If the flag is true the layout initializes + * itself using the model weights + * @param state the new layoutByWeight to set + */ + public void setLayoutByWeight( boolean state ) + { + layoutByWeight = state; + } + + /** + * The specified Node is either the wrong type or was configured + * incorrectly. + */ + public static class InvalidLayoutException extends RuntimeException { + private final Node node; + public InvalidLayoutException(String msg, Node node) { + super(msg); + this.node = node; + } + /** + * @return the invalid Node. + */ + public Node getNode() { return node; } + } + + private void throwInvalidLayout(String msg, Node node) { + throw new InvalidLayoutException(msg, node); + } + + private void checkLayout(Node root) { + if (root instanceof Split) { + Split split = (Split)root; + if (split.getChildren().size() <= 2) { + throwInvalidLayout("Split must have > 2 children", root); + } + Iterator splitChildren = split.getChildren().iterator(); + double weight = 0.0; + while(splitChildren.hasNext()) { + Node splitChild = splitChildren.next(); + if ( !splitChild.isVisible()) { + if ( splitChildren.hasNext()) + splitChildren.next(); + continue; + } + if (splitChild instanceof Divider) { + continue; + //throwInvalidLayout("expected a Split or Leaf Node", splitChild); + } + if (splitChildren.hasNext()) { + Node dividerChild = splitChildren.next(); + if (!(dividerChild instanceof Divider)) { + throwInvalidLayout("expected a Divider Node", dividerChild); + } + } + weight += splitChild.getWeight(); + checkLayout(splitChild); + } + if (weight > 1.0) { + throwInvalidLayout("Split children's total weight > 1.0", root); + } + } + } + + /** + * Compute the bounds of all of the Split/Divider/Leaf Nodes in + * the layout model, and then set the bounds of each child component + * with a matching Leaf Node. + */ + @Override +public void layoutContainer(Container parent) + { + if ( layoutByWeight && floatingDividers ) + doLayoutByWeight( parent ); + + checkLayout(getModel()); + Insets insets = parent.getInsets(); + Dimension size = parent.getSize(); + int width = size.width - (insets.left + insets.right); + int height = size.height - (insets.top + insets.bottom); + Rectangle bounds = new Rectangle( insets.left, insets.top, width, height); + layout1(getModel(), bounds); + layout2(getModel(), bounds); + } + + + private Divider dividerAt(Node root, int x, int y) { + if (root instanceof Divider) { + Divider divider = (Divider)root; + return (divider.getBounds().contains(x, y)) ? divider : null; + } + else if (root instanceof Split) { + Split split = (Split)root; + for(Node child : split.getChildren()) { + if ( !child.isVisible()) + continue; + if (child.getBounds().contains(x, y)) { + return dividerAt(child, x, y); + } + } + } + return null; + } + + /** + * Return the Divider whose bounds contain the specified + * point, or null if there isn't one. + * + * @param x x coordinate + * @param y y coordinate + * @return the Divider at x,y + */ + public Divider dividerAt(int x, int y) { + return dividerAt(getModel(), x, y); + } + + private boolean nodeOverlapsRectangle(Node node, Rectangle r2) { + Rectangle r1 = node.getBounds(); + return + (r1.x <= (r2.x + r2.width)) && ((r1.x + r1.width) >= r2.x) && + (r1.y <= (r2.y + r2.height)) && ((r1.y + r1.height) >= r2.y); + } + + private List dividersThatOverlap(Node root, Rectangle r) { + if (nodeOverlapsRectangle(root, r) && (root instanceof Split)) { + List dividers = new ArrayList(); + for(Node child : ((Split)root).getChildren()) { + if (child instanceof Divider) { + if (nodeOverlapsRectangle(child, r)) { + dividers.add((Divider)child); + } + } + else if (child instanceof Split) { + dividers.addAll(dividersThatOverlap(child, r)); + } + } + return dividers; + } + else { + return Collections.emptyList(); + } + } + + /** + * Return the Dividers whose bounds overlap the specified + * Rectangle. + * + * @param r target Rectangle + * @return the Dividers that overlap r + * @throws IllegalArgumentException if the Rectangle is null + */ + public List dividersThatOverlap(Rectangle r) { + if (r == null) { + throw new IllegalArgumentException("null Rectangle"); + } + return dividersThatOverlap(getModel(), r); + } + + + /** + * Base class for the nodes that model a MultiSplitLayout. + */ + public static abstract class Node implements Serializable { + private Split parent = null; + private Rectangle bounds = new Rectangle(); + private double weight = 0.0; + private boolean isVisible = true; + public void setVisible( boolean b ) { + isVisible = b; + } + + /** + * Determines whether this node should be visible when its + * parent is visible. Nodes are + * initially visible + * @return true if the node is visible, + * false otherwise + */ + public boolean isVisible() { + return isVisible; + } + + /** + * Returns the Split parent of this Node, or null. + * + * @return the value of the parent property. + * @see #setParent + */ + public Split getParent() { return parent; } + + /** + * Set the value of this Node's parent property. The default + * value of this property is null. + * + * @param parent a Split or null + * @see #getParent + */ + public void setParent(Split parent) { + this.parent = parent; + } + + /** + * Returns the bounding Rectangle for this Node. + * + * @return the value of the bounds property. + * @see #setBounds + */ + public Rectangle getBounds() { + return new Rectangle(this.bounds); + } + + /** + * Set the bounding Rectangle for this node. The value of + * bounds may not be null. The default value of bounds + * is equal to new Rectangle(0,0,0,0). + * + * @param bounds the new value of the bounds property + * @throws IllegalArgumentException if bounds is null + * @see #getBounds + */ + public void setBounds(Rectangle bounds) { + if (bounds == null) { + throw new IllegalArgumentException("null bounds"); + } + this.bounds = new Rectangle(bounds); + } + + /** + * Value between 0.0 and 1.0 used to compute how much space + * to add to this sibling when the layout grows or how + * much to reduce when the layout shrinks. + * + * @return the value of the weight property + * @see #setWeight + */ + public double getWeight() { return weight; } + + /** + * The weight property is a between 0.0 and 1.0 used to + * compute how much space to add to this sibling when the + * layout grows or how much to reduce when the layout shrinks. + * If rowLayout is true then this node's width grows + * or shrinks by (extraSpace * weight). If rowLayout is false, + * then the node's height is changed. The default value + * of weight is 0.0. + * + * @param weight a double between 0.0 and 1.0 + * @see #getWeight + * @see MultiSplitLayout#layoutContainer + * @throws IllegalArgumentException if weight is not between 0.0 and 1.0 + */ + public void setWeight(double weight) { + if ((weight < 0.0)|| (weight > 1.0)) { + throw new IllegalArgumentException("invalid weight"); + } + this.weight = weight; + } + + private Node siblingAtOffset(int offset) { + Split p = getParent(); + if (p == null) { return null; } + List siblings = p.getChildren(); + int index = siblings.indexOf(this); + if (index == -1) { return null; } + index += offset; + return ((index > -1) && (index < siblings.size())) ? siblings.get(index) : null; + } + + /** + * Return the Node that comes after this one in the parent's + * list of children, or null. If this node's parent is null, + * or if it's the last child, then return null. + * + * @return the Node that comes after this one in the parent's list of children. + * @see #previousSibling + * @see #getParent + */ + public Node nextSibling() { + return siblingAtOffset(+1); + } + + /** + * Return the Node that comes before this one in the parent's + * list of children, or null. If this node's parent is null, + * or if it's the last child, then return null. + * + * @return the Node that comes before this one in the parent's list of children. + * @see #nextSibling + * @see #getParent + */ + public Node previousSibling() { + return siblingAtOffset(-1); + } + } + + public static class RowSplit extends Split { + public RowSplit() { + } + + public RowSplit(Node... children) { + setChildren(children); + } + + /** + * Returns true if the this Split's children are to be + * laid out in a row: all the same height, left edge + * equal to the previous Node's right edge. If false, + * children are laid on in a column. + * + * @return the value of the rowLayout property. + * @see #setRowLayout + */ + @Override + public final boolean isRowLayout() { return true; } + } + + public static class ColSplit extends Split { + public ColSplit() { + } + + public ColSplit(Node... children) { + setChildren(children); + } + + /** + * Returns true if the this Split's children are to be + * laid out in a row: all the same height, left edge + * equal to the previous Node's right edge. If false, + * children are laid on in a column. + * + * @return the value of the rowLayout property. + * @see #setRowLayout + */ + @Override + public final boolean isRowLayout() { return false; } + } + + /** + * Defines a vertical or horizontal subdivision into two or more + * tiles. + */ + public static class Split extends Node { + private List children = Collections.emptyList(); + private boolean rowLayout = true; + private String name; + + public Split(Node... children) { + setChildren(children); + } + + /** + * Default constructor to support xml (de)serialization and other bean spec dependent ops. + * Resulting instance of Split is invalid until setChildren() is called. + */ + public Split() { + } + + /** + * Determines whether this node should be visible when its + * parent is visible. Nodes are + * initially visible + * @return true if the node is visible, + * false otherwise + */ + @Override + public boolean isVisible() { + for(Node child : children) { + if ( child.isVisible() && !( child instanceof Divider )) + return true; + } + return false; + } + + /** + * Returns true if the this Split's children are to be + * laid out in a row: all the same height, left edge + * equal to the previous Node's right edge. If false, + * children are laid on in a column. + * + * @return the value of the rowLayout property. + * @see #setRowLayout + */ + public boolean isRowLayout() { return rowLayout; } + + /** + * Set the rowLayout property. If true, all of this Split's + * children are to be laid out in a row: all the same height, + * each node's left edge equal to the previous Node's right + * edge. If false, children are laid on in a column. Default + * value is true. + * + * @param rowLayout true for horizontal row layout, false for column + * @see #isRowLayout + */ + public void setRowLayout(boolean rowLayout) { + this.rowLayout = rowLayout; + } + + /** + * Returns this Split node's children. The returned value + * is not a reference to the Split's internal list of children + * + * @return the value of the children property. + * @see #setChildren + */ + public List getChildren() { + return new ArrayList(children); + } + + + /** + * Remove a node from the layout. Any sibling dividers will also be removed + * @param n the node to be removed + */ + public void remove( Node n ) { + if ( n.nextSibling() instanceof Divider ) + children.remove( n.nextSibling() ); + else if ( n.previousSibling() instanceof Divider ) + children.remove( n.previousSibling() ); + children.remove( n ); + } + + /** + * Replace one node with another. This method is used when a child is removed + * from a split and the split is no longer required, in which case the + * remaining node in the child split can replace the split in the parent + * node + * @param target the node being replaced + * @param replacement the replacement node + */ + public void replace( Node target, Node replacement ) { + int idx = children.indexOf( target ); + children.remove( target ); + children.add( idx, replacement ); + + replacement.setParent ( this ); + target.setParent( this ); + } + + /** + * Change a node to being hidden. Any associated divider nodes are also hidden + * @param target the node to hide + */ + public void hide( Node target ){ + Node next = target.nextSibling(); + if ( next instanceof Divider ) + next.setVisible( false ); + else { + Node prev = target.previousSibling(); + if ( prev instanceof Divider ) + prev.setVisible( false ); + } + target.setVisible( false ); + } + + /** + * Check the dividers to ensure that redundant dividers are hidden and do + * not interfere in the layout, for example when all the children of a split + * are hidden (the split is then invisible), so two dividers may otherwise + * appear next to one another. + * @param split the split to check + */ + public void checkDividers( Split split ) { + ListIterator splitChildren = split.getChildren().listIterator(); + while( splitChildren.hasNext()) { + Node splitChild = splitChildren.next(); + if ( !splitChild.isVisible()) { + continue; + } + else if ( splitChildren.hasNext()) { + Node dividerChild = splitChildren.next(); + if ( dividerChild instanceof Divider ) { + if ( splitChildren.hasNext()) { + Node rightChild = splitChildren.next(); + while ( !rightChild.isVisible()) { + rightChild = rightChild.nextSibling(); + if ( rightChild == null ) { + // No visible right sibling found, so hide the divider + dividerChild.setVisible( false ); + break; + } + } + + // A visible child is found but it's a divider and therefore + // we have to visible and adjacent dividers - so we hide one + if (( rightChild != null ) && ( rightChild instanceof Divider )) + dividerChild.setVisible( false ); + } + } + else if (( splitChild instanceof Divider ) && ( dividerChild instanceof Divider )) { + splitChild.setVisible( false ); + } + } + } + } + + /** + * Restore any of the hidden dividers that are required to separate visible nodes + * @param split the node to check + */ + public void restoreDividers( Split split ) { + boolean nextDividerVisible = false; + ListIterator splitChildren = split.getChildren().listIterator(); + while( splitChildren.hasNext()) { + Node splitChild = splitChildren.next(); + if ( splitChild instanceof Divider ) { + Node prev = splitChild.previousSibling(); + if ( prev.isVisible()) { + Node next = splitChild.nextSibling(); + while ( next != null ) { + if ( next.isVisible()) { + splitChild.setVisible( true ); + break; + } + next = next.nextSibling(); + } + } + } + } + if ( split.getParent() != null ) + restoreDividers( split.getParent()); + } + + /** + * Set's the children property of this Split node. The parent + * of each new child is set to this Split node, and the parent + * of each old child (if any) is set to null. This method + * defensively copies the incoming List. Default value is + * an empty List. + * + * @param children List of children + * @see #getChildren + * @throws IllegalArgumentException if children is null + */ + public void setChildren(List children) { + if (children == null) { + throw new IllegalArgumentException("children must be a non-null List"); + } + for(Node child : this.children) { + child.setParent(null); + } + + this.children = new ArrayList(children); + for(Node child : this.children) { + child.setParent(this); + } + } + + /** + * Convenience method for setting the children of this Split node. The parent + * of each new child is set to this Split node, and the parent + * of each old child (if any) is set to null. This method + * defensively copies the incoming array. + * + * @param children array of children + * @see #getChildren + * @throws IllegalArgumentException if children is null + */ + public void setChildren(Node... children) { + setChildren(children == null ? null : Arrays.asList(children)); + } + + /** + * Convenience method that returns the last child whose weight + * is > 0.0. + * + * @return the last child whose weight is > 0.0. + * @see #getChildren + * @see Node#getWeight + */ + public final Node lastWeightedChild() { + List kids = getChildren(); + Node weightedChild = null; + for(Node child : kids) { + if ( !child.isVisible()) + continue; + if (child.getWeight() > 0.0) { + weightedChild = child; + } + } + return weightedChild; + } + + /** + * Return the Leaf's name. + * + * @return the value of the name property. + * @see #setName + */ + public String getName() { return name; } + + /** + * Set the value of the name property. Name may not be null. + * + * @param name value of the name property + * @throws IllegalArgumentException if name is null + */ + public void setName(String name) { + if (name == null) { + throw new IllegalArgumentException("name is null"); + } + this.name = name; + } + + @Override + public String toString() { + int nChildren = getChildren().size(); + StringBuffer sb = new StringBuffer("MultiSplitLayout.Split"); + sb.append(" \""); + sb.append(getName()); + sb.append("\""); + sb.append(isRowLayout() ? " ROW [" : " COLUMN ["); + sb.append(nChildren + ((nChildren == 1) ? " child" : " children")); + sb.append("] "); + sb.append(getBounds()); + return sb.toString(); + } + } + + + /** + * Models a java.awt Component child. + */ + public static class Leaf extends Node { + private String name = ""; + + /** + * Create a Leaf node. The default value of name is "". + */ + public Leaf() { } + + + /** + * Create a Leaf node with the specified name. Name can not + * be null. + * + * @param name value of the Leaf's name property + * @throws IllegalArgumentException if name is null + */ + public Leaf(String name) { + if (name == null) { + throw new IllegalArgumentException("name is null"); + } + this.name = name; + } + + /** + * Return the Leaf's name. + * + * @return the value of the name property. + * @see #setName + */ + public String getName() { return name; } + + /** + * Set the value of the name property. Name may not be null. + * + * @param name value of the name property + * @throws IllegalArgumentException if name is null + */ + public void setName(String name) { + if (name == null) { + throw new IllegalArgumentException("name is null"); + } + this.name = name; + } + + @Override + public String toString() { + StringBuffer sb = new StringBuffer("MultiSplitLayout.Leaf"); + sb.append(" \""); + sb.append(getName()); + sb.append("\""); + sb.append(" weight="); + sb.append(getWeight()); + sb.append(" "); + sb.append(getBounds()); + return sb.toString(); + } + } + + + /** + * Models a single vertical/horiztonal divider. + */ + public static class Divider extends Node { + /** + * Convenience method, returns true if the Divider's parent + * is a Split row (a Split with isRowLayout() true), false + * otherwise. In other words if this Divider's major axis + * is vertical, return true. + * + * @return true if this Divider is part of a Split row. + */ + public final boolean isVertical() { + Split parent = getParent(); + return (parent != null) ? parent.isRowLayout() : false; + } + + /** + * Dividers can't have a weight, they don't grow or shrink. + * @throws UnsupportedOperationException + */ + @Override + public void setWeight(double weight) { + throw new UnsupportedOperationException(); + } + + @Override + public String toString() { + return "MultiSplitLayout.Divider " + getBounds().toString(); + } + } + + + private static void throwParseException(StreamTokenizer st, String msg) throws Exception { + throw new Exception("MultiSplitLayout.parseModel Error: " + msg); + } + + private static void parseAttribute(String name, StreamTokenizer st, Node node) throws Exception { + if ((st.nextToken() != '=')) { + throwParseException(st, "expected '=' after " + name); + } + if (name.equalsIgnoreCase("WEIGHT")) { + if (st.nextToken() == StreamTokenizer.TT_NUMBER) { + node.setWeight(st.nval); + } + else { + throwParseException(st, "invalid weight"); + } + } + else if (name.equalsIgnoreCase("NAME")) { + if (st.nextToken() == StreamTokenizer.TT_WORD) { + if (node instanceof Leaf) { + ((Leaf)node).setName(st.sval); + } + else if (node instanceof Split) { + ((Split)node).setName(st.sval); + } + else { + throwParseException(st, "can't specify name for " + node); + } + } + else { + throwParseException(st, "invalid name"); + } + } + else { + throwParseException(st, "unrecognized attribute \"" + name + "\""); + } + } + + private static void addSplitChild(Split parent, Node child) { + List children = new ArrayList(parent.getChildren()); + if (children.size() == 0) { + children.add(child); + } + else { + children.add(new Divider()); + children.add(child); + } + parent.setChildren(children); + } + + private static void parseLeaf(StreamTokenizer st, Split parent) throws Exception { + Leaf leaf = new Leaf(); + int token; + while ((token = st.nextToken()) != StreamTokenizer.TT_EOF) { + if (token == ')') { + break; + } + if (token == StreamTokenizer.TT_WORD) { + parseAttribute(st.sval, st, leaf); + } + else { + throwParseException(st, "Bad Leaf: " + leaf); + } + } + addSplitChild(parent, leaf); + } + + private static void parseSplit(StreamTokenizer st, Split parent) throws Exception { + int token; + while ((token = st.nextToken()) != StreamTokenizer.TT_EOF) { + if (token == ')') { + break; + } + else if (token == StreamTokenizer.TT_WORD) { + if (st.sval.equalsIgnoreCase("WEIGHT")) { + parseAttribute(st.sval, st, parent); + } + else if (st.sval.equalsIgnoreCase("NAME")) { + parseAttribute(st.sval, st, parent); + } + else { + addSplitChild(parent, new Leaf(st.sval)); + } + } + else if (token == '(') { + if ((token = st.nextToken()) != StreamTokenizer.TT_WORD) { + throwParseException(st, "invalid node type"); + } + String nodeType = st.sval.toUpperCase(); + if (nodeType.equals("LEAF")) { + parseLeaf(st, parent); + } + else if (nodeType.equals("ROW") || nodeType.equals("COLUMN")) { + Split split = new Split(); + split.setRowLayout(nodeType.equals("ROW")); + addSplitChild(parent, split); + parseSplit(st, split); + } + else { + throwParseException(st, "unrecognized node type '" + nodeType + "'"); + } + } + } + } + + private static Node parseModel(Reader r) { + StreamTokenizer st = new StreamTokenizer(r); + try { + Split root = new Split(); + parseSplit(st, root); + return root.getChildren().get(0); + } + catch (Exception e) { + System.err.println(e); + } + finally { + try { r.close(); } catch (IOException ignore) {} + } + return null; + } + + /** + * A convenience method that converts a string to a + * MultiSplitLayout model (a tree of Nodes) using a + * a simple syntax. Nodes are represented by + * parenthetical expressions whose first token + * is one of ROW/COLUMN/LEAF. ROW and COLUMN specify + * horizontal and vertical Split nodes respectively, + * LEAF specifies a Leaf node. A Leaf's name and + * weight can be specified with attributes, + * name=myLeafName weight=myLeafWeight. + * Similarly, a Split's weight can be specified with + * weight=mySplitWeight. + * + *

    For example, the following expression generates + * a horizontal Split node with three children: + * the Leafs named left and right, and a Divider in + * between: + *

    +   * (ROW (LEAF name=left) (LEAF name=right weight=1.0))
    +   * 
    + * + *

    Dividers should not be included in the string, + * they're added automatcially as needed. Because + * Leaf nodes often only need to specify a name, one + * can specify a Leaf by just providing the name. + * The previous example can be written like this: + *

    +   * (ROW left (LEAF name=right weight=1.0))
    +   * 
    + * + *

    Here's a more complex example. One row with + * three elements, the first and last of which are columns + * with two leaves each: + *

    +   * (ROW (COLUMN weight=0.5 left.top left.bottom)
    +   *      (LEAF name=middle)
    +   *      (COLUMN weight=0.5 right.top right.bottom))
    +   * 
    + * + * + *

    This syntax is not intended for archiving or + * configuration files . It's just a convenience for + * examples and tests. + * + * @return the Node root of a tree based on s. + */ + public static Node parseModel(String s) { + return parseModel(new StringReader(s)); + } + + + private static void printModel(String indent, Node root) { + if (root instanceof Split) { + Split split = (Split)root; + System.out.println(indent + split); + for(Node child : split.getChildren()) { + printModel(indent + " ", child); + } + } + else { + System.out.println(indent + root); + } + } + + /** + * Print the tree with enough detail for simple debugging. + */ + public static void printModel(Node root) { + printModel("", root); + } +} diff --git a/src/main/java/org/jdesktop/swingx/RepaintManagerX.java b/src/main/java/org/jdesktop/swingx/RepaintManagerX.java new file mode 100644 index 0000000000..67c81cee0b --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/RepaintManagerX.java @@ -0,0 +1,66 @@ +/* + * $Id: RepaintManagerX.java 4249 2012-11-13 18:12:49Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx; + +import javax.swing.*; +import java.awt.*; + +/** + *

    An implementation of {@link RepaintManager} which adds support for transparency + * in {@link JXPanel}s. JXPanel (which supports translucency) will + * replace the current RepaintManager with an instance of RepaintManagerX + * unless the current RepaintManager is tagged by the {@link TranslucentRepaintManager} + * annotation.

    + * + * @author zixle + * @author rbair + * @author Karl Schaefer + */ +@TranslucentRepaintManager +public class RepaintManagerX extends ForwardingRepaintManager { + /** + * Creates a new manager that forwards all calls to the delegate. + * + * @param delegate + * the manager backing this {@code RepaintManagerX} + * @throws NullPointerException + * if {@code delegate} is {@code null} + */ + public RepaintManagerX(RepaintManager delegate) { + super(delegate); + } + + /** + * {@inheritDoc} + */ + @Override + public void addDirtyRegion(JComponent c, int x, int y, int w, int h) { + AlphaPaintable alphaPaintable = (AlphaPaintable) SwingUtilities.getAncestorOfClass(AlphaPaintable.class, c); + + if (alphaPaintable != null && alphaPaintable.getAlpha() < 1f) { + Point p = SwingUtilities.convertPoint(c, x, y, (JComponent) alphaPaintable); + addDirtyRegion((JComponent) alphaPaintable, p.x, p.y, w, h); + } else { + super.addDirtyRegion(c, x, y, w, h); + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/ScrollableSizeHint.java b/src/main/java/org/jdesktop/swingx/ScrollableSizeHint.java new file mode 100644 index 0000000000..e5ed79b928 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/ScrollableSizeHint.java @@ -0,0 +1,146 @@ +/* + * $Id$ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx; + +import org.jdesktop.swingx.util.Contract; + +import javax.swing.*; + +/** + * Sizing hints for layout, useful f.i. in a Scrollable implementation.

    + * + * Inspired by Rob Camick. + *

    + * PENDING JW: naming... suggestions?
    + * KS: I'd go with TrackingHint or ScrollableTrackingHint, since it is used in getScrollableTracksViewportXXX. + * + * + * @author Jeanette Winzenburg + * @author Karl Schaefer + */ +@SuppressWarnings("nls") +public enum ScrollableSizeHint { + /** + * Size should be unchanged. + */ + NONE { + /** + * {@inheritDoc} + */ + @Override + boolean getTracksParentSizeImpl(JComponent component, int orientation) { + return false; + } + }, + + /** + * Size should be adjusted to parent size. + */ + FIT { + /** + * {@inheritDoc} + */ + @Override + boolean getTracksParentSizeImpl(JComponent component, int orientation) { + return true; + } + }, + + /** + * Stretches the component when its parent is larger than its minimum size. + */ + MINIMUM_STRETCH { + /** + * {@inheritDoc} + */ + @Override + boolean getTracksParentSizeImpl(JComponent component, int orientation) { + switch (orientation) { + case SwingConstants.HORIZONTAL: + return component.getParent() instanceof JViewport + && component.getParent().getWidth() > component.getMinimumSize().width + && component.getParent().getWidth() < component.getMaximumSize().width; + case SwingConstants.VERTICAL: + return component.getParent() instanceof JViewport + && component.getParent().getHeight() > component.getMinimumSize().height + && component.getParent().getHeight() < component.getMaximumSize().height; + default: + throw new IllegalArgumentException("invalid orientation"); + } + } + }, + + /** + * Stretches the component when its parent is larger than its preferred size. + */ + PREFERRED_STRETCH { + /** + * {@inheritDoc} + */ + @Override + boolean getTracksParentSizeImpl(JComponent component, int orientation) { + switch (orientation) { + case SwingConstants.HORIZONTAL: + return component.getParent() instanceof JViewport + && component.getParent().getWidth() > component.getPreferredSize().width + && component.getParent().getWidth() < component.getMaximumSize().width; + case SwingConstants.VERTICAL: + return component.getParent() instanceof JViewport + && component.getParent().getHeight() > component.getPreferredSize().height + && component.getParent().getHeight() < component.getMaximumSize().height; + default: + throw new IllegalArgumentException("invalid orientation"); + } + } + }, + ; + + /** + * Returns a boolean indicating whether the component's size should be + * adjusted to parent. + * + * @param component the component resize, must not be null + * @return a boolean indicating whether the component's size should be + * adjusted to parent + * + * @throws NullPointerException if component is null + * @throws IllegalArgumentException if orientation is invalid + */ + public boolean getTracksParentSize(JComponent component, int orientation) { + Contract.asNotNull(component, "component must be not-null"); + + return getTracksParentSizeImpl(component, orientation); + } + + /** + * Determines whether the supplied component is smaller than its parent; used to determine + * whether to track with the parents size. + * + * @param component + * the component to test + * @param orientation + * the orientation to test + * @return {@code true} to track; {@code false} otherwise + */ + abstract boolean getTracksParentSizeImpl(JComponent component, int orientation); +} diff --git a/src/main/java/org/jdesktop/swingx/StackLayout.java b/src/main/java/org/jdesktop/swingx/StackLayout.java new file mode 100644 index 0000000000..8b18227229 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/StackLayout.java @@ -0,0 +1,196 @@ +/* + * $Id: StackLayout.java 3793 2010-09-28 05:15:02Z kschaefe $ + * + * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx; + +import java.awt.*; +import java.util.LinkedList; +import java.util.List; + +/** + *

    StackLayout is a Swing layout aimed to act as the layers + * stack of most popuplar graphics editing tools like The GIMP or + * Photoshop. While similar to CardLayout, this layout + * displays all the components of the container. If you are using non-rectangular + * components (i.e. transparent) you will see them from top to bottom of the + * stack.

    + *

    When using this layout, each component can be added in the container + * either on top of the stack or at the bottom:

    + *
    + * JPanel panel = new JPanel(new StackLayout());
    + * panel.add(new JLabel("On top"),    StackLayout.TOP);
    + * panel.add(new JLabel("At bottom"), StackLayout.BOTTOM);
    + * 
    + * If you don't specify the constraint, the component will be added at the top + * of the components stack.

    + *

    All the components managed by this layout will be given the same size as + * the container itself. The minimum, maximum and preferred size of the + * container are based upon the largest minimum, maximum and preferred size of + * the children components.

    + *

    StackLayout works only with JSE 1.5 and Java SE 6 and + * greater.

    + * + * @author Romain Guy + */ + +public class StackLayout implements LayoutManager2 { + /** Use this constraint to add a component at the bottom of the stack. */ + public static final String BOTTOM = "bottom"; + /** Use this contrainst to add a component at the top of the stack. */ + public static final String TOP = "top"; + + // removing components does not happen often compared to adding components + // hence we choose a linked list to make insertion at the bottom faster + private List components = new LinkedList(); + + /** + * {@inheritDoc} + */ + @Override + public void addLayoutComponent(final Component comp, + final Object constraints) { + synchronized (comp.getTreeLock()) { + if (BOTTOM.equals(constraints)) { + components.add(0, comp); + } else if (TOP.equals(constraints)) { + components.add(comp); + } else { + components.add(comp); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public void addLayoutComponent(final String name, final Component comp) { + addLayoutComponent(comp, TOP); + } + + /** + * {@inheritDoc} + */ + @Override + public void removeLayoutComponent(final Component comp) { + synchronized (comp.getTreeLock()) { + components.remove(comp); + } + } + + /** + * {@inheritDoc} + */ + @Override + public float getLayoutAlignmentX(final Container target) { + return 0.5f; + } + + /** + * {@inheritDoc} + */ + @Override + public float getLayoutAlignmentY(final Container target) { + return 0.5f; + } + + /** + * {@inheritDoc} + */ + @Override + public void invalidateLayout(final Container target) { + } + + /** + * {@inheritDoc} + */ + @Override + public Dimension preferredLayoutSize(final Container parent) { + synchronized (parent.getTreeLock()) { + int width = 0; + int height = 0; + + for (Component comp: components) { + Dimension size = comp.getPreferredSize(); + width = Math.max(size.width, width); + height = Math.max(size.height, height); + } + + Insets insets = parent.getInsets(); + width += insets.left + insets.right; + height += insets.top + insets.bottom; + + return new Dimension(width, height); + } + } + + /** + * {@inheritDoc} + */ + @Override + public Dimension minimumLayoutSize(final Container parent) { + synchronized (parent.getTreeLock()) { + int width = 0; + int height = 0; + + for (Component comp: components) { + Dimension size = comp.getMinimumSize(); + width = Math.max(size.width, width); + height = Math.max(size.height, height); + } + + Insets insets = parent.getInsets(); + width += insets.left + insets.right; + height += insets.top + insets.bottom; + + return new Dimension(width, height); + } + } + + /** + * {@inheritDoc} + */ + @Override + public Dimension maximumLayoutSize(final Container target) { + return new Dimension(Integer.MAX_VALUE, + Integer.MAX_VALUE); + } + + /** + * {@inheritDoc} + */ + @Override + public void layoutContainer(final Container parent) { + synchronized (parent.getTreeLock()) { + int width = parent.getWidth(); + int height = parent.getHeight(); + + Rectangle bounds = new Rectangle(0, 0, width, height); + + int componentsCount = components.size(); + + for (int i = 0; i < componentsCount; i++) { + Component comp = components.get(i); + comp.setBounds(bounds); + parent.setComponentZOrder(comp, componentsCount - i - 1); + } + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/SwingXUtilities.java b/src/main/java/org/jdesktop/swingx/SwingXUtilities.java new file mode 100644 index 0000000000..fade647a3d --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/SwingXUtilities.java @@ -0,0 +1,603 @@ +/* + * $Id: SwingXUtilities.java 4262 2012-11-19 18:40:10Z kschaefe $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx; + +import org.jdesktop.swingx.painter.Painter; + +import javax.swing.*; +import javax.swing.plaf.ComponentInputMapUIResource; +import javax.swing.plaf.UIResource; +import javax.swing.text.html.HTMLDocument; +import java.awt.*; +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; +import java.io.IOException; +import java.io.StringReader; +import java.lang.reflect.InvocationTargetException; +import java.util.Locale; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.FutureTask; + +/** + * A collection of utility methods for Swing(X) classes. + * + *
      + * PENDING JW: think about location of this class and/or its methods, Options: + * + *
    • move this class to the swingx utils package which already has a bunch of xxUtils + *
    • move methods between xxUtils classes as appropriate (one window/comp related util) + *
    • keep here in swingx (consistent with swingutilities in core) + *
    + * @author Karl George Schaefer + */ +public final class SwingXUtilities { + private SwingXUtilities() { + //does nothing + } + + + /** + * A helper for creating and updating key bindings for components with + * mnemonics. The {@code pressed} action will be invoked when the mnemonic + * is activated. + * + * @param c + * the component bindings to update + * @param pressed + * the name of the action in the action map to invoke when the + * mnemonic is pressed + * @throws NullPointerException + * if the component is {@code null} + */ + public static void updateMnemonicBinding(T c, String pressed) { + updateMnemonicBinding(c, pressed, null); + } + + /** + * A helper for creating and updating key bindings for components with + * mnemonics. The {@code pressed} action will be invoked when the mnemonic + * is activated and the {@code released} action will be invoked when the + * mnemonic is deactivated. + * + * @param c + * the component bindings to update + * @param pressed + * the name of the action in the action map to invoke when the + * mnemonic is pressed + * @param released + * the name of the action in the action map to invoke when the + * mnemonic is released (if the action is a toggle style, then + * this parameter should be {@code null}) + * @throws NullPointerException + * if the component is {@code null} + */ + public static void updateMnemonicBinding(T c, String pressed, String released) { + int m = c.getMnemonic(); + + InputMap map = SwingUtilities.getUIInputMap(c, + JComponent.WHEN_IN_FOCUSED_WINDOW); + + if (m != 0) { + if (map == null) { + map = new ComponentInputMapUIResource(c); + SwingUtilities.replaceUIInputMap(c, + JComponent.WHEN_IN_FOCUSED_WINDOW, map); + } + + map.clear(); + + //TODO is ALT_MASK right for all platforms? + map.put(KeyStroke.getKeyStroke(m, InputEvent.ALT_MASK, false), + pressed); + map.put(KeyStroke.getKeyStroke(m, InputEvent.ALT_MASK, true), + released); + map.put(KeyStroke.getKeyStroke(m, 0, true), released); + } else { + if (map != null) { + map.clear(); + } + } + } + + @SuppressWarnings("unchecked") + static void paintBackground(C comp, Graphics2D g) { + // we should be painting the background behind the painter if we have one + // this prevents issues with buffer reuse where visual artifacts sneak in + if (comp.isOpaque() + || (comp instanceof AlphaPaintable && ((AlphaPaintable) comp).getAlpha() < 1f) + || UIManager.getLookAndFeel().getID().equals("Nimbus")) { + g.setColor(comp.getBackground()); + g.fillRect(0, 0, comp.getWidth(), comp.getHeight()); + } + + Painter painter = comp.getBackgroundPainter(); + + if (painter != null) { + if (comp.isPaintBorderInsets()) { + painter.paint(g, comp, comp.getWidth(), comp.getHeight()); + } else { + Insets insets = comp.getInsets(); + g.translate(insets.left, insets.top); + painter.paint(g, comp, comp.getWidth() - insets.left - insets.right, + comp.getHeight() - insets.top - insets.bottom); + g.translate(-insets.left, -insets.top); + } + } + } + + private static Component[] getChildren(Component c) { + Component[] children = null; + + if (c instanceof MenuElement) { + MenuElement[] elements = ((MenuElement) c).getSubElements(); + children = new Component[elements.length]; + + for (int i = 0; i < elements.length; i++) { + children[i] = elements[i].getComponent(); + } + } else if (c instanceof Container) { + children = ((Container) c).getComponents(); + } + + return children; + } + + /** + * Enables or disables of the components in the tree starting with {@code c}. + * + * @param c + * the starting component + * @param enabled + * {@code true} if the component is to enabled; {@code false} otherwise + */ + public static void setComponentTreeEnabled(Component c, boolean enabled) { + c.setEnabled(enabled); + + Component[] children = getChildren(c); + + if (children != null) { + for(int i = 0; i < children.length; i++) { + setComponentTreeEnabled(children[i], enabled); + } + } + } + + /** + * Sets the locale for an entire component hierarchy to the specified + * locale. + * + * @param c + * the starting component + * @param locale + * the locale to set + */ + public static void setComponentTreeLocale(Component c, Locale locale) { + c.setLocale(locale); + + Component[] children = getChildren(c); + + if (children != null) { + for(int i = 0; i < children.length; i++) { + setComponentTreeLocale(children[i], locale); + } + } + } + + /** + * Sets the background for an entire component hierarchy to the specified + * color. + * + * @param c + * the starting component + * @param color + * the color to set + */ + public static void setComponentTreeBackground(Component c, Color color) { + c.setBackground(color); + + Component[] children = getChildren(c); + + if (children != null) { + for(int i = 0; i < children.length; i++) { + setComponentTreeBackground(children[i], color); + } + } + } + + /** + * Sets the foreground for an entire component hierarchy to the specified + * color. + * + * @param c + * the starting component + * @param color + * the color to set + */ + public static void setComponentTreeForeground(Component c, Color color) { + c.setForeground(color); + + Component[] children = getChildren(c); + + if (children != null) { + for(int i = 0; i < children.length; i++) { + setComponentTreeForeground(children[i], color); + } + } + } + + /** + * Sets the font for an entire component hierarchy to the specified font. + * + * @param c + * the starting component + * @param font + * the font to set + */ + public static void setComponentTreeFont(Component c, Font font) { + c.setFont(font); + + Component[] children = getChildren(c); + + if (children != null) { + for(int i = 0; i < children.length; i++) { + setComponentTreeFont(children[i], font); + } + } + } + + private static String STYLESHEET = + "body { margin-top: 0; margin-bottom: 0; margin-left: 0; margin-right: 0;" + + " font-family: %s; font-size: %dpt; }" + + "a, p, li { margin-top: 0; margin-bottom: 0; margin-left: 0;" + + " margin-right: 0; font-family: %s; font-size: %dpt; }"; + + /** + * Sets the font used for HTML displays to the specified font. Components + * that display HTML do not necessarily honor font properties, since the + * HTML document can override these values. Calling {@code setHtmlFont} + * after the data is set will force the HTML display to use the font + * specified to this method. + * + * @param doc + * the HTML document to update + * @param font + * the font to use + * @throws NullPointerException + * if any parameter is {@code null} + */ + public static void setHtmlFont(HTMLDocument doc, Font font) { + String stylesheet = String.format(STYLESHEET, font.getName(), + font.getSize(), font.getName(), font.getSize()); + + try { + doc.getStyleSheet().loadRules(new StringReader(stylesheet), null); + } catch (IOException e) { + //this should never happen with our sheet + throw new IllegalStateException(e); + } + } + + /** + * Updates the componentTreeUI of all top-level windows of the + * current application. + * + */ + public static void updateAllComponentTreeUIs() { +// for (Frame frame : Frame.getFrames()) { +// updateAllComponentTreeUIs(frame); +// } + // JW: updated to new 1.6 api - returns all windows, owned and ownerless + for (Window window: Window.getWindows()) { + SwingUtilities.updateComponentTreeUI(window); + } + } + + + + /** + * Updates the componentTreeUI of the given window and all its + * owned windows, recursively. + * + * + * @param window the window to update + */ + public static void updateAllComponentTreeUIs(Window window) { + SwingUtilities.updateComponentTreeUI(window); + for (Window owned : window.getOwnedWindows()) { + updateAllComponentTreeUIs(owned); + } + } + + /** + * A version of {@link SwingUtilities#invokeLater(Runnable)} that supports return values. + * + * @param + * the return type of the callable + * @param callable + * the callable to execute + * @return a future task for accessing the return value + * @see Callable + */ + public static FutureTask invokeLater(Callable callable) { + FutureTask task = new FutureTask(callable); + + SwingUtilities.invokeLater(task); + + return task; + } + + /** + * A version of {@link SwingUtilities#invokeAndWait(Runnable)} that supports return values. + * + * @param + * the return type of the callable + * @param callable + * the callable to execute + * @return the value returned by the callable + * @throws InterruptedException + * if we're interrupted while waiting for the event dispatching thread to finish + * executing {@code callable.call()} + * @throws InvocationTargetException + * if an exception is thrown while running {@code callable} + * @see Callable + */ + public static T invokeAndWait(Callable callable) throws InterruptedException, + InvocationTargetException { + try { + //blocks until future returns + return invokeLater(callable).get(); + } catch (ExecutionException e) { + Throwable t = e.getCause(); + + if (t instanceof RuntimeException) { + throw (RuntimeException) t; + } else if (t instanceof InvocationTargetException) { + throw (InvocationTargetException) t; + } else { + throw new InvocationTargetException(t); + } + } + } + + /** + * An improved version of + * {@link SwingUtilities#getAncestorOfClass(Class, Component)}. This method + * traverses {@code JPopupMenu} invoker and uses generics to return an + * appropriately typed object. + * + * @param + * the type of ancestor to find + * @param clazz + * the class instance of the ancestor to find + * @param c + * the component to start the search from + * @return an ancestor of the correct type or {@code null} if no such + * ancestor exists. This method also returns {@code null} if any + * parameter is {@code null}. + */ + @SuppressWarnings("unchecked") + public static T getAncestor(Class clazz, Component c) { + if (clazz == null || c == null) { + return null; + } + + Component parent = c.getParent(); + + while (parent != null && !(clazz.isInstance(parent))) { + parent = parent instanceof JPopupMenu + ? ((JPopupMenu) parent).getInvoker() : parent.getParent(); + } + + return (T) parent; + } + + /** + * Returns whether the component is part of the parent's + * container hierarchy. If a parent in the chain is of type + * JPopupMenu, the parent chain of its invoker is walked. + * + * @param focusOwner + * @param parent + * @return true if the component is contained under the parent's + * hierarchy, coping with JPopupMenus. + */ + public static boolean isDescendingFrom(Component focusOwner, Component parent) { + while (focusOwner != null) { + if (focusOwner instanceof JPopupMenu) { + focusOwner = ((JPopupMenu) focusOwner).getInvoker(); + if (focusOwner == null) { + return false; + } + } + if (focusOwner == parent) { + return true; + } + focusOwner = focusOwner.getParent(); + } + return false; + } + + /** + * Obtains a {@code TranslucentRepaintManager} from the specified manager. + * If the current manager is a {@code TranslucentRepaintManager} or a + * {@code ForwardingRepaintManager} that contains a {@code + * TranslucentRepaintManager}, then the passed in manager is returned. + * Otherwise a new repaint manager is created and returned. + * + * @param delegate + * the current repaint manager + * @return a non-{@code null} {@code TranslucentRepaintManager} + * @throws NullPointerException if {@code delegate} is {@code null} + */ + static RepaintManager getTranslucentRepaintManager(RepaintManager delegate) { + RepaintManager manager = delegate; + + while (manager != null && !manager.getClass().isAnnotationPresent(TranslucentRepaintManager.class)) { + if (manager instanceof ForwardingRepaintManager) { + manager = ((ForwardingRepaintManager) manager).getDelegateManager(); + } else { + manager = null; + } + } + + return manager == null ? new RepaintManagerX(delegate) : delegate; + } + + /** + * Checks and returns whether the given property should be replaced + * by the UI's default value. + * + * @param property the property to check. + * @return true if the given property should be replaced by the UI's + * default value, false otherwise. + */ + public static boolean isUIInstallable(Object property) { + return (property == null) || (property instanceof UIResource); + } + +//---- methods c&p'ed from SwingUtilities2 to reduce dependencies on sun packages + + /** + * Updates lead and anchor selection index without changing the selection. + * + * Note: this is c&p'ed from SwingUtilities2 to not have any direct + * dependency. + * + * @param selectionModel the selection model to change lead/anchor + * @param lead the lead selection index + * @param anchor the anchor selection index + */ + public static void setLeadAnchorWithoutSelection( + ListSelectionModel selectionModel, int lead, int anchor) { + if (anchor == -1) { + anchor = lead; + } + if (lead == -1) { + selectionModel.setAnchorSelectionIndex(-1); + selectionModel.setLeadSelectionIndex(-1); + } else { + if (selectionModel.isSelectedIndex(lead)) + selectionModel.addSelectionInterval(lead, lead); + else { + selectionModel.removeSelectionInterval(lead, lead); + } + selectionModel.setAnchorSelectionIndex(anchor); + } + } + + public static boolean shouldIgnore(MouseEvent mouseEvent, + JComponent component) { + return ((component == null) || (!(component.isEnabled())) + || (!(SwingUtilities.isLeftMouseButton(mouseEvent))) + || (mouseEvent.isConsumed())); + } + + + public static int loc2IndexFileList(JList list, Point point) { + int i = list.locationToIndex(point); + if (i != -1) { + Object localObject = list + .getClientProperty("List.isFileList"); + if ((localObject instanceof Boolean) + && (((Boolean) localObject).booleanValue()) + // PENDING JW: this isn't aware of sorting/filtering - fix! + && (!(pointIsInActualBounds(list, i, point)))) { + i = -1; + } + } + return i; + } + + // PENDING JW: this isn't aware of sorting/filtering - fix! + private static boolean pointIsInActualBounds(JList list, int index, + Point point) { + ListCellRenderer renderer = list.getCellRenderer(); + ListModel model = list.getModel(); + Object element = model.getElementAt(index); + Component comp = renderer.getListCellRendererComponent(list, element, + index, false, false); + + Dimension prefSize = comp.getPreferredSize(); + Rectangle cellBounds = list.getCellBounds(index, index); + if (!(comp.getComponentOrientation().isLeftToRight())) { + cellBounds.x += cellBounds.width - prefSize.width; + } + cellBounds.width = prefSize.width; + + return cellBounds.contains(point); + } + + public static void adjustFocus(JComponent component) { + if ((!(component.hasFocus())) && (component.isRequestFocusEnabled())) + component.requestFocus(); + } + + public static int convertModifiersToDropAction(int modifiers, + int sourcActions) { + // PENDING JW: c'p from a decompiled SunDragSourceContextPeer + // PENDING JW: haha ... completely readable, right ;-) + int i = 0; + + switch (modifiers & 0xC0) { + case 192: + i = 1073741824; + break; + case 128: + i = 1; + break; + case 64: + i = 2; + break; + default: + if ((sourcActions & 0x2) != 0) { + i = 2; + break; + } + if ((sourcActions & 0x1) != 0) { + i = 1; + break; + } + if ((sourcActions & 0x40000000) == 0) + break; + i = 1073741824; + } + + // label88: + return (i & sourcActions); + } + + private static final Class appletClass; + + static { + Class cls; + try { + cls = Class.forName("java.applet.Applet"); + } catch (ClassNotFoundException ex) { + cls = null; + } + appletClass = cls; + } + + static boolean isApplet(Component c) { + return appletClass != null && appletClass.isInstance(c); + } +} diff --git a/src/main/java/org/jdesktop/swingx/TranslucentRepaintManager.java b/src/main/java/org/jdesktop/swingx/TranslucentRepaintManager.java new file mode 100644 index 0000000000..44ad694ba9 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/TranslucentRepaintManager.java @@ -0,0 +1,51 @@ +/* + * $Id: TranslucentRepaintManager.java 2476 2007-11-25 15:52:59Z kschaefe $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + *

    An annotation that can be applied to a {@link javax.swing.RepaintManager} to suggest that + * the RepaintManager supports translucency. If a JXPanel + * is made translucent by setting it's alpha property to a value between 0 and 1, + * then the JXPanel must ensure that a RepaintManager + * capable of handling transparency is installed. This annotation tells the + * JXPanel that the installed RepaintManager does not + * need to be replaced. This is critical for custom RepaintManagers + * which are used in applications along with transparent JXPanels.

    + * + *

    A RepaintManager supports translucency if, when a repaint on a + * child component occurs, it begins painting not on the child component, + * but on the child component's JXPanel ancestor if: a) there is such + * an ancestor and b) the ancestor returns an effective alpha of < 1.

    + * + * @see RepaintManagerX + * @see JXPanel + * @author rbair + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface TranslucentRepaintManager { +} diff --git a/src/main/java/org/jdesktop/swingx/VerticalLayout.java b/src/main/java/org/jdesktop/swingx/VerticalLayout.java new file mode 100644 index 0000000000..1a88edc608 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/VerticalLayout.java @@ -0,0 +1,123 @@ +/* + * $Id: VerticalLayout.java 4147 2012-02-01 17:13:24Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.util.Separator; + +import java.awt.*; +import java.io.Serial; + +/** + * Organizes components in a vertical layout. + * + * @author fred + * @author Karl Schaefer + */ +@JavaBean +public class VerticalLayout extends AbstractLayoutManager { + @Serial + private static final long serialVersionUID = 5342270033773736441L; + + private int gap; + + /** + * Creates a layout without a gap between components. + */ + public VerticalLayout() { + this(0); + } + + /** + * Creates a layout with the specified gap between components. + * + * @param gap + * the gap between components + */ + //TODO should we allow negative gaps? + public VerticalLayout(int gap) { + this.gap = gap; + } + + /** + * The current gap to place between components. + * + * @return the current gap + */ + public int getGap() { + return gap; + } + + /** + * The new gap to place between components. + * + * @param gap + * the new gap + */ + //TODO should we allow negative gaps? + public void setGap(int gap) { + this.gap = gap; + } + + /** + * {@inheritDoc} + */ + @Override + public Dimension preferredLayoutSize(Container parent) { + Dimension pref = new Dimension(0, 0); + Separator sep = new Separator<>(0, gap); + + for (int i = 0, c = parent.getComponentCount(); i < c; i++) { + Component m = parent.getComponent(i); + + if (m.isVisible()) { + Dimension componentPreferredSize = parent.getComponent(i).getPreferredSize(); + pref.height += componentPreferredSize.height + sep.get(); + pref.width = Math.max(pref.width, componentPreferredSize.width); + } + } + + Insets insets = parent.getInsets(); + pref.width += insets.left + insets.right; + pref.height += insets.top + insets.bottom; + + return pref; + } + + /** + * {@inheritDoc} + */ + @Override + public void layoutContainer(Container parent) { + Insets insets = parent.getInsets(); + Dimension size = parent.getSize(); + int width = size.width - insets.left - insets.right; + int height = insets.top; + + for (int i = 0, c = parent.getComponentCount(); i < c; i++) { + Component m = parent.getComponent(i); + if (m.isVisible()) { + m.setBounds(insets.left, height, width, m.getPreferredSize().height); + height += m.getSize().height + gap; + } + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/WrapLayout.java b/src/main/java/org/jdesktop/swingx/WrapLayout.java new file mode 100644 index 0000000000..71659932e0 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/WrapLayout.java @@ -0,0 +1,176 @@ +package org.jdesktop.swingx; + +import javax.swing.*; +import java.awt.*; + +/** + * FlowLayout subclass that fully supports wrapping of components. + */ +public class WrapLayout extends FlowLayout { + private static final long serialVersionUID = 1L; + + /** + * Constructs a new WrapLayout with a left alignment and a default 5-unit + * horizontal and vertical gap. + */ + public WrapLayout() { + super(); + } + + /** + * Constructs a new FlowLayout with the specified alignment and a default 5-unit + * horizontal and vertical gap. The value of the alignment argument must be one of + * WrapLayout, WrapLayout, or WrapLayout. + * + * @param align + * the alignment value + */ + public WrapLayout(int align) { + super(align); + } + + /** + * Creates a new flow layout manager with the indicated alignment and the indicated horizontal + * and vertical gaps. + *

    + * The value of the alignment argument must be one of WrapLayout, + * WrapLayout, or WrapLayout. + * + * @param align + * the alignment value + * @param hgap + * the horizontal gap between components + * @param vgap + * the vertical gap between components + */ + public WrapLayout(int align, int hgap, int vgap) { + super(align, hgap, vgap); + } + + /** + * Returns the preferred dimensions for this layout given the visible components in the + * specified target container. + * + * @param target + * the component which needs to be laid out + * @return the preferred dimensions to lay out the subcomponents of the specified container + */ + @Override + public Dimension preferredLayoutSize(Container target) { + return layoutSize(target, true); + } + + /** + * Returns the minimum dimensions needed to layout the visible components contained in + * the specified target container. + * + * @param target + * the component which needs to be laid out + * @return the minimum dimensions to lay out the subcomponents of the specified container + */ + @Override + public Dimension minimumLayoutSize(Container target) { + Dimension minimum = layoutSize(target, false); + minimum.width -= (getHgap() + 1); + return minimum; + } + + /** + * Returns the minimum or preferred dimension needed to layout the target container. + * + * @param target + * target to get layout size for + * @param preferred + * should preferred size be calculated + * @return the dimension to layout the target container + */ + private Dimension layoutSize(Container target, boolean preferred) { + synchronized (target.getTreeLock()) { + // Each row must fit with the width allocated to the containter. + // When the container width = 0, the preferred width of the container + // has not yet been calculated so lets ask for the maximum. + + int targetWidth = target.getSize().width; + + if (targetWidth == 0) + targetWidth = Integer.MAX_VALUE; + + int hgap = getHgap(); + int vgap = getVgap(); + Insets insets = target.getInsets(); + int horizontalInsetsAndGap = insets.left + insets.right + (hgap * 2); + int maxWidth = targetWidth - horizontalInsetsAndGap; + + // Fit components into the allowed width + + Dimension dim = new Dimension(0, 0); + int rowWidth = 0; + int rowHeight = 0; + + int nmembers = target.getComponentCount(); + + for (int i = 0; i < nmembers; i++) { + Component m = target.getComponent(i); + + if (m.isVisible()) { + Dimension d = preferred ? m.getPreferredSize() : m.getMinimumSize(); + + // Can't add the component to current row. Start a new row. + + if (rowWidth + d.width > maxWidth) { + addRow(dim, rowWidth, rowHeight); + rowWidth = 0; + rowHeight = 0; + } + + // Add a horizontal gap for all components after the first + + if (rowWidth != 0) { + rowWidth += hgap; + } + + rowWidth += d.width; + rowHeight = Math.max(rowHeight, d.height); + } + } + + addRow(dim, rowWidth, rowHeight); + + dim.width += horizontalInsetsAndGap; + dim.height += insets.top + insets.bottom + vgap * 2; + + // When using a scroll pane or the DecoratedLookAndFeel we need to + // make sure the preferred size is less than the size of the + // target containter so shrinking the container size works + // correctly. Removing the horizontal gap is an easy way to do this. + + Container scrollPane = SwingUtilities.getAncestorOfClass(JScrollPane.class, target); + + if (scrollPane != null) { + dim.width -= (hgap + 1); + } + + return dim; + } + } + + /* + * A new row has been completed. Use the dimensions of this row to update the preferred size for + * the container. + * + * @param dim update the width and height when appropriate + * + * @param rowWidth the width of the row to add + * + * @param rowHeight the height of the row to add + */ + private void addRow(Dimension dim, int rowWidth, int rowHeight) { + dim.width = Math.max(dim.width, rowWidth); + + if (dim.height > 0) { + dim.height += getVgap(); + } + + dim.height += rowHeight; + } +} diff --git a/src/main/java/org/jdesktop/swingx/action/AbstractActionExt.java b/src/main/java/org/jdesktop/swingx/action/AbstractActionExt.java new file mode 100644 index 0000000000..bf77e43af8 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/action/AbstractActionExt.java @@ -0,0 +1,443 @@ +/* + * $Id: AbstractActionExt.java 4158 2012-02-03 18:29:40Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.action; + +import javax.swing.*; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.beans.PropertyChangeListener; + +/** + * Extends the concept of the Action to include toggle or group states. + *

    + * SwingX 1.6.3 updates {@code AbstractActionExt} to use new features of {@link Action} that were + * added in {@code Java 1.6}. The selection is now managed with {@link Action#SELECTED_KEY}, which + * allows the action to correctly configured Swing buttons. The {@link #LARGE_ICON} has also been + * changed to correspond to {@link Action#LARGE_ICON_KEY}. + * + */ +public abstract class AbstractActionExt extends AbstractAction + implements ItemListener { + + /** + * The key for the large icon + *

    + * As of SwingX 1.6.3 is now has the same value as {@link Action#LARGE_ICON_KEY}, which is new to 1.6. + */ + public static final String LARGE_ICON = Action.LARGE_ICON_KEY; + + /** + * The key for the button group + */ + public static final String GROUP = "__Group__"; + + /** + * The key for the flag which indicates that this is a state type. + */ + public static final String IS_STATE = "__State__"; + + /** + * Default constructor, does nothing. + */ + public AbstractActionExt() { + this((String) null); + } + + /** + * Copy constructor copies the state. + */ + public AbstractActionExt(AbstractActionExt action) { + Object[] keys = action.getKeys(); + for (int i = 0; i < keys.length; i++) { + putValue((String)keys[i], action.getValue((String)keys[i])); + } + this.enabled = action.enabled; + + // Copy change listeners. + PropertyChangeListener[] listeners = action.getPropertyChangeListeners(); + for (int i = 0; i < listeners.length; i++) { + addPropertyChangeListener(listeners[i]); + } + } + + public AbstractActionExt(String name) { + super(name); + } + + public AbstractActionExt(String name, Icon icon) { + super(name, icon); + } + + /** + * Constructs an Action with the label and command + * + * @param name name of the action usually used as a label + * @param command command key of the action + */ + public AbstractActionExt(String name, String command) { + this(name); + setActionCommand(command); + } + + /** + * @param name display name of the action + * @param command the value of the action command key + * @param icon icon to display + */ + public AbstractActionExt(String name, String command, Icon icon) { + super(name, icon); + setActionCommand(command); + } + /** + * Returns a short description of the action. + * + * @return the short description or null + */ + public String getShortDescription() { + return (String)getValue(Action.SHORT_DESCRIPTION); + } + + /** + * Sets the short description of the action. This will also + * set the long description value is it is null. + *

    + * This is a convenience method for putValue with the + * Action.SHORT_DESCRIPTION key. + * + * @param desc the short description; can be nullw + * @see Action#SHORT_DESCRIPTION + * @see Action#putValue + */ + public void setShortDescription(String desc) { + putValue(Action.SHORT_DESCRIPTION, desc); + if (desc != null && getLongDescription() == null) { + setLongDescription(desc); + } + } + + /** + * Returns a long description of the action. + * + * @return the long description or null + */ + public String getLongDescription() { + return (String)getValue(Action.LONG_DESCRIPTION); + } + + /** + * Sets the long description of the action. This will also set the + * value of the short description if that value is null. + *

    + * This is a convenience method for putValue with the + * Action.LONG_DESCRIPTION key. + * + * @param desc the long description; can be null + * @see Action#LONG_DESCRIPTION + * @see Action#putValue + */ + public void setLongDescription(String desc) { + putValue(Action.LONG_DESCRIPTION, desc); + if (desc != null && getShortDescription() == null) { + setShortDescription(desc); + } + } + + /** + * Returns a small icon which represents the action. + * + * @return the small icon or null + */ + public Icon getSmallIcon() { + return (Icon)getValue(SMALL_ICON); + } + + /** + * Sets the small icon which represents the action. + *

    + * This is a convenience method for putValue with the + * Action.SMALL_ICON key. + * + * @param icon the small icon; can be null + * @see Action#SMALL_ICON + * @see Action#putValue + */ + public void setSmallIcon(Icon icon) { + putValue(SMALL_ICON, icon); + } + + /** + * Returns a large icon which represents the action. + * + * @return the large icon or null + */ + public Icon getLargeIcon() { + return (Icon)getValue(LARGE_ICON); + } + + /** + * Sets the large icon which represents the action. + *

    + * This is a convenience method for putValue with the + * LARGE_ICON key. + * + * @param icon the large icon; can be null + * @see #LARGE_ICON + * @see Action#putValue + */ + public void setLargeIcon(Icon icon) { + putValue(LARGE_ICON, icon); + } + + /** + * Sets the name of the action. + *

    + * This is a convenience method for putValue with the + * Action.NAME key. + * + * @param name the name of the action; can be null + * @see Action#NAME + * @see Action#putValue + */ + public void setName(String name) { + putValue(Action.NAME, name); + } + + /** + * Returns the name of the action. + * + * @return the name of the action or null + */ + public String getName() { + return (String)getValue(Action.NAME); + } + + public void setMnemonic(String mnemonic) { + if (mnemonic != null && mnemonic.length() > 0) { + putValue(Action.MNEMONIC_KEY, mnemonic.charAt(0)); + } + } + + /** + * Sets the mnemonic key code for the action. + *

    + * This is a convenience method for putValue with the + * Action.MNEMONIC_KEY key. + *

    + * This method does not validate the value. Please see + * {@link javax.swing.AbstractButton#setMnemonic(int)} for details + * concerning the value of the mnemonic. + * + * @param mnemonic an int key code mnemonic or 0 + * @see javax.swing.AbstractButton#setMnemonic(int) + * @see Action#MNEMONIC_KEY + * @see Action#putValue + */ + public void setMnemonic(int mnemonic) { + putValue(Action.MNEMONIC_KEY, mnemonic); + } + + /** + * Return the mnemonic key code for the action. + * + * @return the mnemonic or 0 + */ + public int getMnemonic() { + Integer value = (Integer)getValue(Action.MNEMONIC_KEY); + if (value != null) { + return value.intValue(); + } + return '\0'; + } + + /** + * Sets the action command key. The action command key + * is used to identify the action. + *

    + * This is a convenience method for putValue with the + * Action.ACTION_COMMAND_KEY key. + * + * @param key the action command + * @see Action#ACTION_COMMAND_KEY + * @see Action#putValue + */ + public void setActionCommand(String key) { + putValue(Action.ACTION_COMMAND_KEY, key); + } + + /** + * Returns the action command. + * + * @return the action command or null + */ + public String getActionCommand() { + return (String) getValue(Action.ACTION_COMMAND_KEY); + } + + /** + * Returns the key stroke which represents an accelerator + * for the action. + * + * @return the key stroke or null + */ + public KeyStroke getAccelerator() { + return (KeyStroke)getValue(Action.ACCELERATOR_KEY); + } + + /** + * Sets the key stroke which represents an accelerator + * for the action. + *

    + * This is a convenience method for putValue with the + * Action.ACCELERATOR_KEY key. + * + * @param key the key stroke; can be null + * @see Action#ACCELERATOR_KEY + * @see Action#putValue + */ + public void setAccelerator(KeyStroke key) { + putValue(Action.ACCELERATOR_KEY, key); + } + + /** + * Sets the group identity of the state action. This is used to + * identify the action as part of a button group. + */ + public void setGroup(Object group) { + putValue(GROUP, group); + } + + public Object getGroup() { + return getValue(GROUP); + } + + /** + * Will perform cleanup on the object. + * Should be called when finished with the Action. This should be used if + * a new action is constructed from the properties of an old action. + * The old action properties should be disposed. + */ + public void dispose() { + PropertyChangeListener[] listeners = getPropertyChangeListeners(); + for (int i = 0; i < listeners.length; i++) { + removePropertyChangeListener(listeners[i]); + } + } + + // Properties etc.... + + /** + * Indicates if this action has states. If this method returns + * true then the this will send ItemEvents to ItemListeners + * when the control constructed with this action in invoked. + * + * @return true if this can handle states + */ + public boolean isStateAction() { + Boolean state = (Boolean)getValue(IS_STATE); + if (state != null) { + return state.booleanValue(); + } + return false; + } + + /** + * Set the state property to true. + */ + public void setStateAction() { + setStateAction(true); + } + + /** + * Set the state property. + * + * @param state if true then this action will fire ItemEvents + */ + public void setStateAction(boolean state) { + putValue(IS_STATE, Boolean.valueOf(state)); + } + + /** + * @return true if the action is in the selected state + */ + public boolean isSelected() { + Boolean selected = (Boolean) getValue(SELECTED_KEY); + + if (selected == null) { + return false; + } + + return selected.booleanValue(); + } + + /** + * Changes the state of the action. This is a convenience method for updating the Action via the + * value map. + * + * @param newValue + * true to set the action as selected of the action. + * @see Action#SELECTED_KEY + */ + public void setSelected(boolean newValue) { + putValue(SELECTED_KEY, newValue); + } + + @Override + public String toString() { + StringBuffer buffer = new StringBuffer("["); + // RG: Fix for J2SE 5.0; Can't cascade append() calls because + // return type in StringBuffer and AbstractStringBuilder are different + buffer.append(this.getClass().toString()); + buffer.append(":"); + try { + Object[] keys = getKeys(); + for (int i = 0; i < keys.length; i++) { + buffer.append(keys[i]); + buffer.append('='); + buffer.append(getValue( (String) keys[i]).toString()); + if (i < keys.length - 1) { + buffer.append(','); + } + } + buffer.append(']'); + } + catch (Exception ex) { // RG: append(char) throws IOException in J2SE 5.0 + /** @todo Log it */ + } + return buffer.toString(); + } + + /** + * Callback method as ItemListener. Updates internal state based + * on the given ItemEvent.

    + * + * Here: synchs selected property if isStateAction(), does nothing otherwise. + * + * @param e the ItemEvent fired by a ItemSelectable on changing the selected + * state. + */ + @Override + public void itemStateChanged(ItemEvent e) { + if (isStateAction()) { + setSelected(ItemEvent.SELECTED == e.getStateChange()); + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/action/ActionContainerFactory.java b/src/main/java/org/jdesktop/swingx/action/ActionContainerFactory.java new file mode 100644 index 0000000000..8d0b5ba572 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/action/ActionContainerFactory.java @@ -0,0 +1,586 @@ +/* + * $Id: ActionContainerFactory.java 3980 2011-03-28 20:24:46Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.action; + +import javax.swing.*; +import java.awt.*; +import java.beans.PropertyChangeListener; +import java.util.List; +import java.util.*; + +/** + * Creates user interface elements based on action ids and lists of action ids. + * All action ids must represent actions managed by the ActionManager. + *

    + *

    Action Lists

    + * Use the createXXX(List) methods to construct containers of actions like menu + * bars, menus, popups and toolbars from actions represented as action ids in a + * java.util.List. Each element in the action-list can be one of 3 types: + *
      + *
    • action id: corresponds to an action managed by the ActionManager + *
    • null: indicates a separator should be inserted. + *
    • java.util.List: represents a submenu. See the note below which describes + * the configuration of menus. + *
    • + * The order of elements in an action-list determines the arrangement of the ui + * components which are constructed from the action-list. + *

      + * For a menu or submenu, the first element in the action-list represents a menu + * and subsequent elements represent menu items or separators (if null). + *

      + * This class can be used as a general component factory which will construct + * components from Actions if the create<comp>(Action,...) + * methods are used. + * + * @see ActionManager + */ +public class ActionContainerFactory { + /** + * Standard margin for toolbar buttons to improve their look + */ + private static Insets TOOLBAR_BUTTON_MARGIN = new Insets(1, 1, 1, 1); + + private ActionMap manager; + + // Map between group id + component and the ButtonGroup + private Map groupMap; + + /** + * Constructs an container factory which uses the default + * ActionManager. + * + */ + public ActionContainerFactory() { + } + /** + * Constructs an container factory which uses managed actions. + * + * @param manager use the actions managed with this manager for + * constructing ui componenents. + */ + public ActionContainerFactory(ActionMap manager) { + setActionManager(manager); + } + + /** + * Gets the ActionManager instance. If the ActionManager has not been explicitly + * set then the default ActionManager instance will be used. + * + * @return the ActionManager used by the ActionContainerFactory. + * @see #setActionManager + */ + public ActionMap getActionManager() { + if (manager == null) { + manager = ActionManager.getInstance(); + } + return manager; + } + + /** + * Sets the ActionManager instance that will be used by this + * ActionContainerFactory + */ + public void setActionManager(ActionMap manager) { + this.manager = manager; + } + + /** + * Constructs a toolbar from an action-list id. By convention, + * the identifier of the main toolbar should be "main-toolbar" + * + * @param list a list of action ids used to construct the toolbar. + * @return the toolbar or null + */ + public JToolBar createToolBar(Object[] list) { + return createToolBar(Arrays.asList(list)); + } + + /** + * Constructs a toolbar from an action-list id. By convention, + * the identifier of the main toolbar should be "main-toolbar" + * + * @param list a list of action ids used to construct the toolbar. + * @return the toolbar or null + */ + public JToolBar createToolBar(List list) { + JToolBar toolbar = new JToolBar(); + Iterator iter = list.iterator(); + while(iter.hasNext()) { + Object element = iter.next(); + + if (element == null) { + toolbar.addSeparator(); + } else { + AbstractButton button = createButton(element, toolbar); + // toolbar buttons shouldn't steal focus + button.setFocusable(false); + /* + * TODO + * The next two lines improve the default look of the buttons. + * This code should be changed to retrieve the default look + * from some UIDefaults object. + */ + button.setMargin(TOOLBAR_BUTTON_MARGIN); + button.setBorderPainted(false); + + toolbar.add(button); + } + } + return toolbar; + } + + + /** + * Constructs a popup menu from an array of action ids. + * + * @param list an array of action ids used to construct the popup. + * @return the popup or null + */ + public JPopupMenu createPopup(Object[] list) { + return createPopup(Arrays.asList(list)); + } + + /** + * Constructs a popup menu from a list of action ids. + * + * @param list a list of action ids used to construct the popup. + * @return the popup or null + */ + public JPopupMenu createPopup(List list) { + JPopupMenu popup = new JPopupMenu(); + Iterator iter = list.iterator(); + while(iter.hasNext()) { + Object element = iter.next(); + + if (element == null) { + popup.addSeparator(); + } else if (element instanceof List) { + JMenu newMenu= createMenu((List)element); + if (newMenu!= null) { + popup.add(newMenu); + } + } else { + popup.add(createMenuItem(element, popup)); + } + } + return popup; + } + + /** + * Constructs a menu tree from a list of actions or lists of lists or actions. + * + * @param actionIds an array which represents the root item. + * @return a menu bar which represents the menu bar tree + */ + public JMenuBar createMenuBar(Object[] actionIds) { + return createMenuBar(Arrays.asList(actionIds)); + } + + /** + * Constructs a menu tree from a list of actions or lists of lists or actions. + * + * @param list a list which represents the root item. + * @return a menu bar which represents the menu bar tree + */ + public JMenuBar createMenuBar(List list) { + final JMenuBar menubar = new JMenuBar(); + + for (Object element : list) { + if (element == null) { + continue; + } + + JMenuItem menu; + + if (element instanceof Object[]) { + menu = createMenu((Object[]) element); + } else if (element instanceof List) { + menu = createMenu((List) element); + } else { + menu = createMenuItem(element, menubar); + } + + if (menu != null) { + menubar.add(menu); + } + } + + return menubar; + } + + + /** + * Creates and returns a menu from a List which represents actions, separators + * and sub-menus. The menu + * constructed will have the attributes from the first action in the List. + * Subsequent actions in the list represent menu items. + * + * @param actionIds an array of action ids used to construct the menu and menu items. + * the first element represents the action used for the menu, + * @return the constructed JMenu or null + */ + public JMenu createMenu(Object[] actionIds) { + return createMenu(Arrays.asList(actionIds)); + } + + /** + * Creates and returns a menu from a List which represents actions, separators + * and sub-menus. The menu + * constructed will have the attributes from the first action in the List. + * Subsequent actions in the list represent menu items. + * + * @param list a list of action ids used to construct the menu and menu items. + * the first element represents the action used for the menu, + * @return the constructed JMenu or null + */ + public JMenu createMenu(List list) { + // The first item will be the action for the JMenu + Action action = getAction(list.get(0)); + + if (action == null) { + return null; + } + + JMenu menu = new JMenu(action); + + // The rest of the items represent the menu items. + for (Object element : list.subList(1, list.size())) { + if (element == null) { + menu.addSeparator(); + } else { + JMenuItem newMenu; + + if (element instanceof Object[]) { + newMenu = createMenu((Object[]) element); + } else if (element instanceof List) { + newMenu = createMenu((List) element); + } else { + newMenu = createMenuItem(element, menu); + } + + if (newMenu != null) { + menu.add(newMenu); + } + } + } + + return menu; + } + + /** + * Convenience method to get the action from an ActionManager. + */ + private Action getAction(Object id) { + return getActionManager().get(id); + } + + /** + * Returns the button group corresponding to the groupid + * + * @param groupid the value of the groupid attribute for the action element + * @param container a container which will further identify the ButtonGroup + */ + private ButtonGroup getGroup(String groupid, JComponent container) { + if (groupMap == null) { + groupMap = new HashMap(); + } + int intCode = groupid.hashCode(); + if (container != null) { + intCode ^= container.hashCode(); + } + Integer hashCode = intCode; + + ButtonGroup group = groupMap.get(hashCode); + if (group == null) { + group = new ButtonGroup(); + groupMap.put(hashCode, group); + } + return group; + } + + /** + * Creates a menu item based on the attributes of the action element. + * Will return a JMenuItem, JRadioButtonMenuItem or a JCheckBoxMenuItem + * depending on the context of the Action. + * + * @return a JMenuItem or subclass depending on type. + */ + private JMenuItem createMenuItem(Object id, JComponent container) { + return createMenuItem(getAction(id), container); + } + + + /** + * Creates a menu item based on the attributes of the action element. + * Will return a JMenuItem, JRadioButtonMenuItem or a JCheckBoxMenuItem + * depending on the context of the Action. + * + * @param action a managed Action + * @param container the parent container may be null for non-group actions. + * @return a JMenuItem or subclass depending on type. + */ + private JMenuItem createMenuItem(Action action, JComponent container) { + JMenuItem menuItem = null; + if (action instanceof AbstractActionExt) { + AbstractActionExt ta = (AbstractActionExt)action; + + if (ta.isStateAction()) { + String groupid = (String)ta.getGroup(); + if (groupid != null) { + // If this action has a groupid attribute then it's a + // GroupAction + menuItem = createRadioButtonMenuItem(getGroup(groupid, container), + (AbstractActionExt)action); + } else { + menuItem = createCheckBoxMenuItem((AbstractActionExt)action); + } + } + } + + if (menuItem == null) { + menuItem= new JMenuItem(action); + configureMenuItemFromExtActionProperties(menuItem, action); + } + return menuItem; + } + + /** + * Creates a menu item based on the attributes of the action. + * Will return a JMenuItem, JRadioButtonMenuItem or a JCheckBoxMenuItem + * depending on the context of the Action. + * + * @param action an action used to create the menu item + * @return a JMenuItem or subclass depending on type. + */ + public JMenuItem createMenuItem(Action action) { + return createMenuItem(action, null); + } + + + /** + * Creates, configures and returns an AbstractButton. + * + * The attributes of the action element + * registered with the ActionManger by the given id. + * Will return a JButton or a JToggleButton. + * + * @param id the identifier + * @param container the JComponent which parents the group, if any. + * @return an AbstractButton based on the + */ + public AbstractButton createButton(Object id, JComponent container) { + return createButton(getAction(id), container); + } + + /** + * Creates a button based on the attributes of the action. If the container + * parameter is non-null then it will be used to uniquely identify + * the returned component within a ButtonGroup. If the action doesn't + * represent a grouped component then this value can be null. + * + * @param action an action used to create the button + * @param container the parent container to uniquely identify + * grouped components or null + * @return will return a JButton or a JToggleButton. + */ + public AbstractButton createButton(Action action, JComponent container) { + if (action == null) { + return null; + } + + AbstractButton button = null; + if (action instanceof AbstractActionExt) { + // Check to see if we should create a toggle button + AbstractActionExt ta = (AbstractActionExt)action; + + if (ta.isStateAction()) { + // If this action has a groupid attribute then it's a + // GroupAction + String groupid = (String)ta.getGroup(); + if (groupid == null) { + button = createToggleButton(ta); + } else { + button = createToggleButton(ta, getGroup(groupid, container)); + } + } + } + + if (button == null) { + // Create a regular button + button = new JButton(action); + configureButtonFromExtActionProperties(button, action); + } + return button; + } + + /** + * Creates a button based on the attributes of the action. + * + * @param action an action used to create the button + * @return will return a JButton or a JToggleButton. + */ + public AbstractButton createButton(Action action) { + return createButton(action, null); + } + + /** + * Adds and configures a toggle button. + * @param a an abstraction of a toggle action. + */ + private JToggleButton createToggleButton(AbstractActionExt a) { + return createToggleButton(a, null); + } + + /** + * Adds and configures a toggle button. + * @param a an abstraction of a toggle action. + * @param group the group to add the toggle button or null + */ + private JToggleButton createToggleButton(AbstractActionExt a, ButtonGroup group) { + JToggleButton button = new JToggleButton(); + configureButton(button, a, group); + return button; + } + + /** + * + * @param button + * @param a + * @param group + */ + public void configureButton(JToggleButton button, AbstractActionExt a, ButtonGroup group) { + configureSelectableButton(button, a, group); + configureButtonFromExtActionProperties(button, a); + } + + /** + * method to configure a "selectable" button from the given AbstractActionExt. + * As there is some un-/wiring involved to support synch of the selected property between + * the action and the button, all config and unconfig (== setting a null action!) + * should be passed through this method.

      + * + * It's up to the client to only pass in button's where selected and/or the + * group property makes sense. + * + * PENDING: the group properties are yet untested. + * PENDING: think about automated unconfig. + * + * @param button where selected makes sense + * @param a + * @param group the button should be added to. + * @throws IllegalArgumentException if the given action doesn't have the state flag set. + * + */ + public void configureSelectableButton(AbstractButton button, AbstractActionExt a, ButtonGroup group){ + if ((a != null) && !a.isStateAction()) throw + new IllegalArgumentException("the Action must be a stateAction"); + // we assume that all button configuration is done exclusively through this method!! + if (button.getAction() == a) return; + + // unconfigure if the old Action is a state AbstractActionExt + // PENDING JW: automate unconfigure via a PCL that is listening to + // the button's action property? Think about memory leak implications! + Action oldAction = button.getAction(); + if (oldAction instanceof AbstractActionExt) { + AbstractActionExt actionExt = (AbstractActionExt) oldAction; + // remove as itemListener + button.removeItemListener(actionExt); + // remove the button related PCL from the old actionExt + PropertyChangeListener[] l = actionExt.getPropertyChangeListeners(); + for (int i = l.length - 1; i >= 0; i--) { + if (l[i] instanceof ToggleActionPropertyChangeListener) { + ToggleActionPropertyChangeListener togglePCL = (ToggleActionPropertyChangeListener) l[i]; + if (togglePCL.isToggling(button)) { + actionExt.removePropertyChangeListener(togglePCL); + } + } + } + } + + button.setAction(a); + if (group != null) { + group.add(button); + } + if (a != null) { + button.addItemListener(a); + // JW: move the initial config into the PCL?? + button.setSelected(a.isSelected()); + new ToggleActionPropertyChangeListener(a, button); +// new ToggleActionPCL(button, a); + } + + } + + /** + * This method will be called after buttons created from an action. Override + * for custom configuration. + * + * @param button the button to be configured + * @param action the action used to construct the menu item. + */ + protected void configureButtonFromExtActionProperties(AbstractButton button, Action action) { + if (action.getValue(Action.SHORT_DESCRIPTION) == null) { + button.setToolTipText((String)action.getValue(Action.NAME)); + } + // Use the large icon for toolbar buttons. + if (action.getValue(AbstractActionExt.LARGE_ICON) != null) { + button.setIcon((Icon)action.getValue(AbstractActionExt.LARGE_ICON)); + } + // Don't show the text under the toolbar buttons if they have an icon + if (button.getIcon() != null) { + button.setText(""); + } + } + + + /** + * This method will be called after menu items are created. + * Override for custom configuration. + * + * @param menuItem the menu item to be configured + * @param action the action used to construct the menu item. + */ + protected void configureMenuItemFromExtActionProperties(JMenuItem menuItem, Action action) { + } + + /** + * Helper method to add a checkbox menu item. + */ + private JCheckBoxMenuItem createCheckBoxMenuItem(AbstractActionExt a) { + JCheckBoxMenuItem mi = new JCheckBoxMenuItem(); + configureSelectableButton(mi, a, null); + configureMenuItemFromExtActionProperties(mi, a); + return mi; + } + + /** + * Helper method to add a radio button menu item. + */ + private JRadioButtonMenuItem createRadioButtonMenuItem(ButtonGroup group, + AbstractActionExt a) { + JRadioButtonMenuItem mi = new JRadioButtonMenuItem(); + configureSelectableButton(mi, a, group); + configureMenuItemFromExtActionProperties(mi, a); + return mi; + } + + +} diff --git a/src/main/java/org/jdesktop/swingx/action/ActionFactory.java b/src/main/java/org/jdesktop/swingx/action/ActionFactory.java new file mode 100644 index 0000000000..8e287d4d85 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/action/ActionFactory.java @@ -0,0 +1,157 @@ +/* + * $Id: ActionFactory.java 3972 2011-03-17 20:31:58Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.action; + +import javax.swing.*; + +/** + * A collection of static methods to make it easier to construct + * Actions. Not sure how usefull they are in reality but it saves a + * lot of typing. + * + * @author Mark Davidson + */ +public class ActionFactory { + + /** + * Factory Methods for creating BoundActions + */ + public static BoundAction createBoundAction(String id, String name, + String mnemonic) { + return createBoundAction(id, name, mnemonic, false); + } + + public static BoundAction createBoundAction(String id, String name, + String mnemonic, boolean toggle) { + return createBoundAction(id, name, mnemonic, toggle, null); + } + + + public static BoundAction createBoundAction(String id, String name, + String mnemonic, boolean toggle, + String group) { + return (BoundAction)configureAction(new BoundAction(name, id), + mnemonic, toggle, group); + } + + /** + * Factory Methods for creating CompositeAction + * @see CompositeAction + */ + public static CompositeAction createCompositeAction(String id, String name, + String mnemonic) { + return createCompositeAction(id, name, mnemonic, false); + } + + public static CompositeAction createCompositeAction(String id, String name, + String mnemonic, boolean toggle) { + return createCompositeAction(id, name, mnemonic, toggle, null); + } + + public static CompositeAction createCompositeAction(String id, String name, + String mnemonic, boolean toggle, + String group) { + return (CompositeAction)configureAction(new CompositeAction(name, id), + mnemonic, toggle, group); + } + + + public static ServerAction createServerAction(String id, String name, + String mnemonic) { + ServerAction action = new ServerAction(name, id); + if (mnemonic != null && !mnemonic.equals("")) { + action.putValue(Action.MNEMONIC_KEY, (int) mnemonic.charAt(0)); + } + return action; + } + + + /** + * These methods are usefull for creating targetable actions + */ + public static TargetableAction createTargetableAction(String id, String name) { + return createTargetableAction(id, name, null); + } + + public static TargetableAction createTargetableAction(String id, String name, + String mnemonic) { + return createTargetableAction(id, name, mnemonic, false); + } + + public static TargetableAction createTargetableAction(String id, String name, + String mnemonic, boolean toggle) { + return createTargetableAction(id, name, mnemonic, toggle, null); + } + + public static TargetableAction createTargetableAction(String id, String name, + String mnemonic, boolean toggle, + String group) { + return (TargetableAction)configureAction(new TargetableAction(name, id), + mnemonic, toggle, group); + } + + private static Action configureAction(AbstractActionExt action, + String mnemonic, boolean toggle, + String group) { + action.setMnemonic(mnemonic); + String description = action.getName() + " action with comand " + action.getActionCommand(); + action.setShortDescription(description); + action.setLongDescription(description); + + if (toggle) { + action.setStateAction(); + } + if (group != null) { + action.setGroup(group); + } + return action; + } + + /** + * Add additional attributes to the action. If any of these attributes + * are null then they will still be set on the action. Many of these + * attributes map to the set methods on AbstractActionExt + * + * @see AbstractActionExt + * @param action the action which will all the attributes will be applied + */ + public static void decorateAction(AbstractAction action, + String shortDesc, String longDesc, + Icon smallIcon, Icon largeIcon, + KeyStroke accel) { + if (action instanceof AbstractActionExt) { + AbstractActionExt a = (AbstractActionExt)action; + a.setShortDescription(shortDesc); + a.setLongDescription(longDesc); + a.setSmallIcon(smallIcon); + a.setLargeIcon(largeIcon); + a.setAccelerator(accel); + } + else { + action.putValue(Action.SHORT_DESCRIPTION, shortDesc); + action.putValue(Action.LONG_DESCRIPTION, longDesc); + action.putValue(Action.SMALL_ICON, smallIcon); + action.putValue(AbstractActionExt.LARGE_ICON, largeIcon); + action.putValue(Action.ACCELERATOR_KEY, accel); + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/action/ActionManager.java b/src/main/java/org/jdesktop/swingx/action/ActionManager.java new file mode 100644 index 0000000000..bd2dfcf600 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/action/ActionManager.java @@ -0,0 +1,383 @@ +/* + * $Id: ActionManager.java 3972 2011-03-17 20:31:58Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.action; + +import javax.swing.*; +import java.io.PrintStream; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * The ActionManager manages sets of javax.swing.Actions for an + * application. There are convenience methods for getting and setting the state + * of the action. + * All of these elements have a unique id tag which is used by the ActionManager + * to reference the action. This id maps to the Action.ACTION_COMMAND_KEY + * on the Action. + *

      + * The ActionManager may be used to conveniently register callback methods + * on BoundActions. + *

      + * A typical use case of the ActionManager is: + *

      + *

      + *   ActionManager manager = ActionManager.getInstance();
      + *
      + *   // load Actions
      + *   manager.addAction(action);
      + *
      + *   // Change the state of the action:
      + *   manager.setEnabled("new-action", newState);
      + * 
      + * + * The ActionManager also supports Actions that can have a selected state + * associated with them. These Actions are typically represented by a + * JCheckBox or similar widget. For such actions the registered method is + * invoked with an additional parameter indicating the selected state of + * the widget. For example, for the callback handler: + *

      + *

      + *  public class Handler {
      + *      public void stateChanged(boolean newState);
      + *   }
      + * 
      + * The registration method would look similar: + *
      + *  manager.registerCallback("select-action", new Handler(), "stateChanged");
      + * 
      + *

      + * The stateChanged method would be invoked as the selected state of + * the widget changed. Additionally if you need to change the selected + * state of the Action use the ActionManager method setSelected. + *

      + * The ActionContainerFactory uses the managed Actions in a + * ActionManager to create user interface components. It uses the shared + * instance of ActionManager by default. For example, to create a JMenu based on an + * action-list id: + *

      + * ActionContainerFactory factory = new ActionContainerFactory();
      + * JMenu file = factory.createMenu(list);
      + * 
      + * + * @see ActionContainerFactory + * @see TargetableAction + * @see BoundAction + * @author Mark Davidson + * @author Neil Weber + */ +public class ActionManager extends ActionMap { + + /** + * Shared instance of the singleton ActionManager. + */ + private static ActionManager INSTANCE; + + /** + * Creates the action manager. Use this constuctor if the application should + * support many ActionManagers. Otherwise, using the getInstance method will + * return a singleton. + */ + public ActionManager() { + } + + /** + * Return the instance of the ActionManger. If this has not been explicity + * set then it will be created. + * + * @return the ActionManager instance. + * @see #setInstance + */ + public static ActionManager getInstance() { + if (INSTANCE == null) { + INSTANCE = new ActionManager(); + } + return INSTANCE; + } + + /** + * Sets the ActionManager instance. + */ + public static void setInstance(ActionManager manager) { + INSTANCE = manager; + } + + /** + * Returns the ids for all the managed actions. + *

      + * An action id is a unique idenitfier which can + * be used to retrieve the corrspondng Action from the ActionManager. + * This identifier can also + * be used to set the properties of the action through the action + * manager like setting the state of the enabled or selected flags. + * + * @return a set which represents all the action ids + */ + public Set getActionIDs() { + Object[] keys = keys(); + if (keys == null) { + return null; + } + + return new HashSet(Arrays.asList(keys)); + } + + public Action addAction(Action action) { + return addAction(action.getValue(Action.ACTION_COMMAND_KEY), action); + } + + /** + * Adds an action to the ActionManager + * @param id value of the action id - which is value of the ACTION_COMMAND_KEY + * @param action Action to be managed + * @return the action that was added + */ + public Action addAction(Object id, Action action) { + put(id, action); + return action; + } + + /** + * Retrieves the action corresponding to an action id. + * + * @param id value of the action id + * @return an Action or null if id + */ + public Action getAction(Object id) { + return get(id); + } + + /** + * Convenience method for returning the TargetableAction + * + * @param id value of the action id + * @return the TargetableAction referenced by the named id or null + */ + public TargetableAction getTargetableAction(Object id) { + Action a = getAction(id); + if (a instanceof TargetableAction) { + return (TargetableAction)a; + } + return null; + } + + /** + * Convenience method for returning the BoundAction + * + * @param id value of the action id + * @return the TargetableAction referenced by the named id or null + */ + public BoundAction getBoundAction(Object id) { + Action a = getAction(id); + if (a instanceof BoundAction) { + return (BoundAction)a; + } + return null; + } + + /** + * Convenience method for returning the ServerAction + * + * @param id value of the action id + * @return the TargetableAction referenced by the named id or null + */ + public ServerAction getServerAction(Object id) { + Action a = getAction(id); + if (a instanceof ServerAction) { + return (ServerAction)a; + } + return null; + } + + /** + * Convenience method for returning the CompositeAction + * + * @param id value of the action id + * @return the TargetableAction referenced by the named id or null + */ + public CompositeAction getCompositeAction(Object id) { + Action a = getAction(id); + if (a instanceof CompositeAction) { + return (CompositeAction)a; + } + return null; + } + + /** + * Convenience method for returning the StateChangeAction + * + * @param id value of the action id + * @return the StateChangeAction referenced by the named id or null + */ + private AbstractActionExt getStateChangeAction(Object id) { + Action a = getAction(id); + if (a != null && a instanceof AbstractActionExt) { + AbstractActionExt aa = (AbstractActionExt)a; + if (aa.isStateAction()) { + return aa; + } + } + return null; + } + + /** + * Enables or disables the state of the Action corresponding to the + * action id. This method should be used + * by application developers to ensure that all components created from an + * action remain in synch with respect to their enabled state. + * + * @param id value of the action id + * @param enabled true if the action is to be enabled; otherwise false + */ + public void setEnabled(Object id, boolean enabled) { + Action action = getAction(id); + if (action != null) { + action.setEnabled(enabled); + } + } + + + /** + * Returns the enabled state of the Action. When enabled, + * any component associated with this object is active and + * able to fire this object's actionPerformed method. + * + * @param id value of the action id + * @return true if this Action is enabled; false if the + * action doesn't exist or disabled. + */ + public boolean isEnabled(Object id) { + Action action = getAction(id); + if (action != null) { + return action.isEnabled(); + } + return false; + } + + /** + * Sets the selected state of a toggle action. If the id doesn't + * correspond to a toggle action then it will fail silently. + * + * @param id the value of the action id + * @param selected true if the action is to be selected; otherwise false. + */ + public void setSelected(Object id, boolean selected) { + AbstractActionExt action = getStateChangeAction(id); + if (action != null) { + action.setSelected(selected); + } + } + + /** + * Gets the selected state of a toggle action. If the id doesn't + * correspond to a toggle action then it will fail silently. + * + * @param id the value of the action id + * @return true if the action is selected; false if the action + * doesn't exist or is disabled. + */ + public boolean isSelected(Object id) { + AbstractActionExt action = getStateChangeAction(id); + if (action != null) { + return action.isSelected(); + } + return false; + } + + /** + * A diagnostic which prints the Attributes of an action + * on the printStream + */ + static void printAction(PrintStream stream, Action action) { + stream.println("Attributes for " + action.getValue(Action.ACTION_COMMAND_KEY)); + + if (action instanceof AbstractAction) { + Object[] keys = ((AbstractAction)action).getKeys(); + + for (int i = 0; i < keys.length; i++) { + stream.println("\tkey: " + keys[i] + "\tvalue: " + + action.getValue((String)keys[i])); + } + } + } + + /** + * Convenience method to register a callback method on a BoundAction + * + * @see BoundAction#registerCallback + * @param id value of the action id - which is the value of the ACTION_COMMAND_KEY + * @param handler the object which will be perform the action + * @param method the name of the method on the handler which will be called. + */ + public void registerCallback(Object id, Object handler, String method) { + BoundAction action = getBoundAction(id); + if (action != null) { + action.registerCallback(handler, method); + } + } + + // + // Convenience methods for determining the type of action. + // + + /** + * Determines if the Action corresponding to the action id is a state changed + * action (toggle, group type action). + * + * @param id value of the action id + * @return true if the action id represents a multi state action; false otherwise + */ + public boolean isStateAction(Object id) { + Action action = getAction(id); + if (action != null && action instanceof AbstractActionExt) { + return ((AbstractActionExt)action).isStateAction(); + } + return false; + } + + /** + * Test to determine if the action is a TargetableAction + */ + public boolean isTargetableAction(Object id) { + return (getTargetableAction(id) != null); + } + + /** + * Test to determine if the action is a BoundAction + */ + public boolean isBoundAction(Object id) { + return (getBoundAction(id) != null); + } + + /** + * Test to determine if the action is a BoundAction + */ + public boolean isCompositeAction(Object id) { + return (getCompositeAction(id) != null); + } + + /** + * Test to determine if the action is a ServerAction + */ + public boolean isServerAction(Object id) { + return (getServerAction(id) != null); + } +} diff --git a/src/main/java/org/jdesktop/swingx/action/BoundAction.java b/src/main/java/org/jdesktop/swingx/action/BoundAction.java new file mode 100644 index 0000000000..216ed3ec43 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/action/BoundAction.java @@ -0,0 +1,329 @@ +/* + * $Id: BoundAction.java 3972 2011-03-17 20:31:58Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.action; + +import javax.swing.*; +import javax.swing.event.EventListenerList; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.beans.EventHandler; +import java.beans.Statement; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; +import java.util.EventListener; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * A class that represents the many type of actions that this framework supports. + *

      + * The command invocation of this action may be delegated to another action or item state + * listener. If there isn't an explicit binding then the command is forwarded to + * the TargetManager. + * + * @author Mark Davidson + * @author Karl Schaefer (serialization support) + */ +public class BoundAction extends AbstractActionExt { + private static final Logger LOG = Logger.getLogger(BoundAction.class .getName()); + + // Holds the listeners + private transient EventListenerList listeners; + + public BoundAction() { + this("BoundAction"); + } + + public BoundAction(String name) { + super(name); + } + + /** + * @param name display name of the action + * @param command the value of the action command key + */ + public BoundAction(String name, String command) { + super(name, command); + } + + public BoundAction(String name, Icon icon) { + super(name, icon); + } + + /** + * @param name display name of the action + * @param command the value of the action command key + * @param icon icon to display + */ + public BoundAction(String name, String command, Icon icon) { + super(name, command, icon); + } + + /** + * The callback string will be called to register the action callback. + * Note the toggle property must be set if this is a state action before + * this method is called. + * For example, + *

      +     *     <exec>com.sun.foo.FubarHandler#handleBar</exec>
      +     * 
      + * will register + *
      +     *     registerCallback(com.sun.foo.FubarHandler(), "handleBar");
      +     * 
      + */ + public void setCallback(String callback) { + String[] elems = callback.split("#", 2); + if (elems.length == 2) { + try { + Class clz = Class.forName(elems[0]); + + // May throw a security exception in an Applet + // context. + Object obj = clz.newInstance(); + + registerCallback(obj, elems[1]); + } catch (Exception ex) { + LOG.fine("ERROR: setCallback(" + callback + + ") - " + ex.getMessage()); + } + } + } + + /** + * Registers a callback method when the Action corresponding to + * the action id is invoked. When a Component that was constructed from the + * Action identified by the action id invokes actionPerformed then the method + * named will be invoked on the handler Object. + *

      + * If the Action represented by the action id is a StateChangeAction, then + * the method passed should take an int as an argument. The value of + * getStateChange() on the ItemEvent object will be passed as the parameter. + * + * @param handler the object which will be perform the action + * @param method the name of the method on the handler which will be called. + */ + public void registerCallback(Object handler, String method) { + if (isStateAction()) { + // Create a handler for toggle type actions. + addItemListener(new BooleanInvocationHandler(handler, method)); + } else { + // Create a new ActionListener using the dynamic proxy API. + addActionListener(EventHandler.create(ActionListener.class, + handler, method)); + } + } + + /** + * The callback for the toggle/state changed action that invokes a method + * with a boolean argument on a target. + * + * TODO: should reimplement this class as something that can be persistable. + */ + private class BooleanInvocationHandler implements ItemListener { + + private Statement falseStatement; + private Statement trueStatement; + + public BooleanInvocationHandler(Object target, String methodName) { + // Create the true and false statements. + falseStatement = new Statement(target, methodName, + new Object[] { Boolean.FALSE }); + + trueStatement = new Statement(target, methodName, + new Object[] { Boolean.TRUE }); + } + + @Override + public void itemStateChanged(ItemEvent evt) { + Statement statement = (evt.getStateChange() == ItemEvent.DESELECTED) ? falseStatement + : trueStatement; + + try { + statement.execute(); + } catch (Exception ex) { + LOG.log(Level.FINE, + "Couldn't execute boolean method via Statement " + + statement, ex); + } + } + } + + // Listener registration... + + private void addListener(Class clz, T listener) { + if (listeners == null) { + listeners = new EventListenerList(); + } + listeners.add(clz, listener); + } + + private void removeListener(Class clz, T listener) { + if (listeners != null) { + listeners.remove(clz, listener); + } + } + + private EventListener[] getListeners(Class clz) { + if (listeners == null) { + return null; + } + return listeners.getListeners(clz); + } + + /** + * Add an action listener which will be invoked when this action is invoked. + */ + public void addActionListener(ActionListener listener) { + addListener(ActionListener.class, listener); + } + + public void removeActionListener(ActionListener listener) { + removeListener(ActionListener.class, listener); + } + + public ActionListener[] getActionListeners() { + return (ActionListener[])getListeners(ActionListener.class); + } + + /** + * Add an item listener which will be invoked for toggle actions. + */ + public void addItemListener(ItemListener listener) { + addListener(ItemListener.class, listener); + } + + public void removeItemListener(ItemListener listener) { + removeListener(ItemListener.class, listener); + } + + public ItemListener[] getItemListeners() { + return (ItemListener[])getListeners(ItemListener.class); + } + + // Callbacks... + + /** + * Callback for command actions. + */ + @Override + public void actionPerformed(ActionEvent evt) { + ActionListener[] alist = getActionListeners(); + if (alist != null) { + for (int i = 0 ; i < alist.length; i++) { + alist[i].actionPerformed(evt); + } + } + } + + /** + * Callback for toggle actions. + */ + @Override + public void itemStateChanged(ItemEvent evt) { + // Update all objects that share this item + boolean newValue; + boolean oldValue = isSelected(); + + newValue = evt.getStateChange() == ItemEvent.SELECTED; + + if (oldValue != newValue) { + setSelected(newValue); + + // Forward the event to the delgate for handling. + ItemListener[] ilist = getItemListeners(); + if (ilist != null) { + for (int i = 0; i < ilist.length; i++) { + ilist[i].itemStateChanged(evt); + } + } + } + } + + private void writeObject(ObjectOutputStream s) throws IOException { + s.defaultWriteObject(); + + if (listeners != null) { + Object[] list = listeners.getListenerList(); + + for (int i = 1; i < list.length; i += 2) { + if (Proxy.isProxyClass(list[i].getClass())) { + InvocationHandler h = Proxy.getInvocationHandler(list[i]); + + if (h instanceof EventHandler && ((EventHandler) h).getTarget() instanceof Serializable) { + EventHandler eh = (EventHandler) h; + + s.writeObject("callback"); + s.writeObject(eh.getTarget()); + s.writeObject(eh.getAction()); + } + } else if (list[i] instanceof BooleanInvocationHandler) { + BooleanInvocationHandler bih = (BooleanInvocationHandler) list[i]; + Object target = bih.trueStatement.getTarget(); + + if (target instanceof Serializable) { + s.writeObject(BooleanInvocationHandler.class.getName()); + s.writeObject(target); + s.writeObject(bih.trueStatement.getMethodName()); + } + } else if (list[i] instanceof Serializable) { + s.writeObject(((Class) list[i - 1]).getName()); + s.writeObject(list[i]); + } + } + } + + s.writeObject(null); + } + + @SuppressWarnings("unchecked") + private void readObject(ObjectInputStream s) throws ClassNotFoundException, + IOException { + s.defaultReadObject(); + + Object typeOrNull; + + while (null != (typeOrNull = s.readObject())) { + if ("callback".equals(typeOrNull)) { + Object handler = s.readObject(); + String method = (String) s.readObject(); + + addActionListener(EventHandler.create(ActionListener.class, handler, method)); + } else if (BooleanInvocationHandler.class.getName().equals(typeOrNull)) { + Object handler = s.readObject(); + String method = (String) s.readObject(); + + addItemListener(new BooleanInvocationHandler(handler, method)); + } else { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + EventListener l = (EventListener) s.readObject(); + addListener((Class)Class.forName((String)typeOrNull, true, cl), l); + } + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/action/CompositeAction.java b/src/main/java/org/jdesktop/swingx/action/CompositeAction.java new file mode 100644 index 0000000000..b0debd8e62 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/action/CompositeAction.java @@ -0,0 +1,137 @@ +/* + * $Id: CompositeAction.java 3972 2011-03-17 20:31:58Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.action; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.ItemEvent; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * A class that represents an action which will fire a sequence of actions. + * The action ids are added to the internal list. When this action is invoked, + * the event will be dispatched to the actions in the internal list. + *

      + * The action ids are represented by the value of the Action.ACTION_COMMAND_KEY + * and must be managed by the ActionManager. When this action is + * invoked, then the actions are retrieved from the ActionManager in list order + * and invoked. + * + * @see ActionManager + * @author Mark Davidson + */ +public class CompositeAction extends AbstractActionExt { + + /** + * Keys for storing extended action attributes. May make public. + */ + private static final String LIST_IDS = "action-list-ids"; + + public CompositeAction() { + this("CompositeAction"); + } + + public CompositeAction(String name) { + super(name); + } + + /** + * @param name display name of the action + * @param command the value of the action command key + */ + public CompositeAction(String name, String command) { + super(name, command); + } + + public CompositeAction(String name, Icon icon) { + super(name, icon); + } + + /** + * @param name display name of the action + * @param command the value of the action command key + * @param icon icon to display + */ + public CompositeAction(String name, String command, Icon icon) { + super(name, command, icon); + } + + /** + * Add an action id to the action list. This action will be invoked + * when this composite action is invoked. + */ + @SuppressWarnings("unchecked") + public void addAction(String id) { + List list = (List) getValue(LIST_IDS); + if (list == null) { + list = new ArrayList(); + putValue(LIST_IDS, list); + } + list.add(id); + } + + /** + * Returns a list of action ids which indicates that this is a composite + * action. + * @return a valid list of action ids or null + */ + @SuppressWarnings("unchecked") + public List getActionIDs() { + return (List) getValue(LIST_IDS); + } + + /** + * Callback for composite actions. This method will redispatch the + * ActionEvent to all the actions held in the list. + */ + @Override + public void actionPerformed(ActionEvent evt) { + ActionManager manager = ActionManager.getInstance(); + + Iterator iter = getActionIDs().iterator(); + while (iter.hasNext()) { + String id = iter.next(); + Action action = manager.getAction(id); + if (action != null) { + action.actionPerformed(evt); + } + } + } + + /** + * Callback for toggle actions. + */ + @Override + public void itemStateChanged(ItemEvent evt) { + ActionManager manager = ActionManager.getInstance(); + + Iterator iter = getActionIDs().iterator(); + while (iter.hasNext()) { + String id = iter.next(); + Action action = manager.getAction(id); + if (action != null && action instanceof AbstractActionExt) { + ((AbstractActionExt)action).itemStateChanged(evt); + } + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/action/OpenBrowserAction.java b/src/main/java/org/jdesktop/swingx/action/OpenBrowserAction.java new file mode 100644 index 0000000000..739ac7b460 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/action/OpenBrowserAction.java @@ -0,0 +1,122 @@ +/* + * $Id: OpenBrowserAction.java 3972 2011-03-17 20:31:58Z kschaefe $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.action; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * An action for opening a {@link URI} in a browser. The URI may be {@code null} and if so this + * action does nothing. + * + * @author Karl Schaefer + * @author joshy (original version) + */ +public class OpenBrowserAction extends AbstractAction { + private static Logger log = Logger.getLogger(OpenBrowserAction.class.getName()); + + private URI uri; + + /** Creates a new instance of OpenBrowserAction */ + public OpenBrowserAction() { + this((URI) null); + } + + /** + * Creates a new action for the specified URI. + * + * @param uri + * the URI + * @throws NullPointerException + * if {@code uri} is {@code null} + * @throws IllegalArgumentException + * if the given string violates RFC 2396 + */ + public OpenBrowserAction(String uri) { + this(URI.create(uri)); + } + + /** + * Creates a new action for the specified URL. + * + * @param url + * the URL + * @throws URISyntaxException + * if the URL cannot be converted to a valid URI + */ + public OpenBrowserAction(URL url) throws URISyntaxException { + this(url.toURI()); + } + + /** + * Creates a new action for the specified URI. + * + * @param uri + * the URI + */ + public OpenBrowserAction(URI uri) { + setURI(uri); + } + + /** + * Gets the current URI. + * + * @return the URI + */ + public URI getURI() { + return uri; + } + + /** + * Sets the current URI. + * + * @param uri + * the new URI + */ + public void setURI(URI uri) { + this.uri = uri; + } + + /** + * {@inheritDoc} + */ + @Override + public void actionPerformed(ActionEvent e) { + if (uri == null || !Desktop.isDesktopSupported()) { + return; + } + + if (Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) { + try { + Desktop.getDesktop().browse(uri); + } catch (IOException ioe) { + log.log(Level.WARNING, "unable to browse: " + uri, ioe); + } + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/action/ServerAction.java b/src/main/java/org/jdesktop/swingx/action/ServerAction.java new file mode 100644 index 0000000000..362ed943bd --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/action/ServerAction.java @@ -0,0 +1,324 @@ +/* + * $Id: ServerAction.java 3972 2011-03-17 20:31:58Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.action; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.io.*; +import java.net.*; +import java.security.AccessControlException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +//import org.jdesktop.swing.Application; + + +/** + * An action which will invoke an http POST operation. + * + * @author Mark Davidson + */ +public class ServerAction extends AbstractAction { + // Server action support + private static final Logger LOG = Logger.getLogger(ServerAction.class + .getName()); + private static final String PARAMS = "action-params"; + private static final String HEADERS = "action-headers"; + private static final String URL = "action-url"; + + private static final String URL_CACHE = "_URL-CACHE__"; + + public ServerAction() { + this("action"); + } + + public ServerAction(String name) { + super(name); + } + + /** + * @param name display name of the action + * @param command the value of the action command key + */ + public ServerAction(String name, String command) { + this(name, command, null); + } + + public ServerAction(String name, Icon icon) { + super(name, icon); + } + + /** + * @param name display name of the action + * @param command the value of the action command key + * @param icon icon to display + */ + public ServerAction(String name, String command, Icon icon) { + super(name, icon); + putValue(Action.ACTION_COMMAND_KEY, command); + } + + /** + * Set the url for the action. + *

      + * @param url a string representation of the url + */ + public void setURL(String url) { + putValue(URL, url); + putValue(URL_CACHE, null); + } + + public String getURL() { + return (String)getValue(URL); + } + + @SuppressWarnings("unchecked") + private Map getParams() { + return (Map)getValue(PARAMS); + } + + private void setParams(Map params) { + putValue(PARAMS, params); + } + + /** + * Adds a name value pair which represents a url parameter in an http + * POST request. + */ + public void addParam(String name, String value) { + Map params = getParams(); + if (params == null) { + params = new HashMap(); + setParams(params); + } + params.put(name, value); + } + + /** + * Return a parameter value corresponding to name or null if it doesn't exist. + */ + public String getParamValue(String name) { + Map params = getParams(); + return params == null ? null : params.get(name); + } + + /** + * Return a set of parameter names or null if there are no params + */ + public Set getParamNames() { + Map params = getParams(); + return params == null ? null : params.keySet(); + } + + @SuppressWarnings("unchecked") + private Map getHeaders() { + return (Map)getValue(HEADERS); + } + + private void setHeaders(Map headers) { + putValue(HEADERS, headers); + } + + /** + * Adds a name value pair which represents a url connection request property. + * For example, name could be "Content-Type" and the value could be + * "application/x-www-form-urlencoded" + */ + public void addHeader(String name, String value) { + Map map = getHeaders(); + if (map == null) { + map = new HashMap(); + setHeaders(map); + } + map.put(name, value); + } + + /** + * Return a header value corresponding to name or null if it doesn't exist. + */ + public String getHeaderValue(String name) { + Map headers = getHeaders(); + return headers == null ? null : headers.get(name); + } + + /** + * Return a set of parameter names or null if there are no params + */ + public Set getHeaderNames() { + Map headers = getHeaders(); + return headers == null ? null : headers.keySet(); + } + + /** + * Invokes the server operation when the action has been invoked. + */ + @Override + public void actionPerformed(ActionEvent evt) { + URL execURL = (URL)getValue(URL_CACHE); + if (execURL == null && !"".equals(getURL())) { + try { + String url = getURL(); + if (url.startsWith("http")) { + execURL = new URL(url); + } else { + } + if (execURL == null) { + // XXX TODO: send a message + return; + } else { + // Cache this value. + putValue(URL_CACHE, execURL); + } + + } catch (MalformedURLException ex) { + LOG.log(Level.WARNING, "something went wrong...", ex); + } + } + + try { + URLConnection uc = execURL.openConnection(); + + // Get all the header name/value pairs ans set the request headers + Set headerNames = getHeaderNames(); + if (headerNames != null && !headerNames.isEmpty()) { + Iterator iter = headerNames.iterator(); + while (iter.hasNext()) { + String name = (String)iter.next(); + uc.setRequestProperty(name, getHeaderValue(name)); + } + } + uc.setUseCaches(false); + uc.setDoOutput(true); + + ByteArrayOutputStream byteStream = new ByteArrayOutputStream(512); + PrintWriter out = new PrintWriter(byteStream, true); + out.print(getPostData()); + out.flush(); + + // POST requests must have a content-length. + String length = String.valueOf(byteStream.size()); + uc.setRequestProperty("Content-length", length); + + // Write POST data to real output stream. + byteStream.writeTo(uc.getOutputStream()); + + BufferedReader buf = null; + if (uc instanceof HttpURLConnection) { + HttpURLConnection huc = (HttpURLConnection)uc; + int code = huc.getResponseCode(); + String message = huc.getResponseMessage(); + + // Handle the result. + if (code < 400) { + // action succeeded send to status bar + // XXX TODO: setStatusMessage(createMessage(code, message)); + // Format the response + // TODO: This should load asychnonously + buf = new BufferedReader(new InputStreamReader(uc.getInputStream())); + + } else { + // action has failed show dialog + // XXX TODO: setStatusMessage(createMessage(code, message)); + buf = new BufferedReader(new InputStreamReader(huc.getErrorStream())); + } + String line; + + StringBuffer buffer = new StringBuffer(); + while ((line = buf.readLine()) != null) { + // RG: Fix for J2SE 5.0; Can't cascade append() calls because + // return type in StringBuffer and AbstractStringBuilder are different + buffer.append(line); + buffer.append('\n'); + } + // JW: this used the Debug - maybe use finest level? + LOG.finer("returned from connection\n" + buffer.toString()); + } + } catch (UnknownHostException ex) { + LOG.log(Level.WARNING, "UnknownHostException detected. Could it be a proxy issue?", ex); + + } catch (AccessControlException ex) { + LOG.log(Level.WARNING, "AccessControlException detected", ex); + } catch (IOException ex) { + LOG.log(Level.WARNING, "IOException detected", ex); + } + } + + /** + * Retrieves a string which represents the parameter data for a server action. + * @return a string of name value pairs prefixed by a '?' and delimited by an '&' + */ + private String getPostData() { + // Write the data into local buffer + StringBuffer postData = new StringBuffer(); + + // TODO: the action should be configured to retrieve the data. + + // Get all the param name/value pairs and build the data string + Set paramNames = getParamNames(); + if (paramNames != null && !paramNames.isEmpty()) { + Iterator iter = paramNames.iterator(); + try { + while (iter.hasNext()) { + String name = iter.next(); + postData.append('&').append(name).append('='); + postData.append(getParamValue(name)); + } + } + catch (Exception ex) { // RG: append(char) throws IOException in J2SE 5.0 + /** @todo Log it */ + } + // Replace the first & with a ? + postData.setCharAt(0, '?'); + } + + LOG.finer("ServerAction: POST data: " + postData.toString()); + return postData.toString(); + } + + + /** + * Creates a human readable message from the server code and message result. + * @param code an http error code. + * @param msg server message + */ + private String createMessage(int code, String msg) { + StringBuffer buffer = new StringBuffer("The action \""); + buffer.append(getValue(NAME)); + + if (code < 400) { + buffer.append("\" has succeeded "); + } else { + buffer.append("\" has failed\nPlease check the Java console for more details.\n"); + } + // RG: Fix for J2SE 5.0; Can't cascade append() calls because + // return type in StringBuffer and AbstractStringBuilder are different + buffer.append("\nServer response:\nCode: "); + buffer.append(code); + buffer.append(" Message: "); + buffer.append(msg); + + return buffer.toString(); + } +} diff --git a/src/main/java/org/jdesktop/swingx/action/TargetManager.java b/src/main/java/org/jdesktop/swingx/action/TargetManager.java new file mode 100644 index 0000000000..ef1798a7d9 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/action/TargetManager.java @@ -0,0 +1,280 @@ +/* + * $Id: TargetManager.java 3972 2011-03-17 20:31:58Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.action; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + + + +/** + * The target manager dispatches commands to {@link Targetable} objects + * that it manages. This design of this class is based on the Chain of + * Responsiblity and Mediator design patterns. The target manager + * acts as a mediator between {@link TargetableAction}s and the intended targets. + * This allows Action based components to invoke commands on components + * without explicitly binding the user Action to the component action. + *

      + * The target manager maintains a reference to a current + * target and a target list. + * The target list is managed using the addTarget and + * removeTarget methods. The current target is managed using the + * setTarget and getTarget methods. + *

      + * Commands are dispatched to the Targetable objects in the doCommand + * method in a well defined order. The doCommand method on the Targetable object + * is called and if it returns true then the command has been handled and + * command dispatching will stop. If the Targetable doCommand method returns + * false then the + *

      + * If none of the Targetable objects can handle the command then the default + * behaviour is to retrieve an Action from the {@link ActionMap} of + * the permanent focus owner with a key that matches the command key. If an + * Action can be found then the actionPerformed + * method is invoked using an ActionEvent that was constructed + * using the command string. + *

      + * If the Action is not found on the focus order then the ActionMaps of the ancestor + * hierarchy of the focus owner is searched until a matching Action can be found. + * Finally, if none + * of the components can handle the command then it is dispatched to the ActionMap + * of the current Application instance. + *

      + * The order of command dispatch is as follows: + *

        + *
      • Current Targetable object invoking doCommand method + *
      • List order of Targetable objects invoking doCommand method + *
      • ActionMap entry of the permanent focus owner invoking actionPerfomed + *
      • ActionMap entry of the ancestor hierarchy of the permanent focus owner + *
      • ActionMap entry of the current Application instance + *
      + * + * @see Targetable + * @see TargetableAction + * @author Mark Davidson + */ +public class TargetManager { + + private static TargetManager INSTANCE; + private List targetList; + private Targetable target; + private PropertyChangeSupport propertySupport; + + /** + * Create a target manager. Use this constructor if the application + * may support many target managers. Otherwise, using the getInstance method + * will return a singleton. + */ + public TargetManager() { + propertySupport = new PropertyChangeSupport(this); + } + + /** + * Return the singleton instance. + */ + public static TargetManager getInstance() { + if (INSTANCE == null) { + INSTANCE = new TargetManager(); + } + return INSTANCE; + } + + /** + * Add a target to the target list. Will be appended + * to the list by default. If the prepend flag is true then + * the target will be added at the head of the list. + *

      + * Targets added to the head of the list will will be the first + * to handle the command. + * + * @param target the targeted object to add + * @param prepend if true add at the head of the list; false append + */ + public void addTarget(Targetable target, boolean prepend) { + if (targetList == null) { + targetList = new ArrayList(); + } + if (prepend) { + targetList.add(0, target); + } else { + targetList.add(target); + } + // Should add focus listener to the component. + } + + /** + * Appends the target to the target list. + * @param target the targeted object to add + */ + public void addTarget(Targetable target) { + addTarget(target, false); + } + + /** + * Remove the target from the list + */ + public void removeTarget(Targetable target) { + if (targetList != null) { + targetList.remove(target); + } + } + + /** + * Returns an array of managed targets that were added with the + * addTarget methods. + * + * @return all the Targetable added or an empty array if no + * targets have been added + */ + public Targetable[] getTargets() { + Targetable[] targets; + if (targetList == null) { + targets = new Targetable[0]; + } else { + targets = new Targetable[targetList.size()]; + targets = (Targetable[])targetList.toArray(new Targetable[targetList.size()]); + } + return targets; + } + + /** + * Gets the current targetable component. May or may not + * in the target list. If the current target is null then + * the the current targetable component will be the first one + * in the target list which can execute the command. + * + * This is a bound property and will fire a property change event + * if the value changes. + * + * @param newTarget the current targetable component to set or null if + * the TargetManager shouldn't have a current targetable component. + */ + public void setTarget(Targetable newTarget) { + Targetable oldTarget = target; + if (oldTarget != newTarget) { + target = newTarget; + propertySupport.firePropertyChange("target", oldTarget, newTarget); + } + } + + /** + * Return the current targetable component. The curent targetable component + * is the first place where commands will be dispatched. + * + * @return the current targetable component or null + */ + public Targetable getTarget() { + return target; + } + + public void addPropertyChangeListener(PropertyChangeListener listener) { + propertySupport.addPropertyChangeListener(listener); + } + + public void removePropertyChangeListener(PropertyChangeListener listener) { + propertySupport.removePropertyChangeListener(listener); + } + + /** + * Executes the command on the current targetable component. + * If there isn't current targetable component then the list + * of targetable components are searched and the first component + * which can execute the command. If none of the targetable + * components handle the command then the ActionMaps of the + * focused components are searched. + * + * @param command the key of the command + * @param value the value of the command; depends on context + * @return true if the command has been handled otherwise false + */ + public boolean doCommand(Object command, Object value) { + // Try to invoked the explicit target. + if (target != null) { + if (target.hasCommand(command) && target.doCommand(command, value)) { + return true; + } + } + + // The target list has the next chance to handle the command. + if (targetList != null) { + Iterator iter = targetList.iterator(); + while (iter.hasNext()) { + Targetable target = iter.next(); + if (target.hasCommand(command) && + target.doCommand(command, value)) { + return true; + } + } + } + + ActionEvent evt = null; + if (value instanceof ActionEvent) { + evt = (ActionEvent)value; + } + + // Fall back behavior. Get the component which has focus and search the + // ActionMaps in the containment hierarchy for matching action. + Component comp = KeyboardFocusManager.getCurrentKeyboardFocusManager().getPermanentFocusOwner(); + while (comp != null) { + if (comp instanceof JComponent) { + ActionMap map = ((JComponent)comp).getActionMap(); + Action action = map.get(command); + if (action != null) { + if (evt == null) { + evt = new ActionEvent(comp, 0, command.toString()); + } + action.actionPerformed(evt); + + return true; + } + } + comp = comp.getParent(); + } + + return false; + } + + /** + * Resets the TargetManager. + * This method is package private and for testing purposes only. + */ + void reset() { + if (targetList != null) { + targetList.clear(); + targetList = null; + } + target = null; + + PropertyChangeListener[] listeners = propertySupport.getPropertyChangeListeners(); + for (int i = 0; i < listeners.length; i++) { + propertySupport.removePropertyChangeListener(listeners[i]); + } + INSTANCE = null; + } + +} diff --git a/src/main/java/org/jdesktop/swingx/action/Targetable.java b/src/main/java/org/jdesktop/swingx/action/Targetable.java new file mode 100644 index 0000000000..43712ce47a --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/action/Targetable.java @@ -0,0 +1,64 @@ +/* + * $Id: Targetable.java 3972 2011-03-17 20:31:58Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.action; + + +/** + * An interface which exposes the allowable actions to a TargetManager. + * The getCommands method will expose the allowable actions to another class + * and the doCommand method is called to invoke an action on the class. + *

      + * Usually, the command key will be the key value of the Action. For components + * This could be the ActionMap keys. For actions managed with the ActionManager, + * this will be the value of an actions Action.ACTION_COMMAND_KEY + * + * @see TargetManager + * @author Mark Davidson + */ +public interface Targetable { + + /** + * Perform the command using the object value. + * + * @param command is a Action.ACTION_COMMAND_KEY + * @param value an arbitrary value. Usually this will be + * EventObject which trigered the command. + */ + boolean doCommand(Object command, Object value); + + /** + * Return a flag that indicates if a command is supported. + * + * @param command is a Action.ACTION_COMMAND_KEY + * @return true if command is supported; false otherwise + */ + boolean hasCommand(Object command); + + /** + * Returns an array of supported commands. If this Targetable + * doesn't support any commands (which is unlikely) then an + * empty array is returned. + * + * @return array of supported commands + */ + Object[] getCommands(); +} diff --git a/src/main/java/org/jdesktop/swingx/action/TargetableAction.java b/src/main/java/org/jdesktop/swingx/action/TargetableAction.java new file mode 100644 index 0000000000..f0b10f4b1f --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/action/TargetableAction.java @@ -0,0 +1,140 @@ +/* + * $Id: TargetableAction.java 3972 2011-03-17 20:31:58Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.action; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.ItemEvent; + +/** + * A class that represents a dynamically targetable action. The invocation of this + * action will be dispatched to the TargetManager instance. + *

      + * You would create instances of this class to let the TargetManager handle the + * action invocations from the ui components constructed with this action. + * The TargetManager could be configured depending on application state to + * handle these actions. + * + * @see TargetManager + * @author Mark Davidson + */ +public class TargetableAction extends AbstractActionExt { + + private TargetManager targetManager; + + public TargetableAction() { + this("action"); + } + + public TargetableAction(String name) { + super(name); + } + + /** + * @param name display name of the action + * @param command the value of the action command key + */ + public TargetableAction(String name, String command) { + super(name, command); + } + + /** + * @param name display name of the action + * @param command the value of the action command key + * @param icon icon to display + */ + public TargetableAction(String name, String command, Icon icon) { + super(name, command, icon); + } + + public TargetableAction(String name, Icon icon) { + super(name, icon); + } + + /** + * Set target manager which will handle this command. This action + * may be reset to use the singleton instance if set to null. + * + * @param tm the target manager instance to dispatch the actions + */ + public void setTargetManager(TargetManager tm) { + this.targetManager = tm; + } + + /** + * Returns the target manager instance which will be used for action + * dispatch. If the target manager has not previously been set then the + * singleton instance will be returned. + * + * @return a non null target manager + */ + public TargetManager getTargetManager() { + if (targetManager == null) { + targetManager = TargetManager.getInstance(); + } + return targetManager; + } + + // Callbacks... + + /** + * Callback for command actions. This event will be redispatched to + * the target manager along with the value of the Action.ACTION_COMMAND_KEY + * + * @param evt event which will be forwarded to the TargetManager + * @see TargetManager + */ + @Override + public void actionPerformed(ActionEvent evt) { + if (!isStateAction()) { + // Do not process this event if it's a toggle action. + getTargetManager().doCommand(getActionCommand(), evt); + } + } + + /** + * Callback for toggle actions. This event will be redispatched to + * the target manager along with value of the the Action.ACTION_COMMAND_KEY + * + * @param evt event which will be forwarded to the TargetManager + * @see TargetManager + */ + @Override + public void itemStateChanged(ItemEvent evt) { + // Update all objects that share this item + boolean newValue; + boolean oldValue = isSelected(); + + newValue = evt.getStateChange() == ItemEvent.SELECTED; + + if (oldValue != newValue) { + setSelected(newValue); + + getTargetManager().doCommand(getActionCommand(), evt); + } + } + + @Override + public String toString() { + return super.toString(); + } +} diff --git a/src/main/java/org/jdesktop/swingx/action/TargetableSupport.java b/src/main/java/org/jdesktop/swingx/action/TargetableSupport.java new file mode 100644 index 0000000000..093e68dfdd --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/action/TargetableSupport.java @@ -0,0 +1,72 @@ +/* + * $Id: TargetableSupport.java 3972 2011-03-17 20:31:58Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.action; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +/** + * + * @author rbair + */ +public class TargetableSupport { + private JComponent component; + + /** Creates a new instance of TargetableSupport */ + public TargetableSupport(JComponent component) { + this.component = component; + } + + public boolean doCommand(Object command, Object value) { + // Look at the internal component first. + ActionMap map = component.getActionMap(); + Action action = map.get(command); + + if (action != null) { + if (value instanceof ActionEvent) { + action.actionPerformed( (ActionEvent) value); + } + else { + // XXX should the value represent the event source? + action.actionPerformed(new ActionEvent(value, 0, + command.toString())); + } + return true; + } + return false; + } + + public Object[] getCommands() { + ActionMap map = component.getActionMap(); + return map.allKeys(); + } + + public boolean hasCommand(Object command) { + Object[] commands = getCommands(); + for (int i = 0; i < commands.length; i++) { + if (commands[i].equals(command)) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/org/jdesktop/swingx/action/ToggleActionPropertyChangeListener.java b/src/main/java/org/jdesktop/swingx/action/ToggleActionPropertyChangeListener.java new file mode 100644 index 0000000000..2a23e6332f --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/action/ToggleActionPropertyChangeListener.java @@ -0,0 +1,168 @@ +/* + * $Id: ToggleActionPropertyChangeListener.java 3972 2011-03-17 20:31:58Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.action; + +import javax.swing.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.lang.ref.WeakReference; + +/** + * Added to the Toggle type buttons and menu items so that various components + * which have been created from a single StateChangeAction can be in synch. + * + * This listener is responsible for updating the selected property from the + * Action to the AbstractButton.

      + * + * It guarantees a maximum of 1 instance of + * ToggleActionPCL to be installed per button (PENDING JW: add test to verify). + * It removes all ToggleActionPCLs which are targeted to unreachable buttons + * from the action's listener list. + * + * + */ +class ToggleActionPropertyChangeListener implements PropertyChangeListener { + + + private WeakReference buttonRef; + + public ToggleActionPropertyChangeListener(Action action, AbstractButton button) { + if (shouldAddListener(action, button)) { + this.buttonRef = new WeakReference(button); + action.addPropertyChangeListener(this); + } + } + + + protected synchronized boolean shouldAddListener(Action action, AbstractButton button) { + releasePCLs(action); + // PENDING JW: revisit - we need a configurator to maintain at most a 1:1 from button to + // action anyway: so a true in isToggling must not happen. + // + return !isToggling(action, button); +// return true; + } + + + protected boolean isToggling(Action action, AbstractButton button) { + if (!(action instanceof AbstractAction)) return false; + PropertyChangeListener[] listeners = ((AbstractAction)action).getPropertyChangeListeners(); + for (int i = listeners.length - 1; i >= 0; i--) { + if (listeners[i] instanceof ToggleActionPropertyChangeListener) { + if (((ToggleActionPropertyChangeListener) listeners[i]).isToggling(button)) return true; + } + } + return false; + } + + /** + * Removes all ToggleActionPCLs with unreachable target buttons from the + * Action's PCL-listeners. + * + * @param action to cleanup. + */ + protected void releasePCLs(Action action) { + if (!(action instanceof AbstractAction)) return; + PropertyChangeListener[] listeners = ((AbstractAction)action).getPropertyChangeListeners(); + for (int i = listeners.length - 1; i >= 0; i--) { + if (listeners[i] instanceof ToggleActionPropertyChangeListener) { + ((ToggleActionPropertyChangeListener) listeners[i]).checkReferent(action); + } + } + } + + + @Override + public void propertyChange(PropertyChangeEvent evt) { + AbstractButton button = checkReferent((Action) evt.getSource()); + if (button == null) return; + String propertyName = evt.getPropertyName(); + + if (propertyName.equals("selected")) { + Boolean selected = (Boolean)evt.getNewValue(); + button.setSelected(selected.booleanValue()); + } + } + + /** + * Returns the target button to synchronize from the listener. + * + * Side-effects if the target is no longer reachable: + * - the internal reference to target is nulled. + * - if the given action is != null, this listener removes + * itself from the action's listener list. + * + * @param action The action this is listening to. + * @return the target button if it is strongly reachable or null + * if it is no longer strongly reachable. + */ + protected AbstractButton checkReferent(Action action) { + AbstractButton button = null; + if (buttonRef != null) { + button = buttonRef.get(); + } + if (button == null) { + if (action != null) { + action.removePropertyChangeListener(this); + } + buttonRef = null; + } + return button; + } + + + /** + * Check if this is already synchronizing the given AbstractButton. + * + * This may have the side-effect of releasing the weak reference to + * the target button. + * + * @param button must not be null + * @return true if this target button and the given comp are equal + * false otherwise. + * @throws NullPointerException if the button is null. + */ + public boolean isToggling(AbstractButton button) { + // JW: should check identity instead of equality? + return button.equals(checkReferent(null)); + } + + /** + * Checks if this is synchronizing to the same target button as the + * given listener. + * + * This may have the side-effect of releasing the weak reference to + * the target button. + * + * @param pcl The listener to test, must not be null. + * @return true if the target buttons of the given is equal to this target + * button and the button is strongly reachable, false otherwise. + */ +// public boolean isToggling(ToggleActionPropertyChangeListener pcl) { +// AbstractButton other = pcl.checkReferent(null); +// if (other != null) { +// return isToggling(other); +// } +// return false; +// } + + +} diff --git a/src/main/java/org/jdesktop/swingx/action/package-info.java b/src/main/java/org/jdesktop/swingx/action/package-info.java new file mode 100644 index 0000000000..f2182ec7f4 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/action/package-info.java @@ -0,0 +1,102 @@ +/* + * $Id: package-info.java 3972 2011-03-17 20:31:58Z kschaefe $ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +/** + * Contains classes related to the JDNC actions architecture. The Actions + * architecture maintains the set of user initiated commands (referred to as + * user actions) in an application. These commands are represented as an + * {@link javax.swing.Action} and have properties like name and icon. The + * user actions + * are represented in the user interface by controls like menu items and + * toolbar buttons. + *

      + * The other type of actions used by the architecture are the internal + * swing Actions (refered to as behaviour actions) that are embedded + * within the {@link javax.swing.ActionMap} of a {@link javax.swing.JComponent}. + *

      + * These two types of actions are distinct from each other: user actions + * have a lot of properties but very little semantics by default + * (unless explicity bound). Behavior actions have no properties but have + * semantics. These two types of actions are linked by the action id + * which is the value of the Action.ACTION_COMMAND_KEY + *

      + * The {@link org.jdesktop.swingx.action.AbstractActionExt} class extends the Swing + * concept of the Action by adding support for toggle or two state actions. + * Toggle type actions may be grouped into a set of mutually exclusive actions. + * This binary actions are represented in the user interface as JToggleButtons, + * JCheckBoxMenuItems or JRadioButtonMenuItems. + *

      + * There are two types of user actions: A {@link org.jdesktop.swingx.action.BoundAction} + * is an action that will invoke a specific method. It may be bound to an explict + * component, a callback method on an object instance or one or more listeners. + * A {@link org.jdesktop.swingx.action.TargetableAction} is an action that doesn't have an + * explicit binding and the invocation will be sent to an arbitrator + * (the {@link org.jdesktop.swingx.action.TargetManager}) which dispatches the Action + * to the "current component" - represented by a Targetable instance. + * The current component may be explictly set by some programmatic + * policy (for example, changes in state). + *

      + * By defalt, the current component will be driven by the focus policy as dictated + * by the current FocusManager. If the current component cannot handle the action + * then the action will be dispatched up the containment hierarchy until the action + * is consumed. If the action is not consumed then it will be dispatched to the + * Application instance which manages an application global set of actions. + *

      + * These are the key classes or the actions architecture: + *

      + *

      + *
      {@link org.jdesktop.swingx.action.ActionManager}
      + *
      A repository of all shared actions in the application. + * There will be one instance per application which can be accessed + * via the Application object (was ClientApp) + *
      + * + *
      {@link org.jdesktop.swingx.action.ActionContainerFactory}
      + *
      Constructs JMenuBars, JMenus, JPopupMenus and + * JToolBars using lists of action ids. This functionality may + * be migrated into ActionManager. + *
      + * + *
      {@link org.jdesktop.swingx.action.TargetableAction}
      + *
      Represents an unbound Action. The invocation of this action + * will be dispatched to the TargetManager.
      + * + *
      {@link org.jdesktop.swingx.action.BoundAction}
      + *
      Represents an action which has an exclicit binding.
      + * + *
      {@link org.jdesktop.swingx.action.TargetManager}
      + *
      Manages the targetable policy for actions which have no + * explicit binding. The policy can be set by changes in application + * state, event based criteria or whatever. If the policy has not been + * set then it will dispatch the action to the current focusable + * component. + *
      + * + *
      {@link org.jdesktop.swingx.action.Targetable}
      + *
      An interface that contains a few methods which expose actions to + * the TargetManager. Targetable objects don't have to be visual + * components they only have to be able to handle action invocations. + *
      + *
      + * + *
      + *
      Richard Bair
      + */ +package org.jdesktop.swingx.action; \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/auth/DefaultUserNameStore.java b/src/main/java/org/jdesktop/swingx/auth/DefaultUserNameStore.java new file mode 100644 index 0000000000..3e26f0e1ed --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/auth/DefaultUserNameStore.java @@ -0,0 +1,193 @@ +/* + * $Id: DefaultUserNameStore.java 4147 2012-02-01 17:13:24Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.auth; + +import org.jdesktop.beans.JavaBean; + +import java.util.prefs.Preferences; + +/** + * Saves the user names in Preferences. Because any string could be part + * of the user name, for every user name that must be saved a new Preferences + * key/value pair must be stored. + * + * @author Bino George + * @author rbair + */ +@JavaBean +public class DefaultUserNameStore extends UserNameStore { + /** + * The key for one of the preferences + */ + private static final String USER_KEY = "usernames"; + /** + */ + private static final String NUM_KEY = "usernames.length"; + /** + * The preferences node + */ + private Preferences prefs; + /** + * Contains the user names. Since the list of user names is not + * frequently updated, there is no penalty in storing the values + * in an array. + */ + private String[] userNames; + + /** + * Creates a new instance of DefaultUserNameStore + */ + public DefaultUserNameStore() { + userNames = new String[0]; + } + + /** + * Loads the user names from Preferences + */ + @Override + public void loadUserNames() { + initPrefs(); + if (prefs != null) { + int n = prefs.getInt(NUM_KEY, 0); + String[] names = new String[n]; + for (int i = 0; i < n; i++) { + names[i] = prefs.get(USER_KEY + "." + i, null); + } + setUserNames(names); + } + } + + /** + * Saves the user names to Preferences + */ + @Override + public void saveUserNames() { + initPrefs(); + if (prefs != null) { + prefs.putInt(NUM_KEY, userNames.length); + for (int i = 0; i < userNames.length; i++) { + prefs.put(USER_KEY + "." + i, userNames[i]); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public String[] getUserNames() { + String[] copy = new String[userNames.length]; + System.arraycopy(userNames, 0, copy, 0, userNames.length); + + return copy; + } + + /** + * {@inheritDoc} + */ + @Override + public void setUserNames(String[] userNames) { + userNames = userNames == null ? new String[0] : userNames; + String[] old = getUserNames(); + this.userNames = userNames; + firePropertyChange("userNames", old, getUserNames()); + } + + /** + * Add a username to the store. + * @param name + */ + @Override + public void addUserName(String name) { + if (!containsUserName(name)) { + String[] newNames = new String[userNames.length + 1]; + for (int i=0; iJAASLoginService implements a LoginService + * that uses JAAS for authentication. JAASLoginService uses the + * server name as name of the configuration for JAAS. + * + * @author Bino George + */ +@JavaBean +public class JAASLoginService extends LoginService { + private static final Logger LOG = Logger.getLogger(JAASLoginService.class + .getName()); + + protected LoginContext loginContext; + + /** + * Constructor for JAASLoginService + * @param server server name that is also used for the JAAS config name + */ + public JAASLoginService(String server) { + super(server); + } + + /** + * Default JavaBeans constructor + */ + public JAASLoginService() { + super(); + } + + + /** + * @inheritDoc + * + */ + @Override + public boolean authenticate(String name, char[] password, String server) throws Exception { + // If user has selected a different server, update the login service + if (server != null) { + if (!server.equals(getServer())) { + setServer(server); + } + } + // Clear the login context before attempting authentication + loginContext = null; + // Create a login context for the appropriate server and attempt to + // authenticate the user. + try { + loginContext = new LoginContext(getServer(), + new JAASCallbackHandler(name, password)); + loginContext.login(); + return true; + } catch (AccountExpiredException e) { + // TODO add explanation? + LOG.log(Level.WARNING, "", e); + return false; + } catch (CredentialExpiredException e) { + // TODO add explanation? + LOG.log(Level.WARNING, "", e); + return false; + } catch (FailedLoginException e) { + // TODO add explanation? + LOG.log(Level.WARNING, "", e); + return false; + } catch (LoginException e) { + // TODO add explanation? + LOG.log(Level.WARNING, "", e); + return false; + } catch (Throwable e) { + // TODO add explanation? + LOG.log(Level.WARNING, "", e); + return false; + } + } + + /** + * Returns the LoginContext used during the authentication + * process. + */ + public LoginContext getLoginContext() + { + return loginContext; + } + + /** + * Returns the Subject representing the authenticated + * individual, or null if the user has not yet been + * successfully authenticated. + */ + public Subject getSubject() + { + if (loginContext == null) + return null; + return loginContext.getSubject(); + } + + class JAASCallbackHandler implements CallbackHandler { + + private String name; + + private char[] password; + + public JAASCallbackHandler(String name, char[] passwd) { + this.name = name; + this.password = passwd; + } + + public void handle(Callback[] callbacks) throws java.io.IOException { + for (int i = 0; i < callbacks.length; i++) { + if (callbacks[i] instanceof NameCallback) { + NameCallback cb = (NameCallback) callbacks[i]; + cb.setName(name); + } else if (callbacks[i] instanceof PasswordCallback) { + PasswordCallback cb = (PasswordCallback) callbacks[i]; + cb.setPassword(password); + } + } + } + + } +} diff --git a/src/main/java/org/jdesktop/swingx/auth/JDBCLoginService.java b/src/main/java/org/jdesktop/swingx/auth/JDBCLoginService.java new file mode 100644 index 0000000000..42a829f76d --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/auth/JDBCLoginService.java @@ -0,0 +1,237 @@ +/* + * $Id: JDBCLoginService.java 4147 2012-02-01 17:13:24Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.auth; + +import org.jdesktop.beans.JavaBean; + +import javax.naming.InitialContext; +import java.sql.Connection; +import java.sql.DriverManager; +import java.util.Properties; +import java.util.logging.Level; +import java.util.logging.Logger; +/** + * A login service for connecting to SQL based databases via JDBC + * + * @author rbair + */ +@JavaBean +public class JDBCLoginService extends LoginService { + private static final Logger LOG = Logger.getLogger(JDBCLoginService.class + .getName()); + + /** + * The connection to the database + */ + private Connection conn; + /** + * If used, defines the JNDI context from which to get a connection to + * the data base + */ + private String jndiContext; + /** + * When using the DriverManager to connect to the database, this specifies + * any additional properties to use when connecting. + */ + private Properties properties; + + /** + * Create a new JDBCLoginService and initializes it to connect to a + * database using the given params. + * @param driver + * @param url + */ + public JDBCLoginService(String driver, String url) { + super(url); + try { + Class.forName(driver); + } catch (Exception e) { + LOG.log(Level.WARNING, "The driver passed to the " + + "JDBCLoginService constructor could not be loaded. " + + "This may be due to the driver not being on the classpath", e); + } + this.setUrl(url); + } + + /** + * Create a new JDBCLoginService and initializes it to connect to a + * database using the given params. + * @param driver + * @param url + * @param props + */ + public JDBCLoginService(String driver, String url, Properties props) { + super(url); + try { + Class.forName(driver); + } catch (Exception e) { + LOG.log(Level.WARNING, "The driver passed to the " + + "JDBCLoginService constructor could not be loaded. " + + "This may be due to the driver not being on the classpath", e); + } + this.setUrl(url); + this.setProperties(props); + } + + /** + * Create a new JDBCLoginService and initializes it to connect to a + * database using the given params. + * @param jndiContext + */ + public JDBCLoginService(String jndiContext) { + super(jndiContext); + this.jndiContext = jndiContext; + } + + /** + * Default JavaBean constructor + */ + public JDBCLoginService() { + super(); + } + + /** + * @return the JDBC connection url + */ + public String getUrl() { + return getServer(); + } + + /** + * @param url set the JDBC connection url + */ + public void setUrl(String url) { + String old = getUrl(); + setServer(url); + firePropertyChange("url", old, getUrl()); + } + + /** + * @return JDBC connection properties + */ + public Properties getProperties() { + return properties; + } + + /** + * @param properties miscellaneous JDBC properties to use when connecting + * to the database via the JDBC driver + */ + public void setProperties(Properties properties) { + Properties old = getProperties(); + this.properties = properties; + firePropertyChange("properties", old, getProperties()); + } + + public Connection getConnection() { + return conn; + } + + public void setConnection(Connection conn) { + Connection old = getConnection(); + this.conn = conn; + firePropertyChange("connection", old, getConnection()); + } + + /** + * Attempts to get a JDBC Connection from a JNDI javax.sql.DataSource, using + * that connection for interacting with the database. + * @throws Exception + */ + private void connectByJNDI(String userName, char[] password) throws Exception { + InitialContext ctx = new InitialContext(); + javax.sql.DataSource ds = (javax.sql.DataSource)ctx.lookup(jndiContext); + conn = ds.getConnection(userName, new String(password)); + conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ); + } + + /** + * Attempts to get a JDBC Connection from a DriverManager. If properties + * is not null, it tries to connect with those properties. If that fails, + * it then attempts to connect with a user name and password. If that fails, + * it attempts to connect without any credentials at all. + *

      + * If, on the other hand, properties is null, it first attempts to connect + * with a username and password. Failing that, it tries to connect without + * any credentials at all. + * @throws Exception + */ + private void connectByDriverManager(String userName, char[] password) throws Exception { + if (getProperties() != null) { + try { + conn = DriverManager.getConnection(getUrl(), getProperties()); + conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ); + } catch (Exception e) { + try { + conn = DriverManager.getConnection(getUrl(), userName, new String(password)); + conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ); + } catch (Exception ex) { + conn = DriverManager.getConnection(getUrl()); + conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ); + } + } + } else { + try { + conn = DriverManager.getConnection(getUrl(), userName, new String(password)); + } catch (Exception e) { + LOG.log(Level.WARNING, "Connection with properties failed. " + + "Tryint to connect without.", e); + //try to connect without using the userName and password + conn = DriverManager.getConnection(getUrl()); + + } + } + } + + /** + * @param name user name + * @param password user password + * @param server Must be either a valid JDBC URL for the type of JDBC driver you are using, + * or must be a valid JNDIContext from which to get the database connection + */ + @Override + public boolean authenticate(String name, char[] password, String server) throws Exception { + //try to form a connection. If it works, conn will not be null + //if the jndiContext is not null, then try to get the DataSource to use + //from jndi + if (jndiContext != null) { + try { + connectByJNDI(name, password); + } catch (Exception e) { + try { + connectByDriverManager(name, password); + } catch (Exception ex) { + LOG.log(Level.WARNING, "Login failed", ex); + //login failed + return false; + } + } + } else { + try { + connectByDriverManager(name, password); + } catch (Exception ex) { + LOG.log(Level.WARNING, "", ex); + return false; + } + } + return true; + } +} diff --git a/src/main/java/org/jdesktop/swingx/auth/KeyChain.java b/src/main/java/org/jdesktop/swingx/auth/KeyChain.java new file mode 100644 index 0000000000..db2448522b --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/auth/KeyChain.java @@ -0,0 +1,198 @@ +/* + * $Id: KeyChain.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.auth; + +import javax.crypto.spec.SecretKeySpec; +import java.io.*; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableEntryException; +import java.security.cert.CertificateException; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * KeyChain is a class that implements the "KeyChain" concept. + * Fundamentally, it allows you to store multiple keys/credentials + * in a central password store. Access to this central store is + * controlled through a master password. This mechanism is used in + * many popular client applications where you need to store credentials + * for multiple servers/accounts. The actual store for the KeyStore + * can be any OutputStream and it can work in the webstart sandbox + * using Muffins. + *

      + *

      + * To contstruct a KeyChain, you need to pass in an InputStream to the + * store and it will initialize the KeyStore from the InputStream. + * You can add and remove entries any time once you have an instance of + * KeyChain. To persist the KeyChain and reflect any changes, you need to + * call store method with an OutputStream. + *

      + * + * @author Bino George + */ +public class KeyChain { + private static final Logger LOG = Logger + .getLogger(KeyChain.class.getName()); + + private KeyStore store; + + private char[] masterPassword; + + /** + * Creates an instance of KeyChain and initializes the store + * from the InputStream. + * + * @param masterPassword + * @param inputStream + * @throws IOException + */ + public KeyChain(char[] masterPassword, InputStream inputStream) + throws IOException { + this.masterPassword = masterPassword; + + try { + store = KeyStore.getInstance("JCEKS"); + store.load(inputStream, masterPassword); + + } catch (KeyStoreException ex) { + LOG.log(Level.WARNING, "", ex); + } catch (CertificateException ex) { + LOG.log(Level.WARNING, "", ex); + } catch (NoSuchAlgorithmException ex) { + LOG.log(Level.WARNING, "", ex); + } catch (EOFException ex) { + LOG.log(Level.WARNING, "", ex); + } + + } + + /** + * Fetches the password for a given account/user and server. + * @param user + * @param server + * @return null if no password could be obtained, the password + * otherwise + */ + public String getPassword(String user, String server) { + + try { + + KeyStore.SecretKeyEntry entry2 = (KeyStore.SecretKeyEntry) store + .getEntry(user + "@" + server, + new KeyStore.PasswordProtection(masterPassword)); + return new String(entry2.getSecretKey().getEncoded()); + } catch (KeyStoreException ex) { + LOG.log(Level.WARNING, "", ex); + } catch (UnrecoverableEntryException ex) { + LOG.log(Level.WARNING, "", ex); + } catch (NoSuchAlgorithmException ex) { + LOG.log(Level.WARNING, "", ex); + } + + return null; + } + + /** + * Adds a password to the KeyChain for a given account/user and server. + * + * @param user + * @param server + * @param password + */ + public void addPassword(String user, String server, char[] password) + { + String pass = new String(password); + SecretKeySpec passwordKey = new SecretKeySpec(pass.getBytes(), "JCEKS"); + KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(passwordKey); + try { + store.setEntry(user + "@" + server, entry, + new KeyStore.PasswordProtection(masterPassword)); + } catch (KeyStoreException e) { + LOG.log(Level.WARNING, "", e); + } + } + + /** + * Removes a password for a given account/user and server. + * + * @param user + * @param server + */ + public void removePassword(String user, String server) { + try { + store.deleteEntry(user + "@" + server); + } catch (KeyStoreException e) { + LOG.log(Level.WARNING, "", e); + } + } + + /** + * Persists the KeyChain to an OutputStream + * + * @param ostream + * @throws IOException + */ + + public void store(OutputStream ostream) throws IOException { + try { + store.store(ostream, masterPassword); + } catch (KeyStoreException ex) { + LOG.log(Level.WARNING, "", ex); + } catch (CertificateException ex) { + LOG.log(Level.WARNING, "", ex); + } catch (NoSuchAlgorithmException ex) { + LOG.log(Level.WARNING, "", ex); + } + } + + + public static void main(String[] args) { + try { + File file = new File("c:\\test.txt"); + FileInputStream fis; + if (!file.exists()) { + file.createNewFile(); + fis = null; + } else { + fis = new FileInputStream(file); + } + KeyChain kc = new KeyChain("test".toCharArray(), fis); + kc.addPassword("bino", "sun-ds.sfbay", "test123".toCharArray()); + LOG.fine("pass = " + + kc.getPassword("bino", "sun-ds.sfbay")); + + LOG.fine("More testing :"); + for (int i = 0; i < 100; i++) { + kc.addPassword("" + i, "sun-ds.sfbay", ("" + i).toCharArray()); + } + for (int i = 0; i < 100; i++) { + LOG.fine("key =" + i + " pass =" + + kc.getPassword("" + i, "sun-ds.sfbay")); + } + kc.store(new FileOutputStream(file)); + } catch (Exception e) { + LOG.log(Level.WARNING, "", e); + } + } + +} diff --git a/src/main/java/org/jdesktop/swingx/auth/LoginAdapter.java b/src/main/java/org/jdesktop/swingx/auth/LoginAdapter.java new file mode 100644 index 0000000000..51449d423d --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/auth/LoginAdapter.java @@ -0,0 +1,48 @@ +/* + * $Id: LoginAdapter.java 603 2005-11-11 23:05:18Z rbair $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.auth; + +/** + * + * @author rbair + */ +public abstract class LoginAdapter implements LoginListener { + /** + * @inheritDoc + */ + public void loginSucceeded(LoginEvent source) {} + + /** + * @inheritDoc + */ + public void loginStarted(LoginEvent source) {} + + /** + * @inheritDoc + */ + public void loginFailed(LoginEvent source) {} + + /** + * @inheritDoc + */ + public void loginCanceled(LoginEvent source) {} +} diff --git a/src/main/java/org/jdesktop/swingx/auth/LoginEvent.java b/src/main/java/org/jdesktop/swingx/auth/LoginEvent.java new file mode 100644 index 0000000000..2057992a91 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/auth/LoginEvent.java @@ -0,0 +1,45 @@ +/* + * $Id: LoginEvent.java 648 2005-11-30 05:21:56Z rbair $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.auth; +import java.util.EventObject; + +/** + * This is an event object that is passed to login listener methods + * + * @author Shai Almog + */ +public class LoginEvent extends EventObject { + private Throwable cause; + + public LoginEvent(Object source) { + this(source, null); + } + + /** Creates a new instance of LoginEvent */ + public LoginEvent(Object source, Throwable cause) { + super(source); + this.cause = cause; + } + + public Throwable getCause() { + return cause; + } +} diff --git a/src/main/java/org/jdesktop/swingx/auth/LoginListener.java b/src/main/java/org/jdesktop/swingx/auth/LoginListener.java new file mode 100644 index 0000000000..d15603790a --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/auth/LoginListener.java @@ -0,0 +1,60 @@ +/* + * $Id: LoginListener.java 3707 2010-07-08 19:19:25Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.auth; + +import java.util.EventListener; + +/** + * LoginListener provides a listener for the actual login + * process. + * + * @author Bino George + * @author Shai Almog + */ +public interface LoginListener extends EventListener { + + /** + * Called by the JXLoginPane in the event of a login failure + * + * @param source panel that fired the event + */ + public void loginFailed(LoginEvent source); + /** + * Called by the JXLoginPane when the Authentication + * operation is started. + * @param source panel that fired the event + */ + public void loginStarted(LoginEvent source); + /** + * Called by the JXLoginPane in the event of a login + * cancellation by the user. + * + * @param source panel that fired the event + */ + public void loginCanceled(LoginEvent source); + /** + * Called by the JXLoginPane in the event of a + * successful login. + * + * @param source panel that fired the event + */ + public void loginSucceeded(LoginEvent source); +} diff --git a/src/main/java/org/jdesktop/swingx/auth/LoginService.java b/src/main/java/org/jdesktop/swingx/auth/LoginService.java new file mode 100644 index 0000000000..928bcb1a4d --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/auth/LoginService.java @@ -0,0 +1,290 @@ +/* + * $Id: LoginService.java 3661 2010-04-13 13:19:47Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.auth; + +import org.jdesktop.beans.AbstractBean; + +import javax.swing.*; +import javax.swing.event.EventListenerList; +import java.awt.*; +import java.util.logging.Logger; + +/** + * LoginService is the abstract base class for all classes implementing + * a login mechanism. It allows you to customize the threading behaviour used to + * perform the login. Subclasses need to override the authenticate + * method. Subclasses may implement the getUserRoles() method to return a + * meaningful value this method will be called once upon a successful login to + * determine the user roles. It is not defined as abstract to simplify the task + * of implementing a login service for those who do not require this + * functionality. + * + * @author Bino George + * @author Shai Almog + * @author Karl Schaefer + */ +public abstract class LoginService extends AbstractBean { + @SuppressWarnings("unused") + private Logger LOG = Logger.getLogger(LoginService.class.getName()); + + private EventListenerList listenerList = new EventListenerList(); + + private SwingWorker loginWorker; + + /* + * Controls the authentication behaviour to be either synchronous or + * asynchronous + */ + private boolean synchronous; + + private String server; + + public LoginService() { + } + + public LoginService(String server) { + setServer(server); + } + + /** + * This method is intended to be implemented by clients wishing to + * authenticate a user with a given password. Clients should implement the + * authentication in a manner that the authentication can be cancelled at + * any time. + * + * @param name + * username + * @param password + * password + * @param server + * server (optional) + * + * @return true on authentication success + * @throws Exception + */ + public abstract boolean authenticate(String name, char[] password, + String server) throws Exception; + + /** + * Called immediately after a successful authentication. This method should + * return an array of user roles or null if role based permissions are not + * used. + * + * @return per default null + */ + public String[] getUserRoles() { + return null; + } + + /** + * Notifies the LoginService that an already running authentication request + * should be cancelled. This method is intended to be used by clients who + * want to provide user with control over cancelling a long running + * authentication request. + */ + public void cancelAuthentication() { + if (loginWorker != null) { + loginWorker.cancel(true); + } + } + + /** + * This method starts the authentication process and is either synchronous + * or asynchronous based on the synchronous property + * + * @param user + * user + * @param password + * password + * @param server + * server + * @throws Exception + */ + public void startAuthentication(final String user, final char[] password, + final String server) throws Exception { + if (getSynchronous()) { + try { + if (authenticate(user, password, server)) { + fireLoginSucceeded(new LoginEvent(this)); + } else { + fireLoginFailed(new LoginEvent(this)); + } + } catch (Throwable e) { + fireLoginFailed(new LoginEvent(this, e)); + } + } else { + loginWorker = new SwingWorker() { + @Override + protected Boolean doInBackground() throws Exception { + try { + final boolean result = authenticate(user, password, + server); + if (isCancelled()) { + EventQueue.invokeLater(new Runnable() { + public void run() { + fireLoginCanceled(new LoginEvent(this)); + } + }); + return false; + } + EventQueue.invokeLater(new Runnable() { + public void run() { + if (result) { + fireLoginSucceeded(new LoginEvent( + LoginService.this)); + } else { + fireLoginFailed(new LoginEvent( + LoginService.this)); + } + } + }); + return result; + } catch (final Throwable failed) { + if (!isCancelled()) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + fireLoginFailed(new LoginEvent( + LoginService.this, failed)); + } + }); + } else { + EventQueue.invokeLater(new Runnable() { + public void run() { + fireLoginCanceled(new LoginEvent(this)); + } + }); + } + return false; + } + } + }; + loginWorker.execute(); + fireLoginStarted(new LoginEvent(this)); + } + } + + /** + * Get the synchronous property + * + * @return the synchronous property + */ + public boolean getSynchronous() { + return synchronous; + } + + /** + * Sets the synchronous property + * + * @param synchronous + * synchronous property + */ + public void setSynchronous(boolean synchronous) { + boolean old = getSynchronous(); + this.synchronous = synchronous; + firePropertyChange("synchronous", old, getSynchronous()); + } + + /** + * Adds a LoginListener to the list of listeners + * + * @param listener + * listener + */ + + public void addLoginListener(LoginListener listener) { + listenerList.add(LoginListener.class, listener); + } + + /** + * Removes a LoginListener from the list of listeners + * + * @param listener + * listener + */ + public void removeLoginListener(LoginListener listener) { + listenerList.remove(LoginListener.class, listener); + } + + void fireLoginStarted(final LoginEvent source) { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i] == LoginListener.class) { + ((LoginListener) listeners[i+1]).loginStarted(source); + } + } + } + + void fireLoginSucceeded(final LoginEvent source) { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i] == LoginListener.class) { + ((LoginListener) listeners[i+1]).loginSucceeded(source); + } + } + } + + void fireLoginFailed(final LoginEvent source) { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i] == LoginListener.class) { + ((LoginListener) listeners[i+1]).loginFailed(source); + } + } + } + + void fireLoginCanceled(final LoginEvent source) { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i] == LoginListener.class) { + ((LoginListener) listeners[i+1]).loginCanceled(source); + } + } + } + + /** + * @return Returns the server. + */ + public String getServer() { + return server; + } + + /** + * @param server + * The server to set. + */ + public void setServer(String server) { + String old = getServer(); + this.server = server; + firePropertyChange("server", old, getServer()); + } +} diff --git a/src/main/java/org/jdesktop/swingx/auth/PasswordStore.java b/src/main/java/org/jdesktop/swingx/auth/PasswordStore.java new file mode 100644 index 0000000000..425c72a25c --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/auth/PasswordStore.java @@ -0,0 +1,55 @@ +/* + * $Id: PasswordStore.java 3343 2009-05-25 00:33:12Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.auth; + +/** + * PasswordStore specifies a mechanism to store passwords used to authenticate + * using the LoginService. The actual mechanism used + * to store the passwords is left up to the implementation. + * + * @author Bino George + * @author Jonathan Giles + */ +public abstract class PasswordStore { + /** + * Saves a password for future use. + * + * @param username username used to authenticate. + * @param server server used for authentication + * @param password password to save. Password can't be null. Use empty array for empty password. + */ + public abstract boolean set(String username, String server, char[] password); + + /** + * Fetches the password for a given server and username. + * @param username username + * @param server server + * @return null if not found, a character array representing the password + * otherwise. Returned array can be empty if the password is empty. + */ + public abstract char[] get(String username, String server); + + /** + * This should attempt to remove the given username from the password store, as well as any associated password. + * @param username The username to remove + */ + public abstract void removeUserPassword(String username); +} diff --git a/src/main/java/org/jdesktop/swingx/auth/SimpleLoginService.java b/src/main/java/org/jdesktop/swingx/auth/SimpleLoginService.java new file mode 100644 index 0000000000..5e4d5c782f --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/auth/SimpleLoginService.java @@ -0,0 +1,60 @@ +/* + * $Id: SimpleLoginService.java 3475 2009-08-28 08:30:47Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.auth; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * An implementation of LoginService that simply matches + * the username/password against a list of known users and their passwords. + * This is useful for demos or prototypes where a proper login server is not available. + * + * This Implementation is NOT secure. DO NOT USE this in a real application + * To make this implementation more secure, the passwords should be passed in and + * stored as the result of a one way hash algorithm. That way an attacker cannot + * simply read the password in memory to crack into the system. + * + * @author rbair + */ +public final class SimpleLoginService extends LoginService { + private Map passwordMap; + + /** + * Creates a new SimpleLoginService based on the given password map. + */ + public SimpleLoginService(Map passwordMap) { + if (passwordMap == null) { + passwordMap = new HashMap(); + } + this.passwordMap = passwordMap; + } + + /** + * Attempts to authenticate the given username and password against the password map + */ + @Override + public boolean authenticate(String name, char[] password, String server) throws Exception { + char[] p = passwordMap.get(name); + return Arrays.equals(password, p); + } +} diff --git a/src/main/java/org/jdesktop/swingx/auth/UserNameStore.java b/src/main/java/org/jdesktop/swingx/auth/UserNameStore.java new file mode 100644 index 0000000000..c43f26f5d7 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/auth/UserNameStore.java @@ -0,0 +1,62 @@ +/* + * $Id: UserNameStore.java 1387 2006-09-11 22:37:44Z rbair $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.auth; + +import org.jdesktop.beans.AbstractBean; + +/** + * UsernameStore is a class that implements persistence of usernames + * + * @author Bino George + * @author rbair + */ +public abstract class UserNameStore extends AbstractBean { + /** + * Gets the current list of users. + */ + public abstract String[] getUserNames(); + /** + */ + public abstract void setUserNames(String[] names); + /** + * lifecycle method for loading names from persistent storage + */ + public abstract void loadUserNames(); + /** + * lifecycle method for saving name to persistent storage + */ + public abstract void saveUserNames(); + /** + */ + public abstract boolean containsUserName(String name); + + /** + * Add a username to the store. + * @param userName + */ + public abstract void addUserName(String userName); + + /** + * Removes a username from the list. + * @param userName + */ + public abstract void removeUserName(String userName); +} diff --git a/src/main/java/org/jdesktop/swingx/auth/UserPermissions.java b/src/main/java/org/jdesktop/swingx/auth/UserPermissions.java new file mode 100644 index 0000000000..03811536d9 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/auth/UserPermissions.java @@ -0,0 +1,126 @@ +/* + * $Id: UserPermissions.java 542 2005-10-10 18:03:15Z rbair $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.auth; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; + +/** + * This is a singleton that marks the set of permissions for a given logged in user. + * It is one of the optional results of a successful login operation. + * The purpose of this class is to provide a central location and client side bridge + * to the server side permissions and user roles (see J2EE role based authorization). + * This class is used by gui widgets and actions to determine visibility and enabled + * status and thus a UI can adapt itself to users with a lower set of privileges. + * + * This class is not meant as a secure barrier! It is only a thin layer to supplant the + * server side permissions. This class can be compromized by the user and thus its purpose + * is only to help UI flow and navigation and not to prevent attack against a client side + * UI. A server implementation must ALWAYS recheck permissions sent by the client regardless + * of the client. + * + * @author Shai Almog + */ +public class UserPermissions { + private static final UserPermissions INSTANCE = new UserPermissions(); + private PropertyChangeSupport propertyChange = new PropertyChangeSupport(this); + private String[] roles; + + /** Creates a new instance of UserPermissions */ + private UserPermissions() { + } + + public void addPropertyChangeListener(PropertyChangeListener listener) { + propertyChange.addPropertyChangeListener(listener); + } + + public void addPropertyChangeListener(String name, PropertyChangeListener listener) { + propertyChange.addPropertyChangeListener(name, listener); + } + + public void removePropertyChangeListener(PropertyChangeListener listener) { + propertyChange.removePropertyChangeListener(listener); + } + + public void removePropertyChangeListener(String name, PropertyChangeListener listener) { + propertyChange.removePropertyChangeListener(name, listener); + } + + /** + * Returns the singleton instance of this class. A singleton is used to simplify access for + * the permissions from every point in the application. + */ + public static UserPermissions getInstance() { + return INSTANCE; + } + + /** + * Returns the roles of the currently logged in user + */ + public String[] getRoles() { + return roles; + } + + /** + * Returns true if the user is in the given role (case sensitive). + */ + public boolean isUserInRole(String role) { + if(roles != null) { + for(int iter = 0 ; iter < roles.length ; iter++) { + if(roles[iter].equals(role)) { + return true; + } + } + } + return false; + } + + /** + * Returns true if the user is in one of the given roles (case sensitive). + */ + public boolean isUserInARole(String[] roles) { + for(int iter = 0 ; iter < roles.length ; iter++) { + if(isUserInRole(roles[iter])) { + return true; + } + } + return false; + } + + /** + * Returns true if the user is in all of the given roles (case sensitive). + */ + public boolean isUserInRoles(String[] roles) { + for(int iter = 0 ; iter < roles.length ; iter++) { + if(!isUserInRole(roles[iter])) { + return false; + } + } + return true; + } + + void setRoles(String[] roles) { + String[] oldValue = this.roles; + this.roles = roles; + propertyChange.firePropertyChange("roles", oldValue, roles); + } +} + diff --git a/src/main/java/org/jdesktop/swingx/auth/package-info.java b/src/main/java/org/jdesktop/swingx/auth/package-info.java new file mode 100644 index 0000000000..c55784938b --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/auth/package-info.java @@ -0,0 +1,25 @@ +/* + * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +/** + * Contains classes and interfaces used by the {@code JXLoginPane} component. + */ +package org.jdesktop.swingx.auth; + diff --git a/src/main/java/org/jdesktop/swingx/autocomplete/AbstractAutoCompleteAdaptor.java b/src/main/java/org/jdesktop/swingx/autocomplete/AbstractAutoCompleteAdaptor.java new file mode 100644 index 0000000000..8cd5ad5c5d --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/autocomplete/AbstractAutoCompleteAdaptor.java @@ -0,0 +1,118 @@ +/* + * $Id: AbstractAutoCompleteAdaptor.java 4045 2011-07-19 18:39:17Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.autocomplete; + +import javax.swing.text.JTextComponent; + +/** + * This is the interface that binds the mechanism for automatic completion to + * a data model, a selection model (e.g. those used by JList, JComboBox and JTable) + * and the JTextComponent for which the automatic completion should happen. + * It is used to search and select a matching item and to mark the completed text + * inside the JTextComponent. Using this interface the mechanism for automatic + * completion is independent from the underlying data and selection model. + * + * @see ComboBoxAdaptor + * @see ListAdaptor + * + * @author Thomas Bierhance + */ +public abstract class AbstractAutoCompleteAdaptor { + + /** the string representation in use for the currently selected item*/ + private String selectedItemAsString; + + /** + * Returns the currently selected item. + * @return the selected item + */ + public abstract Object getSelectedItem(); + + /** + * Sets the selected item. + * @param item the item that is to be selected + */ + public abstract void setSelectedItem(Object item); + + /** + * Returns the string representation in use for the currently selected item. + * @return the string representation in use for the currently selected item + */ + public String getSelectedItemAsString() { + return this.selectedItemAsString; + } + + /** + * Sets the string representation in use for the currently selected item. + * @param itemAsString the string representation in use for the currently selected item + */ + public void setSelectedItemAsString(String itemAsString) { + this.selectedItemAsString = itemAsString; + } + + /** + * Returns the number of items in the list. + * @return the number of items in the list + */ + public abstract int getItemCount(); + + /** + * Returns the item at a given index. It is supposed that 0<=index<getItemCount(). + * @param index the index of the item that is to be returned + * @return the item at the given index + */ + public abstract Object getItem(int index); + + /** + * Returns true if the list contains the currently selected item. + * @return true if the list contains the currently selected item. + */ + public boolean listContainsSelectedItem() { + Object selectedItem = getSelectedItem(); + for (int i=0,n=getItemCount(); istart. + * @param start index of the first character that should be marked + */ + public void markText(int start) { + getTextComponent().setCaretPosition(getTextComponent().getText().length()); + getTextComponent().moveCaretPosition(start); + } +} diff --git a/src/main/java/org/jdesktop/swingx/autocomplete/AutoComplete.java b/src/main/java/org/jdesktop/swingx/autocomplete/AutoComplete.java new file mode 100644 index 0000000000..9e85fd0207 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/autocomplete/AutoComplete.java @@ -0,0 +1,225 @@ +/* + * $Id: AutoComplete.java 4051 2011-07-19 20:17:05Z kschaefe $ + * + * Copyright 2010 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.autocomplete; + +import javax.swing.*; +import javax.swing.text.JTextComponent; +import java.awt.event.ActionEvent; +import java.awt.event.FocusEvent; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.beans.PropertyChangeEvent; + +import static org.jdesktop.swingx.autocomplete.AutoCompleteDecorator.*; + +/** + * + * @author kschaefer + */ +final class AutoComplete { + static class InputMap extends javax.swing.InputMap { + private static final long serialVersionUID = 1L; + } + + static class FocusAdapter extends java.awt.event.FocusAdapter { + private AbstractAutoCompleteAdaptor adaptor; + + public FocusAdapter(AbstractAutoCompleteAdaptor adaptor) { + this.adaptor = adaptor; + } + + @Override + public void focusGained(FocusEvent e) { + adaptor.markEntireText(); + } + } + + static class KeyAdapter extends java.awt.event.KeyAdapter { + private JComboBox comboBox; + + public KeyAdapter(JComboBox comboBox) { + this.comboBox = comboBox; + } + + @Override + public void keyPressed(KeyEvent keyEvent) { + // don't popup on action keys (cursor movements, etc...) + if (keyEvent.isActionKey()) { + return; + } + + // don't popup if the combobox isn't visible or empty anyway + if (comboBox.isDisplayable() && !comboBox.isPopupVisible() && comboBox.getModel().getSize() != 0) { + int keyCode = keyEvent.getKeyCode(); + // don't popup when the user hits shift,ctrl or alt + if (keyCode==KeyEvent.VK_SHIFT || keyCode==KeyEvent.VK_CONTROL || keyCode==KeyEvent.VK_ALT) return; + // don't popup when the user hits escape (see issue #311) + if (keyCode==KeyEvent.VK_ENTER || keyCode==KeyEvent.VK_ESCAPE) return; + comboBox.setPopupVisible(true); + } + } + } + + static class PropertyChangeListener implements java.beans.PropertyChangeListener { + private JComboBox comboBox; + + public PropertyChangeListener(JComboBox comboBox) { + this.comboBox = comboBox; + } + + /** + * {@inheritDoc} + */ + @Override + @SuppressWarnings("nls") + public void propertyChange(PropertyChangeEvent evt) { + if ("editor".equals(evt.getPropertyName())) { + handleEditor(evt); + } else if ("enabled".equals(evt.getPropertyName())) { + handleEnabled(evt); + } + } + + private void handleEnabled(PropertyChangeEvent evt) { + if (Boolean.TRUE.equals(evt.getNewValue())) { + comboBox.setEditable(true); + } else { + JTextComponent textComponent = (JTextComponent) comboBox.getEditor().getEditorComponent(); + boolean strictMatching = ((AutoCompleteDocument) textComponent.getDocument()).strictMatching; + + comboBox.setEditable(!strictMatching); + } + } + + private void handleEditor(PropertyChangeEvent evt) { + if (evt.getNewValue() instanceof AutoCompleteComboBoxEditor) { + return; + } + + AutoCompleteComboBoxEditor acEditor = (AutoCompleteComboBoxEditor) evt.getOldValue(); + boolean strictMatching = false; + + if (acEditor.getEditorComponent() != null) { + JTextComponent textComponent = (JTextComponent) acEditor.getEditorComponent(); + strictMatching = ((AutoCompleteDocument) textComponent.getDocument()).strictMatching; + + undecorate(textComponent); + + for (KeyListener l : textComponent.getKeyListeners()) { + if (l instanceof KeyAdapter) { + textComponent.removeKeyListener(l); + break; + } + } + } + + JTextComponent editorComponent = (JTextComponent) comboBox.getEditor().getEditorComponent(); + AbstractAutoCompleteAdaptor adaptor = new ComboBoxAdaptor(comboBox); + AutoCompleteDocument document = createAutoCompleteDocument(adaptor, strictMatching, + acEditor.stringConverter, editorComponent.getDocument()); + decorate(editorComponent, document, adaptor); + + editorComponent.addKeyListener(new KeyAdapter(comboBox)); + + //set before adding the listener for the editor + comboBox.setEditor(new AutoCompleteComboBoxEditor(comboBox.getEditor(), document.stringConverter)); + } + } + + static class SelectionAction implements Action { + private Action delegate; + + public SelectionAction(Action delegate) { + this.delegate = delegate; + } + + /** + * {@inheritDoc} + */ + @Override + public void actionPerformed(ActionEvent e) { + JComboBox comboBox = (JComboBox) e.getSource(); + JTextComponent textComponent = (JTextComponent) comboBox.getEditor().getEditorComponent(); + AutoCompleteDocument doc = (AutoCompleteDocument) textComponent.getDocument(); + + // doing this prevents the updating of the selected item to "" during the remove prior + // to the insert in JTextComponent.setText + doc.strictMatching = true; + try { + delegate.actionPerformed(e); + } finally { + doc.strictMatching = false; + } + } + + /** + * {@inheritDoc} + */ + @Override + public void addPropertyChangeListener(java.beans.PropertyChangeListener listener) { + delegate.addPropertyChangeListener(listener); + } + + /** + * {@inheritDoc} + */ + @Override + public void removePropertyChangeListener(java.beans.PropertyChangeListener listener) { + delegate.removePropertyChangeListener(listener); + } + + /** + * {@inheritDoc} + */ + @Override + public Object getValue(String key) { + return delegate.getValue(key); + } + + /** + * {@inheritDoc} + */ + @Override + public void putValue(String key, Object value) { + delegate.putValue(key, value); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isEnabled() { + return delegate.isEnabled(); + } + + /** + * {@inheritDoc} + */ + @Override + public void setEnabled(boolean b) { + delegate.setEnabled(b); + } + } + + private AutoComplete() { + // prevent instantiation + } +} diff --git a/src/main/java/org/jdesktop/swingx/autocomplete/AutoCompleteComboBoxEditor.java b/src/main/java/org/jdesktop/swingx/autocomplete/AutoCompleteComboBoxEditor.java new file mode 100644 index 0000000000..6ea656359f --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/autocomplete/AutoCompleteComboBoxEditor.java @@ -0,0 +1,122 @@ +/* + * $Id: AutoCompleteComboBoxEditor.java 4045 2011-07-19 18:39:17Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.autocomplete; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionListener; + +/** + *

      + * Wrapper around the combobox editor that translates combobox items into + * strings. The methods setItem and getItem are modified + * to account for the string conversion. + *

      + * This is necessary for those cases where the combobox items have no useful + * toString() method and a custom ObjectToStringConverter is + * used. + *

      + * If we do not do this, the interaction between ComboBoxEditor and JComboBox + * will result in firing ActionListener events with the string value of + * ComboBoxEditor as the currently selected value. + *

      + * @author Noel Grandin noelgrandin@gmail.com + * @author Thomas Bierhance + */ +public class AutoCompleteComboBoxEditor implements ComboBoxEditor { + + /** the original combo box editor*/ + final ComboBoxEditor wrapped; + /** the converter used to convert items into their string representation */ + final ObjectToStringConverter stringConverter; + /** last selected item */ + private Object oldItem; + + /** + * Creates a new AutoCompleteComboBoxEditor. + * + * @param wrapped the original ComboBoxEditor to be wrapped + * @param stringConverter the converter to use to convert items into their + * string representation. + */ + public AutoCompleteComboBoxEditor(ComboBoxEditor wrapped, ObjectToStringConverter stringConverter) { + this.wrapped = wrapped; + this.stringConverter = stringConverter; + } + + /* (non-javadoc) + * @see javax.swing.ComboBoxEditor#getEditorComponent() + */ + @Override + public Component getEditorComponent() { + return wrapped.getEditorComponent(); + } + + /* (non-javadoc) + * @see javax.swing.ComboBoxEditor#setItem(java.lang.Object) + */ + @Override + public void setItem(Object anObject) { + this.oldItem = anObject; + wrapped.setItem(stringConverter.getPreferredStringForItem(anObject)); + } + + /* (non-javadoc) + * @see javax.swing.ComboBoxEditor#getItem() + */ + @Override + public Object getItem() { + final Object wrappedItem = wrapped.getItem(); + + String[] oldAsStrings = stringConverter.getPossibleStringsForItem(oldItem); + for (int i=0, n=oldAsStrings.length; iUsage examples:

      + *

      
      + * JComboBox comboBox = [...];
      + * AutoCompleteDecorator.decorate(comboBox);
      + * 
      + * List items = [...];
      + * JTextField textField = [...];
      + * AutoCompleteDecorator.decorate(textField, items);
      + * 
      + * JList list = [...];
      + * JTextField textField = [...];
      + * AutoCompleteDecorator.decorate(list, textField);
      + * 

      + * + * @author Thomas Bierhance + * @author Karl Schaefer + */ +@SuppressWarnings({"nls", "serial"}) +public class AutoCompleteDecorator { + //these keys were pulled from BasicComboBoxUI from Sun JDK 1.6.0_20 + private static final List COMBO_BOX_ACTIONS = unmodifiableList(asList("selectNext", + "selectNext2", "selectPrevious", "selectPrevious2", "pageDownPassThrough", + "pageUpPassThrough", "homePassThrough", "endPassThrough")); + /** + * A TextAction that provides an error feedback for the text component that invoked + * the action. The error feedback is most likely a "beep". + */ + private static final Object errorFeedbackAction = new TextAction("provide-error-feedback") { + @Override + public void actionPerformed(ActionEvent e) { + UIManager.getLookAndFeel().provideErrorFeedback(getTextComponent(e)); + } + }; + + private AutoCompleteDecorator() { + //prevents instantiation + } + + private static void installMap(InputMap componentMap, boolean strict) { + InputMap map = new AutoComplete.InputMap(); + + if (strict) { + map.put(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_BACK_SPACE, 0), DefaultEditorKit.selectionBackwardAction); + // ignore VK_DELETE and CTRL+VK_X and beep instead when strict matching + map.put(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_DELETE, 0), errorFeedbackAction); + map.put(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_X, java.awt.event.InputEvent.CTRL_DOWN_MASK), errorFeedbackAction); + } else { + // VK_BACKSPACE will move the selection to the left if the selected item is in the list + // it will delete the previous character otherwise + map.put(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_BACK_SPACE, 0), "nonstrict-backspace"); + // leave VK_DELETE and CTRL+VK_X as is + } + + map.setParent(componentMap.getParent()); + componentMap.setParent(map); + } + + static AutoCompleteDocument createAutoCompleteDocument( + AbstractAutoCompleteAdaptor adaptor, boolean strictMatching, + ObjectToStringConverter stringConverter, Document delegate) { + if (delegate instanceof StyledDocument) { + return new AutoCompleteStyledDocument(adaptor, strictMatching, + stringConverter, (StyledDocument) delegate); + } + + return new AutoCompleteDocument(adaptor, strictMatching, + stringConverter, delegate); + } + + /** + * Enables automatic completion for the given JComboBox. The automatic + * completion will be strict (only items from the combo box can be selected) + * if the combo box is not editable. + * @param comboBox a combo box + * @see #decorate(JComboBox, ObjectToStringConverter) + */ + public static void decorate(JComboBox comboBox) { + decorate(comboBox, null); + } + + /** + * Enables automatic completion for the given JComboBox. The automatic + * completion will be strict (only items from the combo box can be selected) + * if the combo box is not editable. + *

      + * Note: the {@code AutoCompleteDecorator} will alter the state of + * the {@code JComboBox} to be editable. This can cause side effects with + * layouts and sizing. {@code JComboBox} caches the size, which differs + * depending on the component's editability. Therefore, if the component's + * size is accessed prior to being decorated and then the cached size is + * forced to be recalculated, the size of the component will change. + *

      + * Because the size of the component can be altered (recalculated), the + * decorator does not attempt to set any sizes on the supplied + * {@code JComboBox}. Users that need to ensure sizes of supplied combos + * should take measures to set the size of the combo. + * + * @param comboBox + * a combo box + * @param stringConverter + * the converter used to transform items to strings + */ + public static void decorate(JComboBox comboBox, ObjectToStringConverter stringConverter) { + undecorate(comboBox); + + boolean strictMatching = !comboBox.isEditable(); + // has to be editable + comboBox.setEditable(true); + // fix the popup location + MacOSXPopupLocationFix.install(comboBox); + + // configure the text component=editor component + JTextComponent editorComponent = (JTextComponent) comboBox.getEditor().getEditorComponent(); + final AbstractAutoCompleteAdaptor adaptor = new ComboBoxAdaptor(comboBox); + final AutoCompleteDocument document = createAutoCompleteDocument(adaptor, strictMatching, + stringConverter, editorComponent.getDocument()); + decorate(editorComponent, document, adaptor); + + editorComponent.addKeyListener(new AutoComplete.KeyAdapter(comboBox)); + + //set before adding the listener for the editor + comboBox.setEditor(new AutoCompleteComboBoxEditor(comboBox.getEditor(), document.stringConverter)); + + // Changing the l&f can change the combobox' editor which in turn + // would not be autocompletion-enabled. The new editor needs to be set-up. + AutoComplete.PropertyChangeListener pcl = new AutoComplete.PropertyChangeListener(comboBox); + comboBox.addPropertyChangeListener("editor", pcl); + comboBox.addPropertyChangeListener("enabled", pcl); + + if (!strictMatching) { + ActionMap map = comboBox.getActionMap(); + + for (String key : COMBO_BOX_ACTIONS) { + Action a = map.get(key); + map.put(key, new AutoComplete.SelectionAction(a)); + } + } + } + + static void undecorate(JComboBox comboBox) { + JTextComponent editorComponent = (JTextComponent) comboBox.getEditor().getEditorComponent(); + + if (editorComponent.getDocument() instanceof AutoCompleteDocument) { + AutoCompleteDocument doc = (AutoCompleteDocument) editorComponent.getDocument(); + + if (doc.strictMatching) { + ActionMap map = comboBox.getActionMap(); + + for (String key : COMBO_BOX_ACTIONS) { + map.put(key, null); + } + } + + //remove old property change listener + for (PropertyChangeListener l : comboBox.getPropertyChangeListeners("editor")) { + if (l instanceof AutoComplete.PropertyChangeListener) { + comboBox.removePropertyChangeListener("editor", l); + } + } + + for (PropertyChangeListener l : comboBox.getPropertyChangeListeners("enabled")) { + if (l instanceof AutoComplete.PropertyChangeListener) { + comboBox.removePropertyChangeListener("enabled", l); + } + } + + AutoCompleteComboBoxEditor editor = (AutoCompleteComboBoxEditor) comboBox.getEditor(); + comboBox.setEditor(editor.wrapped); + + //remove old key listener + for (KeyListener l : editorComponent.getKeyListeners()) { + if (l instanceof AutoComplete.KeyAdapter) { + editorComponent.removeKeyListener(l); + break; + } + } + + undecorate(editorComponent); + + for (ActionListener l : comboBox.getActionListeners()) { + if (l instanceof ComboBoxAdaptor) { + comboBox.removeActionListener(l); + break; + } + } + + //TODO remove aqua fix + + //TODO reset editibility + } + } + + /** + * Enables automatic completion for the given JTextComponent based on the + * items contained in the given JList. The two components will be + * synchronized. The automatic completion will always be strict. + * @param list a JList containing the items for automatic completion + * @param textComponent the text component that will be enabled for automatic + * completion + */ + public static void decorate(JList list, JTextComponent textComponent) { + decorate(list, textComponent, null); + } + + /** + * Enables automatic completion for the given JTextComponent based on the + * items contained in the given JList. The two components will be + * synchronized. The automatic completion will always be strict. + * @param list a JList containing the items for automatic completion + * @param textComponent the text component that will be used for automatic + * completion + * @param stringConverter the converter used to transform items to strings + */ + public static void decorate(JList list, JTextComponent textComponent, ObjectToStringConverter stringConverter) { + undecorate(list); + + AbstractAutoCompleteAdaptor adaptor = new ListAdaptor(list, textComponent, stringConverter); + AutoCompleteDocument document = createAutoCompleteDocument(adaptor, true, stringConverter, textComponent.getDocument()); + decorate(textComponent, document, adaptor); + } + + static void undecorate(JList list) { + for (ListSelectionListener l : list.getListSelectionListeners()) { + if (l instanceof ListAdaptor) { + list.removeListSelectionListener(l); + break; + } + } + } + + /** + * Enables automatic completion for the given JTextComponent based on the + * items contained in the given List. + * @param textComponent the text component that will be used for automatic + * completion. + * @param items contains the items that are used for autocompletion + * @param strictMatching true, if only given items should be allowed to be entered + */ + public static void decorate(JTextComponent textComponent, List items, boolean strictMatching) { + decorate(textComponent, items, strictMatching, null); + } + + /** + * Enables automatic completion for the given JTextComponent based on the + * items contained in the given List. + * @param items contains the items that are used for autocompletion + * @param textComponent the text component that will be used for automatic + * completion. + * @param strictMatching true, if only given items should be allowed to be entered + * @param stringConverter the converter used to transform items to strings + */ + public static void decorate(JTextComponent textComponent, List items, boolean strictMatching, ObjectToStringConverter stringConverter) { + AbstractAutoCompleteAdaptor adaptor = new TextComponentAdaptor(textComponent, items); + AutoCompleteDocument document = createAutoCompleteDocument(adaptor, strictMatching, stringConverter, textComponent.getDocument()); + decorate(textComponent, document, adaptor); + } + + /** + * Decorates a given text component for automatic completion using the + * given AutoCompleteDocument and AbstractAutoCompleteAdaptor. + * + * @param textComponent a text component that should be decorated + * @param document the AutoCompleteDocument to be installed on the text component + * @param adaptor the AbstractAutoCompleteAdaptor to be used + */ + public static void decorate(JTextComponent textComponent, AutoCompleteDocument document, AbstractAutoCompleteAdaptor adaptor) { + undecorate(textComponent); + + // install the document on the text component + textComponent.setDocument(document); + + // mark entire text when the text component gains focus + // otherwise the last mark would have been retained which is quiet confusing + textComponent.addFocusListener(new AutoComplete.FocusAdapter(adaptor)); + + // Tweak some key bindings + InputMap editorInputMap = textComponent.getInputMap(); + + while (editorInputMap != null) { + InputMap parent = editorInputMap.getParent(); + + if (parent instanceof UIResource) { + installMap(editorInputMap, document.isStrictMatching()); + break; + } + + editorInputMap = parent; + } + + ActionMap editorActionMap = textComponent.getActionMap(); + editorActionMap.put("nonstrict-backspace", new NonStrictBackspaceAction( + editorActionMap.get(DefaultEditorKit.deletePrevCharAction), + editorActionMap.get(DefaultEditorKit.selectionBackwardAction), + adaptor)); + } + + static void undecorate(JTextComponent textComponent) { + Document doc = textComponent.getDocument(); + + if (doc instanceof AutoCompleteDocument) { + //remove autocomplete key/action mappings + InputMap map = textComponent.getInputMap(); + + while (map.getParent() != null) { + InputMap parent = map.getParent(); + + if (parent instanceof AutoComplete.InputMap) { + map.setParent(parent.getParent()); + } + + map = parent; + } + + textComponent.getActionMap().put("nonstrict-backspace", null); + + //remove old focus listener + for (FocusListener l : textComponent.getFocusListeners()) { + if (l instanceof AutoComplete.FocusAdapter) { + textComponent.removeFocusListener(l); + break; + } + } + + //reset to original document + textComponent.setDocument(((AutoCompleteDocument) doc).delegate); + } + } + + static class NonStrictBackspaceAction extends TextAction { + Action backspace; + Action selectionBackward; + AbstractAutoCompleteAdaptor adaptor; + + public NonStrictBackspaceAction(Action backspace, Action selectionBackward, AbstractAutoCompleteAdaptor adaptor) { + super("nonstrict-backspace"); + this.backspace = backspace; + this.selectionBackward = selectionBackward; + this.adaptor = adaptor; + } + + @Override + public void actionPerformed(ActionEvent e) { + if (adaptor.listContainsSelectedItem()) { + selectionBackward.actionPerformed(e); + } else { + backspace.actionPerformed(e); + } + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/autocomplete/AutoCompleteDocument.java b/src/main/java/org/jdesktop/swingx/autocomplete/AutoCompleteDocument.java new file mode 100644 index 0000000000..e33a7524b7 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/autocomplete/AutoCompleteDocument.java @@ -0,0 +1,547 @@ +/* + * $Id: AutoCompleteDocument.java 4051 2011-07-19 20:17:05Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.autocomplete; + +import org.jdesktop.swingx.util.Contract; + +import javax.swing.*; +import javax.swing.event.*; +import javax.swing.text.*; +import java.util.Comparator; + +import static org.jdesktop.swingx.autocomplete.ObjectToStringConverter.DEFAULT_IMPLEMENTATION; + +/** + * A document that can be plugged into any JTextComponent to enable automatic completion. + * It finds and selects matching items using any implementation of the AbstractAutoCompleteAdaptor. + */ +@SuppressWarnings("nls") +public class AutoCompleteDocument implements Document { + private class Handler implements DocumentListener, UndoableEditListener { + private final EventListenerList listenerList = new EventListenerList(); + + public void addDocumentListener(DocumentListener listener) { + listenerList.add(DocumentListener.class, listener); + } + + public void addUndoableEditListener(UndoableEditListener listener) { + listenerList.add(UndoableEditListener.class, listener); + } + + /** + * {@inheritDoc} + */ + public void removeDocumentListener(DocumentListener listener) { + listenerList.remove(DocumentListener.class, listener); + } + + /** + * {@inheritDoc} + */ + public void removeUndoableEditListener(UndoableEditListener listener) { + listenerList.remove(UndoableEditListener.class, listener); + } + + /** + * {@inheritDoc} + */ + @Override + public void changedUpdate(DocumentEvent e) { + e = new DelegatingDocumentEvent(AutoCompleteDocument.this, e); + + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==DocumentListener.class) { + // Lazily create the event: + // if (e == null) + // e = new ListSelectionEvent(this, firstIndex, lastIndex); + ((DocumentListener)listeners[i+1]).changedUpdate(e); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public void insertUpdate(DocumentEvent e) { + e = new DelegatingDocumentEvent(AutoCompleteDocument.this, e); + + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==DocumentListener.class) { + // Lazily create the event: + // if (e == null) + // e = new ListSelectionEvent(this, firstIndex, lastIndex); + ((DocumentListener)listeners[i+1]).insertUpdate(e); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public void removeUpdate(DocumentEvent e) { + e = new DelegatingDocumentEvent(AutoCompleteDocument.this, e); + + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==DocumentListener.class) { + // Lazily create the event: + // if (e == null) + // e = new ListSelectionEvent(this, firstIndex, lastIndex); + ((DocumentListener)listeners[i+1]).removeUpdate(e); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public void undoableEditHappened(UndoableEditEvent e) { + e = new UndoableEditEvent(AutoCompleteDocument.this, e.getEdit()); + + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==UndoableEditListener.class) { + // Lazily create the event: + // if (e == null) + // e = new ListSelectionEvent(this, firstIndex, lastIndex); + ((UndoableEditListener)listeners[i+1]).undoableEditHappened(e); + } + } + } + } + + /** + * true, if only items from the adaptors's list can be entered + * false, otherwise (selected item might not be in the adaptors's list) + */ + protected boolean strictMatching; + + protected final Document delegate; + + /** Flag to indicate if adaptor.setSelectedItem has been called. + * Subsequent calls to remove/insertString should be ignored + * as they are likely have been caused by the adapted Component that + * is trying to set the text for the selected component.*/ + boolean selecting = false; + + /** + * The adaptor that is used to find and select items. + */ + AbstractAutoCompleteAdaptor adaptor; + + ObjectToStringConverter stringConverter; + + private final Handler handler; + + // Note: these comparators do not impose any ordering - e.g. they do not ensure that sgn(compare(x, y)) == -sgn(compare(y, x)) + private static final Comparator EQUALS_IGNORE_CASE = new Comparator() { + @Override + public int compare(String o1, String o2) { + return o1.equalsIgnoreCase(o2) ? 0 : -1; + } + }; + + private static final Comparator STARTS_WITH_IGNORE_CASE = new Comparator() { + @Override + public int compare(String o1, String o2) { + if (o1.length() < o2.length()) return -1; + return o1.regionMatches(true, 0, o2, 0, o2.length()) ? 0 : -1; + } + }; + + private static final Comparator EQUALS = new Comparator() { + @Override + public int compare(String o1, String o2) { + return o1.equals(o2) ? 0 : -1; + } + }; + + private static final Comparator STARTS_WITH = new Comparator() { + @Override + public int compare(String o1, String o2) { + return o1.startsWith(o2) ? 0 : -1; + } + }; + + /** + * Creates a new AutoCompleteDocument for the given AbstractAutoCompleteAdaptor. + * @param adaptor The adaptor that will be used to find and select matching + * items. + * @param strictMatching true, if only items from the adaptor's list should + * be allowed to be entered + * @param stringConverter the converter used to transform items to strings + * @param delegate the {@code Document} delegate backing this document + */ + public AutoCompleteDocument(AbstractAutoCompleteAdaptor adaptor, boolean strictMatching, + ObjectToStringConverter stringConverter, Document delegate) { + this.adaptor = Contract.asNotNull(adaptor, "adaptor cannot be null"); + this.strictMatching = strictMatching; + this.stringConverter = stringConverter == null ? DEFAULT_IMPLEMENTATION : stringConverter; + this.delegate = delegate == null ? createDefaultDocument() : delegate; + + handler = new Handler(); + this.delegate.addDocumentListener(handler); + + // Handle initially selected object + Object selected = adaptor.getSelectedItem(); + if (selected != null) { + String itemAsString = this.stringConverter.getPreferredStringForItem(selected); + setText(itemAsString); + adaptor.setSelectedItemAsString(itemAsString); + } + this.adaptor.markEntireText(); + } + + + /** + * Creates a new AutoCompleteDocument for the given AbstractAutoCompleteAdaptor. + * @param adaptor The adaptor that will be used to find and select matching + * items. + * @param strictMatching true, if only items from the adaptor's list should + * be allowed to be entered + * @param stringConverter the converter used to transform items to strings + */ + public AutoCompleteDocument(AbstractAutoCompleteAdaptor adaptor, boolean strictMatching, ObjectToStringConverter stringConverter) { + this(adaptor, strictMatching, stringConverter, null); + } + + /** + * Creates a new AutoCompleteDocument for the given AbstractAutoCompleteAdaptor. + * @param strictMatching true, if only items from the adaptor's list should + * be allowed to be entered + * @param adaptor The adaptor that will be used to find and select matching + * items. + */ + public AutoCompleteDocument(AbstractAutoCompleteAdaptor adaptor, boolean strictMatching) { + this(adaptor, strictMatching, null); + } + + /** + * Creates the default backing document when no delegate is passed to this + * document. + * + * @return the default backing document + */ + protected Document createDefaultDocument() { + return new PlainDocument(); + } + + @Override + public void remove(int offs, int len) throws BadLocationException { + // return immediately when selecting an item + if (selecting) return; + delegate.remove(offs, len); + if (!strictMatching) { + setSelectedItem(getText(0, getLength()), getText(0, getLength())); + adaptor.getTextComponent().setCaretPosition(offs); + } + } + + @Override + public void insertString(int offs, String str, AttributeSet a) throws BadLocationException { + // return immediately when selecting an item + if (selecting) return; + // insert the string into the document + delegate.insertString(offs, str, a); + // lookup and select a matching item + LookupResult lookupResult; + String pattern = getText(0, getLength()); + + if(pattern == null || pattern.length() == 0) { + lookupResult = new LookupResult(null, ""); + setSelectedItem(lookupResult.matchingItem, lookupResult.matchingString); + } else { + lookupResult = lookupItem(pattern); + } + + if (lookupResult.matchingItem != null) { + setSelectedItem(lookupResult.matchingItem, lookupResult.matchingString); + } else { + if (strictMatching) { + // keep old item selected if there is no match + lookupResult.matchingItem = adaptor.getSelectedItem(); + lookupResult.matchingString = adaptor.getSelectedItemAsString(); + // imitate no insert (later on offs will be incremented by + // str.length(): selection won't move forward) + offs = str == null ? offs : offs - str.length(); + + if (str != null && !str.isEmpty()) { + // provide feedback to the user that his input has been received but can not be accepted + UIManager.getLookAndFeel().provideErrorFeedback(adaptor.getTextComponent()); + } + } else { + // no item matches => use the current input as selected item + lookupResult.matchingItem=getText(0, getLength()); + lookupResult.matchingString=getText(0, getLength()); + setSelectedItem(lookupResult.matchingItem, lookupResult.matchingString); + } + } + + setText(lookupResult.matchingString); + + // select the completed part + int len = str == null ? 0 : str.length(); + offs = lookupResult.matchingString == null ? 0 : offs + len; + adaptor.markText(offs); + } + + /** + * Sets the text of this AutoCompleteDocument to the given text. + * + * @param text the text that will be set for this document + */ + private void setText(String text) { + try { + // remove all text and insert the completed string + delegate.remove(0, getLength()); + delegate.insertString(0, text, null); + } catch (BadLocationException e) { + throw new RuntimeException(e.toString()); + } + } + + /** + * Selects the given item using the AbstractAutoCompleteAdaptor. + * @param itemAsString string representation of the item to be selected + * @param item the item that is to be selected + */ + private void setSelectedItem(Object item, String itemAsString) { + selecting = true; + adaptor.setSelectedItem(item); + adaptor.setSelectedItemAsString(itemAsString); + selecting = false; + } + + /** + * Searches for an item that matches the given pattern. The AbstractAutoCompleteAdaptor + * is used to access the candidate items. The match is not case-sensitive + * and will only match at the beginning of each item's string representation. + * + * @param pattern the pattern that should be matched + * @return the first item that matches the pattern or null if no item matches + */ + private LookupResult lookupItem(String pattern) { + Object selectedItem = adaptor.getSelectedItem(); + + LookupResult lookupResult; + + // first try: case sensitive + + lookupResult = lookupItem(pattern, EQUALS); + if (lookupResult != null) return lookupResult; + + lookupResult = lookupOneItem(selectedItem, pattern, STARTS_WITH); + if (lookupResult != null) return lookupResult; + + lookupResult = lookupItem(pattern, STARTS_WITH); + if (lookupResult != null) return lookupResult; + + // second try: ignore case + + lookupResult = lookupItem(pattern, EQUALS_IGNORE_CASE); + if (lookupResult != null) return lookupResult; + + lookupResult = lookupOneItem(selectedItem, pattern, STARTS_WITH_IGNORE_CASE); + if (lookupResult != null) return lookupResult; + + lookupResult = lookupItem(pattern, STARTS_WITH_IGNORE_CASE); + if (lookupResult != null) return lookupResult; + + // no item starts with the pattern => return null + return new LookupResult(null, ""); + } + + private LookupResult lookupOneItem(Object item, String pattern, Comparator comparator) { + String[] possibleStrings = stringConverter.getPossibleStringsForItem(item); + if (possibleStrings != null) { + for (int j = 0; j < possibleStrings.length; j++) { + if (comparator.compare(possibleStrings[j], pattern) == 0) { + return new LookupResult(item, possibleStrings[j]); + } + } + } + return null; + } + + private LookupResult lookupItem(String pattern, Comparator comparator) { + // iterate over all items and return first match + for (int i = 0, n = adaptor.getItemCount(); i < n; i++) { + Object currentItem = adaptor.getItem(i); + LookupResult result = lookupOneItem(currentItem, pattern, comparator); + if (result != null) return result; + } + return null; + } + + private static class LookupResult { + Object matchingItem; + String matchingString; + public LookupResult(Object matchingItem, String matchingString) { + this.matchingItem = matchingItem; + this.matchingString = matchingString; + } + } + + /** + * {@inheritDoc} + */ + @Override + public void addDocumentListener(DocumentListener listener) { + handler.addDocumentListener(listener); + } + + /** + * {@inheritDoc} + */ + @Override + public void addUndoableEditListener(UndoableEditListener listener) { + handler.addUndoableEditListener(listener); + } + + /** + * {@inheritDoc} + */ + @Override + public Position createPosition(int offs) throws BadLocationException { + return delegate.createPosition(offs); + } + + /** + * {@inheritDoc} + */ + @Override + public Element getDefaultRootElement() { + return delegate.getDefaultRootElement(); + } + + /** + * {@inheritDoc} + */ + @Override + public Position getEndPosition() { + return delegate.getEndPosition(); + } + + /** + * {@inheritDoc} + */ + @Override + public int getLength() { + return delegate.getLength(); + } + + /** + * {@inheritDoc} + */ + @Override + public Object getProperty(Object key) { + return delegate.getProperty(key); + } + + /** + * {@inheritDoc} + */ + @Override + public Element[] getRootElements() { + return delegate.getRootElements(); + } + + /** + * {@inheritDoc} + */ + @Override + public Position getStartPosition() { + return delegate.getStartPosition(); + } + + /** + * {@inheritDoc} + */ + @Override + public String getText(int offset, int length) throws BadLocationException { + return delegate.getText(offset, length); + } + + /** + * {@inheritDoc} + */ + @Override + public void getText(int offset, int length, Segment txt) throws BadLocationException { + delegate.getText(offset, length, txt); + } + + /** + * {@inheritDoc} + */ + @Override + public void putProperty(Object key, Object value) { + delegate.putProperty(key, value); + } + + /** + * {@inheritDoc} + */ + @Override + public void removeDocumentListener(DocumentListener listener) { + handler.removeDocumentListener(listener); + } + + /** + * {@inheritDoc} + */ + @Override + public void removeUndoableEditListener(UndoableEditListener listener) { + handler.removeUndoableEditListener(listener); + } + + /** + * {@inheritDoc} + */ + @Override + public void render(Runnable r) { + delegate.render(r); + } + + /** + * Returns if only items from the adaptor's list should be allowed to be entered. + * @return if only items from the adaptor's list should be allowed to be entered + */ + public boolean isStrictMatching() { + return strictMatching; + } +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/autocomplete/AutoCompleteStyledDocument.java b/src/main/java/org/jdesktop/swingx/autocomplete/AutoCompleteStyledDocument.java new file mode 100644 index 0000000000..d8ea38d593 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/autocomplete/AutoCompleteStyledDocument.java @@ -0,0 +1,168 @@ +/* + * $Id: AutoCompleteStyledDocument.java 4045 2011-07-19 18:39:17Z kschaefe $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.autocomplete; + +import javax.swing.text.*; +import java.awt.*; + +/** + * + * @author Karl George Schaefer + */ +public class AutoCompleteStyledDocument extends AutoCompleteDocument implements + StyledDocument { + /** + * @param adaptor + * @param strictMatching + * @param stringConverter + * @param delegate + */ + public AutoCompleteStyledDocument(AbstractAutoCompleteAdaptor adaptor, + boolean strictMatching, ObjectToStringConverter stringConverter, + StyledDocument delegate) { + super(adaptor, strictMatching, stringConverter, delegate); + } + + /** + * @param adaptor + * @param strictMatching + * @param stringConverter + */ + public AutoCompleteStyledDocument(AbstractAutoCompleteAdaptor adaptor, + boolean strictMatching, ObjectToStringConverter stringConverter) { + super(adaptor, strictMatching, stringConverter); + } + + /** + * @param adaptor + * @param strictMatching + */ + public AutoCompleteStyledDocument(AbstractAutoCompleteAdaptor adaptor, + boolean strictMatching) { + super(adaptor, strictMatching); + } + + /** + * {@inheritDoc} + */ + @Override + protected Document createDefaultDocument() { + return new DefaultStyledDocument(); + } + + /** + * {@inheritDoc} + */ + @Override + public Style addStyle(String nm, Style parent) { + return ((StyledDocument) delegate).addStyle(nm, parent); + } + + /** + * {@inheritDoc} + */ + @Override + public Color getBackground(AttributeSet attr) { + return ((StyledDocument) delegate).getBackground(attr); + } + + /** + * {@inheritDoc} + */ + @Override + public Element getCharacterElement(int pos) { + return ((StyledDocument) delegate).getCharacterElement(pos); + } + + /** + * {@inheritDoc} + */ + @Override + public Font getFont(AttributeSet attr) { + return ((StyledDocument) delegate).getFont(attr); + } + + /** + * {@inheritDoc} + */ + @Override + public Color getForeground(AttributeSet attr) { + return ((StyledDocument) delegate).getForeground(attr); + } + + /** + * {@inheritDoc} + */ + @Override + public Style getLogicalStyle(int p) { + return ((StyledDocument) delegate).getLogicalStyle(p); + } + + /** + * {@inheritDoc} + */ + @Override + public Element getParagraphElement(int pos) { + return ((StyledDocument) delegate).getParagraphElement(pos); + } + + /** + * {@inheritDoc} + */ + @Override + public Style getStyle(String nm) { + return ((StyledDocument) delegate).getStyle(nm); + } + + /** + * {@inheritDoc} + */ + @Override + public void removeStyle(String nm) { + ((StyledDocument) delegate).removeStyle(nm); + } + + /** + * {@inheritDoc} + */ + @Override + public void setCharacterAttributes(int offset, int length, + AttributeSet s, boolean replace) { + ((StyledDocument) delegate).setCharacterAttributes(offset, length, s, replace); + } + + /** + * {@inheritDoc} + */ + @Override + public void setLogicalStyle(int pos, Style s) { + ((StyledDocument) delegate).setLogicalStyle(pos, s); + } + + /** + * {@inheritDoc} + */ + @Override + public void setParagraphAttributes(int offset, int length, + AttributeSet s, boolean replace) { + ((StyledDocument) delegate).setParagraphAttributes(offset, length, s, replace); + } +} diff --git a/src/main/java/org/jdesktop/swingx/autocomplete/ComboBoxAdaptor.java b/src/main/java/org/jdesktop/swingx/autocomplete/ComboBoxAdaptor.java new file mode 100644 index 0000000000..b285aab238 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/autocomplete/ComboBoxAdaptor.java @@ -0,0 +1,117 @@ +/* + * $Id: ComboBoxAdaptor.java 4051 2011-07-19 20:17:05Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.autocomplete; + +import javax.accessibility.Accessible; +import javax.swing.*; +import javax.swing.plaf.basic.ComboPopup; +import javax.swing.text.JTextComponent; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/** + * An implementation of the AbstractAutoCompleteAdaptor that is suitable for JComboBox. + * + * @author Thomas Bierhance + * @author Karl Schaefer + */ +@SuppressWarnings("nls") +public class ComboBoxAdaptor extends AbstractAutoCompleteAdaptor implements ActionListener { + + /** the combobox being adapted */ + private JComboBox comboBox; + + /** + * Creates a new ComobBoxAdaptor for the given combobox. + * @param comboBox the combobox that should be adapted + */ + public ComboBoxAdaptor(JComboBox comboBox) { + this.comboBox = comboBox; + // mark the entire text when a new item is selected + comboBox.addActionListener(this); + } + + /** + * Implementation side effect - do not invoke. + * @param actionEvent - + */ + // ActionListener (listening to comboBox) + @Override + public void actionPerformed(ActionEvent actionEvent) { + markEntireText(); + } + + @Override + public int getItemCount() { + return comboBox.getItemCount(); + } + + @Override + public Object getItem(int index) { + return comboBox.getItemAt(index); + } + + @Override + public void setSelectedItem(Object item) { + //SwingX 834: avoid moving when already selected + if (item == getSelectedItem()) { + return; + } + + // kgs - back door our way to finding the JList that displays the data. + // then we ask the list to scroll until the last cell is visible. this + // will cause the selected item to appear closest to the top. + // + // it is unknown whether this functionality will work outside of Sun's + // implementation, but the code is safe and will "fail gracefully" on + // other systems + Accessible a = comboBox.getUI().getAccessibleChild(comboBox, 0); + + if (getItemCount() > 0 && a instanceof ComboPopup) { + JList list = ((ComboPopup) a).getList(); + int lastIndex = list.getModel().getSize() - 1; + + Rectangle rect = list.getCellBounds(lastIndex, lastIndex); + + if (rect == null) { + throw new IllegalStateException( + "attempting to access index " + lastIndex + " for " + comboBox); + } + + list.scrollRectToVisible(rect); + } + + //setting the selected item should scroll it into the visible region + comboBox.setSelectedItem(item); + } + + @Override + public Object getSelectedItem() { + return comboBox.getModel().getSelectedItem(); + } + + @Override + public JTextComponent getTextComponent() { + // returning the component of the combobox's editor + return (JTextComponent) comboBox.getEditor().getEditorComponent(); + } +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/autocomplete/ComboBoxCellEditor.java b/src/main/java/org/jdesktop/swingx/autocomplete/ComboBoxCellEditor.java new file mode 100644 index 0000000000..19eb6e508b --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/autocomplete/ComboBoxCellEditor.java @@ -0,0 +1,110 @@ +/* + * $Id: ComboBoxCellEditor.java 4051 2011-07-19 20:17:05Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.autocomplete; + +import javax.swing.*; +import javax.swing.text.JTextComponent; +import java.awt.event.ActionEvent; +import java.awt.event.MouseEvent; +import java.util.EventObject; + +/** + *

      This is a cell editor that can be used when a combo box (that has been set + * up for automatic completion) is to be used in a JTable. The + * {@link DefaultCellEditor DefaultCellEditor} won't work in this + * case, because each time an item gets selected it stops cell editing and hides + * the combo box. + *

      + *

      + * Usage example: + *

      + *

      + *

      
      + * JTable table = ...;
      + * JComboBox comboBox = ...;
      + * ...
      + * TableColumn column = table.getColumnModel().getColumn(0);
      + * column.setCellEditor(new ComboBoxCellEditor(comboBox));
      + * 
      + *

      + */ +public class ComboBoxCellEditor extends DefaultCellEditor { + + /** + * Creates a new ComboBoxCellEditor. + * @param comboBox the comboBox that should be used as the cell editor. + */ + public ComboBoxCellEditor(final JComboBox comboBox) { + super(comboBox); + + comboBox.removeActionListener(this.delegate); + + this.delegate = new EditorDelegate() { + @Override + public void setValue(Object value) { + comboBox.setSelectedItem(value); + } + + @Override + public Object getCellEditorValue() { + return comboBox.getSelectedItem(); + } + + @Override + public boolean shouldSelectCell(EventObject anEvent) { + if (anEvent instanceof MouseEvent) { + MouseEvent e = (MouseEvent) anEvent; + return e.getID() != MouseEvent.MOUSE_DRAGGED; + } + return true; + } + + @Override + public boolean stopCellEditing() { + if (comboBox.isEditable()) { + // Commit edited value. + comboBox.actionPerformed(new ActionEvent(ComboBoxCellEditor.this, 0, "")); + } + return super.stopCellEditing(); + } + + @Override + public void actionPerformed(ActionEvent e) { + JTextComponent editorComponent = (JTextComponent) comboBox.getEditor() + .getEditorComponent(); + + if (editorComponent.getDocument() instanceof AutoCompleteDocument) { + AutoCompleteDocument document = (AutoCompleteDocument) editorComponent + .getDocument(); + // if auto completion is happening right now, cell editing should not be stopped + if (!document.selecting) { + ComboBoxCellEditor.this.stopCellEditing(); + } + + } else { + ComboBoxCellEditor.this.stopCellEditing(); + } + } + }; + + comboBox.addActionListener(this.delegate); + } +} diff --git a/src/main/java/org/jdesktop/swingx/autocomplete/DelegatingDocumentEvent.java b/src/main/java/org/jdesktop/swingx/autocomplete/DelegatingDocumentEvent.java new file mode 100644 index 0000000000..393fa81ecb --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/autocomplete/DelegatingDocumentEvent.java @@ -0,0 +1,63 @@ +/** + * + */ +package org.jdesktop.swingx.autocomplete; + +import javax.swing.event.DocumentEvent; +import javax.swing.text.Document; +import javax.swing.text.Element; + +/** + * @author Karl George Schaefer + * + */ +final class DelegatingDocumentEvent implements DocumentEvent { + private final Document resourcedDocument; + private final DocumentEvent sourceEvent; + + public DelegatingDocumentEvent(Document resourcedDocument, DocumentEvent sourceEvent) { + this.resourcedDocument = resourcedDocument; + this.sourceEvent = sourceEvent; + } + + /** + * {@inheritDoc} + */ + @Override + public ElementChange getChange(Element elem) { + return sourceEvent.getChange(elem); + } + + /** + * {@inheritDoc} + */ + @Override + public Document getDocument() { + return resourcedDocument; + } + + /** + * {@inheritDoc} + */ + @Override + public int getLength() { + return sourceEvent.getLength(); + } + + /** + * {@inheritDoc} + */ + @Override + public int getOffset() { + return sourceEvent.getOffset(); + } + + /** + * {@inheritDoc} + */ + @Override + public EventType getType() { + return sourceEvent.getType(); + } + +} diff --git a/src/main/java/org/jdesktop/swingx/autocomplete/ListAdaptor.java b/src/main/java/org/jdesktop/swingx/autocomplete/ListAdaptor.java new file mode 100644 index 0000000000..ea86fde491 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/autocomplete/ListAdaptor.java @@ -0,0 +1,106 @@ +/* + * $Id: ListAdaptor.java 4045 2011-07-19 18:39:17Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.autocomplete; + +import javax.swing.*; +import javax.swing.event.ListSelectionListener; +import javax.swing.text.JTextComponent; + +/** + * An implementation of the AbstractAutoCompleteAdaptor that is suitable for a + * JList in conjunction with a JTextComponent. + * + * @author Thomas Bierhance + */ +public class ListAdaptor extends AbstractAutoCompleteAdaptor implements ListSelectionListener { + + /** the list containing the items */ + JList list; + /** the text component that is used for automatic completion*/ + JTextComponent textComponent; + /** the converter used to transform items to strings */ + ObjectToStringConverter stringConverter; + + /** + * Creates a new JListAdaptor for the given list and text component. + * @param list the list that contains the items that are used for automatic + * completion + * @param textComponent the text component that will be used automatic + * completion + */ + public ListAdaptor(JList list, JTextComponent textComponent) { + this(list, textComponent, ObjectToStringConverter.DEFAULT_IMPLEMENTATION); + } + + /** + * Creates a new JListAdaptor for the given list and text component. + * @param list the list that contains the items that are used for automatic + * completion + * @param textComponent the text component that will be used automatic + * completion + * @param stringConverter the converter used to transform items to strings + */ + public ListAdaptor(JList list, JTextComponent textComponent, ObjectToStringConverter stringConverter) { + this.list = list; + this.textComponent = textComponent; + this.stringConverter = stringConverter; + // when a new item is selected set and mark the text + list.addListSelectionListener(this); + } + + /** + * Implementation side effect - do not invoke. + * @param listSelectionEvent - + */ + // ListSelectionListener (listening to list) + @Override + public void valueChanged(javax.swing.event.ListSelectionEvent listSelectionEvent) { + // set the text to the currently selected item + getTextComponent().setText(stringConverter.getPreferredStringForItem(list.getSelectedValue())); + // mark the entire text + markEntireText(); + } + + @Override + public Object getSelectedItem() { + return list.getSelectedValue(); + } + + @Override + public int getItemCount() { + return list.getModel().getSize(); + } + + @Override + public Object getItem(int index) { + return list.getModel().getElementAt(index); + } + + @Override + public void setSelectedItem(Object item) { + list.setSelectedValue(item, true); + } + + @Override + public JTextComponent getTextComponent() { + return textComponent; + } +} diff --git a/src/main/java/org/jdesktop/swingx/autocomplete/ObjectToStringConverter.java b/src/main/java/org/jdesktop/swingx/autocomplete/ObjectToStringConverter.java new file mode 100644 index 0000000000..84b0a28ad1 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/autocomplete/ObjectToStringConverter.java @@ -0,0 +1,105 @@ +/* + * $Id: ObjectToStringConverter.java 4045 2011-07-19 18:39:17Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.autocomplete; + +/** + *

      + * This class is used to provide string representations for objects when + * doing automatic completion. + *

      + * A class inherited from this class could be used, when the object's + * toString method is not appropriate for automatic completion. + *

      + * An example for i18n: + *

      + *

      + * public class I18NStringConverter extends ObjectToStringConverter {
      + *   ResourceBundle bundle;
      + *
      + *   public I18NStringConverter(ResourceBundle bundle) {
      + *     this.bundle = bundle;
      + *   }
      + *
      + *   public String getPreferredStringForItem(Object item) {
      + *     return item==null ? null : bundle.getString(item.toString());
      + *   }
      + * }
      + * 
      + *

      + * It's also possible to return more than one string representation. The + * following example shows a converter that will allow a user to choose an + * airport using either the airport's full description (toString()) or + * its ICAO/IATA code: + *

      + *

      
      + * public class AirportConverter extends ObjectToStringConverter {
      + *
      + *   public String[] getPossibleStringsForItem(Object item) {
      + *     if (item==null) return new String[0];
      + *     if (!(item instanceof Airport)) throw new IllegalArgumentException();
      + *     Airport airport = (Airport) item;
      + *     return new String[]{airport.toString(), airport.icaoCode, airport.iataCode};
      + *   }
      + *       
      + *   public String getPreferredStringForItem(Object item) {
      + *     return item==null?null:getPossibleStringsForItem(item)[0];
      + *   }
      + * }
      + * 
      + *

      + * @author Thomas Bierhance + */ +public abstract class ObjectToStringConverter { + + /** + * Returns all possible String representations for a given item. + * The default implementation wraps the method getPreferredStringForItem. + * It returns an empty array, if the wrapped method returns null. Otherwise + * it returns a one dimensional array containing the wrapped method's return value. + * + * @param item the item to convert + * @return possible String representation for the given item. + */ + public String[] getPossibleStringsForItem(Object item) { + String preferred = getPreferredStringForItem(item); + return preferred == null ? new String[0] : new String[] { preferred }; + } + + /** + * Returns the preferred String representations for a given item. + * @param item the item to convert + * @return the preferred String representation for the given item. + */ + public abstract String getPreferredStringForItem(Object item); + + /** + * This field contains the default implementation, that returns item.toString() + * for any item !=null. For any item ==null, it returns null as well. + */ + public static final ObjectToStringConverter DEFAULT_IMPLEMENTATION = new DefaultObjectToStringConverter(); + + private static class DefaultObjectToStringConverter extends ObjectToStringConverter { + @Override + public String getPreferredStringForItem(Object item) { + return item==null ? null : item.toString(); + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/autocomplete/TextComponentAdaptor.java b/src/main/java/org/jdesktop/swingx/autocomplete/TextComponentAdaptor.java new file mode 100644 index 0000000000..704fa73a26 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/autocomplete/TextComponentAdaptor.java @@ -0,0 +1,80 @@ +/* + * $Id: TextComponentAdaptor.java 4045 2011-07-19 18:39:17Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.autocomplete; + +import javax.swing.text.JTextComponent; +import java.util.List; + +/** + * An implementation of the AbstractAutoCompleteAdaptor that is suitable for a + * JTextComponent. + * + * @author Thomas Bierhance + */ +public class TextComponentAdaptor extends AbstractAutoCompleteAdaptor { + + /** a List containing the strings to be used for automatic + * completion */ + List items; + /** the text component that is used for automatic completion*/ + JTextComponent textComponent; + /** the item that is currently selected */ + Object selectedItem; + + /** + * Creates a new TextComponentAdaptor for the given list and text + * component. + * + * @param items a List that contains the items that are used for + * automatic completion + * @param textComponent the text component that will be used automatic + * completion + */ + public TextComponentAdaptor(JTextComponent textComponent, List items) { + this.items = items; + this.textComponent = textComponent; + } + + @Override + public Object getSelectedItem() { + return selectedItem; + } + + @Override + public int getItemCount() { + return items.size(); + } + + @Override + public Object getItem(int index) { + return items.get(index); + } + + @Override + public void setSelectedItem(Object item) { + selectedItem = item; + } + + @Override + public JTextComponent getTextComponent() { + return textComponent; + } +} diff --git a/src/main/java/org/jdesktop/swingx/autocomplete/package-info.java b/src/main/java/org/jdesktop/swingx/autocomplete/package-info.java new file mode 100644 index 0000000000..7f99224f86 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/autocomplete/package-info.java @@ -0,0 +1,91 @@ +/* + * $Id: package-info.java 4045 2011-07-19 18:39:17Z kschaefe $ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +/** + * Contains classes to enable automatic completion for JComboBox and other + * components. + *

      + * The automatic completion feature allows the user to enter a few characters + * using the keyboard - meanwhile, the computer "guesses" what the + * user intents to enter. Take a look at the example below to get an idea of the + * resulting user experience. Suppose the user types 'J','O','R' and 'G'... + *

      + *

      + * + *

      + *

      + * The easiest way to get automatic completion for a component is to use the + * {@link org.jdesktop.swingx.autocomplete.AutoCompleteDecorator AutoCompleteDecorator}. + *

      + *

      Enabling automatic completion for e.g. a JComboBox is only one line of + * code:

      + *

      + * import org.jdesktop.swingx.autocomplete.AutoCompleteDecorator;
      + * [...]
      + * JComboBox comboBox = [...];
      + * AutoCompleteDecorator.decorate(comboBox); + *

      + *

      When the combo box is not editable when calling + * {@link org.jdesktop.swingx.autocomplete.AutoCompleteDecorator#decorate(JComboBox) decorate}, + * the automatic completion will be strict (only allowing items contained in + * the combo box). When the combo box is editable it will also be possible to + * enter items that are not contained in the combo box. + *

      + *

      Take care when enabling automatic completion for a JComboBox that is used + * as the cell editor for a JTable. You need to use the special + * {@link org.jdesktop.swingx.autocomplete.ComboBoxCellEditor ComboBoxCellEditor} + * instead of the standard DefaultCellEditor: + *

      + *

      + * JTable table = [...];
      + * JComboBox comboBox = [...];
      + * [...]
      + * TableColumn column = table.getColumnModel().getColumn([...]);
      + * column.setCellEditor(new ComboBoxCellEditor(comboBox)); + *

      + *

      + * If you want to enable automatic completion for a component that is not + * supported by the {@link org.jdesktop.swingx.autocomplete.AutoCompleteDecorator AutoCompleteDecorator}, you + * need to implement {@link org.jdesktop.swingx.autocomplete.AbstractAutoCompleteAdaptor AbstractAutoCompleteAdaptor}. For + * an example see {@link org.jdesktop.swingx.autocomplete.ComboBoxAdaptor ComboBoxAdaptor} + * and {@link org.jdesktop.swingx.autocomplete.ListAdaptor ListAdaptor}. + *

      + *

      + * The automatic completion works only for subclasses of + * {@link javax.swing.text.JTextComponent JTextComponent}. So you either use a component + * that contains a JTextComponent (e.g. JComboBox) or you connect a + * JTextComponent with another component (e.g. a JTextField and a JList). + * Of course, it's also possible to enable automatic completion for a + * JTextComponent without another visual component. + *

      + *

      Once you have a custom implementation of + * {@link org.jdesktop.swingx.autocomplete.AbstractAutoCompleteAdaptor AbstractAutoCompleteAdaptor}, + * you normally would only have to make three more calls: + *

      + *

      + * + * AbstractAutoCompleteAdaptor adaptor = new YourAdaptor([...]);
      + * AutoCompleteDocument document = new AutoCompleteDocument(adaptor, true); // or false if you need non-strict matching
      + * AutoCompleteDecorator.decorate(yourTextComponent, document, adaptor); + *
      + *

      + */ +package org.jdesktop.swingx.autocomplete; diff --git a/src/main/java/org/jdesktop/swingx/autocomplete/workarounds/MacOSXPopupLocationFix.java b/src/main/java/org/jdesktop/swingx/autocomplete/workarounds/MacOSXPopupLocationFix.java new file mode 100644 index 0000000000..f3c4bac468 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/autocomplete/workarounds/MacOSXPopupLocationFix.java @@ -0,0 +1,213 @@ +/* + * $Id: MacOSXPopupLocationFix.java 4019 2011-05-11 16:52:30Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.autocomplete.workarounds; + +import javax.swing.*; +import javax.swing.event.PopupMenuEvent; +import javax.swing.event.PopupMenuListener; +import java.awt.*; + +/** + * Fix a problem where the JComboBox's popup obscures its editor in the Mac OS X + * Aqua look and feel. + * + *

      Installing this fix will resolve the problem for Aqua without having + * side-effects for other look-and-feels. It also supports dynamically changed + * look and feels. + * + * @see Glazed Lists bug entry + * @see SwingX bug entry + * + * @author Jesse Wilson + */ +public final class MacOSXPopupLocationFix { + + /** the components being fixed */ + private final JComboBox comboBox; + private final JPopupMenu popupMenu; + + /** the listener provides callbacks as necessary */ + private final Listener listener = new Listener(); + + /** + * Private constructor so users use the more action-oriented + * {@link #install} method. + */ + private MacOSXPopupLocationFix(JComboBox comboBox) { + this.comboBox = comboBox; + this.popupMenu = (JPopupMenu)comboBox.getUI().getAccessibleChild(comboBox, 0); + + popupMenu.addPopupMenuListener(listener); + } + + /** + * Install the fix for the specified combo box. + */ + public static MacOSXPopupLocationFix install(JComboBox comboBox) { + if(comboBox == null) throw new IllegalArgumentException(); + return new MacOSXPopupLocationFix(comboBox); + } + + /** + * Uninstall the fix. Usually this is unnecessary since letting the combo + * box go out of scope is sufficient. + */ + public void uninstall() { + popupMenu.removePopupMenuListener(listener); + } + + /** + * Reposition the popup immediately before it is shown. + */ + private class Listener implements PopupMenuListener { + @Override + public void popupMenuWillBecomeVisible(PopupMenuEvent e) { + final JComponent popupComponent = (JComponent) e.getSource(); + fixPopupLocation(popupComponent); + } + @Override + public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { + // do nothing + } + @Override + public void popupMenuCanceled(PopupMenuEvent e) { + // do nothing + } + } + + /** + * Do the adjustment on the specified popupComponent immediately before + * it is displayed. + */ + private void fixPopupLocation(JComponent popupComponent) { + // we only need to fix Apple's aqua look and feel + if(popupComponent.getClass().getName().indexOf("apple.laf") != 0) { + return; + } + + // put the popup right under the combo box so it looks like a + // normal Aqua combo box + Point comboLocationOnScreen = comboBox.getLocationOnScreen(); + int comboHeight = comboBox.getHeight(); + int popupY = comboLocationOnScreen.y + comboHeight; + + // ...unless the popup overflows the screen, in which case we put it + // above the combobox + Rectangle screenBounds = new ScreenGeometry(comboBox).getScreenBounds(); + int popupHeight = popupComponent.getPreferredSize().height; + if(comboLocationOnScreen.y + comboHeight + popupHeight > screenBounds.x + screenBounds.height) { + popupY = comboLocationOnScreen.y - popupHeight; + } + + popupComponent.setLocation(comboLocationOnScreen.x, popupY); + } + + /** + * Figure out the dimensions of our screen. + * + *

      This code is inspired by similar in + * JPopupMenu.adjustPopupLocationToFitScreen(). + * + * @author Jesse Wilson + */ + private final static class ScreenGeometry { + + final GraphicsConfiguration graphicsConfiguration; + final boolean aqua; + + public ScreenGeometry(JComponent component) { + this.aqua = UIManager.getLookAndFeel().getName().indexOf("Aqua") != -1; + this.graphicsConfiguration = graphicsConfigurationForComponent(component); + } + + /** + * Get the best graphics configuration for the specified point and component. + */ + private GraphicsConfiguration graphicsConfigurationForComponent(Component component) { + Point point = component.getLocationOnScreen(); + + // try to find the graphics configuration for our point of interest + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice[] gd = ge.getScreenDevices(); + for(int i = 0; i < gd.length; i++) { + if(gd[i].getType() != GraphicsDevice.TYPE_RASTER_SCREEN) continue; + GraphicsConfiguration defaultGraphicsConfiguration = gd[i].getDefaultConfiguration(); + if(!defaultGraphicsConfiguration.getBounds().contains(point)) continue; + return defaultGraphicsConfiguration; + } + + // we couldn't find a graphics configuration, use the component's + return component.getGraphicsConfiguration(); + } + + /** + * Get the bounds of where we can put a popup. + */ + public Rectangle getScreenBounds() { + Rectangle screenSize = getScreenSize(); + Insets screenInsets = getScreenInsets(); + + return new Rectangle( + screenSize.x + screenInsets.left, + screenSize.y + screenInsets.top, + screenSize.width - screenInsets.left - screenInsets.right, + screenSize.height - screenInsets.top - screenInsets.bottom + ); + } + + /** + * Get the bounds of the screen currently displaying the component. + */ + public Rectangle getScreenSize() { + // get the screen bounds and insets via the graphics configuration + if(graphicsConfiguration != null) { + return graphicsConfiguration.getBounds(); + } + + // just use the toolkit bounds, it's less awesome but sufficient + return new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()); + } + + /** + * Fetch the screen insets, the off limits areas around the screen such + * as menu bar, dock or start bar. + */ + public Insets getScreenInsets() { + Insets screenInsets; + if(graphicsConfiguration != null) { + screenInsets = Toolkit.getDefaultToolkit().getScreenInsets(graphicsConfiguration); + } else { + screenInsets = new Insets(0, 0, 0, 0); + } + + // tweak the insets for aqua, they're reported incorrectly there + if(aqua) { + int aquaBottomInsets = 21; // unreported insets, shown in screenshot, https://glazedlists.dev.java.net/issues/show_bug.cgi?id=332 + int aquaTopInsets = 22; // for Apple menu bar, found via debugger + + screenInsets.bottom = Math.max(screenInsets.bottom, aquaBottomInsets); + screenInsets.top = Math.max(screenInsets.top, aquaTopInsets); + } + + return screenInsets; + } + } +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/autocomplete/workarounds/package-info.java b/src/main/java/org/jdesktop/swingx/autocomplete/workarounds/package-info.java new file mode 100644 index 0000000000..ba1fb02945 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/autocomplete/workarounds/package-info.java @@ -0,0 +1,26 @@ +/* + * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +/** + * Contains classes to workaround Look and Feel implemetation problems caused + * when applying the autocomplete decorators. + */ +package org.jdesktop.swingx.autocomplete.workarounds; + diff --git a/src/main/java/org/jdesktop/swingx/border/DropShadowBorder.java b/src/main/java/org/jdesktop/swingx/border/DropShadowBorder.java new file mode 100644 index 0000000000..a7d3ca6fa8 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/border/DropShadowBorder.java @@ -0,0 +1,440 @@ +/* + * $Id: DropShadowBorder.java 4147 2012-02-01 17:13:24Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.border; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.util.GraphicsUtilities; + +import javax.swing.border.Border; +import java.awt.*; +import java.awt.geom.RoundRectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.ConvolveOp; +import java.awt.image.Kernel; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +/** + * Implements a DropShadow for components. In general, the DropShadowBorder will + * work with any rectangular components that do not have a default border + * installed as part of the look and feel, or otherwise. For example, + * DropShadowBorder works wonderfully with JPanel, but horribly with JComboBox. + *

      + * Note: {@code DropShadowBorder} should usually be added to non-opaque + * components, otherwise the background is likely to bleed through.

      + *

      Note: Since generating drop shadows is relatively expensive operation, + * {@code DropShadowBorder} keeps internal static cache that allows sharing + * same border for multiple re-rendering and between different instances of the + * class. Since this cache is shared at class level and never reset, it might + * bleed your app memory in case you tend to create many different borders + * rapidly.

      + * @author rbair + */ +@JavaBean +public class DropShadowBorder implements Border, Serializable { + /** + * + */ + private static final long serialVersionUID = 715287754750604058L; + + private static enum Position {TOP, TOP_LEFT, LEFT, BOTTOM_LEFT, + BOTTOM, BOTTOM_RIGHT, RIGHT, TOP_RIGHT} + + private static final Map> CACHE + = new HashMap>(); + + private Color shadowColor; + private int shadowSize; + private float shadowOpacity; + private int cornerSize; + private boolean showTopShadow; + private boolean showLeftShadow; + private boolean showBottomShadow; + private boolean showRightShadow; + + public DropShadowBorder() { + this(Color.BLACK, 5); + } + + public DropShadowBorder(Color shadowColor, int shadowSize) { + this(shadowColor, shadowSize, .5f, 12, false, false, true, true); + } + + public DropShadowBorder(boolean showLeftShadow) { + this(Color.BLACK, 5, .5f, 12, false, showLeftShadow, true, true); + } + + public DropShadowBorder(Color shadowColor, int shadowSize, + float shadowOpacity, int cornerSize, boolean showTopShadow, + boolean showLeftShadow, boolean showBottomShadow, boolean showRightShadow) { + this.shadowColor = shadowColor; + this.shadowSize = shadowSize; + this.shadowOpacity = shadowOpacity; + this.cornerSize = cornerSize; + this.showTopShadow = showTopShadow; + this.showLeftShadow = showLeftShadow; + this.showBottomShadow = showBottomShadow; + this.showRightShadow = showRightShadow; + } + + /** + * {@inheritDoc} + */ + @Override + public void paintBorder(Component c, Graphics graphics, int x, int y, int width, int height) { + /* + * 1) Get images for this border + * 2) Paint the images for each side of the border that should be painted + */ + Map images = getImages((Graphics2D)graphics); + + Graphics2D g2 = (Graphics2D)graphics.create(); + + try { + //The location and size of the shadows depends on which shadows are being + //drawn. For instance, if the left & bottom shadows are being drawn, then + //the left shadow extends all the way down to the corner, a corner is drawn, + //and then the bottom shadow begins at the corner. If, however, only the + //bottom shadow is drawn, then the bottom-left corner is drawn to the + //right of the corner, and the bottom shadow is somewhat shorter than before. + + int shadowOffset = 2; //the distance between the shadow and the edge + + Point topLeftShadowPoint = null; + if (showLeftShadow || showTopShadow) { + topLeftShadowPoint = new Point(); + if (showLeftShadow && !showTopShadow) { + topLeftShadowPoint.setLocation(x, y + shadowOffset); + } else if (showLeftShadow && showTopShadow) { + topLeftShadowPoint.setLocation(x, y); + } else if (!showLeftShadow && showTopShadow) { + topLeftShadowPoint.setLocation(x + shadowSize, y); + } + } + + Point bottomLeftShadowPoint = null; + if (showLeftShadow || showBottomShadow) { + bottomLeftShadowPoint = new Point(); + if (showLeftShadow && !showBottomShadow) { + bottomLeftShadowPoint.setLocation(x, y + height - shadowSize - shadowSize); + } else if (showLeftShadow && showBottomShadow) { + bottomLeftShadowPoint.setLocation(x, y + height - shadowSize); + } else if (!showLeftShadow && showBottomShadow) { + bottomLeftShadowPoint.setLocation(x + shadowSize, y + height - shadowSize); + } + } + + Point bottomRightShadowPoint = null; + if (showRightShadow || showBottomShadow) { + bottomRightShadowPoint = new Point(); + if (showRightShadow && !showBottomShadow) { + bottomRightShadowPoint.setLocation(x + width - shadowSize, y + height - shadowSize - shadowSize); + } else if (showRightShadow && showBottomShadow) { + bottomRightShadowPoint.setLocation(x + width - shadowSize, y + height - shadowSize); + } else if (!showRightShadow && showBottomShadow) { + bottomRightShadowPoint.setLocation(x + width - shadowSize - shadowSize, y + height - shadowSize); + } + } + + Point topRightShadowPoint = null; + if (showRightShadow || showTopShadow) { + topRightShadowPoint = new Point(); + if (showRightShadow && !showTopShadow) { + topRightShadowPoint.setLocation(x + width - shadowSize, y + shadowOffset); + } else if (showRightShadow && showTopShadow) { + topRightShadowPoint.setLocation(x + width - shadowSize, y); + } else if (!showRightShadow && showTopShadow) { + topRightShadowPoint.setLocation(x + width - shadowSize - shadowSize, y); + } + } + + g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g2.setRenderingHint(RenderingHints.KEY_RENDERING, + RenderingHints.VALUE_RENDER_SPEED); + + if (showLeftShadow) { + Rectangle leftShadowRect = + new Rectangle(x, + topLeftShadowPoint.y + shadowSize, + shadowSize, + bottomLeftShadowPoint.y - topLeftShadowPoint.y - shadowSize); + g2.drawImage(images.get(Position.LEFT), + leftShadowRect.x, leftShadowRect.y, + leftShadowRect.width, leftShadowRect.height, null); + } + + if (showBottomShadow) { + Rectangle bottomShadowRect = + new Rectangle(bottomLeftShadowPoint.x + shadowSize, + y + height - shadowSize, + bottomRightShadowPoint.x - bottomLeftShadowPoint.x - shadowSize, + shadowSize); + g2.drawImage(images.get(Position.BOTTOM), + bottomShadowRect.x, bottomShadowRect.y, + bottomShadowRect.width, bottomShadowRect.height, null); + } + + if (showRightShadow) { + Rectangle rightShadowRect = + new Rectangle(x + width - shadowSize, + topRightShadowPoint.y + shadowSize, + shadowSize, + bottomRightShadowPoint.y - topRightShadowPoint.y - shadowSize); + g2.drawImage(images.get(Position.RIGHT), + rightShadowRect.x, rightShadowRect.y, + rightShadowRect.width, rightShadowRect.height, null); + } + + if (showTopShadow) { + Rectangle topShadowRect = + new Rectangle(topLeftShadowPoint.x + shadowSize, + y, + topRightShadowPoint.x - topLeftShadowPoint.x - shadowSize, + shadowSize); + g2.drawImage(images.get(Position.TOP), + topShadowRect.x, topShadowRect.y, + topShadowRect.width, topShadowRect.height, null); + } + + if (showLeftShadow || showTopShadow) { + g2.drawImage(images.get(Position.TOP_LEFT), + topLeftShadowPoint.x, topLeftShadowPoint.y, null); + } + if (showLeftShadow || showBottomShadow) { + g2.drawImage(images.get(Position.BOTTOM_LEFT), + bottomLeftShadowPoint.x, bottomLeftShadowPoint.y, null); + } + if (showRightShadow || showBottomShadow) { + g2.drawImage(images.get(Position.BOTTOM_RIGHT), + bottomRightShadowPoint.x, bottomRightShadowPoint.y, null); + } + if (showRightShadow || showTopShadow) { + g2.drawImage(images.get(Position.TOP_RIGHT), + topRightShadowPoint.x, topRightShadowPoint.y, null); + } + } finally { + g2.dispose(); + } + } + + private Map getImages(Graphics2D g2) { + //first, check to see if an image for this size has already been rendered + //if so, use the cache. Else, draw and save + Map images = CACHE.get(shadowSize + (shadowColor.hashCode() * .3) + (shadowOpacity * .12));//TODO do a real hash + if (images == null) { + images = new HashMap(); + + /* + * To draw a drop shadow, I have to: + * 1) Create a rounded rectangle + * 2) Create a BufferedImage to draw the rounded rect in + * 3) Translate the graphics for the image, so that the rectangle + * is centered in the drawn space. The border around the rectangle + * needs to be shadowWidth wide, so that there is space for the + * shadow to be drawn. + * 4) Draw the rounded rect as shadowColor, with an opacity of shadowOpacity + * 5) Create the BLUR_KERNEL + * 6) Blur the image + * 7) copy off the corners, sides, etc into images to be used for + * drawing the Border + */ + int rectWidth = cornerSize + 1; + RoundRectangle2D rect = new RoundRectangle2D.Double(0, 0, rectWidth, rectWidth, cornerSize, cornerSize); + int imageWidth = rectWidth + shadowSize * 2; + BufferedImage image = GraphicsUtilities.createCompatibleTranslucentImage(imageWidth, imageWidth); + Graphics2D buffer = (Graphics2D)image.getGraphics(); + + try { + buffer.setPaint(new Color(shadowColor.getRed(), shadowColor.getGreen(), + shadowColor.getBlue(), (int)(shadowOpacity * 255))); +// buffer.setColor(new Color(0.0f, 0.0f, 0.0f, shadowOpacity)); + buffer.translate(shadowSize, shadowSize); + buffer.fill(rect); + } finally { + buffer.dispose(); + } + + float blurry = 1.0f / (shadowSize * shadowSize); + float[] blurKernel = new float[shadowSize * shadowSize]; + for (int i=0; i + * This border is useful when attempting to add {@code Icon}s to pre-existing + * components without requiring specialty painting. + * + * @author Amy Fowler + * @author Karl Schaefer + * + * @version 1.1 + */ +@JavaBean +public class IconBorder implements Border, Serializable { + + /** + * An empty icon. + */ + public static final Icon EMPTY_ICON = new EmptyIcon(); + private int padding; + private Icon icon; + private int iconPosition; + private Rectangle iconBounds = new Rectangle(); + + /** + * Creates an {@code IconBorder} with an empty icon in a trailing position + * with a padding of 4. + * + * @see #EMPTY_ICON + */ + public IconBorder() { + this(null); + } + + /** + * Creates an {@code IconBorder} with the specified icon in a trailing + * position with a padding of 4. + * + * @param validIcon + * the icon to set. This may be {@code null} to represent an + * empty icon. + * @see #EMPTY_ICON + */ + public IconBorder(Icon validIcon) { + this(validIcon, SwingConstants.TRAILING); + } + + /** + * Creates an {@code IconBorder} with the specified constraints and a + * padding of 4. + * + * @param validIcon + * the icon to set. This may be {@code null} to represent an + * empty icon. + * @param iconPosition + * the position to place the icon relative to the component + * contents. This must be one of the following + * {@code SwingConstants}: + *
        + *
      • {@code LEADING}
      • + *
      • {@code TRAILING}
      • + *
      • {@code EAST}
      • + *
      • {@code WEST}
      • + *
      + * @throws IllegalArgumentException + * if {@code iconPosition} is not a valid position. + * @see #EMPTY_ICON + */ + public IconBorder(Icon validIcon, int iconPosition) { + this(validIcon, iconPosition, 4); + } + + /** + * Creates an {@code IconBorder} with the specified constraints. If + * {@code validIcon} is {@code null}, {@code EMPTY_ICON} is used instead. + * If {@code padding} is negative, then the border does not use padding. + * + * @param validIcon + * the icon to set. This may be {@code null} to represent an + * empty icon. + * @param iconPosition + * the position to place the icon relative to the component + * contents. This must be one of the following + * {@code SwingConstants}: + *
        + *
      • {@code LEADING}
      • + *
      • {@code TRAILING}
      • + *
      • {@code EAST}
      • + *
      • {@code WEST}
      • + *
      + * @param padding + * the padding to surround the icon with. All non-positive values + * set the padding to 0. + * @throws IllegalArgumentException + * if {@code iconPosition} is not a valid position. + * @see #EMPTY_ICON + */ + public IconBorder(Icon validIcon, int iconPosition, int padding) { + setIcon(validIcon); + setPadding(padding); + setIconPosition(iconPosition); + } + + private boolean isValidPosition(int position) { + boolean result = false; + + switch (position) { + case SwingConstants.LEADING: + case SwingConstants.TRAILING: + case SwingConstants.EAST: + case SwingConstants.WEST: + result = true; + break; + default: + result = false; + } + + return result; + } + + /** + * {@inheritDoc} + */ + public Insets getBorderInsets(Component c) { + int horizontalInset = icon.getIconWidth() + (2 * padding); + int iconPosition = bidiDecodeLeadingTrailing(c.getComponentOrientation(), this.iconPosition); + if (iconPosition == SwingConstants.EAST) { + return new Insets(0, 0, 0, horizontalInset); + } + return new Insets(0, horizontalInset, 0, 0); + } + + /** + * Sets the icon for this border. + * + * @param validIcon + * the icon to set. This may be {@code null} to represent an + * empty icon. + * @see #EMPTY_ICON + */ + public void setIcon(Icon validIcon) { + this.icon = validIcon == null ? EMPTY_ICON : validIcon; + } + + /** + * This border is not opaque. + * + * @return always returns {@code false} + */ + public boolean isBorderOpaque() { + return false; + } + + /** + * {@inheritDoc} + */ + public void paintBorder(Component c, Graphics g, int x, int y, int width, + int height) { + int iconPosition = bidiDecodeLeadingTrailing(c.getComponentOrientation(), this.iconPosition); + if (iconPosition == SwingConstants.NORTH_EAST) { + iconBounds.y = y + padding; + iconBounds.x = x + width - padding - icon.getIconWidth(); + } else if (iconPosition == SwingConstants.EAST) { // EAST + iconBounds.y = y + + ((height - icon.getIconHeight()) / 2); + iconBounds.x = x + width - padding - icon.getIconWidth(); + } else if (iconPosition == SwingConstants.WEST) { + iconBounds.y = y + + ((height - icon.getIconHeight()) / 2); + iconBounds.x = x + padding; + } + iconBounds.width = icon.getIconWidth(); + iconBounds.height = icon.getIconHeight(); + icon.paintIcon(c, g, iconBounds.x, iconBounds.y); + } + + /** + * Returns EAST or WEST depending on the ComponentOrientation and + * the given postion LEADING/TRAILING this method has no effect for other + * position values + */ + private int bidiDecodeLeadingTrailing(ComponentOrientation c, int position) { + if(position == SwingConstants.TRAILING) { + if(!c.isLeftToRight()) { + return SwingConstants.WEST; + } + return SwingConstants.EAST; + } + if(position == SwingConstants.LEADING) { + if(c.isLeftToRight()) { + return SwingConstants.WEST; + } + return SwingConstants.EAST; + } + return position; + } + + /** + * Gets the padding surrounding the icon. + * + * @return the padding for the icon. This value is guaranteed to be + * nonnegative. + */ + public int getPadding() { + return padding; + } + + /** + * Sets the padding around the icon. + * + * @param padding + * the padding to set. If {@code padding < 0}, then + * {@code padding} will be set to {@code 0}. + */ + public void setPadding(int padding) { + this.padding = padding < 0 ? 0 : padding; + } + + /** + * Returns the position to place the icon (relative to the component contents). + * + * @return one of the following {@code SwingConstants}: + *
        + *
      • {@code LEADING}
      • + *
      • {@code TRAILING}
      • + *
      • {@code EAST}
      • + *
      • {@code WEST}
      • + *
      + */ + public int getIconPosition() { + return iconPosition; + } + + /** + * Sets the position to place the icon (relative to the component contents). + * + * @param iconPosition must be one of the following {@code SwingConstants}: + *
        + *
      • {@code LEADING}
      • + *
      • {@code TRAILING}
      • + *
      • {@code EAST}
      • + *
      • {@code WEST}
      • + *
      + * @throws IllegalArgumentException + * if {@code iconPosition} is not a valid position. + */ + public void setIconPosition(int iconPosition) { + if (!isValidPosition(iconPosition)) { + throw new IllegalArgumentException("Invalid icon position"); + } + this.iconPosition = iconPosition; + } + +} diff --git a/src/main/java/org/jdesktop/swingx/border/MatteBorderExt.java b/src/main/java/org/jdesktop/swingx/border/MatteBorderExt.java new file mode 100644 index 0000000000..0829c6698e --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/border/MatteBorderExt.java @@ -0,0 +1,303 @@ +/* + * $Id: MatteBorderExt.java 4147 2012-02-01 17:13:24Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.border; + +import org.jdesktop.beans.JavaBean; + +import javax.swing.*; +import javax.swing.border.MatteBorder; +import java.awt.*; + +/** + * Matte border that allows specialized icons for corners and sides. + * + * @author Ramesh Gupta + */ +@JavaBean +public class MatteBorderExt extends MatteBorder { + protected Icon[] tileIcons = null; + private Icon defaultIcon = null; + + /** + * Draws a matte border using specialized icons for corners and sides. If + * tileIcons is null, or if the length of tileIcons array is less than 2, this + * defaults to the {@link MatteBorder superclass} behavior. + * Otherwise, tileIcons must specify icons in clockwise order, starting with + * the top-left icon at index zero, culminating with the left icon at index 7. + * If the length of the tileIcons array is greater than 1, but less than 8, + * then tileIcons[0] is used to paint the corners, and tileIcons[1] is used + * to paint the sides, with icons rotated as necessary. Other icons, if any, + * are ignored. + * + * @param top top inset + * @param left left inset + * @param bottom bottom inset + * @param right right inset + * @param tileIcons array of icons starting with top-left in index 0, + * continuing clockwise through the rest of the indices + */ + public MatteBorderExt(int top, int left, int bottom, int right, + Icon[] tileIcons) { + super(top, left, bottom, right, + (tileIcons == null) || (tileIcons.length == 0) ? null : + tileIcons[0]); + this.tileIcons = tileIcons; + } + + /** + * @see MatteBorder#MatteBorder(int, int, int, int, Color) + */ + public MatteBorderExt(int top, int left, int bottom, int right, + Color matteColor) { + super(top, left, bottom, right, matteColor); + } + + /** + * @see MatteBorder#MatteBorder(Insets, Color) + */ + public MatteBorderExt(Insets borderInsets, Color matteColor) { + super(borderInsets, matteColor); + } + + /** + * @see MatteBorder#MatteBorder(int, int, int, int, Icon) + */ + public MatteBorderExt(int top, int left, int bottom, int right, + Icon tileIcon) { + super(top, left, bottom, right, tileIcon); + } + + /** + * @see MatteBorder#MatteBorder(Insets, Icon) + */ + public MatteBorderExt(Insets borderInsets, Icon tileIcon) { + super(borderInsets, tileIcon); + } + + /** + * @see MatteBorder#MatteBorder(Icon) + */ + public MatteBorderExt(Icon tileIcon) { + super(tileIcon); + } + + /** + * Returns the icons used by this border + * + * @return the icons used by this border + */ + public Icon[] getTileIcons() { + return tileIcons; + } + + /** + * {@inheritDoc} + */ + @Override + public void paintBorder(Component c, Graphics g, int x, int y, + int width, int height) { + if ( (tileIcons == null) || (tileIcons.length < 2)) { + super.paintBorder(c, g, x, y, width, height); + return; + } + + Insets insets = getBorderInsets(c); + int clipWidth, clipHeight; + + clipWidth = Math.min(width, insets.left); // clip to component width or insets + clipHeight = Math.min(height, insets.top); // clip to component height or insets + + if ( (clipWidth <= 0) || (clipHeight <= 0)) { + return; // nothing to paint + } + + // We now know that we have at least two icons! + + Color oldColor = g.getColor(); // restore before exiting + g.translate(x, y); // restore before exiting + + for (int i = 0; i < tileIcons.length; i++) { + // Make sure we have an icon to paint with + if (tileIcons[i] == null) { + tileIcons[i] = getDefaultIcon(); + } + } + + paintTopLeft(c, g, 0, 0, insets.left, insets.top); + paintTop(c, g, insets.left, 0, width - insets.left - insets.right, insets.top); + paintTopRight(c, g, width - insets.right, 0, insets.right, insets.top); + paintRight(c, g, width - insets.right, insets.top, insets.right, height - insets.top - insets.bottom); + paintBottomRight(c, g, width - insets.right, height - insets.bottom, insets.right, insets.bottom); + paintBottom(c, g, insets.left, height - insets.bottom, width - insets.left - insets.right, insets.bottom); + paintBottomLeft(c, g, 0, height - insets.bottom, insets.left, insets.bottom); + paintLeft(c, g, 0, insets.top, insets.left, height - insets.top - insets.bottom); + + g.translate( -x, -y); // restore + g.setColor(oldColor); // restore + + } + + protected void paint(Icon icon, Component c, Graphics g, int x, int y, + int width, int height) { + Graphics cg = g.create(); + + try { + cg.setClip(x, y, width, height); + int tileW = icon.getIconWidth(); + int tileH = icon.getIconHeight(); + int xpos, ypos, startx, starty; + for (ypos = 0; height - ypos > 0; ypos += tileH) { + for (xpos = 0; width - xpos > 0; xpos += tileW) { + icon.paintIcon(c, cg, x + xpos, y + ypos); + } + } + } finally { + cg.dispose(); + } + } + + /** + * Only called by paintBorder() + */ + protected void paintTopLeft(Component c, Graphics g, int x, int y, int width, int height) { + Graphics cg = g.create(); + + try { + cg.setClip(x, y, width, height); + tileIcons[0].paintIcon(c, cg, x, y); + } finally { + cg.dispose(); + } + } + + /** + * Only called by paintBorder() + */ + protected void paintTop(Component c, Graphics g, int x, int y, int width, int height) { + paint(tileIcons[1], c, g, x, y, width, height); + } + + /** + * Only called by paintBorder() + */ + protected void paintTopRight(Component c, Graphics g, int x, int y, int width, int height) { + if (tileIcons.length == 8) { + paint(tileIcons[2], c, g, x, y, width, height); + } + else { + Icon icon = tileIcons[0]; + /** @todo Rotate -90 and paint icon */ + } + } + + /** + * Only called by paintBorder() + */ + protected void paintRight(Component c, Graphics g, int x, int y, int width, int height) { + if (tileIcons.length == 8) { + paint(tileIcons[3], c, g, x, y, width, height); + } + else { + Icon icon = tileIcons[1]; + /** @todo Rotate -90 and paint icon */ + } + } + + /** + * Only called by paintBorder() + */ + protected void paintBottomRight(Component c, Graphics g, int x, int y, int width, int height) { + if (tileIcons.length == 8) { + paint(tileIcons[4], c, g, x, y, width, height); + } + else { + Icon icon = tileIcons[0]; + /** @todo Rotate -180 and paint icon */ + } + } + + /** + * Only called by paintBorder() + */ + protected void paintBottom(Component c, Graphics g, int x, int y, int width, int height) { + if (tileIcons.length == 8) { + paint(tileIcons[5], c, g, x, y, width, height); + } + else { + Icon icon = tileIcons[1]; + /** @todo Rotate -180 and paint icon */ + } + } + + /** + * Only called by paintBorder() + */ + protected void paintBottomLeft(Component c, Graphics g, int x, int y, int width, int height) { + if (tileIcons.length == 8) { + paint(tileIcons[6], c, g, x, y, width, height); + } + else { + Icon icon = tileIcons[0]; + /** @todo Rotate -270 and paint icon */ + } + } + + /** + * Only called by paintBorder() + */ + protected void paintLeft(Component c, Graphics g, int x, int y, int width, int height) { + if (tileIcons.length == 8) { + paint(tileIcons[7], c, g, x, y, width, height); + } + else { + Icon icon = tileIcons[1]; + /** @todo Rotate -270 and paint icon */ + } + } + + /** + * Only called by paintBorder() + */ + protected Icon getDefaultIcon() { + if (defaultIcon == null) { + defaultIcon = new Icon() { + private int width = 3; + private int height = 3; + + public int getIconWidth() { + return width; + } + + public int getIconHeight() { + return height; + } + + public void paintIcon(Component c, Graphics g, int x, int y) { + g.setColor(c.getBackground().darker().darker()); + //g.translate(x, y); + g.fillRect(x, y, width, height); + } + }; + } + return defaultIcon; + } +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/border/package-info.java b/src/main/java/org/jdesktop/swingx/border/package-info.java new file mode 100644 index 0000000000..287491e5d7 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/border/package-info.java @@ -0,0 +1,25 @@ +/* + * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +/** + * Contains extensions to the {@code javax.swingx.border} package. + */ +package org.jdesktop.swingx.border; + diff --git a/src/main/java/org/jdesktop/swingx/calendar/AbstractDateSelectionModel.java b/src/main/java/org/jdesktop/swingx/calendar/AbstractDateSelectionModel.java new file mode 100644 index 0000000000..228988b4e3 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/calendar/AbstractDateSelectionModel.java @@ -0,0 +1,329 @@ +/* + * $Id: AbstractDateSelectionModel.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.calendar; + +import org.jdesktop.swingx.event.DateSelectionEvent; +import org.jdesktop.swingx.event.DateSelectionEvent.EventType; +import org.jdesktop.swingx.event.DateSelectionListener; +import org.jdesktop.swingx.event.EventListenerMap; + +import java.util.*; + +/** + * Abstract base implementation of DateSelectionModel. Implements + * notification, Calendar related properties and lower/upper bounds. + * + * @author Jeanette Winzenburg + */ +public abstract class AbstractDateSelectionModel implements DateSelectionModel { + public static final SortedSet EMPTY_DATES = Collections.unmodifiableSortedSet(new TreeSet()); + + protected EventListenerMap listenerMap; + protected boolean adjusting; + protected Calendar calendar; + protected Date upperBound; + protected Date lowerBound; + + /** + * the locale used by the calendar.

      + * NOTE: need to keep separately as a Calendar has no getter. + */ + protected Locale locale; + + /** + * Instantiates a DateSelectionModel with default locale. + */ + public AbstractDateSelectionModel() { + this(null); + } + + /** + * Instantiates a DateSelectionModel with the given locale. If the locale is + * null, the Locale's default is used. + * + * PENDING JW: fall back to JComponent.getDefaultLocale instead? We use this + * with components anyway? + * + * @param locale the Locale to use with this model, defaults to Locale.default() + * if null. + */ + public AbstractDateSelectionModel(Locale locale) { + this.listenerMap = new EventListenerMap(); + setLocale(locale); + } + + /** + * {@inheritDoc} + */ + @Override + public Calendar getCalendar() { + return (Calendar) calendar.clone(); + } + + /** + * {@inheritDoc} + */ + @Override + public int getFirstDayOfWeek() { + return calendar.getFirstDayOfWeek(); + } + + /** + * {@inheritDoc} + */ + @Override + public void setFirstDayOfWeek(final int firstDayOfWeek) { + if (firstDayOfWeek == getFirstDayOfWeek()) return; + calendar.setFirstDayOfWeek(firstDayOfWeek); + fireValueChanged(EventType.CALENDAR_CHANGED); + } + + /** + * {@inheritDoc} + */ + @Override + public int getMinimalDaysInFirstWeek() { + return calendar.getMinimalDaysInFirstWeek(); + } + + /** + * {@inheritDoc} + */ + @Override + public void setMinimalDaysInFirstWeek(int minimalDays) { + if (minimalDays == getMinimalDaysInFirstWeek()) return; + calendar.setMinimalDaysInFirstWeek(minimalDays); + fireValueChanged(EventType.CALENDAR_CHANGED); + } + + + /** + * {@inheritDoc} + */ + @Override + public TimeZone getTimeZone() { + return calendar.getTimeZone(); + } + + /** + * {@inheritDoc} + */ + @Override + public void setTimeZone(TimeZone timeZone) { + if (getTimeZone().equals(timeZone)) return; + TimeZone oldTimeZone = getTimeZone(); + calendar.setTimeZone(timeZone); + adjustDatesToTimeZone(oldTimeZone); + fireValueChanged(EventType.CALENDAR_CHANGED); + } + + /** + * Adjusts all stored dates to a new time zone. + * This method is called after the change had been made.

      + * + * This implementation resets all dates to null, clears everything. + * Subclasses may override to really map to the new time zone. + * + * @param oldTimeZone the old time zone + * + */ + protected void adjustDatesToTimeZone(TimeZone oldTimeZone) { + clearSelection(); + setLowerBound(null); + setUpperBound(null); + setUnselectableDates(EMPTY_DATES); + } + + /** + * {@inheritDoc} + */ + @Override + public Locale getLocale() { + return locale; + } + + /** + * {@inheritDoc} + */ + @Override + public void setLocale(Locale locale) { + if (locale == null) { + locale = Locale.getDefault(); + } + if (locale.equals(getLocale())) return; + this.locale = locale; + if (calendar != null) { + calendar = Calendar.getInstance(calendar.getTimeZone(), locale); + } else { + calendar = Calendar.getInstance(locale); + } + fireValueChanged(EventType.CALENDAR_CHANGED); + } + +//------------------- utility methods + + /** + * Returns the start of the day of the given date in this model's calendar. + * NOTE: the calendar is changed by this operation. + * + * @param date the Date to get the start for. + * @return the Date representing the start of the day of the input date. + */ + protected Date startOfDay(Date date) { + return CalendarUtils.startOfDay(calendar, date); + } + + /** + * Returns the end of the day of the given date in this model's calendar. + * NOTE: the calendar is changed by this operation. + * + * @param date the Date to get the start for. + * @return the Date representing the end of the day of the input date. + */ + protected Date endOfDay(Date date) { + return CalendarUtils.endOfDay(calendar, date); + } + + /** + * Returns a boolean indicating whether the given dates are on the same day in + * the coordinates of the model's calendar. + * + * @param selected one of the dates to check, must not be null. + * @param compare the other of the dates to check, must not be null. + * @return true if both dates represent the same day in this model's calendar. + */ + protected boolean isSameDay(Date selected, Date compare) { + return startOfDay(selected).equals(startOfDay(compare)); + } + +//------------------- bounds + + /** + * {@inheritDoc} + */ + @Override + public Date getUpperBound() { + return upperBound; + } + + /** + * {@inheritDoc} + */ + @Override + public void setUpperBound(Date upperBound) { + if (upperBound != null) { + upperBound = getNormalizedDate(upperBound); + } + if (CalendarUtils.areEqual(upperBound, getUpperBound())) + return; + this.upperBound = upperBound; + if (this.upperBound != null && !isSelectionEmpty()) { + long justAboveUpperBoundMs = this.upperBound.getTime() + 1; + removeSelectionInterval(new Date(justAboveUpperBoundMs), + getLastSelectionDate()); + } + fireValueChanged(EventType.UPPER_BOUND_CHANGED); + } + + /** + * {@inheritDoc} + */ + @Override + public Date getLowerBound() { + return lowerBound; + } + + /** + * {@inheritDoc} + */ + @Override + public void setLowerBound(Date lowerBound) { + if (lowerBound != null) { + lowerBound = getNormalizedDate(lowerBound); + } + if (CalendarUtils.areEqual(lowerBound, getLowerBound())) + return; + this.lowerBound = lowerBound; + if (this.lowerBound != null && !isSelectionEmpty()) { + // Remove anything below the lower bound + long justBelowLowerBoundMs = this.lowerBound.getTime() - 1; + removeSelectionInterval(getFirstSelectionDate(), new Date( + justBelowLowerBoundMs)); + } + fireValueChanged(EventType.LOWER_BOUND_CHANGED); + } + + + /** + * {@inheritDoc} + */ + @Override + public boolean isAdjusting() { + return adjusting; + } + + /** + * {@inheritDoc} + */ + @Override + public void setAdjusting(boolean adjusting) { + if (adjusting == isAdjusting()) return; + this.adjusting = adjusting; + fireValueChanged(adjusting ? EventType.ADJUSTING_STARTED : EventType.ADJUSTING_STOPPED); + + } + +//----------------- notification + /** + * {@inheritDoc} + */ + @Override + public void addDateSelectionListener(DateSelectionListener l) { + listenerMap.add(DateSelectionListener.class, l); + } + + /** + * {@inheritDoc} + */ + @Override + public void removeDateSelectionListener(DateSelectionListener l) { + listenerMap.remove(DateSelectionListener.class, l); + } + + public List getDateSelectionListeners() { + return listenerMap.getListeners(DateSelectionListener.class); + } + + protected void fireValueChanged(EventType eventType) { + List listeners = getDateSelectionListeners(); + DateSelectionEvent e = null; + + for (DateSelectionListener listener : listeners) { + if (e == null) { + e = new DateSelectionEvent(this, eventType, isAdjusting()); + } + listener.valueChanged(e); + } + } + + + +} diff --git a/src/main/java/org/jdesktop/swingx/calendar/CalendarUtils.java b/src/main/java/org/jdesktop/swingx/calendar/CalendarUtils.java new file mode 100644 index 0000000000..baf944f3b1 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/calendar/CalendarUtils.java @@ -0,0 +1,649 @@ +/* + * $Id: CalendarUtils.java 3916 2011-01-12 10:21:58Z kleopatra $ + * + * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.calendar; + +import java.util.Calendar; +import java.util.Date; + +/** + * Calendar manipulation. + * + * PENDING: replace by something tested - as is c&p'ed dateUtils + * to work on a calendar instead of using long + * + * @author Jeanette Winzenburg + */ +public class CalendarUtils { + + // Constants used internally; unit is milliseconds + @SuppressWarnings("unused") + public static final int ONE_MINUTE = 60*1000; + @SuppressWarnings("unused") + public static final int ONE_HOUR = 60*ONE_MINUTE; + @SuppressWarnings("unused") + public static final int THREE_HOURS = 3 * ONE_HOUR; + @SuppressWarnings("unused") + public static final int ONE_DAY = 24*ONE_HOUR; + + public static final int DECADE = 5467; + public static final int YEAR_IN_DECADE = DECADE + 1; + + /** + * Increments the calendar field of the given calendar by amount. + * + * @param calendar + * @param field the field to increment, allowed are all fields known to + * Calendar plus DECADE. + * @param amount + * + * @throws IllegalArgumentException + */ + public static void add(Calendar calendar, int field, int amount) { + if (isNativeField(field)) { + calendar.add(field, amount); + } else { + switch (field) { + case DECADE: + calendar.add(Calendar.YEAR, amount * 10); + break; + default: + throw new IllegalArgumentException("unsupported field: " + field); + } + + } + } + + /** + * Gets the calendar field of the given calendar by amount. + * + * @param calendar + * @param field the field to get, allowed are all fields known to + * Calendar plus DECADE. + * + * @throws IllegalArgumentException + */ + public static int get(Calendar calendar, int field) { + if (isNativeField(field)) { + return calendar.get(field); + } + switch (field) { + case DECADE: + return decade(calendar.get(Calendar.YEAR)); + case YEAR_IN_DECADE: + return calendar.get(Calendar.YEAR) % 10; + default: + throw new IllegalArgumentException("unsupported field: " + field); + } + } + + /** + * Sets the calendar field of the given calendar by amount.

      + * + * NOTE: the custom field implementations are very naive (JSR-310 will do better) + * - for decade: value must be positive, value must be a multiple of 10 and is interpreted as the + * first-year-of-the-decade + * - for year-in-decade: value is added/substracted to/from the start-of-decade of the + * date of the given calendar + * + * @param calendar + * @param field the field to increment, allowed are all fields known to + * Calendar plus DECADE. + * @param value the decade to set, must be a + * + * @throws IllegalArgumentException if the field is unsupported or the value is + * not dividable by 10 or negative. + */ + public static void set(Calendar calendar, int field, int value) { + if (isNativeField(field)) { + calendar.set(field, value); + } else { + switch (field) { + case DECADE: + if(value <= 0 ) { + throw new IllegalArgumentException("value must be a positive but was: " + value); + } + if (value % 10 != 0) { + throw new IllegalArgumentException("value must be a multiple of 10 but was: " + value); + } + int yearInDecade = get(calendar, YEAR_IN_DECADE); + calendar.set(Calendar.YEAR, value + yearInDecade); + break; + case YEAR_IN_DECADE: + int decade = get(calendar, DECADE); + calendar.set(Calendar.YEAR, value + decade); + break; + default: + throw new IllegalArgumentException("unsupported field: " + field); + } + + } + } + + /** + * @param calendarField + * @return + */ + private static boolean isNativeField(int calendarField) { + return calendarField < DECADE; + } + + /** + * Adjusts the Calendar to the end of the day of the last day in DST in the + * current year or unchanged if not using DST. Returns the calendar's date or null, if not + * using DST.

      + * + * + * @param calendar the calendar to adjust + * @return the end of day of the last day in DST, or null if not using DST. + */ + public static Date getEndOfDST(Calendar calendar) { + if (!calendar.getTimeZone().useDaylightTime()) return null; + long old = calendar.getTimeInMillis(); + calendar.set(Calendar.MONTH, Calendar.DECEMBER); + endOfMonth(calendar); + startOfDay(calendar); + for (int i = 0; i < 366; i++) { + calendar.add(Calendar.DATE, -1); + if (calendar.getTimeZone().inDaylightTime(calendar.getTime())) { + endOfDay(calendar); + return calendar.getTime(); + } + } + calendar.setTimeInMillis(old); + return null; + } + + /** + * Adjusts the Calendar to the end of the day of the first day in DST in the + * current year or unchanged if not using DST. Returns the calendar's date or null, if not + * using DST.

      + * + * Note: the start of the day of the first day in DST is ill-defined! + * + * @param calendar the calendar to adjust + * @return the start of day of the first day in DST, or null if not using DST. + */ + public static Date getStartOfDST(Calendar calendar) { + if (!calendar.getTimeZone().useDaylightTime()) return null; + long old = calendar.getTimeInMillis(); + calendar.set(Calendar.MONTH, Calendar.JANUARY); + startOfMonth(calendar); + endOfDay(calendar); + for (int i = 0; i < 366; i++) { + calendar.add(Calendar.DATE, 1); + if (calendar.getTimeZone().inDaylightTime(calendar.getTime())) { + endOfDay(calendar); + return calendar.getTime(); + } + } + calendar.setTimeInMillis(old); + return null; + } + + /** + * Returns a boolean indicating if the given calendar represents the + * start of a day (in the calendar's time zone). The calendar is unchanged. + * + * @param calendar the calendar to check. + * + * @return true if the calendar's time is the start of the day, + * false otherwise. + */ + public static boolean isStartOfDay(Calendar calendar) { + Calendar temp = (Calendar) calendar.clone(); + temp.add(Calendar.MILLISECOND, -1); + return temp.get(Calendar.DATE) != calendar.get(Calendar.DATE); + } + + /** + * Returns a boolean indicating if the given calendar represents the + * end of a day (in the calendar's time zone). The calendar is unchanged. + * + * @param calendar the calendar to check. + * + * @return true if the calendar's time is the end of the day, + * false otherwise. + */ + public static boolean isEndOfDay(Calendar calendar) { + Calendar temp = (Calendar) calendar.clone(); + temp.add(Calendar.MILLISECOND, 1); + return temp.get(Calendar.DATE) != calendar.get(Calendar.DATE); + } + + /** + * Returns a boolean indicating if the given calendar represents the + * start of a month (in the calendar's time zone). Returns true, if the time is + * the start of the first day of the month, false otherwise. The calendar is unchanged. + * + * @param calendar the calendar to check. + * + * @return true if the calendar's time is the start of the first day of the month, + * false otherwise. + */ + public static boolean isStartOfMonth(Calendar calendar) { + Calendar temp = (Calendar) calendar.clone(); + temp.add(Calendar.MILLISECOND, -1); + return temp.get(Calendar.MONTH) != calendar.get(Calendar.MONTH); + } + + /** + * Returns a boolean indicating if the given calendar represents the + * end of a month (in the calendar's time zone). Returns true, if the time is + * the end of the last day of the month, false otherwise. The calendar is unchanged. + * + * @param calendar the calendar to check. + * + * @return true if the calendar's time is the end of the last day of the month, + * false otherwise. + */ + public static boolean isEndOfMonth(Calendar calendar) { + Calendar temp = (Calendar) calendar.clone(); + temp.add(Calendar.MILLISECOND, 1); + return temp.get(Calendar.MONTH) != calendar.get(Calendar.MONTH); + } + + /** + * Returns a boolean indicating if the given calendar represents the + * start of a month (in the calendar's time zone). Returns true, if the time is + * the start of the first day of the month, false otherwise. The calendar is unchanged. + * + * @param calendar the calendar to check. + * + * @return true if the calendar's time is the start of the first day of the month, + * false otherwise. + */ + public static boolean isStartOfWeek(Calendar calendar) { + Calendar temp = (Calendar) calendar.clone(); + temp.add(Calendar.MILLISECOND, -1); + return temp.get(Calendar.WEEK_OF_YEAR) != calendar.get(Calendar.WEEK_OF_YEAR); + } + + /** + * Returns a boolean indicating if the given calendar represents the + * end of a week (in the calendar's time zone). Returns true, if the time is + * the end of the last day of the week, false otherwise. The calendar is unchanged. + * + * @param calendar the calendar to check. + * + * @return true if the calendar's time is the end of the last day of the week, + * false otherwise. + */ + public static boolean isEndOfWeek(Calendar calendar) { + Calendar temp = (Calendar) calendar.clone(); + temp.add(Calendar.MILLISECOND, 1); + return temp.get(Calendar.WEEK_OF_YEAR) != calendar.get(Calendar.WEEK_OF_YEAR); + } + + /** + * Adjusts the calendar to the start of the current week. + * That is, first day of the week with all time fields cleared. + * @param calendar the calendar to adjust. + * @return the Date the calendar is set to + */ + public static void startOfWeek(Calendar calendar) { + calendar.set(Calendar.DAY_OF_WEEK, calendar.getFirstDayOfWeek()); + startOfDay(calendar); + } + + /** + * Adjusts the calendar to the end of the current week. + * That is, last day of the week with all time fields at max. + * @param calendar the calendar to adjust. + */ + public static void endOfWeek(Calendar calendar) { + startOfWeek(calendar); + calendar.add(Calendar.DATE, 7); + calendar.add(Calendar.MILLISECOND, -1); + } + + /** + * Adjusts the calendar to the end of the current week. + * That is, last day of the week with all time fields at max. + * The Date of the adjusted Calendar is + * returned. + * + * @param calendar calendar to adjust. + * @param date the Date to use. + * @return the end of the week of the given date + */ + public static Date endOfWeek(Calendar calendar, Date date) { + calendar.setTime(date); + endOfWeek(calendar); + return calendar.getTime(); + } + + /** + * Adjusts the calendar to the start of the current week. + * That is, last day of the week with all time fields at max. + * The Date of the adjusted Calendar is + * returned. + * + * @param calendar calendar to adjust. + * @param date the Date to use. + * @return the start of the week of the given date + */ + public static Date startOfWeek(Calendar calendar, Date date) { + calendar.setTime(date); + startOfWeek(calendar); + return calendar.getTime(); + } + + /** + * Adjusts the given Calendar to the start of the decade. + * + * @param calendar the calendar to adjust. + */ + public static void startOfDecade(Calendar calendar) { + calendar.set(Calendar.YEAR, decade(calendar.get(Calendar.YEAR)) ); + startOfYear(calendar); + } + + /** + * @param year + * @return + */ + private static int decade(int year) { + return (year / 10) * 10; + } + + /** + * Adjusts the given Calendar to the start of the decade as defined by + * the given date. Returns the calendar's Date. + * + * @param calendar calendar to adjust. + * @param date the Date to use. + * @return the start of the decade of the given date + */ + public static Date startOfDecade(Calendar calendar, Date date) { + calendar.setTime(date); + startOfDecade(calendar); + return calendar.getTime(); + } + + /** + * Returns a boolean indicating if the given calendar represents the + * start of a decade (in the calendar's time zone). Returns true, if the time is + * the start of the first day of the decade, false otherwise. The calendar is unchanged. + * + * @param calendar the calendar to check. + * + * @return true if the calendar's time is the start of the first day of the month, + * false otherwise. + */ + public static boolean isStartOfDecade(Calendar calendar) { + Calendar temp = (Calendar) calendar.clone(); + temp.add(Calendar.MILLISECOND, -1); + return decade(temp.get(Calendar.YEAR)) != decade(calendar.get(Calendar.YEAR)); + } + + /** + * Adjusts the given Calendar to the start of the year. + * + * @param calendar the calendar to adjust. + */ + public static void startOfYear(Calendar calendar) { + calendar.set(Calendar.MONTH, Calendar.JANUARY); + startOfMonth(calendar); + } + + /** + * Adjusts the given Calendar to the start of the year as defined by + * the given date. Returns the calendar's Date. + * + * @param calendar calendar to adjust. + * @param date the Date to use. + * @return the start of the year of the given date + */ + public static Date startOfYear(Calendar calendar, Date date) { + calendar.setTime(date); + startOfYear(calendar); + return calendar.getTime(); + } + + /** + * Returns a boolean indicating if the given calendar represents the + * start of a year (in the calendar's time zone). Returns true, if the time is + * the start of the first day of the year, false otherwise. The calendar is unchanged. + * + * @param calendar the calendar to check. + * + * @return true if the calendar's time is the start of the first day of the month, + * false otherwise. + */ + public static boolean isStartOfYear(Calendar calendar) { + Calendar temp = (Calendar) calendar.clone(); + temp.add(Calendar.MILLISECOND, -1); + return temp.get(Calendar.YEAR) != calendar.get(Calendar.YEAR); + } + + /** + * Adjusts the calendar to the start of the current month. + * That is, first day of the month with all time fields cleared. + * + * @param calendar calendar to adjust. + */ + public static void startOfMonth(Calendar calendar) { + calendar.set(Calendar.DAY_OF_MONTH, 1); + startOfDay(calendar); + } + + /** + * Adjusts the calendar to the end of the current month. + * That is the last day of the month with all time-fields + * at max. + * + * @param calendar calendar to adjust. + */ + public static void endOfMonth(Calendar calendar) { + // start of next month + calendar.add(Calendar.MONTH, 1); + startOfMonth(calendar); + // one millisecond back + calendar.add(Calendar.MILLISECOND, -1); + } + + /** + * Adjust the given calendar to the first millisecond of the given date. + * that is all time fields cleared. The Date of the adjusted Calendar is + * returned. + * + * @param calendar calendar to adjust. + * @param date the Date to use. + * @return the start of the day of the given date + */ + public static Date startOfDay(Calendar calendar, Date date) { + calendar.setTime(date); + startOfDay(calendar); + return calendar.getTime(); + } + + /** + * Adjust the given calendar to the last millisecond of the given date. + * that is all time fields cleared. The Date of the adjusted Calendar is + * returned. + * + * @param calendar calendar to adjust. + * @param date the Date to use. + * @return the end of the day of the given date + */ + public static Date endOfDay(Calendar calendar, Date date) { + calendar.setTime(date); + endOfDay(calendar); + return calendar.getTime(); + } + + /** + * Adjust the given calendar to the first millisecond of the current day. + * that is all time fields cleared. + * + * @param calendar calendar to adjust. + */ + public static void startOfDay(Calendar calendar) { + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MILLISECOND, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.getTimeInMillis(); + } + + /** + * Adjust the given calendar to the last millisecond of the specified date. + * + * @param calendar calendar to adjust. + */ + public static void endOfDay(Calendar calendar) { + calendar.add(Calendar.DATE, 1); + startOfDay(calendar); + calendar.add(Calendar.MILLISECOND, -1); + } + + /** + * Adjusts the given calendar to the start of the period as indicated by the + * given field. This delegates to startOfDay, -Week, -Month, -Year as appropriate. + * + * @param calendar + * @param field the period to adjust, allowed are Calendar.DAY_OF_MONTH, -.MONTH, + * -.WEEK and YEAR and CalendarUtils.DECADE. + */ + public static void startOf(Calendar calendar, int field) { + switch (field) { + case Calendar.DAY_OF_MONTH: + startOfDay(calendar); + break; + case Calendar.MONTH: + startOfMonth(calendar); + break; + case Calendar.WEEK_OF_YEAR: + startOfWeek(calendar); + break; + case Calendar.YEAR: + startOfYear(calendar); + break; + case DECADE: + startOfDecade(calendar); + break; + default: + throw new IllegalArgumentException("unsupported field: " + field); + + } + } + + /** + * Returns a boolean indicating if the calendar is set to the start of a + * period as defined by the + * given field. This delegates to startOfDay, -Week, -Month, -Year as appropriate. + * The calendar is unchanged. + * + * @param calendar + * @param field the period to adjust, allowed are Calendar.DAY_OF_MONTH, -.MONTH, + * -.WEEK and YEAR and CalendarUtils.DECADE. + * @throws IllegalArgumentException if the field is not supported. + */ + public static boolean isStartOf(Calendar calendar, int field) { + switch (field) { + case Calendar.DAY_OF_MONTH: + return isStartOfDay(calendar); + case Calendar.MONTH: + return isStartOfMonth(calendar); + case Calendar.WEEK_OF_YEAR: + return isStartOfWeek(calendar); + case Calendar.YEAR: + return isStartOfYear(calendar); + case DECADE: + return isStartOfDecade(calendar); + default: + throw new IllegalArgumentException("unsupported field: " + field); + } + } + + /** + * Checks the given dates for being equal. + * + * @param current one of the dates to compare + * @param date the otherr of the dates to compare + * @return true if the two given dates both are null or both are not null and equal, + * false otherwise. + */ + public static boolean areEqual(Date current, Date date) { + if ((date == null) && (current == null)) { + return true; + } + if (date != null) { + return date.equals(current); + } + return false; + } + + /** + * Returns a boolean indicating whether the given Date is the same day as + * the day in the calendar. Calendar and date are unchanged by the check. + * + * @param today the Calendar representing a date, must not be null. + * @param now the date to compare to, must not be null + * @return true if the calendar and date represent the same day in the + * given calendar. + */ + public static boolean isSameDay(Calendar today, Date now) { + Calendar temp = (Calendar) today.clone(); + startOfDay(temp); + Date start = temp.getTime(); + temp.setTime(now); + startOfDay(temp); + return start.equals(temp.getTime()); + } + + /** + * Returns a boolean indicating whether the given Date is in the same period as + * the Date in the calendar, as defined by the calendar field. + * Calendar and date are unchanged by the check. + * + * @param today the Calendar representing a date, must not be null. + * @param now the date to compare to, must not be null + * @return true if the calendar and date represent the same day in the + * given calendar. + */ + public static boolean isSame(Calendar today, Date now, int field) { + Calendar temp = (Calendar) today.clone(); + startOf(temp, field); + Date start = temp.getTime(); + temp.setTime(now); + startOf(temp, field); + return start.equals(temp.getTime()); + } + + /** + * Returns a boolean to indicate whether the given calendar is flushed.

      + * + * The only way to guarantee a flushed state is to let client code call + * getTime or getTimeInMillis. See + * + * Despairing + * in Calendar + *

      + * Note: this if for testing only and not entirely safe! + * + * @param calendar + * @return + */ + public static boolean isFlushed(Calendar calendar) { + return !calendar.toString().contains("time=?"); + } +} diff --git a/src/main/java/org/jdesktop/swingx/calendar/DatePickerFormatter.java b/src/main/java/org/jdesktop/swingx/calendar/DatePickerFormatter.java new file mode 100644 index 0000000000..7991b1e683 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/calendar/DatePickerFormatter.java @@ -0,0 +1,248 @@ +/* + * $Id: DatePickerFormatter.java 3140 2008-12-16 15:09:09Z kleopatra $ + * + * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.calendar; + +import org.jdesktop.swingx.plaf.UIManagerExt; +import org.jdesktop.swingx.util.Contract; + +import javax.swing.*; +import javax.swing.plaf.UIResource; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.logging.Logger; + +/** + * Default formatter for the JXDatePicker component. + * It can handle a variety of date formats. + * + * @author Joshua Outwater + */ +public class DatePickerFormatter extends + JFormattedTextField.AbstractFormatter { + + private static final Logger LOG = Logger + .getLogger(DatePickerFormatter.class.getName()); + private DateFormat _formats[] = null; + + + /** + * Instantiates a formatter with the localized format patterns defined + * in the swingx.properties. + * + * These formats are localizable and fields may be re-arranged, such as + * swapping the month and day fields. The keys for localizing these fields + * are: + *

        + *
      • JXDatePicker.longFormat + *
      • JXDatePicker.mediumFormat + *
      • JXDatePicker.shortFormat + *
      + * + */ + public DatePickerFormatter() { + this(null, null); + } + + /** + * Instantiates a formatter with the given date formats. If the + * array is null, default formats are created from the localized + * patterns in swingx.properties. If empty? + * + * @param formats the array of formats to use. May be null to + * use defaults or empty to do nothing (?), but must not contain + * null formats. + */ + public DatePickerFormatter(DateFormat formats[]) { + this(formats, null); + } + + /** + * Instantiates a formatter with default date formats in the + * given locale. The default formats are created from the localized + * patterns in swingx.properties. + * + * @param locale the Locale the use for the default formats. + */ + public DatePickerFormatter(Locale locale) { + this(null, locale); + } + + /** + * Instantiates a formatter with the given formats and locale. + * + * PENDING JW: makes no sense as a public constructor because the locale is ignored + * if the formats are null. So has same public behaviour as the constructor with + * formats only ... + * + * @param formats + * @param locale + */ + public DatePickerFormatter(DateFormat formats[], Locale locale) { +// super(); + if (locale == null) { + locale = Locale.getDefault(); + } + if (formats == null) { + formats = createDefaultFormats(locale); + } + Contract.asNotNull(formats, "The array of DateFormats must not contain null formats"); + _formats = formats; + } + + /** + * Returns an array of the formats used by this formatter. + * + * @return the formats used by this formatter, guaranteed to be + * not null. + */ + public DateFormat[] getFormats() { + DateFormat[] results = new DateFormat[_formats.length]; + System.arraycopy(_formats, 0, results, 0, results.length); + return results; + } + + /** + * {@inheritDoc} + */ + @Override + public Object stringToValue(String text) throws ParseException { + Object result = null; + ParseException pex = null; + + if (text == null || text.trim().length() == 0) { + return null; + } + + // If the current formatter did not work loop through the other + // formatters and see if any of them can parse the string passed + // in. + for (DateFormat _format : _formats) { + try { + result = (_format).parse(text); + pex = null; + break; + } catch (ParseException ex) { + pex = ex; + } + } + + if (pex != null) { + throw pex; + } + + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public String valueToString(Object value) throws ParseException { + if ((value != null) && (_formats.length > 0)){ + return _formats[0].format(value); + } + return null; + } + + /** + * Creates and returns the localized default formats. First tries to + * add formats created using the patterns stored in the UIManager. If + * there are no patterns, use the DateFormat's instance with style + * DateFormat.SHORT. + * + * @return the localized default formats. + */ + protected DateFormat[] createDefaultFormats(Locale locale) { + List f = new ArrayList(); + addFormat(f, "JXDatePicker.longFormat", locale); + addFormat(f, "JXDatePicker.mediumFormat", locale); + addFormat(f, "JXDatePicker.shortFormat", locale); + if (f.size() == 0) { + addSystemDefaultFormat(f, locale); + } + return f.toArray(new DateFormat[f.size()]); + } + + /** + * Adds the system's default DateFormat. This implementation adds a + * dateInstance of style DateFormat.SHORT. + * + * @param f the List of formats to add to + * @param locale the Locale to use for the formatter. + */ + private void addSystemDefaultFormat(List f, Locale locale) { + f.add(DateFormat.getDateInstance(DateFormat.SHORT, locale)); + } + + /** + * Creates and adds a DateFormat to the given list. Looks up + * a format pattern registered in the UIManager for the given + * key and tries to create a SimpleDateFormat. Does nothing + * if there is no format pattern registered or the pattern is + * invalid. + * + * @param f the list of formats + * @param key the key for getting the pattern from the UI + */ + private void addFormat(List f, String key, Locale locale) { + String pattern = UIManagerExt.getString(key, locale); + if (pattern == null) return; + try { + SimpleDateFormat format = new SimpleDateFormat(pattern, locale); + f.add(format); + } catch (RuntimeException e) { + // format string not available or invalid + LOG.finer("creating date format failed for key/pattern: " + key + "/" + pattern); + } + } + + /** + * + * Same as DatePickerFormatter, but tagged as UIResource. + * + * @author Jeanette Winzenburg + */ + public static class DatePickerFormatterUIResource extends DatePickerFormatter + implements UIResource { + + /** + * @param locale + */ + public DatePickerFormatterUIResource(Locale locale) { + super(locale); + } + + /** + * + */ + public DatePickerFormatterUIResource() { + this(null); + } + + public DatePickerFormatterUIResource(DateFormat[] formats, Locale locale) { + super(formats, locale); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/calendar/DateSelectionModel.java b/src/main/java/org/jdesktop/swingx/calendar/DateSelectionModel.java new file mode 100644 index 0000000000..82d122ea60 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/calendar/DateSelectionModel.java @@ -0,0 +1,374 @@ +/* + * $Id: DateSelectionModel.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.calendar; + +import org.jdesktop.swingx.event.DateSelectionListener; + +import java.util.*; + + +/** + * The Model used by calendar components. It controls the Calendar to use and + * keeps selection-related state. + * + * @author Joshua Outwater + */ +public interface DateSelectionModel { + public static enum SelectionMode { + /** + * Mode that allows for selection of a single day. + */ + SINGLE_SELECTION, + /** + * Mode that allows for selecting of multiple consecutive days. + */ + SINGLE_INTERVAL_SELECTION, + /** + * Mode that allows for selecting disjoint days. + */ + MULTIPLE_INTERVAL_SELECTION + } + +//---------------------- mode + /** + * Get the selection mode. + * + * @return return the current selection mode + */ + public SelectionMode getSelectionMode(); + + /** + * Set the selection mode. + * + * @param mode new selection mode + */ + public void setSelectionMode(final SelectionMode mode); + + +//-------------------- calendar + /** + * Returns a clone of the calendar used by this model. It's date is unspecified. + * + * @return a clone of the calendar used by this model. + */ + public Calendar getCalendar(); + + /** + * Gets what the first day of the week is; e.g., + * Calendar.SUNDAY in the U.S., Calendar.MONDAY + * in France. This is needed when the model selection mode is + * WEEK_INTERVAL_SELECTION. + * + * PENDING JW: move week-interval selection from JXMonthView into the model. + * + * @return int The first day of the week. + * @see #setFirstDayOfWeek(int) + */ + public int getFirstDayOfWeek(); + + /** + * Sets what the first day of the week is. E.g., + * Calendar.SUNDAY in US, Calendar.MONDAY + * in France. Fires a DateSelectionEvent of type CALENDAR_CHANGED, if the + * value is different from the old.

      + * + * The default value depends on the Calendar's default. + * + * PENDING JW: actually, it's a bound property. Use a propertyChangeListener? + * + * @param firstDayOfWeek The first day of the week. + * @see #getFirstDayOfWeek() + * @see Calendar + */ + public void setFirstDayOfWeek(final int firstDayOfWeek); + + + /** + * Gets the minimal number of days in the first week of the year. + * + * @return int the minimal number of days in the first week of the year. + */ + public int getMinimalDaysInFirstWeek(); + + /** + * Sets the minimal number of days in the first week of the year. + * Fires a DateSelectionEvent of type CALENDAR_CHANGED, if the + * value is different from the old. + * + * The default value depends on the Calendar's default. + * + * PENDING JW: actually, it's a bound property. Use a propertyChangeListener? + * + * @param minimalDays the minimal number of days in the first week of the year. + * @see #getMinimalDaysInFirstWeek() + * @see Calendar + */ + public void setMinimalDaysInFirstWeek(final int minimalDays); + + /** + * Returns the TimeZone of this model. + * + * @return the TimeZone of this model. + * @see #setTimeZone(TimeZone) + */ + public TimeZone getTimeZone(); + + + /** + * Sets the TimeZone of this model. Fires a DateSelectionEvent of type + * CALENDAR_CHANGED if the new value is different from the old. + * + * The default value depends on the Calendar's default. + * + * PENDING JW: actually, it's a bound property. Use a propertyChangeListener? + * + * @param timeZone the TimeZone to use in this model, must not be null. + * @see #getTimeZone() + */ + public void setTimeZone(TimeZone timeZone); + + /** + * Returns the Locale of this model's calendar. + * @return the Locale of this model's calendar. + */ + public Locale getLocale(); + + /** + * Sets the Locale of this model's calendar. Fires a DateSelectionEvent of type + * CALENDAR_CHANGED if the new value is different from the old.

      + * + * The default value is Locale.default().

      + * + * PENDING JW: fall back to JComponent.getDefaultLocale instead? We use this + * with components anyway?

      + * PENDING JW: actually, it's a bound property. Use a propertyChangeListener? + * + * @param locale the Locale to use. If null, the default Locale is used. + */ + public void setLocale(Locale locale); + + //-------------------- selection + + /** + * Adds the specified selection interval to the selection model. + * + * @param startDate interval start date, must not be null + * @param endDate interval end date >= start date, must not be null + * @throws NullPointerException if any of the dates is null + */ + public void addSelectionInterval(Date startDate, Date endDate); + + /** + * Sest the specified selection interval to the selection model. + * + * @param startDate interval start date, must not be null + * @param endDate interval end date >= start date, must not be null + * @throws NullPointerException if any of the dates is null + */ + public void setSelectionInterval(Date startDate, Date endDate); + + /** + * Removes the specifed selection interval from the selection model. If + * the selection is changed by this method, it fires a DateSelectionEvent + * of type DATES_REMOVED. + * + * @param startDate interval start date, must not be null + * @param endDate interval end date >= start date, must not be null + * @throws NullPointerException if any of the dates is null + */ + public void removeSelectionInterval(Date startDate, Date endDate); + + /** + * Clears any selection from the selection model. Fires an Event of + * type SELECTION_CLEARED if there had been a selection, does nothing + * otherwise. + */ + public void clearSelection(); + + /** + * Returns the current selection. + * + * @return sorted set of selected dates, guaranteed to be never null. + */ + public SortedSet getSelection(); + + /** + * Returns the earliest date in the selection or null if the selection is empty. + * + * @return the earliest date in the selection, or null if isSelectionEmpty. + * + * @see #getLastSelectionDate() + * @see #getSelection() + * @see #isSelectionEmpty() + */ + public Date getFirstSelectionDate(); + + /** + * Returns the latest date in the selection or null if the selection is empty. + * + * @return the lastest date in the selection, or null if isSelectionEmpty. + * + * @see #getFirstSelectionDate() + * @see #getSelection() + * @see #isSelectionEmpty() + */ + public Date getLastSelectionDate(); + + + /** + * Returns true if the date specified is selected, false otherwise.

      + * + * Note: it is up to implementations to define the exact notion of selected. + * It does not imply the exact date as given is contained the set returned from + * getSelection(). + * + * @param date date to check for selection, must not be null + * @return true if the date is selected, false otherwise + * @throws NullPointerException if the date is null + */ + public boolean isSelected(final Date date); + + /** + * Returns a normalized Date as used by the implementation, if any. F.i. + * DaySelectionModel returns the start of the day in the model's calendar. + * If no normalization is applied, a clone of the Date itself is returned. + * The given Date is never changed. + *

      + * + * The overall contract: + * + *

      
      +     * if ((date != null) && isSelectable(date)) {
      +     *     setSelectionInterval(date, date);
      +     *     assertEquals(getNormalized(date), getFirstSelectionDate();
      +     * }
      +     * 
      + * + * + * @return the date as it would be normalized before used in the model, + * must not be null. + * @throws NullPointerException if given date is null. + */ + public Date getNormalizedDate(Date date); + + /** + * Returns true if the selection is empty, false otherwise. + * + * @return true if the selection is empty, false otherwise + */ + public boolean isSelectionEmpty(); + + + /** + * Returns a SortedSet of Dates that are unselectable. + * + * @return sorted set of dates + */ + public SortedSet getUnselectableDates(); + + /** + * Sets a collection of dates which are not selectable.

      + * + * Note: it is up to implementations to define the exact notion of unselectableDate. + * It does not imply the only the exact date as given is unselectable, it might + * have a period like "all dates on the same day". + * + * PENDING JW: any collection would do - why insist on a SortedSet? + * + * @param unselectableDates dates that are unselectable, must not be null and + * must not contain null dates. + */ + public void setUnselectableDates(SortedSet unselectableDates); + + /** + * Returns true is the specified date is unselectable. + * + * @param unselectableDate the date to check for unselectability, must not be null. + * @return true is the date is unselectable, false otherwise + */ + public boolean isUnselectableDate(Date unselectableDate); + + /** + * Return the upper bound date that is allowed to be selected for this + * model. + * + * @return upper bound date or null if not set + */ + public Date getUpperBound(); + + /** + * Set the upper bound date that is allowed to be selected for this model. + * + * @param upperBound upper bound + */ + public void setUpperBound(final Date upperBound); + + /** + * Return the lower bound date that is allowed to be selected for this + * model. + * + * @return lower bound date or null if not set + */ + public Date getLowerBound(); + + /** + * Set the lower bound date that is allowed to be selected for this model. + * + * @param lowerBound lower bound date or null if not set + */ + public void setLowerBound(final Date lowerBound); + + /** + * Set the property to mark upcoming selections as intermediate/ + * final. This will fire a event of type adjusting_start/stop. + * + * The default value is false. + * + * Note: Client code marking as intermediate must take care of + * finalizing again. + * + * @param adjusting a flag to turn the adjusting property on/off. + */ + public void setAdjusting(boolean adjusting); + + /** + * Returns the property to decide whether the selection is + * intermediate or final. + * + * @return the adjusting property. + */ + public boolean isAdjusting(); + + /** + * Add the specified listener to this model. + * + * @param listener listener to add to this model + */ + public void addDateSelectionListener(DateSelectionListener listener); + + /** + * Remove the specified listener to this model. + * + * @param listener listener to remove from this model + */ + public void removeDateSelectionListener(DateSelectionListener listener); + +} diff --git a/src/main/java/org/jdesktop/swingx/calendar/DateSpan.java b/src/main/java/org/jdesktop/swingx/calendar/DateSpan.java new file mode 100644 index 0000000000..938afdfa00 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/calendar/DateSpan.java @@ -0,0 +1,221 @@ +/* + * $Id: DateSpan.java 542 2005-10-10 18:03:15Z rbair $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.calendar; + +import java.util.Date; + +/** + * An immutable representation of a time range. The time range is + * internally represented as two longs. The methods that take and return + * Dates create the Dates as needed, so that + * if you modify returned Dates you will not effect + * the DateSpan. The end points are inclusive. + * + * @version $Revision: 542 $ + */ +public class DateSpan { + private long _start; + private long _end; + + /** + * Creates a DateSpan between the two end points. + * + * @param start Beginning date + * @param end Ending date + * @throws IllegalArgumentException if start is after + * end + */ + public DateSpan(long start, long end) { + _start = start; + _end = end; + if (_start > _end) { + throw new IllegalArgumentException( + "Start date must be before end date"); + } + } + + /** + * Creates a DateSpan between the two end points. This + * is a conveniance constructor that is equivalent to + * new Date(start.getTime(), end.getTime());. + * + * @param start Beginning date + * @param end Ending date + */ + public DateSpan(Date start, Date end) { + this(start.getTime(), end.getTime()); + } + + /** + * Returns the start of the date span. + * + * @return start of the span. + */ + public long getStart() { + return _start; + } + + /** + * Returns the end of the date span. + * + * @return end of the span. + */ + public long getEnd() { + return _end; + } + + /** + * Returns the start of the date span as a Date. + * + * @return start of the span. + */ + public Date getStartAsDate() { + return new Date(getStart()); + } + + /** + * Returns the end of the date span as a Date. + * + * @return end of the span. + */ + public Date getEndAsDate() { + return new Date(getEnd()); + } + + /** + * Returns true if this DateSpan contains the specified + * DateSpan. + * + * @param span Date to check + * @return true if this DateSpan contains span. + */ + public boolean contains(DateSpan span) { + return (contains(span.getStart()) && contains(span.getEnd())); + } + + /** + * Returns whether or not this DateSpan contains the specified + * time. + * + * @param time time check + * @return true if this DateSpan contains time. + */ + public boolean contains(long time) { + return (time >= getStart() && time <= getEnd()); + } + + /** + * Returns whether or not this DateSpan contains the + * specified date span. + * + * @param start Start of time span + * @param end End of time + * @return true if this DateSpan contains the specified + * date span. + */ + public boolean contains(long start, long end) { + return (start >= getStart() && end <= getEnd()); + } + + /** + * Returns true if the this DateSpan intersects with the + * specified time. + * + * @param start Start time + * @param end End time + * @return true if this DateSpan intersects with the specified + * time. + */ + public boolean intersects(long start, long end) { + return (start <= getEnd() && end >= getStart()); + } + + /** + * Returns true if the this DateSpan intersects with the + * specified DateSpan. + * + * @param span DateSpan to compare to + * @return true if this DateSpan intersects with the specified + * time. + */ + public boolean intersects(DateSpan span) { + return intersects(span.getStart(), span.getEnd()); + } + + /** + * Returns a new DateSpan that is the union of this + * DateSpan and span. + * + * @param span DateSpan to add + * @return union of this DateSpan and span + */ + public DateSpan add(DateSpan span) { + return add(span.getStart(), span.getEnd()); + } + + /** + * Returns a new DateSpan that is the union of this + * DateSpan and the passed in span. + * + * @param start Start of region to add + * @param end End of region to end + * @return union of this DateSpan and start, end + */ + public DateSpan add(long start, long end) { + return new DateSpan(Math.min(start, getStart()), + Math.max(end, getEnd())); + } + + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (o instanceof DateSpan) { + DateSpan ds = (DateSpan)o; + return (_start == ds.getStart() && _end == ds.getEnd()); + } + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + int result = 17; + result = 37 * result + (int)(_start ^ (_start >>> 32)); + result = 37 * result + (int)(_end ^ (_end >>> 32)); + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "DateSpan [" + getStartAsDate() + "-" + getEndAsDate() + "]"; + } +} diff --git a/src/main/java/org/jdesktop/swingx/calendar/DateUtils.java b/src/main/java/org/jdesktop/swingx/calendar/DateUtils.java new file mode 100644 index 0000000000..61b36f37e9 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/calendar/DateUtils.java @@ -0,0 +1,405 @@ +/* + * $Id: DateUtils.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.calendar; + +import java.util.Calendar; +import java.util.Date; + +/** + * Utility methods for Date manipulation.

      + * + * This utility class is replaced by CalendarUtils because day related manipulation + * are meaningfull relative to a Calendar only. Always doing so against the default + * calendar instance isn't enough. + * + * PENDING JW: move the missing ops. Volunteers, please! Once done, this will be deprecated. + * + * @author Scott Violet + * @version $Revision: 3100 $ + * + */ +public class DateUtils { + private static Calendar CALENDAR = Calendar.getInstance(); + + /** + * Returns the last millisecond of the specified date. + * + * @param date Date to calculate end of day from + * @return Last millisecond of date + */ + public static Date endOfDay(Date date) { + Calendar calendar = CALENDAR; + synchronized(calendar) { + calendar.setTime(date); + calendar.set(Calendar.HOUR_OF_DAY, 23); + calendar.set(Calendar.MILLISECOND, 999); + calendar.set(Calendar.SECOND, 59); + calendar.set(Calendar.MINUTE, 59); + return calendar.getTime(); + } + } + + + /** + * Returns a new Date with the hours, milliseconds, seconds and minutes + * set to 0. + * + * @param date Date used in calculating start of day + * @return Start of date + */ + public static Date startOfDay(Date date) { + Calendar calendar = CALENDAR; + synchronized(calendar) { + calendar.setTime(date); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MILLISECOND, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MINUTE, 0); + return calendar.getTime(); + } + } + + /** + * Returns day in millis with the hours, milliseconds, seconds and minutes + * set to 0. + * + * @param date long used in calculating start of day + * @return Start of date + */ + public static long startOfDayInMillis(long date) { + Calendar calendar = CALENDAR; + synchronized(calendar) { + calendar.setTimeInMillis(date); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MILLISECOND, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MINUTE, 0); + return calendar.getTimeInMillis(); + } + } + + /** + * Returns the last millisecond of the specified date. + * + * @param date long to calculate end of day from + * @return Last millisecond of date + */ + public static long endOfDayInMillis(long date) { + Calendar calendar = CALENDAR; + synchronized(calendar) { + calendar.setTimeInMillis(date); + calendar.set(Calendar.HOUR_OF_DAY, 23); + calendar.set(Calendar.MILLISECOND, 999); + calendar.set(Calendar.SECOND, 59); + calendar.set(Calendar.MINUTE, 59); + return calendar.getTimeInMillis(); + } + } + + + /** + * Returns the day after date. + * + * @param date Date used in calculating next day + * @return Day after date. + */ + public static Date nextDay(Date date) { + return new Date(addDays(date.getTime(), 1)); + } + + /** + * Adds amount days to time and returns + * the resulting time. + * + * @param time Base time + * @param amount Amount of increment. + * + * @return the time + amount days + */ + public static long addDays(long time, int amount) { + Calendar calendar = CALENDAR; + synchronized(calendar) { + calendar.setTimeInMillis(time); + calendar.add(Calendar.DAY_OF_MONTH, amount); + return calendar.getTimeInMillis(); + } + } + + /** + * Returns the day after date. + * + * @param date Date used in calculating next day + * @return Day after date. + */ + public static long nextDay(long date) { + return addDays(date, 1); + } + + /** + * Returns the week after date. + * + * @param date Date used in calculating next week + * @return week after date. + */ + public static long nextWeek(long date) { + return addDays(date, 7); + } + + + /** + * Returns the number of days difference between t1 and + * t2. + * + * @param t1 Time 1 + * @param t2 Time 2 + * @param checkOverflow indicates whether to check for overflow + * @return Number of days between start and end + */ + public static int getDaysDiff(long t1, long t2, boolean checkOverflow) { + if (t1 > t2) { + long tmp = t1; + t1 = t2; + t2 = tmp; + } + Calendar calendar = CALENDAR; + synchronized(calendar) { + calendar.setTimeInMillis(t1); + int delta = 0; + while (calendar.getTimeInMillis() < t2) { + calendar.add(Calendar.DAY_OF_MONTH, 1); + delta++; + } + if (checkOverflow && (calendar.getTimeInMillis() > t2)) { + delta--; + } + return delta; + } + } + + /** + * Returns the number of days difference between t1 and + * t2. + * + * @param t1 Time 1 + * @param t2 Time 2 + * @return Number of days between start and end + */ + public static int getDaysDiff(long t1, long t2) { + return getDaysDiff(t1, t2, true); + } + + /** + * Check, whether the date passed in is the first day of the year. + * + * @param date date to check in millis + * @return true if date corresponds to the first + * day of a year + * @see Date#getTime() + */ + public static boolean isFirstOfYear(long date) { + boolean ret = false; + Calendar calendar = CALENDAR; + synchronized(calendar) { + calendar.setTimeInMillis(date); + int currentYear = calendar.get(Calendar.YEAR); + // Check yesterday + calendar.add(Calendar.DATE,-1); + int yesterdayYear = calendar.get(Calendar.YEAR); + ret = (currentYear != yesterdayYear); + } + return ret; + } + + /** + * Check, whether the date passed in is the first day of the month. + * + * @param date date to check in millis + * @return true if date corresponds to the first + * day of a month + * @see Date#getTime() + */ + public static boolean isFirstOfMonth(long date) { + boolean ret = false; + Calendar calendar = CALENDAR; + synchronized(calendar) { + calendar.setTimeInMillis(date); + int currentMonth = calendar.get(Calendar.MONTH); + // Check yesterday + calendar.add(Calendar.DATE,-1); + int yesterdayMonth = calendar.get(Calendar.MONTH); + ret = (currentMonth != yesterdayMonth); + } + return ret; + } + + + /** + * Returns the day before date. + * + * @param date Date used in calculating previous day + * @return Day before date. + */ + public static long previousDay(long date) { + return addDays(date, -1); + } + + /** + * Returns the week before date. + * + * @param date Date used in calculating previous week + * @return week before date. + */ + public static long previousWeek(long date) { + return addDays(date, -7); + } + + + /** + * Returns the first day before date that has the + * day of week matching startOfWeek. For example, if you + * want to find the previous monday relative to date you + * would call getPreviousDay(date, Calendar.MONDAY). + * + * @param date Base date + * @param startOfWeek Calendar constant correspoding to start of week. + * @return start of week, return value will have 0 hours, 0 minutes, + * 0 seconds and 0 ms. + * + */ + public static long getPreviousDay(long date, int startOfWeek) { + return getDay(date, startOfWeek, -1); + } + + /** + * Returns the first day after date that has the + * day of week matching startOfWeek. For example, if you + * want to find the next monday relative to date you + * would call getPreviousDay(date, Calendar.MONDAY). + * + * @param date Base date + * @param startOfWeek Calendar constant correspoding to start of week. + * @return start of week, return value will have 0 hours, 0 minutes, + * 0 seconds and 0 ms. + * + */ + public static long getNextDay(long date, int startOfWeek) { + return getDay(date, startOfWeek, 1); + } + + private static long getDay(long date, int startOfWeek, int increment) { + Calendar calendar = CALENDAR; + synchronized(calendar) { + calendar.setTimeInMillis(date); + int day = calendar.get(Calendar.DAY_OF_WEEK); + // Normalize the view starting date to a week starting day + while (day != startOfWeek) { + calendar.add(Calendar.DATE, increment); + day = calendar.get(Calendar.DAY_OF_WEEK); + } + return startOfDayInMillis(calendar.getTimeInMillis()); + } + } + + /** + * Returns the previous month. + * + * @param date Base date + * @return previous month + */ + public static long getPreviousMonth(long date) { + return incrementMonth(date, -1); + } + + /** + * Returns the next month. + * + * @param date Base date + * @return next month + */ + public static long getNextMonth(long date) { + return incrementMonth(date, 1); + } + + private static long incrementMonth(long date, int increment) { + Calendar calendar = CALENDAR; + synchronized(calendar) { + calendar.setTimeInMillis(date); + calendar.add(Calendar.MONTH, increment); + return calendar.getTimeInMillis(); + } + } + + /** + * Returns the date corresponding to the start of the month. + * + * @param date Base date + * @return Start of month. + */ + public static long getStartOfMonth(long date) { + return getMonth(date, -1); + } + + /** + * Returns the date corresponding to the end of the month. + * + * @param date Base date + * @return End of month. + */ + public static long getEndOfMonth(long date) { + return getMonth(date, 1); + } + + private static long getMonth(long date, int increment) { + long result; + Calendar calendar = CALENDAR; + synchronized(calendar) { + calendar.setTimeInMillis(date); + if (increment == -1) { + calendar.set(Calendar.DAY_OF_MONTH, 1); + result = startOfDayInMillis(calendar.getTimeInMillis()); + } else { + calendar.add(Calendar.MONTH, 1); + calendar.set(Calendar.DAY_OF_MONTH, 1); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MILLISECOND, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.add(Calendar.MILLISECOND, -1); + result = calendar.getTimeInMillis(); + } + } + return result; + } + + /** + * Returns the day of the week. + * + * @param date date + * @return day of week. + */ + public static int getDayOfWeek(long date) { + Calendar calendar = CALENDAR; + synchronized(calendar) { + calendar.setTimeInMillis(date); + return (calendar.get(Calendar.DAY_OF_WEEK)); + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/calendar/DaySelectionModel.java b/src/main/java/org/jdesktop/swingx/calendar/DaySelectionModel.java new file mode 100644 index 0000000000..82b9b53711 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/calendar/DaySelectionModel.java @@ -0,0 +1,296 @@ +/* + * $Id: DaySelectionModel.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.calendar; + +import org.jdesktop.swingx.event.DateSelectionEvent.EventType; +import org.jdesktop.swingx.event.EventListenerMap; +import org.jdesktop.swingx.util.Contract; + +import java.util.*; + +/** + * + * DaySelectionModel is a (temporary?) implementation of DateSelectionModel + * which normalizes all dates to the start of the day, that is zeroes all + * time fields. Responsibility extracted from JXMonthView (which must + * respect rules of model instead of trying to be clever itself). + * + * @author Joshua Outwater + */ +public class DaySelectionModel extends AbstractDateSelectionModel { + private SelectionMode selectionMode; + private SortedSet selectedDates; + private SortedSet unselectableDates; + + /** + * + */ + public DaySelectionModel() { + this(null); + } + + /** + * + */ + public DaySelectionModel(Locale locale) { + super(locale); + this.listenerMap = new EventListenerMap(); + this.selectionMode = SelectionMode.SINGLE_SELECTION; + this.selectedDates = new TreeSet(); + this.unselectableDates = new TreeSet(); + + } + /** + * + */ + @Override + public SelectionMode getSelectionMode() { + return selectionMode; + } + + /** + * + */ + @Override + public void setSelectionMode(final SelectionMode selectionMode) { + this.selectionMode = selectionMode; + clearSelection(); + } + + //---------------------- selection ops + /** + * {@inheritDoc} + */ + @Override + public void addSelectionInterval(Date startDate, Date endDate) { + if (startDate.after(endDate)) { + return; + } + startDate = startOfDay(startDate); + endDate = startOfDay(endDate); + boolean added = false; + switch (selectionMode) { + case SINGLE_SELECTION: + if (isSelected(startDate)) return; + clearSelectionImpl(); + added = addSelectionImpl(startDate, startDate); + break; + case SINGLE_INTERVAL_SELECTION: + if (isIntervalSelected(startDate, endDate)) return; + clearSelectionImpl(); + added = addSelectionImpl(startDate, endDate); + break; + case MULTIPLE_INTERVAL_SELECTION: + if (isIntervalSelected(startDate, endDate)) return; + added = addSelectionImpl(startDate, endDate); + break; + default: + break; + } + if (added) { + fireValueChanged(EventType.DATES_ADDED); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void setSelectionInterval(Date startDate, Date endDate) { + startDate = startOfDay(startDate); + endDate = startOfDay(endDate); + if (SelectionMode.SINGLE_SELECTION.equals(selectionMode)) { + if (isSelected(startDate)) return; + endDate = startDate; + } else { + if (isIntervalSelected(startDate, endDate)) return; + } + clearSelectionImpl(); + if (addSelectionImpl(startDate, endDate)) { + fireValueChanged(EventType.DATES_SET); + } + } + + /** + * Checks and returns if the single date interval bounded by startDate and endDate + * is selected. This is useful only for SingleInterval mode. + * + * @param startDate the start of the interval + * @param endDate the end of the interval, must be >= startDate + * @return true the interval is selected, false otherwise. + */ + private boolean isIntervalSelected(Date startDate, Date endDate) { + if (isSelectionEmpty()) return false; + startDate = startOfDay(startDate); + endDate = startOfDay(endDate); + return selectedDates.first().equals(startDate) + && selectedDates.last().equals(endDate); + } + + /** + * {@inheritDoc} + */ + @Override + public void removeSelectionInterval(Date startDate, Date endDate) { + if (startDate.after(endDate)) { + return; + } + + startDate = startOfDay(startDate); + endDate = startOfDay(endDate); + long startDateMs = startDate.getTime(); + long endDateMs = endDate.getTime(); + ArrayList datesToRemove = new ArrayList(); + for (Date selectedDate : selectedDates) { + long selectedDateMs = selectedDate.getTime(); + if (selectedDateMs >= startDateMs && selectedDateMs <= endDateMs) { + datesToRemove.add(selectedDate); + } + } + + if (!datesToRemove.isEmpty()) { + selectedDates.removeAll(datesToRemove); + fireValueChanged(EventType.DATES_REMOVED); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void clearSelection() { + if (isSelectionEmpty()) return; + clearSelectionImpl(); + fireValueChanged(EventType.SELECTION_CLEARED); + } + + private void clearSelectionImpl() { + selectedDates.clear(); + } + + /** + * {@inheritDoc} + */ + @Override + public SortedSet getSelection() { + return new TreeSet(selectedDates); + } + + /** + * {@inheritDoc} + */ + @Override + public Date getFirstSelectionDate() { + return isSelectionEmpty() ? null : selectedDates.first(); + } + + /** + * {@inheritDoc} + */ + @Override + public Date getLastSelectionDate() { + return isSelectionEmpty() ? null : selectedDates.last(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isSelected(Date date) { + // JW: don't need Contract ... startOfDay will throw NPE if null + return selectedDates.contains(startOfDay(date)); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isSelectionEmpty() { + return selectedDates.isEmpty(); + } + + /** + * {@inheritDoc} + */ + @Override + public SortedSet getUnselectableDates() { + return new TreeSet(unselectableDates); + } + + /** + * {@inheritDoc} + */ + @Override + public void setUnselectableDates(SortedSet unselectables) { + this.unselectableDates.clear(); + for (Date date : unselectables) { + unselectableDates.add(startOfDay(date)); + } + for (Date unselectableDate : this.unselectableDates) { + removeSelectionInterval(unselectableDate, unselectableDate); + } + fireValueChanged(EventType.UNSELECTED_DATES_CHANGED); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isUnselectableDate(Date date) { + date = startOfDay(date); + return upperBound != null && upperBound.getTime() < date.getTime() || + lowerBound != null && lowerBound.getTime() > date.getTime() || + unselectableDates != null && unselectableDates.contains(date); + } + + + private boolean addSelectionImpl(final Date startDate, final Date endDate) { + boolean hasAdded = false; + calendar.setTime(startDate); + Date date = calendar.getTime(); + while (date.before(endDate) || date.equals(endDate)) { + if (!isUnselectableDate(date)) { + hasAdded = true; + selectedDates.add(date); + } + calendar.add(Calendar.DATE, 1); + date = calendar.getTime(); + } + return hasAdded; + } + + + /** + * {@inheritDoc} + */ + + /** + * {@inheritDoc}

      + * + * Implemented to return the start of the day which contains the date. + */ + @Override + public Date getNormalizedDate(Date date) { + Contract.asNotNull(date, "date must not be null"); + return startOfDay(date); + } + +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/calendar/DefaultDateSelectionModel.java b/src/main/java/org/jdesktop/swingx/calendar/DefaultDateSelectionModel.java new file mode 100644 index 0000000000..ef08080a57 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/calendar/DefaultDateSelectionModel.java @@ -0,0 +1,273 @@ +/* + * $Id: DefaultDateSelectionModel.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.calendar; + +import org.jdesktop.swingx.event.DateSelectionEvent.EventType; +import org.jdesktop.swingx.util.Contract; + +import java.util.*; + +/** + * + * @author Joshua Outwater + */ +public class DefaultDateSelectionModel extends AbstractDateSelectionModel { + private SelectionMode selectionMode; + private SortedSet selectedDates; + private SortedSet unselectableDates; + + /** + * + */ + public DefaultDateSelectionModel() { + this(null); + } + + /** + *

      + * + * The selection mode defaults to SINGLE_SELECTION. + */ + public DefaultDateSelectionModel(Locale locale) { + super(locale); + this.selectionMode = SelectionMode.SINGLE_SELECTION; + this.selectedDates = new TreeSet(); + this.unselectableDates = new TreeSet(); + } + /** + * {@inheritDoc} + */ + @Override + public SelectionMode getSelectionMode() { + return selectionMode; + } + + /** + * {@inheritDoc} + */ + @Override + public void setSelectionMode(final SelectionMode selectionMode) { + this.selectionMode = selectionMode; + clearSelection(); + } + + +//------------------- selection ops + /** + * {@inheritDoc} + */ + @Override + public void addSelectionInterval(Date startDate, Date endDate) { + if (startDate.after(endDate)) { + return; + } + boolean added = false; + switch (selectionMode) { + case SINGLE_SELECTION: + if (isSelected(startDate)) return; + clearSelectionImpl(); + added = addSelectionImpl(startDate, startDate); + break; + case SINGLE_INTERVAL_SELECTION: + if (isIntervalSelected(startDate, endDate)) return; + clearSelectionImpl(); + added = addSelectionImpl(startDate, endDate); + break; + case MULTIPLE_INTERVAL_SELECTION: + if (isIntervalSelected(startDate, endDate)) return; + added = addSelectionImpl(startDate, endDate); + break; + default: + break; + } + if (added) { + fireValueChanged(EventType.DATES_ADDED); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void setSelectionInterval(final Date startDate, Date endDate) { + if (SelectionMode.SINGLE_SELECTION.equals(selectionMode)) { + if (isSelected(startDate)) return; + endDate = startDate; + } else { + if (isIntervalSelected(startDate, endDate)) return; + } + clearSelectionImpl(); + if (addSelectionImpl(startDate, endDate)) { + fireValueChanged(EventType.DATES_SET); + } + } + + /** + * Checks and returns if the single date interval bounded by startDate and endDate + * is selected. This is useful only for SingleInterval mode. + * + * @param startDate the start of the interval + * @param endDate the end of the interval, must be >= startDate + * @return true the interval is selected, false otherwise. + */ + private boolean isIntervalSelected(Date startDate, Date endDate) { + if (isSelectionEmpty()) return false; + return selectedDates.first().equals(startDate) + && selectedDates.last().equals(endDate); + } + + /** + * {@inheritDoc} + */ + @Override + public void removeSelectionInterval(final Date startDate, final Date endDate) { + if (startDate.after(endDate)) { + return; + } + + long startDateMs = startDate.getTime(); + long endDateMs = endDate.getTime(); + ArrayList datesToRemove = new ArrayList(); + for (Date selectedDate : selectedDates) { + long selectedDateMs = selectedDate.getTime(); + if (selectedDateMs >= startDateMs && selectedDateMs <= endDateMs) { + datesToRemove.add(selectedDate); + } + } + + if (!datesToRemove.isEmpty()) { + selectedDates.removeAll(datesToRemove); + fireValueChanged(EventType.DATES_REMOVED); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void clearSelection() { + if (isSelectionEmpty()) return; + clearSelectionImpl(); + fireValueChanged(EventType.SELECTION_CLEARED); + } + + private void clearSelectionImpl() { + selectedDates.clear(); + } + + /** + * {@inheritDoc} + */ + @Override + public SortedSet getSelection() { + return new TreeSet(selectedDates); + } + + /** + * {@inheritDoc} + */ + @Override + public Date getFirstSelectionDate() { + return isSelectionEmpty() ? null : selectedDates.first(); + } + + /** + * {@inheritDoc} + */ + @Override + public Date getLastSelectionDate() { + return isSelectionEmpty() ? null : selectedDates.last(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isSelected(final Date date) { + Contract.asNotNull(date, "date must not be null"); + return selectedDates.contains(date); + } + + /** + * {@inheritDoc} + */ + @Override + public Date getNormalizedDate(Date date) { + return new Date(date.getTime()); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isSelectionEmpty() { + return selectedDates.isEmpty(); + } + + + /** + * {@inheritDoc} + */ + @Override + public SortedSet getUnselectableDates() { + return new TreeSet(unselectableDates); + } + + /** + * {@inheritDoc} + */ + @Override + public void setUnselectableDates(SortedSet unselectableDates) { + this.unselectableDates = unselectableDates; + for (Date unselectableDate : this.unselectableDates) { + removeSelectionInterval(unselectableDate, unselectableDate); + } + fireValueChanged(EventType.UNSELECTED_DATES_CHANGED); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isUnselectableDate(Date date) { + return upperBound != null && upperBound.getTime() < date.getTime() || + lowerBound != null && lowerBound.getTime() > date.getTime() || + unselectableDates != null && unselectableDates.contains(date); + } + + + private boolean addSelectionImpl(final Date startDate, final Date endDate) { + boolean hasAdded = false; + calendar.setTime(startDate); + Date date = calendar.getTime(); + while (date.before(endDate) || date.equals(endDate)) { + if (!isUnselectableDate(date)) { + hasAdded = true; + selectedDates.add(date); + } + calendar.add(Calendar.DATE, 1); + date = calendar.getTime(); + } + return hasAdded; + } + + +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/calendar/SingleDaySelectionModel.java b/src/main/java/org/jdesktop/swingx/calendar/SingleDaySelectionModel.java new file mode 100644 index 0000000000..50a01b6765 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/calendar/SingleDaySelectionModel.java @@ -0,0 +1,345 @@ +/* + * $Id: SingleDaySelectionModel.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.calendar; + +import org.jdesktop.swingx.event.DateSelectionEvent.EventType; +import org.jdesktop.swingx.util.Contract; + +import java.util.Date; +import java.util.Locale; +import java.util.SortedSet; +import java.util.TreeSet; + +/** + * DateSelectionModel which allows a single selection only.

      + * + * Temporary quick & dirty class to explore requirements as needed by + * a DatePicker. Need to define the states more exactly. Currently + * + *

    • takes all Dates as-are (that is the normalized is the same as the given): + * selected, unselectable, lower/upper bounds + *
    • interprets any Date between the start/end of day of the selected as selected + *
    • interprets any Date between the start/end of an unselectable date as unselectable + *
    • interprets the lower/upper bounds as being the start/end of the given + * dates, that is any Date after the start of day of the lower and before the end of + * day of the upper is selectable. + * + * + * @author Jeanette Winzenburg + */ +public class SingleDaySelectionModel extends AbstractDateSelectionModel { + + private SortedSet selectedDates; + private SortedSet unselectableDates; + + /** + * Instantiates a SingleDaySelectionModel with default locale. + */ + public SingleDaySelectionModel() { + this(null); + } + + /** + * Instantiates a SingleSelectionModel with the given locale. If the locale is + * null, the Locale's default is used. + * + * PENDING JW: fall back to JComponent.getDefaultLocale instead? We use this + * with components anyway? + * + * @param locale the Locale to use with this model, defaults to Locale.default() + * if null. + */ + public SingleDaySelectionModel(Locale locale) { + super(locale); + this.selectedDates = new TreeSet(); + this.unselectableDates = new TreeSet(); + } + + /** + * {@inheritDoc} + */ + @Override + public SelectionMode getSelectionMode() { + return SelectionMode.SINGLE_SELECTION; + } + + /** + * {@inheritDoc}

      + * + * Implemented to do nothing. + * + */ + @Override + public void setSelectionMode(final SelectionMode selectionMode) { + } + + + //---------------------- selection ops + /** + * {@inheritDoc}

      + * + * Implemented to call setSelectionInterval with startDate for both + * parameters. + */ + @Override + public void addSelectionInterval(Date startDate, Date endDate) { + setSelection(startDate); + } + + /** + * {@inheritDoc}

      + * + * PENDING JW: define what happens if we have a selection but the interval + * isn't selectable. + */ + @Override + public void setSelectionInterval(Date startDate, Date endDate) { + setSelection(startDate); + } + + /** + * {@inheritDoc} + */ + @Override + public void removeSelectionInterval(Date startDate, Date endDate) { + Contract.asNotNull(startDate, "date must not be null"); + if (isSelectionEmpty()) return; + if (isSelectionInInterval(startDate, endDate)) { + selectedDates.clear(); + fireValueChanged(EventType.DATES_REMOVED); + } + } + + /** + * Checks and returns whether the selected date is contained in the interval + * given by startDate/endDate. The selection must not be empty when + * calling this method.

      + * + * This implementation interprets the interval between the start of the day + * of startDay to the end of the day of endDate. + * + * @param startDate the start of the interval, must not be null + * @param endDate the end of the interval, must not be null + * @return true if the selected date is contained in the interval + */ + protected boolean isSelectionInInterval(Date startDate, Date endDate) { + if (selectedDates.first().before(startOfDay(startDate)) + || (selectedDates.first().after(endOfDay(endDate)))) return false; + return true; + } + + /** + * Selects the given date if it is selectable and not yet selected. + * Does nothing otherwise. + * If this operation changes the current selection, it will fire a + * DateSelectionEvent of type DATES_SET. + * + * @param date the Date to select, must not be null. + */ + protected void setSelection(Date date) { + Contract.asNotNull(date, "date must not be null"); + if (isSelectedStrict(date)) return; + if (isSelectable(date)) { + selectedDates.clear(); + // PENDING JW: use normalized + selectedDates.add(date); + fireValueChanged(EventType.DATES_SET); + } + } + + /** + * Checks and returns whether the given date is contained in the selection. + * This differs from isSelected in that it tests for the exact (normalized) + * Date instead of for the same day. + * + * @param date the Date to test. + * @return true if the given date is contained in the selection, + * false otherwise + * + */ + private boolean isSelectedStrict(Date date) { + if (!isSelectionEmpty()) { + // PENDING JW: use normalized + return selectedDates.first().equals(date); + } + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public Date getFirstSelectionDate() { + return isSelectionEmpty() ? null : selectedDates.first(); + } + + /** + * {@inheritDoc} + */ + @Override + public Date getLastSelectionDate() { + return isSelectionEmpty() ? null : selectedDates.last(); + } + + /** + * Returns a boolean indicating whether the given date is selectable. + * + * @param date the date to check for selectable, must not be null. + * @return true if the given date is selectable, false if not. + */ + public boolean isSelectable(Date date) { + if (outOfBounds(date)) return false; + return !inUnselectables(date); + } + + /** + * @param date + * @return + */ + private boolean inUnselectables(Date date) { + for (Date unselectable : unselectableDates) { + if (isSameDay(unselectable, date)) return true; + } + return false; + } + + /** + * Returns a boolean indication whether the given date is below + * the lower or above the upper bound. + * + * @param date + * @return + */ + private boolean outOfBounds(Date date) { + if (belowLowerBound(date)) return true; + if (aboveUpperBound(date)) return true; + return false; + } + + /** + * @param date + * @return + */ + private boolean aboveUpperBound(Date date) { + if (upperBound != null) { + return endOfDay(upperBound).before(date); + } + return false; + } + + /** + * @param date + * @return + */ + private boolean belowLowerBound(Date date) { + if (lowerBound != null) { + return startOfDay(lowerBound).after(date); + } + return false; + } + + + /** + * {@inheritDoc} + */ + @Override + public void clearSelection() { + if (isSelectionEmpty()) return; + selectedDates.clear(); + fireValueChanged(EventType.SELECTION_CLEARED); + } + + + /** + * {@inheritDoc} + */ + @Override + public SortedSet getSelection() { + return new TreeSet(selectedDates); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isSelected(Date date) { + Contract.asNotNull(date, "date must not be null"); + if (isSelectionEmpty()) return false; + return isSameDay(selectedDates.first(), date); + } + + + + /** + * {@inheritDoc}

      + * + * Implemented to return the date itself. + */ + @Override + public Date getNormalizedDate(Date date) { + return new Date(date.getTime()); + } + + + /** + * {@inheritDoc} + */ + @Override + public boolean isSelectionEmpty() { + return selectedDates.isEmpty(); + } + + + /** + * {@inheritDoc} + */ + @Override + public SortedSet getUnselectableDates() { + return new TreeSet(unselectableDates); + } + + /** + * {@inheritDoc} + */ + @Override + public void setUnselectableDates(SortedSet unselectables) { + Contract.asNotNull(unselectables, "unselectable dates must not be null"); + this.unselectableDates.clear(); + for (Date unselectableDate : unselectables) { + removeSelectionInterval(unselectableDate, unselectableDate); + unselectableDates.add(unselectableDate); + } + fireValueChanged(EventType.UNSELECTED_DATES_CHANGED); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isUnselectableDate(Date date) { + return !isSelectable(date); + } + + + + + +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/calendar/package-info.java b/src/main/java/org/jdesktop/swingx/calendar/package-info.java new file mode 100644 index 0000000000..4e4738c59e --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/calendar/package-info.java @@ -0,0 +1,26 @@ +/* + * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +/** + * Contains classes and interfaces used by the {@code JXDatePicker} and + * {@code JXMonthView} components. + */ +package org.jdesktop.swingx.calendar; + diff --git a/src/main/java/org/jdesktop/swingx/color/EyeDropperColorChooserPanel.form b/src/main/java/org/jdesktop/swingx/color/EyeDropperColorChooserPanel.form new file mode 100644 index 0000000000..929940c4d7 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/color/EyeDropperColorChooserPanel.form @@ -0,0 +1,125 @@ + + +

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/org/jdesktop/swingx/color/EyeDropperColorChooserPanel.java b/src/main/java/org/jdesktop/swingx/color/EyeDropperColorChooserPanel.java new file mode 100644 index 0000000000..3937fc46e0 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/color/EyeDropperColorChooserPanel.java @@ -0,0 +1,289 @@ +/* + * $Id: EyeDropperColorChooserPanel.java 4082 2011-11-15 18:39:43Z kschaefe $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.color; + +import org.jdesktop.swingx.JXColorSelectionButton; +import org.jdesktop.swingx.util.PaintUtils; + +import javax.swing.*; +import javax.swing.colorchooser.AbstractColorChooserPanel; +import javax.swing.event.MouseInputAdapter; +import java.awt.*; +import java.awt.event.MouseEvent; +import java.awt.geom.Point2D; +import java.awt.image.BufferedImage; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +/** + *

      EyeDropperColorChooserPanel is a pluggable panel for the + * {@link JColorChooser} which allows the user to grab any + * color from the screen using a magnifying glass.

      + * + *

      Example usage:

      + *
      
      + *    public static void main(String ... args) {
      +        SwingUtilities.invokeLater(new Runnable() {
      +            public void run() {
      +                JColorChooser chooser = new JColorChooser();
      +                chooser.addChooserPanel(new EyeDropperColorChooserPanel());
      +                JFrame frame = new JFrame();
      +                frame.add(chooser);
      +                frame.pack();
      +                frame.setVisible(true);
      +            }
      +        });
      +    }
      + * 
      + * + * @author joshua@marinacci.org + */ +public class EyeDropperColorChooserPanel extends AbstractColorChooserPanel { + + /** + * Example usage + */ + public static void main(String ... args) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + JColorChooser chooser = new JColorChooser(); + chooser.addChooserPanel(new EyeDropperColorChooserPanel()); + JFrame frame = new JFrame(); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.add(chooser); + frame.pack(); + frame.setVisible(true); + } + }); + } + + /** + * Creates new EyeDropperColorChooserPanel + */ + public EyeDropperColorChooserPanel() { + initComponents(); + MouseInputAdapter mia = new MouseInputAdapter() { + @Override + public void mousePressed(MouseEvent evt) { + } + @Override + public void mouseDragged(MouseEvent evt) { + Point pt = evt.getPoint(); + SwingUtilities.convertPointToScreen(pt,evt.getComponent()); + ((MagnifyingPanel)magPanel).setMagPoint(pt); + } + @Override + public void mouseReleased(MouseEvent evt) { + Color newColor = new Color(((MagnifyingPanel)magPanel).activeColor); + getColorSelectionModel().setSelectedColor(newColor); + } + }; + eyeDropper.addMouseListener(mia); + eyeDropper.addMouseMotionListener(mia); + try { + eyeDropper.setIcon(new ImageIcon( + EyeDropperColorChooserPanel.class.getResource("mag.png"))); + eyeDropper.setText(""); + } catch (Exception ex) { + ex.printStackTrace(); + } + + magPanel.addPropertyChangeListener(new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent evt) { + Color color = new Color(((MagnifyingPanel)magPanel).activeColor); + activeColor.setBackground(color); + hexColor.setText(PaintUtils.toHexString(color).substring(1)); + rgbColor.setText(color.getRed() +"," + color.getGreen() + "," + color.getBlue()); + } + }); + } + + private class MagnifyingPanel extends JPanel { + private Point2D point; + private int activeColor; + public void setMagPoint(Point2D point) { + this.point = point; + repaint(); + } + @Override + public void paintComponent(Graphics g) { + if(point != null) { + Rectangle rect = new Rectangle((int)point.getX()-10,(int)point.getY()-10,20,20); + try { + BufferedImage img =new Robot().createScreenCapture(rect); + g.drawImage(img,0,0,getWidth(),getHeight(),null); + int oldColor = activeColor; + activeColor = img.getRGB(img.getWidth()/2,img.getHeight()/2); + firePropertyChange("activeColor", oldColor, activeColor); + } catch (AWTException ex) { + ex.printStackTrace(); + } + } + g.setColor(Color.black); + g.drawRect(getWidth()/2 - 5, getHeight()/2 -5, 10,10); + } + } + + + /** This method is called from within the constructor to + * initialize the form. + * WARNING: Do NOT modify this code. The content of this method is + * always regenerated by the Form Editor. + */ + // //GEN-BEGIN:initComponents + private void initComponents() { + java.awt.GridBagConstraints gridBagConstraints; + + eyeDropper = new javax.swing.JButton(); + magPanel = new MagnifyingPanel(); + activeColor = new JXColorSelectionButton(); + hexColor = new javax.swing.JTextField(); + JTextArea jTextArea1 = new JTextArea(); + jLabel1 = new JLabel(); + rgbColor = new javax.swing.JTextField(); + JLabel jLabel2 = new JLabel(); + + setLayout(new java.awt.GridBagLayout()); + + eyeDropper.setText("eye"); + add(eyeDropper, new java.awt.GridBagConstraints()); + + magPanel.setLayout(new java.awt.BorderLayout()); + + magPanel.setBorder(javax.swing.BorderFactory.createLineBorder(new Color(0, 0, 0))); + magPanel.setMinimumSize(new java.awt.Dimension(100, 100)); + magPanel.setPreferredSize(new java.awt.Dimension(100, 100)); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 1; + gridBagConstraints.gridheight = 3; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 12); + add(magPanel, gridBagConstraints); + + activeColor.setEnabled(false); + activeColor.setPreferredSize(new java.awt.Dimension(40, 40)); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 2; + gridBagConstraints.gridy = 3; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.insets = new java.awt.Insets(2, 0, 2, 0); + add(activeColor, gridBagConstraints); + + hexColor.setEditable(false); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 2; + gridBagConstraints.gridy = 1; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.insets = new java.awt.Insets(2, 0, 2, 0); + add(hexColor, gridBagConstraints); + + jTextArea1.setColumns(20); + jTextArea1.setEditable(false); + jTextArea1.setLineWrap(true); + jTextArea1.setRows(5); + jTextArea1.setText("Drag the magnifying glass to select a color from the screen."); + jTextArea1.setWrapStyleWord(true); + jTextArea1.setOpaque(false); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridwidth = 2; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH; + gridBagConstraints.weightx = 10.0; + gridBagConstraints.weighty = 10.0; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 7, 0); + add(jTextArea1, gridBagConstraints); + + jLabel1.setText("#"); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 1; + gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST; + gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 4); + add(jLabel1, gridBagConstraints); + + rgbColor.setEditable(false); + rgbColor.setText("255,255,255"); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 2; + gridBagConstraints.gridy = 2; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.insets = new java.awt.Insets(2, 0, 2, 0); + add(rgbColor, gridBagConstraints); + + jLabel2.setText("RGB"); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 2; + gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST; + gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 4); + add(jLabel2, gridBagConstraints); + + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton activeColor; + private javax.swing.JButton eyeDropper; + private javax.swing.JTextField hexColor; + private JLabel jLabel1; + private JPanel magPanel; + private javax.swing.JTextField rgbColor; + // End of variables declaration//GEN-END:variables + + /** + * {@inheritDoc} + */ + @Override + public void updateChooser() { + } + + /** + * {@inheritDoc} + */ + @Override + protected void buildChooser() { + } + + /** + * {@inheritDoc} + */ + @Override + public String getDisplayName() { + return "Grab from Screen"; + } + + /** + * {@inheritDoc} + */ + @Override + public Icon getSmallDisplayIcon() { + return new ImageIcon(); + } + + /** + * {@inheritDoc} + */ + @Override + public Icon getLargeDisplayIcon() { + return new ImageIcon(); + } +} diff --git a/src/main/java/org/jdesktop/swingx/color/GradientPreviewPanel.java b/src/main/java/org/jdesktop/swingx/color/GradientPreviewPanel.java new file mode 100644 index 0000000000..02c4c624ae --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/color/GradientPreviewPanel.java @@ -0,0 +1,285 @@ +/* + * $Id: GradientPreviewPanel.java 4082 2011-11-15 18:39:43Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.color; + +import org.jdesktop.swingx.JXGradientChooser; +import org.jdesktop.swingx.JXPanel; +import org.jdesktop.swingx.multislider.MultiThumbModel; +import org.jdesktop.swingx.multislider.Thumb; +import org.jdesktop.swingx.util.PaintUtils; + +import javax.swing.event.MouseInputAdapter; +import java.awt.*; +import java.awt.event.MouseEvent; +import java.awt.geom.Point2D; +import java.util.List; +import java.util.logging.Logger; + +/** + *

      Dependency: Because this class relies on LinearGradientPaint and + * RadialGradientPaint, it requires the optional MultipleGradientPaint.jar

      + * + * @author joshy + */ +public class GradientPreviewPanel extends JXPanel { + private Paint checker_texture = null; + private Point2D start, end; + public JXGradientChooser picker; + boolean moving_start = false; + boolean moving_end = false; + private boolean radial = false; + private boolean reversed = false; + private boolean reflected = false; + private boolean repeated = false; + private MultipleGradientPaint gradient; + + public GradientPreviewPanel() { + start = new Point2D.Float(10,10); + end = new Point2D.Float(80,10); + checker_texture = PaintUtils.getCheckerPaint(); + MouseInputAdapter ma = new GradientMouseHandler(); + this.addMouseListener(ma); + this.addMouseMotionListener(ma); + } + + public void setGradient() { + repaint(); + } + + public void setGradient(MultipleGradientPaint grad) { + MultipleGradientPaint old = getGradient(); + if(grad instanceof LinearGradientPaint) { + LinearGradientPaint paint = (LinearGradientPaint)grad; + this.start = paint.getStartPoint(); + this.end = paint.getEndPoint(); + } else { + RadialGradientPaint paint = (RadialGradientPaint)grad; + this.start = paint.getCenterPoint(); + this.end = new Point2D.Double(start.getX(),start.getY()+paint.getRadius()); + } + this.gradient = grad; + firePropertyChange("gradient", old, getGradient()); + repaint(); + } + + public MultipleGradientPaint getGradient() { + return this.gradient; + } + + public MultipleGradientPaint calculateGradient() { + List> stops = getStops(); + int len = stops.size(); + + // set up the data for the gradient + float[] fractions = new float[len]; + Color[] colors = new Color[len]; + int i = 0; + for (Thumb thumb : stops) { + colors[i] = (Color)thumb.getObject(); + fractions[i] = thumb.getPosition(); + i++; + } + + // get the final gradient + this.setGradient(calculateGradient(fractions, colors)); + return getGradient(); + } + + private MultiThumbModel model; + private Logger log = Logger.getLogger(GradientPreviewPanel.class.getName()); + + private List> getStops() { + // calculate the color stops + return model == null ? null : model.getSortedThumbs(); + } + + public void setMultiThumbModel(MultiThumbModel model) { + MultiThumbModel old = getMultiThumbModel(); + this.model = model; + firePropertyChange("multiThumbModel", old, getMultiThumbModel()); + } + + public MultiThumbModel getMultiThumbModel() { + return this.model; + } + + @Override + protected void paintComponent(Graphics g) { + try { + Graphics2D g2 = (Graphics2D)g; + + // fill the background with checker first + g2.setPaint(checker_texture); + g.fillRect(0,0,getWidth(),getHeight()); + + + Paint paint = getGradient(); + // fill the area + if(paint != null) { + g2.setPaint(paint); + } else { + g2.setPaint(Color.black); + } + + g.fillRect(0,0,getWidth(),getHeight()); + + drawHandles(g2); + } catch (Exception ex) { + log .severe("ex: " + ex); + } + } + + private MultipleGradientPaint calculateGradient(final float[] fractions, final Color[] colors) { + // set up the end points + Point2D start = this.start; + Point2D end = this.end; + if(isReversed()) { + //if(picker.reversedCheck.isSelected()) { + start = this.end; + end = this.start; + } + + // set up the cycle type + MultipleGradientPaint.CycleMethod cycle = MultipleGradientPaint.CycleMethod.NO_CYCLE; + if(isRepeated()) { + //if(picker.repeatedRadio.isSelected()) { + cycle = MultipleGradientPaint.CycleMethod.REPEAT; + } + if(isReflected()) { + //if(picker.reflectedRadio.isSelected()) { + cycle = MultipleGradientPaint.CycleMethod.REFLECT; + } + + // create the underlying gradient paint + MultipleGradientPaint paint = null; + if(isRadial()) { //picker.styleCombo.getSelectedItem().toString().equals("Radial")) { + paint = new RadialGradientPaint( + start, (float)start.distance(end),start, + fractions, colors, cycle, MultipleGradientPaint.ColorSpaceType.SRGB, null + ); + } else { + paint = new LinearGradientPaint( + (float)start.getX(), + (float)start.getY(), + (float)end.getX(), + (float)end.getY(), + fractions,colors,cycle); + } + return paint; + } + + private void drawHandles(final Graphics2D g2) { + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + // draw the points and gradient line + g2.setColor(Color.black); + g2.drawOval((int)start.getX()-5,(int)start.getY()-5,10,10); + g2.setColor(Color.white); + g2.drawOval((int)start.getX()-4,(int)start.getY()-4,8,8); + + g2.setColor(Color.black); + g2.drawOval((int)end.getX()-5,(int)end.getY()-5,10,10); + g2.setColor(Color.white); + g2.drawOval((int)end.getX()-4,(int)end.getY()-4,8,8); + + g2.setColor(Color.darkGray); + g2.drawLine((int)start.getX(),(int)start.getY(), + (int)end.getX(),(int)end.getY()); + g2.setColor(Color.gray); + g2.drawLine((int)start.getX()-1,(int)start.getY()-1, + (int)end.getX()-1,(int)end.getY()-1); + } + + private class GradientMouseHandler extends MouseInputAdapter { + + @Override + public void mousePressed(MouseEvent evt) { + moving_start = false; + moving_end = false; + if (evt.getPoint().distance(start) < 5) { + moving_start = true; + start = evt.getPoint(); + return; + } + + if (evt.getPoint().distance(end) < 5) { + moving_end = true; + end = evt.getPoint(); + return; + } + + start = evt.getPoint(); + } + + @Override + public void mouseDragged(MouseEvent evt) { + if (moving_start) { + start = evt.getPoint(); + } else { + end = evt.getPoint(); + } + calculateGradient(); + repaint(); + } + } + + public boolean isRadial() { + return radial; + } + + public void setRadial(boolean radial) { + boolean old = isRadial(); + this.radial = radial; + firePropertyChange("radial", old, isRadial()); + } + + public boolean isReversed() { + return reversed; + } + + public void setReversed(boolean reversed) { + boolean old = isReversed(); + this.reversed = reversed; + firePropertyChange("reversed", old, isReversed()); + } + + public boolean isReflected() { + return reflected; + } + + public void setReflected(boolean reflected) { + boolean old = isReflected(); + this.reflected = reflected; + firePropertyChange("reflected", old, isReflected()); + } + + public boolean isRepeated() { + return repeated; + } + + public void setRepeated(boolean repeated) { + boolean old = isRepeated(); + this.repeated = repeated; + firePropertyChange("repeated", old, isRepeated()); + } +} + diff --git a/src/main/java/org/jdesktop/swingx/color/GradientThumbRenderer.java b/src/main/java/org/jdesktop/swingx/color/GradientThumbRenderer.java new file mode 100644 index 0000000000..eaf4b3c060 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/color/GradientThumbRenderer.java @@ -0,0 +1,67 @@ +/* + * $Id: GradientThumbRenderer.java 4082 2011-11-15 18:39:43Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.color; + +import org.jdesktop.swingx.JXMultiThumbSlider; +import org.jdesktop.swingx.multislider.ThumbRenderer; +import org.jdesktop.swingx.util.PaintUtils; + +import javax.imageio.ImageIO; +import javax.swing.*; +import java.awt.*; + +public class GradientThumbRenderer extends JComponent implements ThumbRenderer { + private Image thumb_black; + private Image thumb_gray; + + public GradientThumbRenderer() { + super(); + + try { + thumb_black = ImageIO.read(GradientThumbRenderer.class.getResourceAsStream("/icons/thumb_black.png")); + thumb_gray = ImageIO.read(GradientThumbRenderer.class.getResourceAsStream("/icons/thumb_gray.png")); + } catch (Exception ex) { +// ex.printStackTrace(); + } + } + + private boolean selected; + @Override + protected void paintComponent(Graphics g) { + JComponent thumb = this; + int w = thumb.getWidth(); + g.setColor(getForeground()); + g.fillRect(0, 0, w - 1, w - 1); + if (selected) { + g.drawImage(thumb_black, 0, 0, null); + } else { + g.drawImage(thumb_gray, 0, 0, null); + } + } + + public JComponent getThumbRendererComponent(JXMultiThumbSlider slider, int index, boolean selected) { + Color c = (Color)slider.getModel().getThumbAt(index).getObject(); + c = PaintUtils.removeAlpha(c); + this.setForeground(c); + this.selected = selected; + return this; + } +} diff --git a/src/main/java/org/jdesktop/swingx/color/GradientTrackRenderer.java b/src/main/java/org/jdesktop/swingx/color/GradientTrackRenderer.java new file mode 100644 index 0000000000..3e6db85748 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/color/GradientTrackRenderer.java @@ -0,0 +1,103 @@ +/* + * $Id: GradientTrackRenderer.java 4082 2011-11-15 18:39:43Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.color; + +import org.jdesktop.swingx.JXMultiThumbSlider; +import org.jdesktop.swingx.multislider.Thumb; +import org.jdesktop.swingx.multislider.TrackRenderer; +import org.jdesktop.swingx.util.PaintUtils; + +import javax.swing.*; +import java.awt.*; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.List; + +/** + *

      Dependency: Because this class relies on LinearGradientPaint and + * RadialGradientPaint, it requires the optional MultipleGradientPaint.jar

      + */ +public class GradientTrackRenderer extends JComponent implements TrackRenderer { + private Paint checker_paint; + + public GradientTrackRenderer() { + checker_paint = PaintUtils.getCheckerPaint(); + } + + private JXMultiThumbSlider slider; + + @Override + public void paint(Graphics g) { + super.paint(g); + paintComponent(g); + } + + @Override + protected void paintComponent(Graphics gfx) { + Graphics2D g = (Graphics2D)gfx; + + // get the list of colors + List> stops = slider.getModel().getSortedThumbs(); + int len = stops.size(); + + // set up the data for the gradient + float[] fractions = new float[len]; + Color[] colors = new Color[len]; + int i = 0; + for(Thumb thumb : stops) { + colors[i] = (Color)thumb.getObject(); + fractions[i] = thumb.getPosition(); + i++; + } + + // calculate the track area + int thumb_width = 12; + int track_width = slider.getWidth() - thumb_width; + g.translate(thumb_width / 2, 12); + Rectangle2D rect = new Rectangle(0, 0, track_width, 20); + + // fill in the checker + g.setPaint(checker_paint); + g.fill(rect); + + // fill in the gradient + Point2D start = new Point2D.Float(0,0); + Point2D end = new Point2D.Float(track_width,0); + MultipleGradientPaint paint = new LinearGradientPaint( + (float)start.getX(), + (float)start.getY(), + (float)end.getX(), + (float)end.getY(), + fractions,colors); + g.setPaint(paint); + g.fill(rect); + + // draw a border + g.setColor(Color.black); + g.draw(rect); + g.translate(-thumb_width / 2, -12); + } + + public JComponent getRendererComponent(JXMultiThumbSlider slider) { + this.slider = slider; + return this; + } +} diff --git a/src/main/java/org/jdesktop/swingx/color/colorwell.png b/src/main/java/org/jdesktop/swingx/color/colorwell.png new file mode 100644 index 0000000000000000000000000000000000000000..edf111fc8fbc46631f08ae6494a39b25a309a410 GIT binary patch literal 725 zcmV;`0xJE9P)z@;j(q!3lK=n!AY({UO#lFTB>(_`g8%^e{{R4h=>PzA zFaQARU;qF*m;eA5Z<1fdMgRZ;OG!jQRCwC#TCuU)FbsWwApP1+?vg4CWQt7VH9UfM z@EqA91Eh$XxUzL;>61hX=x9oo?%&y!KFp3FA&E}__yCf2yB!u95dl!uQlhF8xe3>< zD*(3J?PjydQ?q8NUkb}tmTjfFm&;|p-wS~A`OM5ViHNf1{c|&@`H9xe!qw@fl-jl( zEaG%JbzN6gmBC^9DJ1|R5<*Z_8%-(MXpFI#oMOaEb^E@*UatcXbzO@{(==68g%GN$ zQq`0aGuw0R1w>>LBC6{;Lmq;@yi8qU2W>r;H$;`~HT?`iC)+5T4VhENcKlKKL`2`WlWWcXQSjX}chd@jz zw!>hV*E_O^Nma+sW-&vt{cWFA*6bLsmMy|FkC<@{e?W{Pg?An?-8E*<2%NxS*{1hk zIbxQc36n3^X{WYxFTUi4|U8k<9#u#B|pE1U^ zZ9@o?Ka($GErDvI_aC$(-LC8Uz8`=%91agKklF5fb^i|lry!$8r!90^V+LuOXuOOXFfI%w5kq23CL~0}1sAtOcdks( zuwdZ=FDP!bkchZ2LX9yXF*c|RG?5{qK&cE3T3TprnVuQuaxN~4#Kbs3>bv?k-;?iq zzxVqEf778uNArde?`$E8LMFZ-K`TKLQA(oZz{ZVFzrk%v{Pozs|I@9J7JE{z?WfXd z9LGXR2|yTz)PsO(rBZ6t*tUK9hSF_;_wGHo)kw+V?p!aP=VF=~r4#^B7-5(ON-3&A zmC5qO`RjAx!#j37f2kc<@7_I}4`s9`m+Qm#Gk9qS+cuF>5=JrA>J7qHh*FBK?oI?N z{7U8LVPI`Lu#BSWOMW)vyRL)lI%G0wIx-%BPTPvhY@|J0! zwL}UK2$WJ(sx>Uz!ZHowBt}Y!5P~F0=*{&~uh-gktPNA4ltKu>^-7hXT4&a_P)d;T z(iln+X?+U}5XUi%#*MbA&Gg`)&QxA00Z1;KpBQbmj03AyJyvQo8on>D z4iDB2=T{dz=E~!oz2fZO{K1uNELHD4mz%rx!z)Sc?CZ(Q$B!o`U-pxIn{vbdu;RaJ z?SMC3o4s7TcW4bVb2pxxMOG#YgWcKuS7X7*9fQ9-7nBAZIdN_LO!2{WPa-pS;j0;R zWwOwh?#zE%4o1|S+HvC7pwutr#Prxt#Yfk#LwbF5nX?$dWT8W6^J*zxpS&EDvLO@G zV`qx1pLq)D=17aHd~>Q^P)l88=}NWK6)nQ=U-Ja-?E4W`Zb0xqX-i@Gu;a+B`o7JS cr|L)k1aI$M*{)DsFaQ7m07*qoM6N<$g7Po#qyPW_ literal 0 HcmV?d00001 diff --git a/src/main/java/org/jdesktop/swingx/color/package-info.java b/src/main/java/org/jdesktop/swingx/color/package-info.java new file mode 100644 index 0000000000..3c9ced2f2a --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/color/package-info.java @@ -0,0 +1,25 @@ +/* + * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +/** + * Contains classes and interfaces used by the {@code JXGradientChooser} + * component. + */ +package org.jdesktop.swingx.color; diff --git a/src/main/java/org/jdesktop/swingx/combobox/EnumComboBoxModel.java b/src/main/java/org/jdesktop/swingx/combobox/EnumComboBoxModel.java new file mode 100644 index 0000000000..8788bf0380 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/combobox/EnumComboBoxModel.java @@ -0,0 +1,192 @@ +/* + * $Id: EnumComboBoxModel.java 3475 2009-08-28 08:30:47Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.combobox; + +import java.util.*; + +/** + *

      + * A ComboBoxModel implementation that safely wraps an Enum. It allows the + * developer to directly use an enum as their model for a combobox without any + * extra work, though the display can can be further customized. + *

      + * + *

      Simple Usage

      + * + *

      + * The simplest usage is to wrap an enum inside the + * EnumComboBoxModel and then set it as the model on the combo + * box. The combo box will then appear on screen with each value in the + * enum as a value in the combobox. + *

      + *

      + * ex: + *

      + * + *
      
      + *  enum MyEnum { GoodStuff, BadStuff };
      + *  ...
      + *  JComboBox combo = new JComboBox();
      + *  combo.setModel(new EnumComboBoxModel(MyEnum.class));
      + * 
      + * + *

      Type safe access

      + *

      + * By using generics and co-variant types you can make accessing elements from + * the model be completely typesafe. ex: + *

      + * + *
      
      + * EnumComboBoxModel<MyEnum> enumModel = new EnumComboBoxModel<MyEnum1>(
      + *         MyEnum1.class);
      + * 
      + * MyEnum first = enumModel.getElement(0);
      + * 
      + * MyEnum selected = enumModel.getSelectedItem();
      + * 
      + * + *

      Advanced Usage

      + *

      + * Since the exact toString() value of each enum constant may not + * be exactly what you want on screen (the values won't have spaces, for + * example) you can override to toString() method on the values when you declare + * your enum. Thus the display value is localized to the enum and not in your + * GUI code. ex: + * + *

      
      + *    private enum MyEnum {GoodStuff, BadStuff;
      + *        public String toString() {
      + *           switch(this) {
      + *               case GoodStuff: return "Some Good Stuff";
      + *               case BadStuff: return "Some Bad Stuff";
      + *           }
      + *           return "ERROR";
      + *        }
      + *    };
      + * 
      + * + * Note: if more than one enum constant returns the same {@code String} via + * {@code toString()}, this model will throw an exception on creation. + * + * @author joshy + * @author Karl Schaefer + */ +public class EnumComboBoxModel> extends ListComboBoxModel { + private static final long serialVersionUID = 2176566393195371004L; + + private final Map valueMap; + private final Class enumClass; + + /** + * Creates an {@code EnumComboBoxModel} for the enum represent by the + * {@code Class} {@code en}. + * + * @param en + * the enum class type + * @throws IllegalArgumentException + * if the {@code Enum.toString} returns the same value for more + * than one constant + */ + public EnumComboBoxModel(Class en) { + super(new ArrayList(EnumSet.allOf(en))); + + //we could size these, probably not worth it; enums are usually small + valueMap = new HashMap(); + enumClass = en; + + Iterator iter = data.iterator(); + + while (iter.hasNext()) { + E element = iter.next(); + String s = element.toString(); + + if (valueMap.containsKey(s)) { + throw new IllegalArgumentException( + "multiple constants map to one string value"); + } + + valueMap.put(s, element); + } + } + + /** + * {@inheritDoc} + */ + @Override + @SuppressWarnings("unchecked") + public void setSelectedItem(Object anItem) { + E input = null; + + if (enumClass.isInstance(anItem)) { + input = (E) anItem; + } else { + input = valueMap.get(anItem); + } + + if (input != null || anItem == null) { + selected = input; + } + + this.fireContentsChanged(this, 0, getSize()); + } + + /* + public static void main(String[] args) { + JFrame frame = new JFrame(); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setLayout(new BoxLayout(frame.getContentPane(),BoxLayout.Y_AXIS)); + + + JComboBox combo1 = new JComboBox(); + combo1.setModel(new EnumComboBoxModel(MyEnum1.class)); + frame.add(combo1); + + JComboBox combo2 = new JComboBox(); + combo2.setModel(new EnumComboBoxModel(MyEnum2.class)); + frame.add(combo2); + + EnumComboBoxModel enumModel = new EnumComboBoxModel(MyEnum1.class); + JComboBox combo3 = new JComboBox(); + combo3.setModel(enumModel); + frame.add(combo3); + + MyEnum1 selected = enumModel.getSelectedItem(); + + //uncomment to see the ClassCastException +// enumModel.setSelectedItem("Die clown"); + + frame.pack(); + frame.setVisible(true); + } + + private enum MyEnum1 {GoodStuff, BadStuff}; + private enum MyEnum2 {GoodStuff, BadStuff; + public String toString() { + switch(this) { + case GoodStuff: return "Some Good Stuff"; + case BadStuff: return "Some Bad Stuff"; + } + return "ERROR"; + } + }; + */ + +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/combobox/JXTextFieldComboBoxEditor.java b/src/main/java/org/jdesktop/swingx/combobox/JXTextFieldComboBoxEditor.java new file mode 100644 index 0000000000..028ca4fe11 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/combobox/JXTextFieldComboBoxEditor.java @@ -0,0 +1,60 @@ +package org.jdesktop.swingx.combobox; + +import org.jdesktop.swingx.JXTextField; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionListener; + +public class JXTextFieldComboBoxEditor implements ComboBoxEditor { + private JXTextField textField; + + public JXTextFieldComboBoxEditor() { + this(null); + } + + public JXTextFieldComboBoxEditor(String promptText) { + this(promptText, null); + } + + public JXTextFieldComboBoxEditor(String promptText, Color promptForeground) { + this(promptText, promptForeground, null); + } + + public JXTextFieldComboBoxEditor(String promptText, Color promptForeground, Color promptBackground) { + textField = new JXTextField(promptText, promptForeground, promptBackground); + textField.setBorder(BorderFactory.createEmptyBorder()); + textField.setOuterMargin(new Insets(0, 2, 0, 2)); + } + + @Override + public JXTextField getEditorComponent() { + return textField; + } + + @Override + public void setItem(Object anObject) { + textField.setText(anObject != null ? anObject.toString() : ""); + } + + @Override + public Object getItem() { + return textField.getText(); + } + + @Override + public void selectAll() { + textField.selectAll(); + } + + @Override + public void addActionListener(ActionListener l) { + textField.addActionListener(l); + } + + @Override + public void removeActionListener(ActionListener l) { + textField.removeActionListener(l); + } + +} diff --git a/src/main/java/org/jdesktop/swingx/combobox/ListComboBoxModel.java b/src/main/java/org/jdesktop/swingx/combobox/ListComboBoxModel.java new file mode 100644 index 0000000000..ca9366fbf8 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/combobox/ListComboBoxModel.java @@ -0,0 +1,124 @@ +/* + * $Id: ListComboBoxModel.java 3935 2011-03-02 19:06:41Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.combobox; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.List; + +/** + * A {@code ComboBoxModel} for {@code List}s. + * + * @param the type of elements maintained by the list backing this model + * + * @author jm158417 + * @author Karl George Schaefer + */ +public class ListComboBoxModel extends AbstractListModel implements ComboBoxModel, ActionListener { + /** + * A key used to notify the model that the backing {@code List} has changed. + */ + public static final String UPDATE = "update"; + + /** + * A reference to the list backing this model. + *

      + * This model does not make a copy of the list, so any changes in + * the list without synchronizing the model may have drastic effects. + */ + protected final List data; + + /** + * The currently selected item. + */ + protected E selected; + + /** + * Creates a {@code ListComboBoxModel} backed by the supplied {@code list}. + * + * @param list + * the list backing this model + * @throws NullPointerException + * if {@code list} is {@code null} + */ + public ListComboBoxModel(List list) { + this.data = list; + + if(list.size() > 0) { + selected = list.get(0); + } + } + + /** + * Set the selected item. The implementation of this method should notify + * all registered {@code ListDataListener}s that the contents have changed. + * + * @param item + * the list object to select or {@code null} to clear the + * selection + * @throws ClassCastException + * if {@code item} is not of type {@code E} + */ + @Override + @SuppressWarnings("unchecked") + public void setSelectedItem(Object item) { + if ((selected != null && !selected.equals(item)) + || selected == null && item != null) { + selected = (E) item; + fireContentsChanged(this, -1, -1); + } + } + + /** + * {@inheritDoc} + */ + @Override + public E getSelectedItem() { + return this.selected; + } + + /** + * {@inheritDoc} + */ + @Override + public E getElementAt(int index) { + return data.get(index); + } + + /** + * {@inheritDoc} + */ + @Override + public int getSize() { + return data.size(); + } + + /** + * {@inheritDoc} + */ + @Override + public void actionPerformed(ActionEvent evt) { + if(evt.getActionCommand().equals(UPDATE)) { + this.fireContentsChanged(this, 0, getSize() - 1); + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/combobox/ListModelComboBoxWrapper.java b/src/main/java/org/jdesktop/swingx/combobox/ListModelComboBoxWrapper.java new file mode 100644 index 0000000000..29af9b4e80 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/combobox/ListModelComboBoxWrapper.java @@ -0,0 +1,51 @@ +package org.jdesktop.swingx.combobox; + +import javax.swing.*; +import javax.swing.event.ListDataListener; + +public class ListModelComboBoxWrapper extends AbstractListModel implements ComboBoxModel { + private ListModel delegate; + + private Object selectedItem; + + public ListModelComboBoxWrapper(ListModel delegate) { + this.delegate = delegate; + } + + @Override + public int getSize() { + return delegate.getSize(); + } + + @Override + public Object getElementAt(int index) { + return delegate.getElementAt(index); + } + + @Override + public void addListDataListener(ListDataListener l) { + super.addListDataListener(l); + delegate.addListDataListener(l); + } + + @Override + public void removeListDataListener(ListDataListener l) { + delegate.removeListDataListener(l); + super.removeListDataListener(l); + } + + @Override + public void setSelectedItem(Object anItem) { + if ((selectedItem != null && !selectedItem.equals(anItem)) + || selectedItem == null && anItem != null) { + selectedItem = anItem; + + fireContentsChanged(this, -1, -1); + } + } + + @Override + public Object getSelectedItem() { + return selectedItem; + } +} diff --git a/src/main/java/org/jdesktop/swingx/combobox/MapComboBoxModel.java b/src/main/java/org/jdesktop/swingx/combobox/MapComboBoxModel.java new file mode 100644 index 0000000000..7ee5daa0cc --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/combobox/MapComboBoxModel.java @@ -0,0 +1,133 @@ +/* + * $Id: MapComboBoxModel.java 3751 2010-08-08 04:07:44Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.combobox; + +import java.awt.event.ActionEvent; +import java.util.*; + +/** + * A {@code ComboBoxModel} for {@code Map}s. The model will always present a {@code Map} + * consistently, once it is instantiated. However, unless the {@code Map} is ordered, as a + * {@code java.util.TreeMap} is, the model is not guaranteed to present the maps in a consistent + * order between instantiations. + * + * @author jm158417 + * @author Karl George Schaefer + * + * @param + * the type of keys maintained by the map backing this model + * @param + * the type of mapped values + */ +public class MapComboBoxModel extends ListComboBoxModel { + + /** + * The map backing this model. + */ + protected Map map_data; + + /** + * Creates an empty model. + */ + public MapComboBoxModel() { + this(new LinkedHashMap()); + } + + /** + * Creates a model backed by the specified map. + * + * @param map + * the map backing this model + */ + public MapComboBoxModel(Map map) { + super(buildIndex(map)); + + this.map_data = map; + } + + /** + * Builds an index for this model. This method ensures that the map is always presented in a + * consistent order. + *

      + * This method is called by the constructor and the {@code List} is passed to {@code super}. + * + * @param + * the type of keys for the map + * @param map + * the map to build an index for + * @return a list containing the map keys + */ + private static List buildIndex(Map map) { + return new ArrayList(map.keySet()); + } + + /** + * {@inheritDoc} + */ + @Override + public int getSize() { + return map_data.size(); + } + + /** + * {@inheritDoc} + */ + @Override + public void actionPerformed(ActionEvent evt) { + //kgs - this code might not be performant with large maps + if(evt.getActionCommand().equals(UPDATE)) { + //add new keys + Set keys = map_data.keySet(); + keys.removeAll(data); + data.addAll(keys); + + //remove dead keys + List copy = new ArrayList(data); + keys = map_data.keySet(); + copy.removeAll(keys); + data.removeAll(copy); + + fireContentsChanged(this, 0, getSize() - 1); + } + } + + /** + * Selects an item from the model and returns that map value. + * + * @param selectedItem + * the item to select + * @return the value for the selected item + */ + public V getValue(Object selectedItem) { + return map_data.get(selectedItem); + } + + /** + * Selects an item from the model and returns that map value. + * + * @param selectedItem + * selects the item at the specified index in this model + * @return the value for the item at the selected index + */ + public V getValue(int selectedItem) { + return getValue(getElementAt(selectedItem)); + } +} diff --git a/src/main/java/org/jdesktop/swingx/combobox/package-info.java b/src/main/java/org/jdesktop/swingx/combobox/package-info.java new file mode 100644 index 0000000000..c7a09d8d58 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/combobox/package-info.java @@ -0,0 +1,25 @@ +/* + * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +/** + * Contains classes and interfaces used by the {@code JComboBox} component. + */ +package org.jdesktop.swingx.combobox; + diff --git a/src/main/java/org/jdesktop/swingx/decorator/AbstractHighlighter.java b/src/main/java/org/jdesktop/swingx/decorator/AbstractHighlighter.java new file mode 100644 index 0000000000..9b55bb257e --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/decorator/AbstractHighlighter.java @@ -0,0 +1,285 @@ +/* + * $Id: AbstractHighlighter.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.decorator; + +import org.jdesktop.swingx.event.WeakEventListenerList; + +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import java.awt.*; + +/** + * Abstract Highlighter implementation which manages change + * notification and supports conditional highlighting. + * Subclasses are required to fire ChangeEvents on internal changes which might + * effect the highlight. The HighlightPredicate controls whether or not + * a highlight should be applied for the given ComponentAdapter, + * subclasses must guarantee to respect its decision. + *

      + * + * Concrete custom implementations should focus on a single (or few) visual + * attribute to highlight. This allows easy re-use by composition. F.i. a custom + * FontHighlighter: + * + *

      
      + * public static class FontHighlighter extends AbstractHighlighter {
      + * 
      + *     private Font font;
      + * 
      + *     public FontHighlighter(HighlightPredicate predicate, Font font) {
      + *         super(predicate);
      + *         setFont(font);
      + *     }
      + * 
      + *     @Override
      + *     protected Component doHighlight(Component component,
      + *             ComponentAdapter adapter) {
      + *         component.setFont(font);
      + *         return component;
      + *     }
      + *     
      + *     public final void setFont(Font font) {
      + *        if (equals(font, this.font)) return;
      + *        this.font = font;
      + *        fireStateChanged();
      + *     }
      + *     
      + * 
      + * }
      + * 
      + * 
      + * + * Client code can combine the effect with a f.i. Color decoration, and use a + * shared HighlightPredicate to apply both for the same condition. + * + *
      
      + * HighlightPredicate predicate = new HighlightPredicate() {
      + *     public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
      + *         Object value = adapter.getFilteredValueAt(adapter.row, adapter.column);
      + *         return (value instanceof Number) && ((Number) value).intValue() < 0;
      + *     }
      + * };
      + * table.setHighlighters(
      + *         new ColorHighlighter(predicate, Color.RED, null),
      + *         new FontHighlighter(predicate, myBoldFont));
      + * 
      + * + * @author Jeanette Winzenburg + * + * @see HighlightPredicate + * @see org.jdesktop.swingx.renderer.ComponentProvider + */ +public abstract class AbstractHighlighter implements Highlighter { + + /** + * Only one ChangeEvent is needed per model instance since the + * event's only (read-only) state is the source property. The source + * of events generated here is always "this". + */ + private transient ChangeEvent changeEvent; + + /** The listeners waiting for model changes. */ + protected WeakEventListenerList listenerList = new WeakEventListenerList(); + + /** the HighlightPredicate to use. */ + private HighlightPredicate predicate; + + /** + * Instantiates a Highlighter with default HighlightPredicate. + * + * @see #setHighlightPredicate(HighlightPredicate) + */ + public AbstractHighlighter() { + this(null); + } + + /** + * Instantiates a Highlighter with the given + * HighlightPredicate.

      + * + * @param predicate the HighlightPredicate to use. + * + * @see #setHighlightPredicate(HighlightPredicate) + */ + public AbstractHighlighter(HighlightPredicate predicate) { + setHighlightPredicate(predicate); + } + + /** + * Set the HighlightPredicate used to decide whether a cell should + * be highlighted. If null, sets the predicate to HighlightPredicate.ALWAYS. + * + * The default value is HighlightPredicate.ALWAYS. + * + * @param predicate the HighlightPredicate to use. + */ + public void setHighlightPredicate(HighlightPredicate predicate) { + if (predicate == null) { + predicate = HighlightPredicate.ALWAYS; + } + if (areEqual(predicate, getHighlightPredicate())) return; + this.predicate = predicate; + fireStateChanged(); + } + + /** + * Returns the HighlightPredicate used to decide whether a cell + * should be highlighted. Guaranteed to be never null. + * + * @return the HighlightPredicate to use, never null. + */ + public HighlightPredicate getHighlightPredicate() { + return predicate; + } + + //----------------------- implement predicate respecting highlight + + /** + * {@inheritDoc} + * + * This calls doHighlight to apply the decoration if both HighlightPredicate + * isHighlighted and canHighlight return true. Returns the undecorated component otherwise. + * + * @param component the cell renderer component that is to be decorated + * @param adapter the ComponentAdapter for this decorate operation + * + * @see #canHighlight(Component, ComponentAdapter) + * @see #doHighlight(Component, ComponentAdapter) + * @see #getHighlightPredicate() + */ + @Override + public Component highlight(Component component, ComponentAdapter adapter) { + if (canHighlight(component, adapter) && + getHighlightPredicate().isHighlighted(component, adapter)) { + component = doHighlight(component, adapter); + } + return component; + } + + /** + * Subclasses may override to further limit the highlighting based + * on Highlighter state, f.i. a PainterHighlighter can only be applied + * to PainterAware components.

      + * + * This implementation returns true always. + * + * @param component + * @param adapter + * @return a boolean indication if the adapter can be highlighted based + * general state. This implementation returns true always. + */ + protected boolean canHighlight(Component component, ComponentAdapter adapter) { + return true; + } + + + /** + * Apply the highlights. + * + * @param component the cell renderer component that is to be decorated + * @param adapter the ComponentAdapter for this decorate operation + * + * @see #highlight(Component, ComponentAdapter) + */ + protected abstract Component doHighlight(Component component, + ComponentAdapter adapter); + + + /** + * Returns true if the to objects are either both null or equal + * each other. + * + * @param oneItem one item + * @param anotherItem another item + * @return true if both are null or equal other, false otherwise. + */ + protected boolean areEqual(Object oneItem, Object anotherItem) { + if ((oneItem == null) && (anotherItem == null)) return true; + if (anotherItem != null) { + return anotherItem.equals(oneItem); + } + return false; + } + + //------------------------ implement Highlighter change notification + + /** + * Adds a ChangeListener. ChangeListeners are + * notified after changes of any attribute. + * + * @param l the ChangeListener to add + * @see #removeChangeListener + */ + @Override + public final void addChangeListener(ChangeListener l) { + listenerList.add(ChangeListener.class, l); + } + + /** + * Removes a ChangeListenere. + * + * @param l the ChangeListener to remove + * @see #addChangeListener + */ + @Override + public final void removeChangeListener(ChangeListener l) { + listenerList.remove(ChangeListener.class, l); + } + + /** + * Returns an array of all the change listeners + * registered on this Highlighter. + * + * @return all of this model's ChangeListeners + * or an empty + * array if no change listeners are currently registered + * + * @see #addChangeListener + * @see #removeChangeListener + * + * @since 1.4 + */ + @Override + public final ChangeListener[] getChangeListeners() { + return listenerList.getListeners(ChangeListener.class); + } + + /** + * Notifies registered ChangeListeners about + * state changes.

      + * + * Note: subclasses should be polite and implement any property + * setters to fire only if the property is really changed. + * + */ + protected final void fireStateChanged() { + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length - 2; i >= 0; i -= 2) { + if (listeners[i] == ChangeListener.class) { + if (changeEvent == null) { + changeEvent = new ChangeEvent(this); + } + ((ChangeListener) listeners[i + 1]).stateChanged(changeEvent); + } + } + } + +} diff --git a/src/main/java/org/jdesktop/swingx/decorator/AlignmentHighlighter.java b/src/main/java/org/jdesktop/swingx/decorator/AlignmentHighlighter.java new file mode 100644 index 0000000000..84499f8798 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/decorator/AlignmentHighlighter.java @@ -0,0 +1,160 @@ +/* + * $Id$ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.decorator; + +import javax.swing.*; +import java.awt.*; + +/** + * A Highlighter which sets the horizontal alignment. + * + * @author Jeanette Winzenburg (slight cleanup) + * @author original contributed by swingx member martinm1000 + */ +public class AlignmentHighlighter extends AbstractHighlighter { + private static final int defaultAlignment = SwingConstants.LEADING; + private int alignment; + + + /** + * Instantiates a AlignmentHighlighter with default alignment LEADING. The Highlighter is + * applied always. + */ + public AlignmentHighlighter() { + this(defaultAlignment); + } + + + /** + * Instantiates a AlignmentHighlighter with the specified alignment. The Highlighter is + * applied always. + * + * @param alignment the horizontal alignment to use. + * @throws IllegalArgumentException if not one of the constants allowed as horizontal alignment, + * that is one of LEADING, LEFT, CENTER, RIGHT, TRAILING + */ + public AlignmentHighlighter(int alignment) { + this(null, alignment); + } + + + /** + * Instantiates a FontHighlighter with the given HighlightPredicate and default + * horizontal alignement. + * + * @param predicate the HighlightPredicate to use, may be null to default to ALWAYS. + */ + public AlignmentHighlighter(HighlightPredicate predicate) { + this(predicate, defaultAlignment); + } + + + /** + * Instantiates a FontHighlighter with the given HighlightPredicate and null Font. + * + * @param predicate the HighlightPredicate to use, may be null to default to ALWAYS. + * @param alignment the horizontal alignment to use. + * @throws IllegalArgumentException if not one of the constants allowed as horizontal alignment, + * that is one of LEADING, LEFT, CENTER, RIGHT, TRAILING + */ + public AlignmentHighlighter(HighlightPredicate predicate, int alignment) { + super(predicate); + this.alignment = checkHorizontalAlignment(alignment); + } + + /** + * Returns the alignment which is applied. + * @return the alignment + */ + public int getHorizontalAlignment() { + return alignment; + } + + + /** + * Sets the horizontal alignment to apply. + * + * @param alignment the horizontal alignment to set + * @throws IllegalArgumentException if not one of the constants allowed as horizontal alignment, + * that is one of LEADING, LEFT, CENTER, RIGHT, TRAILING + */ + public void setHorizontalAlignment(int alignment) { + if (getHorizontalAlignment() == alignment) return; + this.alignment = checkHorizontalAlignment(alignment); + fireStateChanged(); + } + + + /** + * Checks if the horizontal alignment is valid. + * + * @param alignment the horizontal alignment to check + * @throws IllegalArgumentException if not one of the constants allowed as horizontal alignment, + * that is one of LEADING, LEFT, CENTER, RIGHT, TRAILING + */ + private int checkHorizontalAlignment(int alignment) { + if ((alignment == SwingConstants.LEFT) || + (alignment == SwingConstants.CENTER) || + (alignment == SwingConstants.RIGHT) || + (alignment == SwingConstants.LEADING) || + (alignment == SwingConstants.TRAILING)) { + return alignment; + } + else { + throw new IllegalArgumentException("invalid horizontal alignment, expected one of " + + SwingConstants.LEFT + " / " + SwingConstants.CENTER + + " / " + SwingConstants.RIGHT + " / " + SwingConstants.LEADING + + " / " + SwingConstants.TRAILING + " but was: " + alignment); + } + } + + /** + * {@inheritDoc}

      + * + * Implemented to set the horizontal alignement of the rendering component. + */ + @Override + protected Component doHighlight(Component renderer, ComponentAdapter adapter) { + if (renderer instanceof JLabel) { + ((JLabel) renderer).setHorizontalAlignment(getHorizontalAlignment()); + } else if (renderer instanceof AbstractButton ) { + ((AbstractButton) renderer).setHorizontalAlignment(getHorizontalAlignment()); + } else { + ((JTextField) renderer).setHorizontalAlignment(getHorizontalAlignment()); + } + return renderer; + } + + /** + * {@inheritDoc}

      + * + * Implemented to return true for components of type JLabel, AbstractButton or JTextField, + * false otherwise. + */ + @Override + protected boolean canHighlight(Component component, ComponentAdapter adapter) { + return (component instanceof JLabel) + || (component instanceof AbstractButton) + || (component instanceof JTextField) + ; + } +} diff --git a/src/main/java/org/jdesktop/swingx/decorator/BorderHighlighter.java b/src/main/java/org/jdesktop/swingx/decorator/BorderHighlighter.java new file mode 100644 index 0000000000..032bc39575 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/decorator/BorderHighlighter.java @@ -0,0 +1,255 @@ +/* + * $Id: BorderHighlighter.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.decorator; + +import javax.swing.*; +import javax.swing.border.Border; +import java.awt.*; + +/** + * A Highlighter that applies a border the the renderer component. + * + * The resulting border can be configured to + * - ignore the component's border, set this highlighter's border + * - compound of this highlighter's border and component border, with + * this highlighter's border either inner or outer. + * + * The default setting is compound outer. + * + */ +public class BorderHighlighter extends AbstractHighlighter { + + private Border paddingBorder; + private boolean inner; + private boolean compound; + + /** + * + * Instantiates a BorderHighlighter with no padding. The + * Highlighter is applied unconditionally. + * + */ + public BorderHighlighter() { + this((HighlightPredicate) null, null); + } + + /** + * + * Instantiates a BorderHighlighter with no padding, using the + * given predicate. + * + * @param predicate the HighlightPredicate to use + * + */ + public BorderHighlighter(HighlightPredicate predicate) { + this(predicate, null); + } + + + /** + * + * Instantiates a BorderHighlighter with the given padding. The + * Highlighter is applied unconditionally. + * + * @param paddingBorder the border to apply as visual decoration. + * + */ + public BorderHighlighter(Border paddingBorder) { + this(null, paddingBorder); + } + + /** + * + * Instantiates a BorderHighlighter with the given padding, + * HighlightPredicate and default compound property. + * If the predicate is null, the highlighter + * will be applied unconditionally. + * + * @param predicate the HighlightPredicate to use + * @param paddingBorder the border to apply as visual decoration. + * + */ + public BorderHighlighter(HighlightPredicate predicate, Border paddingBorder) { + this(predicate, paddingBorder, true); + } + + /** + * + * Instantiates a BorderHighlighter with the given padding, + * HighlightPredicate, compound property and default inner property. + * If the predicate is null, the highlighter + * will be applied unconditionally. + * + * @param predicate the HighlightPredicate to use + * @param paddingBorder the border to apply as visual decoration. + * @param compound the compound property. + * + */ + public BorderHighlighter(HighlightPredicate predicate, + Border paddingBorder, boolean compound) { + this(predicate, paddingBorder, compound, false); + } + + /** + * + * Instantiates a BorderHighlighter with the given padding, + * HighlightPredicate and compound property. If the predicate is null, the highlighter + * will be applied unconditionally. + * + * @param predicate the HighlightPredicate to use + * @param paddingBorder the border to apply as visual decoration. + * @param compound the compound property + * @param inner the inner property + */ + public BorderHighlighter(HighlightPredicate predicate, + Border paddingBorder, boolean compound, boolean inner) { + super(predicate); + this.paddingBorder = paddingBorder; + this.compound = compound; + this.inner = inner; + } + + + + + /** + * {@inheritDoc} + */ + @Override + protected Component doHighlight(Component renderer, ComponentAdapter adapter) { + ((JComponent) renderer).setBorder(compoundBorder( + ((JComponent) renderer).getBorder())); + return renderer; + } + + /** + * {@inheritDoc}

      + * + * Overridden to prevent highlighting if there's no padding available or + * the renderer is not of type JComponent. + * + * @param component the cell renderer component that is to be decorated + * @param adapter the ComponentAdapter for this decorate operation + * @return true padding is available and the + * renderer is a JComponent. False otherwise. + */ + @Override + protected boolean canHighlight(Component component, ComponentAdapter adapter) { + return (getBorder() != null) && (component instanceof JComponent); + } + + /** + * Sets the compound property. If true, the highlight border will be compounded + * with the renderer's border, if any. Otherwise, the highlight border will + * replace the renderer's border.

      + * + * The default value is true; + * + * @param compound a boolean indicating whether the highlight border should be + * compounded with the component's border. + */ + public void setCompound(boolean compound) { + if (isCompound() == compound) return; + this.compound = compound; + fireStateChanged(); + } + + /** + * + * @return the compound property. + * @see #setCompound(boolean) + */ + public boolean isCompound() { + return compound; + } + + /** + * Sets the inner property. If true/false and compounded is enabled + * the highlight border will be the inner/outer border of the compound. + * + * The default value is false; + * + * @param inner a boolean indicating whether the highlight border should be + * compounded as inner or outer border. + * + * @see #isInner() + */ + public void setInner(boolean inner) { + if (isInner() == inner) return; + this.inner = inner; + fireStateChanged(); + } + + /** + * Returns the inner property. + * + * @return the inner property. + * @see #setInner(boolean) + */ + public boolean isInner() { + return inner; + } + + /** + * Sets the Border used for highlighting.

      + * + * The default value is null. + * + * @param padding the Border to use + */ + public void setBorder(Border padding) { + if (areEqual(padding, getBorder())) return; + this.paddingBorder = padding; + fireStateChanged(); + } + + + /** + * Returns the border used for highlighing.

      + * + * @return the border used to highlight. + */ + public Border getBorder() { + return paddingBorder; + } + + + /** + * PRE: paddingBorder != null. + * @param border + * @return + */ + private Border compoundBorder(Border border) { + if (compound) { + if (border != null) { + if (inner) { + return BorderFactory.createCompoundBorder(border, + paddingBorder); + } + return BorderFactory.createCompoundBorder(paddingBorder, + border); + } + } + return paddingBorder; + } + +} + diff --git a/src/main/java/org/jdesktop/swingx/decorator/ColorHighlighter.java b/src/main/java/org/jdesktop/swingx/decorator/ColorHighlighter.java new file mode 100644 index 0000000000..898bd92dde --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/decorator/ColorHighlighter.java @@ -0,0 +1,267 @@ +/* + * $Id: ColorHighlighter.java 4082 2011-11-15 18:39:43Z kschaefe $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.decorator; + + +import java.awt.*; + +import static org.jdesktop.swingx.util.PaintUtils.blend; + +/** + * A Highlighter to modify component colors. + *

      + * As of SwingX 1.6.1, {@code ColorHighlighter} now blends non-opaque colors. + * This will have little effect on previous users, who were likely to be + * using fully-opaque colors. If you are, however, supplying a non-opaque color + * and need it to be considered opaque, use {@link org.jdesktop.swingx.util.PaintUtils#removeAlpha(Color)}. + * + * @author Jeanette Winzenburg + * @author Karl Schaefer + */ +public class ColorHighlighter extends AbstractHighlighter { + + private Color background; + private Color foreground; + private Color selectedBackground; + private Color selectedForeground; + + /** + * Instantiates a ColorHighlighter with null colors and default + * HighlightPredicate. + */ + public ColorHighlighter() { + this(null); + } + + /** + * Instantiates a ColorHighlighter with null colors and uses the + * specified HighlightPredicate. + * + * @param predicate the HighlightPredicate to use. + */ + public ColorHighlighter(HighlightPredicate predicate) { + this(predicate, null, null); + } + + /** + * Constructs a ColorHighlighter with the specified + * background and foreground colors and null section colors. Uses + * the default predicate. + * + * @param cellBackground background color for unselected cell state + * @param cellForeground foreground color for unselected cell state + */ + public ColorHighlighter(Color cellBackground, Color cellForeground) { + this(null, cellBackground, cellForeground); + } + + /** + * Constructs a ColorHighlighter with the specified + * unselected colors and HighlightPredicate. + * Initializes selected colors to null. + * + * @param predicate the HighlightPredicate to use. + * @param cellBackground background color for unselected cell state + * @param cellForeground foreground color for unselected cell state + */ + public ColorHighlighter(HighlightPredicate predicate, Color cellBackground, + Color cellForeground) { + this(predicate, cellBackground, cellForeground, null, null); + } + + /** + * Constructs a ColorHighlighter with the specified + * background and foreground colors for unselected and selected cells. + * Uses the default HighlightPredicate. + * + * @param cellBackground background color for unselected cell state + * @param cellForeground foreground color for unselected cell state + * @param selectedBackground background color for selected cell state + * @param selectedForeground foreground color for selected cell state + */ + public ColorHighlighter(Color cellBackground, Color cellForeground, + Color selectedBackground, Color selectedForeground) { + this(null, cellBackground, cellForeground, selectedBackground, selectedForeground); + } + + + /** + * Constructs a ColorHighlighter with the specified colors + * and HighlightPredicate. + * + * @param predicate the HighlightPredicate to use. + * @param cellBackground background color for unselected cell state + * @param cellForeground foreground color for unselected cell state + * @param selectedBackground background color for selected cell state + * @param selectedForeground foreground color for selected cell state + */ + public ColorHighlighter(HighlightPredicate predicate, Color cellBackground, + Color cellForeground, Color selectedBackground, + Color selectedForeground) { + super(predicate); + this.background = cellBackground; + this.foreground = cellForeground; + this.selectedBackground = selectedBackground; + this.selectedForeground = selectedForeground; + } + + + + /** + * {@inheritDoc} + */ + @Override + protected Component doHighlight(Component renderer, ComponentAdapter adapter) { + applyBackground(renderer, adapter); + applyForeground(renderer, adapter); + return renderer; + } + + + /** + * Applies a suitable background for the renderer component within the + * specified adapter.

      + * + * This implementation applies its background or selectedBackground color + * (depending on the adapter's selected state) if != null. + * Otherwise it does nothing. + * + * @param renderer the cell renderer component that is to be decorated + * @param adapter the ComponentAdapter for this decorate operation + */ + protected void applyBackground(Component renderer, ComponentAdapter adapter) { + Color color = adapter.isSelected() ? getSelectedBackground() : getBackground(); + + renderer.setBackground(blend(renderer.getBackground(), color)); + } + + /** + * Applies a suitable foreground for the renderer component within the + * specified adapter.

      + * + * This implementation applies its foreground or selectedfForeground color + * (depending on the adapter's selected state) if != null. + * Otherwise it does nothing. + * + * @param renderer the cell renderer component that is to be decorated + * @param adapter the ComponentAdapter for this decorate operation + */ + protected void applyForeground(Component renderer, ComponentAdapter adapter) { + Color color = adapter.isSelected() ? getSelectedForeground() : getForeground(); + + renderer.setForeground(blend(renderer.getForeground(), color)); + } + + +//---------------------- state + + /** + * Returns the background color of this ColorHighlighter. + * + * @return the background color of this ColorHighlighter, + * or null, if no background color has been set + */ + public Color getBackground() { + return background; + } + + /** + * Sets the background color of this ColorHighlighter and + * notifies registered ChangeListeners. + * + * @param color the background color of this Highlighter, + * or null, to clear any existing background color + */ + public void setBackground(Color color) { + if (areEqual(color, getBackground())) return; + background = color; + fireStateChanged(); + } + + /** + * Returns the foreground color of this ColorHighlighter. + * + * @return the foreground color of this ColorHighlighter, + * or null, if no foreground color has been set + */ + public Color getForeground() { + return foreground; + } + + /** + * Sets the foreground color of this ColorHighlighter and notifies + * registered ChangeListeners. + * + * @param color the foreground color of this ColorHighlighter, + * or null, to clear any existing foreground color + */ + public void setForeground(Color color) { + if (areEqual(color, getForeground())) return; + foreground = color; + fireStateChanged(); + } + + /** + * Returns the selected background color of this ColorHighlighter. + * + * @return the selected background color of this ColorHighlighter, + * or null, if no selected background color has been set + */ + public Color getSelectedBackground() { + return selectedBackground; + } + + /** + * Sets the selected background color of this ColorHighlighter + * and notifies registered ChangeListeners. + * + * @param color the selected background color of this ColorHighlighter, + * or null, to clear any existing selected background color + */ + public void setSelectedBackground(Color color) { + if (areEqual(color, getSelectedBackground()))return; + selectedBackground = color; + fireStateChanged(); + } + + /** + * Returns the selected foreground color of this ColorHighlighter. + * + * @return the selected foreground color of this ColorHighlighter, + * or null, if no selected foreground color has been set + */ + public Color getSelectedForeground() { + return selectedForeground; + } + + /** + * Sets the selected foreground color of this ColorHighlighter and + * notifies registered ChangeListeners. + * + * @param color the selected foreground color of this ColorHighlighter, + * or null, to clear any existing selected foreground color + */ + public void setSelectedForeground(Color color) { + if (areEqual(color, getSelectedForeground())) return; + selectedForeground = color; + fireStateChanged(); + } +} diff --git a/src/main/java/org/jdesktop/swingx/decorator/ComponentAdapter.java b/src/main/java/org/jdesktop/swingx/decorator/ComponentAdapter.java new file mode 100644 index 0000000000..3bc595133c --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/decorator/ComponentAdapter.java @@ -0,0 +1,567 @@ +/* + * $Id: ComponentAdapter.java 4158 2012-02-03 18:29:40Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.decorator; + +import org.jdesktop.swingx.renderer.StringValues; + +import javax.swing.*; +import java.awt.*; + +/** + * Abstract base class for all component data adapter classes. A + * ComponentAdapter allows the decoration collaborators like f.i. + * {@link Highlighter} to interact with a {@link #target} component through a + * common API.

      + * + * It has two aspects: + *

        + *
      • interact with the view state for the "current" cell. The row/column + * fields and the parameterless methods service this aspect. The coordinates are + * in view coordinate system. + *
      • interact with the data of the component. The methods for this are those + * taking row/column indices as parameters. The coordinates are in model + * coordinate system. + *
      + * + * Typically, application code is interested in the first aspect. An example is + * highlighting the background of a row in a JXTable based on the value of a + * cell in a specific column. The solution is to implement a custom + * HighlightPredicate which decides if a given cell should be highlighted and + * configure a ColorHighlighter with the predicate and an appropriate background + * color. + * + *
      
      + * HighlightPredicate feverWarning = new HighlightPredicate() {
      + *     int temperatureColumn = 10;
      + * 
      + *     public boolean isHighlighted(Component component, ComponentAdapter adapter) {
      + *         return hasFever(adapter.getValue(temperatureColumn));
      + *     }
      + * 
      + *     private boolean hasFever(Object value) {
      + *         if (!value instanceof Number)
      + *             return false;
      + *         return ((Number) value).intValue() > 37;
      + *     }
      + * };
      + * 
      + * Highlighter hl = new ColorHighlighter(feverWarning, Color.RED, null);
      + * 
      + * + * The adapter is responsible for mapping column and row coordinates. + * + * All input column indices are in model coordinates with exactly two + * exceptions: + *
        + *
      • {@link #column} in column view coordinates + *
      • the mapping method {@link #convertColumnIndexToModel(int)} in view coordinates + *
      + * + * All input row indices are in model coordinates with exactly four exceptions: + *
        + *
      • {@link #row} in row view coordinates + *
      • the mapping method {@link #convertRowIndexToModel(int)} in view coordinates + *
      • the getter for the filtered value {@link #getFilteredValueAt(int, int)} + * takes the row in view coordinates. + *
      • the getter for the filtered string representation {@link #getFilteredStringAt(int, int)} + * takes the row in view coordinates. +*
      + * + * + * PENDING JW: anything to gain by generics here?

      + * PENDING JW: formally document that row/column coordinates must be valid in all methods taking + * model coordinates, that is 0<= row < getRowCount(). + * + * @author Ramesh Gupta + * @author Karl Schaefer + * @author Jeanette Winzenburg + * + * @see HighlightPredicate + * @see Highlighter + */ +public abstract class ComponentAdapter { + public static final Object DEFAULT_COLUMN_IDENTIFIER = "Column0"; + /** current row in view coordinates. */ + public int row = 0; + /** current column in view coordinates. */ + public int column = 0; + protected final JComponent target; + + /** + * Constructs a ComponentAdapter, setting the specified component as the + * target component. + * + * @param component target component for this adapter + */ + public ComponentAdapter(JComponent component) { + target = component; + } + + /** + * Returns the component which is this adapter's target. + * + * @return the component which is this adapter's target. + */ + public JComponent getComponent() { + return target; + } + +//---------------------------- accessing the target's model: column meta data + + /** + * Returns the column's display name (= headerValue) of the column + * at columnIndex in model coordinates. + * + * Used f.i. in SearchPanel to fill the field with the + * column name.

      + * + * Note: it's up to the implementation to decide for which + * columns it returns a name - most will do so for the + * subset with isTestable = true. + * + * This implementation delegates to getColumnIdentifierAt and returns it's + * toString or null. + * + * @param columnIndex in model coordinates + * @return column name or null if not found + */ + public String getColumnName(int columnIndex) { + Object identifier = getColumnIdentifierAt(columnIndex); + return identifier != null ? identifier.toString() : null; + } + + + /** + * Returns logical identifier of the column at + * columnIndex in model coordinates. + * + * Note: it's up to the implementation to decide for which + * columns it returns an identifier - most will do so for the + * subset with isTestable = true.

      + * + * This implementation returns DEFAULT_COLUMN_IDENTIFIER. + * + * PENDING JW: This method replaces the old getColumnIdentifier(int) + * which returned a String which is overly restrictive. + * The only way to gently replace this method was + * to add this with a different name - which makes this name suboptimal. + * Probably should rename again once the old has died out ;-) + * + * @param columnIndex in model coordinates, must be valid. + * @return the identifier of the column at columnIndex or null if it has none. + * @throws ArrayIndexOutOfBoundsException if columnIndex < 0 or columnIndex >= getColumnCount(). + * + * + * @see #getColumnIndex(Object) + */ + public Object getColumnIdentifierAt(int columnIndex) { + if ((columnIndex < 0) || (columnIndex >= getColumnCount())) { + throw new ArrayIndexOutOfBoundsException("invalid column index: " + columnIndex); + } + return DEFAULT_COLUMN_IDENTIFIER; + } + + /** + * Returns the column index in model coordinates for the logical identifier. + *

      + * + * This implementation returns 0 if the identifier is the same as the one + * known identifier returned from getColumnIdentifierAt(0), or -1 otherwise. + * So subclasses with one column and a customizable identifier need not + * override. Subclasses which support multiple columns must override this as + * well to keep the contract as in (assuming that the lookup succeeded): + * + *

      
      +     *  Object id = getColumnIdentifierAt(index);
      +     *  assertEquals(index, getColumnIndex(index);
      +     *  // and the reverse 
      +     *  int column = getColumnIndex(identifier);
      +     *  assertEquals(identifier, getColumnIdentifierAt(column));
      +     * 
      + * + * + * @param identifier the column's identifier, must not be null + * @return the index of the column identified by identifier in model + * coordinates or -1 if no column with the given identifier is + * found. + * @throws NullPointerException if identifier is null. + * @see #getColumnIdentifierAt(int) + */ + public int getColumnIndex(Object identifier) { + if (identifier.equals(getColumnIdentifierAt(0))) { + return 0; + } + return -1; + } + + /** + * Returns true if the column should be included in testing.

      + * + * Here: returns true if visible (that is modelToView gives a valid + * view column coordinate). + * + * @param column the column index in model coordinates + * @return true if the column should be included in testing + */ + public boolean isTestable(int column) { + return convertColumnIndexToView(column) >= 0; + } + + /** + * Returns the common class of all data column identified by the given + * column index in model coordinates.

      + * + * This implementation returns Object.class. Subclasses should + * implement as appropriate. + * + * @return the common class of all data given column in model coordinates. + * + * @see #getColumnClass() + */ + public Class getColumnClass(int column) { + return Object.class; + } + + + /** + * Returns the common class of all data in the current column.

      + * + * This implementation delegates to getColumnClass(int) with the current + * column converted to model coordinates. + * + * @return the common class of all data in the current column. + * @see #getColumnClass(int) + */ + public Class getColumnClass() { + return getColumnClass(convertColumnIndexToModel(column)); + } + + + +//---------------------------- accessing the target's model: meta data + /** + * Returns the number of columns in the target's data model. + * + * @return the number of columns in the target's data model. + */ + public int getColumnCount() { + return 1; // default for combo-boxes, lists, and trees + } + + /** + * Returns the number of rows in the target's data model. + * + * @return the number of rows in the target's data model. + */ + public int getRowCount() { + return 0; + } + +//---------------------------- accessing the target's model: data + /** + * Returns the value of the target component's cell identified by the + * specified row and column in model coordinates. + * + * @param row in model coordinates + * @param column in model coordinates + * @return the value of the target component's cell identified by the + * specified row and column + */ + public abstract Object getValueAt(int row, int column); + + /** + * Determines whether this cell is editable. + * + * @param row the row to query in model coordinates + * @param column the column to query in model coordinates + * @return true if the cell is editable, false + * otherwise + */ + public abstract boolean isCellEditable(int row, int column); + + + /** + * Returns the String representation of the value of the cell identified by this adapter. That is, + * for the at position (adapter.row, adapter.column) in view coordinates.

      + * + * NOTE: this implementation assumes that view coordinates == model + * coordinates, that is simply calls getValueAt(this.row, this.column). It is + * up to subclasses to override appropriately is they support model/view + * coordinate transformation.

      + * + * This implementation messages the StringValue.TO_STRING with the getValue, + * subclasses should re-implement and use the API appropriate for the target component type. + * + * @return the String representation of value of the cell identified by this adapter + * @see #getValueAt(int, int) + * @see #getFilteredValueAt(int, int) + * @see #getValue(int) + */ + public String getString() { + return getString(convertColumnIndexToModel(column)); + } + + /** + * Returns the String representation of the value of the cell identified by the current + * adapter row and the given column index in model coordinates.

      + * + * @param modelColumnIndex the column index in model coordinates + * @return the String representation of the value of the cell identified by this adapter + * + * @see #getFilteredStringAt(int, int) + * @see #getString() + */ + public String getString(int modelColumnIndex) { + return getFilteredStringAt(row, modelColumnIndex); + } + + /** + * Returns the String representation of the filtered value of the cell identified by the row + * in view coordinate and the column in model coordinates.

      + * + * Note: the asymetry of the coordinates is intentional - clients like + * Highlighters are interested in view values but might need to access + * non-visible columns for testing. While it is possible to access + * row coordinates different from the current (that is this.row) it is not + * safe to do so for row > this.row because the adapter doesn't allow to + * query the count of visible rows.

      + * + * This implementation messages the StringValue.TO_STRING with the filteredValue, + * subclasses should re-implement and use the API appropriate for the target component type.

      + * + * PENDING JW: what about null cell values? StringValue has a contract to return a + * empty string then, would that be okay here as well? + * + * @param row the row of the cell in view coordinates + * @param column the column of the cell in model coordinates. + * @return the String representation of the filtered value of the cell identified by the row + * in view coordinate and the column in model coordinates + */ + public String getFilteredStringAt(int row, int column) { + return getStringAt(convertRowIndexToModel(row), column); + } + + /** + * Returns the String representation of the value of the cell identified by the row + * specified row and column in model coordinates.

      + * + * This implementation messages the StringValue.TO_STRING with the valueAt, + * subclasses should re-implement and use the api appropriate for the target component type.

      + * + * @param row in model coordinates + * @param column in model coordinates + * @return the value of the target component's cell identified by the + * specified row and column + */ + public String getStringAt(int row, int column) { + return StringValues.TO_STRING.getString(getValueAt(row, column)); + } + + /** + * Returns the value of the cell identified by this adapter. That is, + * for the at position (adapter.row, adapter.column) in view coordinates.

      + * + * NOTE: this implementation assumes that view coordinates == model + * coordinates, that is simply calls getValueAt(this.row, this.column). It is + * up to subclasses to override appropriately is they support model/view + * coordinate transformation. + * + * @return the value of the cell identified by this adapter + * @see #getValueAt(int, int) + * @see #getFilteredValueAt(int, int) + * @see #getValue(int) + */ + public Object getValue() { + return getValue(convertColumnIndexToModel(column)); + } + + + /** + * Returns the value of the cell identified by the current + * adapter row and the given column index in model coordinates.

      + * + * @param modelColumnIndex the column index in model coordinates + * @return the value of the cell identified by this adapter + * @see #getValueAt(int, int) + * @see #getFilteredValueAt(int, int) + * @see #getValue(int) + */ + public Object getValue(int modelColumnIndex) { + return getFilteredValueAt(row, modelColumnIndex); + } + + /** + * Returns the filtered value of the cell identified by the row + * in view coordinate and the column in model coordinates. + * + * Note: the asymmetry of the coordinates is intentional - clients like + * Highlighters are interested in view values but might need to access + * non-visible columns for testing. While it is possible to access + * row coordinates different from the current (that is this.row) it is not + * safe to do so for row > this.row because the adapter doesn't allow to + * query the count of visible rows. + * + * @param row the row of the cell in view coordinates + * @param column the column of the cell in model coordinates. + * @return the filtered value of the cell identified by the row + * in view coordinate and the column in model coordinates + */ + public Object getFilteredValueAt(int row, int column) { + return getValueAt(convertRowIndexToModel(row), column); + } + + //----------------------- accessing the target's view state + + /** + * Returns the bounds of the cell identified by this adapter.

      + * + * @return the bounds of the cell identified by this adapter + */ + public Rectangle getCellBounds() { + return target.getBounds(); + } + + /** + * Returns true if the cell identified by this adapter currently has focus. + * Otherwise, it returns false. + * + * @return true if the cell identified by this adapter currently has focus; + * Otherwise, return false + */ + public abstract boolean hasFocus(); + + /** + * Returns true if the cell identified by this adapter is currently selected. + * Otherwise, it returns false. + * + * @return true if the cell identified by this adapter is currently selected; + * Otherwise, return false + */ + public abstract boolean isSelected(); + + /** + * Returns {@code true} if the cell identified by this adapter is editable, + * {@code false} otherwise. + * + * @return {@code true} if the cell is editable, {@code false} otherwise + */ + public abstract boolean isEditable(); + + /** + * Returns true if the cell identified by this adapter is currently expanded. + * Otherwise, it returns false. For components that do not support + * hierarchical data, this method always returns true because the cells in + * such components can never be collapsed. + * + * @return true if the cell identified by this adapter is currently expanded; + * Otherwise, return false + */ + public boolean isExpanded() { + return true; // sensible default for JList and JTable + } + + /** + * Returns true if the cell identified by this adapter is a leaf node. + * Otherwise, it returns false. For components that do not support + * hierarchical data, this method always returns true because the cells in + * such components can never have children. + * + * @return true if the cell identified by this adapter is a leaf node; + * Otherwise, return false + */ + public boolean isLeaf() { + return true; // sensible default for JList and JTable + } + + /** + * Returns true if the cell identified by this adapter displays the hierarchical node. + * Otherwise, it returns false. For components that do not support + * hierarchical data, this method always returns false because the cells in + * such components can never have children. + * + * @return true if the cell identified by this adapter displays the hierarchical node; + * Otherwise, return false + */ + public boolean isHierarchical() { + return false; // sensible default for JList and JTable + } + + /** + * Returns the depth of this row in the hierarchy where the root is 0. For + * components that do not contain hierarchical data, this method returns 1. + * + * @return the depth for this adapter + */ + public int getDepth() { + return 1; // sensible default for JList and JTable + } + +//-------------------- cell coordinate transformations + + /** + * For target components that support multiple columns in their model, + * along with column reordering in the view, this method transforms the + * specified columnIndex from model coordinates to view coordinates. For all + * other types of target components, this method returns the columnIndex + * unchanged. + * + * @param columnModelIndex index of a column in model coordinates + * @return index of the specified column in view coordinates + */ + public int convertColumnIndexToView(int columnModelIndex) { + return columnModelIndex; // sensible default for JList and JTree + } + + /** + * For target components that support multiple columns in their model, along + * with column reordering in the view, this method transforms the specified + * columnIndex from view coordinates to model coordinates. For all other + * types of target components, this method returns the columnIndex + * unchanged. + * + * @param columnViewIndex index of a column in view coordinates + * @return index of the specified column in model coordinates + */ + public int convertColumnIndexToModel(int columnViewIndex) { + return columnViewIndex; // sensible default for JList and JTree + } + + /** + * Converts a row index in model coordinates to an index in view coordinates. + * + * @param rowModelIndex index of a row in model coordinates + * @return index of the specified row in view coordinates + */ + public int convertRowIndexToView(int rowModelIndex) { + return rowModelIndex; // sensible default for JTree + } + + /** + * Converts a row index in view coordinates to an index in model coordinates. + * + * @param rowViewIndex index of a row in view coordinates + * @return index of the specified row in model coordinates + */ + public int convertRowIndexToModel(int rowViewIndex) { + return rowViewIndex; // sensible default for JTree + } +} diff --git a/src/main/java/org/jdesktop/swingx/decorator/ComponentOrientationHighlighter.java b/src/main/java/org/jdesktop/swingx/decorator/ComponentOrientationHighlighter.java new file mode 100644 index 0000000000..d041898231 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/decorator/ComponentOrientationHighlighter.java @@ -0,0 +1,93 @@ +/* + * Created on 31.03.2011 + * + */ +package org.jdesktop.swingx.decorator; + +import java.awt.*; + +/** + * A Highlighter which applies the ComponentOrientation to the component. + * + * @author Jeanette Winzenburg, Berlin + */ +public class ComponentOrientationHighlighter extends AbstractHighlighter { + + private ComponentOrientation co; + + /** + * Instantiates a ComponentOrientationHighlighter with ComponentOrientation.LEFT_TO_RIGHT. + * The Highlighter is applied always. + */ + public ComponentOrientationHighlighter() { + this((HighlightPredicate) null); + } + + /** + * Instantiates a ComponentOrientationHighlighter with the given HighlightPredicate + * and ComponentOrientation.LEFT_TO_RIGHT. + * + * @param predicate the HighlightPredicate to use, may be null to default to ALWAYS. + */ + public ComponentOrientationHighlighter(HighlightPredicate predicate) { + this(predicate, null); + } + + /** + * Instantiates a ComponentOrientationHighlighter with the given ComponentOrientation. + * The Highlighter is applied always. + * + * @param co the ComponentOrientation to apply + */ + public ComponentOrientationHighlighter(ComponentOrientation co) { + this(null, co); + } + + /** + * Instantiates a ComponentOrientationHighlighter with the given ComponentOrientation and HighlightPredicate. + * + * @param predicate the HighlightPredicate to use, may be null to default to ALWAYS. + * @param co the ComponentOrientation to apply, may be null + */ + public ComponentOrientationHighlighter(HighlightPredicate predicate, + ComponentOrientation co) { + super(predicate); + setComponentOrientation(co); + } + + /** + * Returns the ComponentOrientation to apply. + * + * @return the ComponentOrientation to apply, guaranteed to be not null. + */ + public ComponentOrientation getComponentOrientation() { + return co; + } + + /** + * Sets the ComponentOrientation to apply. + * + * @param co the co to set, may be null to denote fallback to LEFT_TO_RIGHT + */ + public void setComponentOrientation(ComponentOrientation co) { + if (co == null) { + co = ComponentOrientation.LEFT_TO_RIGHT; + } + if (areEqual(this.co, co)) return; + this.co = co; + fireStateChanged(); + } + + /** + * @inherited

      + * Implementated to decorate the given component with the ComponentOrientation. + */ + @Override + protected Component doHighlight(Component component, + ComponentAdapter adapter) { + component.applyComponentOrientation(getComponentOrientation()); + return component; + } + + +} diff --git a/src/main/java/org/jdesktop/swingx/decorator/CompoundHighlighter.java b/src/main/java/org/jdesktop/swingx/decorator/CompoundHighlighter.java new file mode 100644 index 0000000000..82d8a89545 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/decorator/CompoundHighlighter.java @@ -0,0 +1,243 @@ +/* + * $Id: CompoundHighlighter.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.decorator; + +import org.jdesktop.swingx.plaf.UIDependent; +import org.jdesktop.swingx.util.Contract; + +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import java.awt.*; +import java.util.ArrayList; +import java.util.List; + +/** + * A class which manages the lists of Highlighters. + * + * @see Highlighter + * + * @author Ramesh Gupta + * @author Jeanette Winzenburg + * + */ +public class CompoundHighlighter extends AbstractHighlighter + implements UIDependent { + public static final Highlighter[] EMPTY_HIGHLIGHTERS = new Highlighter[0]; + + protected List highlighters; + + /** the listener for changes in contained Highlighters. */ + private ChangeListener highlighterChangeListener; + + + /** + * Instantiates a CompoundHighlighter containing the given + * Highlighters. + * + * @param inList zero or more not-null Highlighters to manage by this + * CompoundHighlighter. + * @throws NullPointerException if array is null or array contains null values. + */ + public CompoundHighlighter(Highlighter... inList) { + this(null, inList); + } + + /** + * Instantiates a CompoundHighlighter with the given predicate containing the given + * Highlighters. + * + * @param predicate the highlightPredicate to use + * @param inList zero or more not-null Highlighters to manage by this + * CompoundHighlighter. + * @throws NullPointerException if array is null or array contains null values. + */ + public CompoundHighlighter(HighlightPredicate predicate, Highlighter... inList) { + super(predicate); + highlighters = new ArrayList(); + setHighlighters(inList); + } + + /** + * Sets the given + * Highlighters. + * + * @param inList zero or more not-null Highlighters to manage by this + * CompoundHighlighter. + * @throws NullPointerException if array is null or array contains null values. + */ + public void setHighlighters(Highlighter... inList) { + Contract.asNotNull(inList, "Highlighter must not be null"); + if (highlighters.isEmpty() && (inList.length == 0)) return; + removeAllHighlightersSilently(); + for (Highlighter highlighter : inList) { + addHighlighterSilently(highlighter, false); + } + fireStateChanged(); + } + + /** + * Removes all contained highlighters without firing an event. + * Deregisters the listener from all. + */ + private void removeAllHighlightersSilently() { + for (Highlighter highlighter : highlighters) { + highlighter.removeChangeListener(getHighlighterChangeListener()); + } + highlighters.clear(); + } + + /** + * Appends a highlighter to the pipeline. + * + * @param highlighter highlighter to add + * @throws NullPointerException if highlighter is null. + */ + public void addHighlighter(Highlighter highlighter) { + addHighlighter(highlighter, false); + } + + /** + * Adds a highlighter to the pipeline. + * + * PENDING: Duplicate inserts? + * + * @param highlighter highlighter to add + * @param prepend prepend the highlighter if true; false will append + * @throws NullPointerException if highlighter is null. + */ + public void addHighlighter(Highlighter highlighter, boolean prepend) { + addHighlighterSilently(highlighter, prepend); + fireStateChanged(); + } + + private void addHighlighterSilently(Highlighter highlighter, boolean prepend) { + Contract.asNotNull(highlighter, "Highlighter must not be null"); + if (prepend) { + highlighters.add(0, highlighter); + } else { + highlighters.add(highlighters.size(), highlighter); + } + updateUI(highlighter); + highlighter.addChangeListener(getHighlighterChangeListener()); + } + + /** + * Removes a highlighter from the pipeline. + * + * + * @param hl highlighter to remove + */ + public void removeHighlighter(Highlighter hl) { + boolean success = highlighters.remove(hl); + if (success) { + // PENDING: duplicates? + hl.removeChangeListener(getHighlighterChangeListener()); + fireStateChanged(); + } + // should log if this didn't succeed. Maybe + } + + /** + * Returns an array of contained Highlighters. + * + * @return the contained Highlighters, might be empty but never null. + */ + public Highlighter[] getHighlighters() { + if (highlighters.isEmpty()) return EMPTY_HIGHLIGHTERS; + return highlighters.toArray(new Highlighter[highlighters.size()]); + } + +//--------------------- implement UIDependent + + /** + * {@inheritDoc}

      + * + * Implemented to call updateUI on contained Highlighters. + */ + @Override + public void updateUI() { + for (Highlighter highlighter : highlighters) { + updateUI(highlighter); + } + } + + /** + * Returns the ChangeListner to contained + * Highlighters. The listener is lazily created. + * + * @return the listener for contained highlighters, guaranteed + * to be not null. + */ + protected ChangeListener getHighlighterChangeListener() { + if (highlighterChangeListener == null) { + highlighterChangeListener = createHighlighterChangeListener(); + } + return highlighterChangeListener; + } + + /** + * Creates and returns the ChangeListener registered to + * contained Highlighters. Here: fires a + * stateChanged on each notification. + * + * @return the listener for contained Highlighters. + * + */ + protected ChangeListener createHighlighterChangeListener() { + return highlighterChangeListener = new ChangeListener() { + + @Override + public void stateChanged(ChangeEvent e) { + fireStateChanged(); + } + + }; + } + + /** + * Updates the ui-dependent state of the given Highlighter. + * + * @param hl the highlighter to update. + */ + private void updateUI(Highlighter hl) { + if (hl instanceof UIDependent) { + ((UIDependent) hl).updateUI(); + } + } + + +//------------------- implement Highlighter + + /** + * {@inheritDoc} + */ + @Override + protected Component doHighlight(Component stamp, ComponentAdapter adapter) { + for (Highlighter highlighter : highlighters) { + stamp = highlighter.highlight(stamp, adapter); + } + return stamp; + } + + + +} diff --git a/src/main/java/org/jdesktop/swingx/decorator/EnabledHighlighter.java b/src/main/java/org/jdesktop/swingx/decorator/EnabledHighlighter.java new file mode 100644 index 0000000000..fad173128b --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/decorator/EnabledHighlighter.java @@ -0,0 +1,112 @@ +/* + * $Id$ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.decorator; + +import java.awt.*; + +/** + * A Highlighter which sets the enabled property.

      + * + * Note: the enabled is a mutable property of this Highlighter which defaults to false + * because we assume that's the most common use case to make a rendering component + * look disabled when the parent is enabled. It's mutable for symmetry reasons, though + * the other way round - enabled looking rendering component on a disabled parent - + * most probably will confuse users. + * + * @author Jeanette Winzenburg (slight cleanup) + * @author original contributed by swingx member martinm1000 + */ +public class EnabledHighlighter extends AbstractHighlighter { + + private boolean enabled; + + /** + * Instantiates a EnabledHighlighter with default enabled property (== false). + * The Highlighter is applied always. + */ + public EnabledHighlighter() { + this(null); + } + + /** + * Instantiates a EnabledHighlighter with the specified enabled property. + * The Highlighter is applied always. + * + * @param enabled the enabled property + */ + public EnabledHighlighter(boolean enabled) { + this(null, enabled); + } + + /** + * Instantiates a EnabledHighlighter with the specified HighlightPredicate and + * default enabled property (== false). + * @param predicate the HighlightPredicate to use, may be null to default to ALWAYS. + */ + public EnabledHighlighter(HighlightPredicate predicate) { + this(predicate, false); + } + + /** + * Instantiates a EnabledHighlighter with the specified HighlightPredicate and + * default enabled property. + * @param predicate the HighlightPredicate to use, may be null to default to ALWAYS. + * @param enabled the enabled property + */ + public EnabledHighlighter(HighlightPredicate predicate, boolean enabled) { + super(predicate); + this.enabled = enabled; + } + + + /** + * Returns the enabled property. + * @return the enabled + */ + public boolean isEnabled() { + return enabled; + } + + /** + * Sets the enabled property. The default value is false. + * + * @param enabled the enabled to set + */ + public void setEnabled(boolean enabled) { + if (isEnabled() == enabled) return; + this.enabled = enabled; + fireStateChanged(); + } + + /** + * {@inheritDoc}

      + * + * Implemented to set the rendering component's enabled property. + */ + @Override + protected Component doHighlight(Component renderer, ComponentAdapter adapter) { + renderer.setEnabled(enabled); + return renderer; + } + + +} diff --git a/src/main/java/org/jdesktop/swingx/decorator/FontHighlighter.java b/src/main/java/org/jdesktop/swingx/decorator/FontHighlighter.java new file mode 100644 index 0000000000..36ea9d849e --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/decorator/FontHighlighter.java @@ -0,0 +1,117 @@ +/* + * $Id: FontHighlighter.java 1164 2009-11-03 04:22:00Z kschaefe $ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.decorator; + +import java.awt.*; + +/** + * A Highlighter which sets the Font of the component.

      + * + * @author Karl George Schaefer + * + */ +public class FontHighlighter extends AbstractHighlighter { + private Font font; + + /** + * Instantiates a FontHighlighter with null Font. The Highlighter is + * applied always. + */ + public FontHighlighter() { + this((HighlightPredicate) null); + } + + /** + * Instantiates a FontHighlighter with the given Font. The Highlighter is + * applied always. + * + * @param font the Font to apply + */ + public FontHighlighter(Font font) { + this(null, font); + } + + /** + * Instantiates a FontHighlighter with the given HighlightPredicate and null Font. + * + * @param predicate the HighlightPredicate to use, may be null to default to ALWAYS. + */ + public FontHighlighter(HighlightPredicate predicate) { + this(predicate, null); + } + + /** + * Instantiates a FontHighlighter with the given Font and HighlightPredicate. + * + * @param predicate the HighlightPredicate to use, may be null to default to ALWAYS. + * @param font the Font to apply, may be null + */ + public FontHighlighter(HighlightPredicate predicate, Font font) { + super(predicate); + this.font = font; + } + + /** + * Returns the Font used for decoration. + * + * @return the Font used for decoration + * + * @see #setFont(Font) + */ + public Font getFont() { + return font; + } + + /** + * Sets the Font used for decoration. May be null to not decorate. + * + * @param font the Font used for decoration, may be null to not decorate. + * + * @see #getFont() + */ + public void setFont(Font font) { + if (areEqual(font, getFont())) return; + this.font = font; + fireStateChanged(); + } + + /** + * {@inheritDoc}

      + * + * Implemented to return false if the font property is null. + */ + @Override + protected boolean canHighlight(Component component, ComponentAdapter adapter) { + return font != null; + } + + /** + * {@inheritDoc}

      + * + * Implemented to set the component's Font. + */ + @Override + protected Component doHighlight(Component component, ComponentAdapter adapter) { + component.setFont(font); + return component; + } +} diff --git a/src/main/java/org/jdesktop/swingx/decorator/HighlightPredicate.java b/src/main/java/org/jdesktop/swingx/decorator/HighlightPredicate.java new file mode 100644 index 0000000000..1f8e90e9ee --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/decorator/HighlightPredicate.java @@ -0,0 +1,825 @@ +/* + * $Id: HighlightPredicate.java 3935 2011-03-02 19:06:41Z kschaefe $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.decorator; + +import org.jdesktop.swingx.rollover.RolloverProducer; +import org.jdesktop.swingx.util.Contract; + +import javax.swing.*; +import java.awt.*; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +/** + * A controller which decides whether or not a visual decoration should + * be applied to the given Component in the given ComponentAdapter state. + * This is a on/off decision only, the actual decoration is + * left to the AbstractHighlighter which typically respects this predicate.

      + * + * Note: implementations should be immutable because Highlighters + * guarantee to notify listeners on any state change which might effect the highlight. + * They can't comply to that contract if predicate internal state changes under their + * feet. If dynamic predicate state is required, the safe alternative is to create + * and set a new predicate.

      + * + * + * @author Jeanette Winzenburg + * + * @see AbstractHighlighter + */ +public interface HighlightPredicate { + + /** + * Returns a boolean to indicate whether the component should be + * highlighted.

      + * + * Note: both parameters should be considered strictly read-only! + * + * @param renderer the cell renderer component that is to be decorated, + * must not be null + * @param adapter the ComponentAdapter for this decorate operation, + * most not be null + * @return a boolean to indicate whether the component should be highlighted. + */ + boolean isHighlighted(Component renderer, ComponentAdapter adapter); + + +//--------------------- implemented Constants + /** + * Unconditional true. + */ + public static final HighlightPredicate ALWAYS = new HighlightPredicate() { + + /** + * {@inheritDoc}

      + * + * Implemented to return true always. + */ + @Override + public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { + return true; + } + + }; + + /** + * Unconditional false. + */ + public static final HighlightPredicate NEVER = new HighlightPredicate() { + + /** + * {@inheritDoc}

      + * + * Implemented to return false always. + */ + @Override + public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { + return false; + } + + }; + + /** + * Rollover Row. + */ + public static final HighlightPredicate ROLLOVER_ROW = new HighlightPredicate() { + + /** + * @inheritDoc + * Implemented to return true if the adapter's component is enabled and + * the row of its rollover property equals the adapter's row, returns + * false otherwise. + * + * @see RolloverProducer + */ + @Override + public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { + if (!adapter.getComponent().isEnabled()) return false; + Point p = (Point) adapter.getComponent().getClientProperty( + RolloverProducer.ROLLOVER_KEY); + return p != null && p.y == adapter.row; + } + + }; + + /** + * Rollover Column. + */ + public static final HighlightPredicate ROLLOVER_COLUMN = new HighlightPredicate() { + + /** + * @inheritDoc + * Implemented to return true if the adapter's component is enabled and + * the column of its rollover property equals the adapter's columns, returns + * false otherwise. + * + * @see RolloverProducer + */ + @Override + public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { + if (!adapter.getComponent().isEnabled()) return false; + Point p = (Point) adapter.getComponent().getClientProperty( + RolloverProducer.ROLLOVER_KEY); + return p != null && p.x == adapter.column; + } + + }; + /** + * Rollover Cell. + */ + public static final HighlightPredicate ROLLOVER_CELL = new HighlightPredicate() { + + /** + * @inheritDoc + * Implemented to return true if the adapter's component is enabled and + * the column of its rollover property equals the adapter's columns, returns + * false otherwise. + * + * @see RolloverProducer + */ + @Override + public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { + if (!adapter.getComponent().isEnabled()) return false; + Point p = (Point) adapter.getComponent().getClientProperty( + RolloverProducer.ROLLOVER_KEY); + return p != null && p.y == adapter.row && p.x == adapter.column; + } + + }; + + /** + * Is editable. + */ + public static final HighlightPredicate EDITABLE = new HighlightPredicate() { + /** + * {@inheritDoc}

      + * + * Implemented to return true is the given adapter isEditable, false otherwise. + */ + @Override + public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { + return adapter.isEditable(); + } + }; + + /** + * Convenience for read-only (same as !editable). + */ + public static final HighlightPredicate READ_ONLY = new HighlightPredicate() { + /** + * {@inheritDoc}

      + * + * Implemented to return false is the given adapter isEditable, true otherwise. + */ + @Override + public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { + return !adapter.isEditable(); + } + }; + + /** + * Leaf predicate. + */ + public static final HighlightPredicate IS_LEAF = new HighlightPredicate() { + /** + * {@inheritDoc}

      + * + * Implemented to return true if the given adapter isLeaf, false otherwise. + */ + @Override + public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { + return adapter.isLeaf(); + } + }; + + /** + * Folder predicate - convenience: same as !IS_LEAF. + */ + public static final HighlightPredicate IS_FOLDER = new HighlightPredicate() { + /** + * {@inheritDoc}

      + * + * Implemented to return false if the given adapter isLeaf, true otherwise. + */ + @Override + public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { + return !adapter.isLeaf(); + } + }; + + /** + * Selected predicate. + */ + public static final HighlightPredicate IS_SELECTED = new HighlightPredicate() { + + @Override + public boolean isHighlighted(Component renderer, + ComponentAdapter adapter) { + return adapter.isSelected(); + } + + }; + + /** + * Determines if the displayed text is truncated. + * + * @author Karl Schaefer + */ + public static final HighlightPredicate IS_TEXT_TRUNCATED = new HighlightPredicate() { + @Override + public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { + JComponent c = renderer instanceof JComponent ? (JComponent) renderer : null; + String text = adapter.getString(); + Icon icon = null; + //defaults from JLabel + int verticalAlignment = SwingConstants.CENTER; + int horizontalAlignment = SwingConstants.LEADING; + int verticalTextPosition = SwingConstants.CENTER; + int horizontalTextPosition = SwingConstants.TRAILING; + int gap = 0; + + if (renderer instanceof JLabel) { + icon = ((JLabel) renderer).getIcon(); + gap = ((JLabel) renderer).getIconTextGap(); + } else if (renderer instanceof AbstractButton) { + icon = ((AbstractButton) renderer).getIcon(); + gap = ((AbstractButton) renderer).getIconTextGap(); + } + + Rectangle cellBounds = adapter.getCellBounds(); + if (c != null && c.getBorder() != null) { + Insets insets = c.getBorder().getBorderInsets(c); + cellBounds.width -= insets.left + insets.right; + cellBounds.height -= insets.top + insets.bottom; + } + + String result = SwingUtilities.layoutCompoundLabel(c, renderer + .getFontMetrics(renderer.getFont()), text, icon, verticalAlignment, + horizontalAlignment, verticalTextPosition, horizontalTextPosition, cellBounds, + new Rectangle(), new Rectangle(), gap); + + return !text.equals(result); + } + }; + + /** + * Focus predicate. + */ + public static final HighlightPredicate HAS_FOCUS = new HighlightPredicate() { + /** + * {@inheritDoc}

      + * + * Implemented to return truw if the given adapter hasFocus, false otherwise. + */ + @Override + public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { + return adapter.hasFocus(); + } + }; + /** + * Even rows. + * + * PENDING: this is zero based (that is "really" even 0, 2, 4 ..), differing + * from the old AlternateRowHighlighter. + * + */ + public static final HighlightPredicate EVEN = new HighlightPredicate() { + + @Override + public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { + return adapter.row % 2 == 0; + } + + }; + + /** + * Odd rows. + * + * PENDING: this is zero based (that is 1, 3, 4 ..), differs from + * the old implementation which was one based? + * + */ + public static final HighlightPredicate ODD = new HighlightPredicate() { + + @Override + public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { + return !EVEN.isHighlighted(renderer, adapter); + } + + }; + + /** + * Negative BigDecimals. + */ + public static final HighlightPredicate BIG_DECIMAL_NEGATIVE = new HighlightPredicate() { + + @Override + public boolean isHighlighted(Component renderer, + ComponentAdapter adapter) { + return (adapter.getValue() instanceof BigDecimal) + && ((BigDecimal) adapter.getValue()).compareTo(BigDecimal.ZERO) < 0; + } + + }; + + /** + * Negative Number. + */ + public static final HighlightPredicate INTEGER_NEGATIVE = new HighlightPredicate() { + + @Override + public boolean isHighlighted(Component renderer, + ComponentAdapter adapter) { + return (adapter.getValue() instanceof Number) + && ((Number) adapter.getValue()).intValue() < 0; + } + + }; + + // PENDING: these general type empty arrays don't really belong here? + public static final HighlightPredicate[] EMPTY_PREDICATE_ARRAY = new HighlightPredicate[0]; + public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0]; + public static final Integer[] EMPTY_INTEGER_ARRAY = new Integer[0]; + +//----------------- logical implementations amongst HighlightPredicates + + /** + * Negation of a HighlightPredicate. + */ + public static class NotHighlightPredicate implements HighlightPredicate { + + private HighlightPredicate predicate; + + /** + * Instantiates a not against the given predicate. + * @param predicate the predicate to negate, must not be null. + * @throws NullPointerException if the predicate is null + */ + public NotHighlightPredicate(HighlightPredicate predicate) { + if (predicate == null) + throw new NullPointerException("predicate must not be null"); + this.predicate = predicate; + } + + /** + * {@inheritDoc} + * Implemented to return the negation of the given predicate. + */ + @Override + public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { + return !predicate.isHighlighted(renderer, adapter); + } + + /** + * @return the contained HighlightPredicate. + */ + public HighlightPredicate getHighlightPredicate() { + return predicate; + } + + } + + /** + * Ands a list of predicates. + */ + public static class AndHighlightPredicate implements HighlightPredicate { + + private List predicate; + + /** + * Instantiates a predicate which ands all given predicates. + * @param predicate zero or more not null predicates to and + * @throws NullPointerException if the predicate is null + */ + public AndHighlightPredicate(HighlightPredicate... predicate) { + this.predicate = Arrays.asList(Contract.asNotNull(predicate, "predicate must not be null")); + } + + /** + * Instantiates a predicate which ANDs all contained predicates. + * @param list a collection with zero or more not null predicates to AND + * @throws NullPointerException if the collection is null + */ + public AndHighlightPredicate(Collection list) { + this.predicate = new ArrayList(Contract.asNotNull(list, "predicate list must not be null")); + } + + /** + * {@inheritDoc} + * Implemented to return false if any of the contained predicates is + * false or if there are no predicates. + */ + @Override + public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { + for (HighlightPredicate hp : predicate) { + if (!hp.isHighlighted(renderer, adapter)) return false; + } + return !predicate.isEmpty(); + } + + /** + * @return the contained HighlightPredicates. + */ + public HighlightPredicate[] getHighlightPredicates() { + if (predicate.isEmpty()) return EMPTY_PREDICATE_ARRAY; + return predicate.toArray(new HighlightPredicate[predicate.size()]); + } + + + } + + /** + * Or's a list of predicates. + */ + public static class OrHighlightPredicate implements HighlightPredicate { + + private List predicate; + + /** + * Instantiates a predicate which ORs all given predicates. + * @param predicate zero or more not null predicates to OR + * @throws NullPointerException if the predicate is null + */ + public OrHighlightPredicate(HighlightPredicate... predicate) { + this.predicate = Arrays.asList(Contract.asNotNull(predicate, "predicate must not be null")); + } + + /** + * Instantiates a predicate which ORs all contained predicates. + * @param list a collection with zero or more not null predicates to OR + * @throws NullPointerException if the collection is null + */ + public OrHighlightPredicate(Collection list) { + this.predicate = new ArrayList(Contract.asNotNull(list, "predicate list must not be null")); + } + + /** + * {@inheritDoc} + * Implemented to return true if any of the contained predicates is + * true. + */ + @Override + public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { + for (HighlightPredicate hp : predicate) { + if (hp.isHighlighted(renderer, adapter)) return true; + } + return false; + } + /** + * @return all registered predicates + */ + public HighlightPredicate[] getHighlightPredicates() { + if (predicate.isEmpty()) return EMPTY_PREDICATE_ARRAY; + return predicate.toArray(new HighlightPredicate[predicate.size()]); + } + + } + +//------------------------ coordinates + + public static class RowGroupHighlightPredicate implements HighlightPredicate { + + private int linesPerGroup; + + /** + * Instantiates a predicate with the given grouping. + * + * @param linesPerGroup number of lines constituting a group, must + * be > 0 + * @throws IllegalArgumentException if linesPerGroup < 1 + */ + public RowGroupHighlightPredicate(int linesPerGroup) { + if (linesPerGroup < 1) + throw new IllegalArgumentException("a group contain at least 1 row, was: " + linesPerGroup); + this.linesPerGroup = linesPerGroup; + } + + /** + * {@inheritDoc} + * Implemented to return true if the adapter's row falls into a + * odd group number. + */ + @Override + public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { + // JW: oddness check is okay - adapter.row must be a valid view coordinate + return (adapter.row / linesPerGroup) % 2 == 1; + } + + /** + * + * @return the number of lines per group. + */ + public int getLinesPerGroup() { + return linesPerGroup; + } + + } + + /** + * A HighlightPredicate based on column index. + * + */ + public static class ColumnHighlightPredicate implements HighlightPredicate { + List columnList; + + /** + * Instantiates a predicate which returns true for the + * given columns in model coordinates. + * + * @param columns the columns to highlight in model coordinates. + */ + public ColumnHighlightPredicate(int... columns) { + columnList = new ArrayList(); + for (int i = 0; i < columns.length; i++) { + columnList.add(columns[i]); + } + } + + /** + * {@inheritDoc} + * + * This implementation returns true if the adapter's column + * is contained in this predicates list. + * + */ + @Override + public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { + int modelIndex = adapter.convertColumnIndexToModel(adapter.column); + return columnList.contains(modelIndex); + } + + /** + * PENDING JW: get array of int instead of Integer? + * + * @return the columns indices in model coordinates to highlight + */ + public Integer[] getColumns() { + if (columnList.isEmpty()) return EMPTY_INTEGER_ARRAY; + return columnList.toArray(new Integer[columnList.size()]); + } + + } + + + /** + * A HighlightPredicate based on column identifier. + * + */ + public static class IdentifierHighlightPredicate implements HighlightPredicate { + List columnList; + + /** + * Instantiates a predicate which returns true for the + * given column identifiers. + * + * @param columns the identitiers of the columns to highlight. + */ + public IdentifierHighlightPredicate(Object... columns) { + columnList = new ArrayList(); + for (int i = 0; i < columns.length; i++) { + columnList.add(columns[i]); + } + } + + /** + * {@inheritDoc} + * + * This implementation returns true if the adapter's column + * is contained in this predicates list. + * + */ + @Override + public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { + int modelIndex = adapter.convertColumnIndexToModel(adapter.column); + Object identifier = adapter.getColumnIdentifierAt(modelIndex); + return identifier != null ? columnList.contains(identifier) : false; + } + + /** + * @return the identifiers + */ + public Object[] getIdentifiers() { + if (columnList.isEmpty()) return EMPTY_OBJECT_ARRAY; + return columnList.toArray(new Object[0]); + } + + } + + + + /** + * A {@code HighlightPredicate} based on adapter depth. + * + * @author Karl Schaefer + */ + public static class DepthHighlightPredicate implements HighlightPredicate { + private List depthList; + + /** + * Instantiates a predicate which returns true for the + * given depths. + * + * @param depths the depths to highlight + */ + public DepthHighlightPredicate(int... depths) { + depthList = new ArrayList(); + for (int i = 0; i < depths.length; i++) { + depthList.add(depths[i]); + } + } + + /** + * {@inheritDoc} + * + * This implementation returns true if the adapter's depth is contained + * in this predicates list. + * + */ + @Override + public boolean isHighlighted(Component renderer, + ComponentAdapter adapter) { + int depth = adapter.getDepth(); + return depthList.contains(depth); + } + + /** + * @return array of numbers representing different depths + */ + public Integer[] getDepths() { + if (depthList.isEmpty()) return EMPTY_INTEGER_ARRAY; + return depthList.toArray(new Integer[depthList.size()]); + } + + } + + //--------------------- value testing + + /** + * Predicate testing the componentAdapter value against a fixed + * Object. + */ + public static class EqualsHighlightPredicate implements HighlightPredicate { + + private Object compareValue; + + /** + * Instantitates a predicate with null compare value. + * + */ + public EqualsHighlightPredicate() { + this(null); + } + /** + * Instantiates a predicate with the given compare value. + * PENDING JW: support array? + * @param compareValue the fixed value to compare the + * adapter against. + */ + public EqualsHighlightPredicate(Object compareValue) { + this.compareValue = compareValue; + } + + /** + * {@inheritDoc} + * + * Implemented to return true if the adapter value equals the + * this predicate's compare value. + */ + @Override + public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { + if (compareValue == null) return adapter.getValue() == null; + return compareValue.equals(adapter.getValue()); + } + + /** + * @return the value this predicate checks against. + */ + public Object getCompareValue() { + return compareValue; + } + + } + + /** + * Predicate testing the componentAdapter value type against a given + * Class. + */ + public static class TypeHighlightPredicate implements HighlightPredicate { + + private Class clazz; + + /** + * Instantiates a predicate with Object.clazz. This is essentially the + * same as testing the adapter's value against null. + * + */ + public TypeHighlightPredicate() { + this(Object.class); + } + + /** + * Instantiates a predicate with the given compare class.

      + * + * PENDING JW: support array? + * + * @param compareValue the fixed class to compare the + * adapter value against, must not be null + * + * @throws NullPointerException if the class is null. + */ + public TypeHighlightPredicate(Class compareValue) { + this.clazz = Contract.asNotNull(compareValue, "compare class must not be null"); + } + + /** + * {@inheritDoc} + * + * Implemented to return true if the adapter value is an instance + * of this predicate's class type. + */ + @Override + public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { + return adapter.getValue() != null ? + clazz.isAssignableFrom(adapter.getValue().getClass()) : false; + } + + /** + * @return type of predicate compare class + */ + public Class getType() { + return clazz; + } + + } + + /** + * Predicate testing the componentAdapter column type against a given + * Class. + */ + public static class ColumnTypeHighlightPredicate implements HighlightPredicate { + + private Class clazz; + + /** + * Instantitates a predicate with Object.class.

      + * + * PENDING JW: this constructor is not very useful ... concrete implementations of + * ComponentAdapter are required to return a not-null from their + * getColumnClass() methods). + * + */ + public ColumnTypeHighlightPredicate() { + this(Object.class); + } + + /** + * Instantitates a predicate with the given compare class. + * + * @param compareValue the fixed class to compare the + * adapter's column class against, must not be null + * + * @throws NullPointerException if the class is null. + * + */ + public ColumnTypeHighlightPredicate(Class compareValue) { + this.clazz = Contract.asNotNull(compareValue, "compare class must not be null"); + } + + /** + * @inheritDoc + * + * Implemented to return true if the adapter value is an instance + * of this predicate's class type. + */ + @Override + public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { + return clazz.isAssignableFrom(adapter.getColumnClass()); + } + + public Class getType() { + return clazz; + } + + } +} diff --git a/src/main/java/org/jdesktop/swingx/decorator/Highlighter.java b/src/main/java/org/jdesktop/swingx/decorator/Highlighter.java new file mode 100644 index 0000000000..f7a19cae4c --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/decorator/Highlighter.java @@ -0,0 +1,103 @@ +/* + * $Id: Highlighter.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.decorator; + +import javax.swing.event.ChangeListener; +import java.awt.*; + +/** + * Highlighter provide a mechanism to modify visual attributes of + * cell rendering components. The mechanism is uniform across both rendered and + * rendering component types: it is the same for SwingX collection views + * (JXTable, JXList, JXTree/Table) and independent of the concrete component + * type used for rendering the cell. The view cell state is factored into a + * ComponentAdapter. + *

      + * + * For example, in data visualization components that support multiple columns + * with potentially different types of data, a ColorHighlighter + * imparts the same background color consistently across all columns + * of the rendered component regardless of the actual cell renderer registered + * for any specific column. + *

      + * + * The highlightable properties are basically defined by the renderer in use: + * only attributes the renderer guarantees to reset on every call are safe to + * alter. For SwingX renderering support these are listed in + * ComponentProvider. + * + * + * Implementations supporting mutable internal state which effects the + * decoration must notify its listeners about the change. Typically, the + * rendered component installs a listener to its Highlighters + * and triggeres a repaint on notification. + * + * @see ComponentAdapter + * @see org.jdesktop.swingx.renderer.ComponentProvider + * + * @author Ramesh Gupta + * @author Jeanette Winzenburg + */ +public interface Highlighter { + + /** + * Decorates the specified component for the given component + * adapter. + * + * @param renderer the cell rendering component that is to be decorated + * @param adapter the ComponentAdapter for this decorate operation + * @return the decorated cell rendering component + */ + Component highlight(Component renderer, ComponentAdapter adapter); + + /** + * Adds a ChangeListener which are + * notified after changes of any attribute. + * + * @param l the ChangeListener to add + * @see #removeChangeListener + */ + void addChangeListener(ChangeListener l); + + /** + * Removes a ChangeListener. + * + * @param l the ChangeListener to remove + * @see #addChangeListener + */ + void removeChangeListener(ChangeListener l); + + /** + * Returns an array of all the change listeners + * registered on this LegacyHighlighter. + * + * @return all of this model's ChangeListeners + * or an empty + * array if no change listeners are currently registered + * + * @see #addChangeListener + * @see #removeChangeListener + * + * @since 1.4 + */ + ChangeListener[] getChangeListeners(); + +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/decorator/HighlighterFactory.java b/src/main/java/org/jdesktop/swingx/decorator/HighlighterFactory.java new file mode 100644 index 0000000000..a1b0eb0ee5 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/decorator/HighlighterFactory.java @@ -0,0 +1,292 @@ +/* + * $Id: HighlighterFactory.java 4082 2011-11-15 18:39:43Z kschaefe $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.decorator; + +import org.jdesktop.swingx.decorator.HighlightPredicate.NotHighlightPredicate; +import org.jdesktop.swingx.decorator.HighlightPredicate.RowGroupHighlightPredicate; +import org.jdesktop.swingx.plaf.LookAndFeelAddons; +import org.jdesktop.swingx.plaf.UIColorHighlighterAddon; +import org.jdesktop.swingx.plaf.UIDependent; +import org.jdesktop.swingx.util.PaintUtils; + +import javax.swing.*; +import java.awt.*; + +/** + * A Factory which creates common Highlighters.

      + * + * PENDING JW: really need the alternate striping? That's how the + * old AlternateRowHighlighter did it, but feels a bit wrong to + * have one stripe hardcoded to WHITE. Would prefer to remove. + * + * @author Jeanette Winzenburg + */ +public final class HighlighterFactory { + private static Highlighter COMPUTED_FOREGROUND_HIGHLIGHTER = new AbstractHighlighter() { + @Override + protected Component doHighlight(Component component, ComponentAdapter adapter) { + component.setForeground(PaintUtils.computeForeground(component.getBackground())); + + return component; + } + }; + + /** + * Creates a highlighter that sets the foreground color to WHITE or BLACK by computing the best + * match based on the current background color. It is recommended that no background changing + * highlighters be added after this highlighter, lest the computation be incorrect. + * + * @return a highlighter that computes the appropriate foreground color + */ + public static Highlighter createComputedForegroundHighlighter() { + return COMPUTED_FOREGROUND_HIGHLIGHTER; + } + + /** + * Creates and returns a Highlighter which highlights every second row + * background with a color depending on the LookAndFeel. The rows between + * are not highlighted, that is typically, they will show the container's + * background. + * + * @return a Highlighter striping every second row background. + */ + public static Highlighter createSimpleStriping() { + ColorHighlighter hl = new UIColorHighlighter(HighlightPredicate.ODD); + return hl; + } + + /** + * Creates and returns a Highlighter which highlights every second row group + * background with a color depending on LF. The row groups between are not + * highlighted, that is typically, they will show the container's + * background. + * + * @param rowsPerGroup the number of rows in a group + * @return a Highlighter striping every second row group background. + */ + public static Highlighter createSimpleStriping(int rowsPerGroup) { + return new UIColorHighlighter(new RowGroupHighlightPredicate( + rowsPerGroup)); + } + + /** + * Creates and returns a Highlighter which highlights every second row + * background with the given color. The rows between are not highlighted + * that is typically, they will show the container's background. + * + * @param stripeBackground the background color for the striping. + * @return a Highlighter striping every second row background. + */ + public static Highlighter createSimpleStriping(Color stripeBackground) { + ColorHighlighter hl = new ColorHighlighter(HighlightPredicate.ODD, stripeBackground, null); + return hl; + } + + /** + * Creates and returns a Highlighter which highlights every second row group + * background with the given color. The row groups between are not + * highlighted, that is they typically will show the container's background. + * + * @param stripeBackground the background color for the striping. + * @param rowsPerGroup the number of rows in a group + * @return a Highlighter striping every second row group background. + */ + public static Highlighter createSimpleStriping(Color stripeBackground, + int rowsPerGroup) { + HighlightPredicate predicate = new RowGroupHighlightPredicate( + rowsPerGroup); + ColorHighlighter hl = new ColorHighlighter(predicate, stripeBackground, + null); + return hl; + } + + /** + * Creates and returns a Highlighter which highlights + * with alternate background. The first is Color.WHITE, the second + * with the color depending on LF. + * + * @return a Highlighter striping every second row background. + */ + public static Highlighter createAlternateStriping() { + ColorHighlighter first = new ColorHighlighter(HighlightPredicate.EVEN, Color.WHITE, null); + ColorHighlighter hl = new UIColorHighlighter(HighlightPredicate.ODD); + return new CompoundHighlighter(first, hl); + } + + /** + * Creates and returns a Highlighter which highlights + * with alternate background. the first Color.WHITE, the second + * with the color depending on LF. + * + * @param rowsPerGroup the number of rows in a group + * @return a Highlighter striping every second row group background. + */ + public static Highlighter createAlternateStriping(int rowsPerGroup) { + HighlightPredicate predicate = new RowGroupHighlightPredicate(rowsPerGroup); + ColorHighlighter first = new ColorHighlighter(new NotHighlightPredicate(predicate), Color.WHITE, null); + ColorHighlighter hl = new UIColorHighlighter(predicate); + return new CompoundHighlighter(first, hl); + } + + /** + * Creates and returns a Highlighter which highlights with + * alternating background, starting with the base. + * + * @param baseBackground the background color for the even rows. + * @param alternateBackground background color for odd rows. + * @return a Highlighter striping alternating background. + */ + public static Highlighter createAlternateStriping(Color baseBackground, Color alternateBackground) { + ColorHighlighter base = new ColorHighlighter(HighlightPredicate.EVEN, baseBackground, null); + ColorHighlighter alternate = new ColorHighlighter(HighlightPredicate.ODD, alternateBackground, null); + return new CompoundHighlighter(base, alternate); + } + + /** + * Creates and returns a Highlighter which highlights with + * alternating background, starting with the base. + * + * @param baseBackground the background color for the even rows. + * @param alternateBackground background color for odd rows. + * @param linesPerStripe the number of rows in a group + * @return a Highlighter striping every second row group background. + */ + public static Highlighter createAlternateStriping(Color baseBackground, Color alternateBackground, int linesPerStripe) { + HighlightPredicate predicate = new RowGroupHighlightPredicate(linesPerStripe); + ColorHighlighter base = new ColorHighlighter(new NotHighlightPredicate(predicate), baseBackground, null); + ColorHighlighter alternate = new ColorHighlighter(predicate, alternateBackground, null); + + return new CompoundHighlighter(base, alternate); + } + +//--------------------------- UI dependent + + /** + * A ColorHighlighter with UI-dependent background. + * + * PENDING JW: internally install a AND predicate to check for LFs + * which provide striping on the UI-Delegate level? + * + */ + public static class UIColorHighlighter extends ColorHighlighter + implements UIDependent { + + static { + LookAndFeelAddons.contribute(new UIColorHighlighterAddon()); + } + + + /** + * Instantiates a ColorHighlighter with LF provided unselected + * background and default predicate. All other colors are null. + * + */ + public UIColorHighlighter() { + this(null); + } + + + /** + * Instantiates a ColorHighlighter with LF provided unselected + * background and the given predicate. All other colors are null. + * @param odd the predicate to use + */ + public UIColorHighlighter(HighlightPredicate odd) { + super(odd, null, null); + updateUI(); + } + + + /** + * @inheritDoc + */ + @Override + public void updateUI() { + setBackground(getUIColor()); + } + + /** + * Looks up and returns the LF specific color to use for striping + * background highlighting. + * + * Lookup strategy: + *

        + *
      1. in UIManager for key = "UIColorHighlighter.stripingBackground", if null + *
      2. use hard-coded HighlighterFactory.GENERIC_GREY + *
      + * + * PENDING: fallback or not? + * + * @return the LF specific color for background striping. + */ + private Color getUIColor() { + Color color = null; + // JW: can't do - Nimbus stripes even rows (somewhere deep down the ui?) + //, SwingX stripes odd rows + // --> combined == no striping +// color = UIManager.getColor("Table.alternateRowColor"); + if (color == null) { + color = UIManager.getColor("UIColorHighlighter.stripingBackground"); + } + if (color == null) { + color = HighlighterFactory.GENERIC_GRAY; + } + return color; + } +// /** +// * this is a hack until we can think about something better! +// * we map all known selection colors to highlighter colors. +// * +// */ +// private void initColorMap() { +// colorMap = new HashMap(); +// // Ocean +// colorMap.put(new Color(184, 207, 229), new Color(230, 238, 246)); +// // xp blue +// colorMap.put(new Color(49, 106, 197), new Color(224, 233, 246)); +// // xp silver +// colorMap.put(new Color(178, 180, 191), new Color(235, 235, 236)); +// // xp olive +// colorMap.put(new Color(147, 160, 112), new Color(228, 231, 219)); +// // win classic +// colorMap.put(new Color(10, 36, 106), new Color(218, 222, 233)); +// // win 2k? +// colorMap.put(new Color(0, 0, 128), new Color(218, 222, 233)); +// // default metal +// colorMap.put(new Color(205, 205, 255), new Color(235, 235, 255)); +// // mac OS X +// colorMap.put(new Color(56, 117, 215), new Color(237, 243, 254)); +// +// } + + } + + /** predefined colors - from old alternateRow. */ + public final static Color BEIGE = new Color(245, 245, 220); + public final static Color LINE_PRINTER = new Color(0xCC, 0xCC, 0xFF); + public final static Color CLASSIC_LINE_PRINTER = new Color(0xCC, 0xFF, 0xCC); + public final static Color FLORAL_WHITE = new Color(255, 250, 240); + public final static Color QUICKSILVER = new Color(0xF0, 0xF0, 0xE0); + public final static Color GENERIC_GRAY = new Color(229, 229, 229); + public final static Color LEDGER = new Color(0xF5, 0xFF, 0xF5); + public final static Color NOTEPAD = new Color(0xFF, 0xFF, 0xCC); + +} diff --git a/src/main/java/org/jdesktop/swingx/decorator/IconHighlighter.java b/src/main/java/org/jdesktop/swingx/decorator/IconHighlighter.java new file mode 100644 index 0000000000..1192d8554e --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/decorator/IconHighlighter.java @@ -0,0 +1,152 @@ +/* + * $Id: IconHighlighter.java 4192 2012-06-27 19:20:56Z kschaefe $ + * + * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.decorator; + +import org.jdesktop.swingx.renderer.IconAware; + +import javax.swing.*; +import java.awt.*; + +/** + * Highlighter which decorates by setting the icon property of a JLabel.

      + * + * Note: The limitation to JLabel icons (vs. covering AbstractButton as well) + * is intentional. Highlighters are allowed to touch only those properties of the + * rendering component which are guaranteed to be reset by the corresponding + * ComponentProvider, this implementation is safe enough - LabelProvider guarantees + * to reset both text and icon. On the other hand, CheckBoxProvider doesn't touch + * the icon (which is LAF depend), consequently this Highlighter must not touch + * it as well. Custom subclasses trying to cover AbstractButton + * must take care that their custom providers reset the icon property. + * + * @author Jeanette Winzenburg + * + * @see org.jdesktop.swingx.renderer.ComponentProvider + * @see org.jdesktop.swingx.renderer.LabelProvider + * @see org.jdesktop.swingx.renderer.CheckBoxProvider + */ +public class IconHighlighter extends AbstractHighlighter { + + private Icon icon; + + /** + * Instantiates a IconHighlighter with null Icon and default + * HighlightPredicate. + */ + public IconHighlighter() { + this((HighlightPredicate) null); + } + + /** + * Instantiates a IconHighlighter with null Icon the given predicate. + * + * @param predicate the HighlightPredicate to use. + */ + public IconHighlighter(HighlightPredicate predicate) { + this(predicate, null); + } + + + /** + * Instantiates a IconHighlighter with the specified Icon and default + * HighlightPredicate. + * + * @param icon the icon to use for decoration. + */ + public IconHighlighter(Icon icon) { + this(null, icon); + } + + + /** + * Instantiates a IconHighlighter with the specified Icon and + * HighlightPredicate. + * + * @param predicate the HighlightPredicate to use. + * @param icon the Icon to use for decoration. + */ + public IconHighlighter(HighlightPredicate predicate, Icon icon) { + super(predicate); + setIcon(icon); + } + + /** + * Sets the icon to use for decoration. A null icon indicates + * to not decorate.

      + * + * The default value is null. + * + * @param icon the Icon to use for decoration, might be null. + */ + public void setIcon(Icon icon) { + if (areEqual(icon, getIcon())) return; + this.icon = icon; + fireStateChanged(); + } + + /** + * Returns the Icon used for decoration. + * + * @return icon the Icon used for decoration. + * @see #setIcon(Icon) + */ + public Icon getIcon() { + return icon; + } + + /** + * {@inheritDoc} + * + * Implemented to set the component's Icon property, if possible and + * this Highlighter's icon is not null. Does nothing if the decorating icon is null. + * @see #canHighlight(Component, ComponentAdapter) + * @see #setIcon(Icon) + */ + @Override + protected Component doHighlight(Component component, + ComponentAdapter adapter) { + if (getIcon() != null) { + if (component instanceof IconAware) { + ((IconAware) component).setIcon(getIcon()); + } else if (component instanceof JLabel) { + ((JLabel) component).setIcon(getIcon()); + } + } + return component; + } + + /** + * {@inheritDoc}

      + * + * Overridden to return true if the component is of type IconAware or + * of type JLabel, false otherwise.

      + * + * Note: special casing JLabel is for backward compatibility - application + * highlighting code which doesn't use the Swingx renderers would stop working + * otherwise. + */ + @Override + protected boolean canHighlight(Component component, ComponentAdapter adapter) { + return component instanceof IconAware || component instanceof JLabel; + } + + +} diff --git a/src/main/java/org/jdesktop/swingx/decorator/PainterHighlighter.java b/src/main/java/org/jdesktop/swingx/decorator/PainterHighlighter.java new file mode 100644 index 0000000000..210e38d3f2 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/decorator/PainterHighlighter.java @@ -0,0 +1,253 @@ +/* + * $Id: PainterHighlighter.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.decorator; + +import org.jdesktop.swingx.painter.AbstractPainter; +import org.jdesktop.swingx.painter.Painter; +import org.jdesktop.swingx.renderer.PainterAware; + +import java.awt.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +/** + * Highlighter implementation which uses a Painter to decorate the component. + *

      + * + * As Painter implementations can be mutable and Highlighters have the + * responsibility to notify their own listeners about any changes which might + * effect the visuals, this class provides api to install/uninstall a listener + * to the painter, as appropriate. It takes care of Painters of type + * AbstractHighlighter by registering a PropertyChangeListener. Subclasses might + * override to correctly handle different types as well. + *

      + * + * Subclasses might be implemented to change the Painter during the decoration + * process, which must not passed-on to the Highlighter's listeners. The default + * routing is controlled by a flag isAdjusting. This is set/reset in this + * implementation's highlight method to ease subclass' burden (and to keep + * backward compatibility with implementations preceding the introduction of the + * painter listener). That is, subclasses are free to change painter properties + * during the decoration. + *

      + * + * As an example, a ValueBasedPainterHighlighter might safely change any painter + * property to decorate a component depending on content. + * + *

      
      + * @Override
      + * protected Component doHighlight(Component renderer, ComponentAdapter adapter) {
      + *      float end = getEndOfGradient((Number) adapter.getValue());
      + *      RelativePainter painter = (RelativePainter) getPainter();
      + *      painter.setXFraction(end);
      + *      ((PainterAware) renderer).setPainter(painter);
      + *      return renderer;
      + * }
      + * 
      + * @Override
      + * protected boolean canHighlight(Component renderer, ComponentAdapter adapter) {
      + *     return super.canHighlight(renderer, adapter) &&
      + *        (adapter.getValue() instanceof Number);
      + * }
      + * 
      + * + * NOTE: this will change once the Painter api is stable. + * + * @author Jeanette Winzenburg + */ +public class PainterHighlighter extends AbstractHighlighter { + + /** The painter to use for decoration. */ + private Painter painter; + /** The listener registered with the Painter. */ + private PropertyChangeListener painterListener; + /** + * A flag indicating whether or not changes in the Painter + * should be passed-on to the Highlighter's ChangeListeners. + */ + private boolean isAdjusting; + + /** + * Instantiates a PainterHighlighter with null painter and + * default predicate. + */ + public PainterHighlighter() { + this(null, null); + } + /** + * Instantiates a PainterHighlighter with null painter which + * uses the given predicate. + * + * @param predicate the HighlightPredicate which controls the highlight + * application. + */ + public PainterHighlighter(HighlightPredicate predicate) { + this(predicate, null); + } + + /** + * Instantiates a PainterHighlighter with the given Painter and + * default predicate. + * + * @param painter the painter to use + */ + public PainterHighlighter(Painter painter) { + this(null, painter); + } + + /** + * Instantiates a PainterHighlighter with the given painter and + * predicate. + * @param predicate + * @param painter + */ + public PainterHighlighter(HighlightPredicate predicate, Painter painter) { + super(predicate); + setPainter(painter); + } + + + + /** + * Returns to Painter used in this Highlighter. + * + * @return the Painter used in this Highlighter, may be null. + */ + public Painter getPainter() { + return painter; + } + + /** + * Sets the Painter to use in this Highlighter, may be null. + * Un/installs the listener to changes painter's properties. + * + * @param painter the Painter to uses for decoration. + */ + public void setPainter(Painter painter) { + if (areEqual(painter, getPainter())) return; + uninstallPainterListener(); + this.painter = painter; + installPainterListener(); + fireStateChanged(); + } + + /** + * Installs a listener to the painter if appropriate. + * This implementation registers its painterListener if + * the Painter is of type AbstractPainter. + */ + protected void installPainterListener() { + if (getPainter() instanceof AbstractPainter) { + ((AbstractPainter) getPainter()).addPropertyChangeListener(getPainterListener()); + } + } + + /** + * Uninstalls a listener from the painter if appropriate. + * This implementation removes its painterListener if + * the Painter is of type AbstractPainter. + */ + protected void uninstallPainterListener() { + if (getPainter() instanceof AbstractPainter) { + ((AbstractPainter) getPainter()).removePropertyChangeListener(painterListener); + } + } + + + /** + * Lazyly creates and returns the property change listener used + * to listen to changes of the painter. + * + * @return the property change listener used to listen to changes + * of the painter. + */ + protected final PropertyChangeListener getPainterListener() { + if (painterListener == null) { + painterListener = createPainterListener(); + } + return painterListener; + } + + /** + * Creates and returns the property change listener used + * to listen to changes of the painter.

      + * + * This implementation fires a stateChanged on receiving + * any propertyChange, if the isAdjusting flag is false. + * Otherwise does nothing. + * + * @return the property change listener used to listen to changes + * of the painter. + */ + protected PropertyChangeListener createPainterListener() { + PropertyChangeListener l = new PropertyChangeListener() { + + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (isAdjusting) return; + fireStateChanged(); + } + + }; + return l; + } + + /** + * {@inheritDoc}

      + * + * Overridden to set/reset the flag indicating whether or not + * painter's property changes should be passed on to the + * Highlighter's listener. + */ + @Override + public Component highlight(Component component, ComponentAdapter adapter) { + isAdjusting = true; + Component stamp = super.highlight(component, adapter); + isAdjusting = false; + return stamp; + } + + /** + * {@inheritDoc} + *

      + * This implementation sets the painter if it is not null. Does nothing + * otherwise. + */ + @Override + protected Component doHighlight(Component component, + ComponentAdapter adapter) { + ((PainterAware) component).setPainter(painter); + return component; + } + + /** + * {@inheritDoc}

      + * + * Overridden to return false if the Painter is null or the component is not + * of type PainterAware. + */ + @Override + protected boolean canHighlight(Component component, ComponentAdapter adapter) { + return getPainter() != null && (component instanceof PainterAware); + } + + +} diff --git a/src/main/java/org/jdesktop/swingx/decorator/PatternPredicate.java b/src/main/java/org/jdesktop/swingx/decorator/PatternPredicate.java new file mode 100644 index 0000000000..c06783834a --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/decorator/PatternPredicate.java @@ -0,0 +1,223 @@ +/* + * $Id: PatternPredicate.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.decorator; + +import java.awt.*; +import java.util.regex.Pattern; + +/** + * Pattern based HighlightPredicate.

      + * + * Turns on the highlight of a single or all columns of the current row if + * a match of the String representation of cell content against the given Pattern + * is found.

      + * + * The match logic can be configured to either test + * one specific column in the current row or all columns. In the latter case + * the logic is the same as in RowFilters.GeneralFilter: the row is included + * if any of the cell contents in the row are matches.

      + * + * + * @author Jeanette Winzenburg + */ +public class PatternPredicate implements HighlightPredicate { + public static final int ALL = -1; + + private int highlightColumn; + private int testColumn; + private Pattern pattern; + + /** + * Instantiates a Predicate with the given Pattern and testColumn index + * (in model coordinates) highlighting all columns. + * A column index of -1 is interpreted + * as "all". + * + * @param pattern the Pattern to test the cell value against + * @param testColumn the column index in model coordinates + * of the cell which contains the value to test against the pattern + */ + public PatternPredicate(Pattern pattern, int testColumn) { + this(pattern, testColumn, ALL); + } + + /** + * Instantiates a Predicate with the given Pattern testing against + * all columns and highlighting all columns. + * + * @param pattern the Pattern to test the cell value against + */ + public PatternPredicate(Pattern pattern) { + this(pattern, ALL, ALL); + } + + /** + * Instantiates a Predicate with the given Pattern and test-/decorate + * column index in model coordinates. A column index of -1 is interpreted + * as "all". + * + * + * @param pattern the Pattern to test the cell value against + * @param testColumn the column index in model coordinates + * of the cell which contains the value + * to test against the pattern + * @param decorateColumn the column index in model coordinates + * of the cell which should be + * decorated if the test against the value succeeds. + */ + public PatternPredicate(Pattern pattern, int testColumn, int decorateColumn) { + this.pattern = pattern; + this.testColumn = testColumn; + this.highlightColumn = decorateColumn; + } + + /** + * Instantiates a Predicate with the given Pattern testing against + * all columns and highlighting all columns. + * + * @param pattern the Pattern to test the cell value against + */ + public PatternPredicate(String pattern) { + this(pattern, ALL, ALL); + } + + /** + * Instantiates a Predicate with the given regex and test + * column index in model coordinates. The pattern string is compiled to a + * Pattern with flags 0. A column index of -1 is interpreted + * as "all". + * + * @param regex the regex string to test the cell value against + * @param testColumn the column index in model coordinates + * of the cell which contains the value + * to test against the pattern + */ + public PatternPredicate(String regex, int testColumn) { + this(regex, testColumn, ALL); + } + + + /** + * Instantiates a Predicate with the given regex and test-/decorate + * column index in model coordinates. The pattern string is compiled to a + * Pattern with flags 0. A column index of -1 is interpreted + * as "all". + * + * @param regex the regex string to test the cell value against + * @param testColumn the column index in model coordinates + * of the cell which contains the value + * to test against the pattern + * @param decorateColumn the column index in model coordinates + * of the cell which should be + * decorated if the test against the value succeeds. + */ + public PatternPredicate(String regex, int testColumn, int decorateColumn) { + this(Pattern.compile(regex), testColumn, decorateColumn); + } + + /** + * + * @inherited

      + * + * Implemented to return true if the match of cell content's String representation + * against the Pattern if found and the adapter's view column maps to the + * decorateColumn/s. Otherwise returns false. + * + */ + @Override + public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { + if (isHighlightCandidate(adapter)) { + return test(adapter); + } + return false; + } + + /** + * Test the value. This is called only if the + * pre-check returned true, because accessing the + * value might be potentially costly + * @param adapter + * @return + */ + private boolean test(ComponentAdapter adapter) { + // single test column + if (testColumn >= 0) return testColumn(adapter, testColumn); + // test all + for (int column = 0; column < adapter.getColumnCount(); column++) { + boolean result = testColumn(adapter, column); + if (result) return true; + } + return false; + } + + /** + * @param adapter + * @param testColumn + * @return + */ + private boolean testColumn(ComponentAdapter adapter, int testColumn) { + if (!adapter.isTestable(testColumn)) + return false; + String value = adapter.getString(testColumn); + + if ((value == null) || (value.length() == 0)) { + return false; + } + return pattern.matcher(value).find(); + } + + /** + * A quick pre-check. + * @param adapter + * + * @return + */ + private boolean isHighlightCandidate(ComponentAdapter adapter) { + return (pattern != null) && + ((highlightColumn < 0) || + (highlightColumn == adapter.convertColumnIndexToModel(adapter.column))); + } + + /** + * + * @return returns the column index to decorate (in model coordinates) + */ + public int getHighlightColumn() { + return highlightColumn; + } + + /** + * + * @return returns the Pattern to test the cell value against + */ + public Pattern getPattern() { + return pattern; + } + + /** + * + * @return the column to use for testing (in model coordinates) + */ + public int getTestColumn() { + return testColumn; + } + +} diff --git a/src/main/java/org/jdesktop/swingx/decorator/ResetDTCRColorHighlighter.java b/src/main/java/org/jdesktop/swingx/decorator/ResetDTCRColorHighlighter.java new file mode 100644 index 0000000000..5820101351 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/decorator/ResetDTCRColorHighlighter.java @@ -0,0 +1,111 @@ +/* + * $Id: ResetDTCRColorHighlighter.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.decorator; + +import javax.swing.*; +import javax.swing.table.DefaultTableCellRenderer; +import java.awt.*; + + +/** + * This is a hack around DefaultTableCellRenderer color "memory", + * see Issue #258-swingx.

      + * + * The issue is that the default has internal color management + * which is different from other types of renderers. The + * consequence of the internal color handling is that there's + * a color memory which must be reset somehow. The "old" hack around + * reset the xxColors of all types of renderers to the adapter's + * target XXColors, introducing #178-swingx (Highlighgters must not + * change any colors except those for which their color properties are + * explicitly set).

      + * + * This hack limits the interference to renderers of type + * DefaultTableCellRenderer, applying a hacking highlighter which + * resets the renderers XXColors to a previously "memorized" + * color. Note that setting the color to null didn't have the desired + * effect.

      + * + * PENDING: extend ColorHighlighter + */ + +public class ResetDTCRColorHighlighter extends ColorHighlighter { + + public ResetDTCRColorHighlighter() { + super(null, null); + } + + /** + * applies the memory hack for renderers of type DefaultTableCellRenderer, + * does nothing for other types. + * @param renderer the component to highlight + * @param adapter the renderee's component state. + */ + @Override + public Component highlight(Component renderer, ComponentAdapter adapter) { + //JW + // table renderers have different state memory as list/tree renderers + // without the null they don't unstamp! + // but... null has adversory effect on JXList f.i. - selection + // color is changed. This is related to #178-swingx: + // highlighter background computation is weird. + // + if (renderer instanceof DefaultTableCellRenderer) { + return super.highlight(renderer, adapter); + } + return renderer; + } + + @Override + protected void applyBackground(Component renderer, ComponentAdapter adapter) { + if (!adapter.isSelected()) { + Object colorMemory = ((JComponent) renderer).getClientProperty("rendererColorMemory.background"); + if (colorMemory instanceof ColorMemory) { + renderer.setBackground(((ColorMemory) colorMemory).color); + } else { + ((JComponent) renderer).putClientProperty("rendererColorMemory.background", new ColorMemory(renderer.getBackground())); + } + } + } + + @Override + protected void applyForeground(Component renderer, ComponentAdapter adapter) { + if (!adapter.isSelected()) { + Object colorMemory = ((JComponent) renderer).getClientProperty("rendererColorMemory.foreground"); + if (colorMemory instanceof ColorMemory) { + renderer.setForeground(((ColorMemory) colorMemory).color); + } else { + ((JComponent) renderer).putClientProperty("rendererColorMemory.foreground", new ColorMemory(renderer.getForeground())); + } + } + } + + private static class ColorMemory { + public ColorMemory(Color color) { + this.color = color; + } + + Color color; + } + + + +} diff --git a/src/main/java/org/jdesktop/swingx/decorator/SearchPredicate.java b/src/main/java/org/jdesktop/swingx/decorator/SearchPredicate.java new file mode 100644 index 0000000000..4508c7d3ed --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/decorator/SearchPredicate.java @@ -0,0 +1,223 @@ +/* + * $Id: SearchPredicate.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.decorator; + +import java.awt.*; +import java.util.regex.Pattern; + +/** + * Pattern based HighlightPredicate for searching. Highlights + * the current adapter cell if the value matches the pattern. + * The highlight scope can be limited to a certain column and + * row.

      + * + * Note: this differs from PatternPredicate in that it is focused + * on the current cell (highlight coordinates == test coordinates) + * while the PatternPredicate can have separate test and highlight + * coordinates.

      + * + * + * @author Jeanette Winzenburg + */ +public class SearchPredicate implements HighlightPredicate { + public static final int ALL = -1; + public static final String MATCH_ALL = ".*"; + private int highlightColumn; + private int highlightRow; // in view coordinates? + private Pattern pattern; + + /** + * Instantiates a Predicate with the given Pattern. + * All matching cells are highlighted. + * + * + * @param pattern the Pattern to test the cell value against + */ + public SearchPredicate(Pattern pattern) { + this(pattern, ALL, ALL); + } + + /** + * Instantiates a Predicate with the given Pattern. Highlighting + * is limited to matching cells in the given column. + * + * @param pattern the Pattern to test the cell value against + * @param column the column to limit the highlight to + */ + public SearchPredicate(Pattern pattern, int column) { + this(pattern, ALL, column); + } + + /** + * Instantiates a Predicate with the given Pattern. Highlighting + * is limited to matching cells in the given column and row. A + * value of -1 indicates all rows/columns.

      + * + * Note: the coordinates are asymmetric - rows are in view- and + * column in model-coordinates - due to corresponding methods in + * ComponentAdapter. Hmm... no need to? This happens on the + * current adapter state which is view always, so could use view + * only? + * + * @param pattern the Pattern to test the cell value against + * @param row the row index in view coordinates to limit the + * highlight. + * @param column the column in model coordinates + * to limit the highlight to + */ + public SearchPredicate(Pattern pattern, int row, int column) { + this.pattern = pattern; + this.highlightColumn = column; + this.highlightRow = row; + } + + /** + * Instantiates a Predicate with a Pattern compiled from the given + * regular expression. + * All matching cells are highlighted. + * + * @param regex the regular expression to test the cell value against + */ + public SearchPredicate(String regex) { + this(regex, ALL, ALL); + + } + + /** + * Instantiates a Predicate with a Pattern compiled from the given + * regular expression. Highlighting + * is applied to matching cells in all rows, but only in the given column. A + * value of ALL indicates all columns.

      + * + * @param regex the regular expression to test the cell value against + * @param column the column index in model coordinates to limit the highlight to + */ + public SearchPredicate(String regex, int column) { + this(regex, ALL, column); + } + + /** + * Instantiates a Predicate with a Pattern compiled from the given + * regular expression. Highlighting + * is limited to matching cells in the given column and row. A + * value of ALL indicates all rows/columns.

      + * + * Note: the coordinates are asymmetric - rows are in view- and + * column in model-coordinates - due to corresponding methods in + * ComponentAdapter. Hmm... no need to? This happens on the + * current adapter state which is view always, so could use view + * only? + * + * @param regex the Pattern to test the cell value against + * @param row the row index in view coordinates to limit the + * highlight. + * @param column the column in model coordinates + * to limit the highlight to + */ + public SearchPredicate(String regex, int row, int column) { + // test against empty string + this((regex != null) && (regex.length() > 0) ? + Pattern.compile(regex) : null, row, column); + } + + /** + * + * @return returns the column index to decorate (in model coordinates) + */ + public int getHighlightColumn() { + return highlightColumn; + } + + /** + * + * @return returns the column index to decorate (in model coordinates) + */ + public int getHighlightRow() { + return highlightRow; + } + + /** + * + * @return returns the Pattern to test the cell value against + */ + public Pattern getPattern() { + return pattern; + } + + + /** + * {@inheritDoc} + */ + @Override + public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { + if (isHighlightCandidate(renderer, adapter)) { + return test(renderer, adapter); + } + return false; + } + + /** + * Test the value. This is called only if the + * pre-check returned true, because accessing the + * value might be potentially costly + * @param renderer + * @param adapter + * @return + */ + private boolean test(Component renderer, ComponentAdapter adapter) { + // PENDING JW: why convert here? we are focused on the adapter's cell + // looks like an oversight as of ol' days ;-) + int columnToTest = adapter.convertColumnIndexToModel(adapter.column); + String value = adapter.getString(columnToTest); + + if ((value == null) || (value.length() == 0)) { + return false; + } + return pattern.matcher(value).find(); + } + + /** + * A quick pre-check. + * + * @param renderer + * @param adapter + * @return + */ + private boolean isHighlightCandidate(Component renderer, ComponentAdapter adapter) { + if (!isEnabled()) return false; + if (highlightRow >= 0 && (adapter.row != highlightRow)) { + return false; + } + return + ((highlightColumn < 0) || + (highlightColumn == adapter.convertColumnIndexToModel(adapter.column))); + } + + private boolean isEnabled() { + Pattern pattern = getPattern(); + if (pattern == null || MATCH_ALL.equals(pattern.pattern())) { + return false; + } + return true; + } + + +} diff --git a/src/main/java/org/jdesktop/swingx/decorator/ShadingColorHighlighter.java b/src/main/java/org/jdesktop/swingx/decorator/ShadingColorHighlighter.java new file mode 100644 index 0000000000..0666580b3a --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/decorator/ShadingColorHighlighter.java @@ -0,0 +1,85 @@ +/* + * $Id: ShadingColorHighlighter.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.decorator; + +import java.awt.*; + +/** + * Experimental replacement of HierarchicalColumnHighligher. + * Darkens the component's background. + * + * @author Jeanette Winzenburg + */ +public class ShadingColorHighlighter extends ColorHighlighter { + + /** + * Instantiates a Highlighter with null colors using the default + * HighlightPredicate. + * + */ + public ShadingColorHighlighter() { + this(null); + } + + /** + * Instantiates a Highlighter with null colors using the specified + * HighlightPredicate. + * + * @param predicate the HighlightPredicate to use. + */ + public ShadingColorHighlighter(HighlightPredicate predicate) { + super(predicate, null, null); + } + + /** + * Applies a suitable background for the renderer component within the + * specified adapter.

      + * + * This implementation applies its a darkened background to an unselected + * adapter. Does nothing for selected cells. + * + * @param renderer the cell renderer component that is to be decorated + * @param adapter the ComponentAdapter for this decorate operation + */ + @Override + protected void applyBackground(Component renderer, ComponentAdapter adapter) { + if (adapter.isSelected()) + return; + // PENDING JW: really? That would be applying a absolute color, instead + // of shading whatever the renderer has. + Color background = getBackground(); + if (background == null) { + background = renderer.getBackground(); + } + // Change to the following +// Color background = renderer.getBackground(); + if (background != null) { + renderer.setBackground(computeBackgroundSeed(background)); + } + } + + protected Color computeBackgroundSeed(Color seed) { + return new Color(Math.max((int) (seed.getRed() * 0.95), 0), Math.max( + (int) (seed.getGreen() * 0.95), 0), Math.max((int) (seed + .getBlue() * 0.95), 0)); + } + +} diff --git a/src/main/java/org/jdesktop/swingx/decorator/ToolTipHighlighter.java b/src/main/java/org/jdesktop/swingx/decorator/ToolTipHighlighter.java new file mode 100644 index 0000000000..456664f262 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/decorator/ToolTipHighlighter.java @@ -0,0 +1,131 @@ +/* + * $Id: ToolTipHighlighter.java 3676 2010-04-26 15:42:26Z kschaefe $ + * + * Copyright 2010 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.decorator; + +import org.jdesktop.swingx.renderer.StringValue; + +import javax.swing.*; +import java.awt.*; + +/** + * A highlighter for setting a tool tip on the component. + * + * @author kschaefer + */ +public class ToolTipHighlighter extends AbstractHighlighter { + private StringValue toolTipValue; + + /** + * Instantiates a ToolTipHighlighter with null StringValue. The Highlighter is + * applied always. + */ + public ToolTipHighlighter() { + this((HighlightPredicate) null); + } + + /** + * Instantiates a ToolTipHighlighter with the specified StringValue. The Highlighter is applied + * always. + * + * @param toolTipValue + * the StringValue used to create the tool tip + */ + public ToolTipHighlighter(StringValue toolTipValue) { + this(null, toolTipValue); + } + + /** + * Instantiates a ToolTipHighlighter with the specified HighlightPredicate and a null + * StringValue. + * + * @param predicate + * the HighlightPredicate to use, may be null to default to ALWAYS. + */ + public ToolTipHighlighter(HighlightPredicate predicate) { + this(predicate, null); + } + + /** + * Instantiates a ToolTipHighlighter with the specified HighlightPredicate and StringValue. + * + * @param predicate + * the HighlightPredicate to use, may be null to default to ALWAYS. + * @param toolTipValue + * the StringValue used to create the tool tip + */ + public ToolTipHighlighter(HighlightPredicate predicate, StringValue toolTipValue) { + super(predicate); + + this.toolTipValue = toolTipValue; + } + + /** + * Returns the StringValue used for decoration. + * + * @return the StringValue used for decoration + * + * @see #setToolTipValue(Font) + */ + public StringValue getToolTipValue() { + return toolTipValue; + } + + /** + * Sets the StringValue used for decoration. May be null to use default decoration. + * + * @param font the Font used for decoration, may be null to use default decoration. + * + * @see #getToolTipValue() + */ + public void setToolTipValue(StringValue toolTipValue) { + if (areEqual(toolTipValue, getToolTipValue())) return; + this.toolTipValue = toolTipValue; + fireStateChanged(); + } + + /** + * {@inheritDoc}

      + * + * Implemented to return false if the component is not a JComponent. + */ + @Override + protected boolean canHighlight(Component component, ComponentAdapter adapter) { + return component instanceof JComponent; + } + + /** + * {@inheritDoc} + */ + @Override + protected Component doHighlight(Component component, ComponentAdapter adapter) { + String toolTipText = null; + + if (toolTipValue == null) { + toolTipText = adapter.getString(); + } else { + toolTipText = toolTipValue.getString(adapter.getValue()); + } + + ((JComponent) component).setToolTipText(toolTipText); + + return component; + } +} diff --git a/src/main/java/org/jdesktop/swingx/decorator/package-info.java b/src/main/java/org/jdesktop/swingx/decorator/package-info.java new file mode 100644 index 0000000000..b110c4506b --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/decorator/package-info.java @@ -0,0 +1,33 @@ +/* + * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +/** + * Contains API used to implement coordinated sorting, filtering + * and highlighting of the extended Swing cell-rendering component + * classes JXTable, JXTreeTable, JXTree, and JXList.

      + * + * For details, see + * + * SwingX Highlighter Basics (in the wiki) .

      + * + * PENDING JW: describe here instead of linking ;-) + */ +package org.jdesktop.swingx.decorator; + diff --git a/src/main/java/org/jdesktop/swingx/error/ErrorEvent.java b/src/main/java/org/jdesktop/swingx/error/ErrorEvent.java new file mode 100644 index 0000000000..aef5927a02 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/error/ErrorEvent.java @@ -0,0 +1,55 @@ +/* + * $Id: ErrorEvent.java 2979 2008-07-08 01:32:06Z kschaefe $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.error; + +import java.util.EventObject; + +/** + * Defines an event which encapsulates an error which occurred in a JX Swing component + * which supports ErrorListeners. + * + * @author Joshua Marinacci joshua.marinacci@sun.com + * @see ErrorListener + * @see ErrorSupport + */ +public class ErrorEvent extends EventObject { + private Throwable throwable; + + /** + * Creates a new instance of ErrorEvent + * @param throwable The Error or Exception which occurred. + * @param source The object which threw the Error or Exception + */ + public ErrorEvent(Throwable throwable, Object source) { + super(source); + this.throwable = throwable; + } + + /** + * Gets the Error or Exception which occurred. + * @return The Error or Exception which occurred. + */ + public Throwable getThrowable() { + return throwable; + } + +} diff --git a/src/main/java/org/jdesktop/swingx/error/ErrorInfo.java b/src/main/java/org/jdesktop/swingx/error/ErrorInfo.java new file mode 100644 index 0000000000..43260500c3 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/error/ErrorInfo.java @@ -0,0 +1,259 @@ +/* + * $Id: ErrorInfo.java 4170 2012-02-21 14:27:15Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.error; + +import javax.swing.*; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.logging.Level; + +/** + *

      A simple class that encapsulates all the information needed + * to report a problem using the automated report/processing system.

      + * + *

      All HTML referred to in this API refers to version 3.2 of the HTML + * markup specification.

      + * + * @status REVIEWED + * @author Alexander Zuev + * @author rbair + */ +public class ErrorInfo { + /** + * Short string that will be used as a error title + */ + private String title; + /** + * Basic message that describes incident + */ + private String basicErrorMessage; + /** + * Message that will fully describe the incident with all the + * available details + */ + private String detailedErrorMessage; + /** + * A category name, indicating where in the application this incident + * occurred. It is recommended that this be the same value as you + * would use when logging. + */ + private String category; + /** + * Optional Throwable that will be used as a possible source for + * additional information + */ + private Throwable errorException; + /** + * Used to specify how bad this error was. + */ + private Level errorLevel; + /** + * A Map which captures the state of the application + * at the time of an exception. This state is then available for error + * reports. + */ + private Map state; + + /** + * Creates a new ErrorInfo based on the provided data. + * + * @param title used as a quick reference for the + * error (for example, it might be used as the + * title of an error dialog or as the subject of + * an email message). May be null. + * + * @param basicErrorMessage short description of the problem. May be null. + * + * @param detailedErrorMessage full description of the problem. It is recommended, + * though not required, that this String contain HTML + * to improve the look and layout of the detailed + * error message. May be null. + * + * @param category A category name, indicating where in the application + * this incident occurred. It is recommended that + * this be the same value as you would use when logging. + * May be null. + * + * @param errorException Throwable that can be used as a + * source for additional information such as call + * stack, thread name, etc. May be null. + * + * @param errorLevel any Level (Level.SEVERE, Level.WARNING, etc). + * If null, then the level will be set to SEVERE. + * + * @param state the state of the application at the time the incident occured. + * The standard System properties are automatically added to this + * state, and thus do not need to be included. This value may be null. + * If null, the resulting map will contain only the System properties. + * If there is a value in the map with a key that also occurs in the + * System properties (for example: sun.java2d.noddraw), then the + * developer supplied value will be used. In other words, defined + * parameters override standard ones. In addition, the keys + * "System.currentTimeMillis" and "isOnEDT" are both defined + * automatically. + */ + public ErrorInfo(String title, String basicErrorMessage, String detailedErrorMessage, + String category, Throwable errorException, Level errorLevel, Map state) { + this.title = title; + this.basicErrorMessage = basicErrorMessage; + this.detailedErrorMessage = detailedErrorMessage; + this.category = category; + this.errorException = errorException; + this.errorLevel = errorLevel == null ? Level.SEVERE : errorLevel; + this.state = new HashMap(); + + //first add all the System properties + try { + //NOTE: This is not thread safe because System.getProperties() does not appear + //to create a copy of the map. Thus, another thread could be modifying the System + //properties and the "state" at the time of this exception may not be + //accurate! + Properties props = System.getProperties(); + for (Map.Entry entry : props.entrySet()) { + String key = entry.getKey() == null ? null : entry.getKey().toString(); + String val = entry.getKey() == null ? null : entry.getValue().toString(); + if (key != null) { + this.state.put(key, val); + } + } + } catch (SecurityException e) { + //probably running in a sandbox, don't worry about this + } + + //add the automatically supported properties + this.state.put("System.currentTimeMillis", "" + System.currentTimeMillis()); + this.state.put("isOnEDT", "" + SwingUtilities.isEventDispatchThread()); + + //now add all the data in the param "state". Thus, if somebody specified a key in the + //state map, it overrides whatever was in the System map + if (state != null) { + for (Map.Entry entry : state.entrySet()) { + this.state.put(entry.getKey(), entry.getValue()); + } + } + } + + /** + * Gets the string to use for a dialog title or other quick reference. Used + * as a quick reference for the incident. For example, it might be used as the + * title of an error dialog or as the subject of an email message. + * + * @return quick reference String. May be null. + */ + public String getTitle() { + return title; + } + + /** + *

      Gets the basic error message. This message should be clear and user oriented. + * This String may have HTML formatting, but any such formatting should be used + * sparingly. Generally, such formatting makes sense for making certain words bold, + * but should not be used for page layout or other such things.

      + * + *

      For example, the following are perfectly acceptable basic error messages: + *

      +     *      "Your camera cannot be located. Please make sure that it is powered on
      +     *       and that it is connected to this computer. Consult the instructions
      +     *       provided with your camera to make sure you are using the appropriate
      +     *       cable for attaching the camera to this computer"
      +     *
      +     *      "<html>You are running on <b>reserver</b> battery
      +     *       power. Please plug into a power source immediately, or your work may
      +     *       be lost!</html>"
      +     * 

      + * + * @return basic error message or null + */ + public String getBasicErrorMessage() { + return basicErrorMessage; + } + + /** + *

      Gets the detailed error message. Unlike {@link #getBasicErrorMessage}, + * this method may return a more technical message to the user. However, it + * should still be user oriented. This String should be formatted using basic + * HTML to improve readability as necessary.

      + * + *

      This method may return null.

      + * + * @return detailed error message or null + */ + public String getDetailedErrorMessage() { + return detailedErrorMessage; + } + + /** + * Gets the category name. This value indicates where in the application + * this incident occurred. It is recommended that this be the same value as + * you would use when logging. This may be null. + * + * @return the category. May be null. + */ + public String getCategory() { + return category; + } + + /** + * Gets the actual exception that generated the error. If this returns a + * non null value, then {@link #getBasicErrorMessage} may return a null value. + * If this returns a non null value and {@link #getDetailedErrorMessage} returns + * a null value, then this returned Throwable may be used as the + * basis for the detailed error message (generally by showing the stack trace). + * + * @return exception or null + */ + public Throwable getErrorException() { + return errorException; + } + + /** + * Gets the severity of the error. The default level is Level.SEVERE, + * but any {@link Level} may be specified when constructing an + * ErrorInfo. + * + * @return the error level. This will never be null + */ + public Level getErrorLevel() { + return errorLevel; + } + + /** + *

      Gets a copy of the application state at the time that the incident occured. + * This map will never be null. If running with appropriate permissions the + * map will contain all the System properties. In addition, it contains two + * keys, "System.currentTimeMillis" and "isOnEDT".

      + * + *

      Warning: The System.properties may not contain the exact set + * of System properties at the time the exception occured. This is due to the + * nature of System.getProperties() and the Properties collection. While they + * are property synchronized, it is possible that while iterating the set of + * properties in the ErrorInfo constructor that some other code can change + * the properties on another thread. This is unlikely to occur, but in some + * applications may occur.

      + * + * @return a copy of the application state. This will never be null. + */ + public Map getState() { + return new HashMap(state); + } +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/error/ErrorLevel.java b/src/main/java/org/jdesktop/swingx/error/ErrorLevel.java new file mode 100644 index 0000000000..26676c81ff --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/error/ErrorLevel.java @@ -0,0 +1,51 @@ +/* + * $Id: ErrorLevel.java 1557 2006-11-10 17:02:53Z rbair $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.error; + +import java.util.logging.Level; + +/** + *

      Extends {@link Level} adding the FATAL error level. + * Fatal errors are those unrecoverable errors that must result in the termination + * of the application.

      + * + * @status REVIEWED + * @author rbair + */ +public class ErrorLevel extends Level { + /** + * FATAL is a message level indicating a catastrophic failure that should + * result in the immediate termination of the application. + *

      + * In general FATAL messages should describe events that are + * of considerable critical and which will prevent + * program execution. They should be reasonably intelligible + * to end users and to system administrators. + * This level is initialized to 1100. + */ + public static final ErrorLevel FATAL = new ErrorLevel("FATAL", 1100); + + /** Creates a new instance of ErrorLevel */ + protected ErrorLevel(String name, int value) { + super(name, value); + } +} diff --git a/src/main/java/org/jdesktop/swingx/error/ErrorListener.java b/src/main/java/org/jdesktop/swingx/error/ErrorListener.java new file mode 100644 index 0000000000..b4b1d57df2 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/error/ErrorListener.java @@ -0,0 +1,45 @@ +/* + * $Id: ErrorListener.java 2979 2008-07-08 01:32:06Z kschaefe $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.error; + +import java.util.EventListener; + +/** + * ErrorListener defines the interface for an object which listens to errors generated + * by a JX Swing component. ErrorEvents are only generated for internal un-recoverable errors + * that cannot be thrown. An example would be an internal Action implementation that cannot + * throw an Exception directly because the ActionListener interface forbids it. Exceptions + * which can be throw directly (say from the constructor of the JX component) should not use + * the ErrorListener mechanism. + * + * @see ErrorEvent + * @see ErrorSupport + * @author Joshua Marinacci joshua.marinacci@sun.com + */ +public interface ErrorListener extends EventListener { + + /** + * Tells listeners that an error has occured within the watched component. + * @param event + */ + public void errorOccured(ErrorEvent event); +} diff --git a/src/main/java/org/jdesktop/swingx/error/ErrorReporter.java b/src/main/java/org/jdesktop/swingx/error/ErrorReporter.java new file mode 100644 index 0000000000..d9969013ff --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/error/ErrorReporter.java @@ -0,0 +1,49 @@ +/* + * $Id: ErrorReporter.java 3166 2009-01-02 13:27:18Z rah003 $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.error; + +/** + *

      ErrorReporter is used by {@link org.jdesktop.swingx.JXErrorPane} to + * implement a pluggable error reporting API. For example, a + * JXErrorPane may use an EmailErrorReporter, or a + * {@code LogErrorReporter}, or perhaps even an + * RSSErrorReporter.

      + * + * @status REVIEWED + * @author Alexander Zuev + * @author rbair + */ +public interface ErrorReporter { + /** + *

      Reports an error based on the given {@link ErrorInfo}. This + * method may be a long running method, and so should not block the EDT in + * any way. If an error occurs while reporting the error, it must not + * throw an exception from this method. If an error dialog causes another error, + * it should be silently swallowed. If proper heuristics can be used, an attempt + * can be made some time later to re-report failed error reports, but such attempts + * should be transparent to the user.

      + * + * @param info encapsulates all information to report using this facility. Must not be null. + * @exception thrown if the info param is null + */ + public void reportError(ErrorInfo info) throws NullPointerException; +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/error/ErrorSupport.java b/src/main/java/org/jdesktop/swingx/error/ErrorSupport.java new file mode 100644 index 0000000000..0fb4bd9ffa --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/error/ErrorSupport.java @@ -0,0 +1,89 @@ +/* + * $Id: ErrorSupport.java 3840 2010-10-09 03:25:17Z kschaefe $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.error; + +import javax.swing.*; +import java.util.ArrayList; +import java.util.List; + +/** + * ErrorSupport provides support for managing error listeners. + * @author Joshua Marinacci joshua.marinacci@sun.com + * @see ErrorListener + * @see ErrorEvent + */ +public class ErrorSupport { + private List listeners; + private Object source; + + /** + * Creates a new instance of ErrorSupport + * @param source The object which will fire the ErrorEvents + */ + public ErrorSupport(Object source) { + this.source = source; + listeners = new ArrayList(); + } + + /** + * Add an ErrorListener + * @param listener the listener to add + */ + public void addErrorListener(ErrorListener listener) { + listeners.add(listener); + } + + /** + * Remove an error listener + * @param listener the listener to remove + */ + public void removeErrorListener(ErrorListener listener) { + listeners.remove(listener); + } + + /** + * Returns an array of all the listeners which were added to the + * ErrorSupport object with addErrorListener(). + * @return all of the ErrorListeners added or an empty array if no listeners have been + * added. + */ + public ErrorListener[] getErrorListeners() { + return listeners.toArray(new ErrorListener[0]); + } + + /** + * Report that an error has occurred + * @param throwable The {@link Error} or {@link Exception} which occured. + */ + public void fireErrorEvent(final Throwable throwable) { + final ErrorEvent evt = new ErrorEvent(throwable, source); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + for(ErrorListener el : listeners) { + el.errorOccured(evt); + } + } + }); + } + +} diff --git a/src/main/java/org/jdesktop/swingx/error/package-info.java b/src/main/java/org/jdesktop/swingx/error/package-info.java new file mode 100644 index 0000000000..83d9035161 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/error/package-info.java @@ -0,0 +1,25 @@ +/* + * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +/** + * Contains classes and interfaces used by the {@code JErrorPane} component. + */ +package org.jdesktop.swingx.error; + diff --git a/src/main/java/org/jdesktop/swingx/event/CompoundFocusListener.java b/src/main/java/org/jdesktop/swingx/event/CompoundFocusListener.java new file mode 100644 index 0000000000..7db84c38d3 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/event/CompoundFocusListener.java @@ -0,0 +1,174 @@ +/* + * Created on 23.04.2009 + * + */ +package org.jdesktop.swingx.event; + +import org.jdesktop.beans.AbstractBean; +import org.jdesktop.swingx.SwingXUtilities; +import org.jdesktop.swingx.util.Contract; + +import javax.swing.*; +import java.awt.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +/** + * An convenience class which maps focusEvents received + * from a container hierarchy to a bound read-only property. Registered + * PropertyChangeListeners are notified if the focus is transfered into/out of + * the hierarchy of a given root. + *

      + * + * F.i, client code which wants to get notified if focus enters/exits the hierarchy below + * panel would install the compound focus listener like: + * + *

      + * 
      + *         // add some components inside
      + *         panel.add(new JTextField("something to .... focus"));
      + *         panel.add(new JXDatePicker(new Date()));
      + *         JComboBox combo = new JComboBox(new Object[] {"dooooooooo", 1, 2, 3, 4 });
      + *         combo.setEditable(true);
      + *         panel.add(new JButton("something else to ... focus"));
      + *         panel.add(combo);
      + *         panel.setBorder(new TitledBorder("has focus dispatcher"));
      + *         // register the compound dispatcher
      + *         CompoundFocusListener report = new CompoundFocusListener(panel);
      + *         PropertyChangeListener l = new PropertyChangeListener() {
      + * 
      + *             public void propertyChange(PropertyChangeEvent evt) {
      + *                 // do something useful here
      + *                 
      + *             }};
      + *         report.addPropertyChangeListener(l);    
      + *         
      + * 
      + * 
      + * + * PENDING JW: change of current instance of KeyboardFocusManager? + * + */ +public class CompoundFocusListener extends AbstractBean { + + /** the root of the component hierarchy. + * PENDING JW: weak reference and auto-release listener? + */ + private JComponent root; + /** PropertyChangeListener registered with the current keyboardFocusManager. */ + private PropertyChangeListener managerListener; + private boolean focused; + + /** + * Instantiates a CompoundFocusListener on the component hierarchy below the given + * component. + * + * @param root the root of a component hierarchy + * @throws NullPointerException if the root is null + */ + public CompoundFocusListener(JComponent root) { + this.root = Contract.asNotNull(root, "root must not be null"); + KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); + addManagerListener(manager); + permanentFocusOwnerChanged(manager.getPermanentFocusOwner()); + } + + + /** + * Return true if the root or any of its descendants is focused. This is a + * read-only bound property, that is property change event is fired if focus + * is transfered into/out of root's hierarchy. + * + * @return a boolean indicating whether or not any component in the + * container hierarchy below root is permanent focus owner. + */ + public boolean isFocused() { + return focused; + } + + /** + * Releases all listeners and internal references.

      + * + * Note: this instance must not be used after calling this method. + * + */ + public void release() { + removeManagerListener(KeyboardFocusManager.getCurrentKeyboardFocusManager()); + removeAllListeners(); + this.root = null; + } + + /** + * Removes all property change listeners which are registered with this instance. + */ + private void removeAllListeners() { + for (PropertyChangeListener l : getPropertyChangeListeners()) { + removePropertyChangeListener(l); + } + } + + /** + * Updates focused property depending on whether or not the given component + * is below the root's hierarchy.

      + * + * Note: Does nothing if the component is null. This might not be entirely correct, + * but property change events from the focus manager come in pairs, with only + * one of the new/old value not-null. + * + * @param focusOwner the component with is the current focusOwner. + */ + protected void permanentFocusOwnerChanged(Component focusOwner) { + if (focusOwner == null) return; + setFocused(SwingXUtilities.isDescendingFrom(focusOwner, root)); + } + + private void setFocused(boolean focused) { + boolean old = isFocused(); + this.focused = focused; + firePropertyChange("focused", old, isFocused()); + } + + + /** + * Adds all listeners to the given KeyboardFocusManager.

      + * + * @param manager the KeyboardFocusManager to add internal listeners to. + * @see #removeManagerListener(KeyboardFocusManager) + */ + private void addManagerListener(KeyboardFocusManager manager) { + manager.addPropertyChangeListener("permanentFocusOwner", getManagerListener()); + } + + /** + * Removes all listeners this instance has installed from the given KeyboardFocusManager.

      + * + * @param manager the KeyboardFocusManager to remove internal listeners from. + * @see #addManagerListener(KeyboardFocusManager) + */ + private void removeManagerListener(KeyboardFocusManager manager) { + manager.removePropertyChangeListener("permanentFocusOwner", getManagerListener()); + } + + /** + * Lazily creates and returns a property change listener to be registered on the + * KeyboardFocusManager. + * + * @return a property change listener to be registered on the KeyboardFocusManager. + */ + private PropertyChangeListener getManagerListener() { + if (managerListener == null) { + managerListener = new PropertyChangeListener() { + + @Override + public void propertyChange(PropertyChangeEvent evt) { + if ("permanentFocusOwner".equals(evt.getPropertyName())) { + permanentFocusOwnerChanged((Component) evt.getNewValue()); + } + + }}; + } + return managerListener; + } + + +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/event/DateSelectionEvent.java b/src/main/java/org/jdesktop/swingx/event/DateSelectionEvent.java new file mode 100644 index 0000000000..ecaecb81fb --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/event/DateSelectionEvent.java @@ -0,0 +1,100 @@ +/* + * $Id: DateSelectionEvent.java 3272 2009-02-25 11:06:37Z kleopatra $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.event; + +import org.jdesktop.swingx.calendar.DateSelectionModel; + +import java.util.Date; +import java.util.EventObject; +import java.util.SortedSet; + +/** + * @author Joshua Outwater + */ +public class DateSelectionEvent extends EventObject { + public static enum EventType { + DATES_ADDED, + DATES_REMOVED, + DATES_SET, + SELECTION_CLEARED, + SELECTABLE_DATES_CHANGED, + SELECTABLE_RANGE_CHANGED, + UNSELECTED_DATES_CHANGED, + LOWER_BOUND_CHANGED, + UPPER_BOUND_CHANGED, + ADJUSTING_STARTED, ADJUSTING_STOPPED, + CALENDAR_CHANGED, + } + + private EventType eventType; + private boolean adjusting; + + /** + * Constructs a prototypical Event. + * + * @param source The object on which the Event initially occurred. + * @param eventType the type of the event + * @param adjusting the adjusting property of the source + * @throws IllegalArgumentException if source is null. + */ + public DateSelectionEvent(Object source, EventType eventType, boolean adjusting) { + super(source); + this.eventType = eventType; + this.adjusting = adjusting; + } + + /** + * Returns the selection of the source dateSelectionModel.

      + * + * PENDING JW: that's the "live" selection, that is the source is re-queried on every call + * to this method. Bug or feature? + * + * @return the selection of the source. + */ + public SortedSet getSelection() { + return ((DateSelectionModel)source).getSelection(); + } + + /** + * Returns the type of this event. + * + * @return the type of event. + */ + public final EventType getEventType() { + return eventType; + } + + /** + * Returns a boolean indicating whether the event source is in adjusting state. + * + * @return true if the event is fired while the model is in adjusting state. + */ + public boolean isAdjusting() { + return adjusting; + } + + @Override + public String toString() { + return "[" + String.valueOf(getSource()) + " type: " + getEventType() + " isAdjusting: " + isAdjusting(); + } + + +} diff --git a/src/main/java/org/jdesktop/swingx/event/DateSelectionListener.java b/src/main/java/org/jdesktop/swingx/event/DateSelectionListener.java new file mode 100644 index 0000000000..0d9426a1f1 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/event/DateSelectionListener.java @@ -0,0 +1,31 @@ +/* + * $Id: DateSelectionListener.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.event; + + +import java.util.EventListener; + +/** + * @author Joshua Outwater + */ +public interface DateSelectionListener extends EventListener { + public void valueChanged(DateSelectionEvent ev); +} diff --git a/src/main/java/org/jdesktop/swingx/event/EventListenerMap.java b/src/main/java/org/jdesktop/swingx/event/EventListenerMap.java new file mode 100644 index 0000000000..4d52ad884e --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/event/EventListenerMap.java @@ -0,0 +1,136 @@ +/* + * $Id: EventListenerMap.java 3189 2009-01-20 17:46:04Z kschaefe $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.event; + +import java.util.*; + +/** + * Intended to be a replacement for {@link javax.swing.event.EventListenerList}. + * + * @author Joshua Outwater + * @author Karl Schaefer + * @see javax.swing.event.EventListenerList + */ +public class EventListenerMap { + private final Map, List> listenerList = + new HashMap, List>(); + + /** + * Returns a list containing all of the listeners managed by this {@code EventListenerMap}. + * + * @return all managed listeners + */ + public List getListeners() { + List listeners = new ArrayList(); + + for (List list : listenerList.values()) { + listeners.addAll(list); + } + + return listeners; + } + + /** + * Return a list of all the listeners of the given type. + * + * @return all of the listeners of the specified type. + */ + @SuppressWarnings("unchecked") + public List getListeners(Class clazz) { + List list = (List) listenerList.get(clazz); + if (list == null) { + list = new ArrayList(); + } + return list; + } + + /** + * Returns the total number of listeners of the supplied type + * for this listener list. + */ + public int getListenerCount() { + int count = 0; + + for (List list : listenerList.values()) { + count += list.size(); + } + + return count; + } + + /** + * Returns the total number of listeners for this listener type. + */ + @SuppressWarnings("unchecked") + public int getListenerCount(Class clazz) { + List list = (List) listenerList.get(clazz); + if (list != null) { + return list.size(); + } + return 0; + } + + /** + * Adds the listener as a listener of the specified type. + * + * @param + * the type of the listener to be added + * @param clazz + * the class type to add + * @param l + * the listener to be added + */ + @SuppressWarnings("unchecked") + public synchronized void add(Class clazz, T listener) { + if (listener == null) { + return; + } + + List list = (List) listenerList.get(clazz); + if (list == null) { + list = new ArrayList(); + listenerList.put(clazz, list); + } + list.add(listener); + } + + /** + * Removes the listener as a listener of the specified type. + * + * @param + * the type of the listener to remove + * @param clazz + * the class type to remove + * @param l + * the listener to remove + */ + @SuppressWarnings("unchecked") + public synchronized void remove(Class clazz, T listener) { + if (listener == null) { + return; + } + + List list = (List) listenerList.get(clazz); + if (list != null) { + list.remove(listener); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/event/TableColumnModelExtListener.java b/src/main/java/org/jdesktop/swingx/event/TableColumnModelExtListener.java new file mode 100644 index 0000000000..2df544ffd5 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/event/TableColumnModelExtListener.java @@ -0,0 +1,78 @@ +/* + * $Id: TableColumnModelExtListener.java 1395 2006-09-14 14:40:12Z kleopatra $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.event; + +import javax.swing.event.TableColumnModelListener; +import java.beans.PropertyChangeEvent; + +/** + * Extended TableColumnModelListener which is interested + * in property changes of contained TableColumns.

      + * + * Enhanced TableColumnModelExt guarantees to notify + * these extended column listeners. An example of a client which + * adjusts itself based on headerValue property of visible columns: + *

      
      + * TableColumnModelExtListener l = new TableColumnModelExtListener() {
      + * 
      + *     public void columnPropertyChange(PropertyChangeEvent event) {
      + *         if ("headerValue".equals(event.getPropertyName())) {
      + *             TableColumn column = (TableColumn) event.getSource();
      + *             if ((column instanceof TableColumnExt)
      + *                     && !((TableColumnExt) column).isVisible()) {
      + *                 return;
      + *             }
      + *             resizeAndRepaint();
      + *         }
      + *     }
      + * 
      + *     public void columnAdded(TableColumnModelEvent e) {
      + *     }
      + * 
      + *     public void columnMarginChanged(ChangeEvent e) {
      + *     }
      + * 
      + *     public void columnMoved(TableColumnModelEvent e) {
      + *     }
      + * 
      + *     public void columnRemoved(TableColumnModelEvent e) {
      + *     }
      + * 
      + *     public void columnSelectionChanged(ListSelectionEvent e) {
      + *     }
      + * 
      + * };
      + * columnModel.addColumnModelListener(l);
      + * 
      + * + * @author Jeanette Winzenburg + * @see org.jdesktop.swingx.table.TableColumnModelExt + */ +public interface TableColumnModelExtListener extends TableColumnModelListener { + + /** + * Notifies listeners about property changes of contained columns. + * The event is the original as fired from the TableColumn. + * @param event a PropertyChangeEvent fired by a TableColumn + * contained in a TableColumnModel + */ + void columnPropertyChange(PropertyChangeEvent event); +} diff --git a/src/main/java/org/jdesktop/swingx/event/TreeExpansionBroadcaster.java b/src/main/java/org/jdesktop/swingx/event/TreeExpansionBroadcaster.java new file mode 100644 index 0000000000..4c3e5ee282 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/event/TreeExpansionBroadcaster.java @@ -0,0 +1,115 @@ +/* + * $Id$ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.event; + +import javax.swing.event.EventListenerList; +import javax.swing.event.TreeExpansionEvent; +import javax.swing.event.TreeExpansionListener; + +/** + * Helper to listen to TreeExpansion events and notify with a remapped source. + * + * @author Jeanette Winzenburg + */ +public class TreeExpansionBroadcaster implements TreeExpansionListener { + + private Object source; + + private EventListenerList listeners; + + public TreeExpansionBroadcaster(Object source) { + this.source = source; + } + + public void addTreeExpansionListener(TreeExpansionListener l) { + getEventListenerList().add(TreeExpansionListener.class, l); + } + + public void removeTreeExpansionListener(TreeExpansionListener l) { + if (!hasListeners()) + return; + listeners.remove(TreeExpansionListener.class, l); + } + + /** + * @return + */ + private EventListenerList getEventListenerList() { + if (listeners == null) { + listeners = new EventListenerList(); + } + return listeners; + } + + // -------------------- TreeExpansionListener + @Override + public void treeExpanded(TreeExpansionEvent event) { + if (!hasListeners()) + return; + fireTreeExpanded(retarget(event)); + } + + @Override + public void treeCollapsed(TreeExpansionEvent event) { + if (!hasListeners()) + return; + fireTreeCollapsed(retarget(event)); + } + + /** + * @param event + */ + private void fireTreeExpanded(TreeExpansionEvent event) { + TreeExpansionListener[] ls = listeners + .getListeners(TreeExpansionListener.class); + for (int i = ls.length - 1; i >= 0; i--) { + ls[i].treeExpanded(event); + } + } + + /** + * @param event + */ + private void fireTreeCollapsed(TreeExpansionEvent event) { + TreeExpansionListener[] ls = listeners + .getListeners(TreeExpansionListener.class); + for (int i = ls.length - 1; i >= 0; i--) { + ls[i].treeCollapsed(event); + } + } + + /** + * @param event + * @return + */ + private TreeExpansionEvent retarget(TreeExpansionEvent event) { + return new TreeExpansionEvent(source, event.getPath()); + } + + /** + * @return + */ + private boolean hasListeners() { + return listeners != null && listeners.getListenerCount() > 0; + } + +} diff --git a/src/main/java/org/jdesktop/swingx/event/WeakEventListenerList.java b/src/main/java/org/jdesktop/swingx/event/WeakEventListenerList.java new file mode 100644 index 0000000000..b27187bcb7 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/event/WeakEventListenerList.java @@ -0,0 +1,275 @@ +/* + * $Id: WeakEventListenerList.java 3190 2009-01-20 17:47:52Z kschaefe $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.event; + +import java.io.Serializable; +import java.lang.ref.WeakReference; +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.EventListener; +import java.util.List; + +/** + * A class that holds a list of EventListeners. A single instance + * can be used to hold all listeners (of all types) for the instance + * using the list. It is the responsibility of the class using the + * EventListenerList to provide type-safe API (preferably conforming + * to the JavaBeans spec) and methods which dispatch event notification + * methods to appropriate Event Listeners on the list. + * + * The main benefit that this class provides is that it releases + * garbage collected listeners (internally uses weak references).

      + * + * PENDING: serialization support + * + * + * Usage example: + * Say one is defining a class that sends out FooEvents, and one wants + * to allow users of the class to register FooListeners and receive + * notification when FooEvents occur. The following should be added + * to the class definition: + *

      + * EventListenerList listenerList = new EventListenerList();
      + * FooEvent fooEvent = null;
      + *
      + * public void addFooListener(FooListener l) {
      + *     listenerList.add(FooListener.class, l);
      + * }
      + *
      + * public void removeFooListener(FooListener l) {
      + *     listenerList.remove(FooListener.class, l);
      + * }
      + *
      + *
      + * // Notify all listeners that have registered interest for
      + * // notification on this event type.  The event instance 
      + * // is lazily created using the parameters passed into 
      + * // the fire method.
      + *
      + * 
      + * protected void fireFooXXX() {
      + *     // Guaranteed to return a non-null array
      + *     FooListener[] listeners = listenerList.getListeners(FooListener.class);
      + *     // Process the listeners last to first, notifying
      + *     // those that are interested in this event
      + *     for (FooListener listener: listeners) {
      + *             // Lazily create the event:
      + *             if (fooEvent == null)
      + *                 fooEvent = new FooEvent(this);
      + *             listener.fooXXX(fooEvent);
      + *         }
      + *     }
      + * }
      + * 
      + * foo should be changed to the appropriate name, and fireFooXxx to the + * appropriate method name. One fire method should exist for each + * notification method in the FooListener interface. + *

      + * Warning: + * Serialized objects of this class will not be compatible with + * future Swing releases. The current serialization support is + * appropriate for short term storage or RMI between applications running + * the same version of Swing. As of 1.4, support for long term storage + * of all JavaBeansTM + * has been added to the java.beans package. + * Please see {@link java.beans.XMLEncoder}. + * + * @version 1.37 11/17/05 + * @author Georges Saab + * @author Hans Muller + * @author James Gosling + */ +public class WeakEventListenerList implements Serializable { + + protected transient List> weakReferences; + protected transient List> classes; + + /** + * Passes back the event listener list as an array + * of ListenerType-listener pairs. + * As a side-effect, cleans out any + * garbage collected listeners before building the array. + * + * @return a array of listenerType-listener pairs. + */ + public Object[] getListenerList() { + List listeners = cleanReferences(); + Object[] result = new Object[listeners.size() * 2]; + for (int i = 0; i < listeners.size(); i++) { + result[2*i + 1] = listeners.get(i); + result[2*i] = getClasses().get(i); + } + return result; + } + + /** + * Returns a list of strongly referenced EventListeners. Removes + * internal weak references to garbage collected listeners. + * + * @return + */ + @SuppressWarnings("unchecked") + private synchronized List cleanReferences() { + List listeners = new ArrayList(); + for (int i = getReferences().size() - 1; i >= 0; i--) { + + Object listener = getReferences().get(i).get(); + if (listener == null) { + getReferences().remove(i); + getClasses().remove(i); + } else { + listeners.add(0, (T) listener); + } + } + return listeners; + } + + private List> getReferences() { + if (weakReferences == null) { + weakReferences = new ArrayList>(); + } + return weakReferences; + } + + private List> getClasses() { + if (classes == null) { + classes = new ArrayList>(); + + } + return classes; + } + /** + * Return an array of all the listeners of the given type. + * As a side-effect, cleans out any + * garbage collected listeners before building the array. + * @return all of the listeners of the specified type. + * @exception ClassCastException if the supplied class + * is not assignable to EventListener + * + * @since 1.3 + */ + @SuppressWarnings("unchecked") + public T[] getListeners(Class t) { + List liveListeners = cleanReferences(); + List listeners = new ArrayList(); + for (int i = 0; i < liveListeners.size(); i++) { + if (getClasses().get(i) == t) { + listeners.add(liveListeners.get(i)); + } + } + T[] result = (T[])Array.newInstance(t, listeners.size()); + return listeners.toArray(result); + } + + /** + * Adds the listener as a listener of the specified type. + * As a side-effect, cleans out any garbage collected + * listeners before adding. + * @param t the type of the listener to be added + * @param l the listener to be added + */ + public synchronized void add(Class t, T l) { + if (l==null) { + // In an ideal world, we would do an assertion here + // to help developers know they are probably doing + // something wrong + return; + } + if (!t.isInstance(l)) { + throw new IllegalArgumentException("Listener " + l + + " is not of type " + t); + } + cleanReferences(); + getReferences().add(new WeakReference(l)); + getClasses().add(t); + } + + /** + * Removes the listener as a listener of the specified type. + * @param t the type of the listener to be removed + * @param l the listener to be removed + */ + public synchronized void remove(Class t, T l) { + if (l ==null) { + // In an ideal world, we would do an assertion here + // to help developers know they are probably doing + // something wrong + return; + } + if (!t.isInstance(l)) { + throw new IllegalArgumentException("Listener " + l + + " is not of type " + t); + } + for (int i = 0; i < getReferences().size(); i++) { + if (l.equals(getReferences().get(i).get()) && + (t == getClasses().get(i))) { + getReferences().remove(i); + getClasses().remove(i); + break; + } + } + } + +// // Serialization support. +// private void writeObject(ObjectOutputStream s) throws IOException { +// Object[] lList = listenerList; +// s.defaultWriteObject(); +// +// // Save the non-null event listeners: +// for (int i = 0; i < lList.length; i+=2) { +// Class t = (Class)lList[i]; +// EventListener l = (EventListener)lList[i+1]; +// if ((l!=null) && (l instanceof Serializable)) { +// s.writeObject(t.getName()); +// s.writeObject(l); +// } +// } +// +// s.writeObject(null); +// } +// +// private void readObject(ObjectInputStream s) +// throws IOException, ClassNotFoundException { +// listenerList = NULL_ARRAY; +// s.defaultReadObject(); +// Object listenerTypeOrNull; +// +// while (null != (listenerTypeOrNull = s.readObject())) { +// ClassLoader cl = Thread.currentThread().getContextClassLoader(); +// EventListener l = (EventListener)s.readObject(); +// add((Class)Class.forName((String)listenerTypeOrNull, true, cl), l); +// } +// } + +// /** +// * Returns a string representation of the EventListenerList. +// */ +// public String toString() { +// Object[] lList = listenerList; +// String s = "EventListenerList: "; +// s += lList.length/2 + " listeners: "; +// for (int i = 0 ; i <= lList.length-2 ; i+=2) { +// s += " type " + ((Class)lList[i]).getName(); +// s += " listener " + lList[i+1]; +// } +// return s; +// } +} diff --git a/src/main/java/org/jdesktop/swingx/event/package-info.java b/src/main/java/org/jdesktop/swingx/event/package-info.java new file mode 100644 index 0000000000..babe7c2879 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/event/package-info.java @@ -0,0 +1,38 @@ +/* + * $Id: package-info.java 3165 2009-01-02 13:26:07Z rah003 $ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +/**Contains API for events added as part of JDNC's Swing extensions, +such as message and progress events. + +

      Package Specification

      + + + +

      Related Documentation

      + + + +*/ +package org.jdesktop.swingx.event; \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/geom/Morphing2D.java b/src/main/java/org/jdesktop/swingx/geom/Morphing2D.java new file mode 100644 index 0000000000..d77f926ac3 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/geom/Morphing2D.java @@ -0,0 +1,705 @@ +/* + * $Id: Morphing2D.java 3863 2010-10-26 02:53:32Z kschaefe $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.geom; + +import java.awt.*; +import java.awt.geom.*; + +/** + *

      A morphing shape is a shape which geometry is constructed from two + * other shapes: a start shape and an end shape.

      + *

      The morphing property of a morphing shape defines the amount of + * transformation applied to the start shape to turn it into the end shape.

      + *

      Both shapes must have the same winding rule.

      + * + * @author Jim Graham + * @author Romain Guy (Maintainer) + */ +public class Morphing2D implements Shape { + private double morph; + private Geometry startGeometry; + private Geometry endGeometry; + + /** + *

      Creates a new morphing shape. A morphing shape can be used to turn + * one shape into another one. The transformation can be controlled by the + * morph property.

      + * + * @param startShape the shape to morph from + * @param endShape the shape to morph to + * + * @throws IllegalPathStateException if the shapes do not have the same + * winding rule + * @see #getMorphing() + * @see #setMorphing(double) + */ + public Morphing2D(Shape startShape, Shape endShape) { + startGeometry = new Geometry(startShape); + endGeometry = new Geometry(endShape); + if (startGeometry.getWindingRule() != endGeometry.getWindingRule()) { + throw new IllegalPathStateException("shapes must use same " + + "winding rule"); + } + double tvals0[] = startGeometry.getTvals(); + double tvals1[] = endGeometry.getTvals(); + double masterTvals[] = mergeTvals(tvals0, tvals1); + startGeometry.setTvals(masterTvals); + endGeometry.setTvals(masterTvals); + } + + /** + *

      Returns the morphing value between the two shapes.

      + * + * @return the morphing value between the two shapes + * + * @see #setMorphing(double) + */ + public double getMorphing() { + return morph; + } + + /** + *

      Sets the morphing value between the two shapes. This value controls + * the transformation from the start shape to the end shape. A value of 0.0 + * is the start shape. A value of 1.0 is the end shape. A value of 0.5 is a + * new shape, morphed half way from the start shape to the end shape.

      + *

      The specified value should be between 0.0 and 1.0. If not, the value + * is clamped in the appropriate range.

      + * + * @param morph the morphing value between the two shapes + * + * @see #getMorphing() + */ + public void setMorphing(double morph) { + if (morph > 1) { + morph = 1; + } else if (morph >= 0) { + // morphing is finite, not NaN, and in range + } else { + // morph is < 0 or NaN + morph = 0; + } + this.morph = morph; + } + + private static double interp(double v0, double v1, double t) { + return (v0 + ((v1 - v0) * t)); + } + + private static double[] mergeTvals(double tvals0[], double tvals1[]) { + int i0 = 0; + int i1 = 0; + int numtvals = 0; + while (i0 < tvals0.length && i1 < tvals1.length) { + double t0 = tvals0[i0]; + double t1 = tvals1[i1]; + if (t0 <= t1) { + i0++; + } + if (t1 <= t0) { + i1++; + } + numtvals++; + } + double newtvals[] = new double[numtvals]; + i0 = 0; + i1 = 0; + numtvals = 0; + while (i0 < tvals0.length && i1 < tvals1.length) { + double t0 = tvals0[i0]; + double t1 = tvals1[i1]; + if (t0 <= t1) { + newtvals[numtvals] = t0; + i0++; + } + if (t1 <= t0) { + newtvals[numtvals] = t1; + i1++; + } + numtvals++; + } + return newtvals; + } + + /** + * {@inheritDoc} + */ + @Override + public Rectangle getBounds() { + return getBounds2D().getBounds(); + } + + /** + * {@inheritDoc} + */ + @Override + public Rectangle2D getBounds2D() { + int n = startGeometry.getNumCoords(); + double xmin, ymin, xmax, ymax; + xmin = xmax = interp(startGeometry.getCoord(0), endGeometry.getCoord(0), + morph); + ymin = ymax = interp(startGeometry.getCoord(1), endGeometry.getCoord(1), + morph); + for (int i = 2; i < n; i += 2) { + double x = interp(startGeometry.getCoord(i), + endGeometry.getCoord(i), morph); + double y = interp(startGeometry.getCoord(i + 1), + endGeometry.getCoord(i + 1), morph); + if (xmin > x) { + xmin = x; + } + if (ymin > y) { + ymin = y; + } + if (xmax < x) { + xmax = x; + } + if (ymax < y) { + ymax = y; + } + } + return new Rectangle2D.Double(xmin, ymin, xmax - xmin, ymax - ymin); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean contains(double x, double y) { + throw new InternalError("unimplemented"); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean contains(Point2D p) { + return contains(p.getX(), p.getY()); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean intersects(double x, double y, double w, double h) { + throw new InternalError("unimplemented"); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean intersects(Rectangle2D r) { + return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean contains(double x, double y, double w, double h) { + throw new InternalError("unimplemented"); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean contains(Rectangle2D r) { + return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * {@inheritDoc} + */ + @Override + public PathIterator getPathIterator(AffineTransform at) { + return new Iterator(at, startGeometry, endGeometry, morph); + } + + /** + * {@inheritDoc} + */ + @Override + public PathIterator getPathIterator(AffineTransform at, double flatness) { + return new FlatteningPathIterator(getPathIterator(at), flatness); + } + + private static class Geometry { + static final double THIRD = (1.0 / 3.0); + static final double MIN_LEN = 0.001; + double bezierCoords[]; + int numCoords; + int windingrule; + double myTvals[]; + + public Geometry(Shape s) { + // Multiple of 6 plus 2 more for initial moveto + bezierCoords = new double[20]; + PathIterator pi = s.getPathIterator(null); + windingrule = pi.getWindingRule(); + if (pi.isDone()) { + // We will have 1 segment and it will be all zeros + // It will have 8 coordinates (2 for moveto, 6 for cubic) + numCoords = 8; + } + double coords[] = new double[6]; + int type = pi.currentSegment(coords); + pi.next(); + if (type != PathIterator.SEG_MOVETO) { + throw new IllegalPathStateException("missing initial moveto"); + } + double curx = bezierCoords[0] = coords[0]; + double cury = bezierCoords[1] = coords[1]; + double newx, newy; + numCoords = 2; + while (!pi.isDone()) { + if (numCoords + 6 > bezierCoords.length) { + // Keep array size to a multiple of 6 plus 2 + int newsize = (numCoords - 2) * 2 + 2; + double newCoords[] = new double[newsize]; + System.arraycopy(bezierCoords, 0, newCoords, 0, numCoords); + bezierCoords = newCoords; + } + switch (pi.currentSegment(coords)) { + case PathIterator.SEG_MOVETO: + throw new InternalError( + "Cannot handle multiple subpaths"); + case PathIterator.SEG_CLOSE: + if (curx == bezierCoords[0] && cury == bezierCoords[1]) + { + break; + } + coords[0] = bezierCoords[0]; + coords[1] = bezierCoords[1]; + /* NO BREAK */ + case PathIterator.SEG_LINETO: + newx = coords[0]; + newy = coords[1]; + // A third of the way from curxy to newxy: + bezierCoords[numCoords++] = interp(curx, newx, THIRD); + bezierCoords[numCoords++] = interp(cury, newy, THIRD); + // A third of the way from newxy back to curxy: + bezierCoords[numCoords++] = interp(newx, curx, THIRD); + bezierCoords[numCoords++] = interp(newy, cury, THIRD); + bezierCoords[numCoords++] = curx = newx; + bezierCoords[numCoords++] = cury = newy; + break; + case PathIterator.SEG_QUADTO: + double ctrlx = coords[0]; + double ctrly = coords[1]; + newx = coords[2]; + newy = coords[3]; + // A third of the way from ctrlxy back to curxy: + bezierCoords[numCoords++] = interp(ctrlx, curx, THIRD); + bezierCoords[numCoords++] = interp(ctrly, cury, THIRD); + // A third of the way from ctrlxy to newxy: + bezierCoords[numCoords++] = interp(ctrlx, newx, THIRD); + bezierCoords[numCoords++] = interp(ctrly, newy, THIRD); + bezierCoords[numCoords++] = curx = newx; + bezierCoords[numCoords++] = cury = newy; + break; + case PathIterator.SEG_CUBICTO: + bezierCoords[numCoords++] = coords[0]; + bezierCoords[numCoords++] = coords[1]; + bezierCoords[numCoords++] = coords[2]; + bezierCoords[numCoords++] = coords[3]; + bezierCoords[numCoords++] = curx = coords[4]; + bezierCoords[numCoords++] = cury = coords[5]; + break; + } + pi.next(); + } + // Add closing segment if either: + // - we only have initial moveto - expand it to an empty cubic + // - or we are not back to the starting point + if ((numCoords < 8) || + curx != bezierCoords[0] || + cury != bezierCoords[1]) { + newx = bezierCoords[0]; + newy = bezierCoords[1]; + // A third of the way from curxy to newxy: + bezierCoords[numCoords++] = interp(curx, newx, THIRD); + bezierCoords[numCoords++] = interp(cury, newy, THIRD); + // A third of the way from newxy back to curxy: + bezierCoords[numCoords++] = interp(newx, curx, THIRD); + bezierCoords[numCoords++] = interp(newy, cury, THIRD); + bezierCoords[numCoords++] = newx; + bezierCoords[numCoords++] = newy; + } + // Now find the segment endpoint with the smallest Y coordinate + int minPt = 0; + double minX = bezierCoords[0]; + double minY = bezierCoords[1]; + for (int ci = 6; ci < numCoords; ci += 6) { + double x = bezierCoords[ci]; + double y = bezierCoords[ci + 1]; + if (y < minY || (y == minY && x < minX)) { + minPt = ci; + minX = x; + minY = y; + } + } + // If the smallest Y coordinate is not the first coordinate, + // rotate the points so that it is... + if (minPt > 0) { + // Keep in mind that first 2 coords == last 2 coords + double newCoords[] = new double[numCoords]; + // Copy all coordinates from minPt to the end of the + // array to the beginning of the new array + System.arraycopy(bezierCoords, minPt, + newCoords, 0, + numCoords - minPt); + // Now we do not want to copy 0,1 as they are duplicates + // of the last 2 coordinates which we just copied. So + // we start the source copy at index 2, but we still + // copy a full minPt coordinates which copies the two + // coordinates that were at minPt to the last two elements + // of the array, thus ensuring that thew new array starts + // and ends with the same pair of coordinates... + System.arraycopy(bezierCoords, 2, + newCoords, numCoords - minPt, + minPt); + bezierCoords = newCoords; + } + /* Clockwise enforcement: + * - This technique is based on the formula for calculating + * the area of a Polygon. The standard formula is: + * Area(Poly) = 1/2 * sum(x[i]*y[i+1] - x[i+1]y[i]) + * - The returned area is negative if the polygon is + * "mostly clockwise" and positive if the polygon is + * "mostly counter-clockwise". + * - One failure mode of the Area calculation is if the + * Polygon is self-intersecting. This is due to the + * fact that the areas on each side of the self-intersection + * are bounded by segments which have opposite winding + * direction. Thus, those areas will have opposite signs + * on the accumulation of their area summations and end + * up canceling each other out partially. + * - This failure mode of the algorithm in determining the + * exact magnitude of the area is not actually a big problem + * for our needs here since we are only using the sign of + * the resulting area to figure out the overall winding + * direction of the path. If self-intersections cause + * different parts of the path to disagree as to the + * local winding direction, that is no matter as we just + * wait for the final answer to tell us which winding + * direction had greater representation. If the final + * result is zero then the path was equal parts clockwise + * and counter-clockwise and we do not care about which + * way we order it as either way will require half of the + * path to unwind and re-wind itself. + */ + double area = 0; + // Note that first and last points are the same so we + // do not need to process coords[0,1] against coords[n-2,n-1] + curx = bezierCoords[0]; + cury = bezierCoords[1]; + for (int i = 2; i < numCoords; i += 2) { + newx = bezierCoords[i]; + newy = bezierCoords[i + 1]; + area += curx * newy - newx * cury; + curx = newx; + cury = newy; + } + if (area < 0) { + /* The area is negative so the shape was clockwise + * in a Euclidean sense. But, our screen coordinate + * systems have the origin in the upper left so they + * are flipped. Thus, this path "looks" ccw on the + * screen so we are flipping it to "look" clockwise. + * Note that the first and last points are the same + * so we do not need to swap them. + * (Not that it matters whether the paths end up cw + * or ccw in the end as long as all of them are the + * same, but above we called this section "Clockwise + * Enforcement", so we do not want to be liars. ;-) + */ + // Note that [0,1] do not need to be swapped with [n-2,n-1] + // So first pair to swap is [2,3] and [n-4,n-3] + int i = 2; + int j = numCoords - 4; + while (i < j) { + curx = bezierCoords[i]; + cury = bezierCoords[i + 1]; + bezierCoords[i] = bezierCoords[j]; + bezierCoords[i + 1] = bezierCoords[j + 1]; + bezierCoords[j] = curx; + bezierCoords[j + 1] = cury; + i += 2; + j -= 2; + } + } + } + + public int getWindingRule() { + return windingrule; + } + + public int getNumCoords() { + return numCoords; + } + + public double getCoord(int i) { + return bezierCoords[i]; + } + + public double[] getTvals() { + if (myTvals != null) { + return myTvals; + } + + // assert(numCoords >= 8); + // assert(((numCoords - 2) % 6) == 0); + double tvals[] = new double[(numCoords - 2) / 6 + 1]; + + // First calculate total "length" of path + // Length of each segment is averaged between + // the length between the endpoints (a lower bound for a cubic) + // and the length of the control polygon (an upper bound) + double segx = bezierCoords[0]; + double segy = bezierCoords[1]; + double tlen = 0; + int ci = 2; + int ti = 0; + while (ci < numCoords) { + double prevx, prevy, newx, newy; + prevx = segx; + prevy = segy; + newx = bezierCoords[ci++]; + newy = bezierCoords[ci++]; + prevx -= newx; + prevy -= newy; + double len = Math.sqrt(prevx * prevx + prevy * prevy); + prevx = newx; + prevy = newy; + newx = bezierCoords[ci++]; + newy = bezierCoords[ci++]; + prevx -= newx; + prevy -= newy; + len += Math.sqrt(prevx * prevx + prevy * prevy); + prevx = newx; + prevy = newy; + newx = bezierCoords[ci++]; + newy = bezierCoords[ci++]; + prevx -= newx; + prevy -= newy; + len += Math.sqrt(prevx * prevx + prevy * prevy); + // len is now the total length of the control polygon + segx -= newx; + segy -= newy; + len += Math.sqrt(segx * segx + segy * segy); + // len is now sum of linear length and control polygon length + len /= 2; + // len is now average of the two lengths + + /* If the result is zero length then we will have problems + * below trying to do the math and bookkeeping to split + * the segment or pair it against the segments in the + * other shape. Since these lengths are just estimates + * to map the segments of the two shapes onto corresponding + * segments of "approximately the same length", we will + * simply fudge the length of this segment to be at least + * a minimum value and it will simply grow from zero or + * near zero length to a non-trivial size as it morphs. + */ + if (len < MIN_LEN) { + len = MIN_LEN; + } + tlen += len; + tvals[ti++] = tlen; + segx = newx; + segy = newy; + } + + // Now set tvals for each segment to its proportional + // part of the length + double prevt = tvals[0]; + tvals[0] = 0; + for (ti = 1; ti < tvals.length - 1; ti++) { + double nextt = tvals[ti]; + tvals[ti] = prevt / tlen; + prevt = nextt; + } + tvals[ti] = 1; + return (myTvals = tvals); + } + + public void setTvals(double newTvals[]) { + double oldCoords[] = bezierCoords; + double newCoords[] = new double[2 + (newTvals.length - 1) * 6]; + double oldTvals[] = getTvals(); + int oldci = 0; + double x0, xc0, xc1, x1; + double y0, yc0, yc1, y1; + x0 = xc0 = xc1 = x1 = oldCoords[oldci++]; + y0 = yc0 = yc1 = y1 = oldCoords[oldci++]; + int newci = 0; + newCoords[newci++] = x0; + newCoords[newci++] = y0; + double t0 = 0; + double t1 = 0; + int oldti = 1; + int newti = 1; + while (newti < newTvals.length) { + if (t0 >= t1) { + x0 = x1; + y0 = y1; + xc0 = oldCoords[oldci++]; + yc0 = oldCoords[oldci++]; + xc1 = oldCoords[oldci++]; + yc1 = oldCoords[oldci++]; + x1 = oldCoords[oldci++]; + y1 = oldCoords[oldci++]; + t1 = oldTvals[oldti++]; + } + double nt = newTvals[newti++]; + // assert(nt > t0); + if (nt < t1) { + // Make nt proportional to [t0 => t1] range + double relt = (nt - t0) / (t1 - t0); + newCoords[newci++] = x0 = interp(x0, xc0, relt); + newCoords[newci++] = y0 = interp(y0, yc0, relt); + xc0 = interp(xc0, xc1, relt); + yc0 = interp(yc0, yc1, relt); + xc1 = interp(xc1, x1, relt); + yc1 = interp(yc1, y1, relt); + newCoords[newci++] = x0 = interp(x0, xc0, relt); + newCoords[newci++] = y0 = interp(y0, yc0, relt); + xc0 = interp(xc0, xc1, relt); + yc0 = interp(yc0, yc1, relt); + newCoords[newci++] = x0 = interp(x0, xc0, relt); + newCoords[newci++] = y0 = interp(y0, yc0, relt); + } else { + newCoords[newci++] = xc0; + newCoords[newci++] = yc0; + newCoords[newci++] = xc1; + newCoords[newci++] = yc1; + newCoords[newci++] = x1; + newCoords[newci++] = y1; + } + t0 = nt; + } + bezierCoords = newCoords; + numCoords = newCoords.length; + myTvals = newTvals; + } + } + + private static class Iterator implements PathIterator { + AffineTransform at; + Geometry g0; + Geometry g1; + double t; + int cindex; + + public Iterator(AffineTransform at, + Geometry g0, Geometry g1, + double t) { + this.at = at; + this.g0 = g0; + this.g1 = g1; + this.t = t; + } + + /** + * {@inheritDoc} + */ + @Override + public int getWindingRule() { + return g0.getWindingRule(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isDone() { + return (cindex > g0.getNumCoords()); + } + + /** + * {@inheritDoc} + */ + @Override + public void next() { + if (cindex == 0) { + cindex = 2; + } else { + cindex += 6; + } + } + + double dcoords[]; + + /** + * {@inheritDoc} + */ + @Override + public int currentSegment(float[] coords) { + if (dcoords == null) { + dcoords = new double[6]; + } + int type = currentSegment(dcoords); + if (type != SEG_CLOSE) { + coords[0] = (float) dcoords[0]; + coords[1] = (float) dcoords[1]; + if (type != SEG_MOVETO) { + coords[2] = (float) dcoords[2]; + coords[3] = (float) dcoords[3]; + coords[4] = (float) dcoords[4]; + coords[5] = (float) dcoords[5]; + } + } + return type; + } + + /** + * {@inheritDoc} + */ + @Override + public int currentSegment(double[] coords) { + int type; + int n; + if (cindex == 0) { + type = SEG_MOVETO; + n = 2; + } else if (cindex >= g0.getNumCoords()) { + type = SEG_CLOSE; + n = 0; + } else { + type = SEG_CUBICTO; + n = 6; + } + if (n > 0) { + for (int i = 0; i < n; i++) { + coords[i] = interp(g0.getCoord(cindex + i), + g1.getCoord(cindex + i), + t); + } + if (at != null) { + at.transform(coords, 0, coords, 0, n / 2); + } + } + return type; + } + } +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/geom/Star2D.java b/src/main/java/org/jdesktop/swingx/geom/Star2D.java new file mode 100644 index 0000000000..c59ec7b4aa --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/geom/Star2D.java @@ -0,0 +1,340 @@ +/* + * $Id: Star2D.java 3863 2010-10-26 02:53:32Z kschaefe $ + * + * Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy). + * + * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * Copyright (c) 2006 Romain Guy + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jdesktop.swingx.geom; + +import java.awt.*; +import java.awt.geom.*; + +/** + *

      This class provides a star shape. A star is defined by two radii and a + * number of branches. Each branch spans between the two radii. The inner + * radius is the distance between the center of the star and the origin of the + * branches. The outer radius is the distance between the center of the star + * and the tips of the branches.

      + * + * @author Romain Guy + */ + +public class Star2D implements Shape { + private Shape starShape; + private double x; + private double y; + private double innerRadius; + private double outerRadius; + private int branchesCount; + + /** + *

      Creates a new star whose center is located at the specified + * x and y coordinates. The number of branches + * and their length can be specified.

      + * + * @param x the location of the star center + * @param y the location of the star center + * @param innerRadius the distance between the center of the star and the + * origin of the branches + * @param outerRadius the distance between the center of the star and the + * tip of the branches + * @param branchesCount the number of branches in this star; must be >= 3 + * @throws IllegalArgumentException if branchesCount is < 3 or + * if innerRadius is >= outerRadius + */ + public Star2D(double x, double y, + double innerRadius, double outerRadius, + int branchesCount) { + if (branchesCount < 3) { + throw new IllegalArgumentException("The number of branches must" + + " be >= 3."); + } else if (innerRadius >= outerRadius) { + throw new IllegalArgumentException("The inner radius must be < " + + "outer radius."); + } + + this.x = x; + this.y = y; + this.innerRadius = innerRadius; + this.outerRadius = outerRadius; + this.branchesCount = branchesCount; + + starShape = generateStar(x, y, innerRadius, outerRadius, branchesCount); + } + + private static Shape generateStar(double x, double y, + double innerRadius, double outerRadius, + int branchesCount) { + GeneralPath path = new GeneralPath(); + + double outerAngleIncrement = 2 * Math.PI / branchesCount; + + double outerAngle = branchesCount % 2 == 0 ? 0.0 : -(Math.PI / 2.0); + double innerAngle = (outerAngleIncrement / 2.0) + outerAngle; + + float x1 = (float) (Math.cos(outerAngle) * outerRadius + x); + float y1 = (float) (Math.sin(outerAngle) * outerRadius + y); + + float x2 = (float) (Math.cos(innerAngle) * innerRadius + x); + float y2 = (float) (Math.sin(innerAngle) * innerRadius + y); + + path.moveTo(x1, y1); + path.lineTo(x2, y2); + + outerAngle += outerAngleIncrement; + innerAngle += outerAngleIncrement; + + for (int i = 1; i < branchesCount; i++) { + x1 = (float) (Math.cos(outerAngle) * outerRadius + x); + y1 = (float) (Math.sin(outerAngle) * outerRadius + y); + + path.lineTo(x1, y1); + + x2 = (float) (Math.cos(innerAngle) * innerRadius + x); + y2 = (float) (Math.sin(innerAngle) * innerRadius + y); + + path.lineTo(x2, y2); + + outerAngle += outerAngleIncrement; + innerAngle += outerAngleIncrement; + } + + path.closePath(); + return path; + } + + /** + *

      Sets the inner radius of the star, that is the distance between its + * center and the origin of the branches. The inner radius must always be + * lower than the outer radius.

      + * + * @param innerRadius the distance between the center of the star and the + * origin of the branches + * @throws IllegalArgumentException if the inner radius is >= outer radius + */ + public void setInnerRadius(double innerRadius) { + if (innerRadius >= outerRadius) { + throw new IllegalArgumentException("The inner radius must be <" + + " outer radius."); + } + + this.innerRadius = innerRadius; + starShape = generateStar(getX(), getY(), innerRadius, getOuterRadius(), + getBranchesCount()); + } + + /** + *

      Sets location of the center of the star.

      + * + * @param x the x location of the center of the star + */ + public void setX(double x) { + this.x = x; + starShape = generateStar(x, getY(), getInnerRadius(), getOuterRadius(), + getBranchesCount()); + } + + /** + *

      Sets the location of the center of the star.

      + * + * @param y the x location of the center of the star + */ + public void setY(double y) { + this.y = y; + starShape = generateStar(getX(), y, getInnerRadius(), getOuterRadius(), + getBranchesCount()); + } + + /** + *

      Sets the outer radius of the star, that is the distance between its + * center and the tips of the branches. The outer radius must always be + * greater than the inner radius.

      + * + * @param outerRadius the distance between the center of the star and the + * tips of the branches + * @throws IllegalArgumentException if the inner radius is >= outer radius + */ + public void setOuterRadius(double outerRadius) { + if (innerRadius >= outerRadius) { + throw new IllegalArgumentException("The outer radius must be > " + + "inner radius."); + } + + this.outerRadius = outerRadius; + starShape = generateStar(getX(), getY(), getInnerRadius(), outerRadius, + getBranchesCount()); + } + + /** + *

      Sets the number branches of the star. A star must always have at least + * 3 branches.

      + * + * @param branchesCount the number of branches + * @throws IllegalArgumentException if branchesCount is <=2 + */ + public void setBranchesCount(int branchesCount) { + if (branchesCount <= 2) { + throw new IllegalArgumentException("The number of branches must" + + " be >= 3."); + } + + this.branchesCount = branchesCount; + starShape = generateStar(getX(), getY(), getInnerRadius(), + getOuterRadius(), branchesCount); + } + + /** + *

      Returns the location of the center of star.

      + * + * @return the x coordinate of the center of the star + */ + public double getX() { + return x; + } + + /** + *

      Returns the location of the center of star.

      + * + * @return the y coordinate of the center of the star + */ + public double getY() { + return y; + } + + /** + *

      Returns the distance between the center of the star and the origin + * of the branches.

      + * + * @return the inner radius of the star + */ + public double getInnerRadius() { + return innerRadius; + } + + /** + *

      Returns the distance between the center of the star and the tips + * of the branches.

      + * + * @return the outer radius of the star + */ + public double getOuterRadius() { + return outerRadius; + } + + /** + *

      Returns the number of branches of the star.

      + * + * @return the number of branches, always >= 3 + */ + public int getBranchesCount() { + return branchesCount; + } + + /** + * {@inheritDoc} + */ + @Override + public Rectangle getBounds() { + return starShape.getBounds(); + } + + /** + * {@inheritDoc} + */ + @Override + public Rectangle2D getBounds2D() { + return starShape.getBounds2D(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean contains(double x, double y) { + return starShape.contains(x, y); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean contains(Point2D p) { + return starShape.contains(p); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean intersects(double x, double y, double w, double h) { + return starShape.intersects(x, y, w, h); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean intersects(Rectangle2D r) { + return starShape.intersects(r); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean contains(double x, double y, double w, double h) { + return starShape.contains(x, y, w, h); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean contains(Rectangle2D r) { + return starShape.contains(r); + } + + /** + * {@inheritDoc} + */ + @Override + public PathIterator getPathIterator(AffineTransform at) { + return starShape.getPathIterator(at); + } + + /** + * {@inheritDoc} + */ + @Override + public PathIterator getPathIterator(AffineTransform at, double flatness) { + return starShape.getPathIterator(at, flatness); + } +} diff --git a/src/main/java/org/jdesktop/swingx/geom/package-info.java b/src/main/java/org/jdesktop/swingx/geom/package-info.java new file mode 100644 index 0000000000..894798fa94 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/geom/package-info.java @@ -0,0 +1,25 @@ +/* + * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +/** + * Contains custom shapes for 2D rendering. + */ +package org.jdesktop.swingx.geom; + diff --git a/src/main/java/org/jdesktop/swingx/graphics/BlendComposite.java b/src/main/java/org/jdesktop/swingx/graphics/BlendComposite.java new file mode 100644 index 0000000000..6d97cae699 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/graphics/BlendComposite.java @@ -0,0 +1,976 @@ +/* + * $Id: BlendComposite.java 4011 2011-05-05 16:19:34Z kschaefe $ + * + * Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy). + * + * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * Copyright (c) 2006 Romain Guy + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jdesktop.swingx.graphics; + +import java.awt.*; +import java.awt.image.*; + +/** + *

      A blend composite defines the rule according to which a drawing primitive + * (known as the source) is mixed with existing graphics (know as the + * destination.)

      + *

      BlendComposite is an implementation of the + * {@link Composite} interface and must therefore be set as a state on + * a {@link java.awt.Graphics2D} surface.

      + *

      Please refer to {@link java.awt.Graphics2D#setComposite(Composite)} + * for more information on how to use this class with a graphics surface.

      + *

      Blending Modes

      + *

      This class offers a certain number of blending modes, or compositing + * rules. These rules are inspired from graphics editing software packages, + * like Adobe Photoshop or The GIMP.

      + *

      Given the wide variety of implemented blending modes and the difficulty + * to describe them with words, please refer to those tools to visually see + * the result of these blending modes.

      + *

      Opacity

      + *

      Each blending mode has an associated opacity, defined as a float value + * between 0.0 and 1.0. Changing the opacity controls the force with which the + * compositing operation is applied. For instance, a composite with an opacity + * of 0.0 will not draw the source onto the destination. With an opacity of + * 1.0, the source will be fully drawn onto the destination, according to the + * selected blending mode rule.

      + *

      The opacity, or alpha value, is used by the composite instance to mutiply + * the alpha value of each pixel of the source when being composited over the + * destination.

      + *

      Creating a Blend Composite

      + *

      Blend composites can be created in various manners:

      + *
        + *
      • Use one of the pre-defined instance. Example: + * BlendComposite.Average.
      • + *
      • Derive one of the pre-defined instances by calling + * {@link #derive(float)} or {@link #derive(BlendingMode)}. Deriving allows + * you to change either the opacity or the blending mode. Example: + * BlendComposite.Average.derive(0.5f).
      • + *
      • Use a factory method: {@link #getInstance(BlendingMode)} or + * {@link #getInstance(BlendingMode, float)}.
      • + *
      + *

      Functionality Change in SwingX 1.6.3

      + *

      Due to incorrect implementations of various blending modes incompatible changes have occurred. + * The following will help users alleviate problems during migration: + *

        + *
      • {@link BlendingMode#BLUE} and {@link BlendingMode#GREEN} have been swapped.
      • + *
      + *

      + * + * @see BlendingMode + * @see java.awt.Graphics2D + * @see Composite + * @see java.awt.AlphaComposite + * @author Romain Guy + * @author Karl Schaefer (support and additional modes) + */ +public final class BlendComposite implements Composite { + /** + * A blending mode defines the compositing rule of a + * {@link BlendComposite}. + * + * @author Romain Guy + * @author Karl Schaefer (support and additional modes) + */ + public enum BlendingMode { + /** + * The {@code Average} blending mode produces an average of the source and blend colors. The + * image will push colors toward the middle, reducing the extremes. + */ + AVERAGE { + @Override + void blend(int[] src, int[] dst, int[] result) { + result[0] = (src[0] + dst[0]) >> 1; + result[1] = (src[1] + dst[1]) >> 1; + result[2] = (src[2] + dst[2]) >> 1; + result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + + /** + * Similar to {@link #AVERAGE}, but more severely lightens or darkens the edge colors. + */ + STAMP { + @Override + void blend(int[] src, int[] dst, int[] result) { + result[0] = Math.max(0, Math.min(255, dst[0] + 2 * src[0] - 256)); + result[1] = Math.max(0, Math.min(255, dst[1] + 2 * src[1] - 256)); + result[2] = Math.max(0, Math.min(255, dst[2] + 2 * src[2] - 256)); + result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + + /** + * The {@code Darken} blend mode compares the color information for each pixel of the base + * and the blend color and applies the darker color as the result. Any pixels in the base + * image that are lighter than the blend color are replaced, and pixels that are darker are + * left unchanged. No part of the image will become lighter. + */ + DARKEN { + @Override + void blend(int[] src, int[] dst, int[] result) { + result[0] = Math.min(src[0], dst[0]); + result[1] = Math.min(src[1], dst[1]); + result[2] = Math.min(src[2], dst[2]); + result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + + /** + * The {@code Multiply} blend mode multiplies the base color with the blend color. The + * resulting color will always be darker, unless the blend color is white, which will result + * in no change. 100% opaque black multiplied with any color will result in black. As you + * overlay strokes of color with the Multiply blending mode, each stroke will result in + * darker and darker color. + */ + MULTIPLY { + @Override + void blend(int[] src, int[] dst, int[] result) { + result[0] = (src[0] * dst[0] + 2) >> 8; + result[1] = (src[1] * dst[1] + 2) >> 8; + result[2] = (src[2] * dst[2] + 2) >> 8; + result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + + /** + * The {@code Color Burn} blending mode increases the contrast to darken the base color + * while reflecting the blend color. The darker the blend color, the more intensely the + * color will be applied in the base image. White as the blend color produces no change. + */ + COLOR_BURN { + @Override + void blend(int[] src, int[] dst, int[] result) { + result[0] = src[0] == 0 ? 0 : Math.max(0, 255 - (((255 - dst[0]) << 8) / src[0])); + result[1] = src[1] == 0 ? 0 : Math.max(0, 255 - (((255 - dst[1]) << 8) / src[1])); + result[2] = src[2] == 0 ? 0 : Math.max(0, 255 - (((255 - dst[2]) << 8) / src[2])); + result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + + /** + * {@code Inverse Color Burn} is the same as {@link #COLOR_BURN Color Burn} with the source + * and destination swapped. + */ + INVERSE_COLOR_BURN { + @Override + void blend(int[] src, int[] dst, int[] result) { + result[0] = dst[0] == 0 ? 0 : Math.max(0, 255 - (((255 - src[0]) << 8) / dst[0])); + result[1] = dst[1] == 0 ? 0 : Math.max(0, 255 - (((255 - src[1]) << 8) / dst[1])); + result[2] = dst[2] == 0 ? 0 : Math.max(0, 255 - (((255 - src[2]) << 8) / dst[2])); + result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + + SOFT_BURN { + @Override + void blend(int[] src, int[] dst, int[] result) { + result[0] = dst[0] + src[0] < 256 + ? (dst[0] == 255 ? 255 : Math.min(255, (src[0] << 7) / (255 - dst[0]))) + : Math.max(0, 255 - (((255 - dst[0]) << 7) / src[0])); + result[1] = dst[1] + src[1] < 256 + ? (dst[1] == 255 ? 255 : Math.min(255, (src[1] << 7) / (255 - dst[1]))) + : Math.max(0, 255 - (((255 - dst[1]) << 7) / src[1])); + result[2] = dst[2] + src[2] < 256 + ? (dst[2] == 255 ? 255 : Math.min(255, (src[2] << 7) / (255 - dst[2]))) + : Math.max(0, 255 - (((255 - dst[2]) << 7) / src[2])); + result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + + /** + * The {@code Subtract} blend mode is similar to {@link #COLOR_BURN Color Burn} but instead of increasing + * contrast, it decreases brightness to darken the base color and reflect the blend color. + * It is also similar to the Multiply blend mode, but produces a much more intense result. + * White as the blend color produces no change. + *

      + * This mode is also known as {@code Linear Burn}. + */ + SUBTRACT { + @Override + void blend(int[] src, int[] dst, int[] result) { + result[0] = Math.max(0, src[0] + dst[0] - 256); + result[1] = Math.max(0, src[1] + dst[1] - 256); + result[2] = Math.max(0, src[2] + dst[2] - 256); + result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + + /** + * The {@code Lighten} blending mode compares the color information for each pixel of the + * base and the blend color and applies the lighter color as the result. Any pixels in the + * base image that are darker than the blend color are replaced, and pixels that are lighter + * are left unchanged. No part of the image will become darker. + */ + LIGHTEN { + @Override + void blend(int[] src, int[] dst, int[] result) { + result[0] = Math.max(src[0], dst[0]); + result[1] = Math.max(src[1], dst[1]); + result[2] = Math.max(src[2], dst[2]); + result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + + /** + * The {@code Screen} blending mode is the opposite of the {@link #MULTIPLY Multiply} mode + * in that it multiples the inverse of the base color with the blend color. What this means + * is that your image will get lighter overall. In areas where the blend color is black, the + * base image will be unchanged, and in areas where the blend or base color is white, the + * result will be no change. Dark areas in the base image will become significantly lighter, + * and bright areas will become only slightly lighter. + */ + SCREEN { + @Override + void blend(int[] src, int[] dst, int[] result) { + result[0] = 255 - ((255 - src[0]) * (255 - dst[0]) >> 8); + result[1] = 255 - ((255 - src[1]) * (255 - dst[1]) >> 8); + result[2] = 255 - ((255 - src[2]) * (255 - dst[2]) >> 8); + result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + + /** + * The {@code Color Dodge} blending mode is essentially the opposite of {@link #COLOR_BURN + * Color Burn}. The {@code Color Dodge} blending mode decreases the contrast to brighten the + * base color while reflecting the blend color. The lighter the blend color, the more + * significant the color dodge effect will be making the result brighter, with less + * contrast, and tinted toward the blend color. Black as the blend color produces no change. + */ + COLOR_DODGE { + @Override + void blend(int[] src, int[] dst, int[] result) { + result[0] = src[0] == 255 ? 255 : Math.min((dst[0] << 8) / (255 - src[0]), 255); + result[1] = src[1] == 255 ? 255 : Math.min((dst[1] << 8) / (255 - src[1]), 255); + result[2] = src[2] == 255 ? 255 : Math.min((dst[2] << 8) / (255 - src[2]), 255); + result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + + /** + * {@code Inverse Color Dodge} is the same as {@link #COLOR_DODGE Color Dodge} with the + * source and destination swapped. + */ + INVERSE_COLOR_DODGE { + @Override + void blend(int[] src, int[] dst, int[] result) { + result[0] = dst[0] == 255 ? 255 : Math.min((src[0] << 8) / (255 - dst[0]), 255); + result[1] = dst[1] == 255 ? 255 : Math.min((src[1] << 8) / (255 - dst[1]), 255); + result[2] = dst[2] == 255 ? 255 : Math.min((src[2] << 8) / (255 - dst[2]), 255); + result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + + SOFT_DODGE { + @Override + void blend(int[] src, int[] dst, int[] result) { + result[0] = dst[0] + src[0] < 256 + ? (src[0] == 255 ? 255 : Math.min(255, (dst[0] << 7) / (255 - src[0]))) + : Math.max(0, 255 - (((255 - src[0]) << 7) / dst[0])); + result[1] = dst[1] + src[1] < 256 + ? (src[1] == 255 ? 255 : Math.min(255, (dst[1] << 7) / (255 - src[1]))) + : Math.max(0, 255 - (((255 - src[1]) << 7) / dst[1])); + result[2] = dst[2] + src[2] < 256 + ? (src[2] == 255 ? 255 : Math.min(255, (dst[2] << 7) / (255 - src[2]))) + : Math.max(0, 255 - (((255 - src[2]) << 7) / dst[2])); + result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + + /** + * {@code Add} is the opposite of {@link #SUBTRACT Subtract}. It increases brightness to + * lighten the base color and reflect the blend color. It is also similar to the + * {@link #SCREEN Screen} blend mode, but produces a more intense result. Black as the blend + * color produces no change. + *

      + * This mode is also known as {@code Linear Dodge}. + */ + ADD { + @Override + void blend(int[] src, int[] dst, int[] result) { + result[0] = Math.min(255, src[0] + dst[0]); + result[1] = Math.min(255, src[1] + dst[1]); + result[2] = Math.min(255, src[2] + dst[2]); + result[3] = Math.min(255, src[3] + dst[3]); + } + }, + + /** + * The {@code Overlay} blending mode preserves the highlights and shadows of the base color + * while mixing the base color and the blend color. It is a combination of the + * {@link #MULTIPLY Multiply} and {@link #SCREEN Screen} blending modes--multiplying the + * dark areas, and screening the light areas. A blend color of 50% gray has no effect on the + * base image. + */ + OVERLAY { + @Override + void blend(int[] src, int[] dst, int[] result) { + result[0] = dst[0] < 128 ? dst[0] * src[0] >> 7 + : 255 - ((255 - dst[0]) * (255 - src[0]) >> 7); + result[1] = dst[1] < 128 ? dst[1] * src[1] >> 7 + : 255 - ((255 - dst[1]) * (255 - src[1]) >> 7); + result[2] = dst[2] < 128 ? dst[2] * src[2] >> 7 + : 255 - ((255 - dst[2]) * (255 - src[2]) >> 7); + result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + + /** + * The {@code Soft Light} blend mode creates a subtle lighter or darker result depending on + * the brightness of the blend color. Blend colors that are more than 50% brightness will + * lighten the base image and colors that are less than 50% brightness will darken the base + * image. Pure black will create a slightly darker result; pure white will create a slightly + * lighter result, and 50% gray will have no effect on the base image. + */ + SOFT_LIGHT { + @Override + void blend(int[] src, int[] dst, int[] result) { + int mRed = src[0] * dst[0] / 255; + int mGreen = src[1] * dst[1] / 255; + int mBlue = src[2] * dst[2] / 255; + result[0] = mRed + dst[0] * (255 - ((255 - dst[0]) * (255 - src[0]) / 255) - mRed) / 255; + result[1] = mGreen + dst[1] * (255 - ((255 - dst[1]) * (255 - src[1]) / 255) - mGreen) / 255; + result[2] = mBlue + dst[2] * (255 - ((255 - dst[2]) * (255 - src[2]) / 255) - mBlue) / 255; + result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + + /** + * {@code Hard Light} drastically lightens or darkens the base image depending on the + * brightness of the blend color. The effect is more intense than {@link #SOFT_LIGHT Soft + * Light} because the contrast is also increased. Blend colors that are more than 50% + * brightness will lighten the base image in the same way as the screen blending mode. + * Colors that are less than 50% brightness will darken the base image in the same way as + * the multiply blending mode. Pure black will result in black; pure white will create a + * white result, and 50% gray will have no effect on the base image. + */ + HARD_LIGHT { + @Override + void blend(int[] src, int[] dst, int[] result) { + result[0] = src[0] < 128 ? dst[0] * src[0] >> 7 + : 255 - ((255 - src[0]) * (255 - dst[0]) >> 7); + result[1] = src[1] < 128 ? dst[1] * src[1] >> 7 + : 255 - ((255 - src[1]) * (255 - dst[1]) >> 7); + result[2] = src[2] < 128 ? dst[2] * src[2] >> 7 + : 255 - ((255 - src[2]) * (255 - dst[2]) >> 7); + result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + + /** + * Burns or dodges the colors by increasing or decreasing the contrast, depending on the + * blend color. If the blend color is lighter than 50% grey, the image is lightened by + * decreasing the contrast. If the blend color is darker than 50% grey, the image is + * darkened by increasing the contrast. + */ + VIVID_LIGHT { + @Override + void blend(int[] src, int[] dst, int[] result) { + result[0] = src[0] < 128 + ? src[0] == 0 ? 0 : Math.max(0, 255 - ((255 - dst[0]) << 7) / src[0]) + : src[0] == 255 ? 255 : Math.min(255, (dst[0] << 7) / (255 - src[0])); + result[1] = src[1] < 128 + ? src[1] == 0 ? 0 : Math.max(0, 255 - ((255 - dst[1]) << 7) / src[1]) + : src[1] == 255 ? 255 : Math.min(255, (dst[1] << 7) / (255 - src[1])); + result[2] = src[2] < 128 + ? src[2] == 0 ? 0 : Math.max(0, 255 - ((255 - dst[2]) << 7) / src[2]) + : src[2] == 255 ? 255 : Math.min(255, (dst[2] << 7) / (255 - src[2])); + result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + + LINEAR_LIGHT { + @Override + void blend(int[] src, int[] dst, int[] result) { + result[0] = src[0] < 128 ? Math.max(0, dst[0] + (src[0] << 1) - 255) + : Math.min(255, dst[0] + (src[0] - 128 << 1)); + result[1] = src[1] < 128 ? Math.max(0, dst[1] + (src[1] << 1) - 255) + : Math.min(255, dst[1] + (src[1] - 128 << 1)); + result[2] = src[2] < 128 ? Math.max(0, dst[2] + (src[2] << 1) - 255) + : Math.min(255, dst[2] + (src[2] - 128 << 1)); + result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + + PIN_LIGHT { + @Override + void blend(int[] src, int[] dst, int[] result) { + result[0] = src[0] < 128 ? Math.min(dst[0], src[0] << 1) + : Math.max(dst[0], (src[0] - 128) << 1); + result[1] = src[1] < 128 ? Math.min(dst[1], src[1] << 1) + : Math.max(dst[1], (src[1] - 128) << 1); + result[2] = src[2] < 128 ? Math.min(dst[2], src[2] << 1) + : Math.max(dst[2], (src[2] - 128) << 1); + result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + + HARD_MIX { + @Override + void blend(int[] src, int[] dst, int[] result) { + result[0] = src[0] < 256 - dst[0] ? 0 : 255; + result[1] = src[1] < 256 - dst[1] ? 0 : 255; + result[2] = src[2] < 256 - dst[2] ? 0 : 255; + result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + + REFLECT { + @Override + void blend(int[] src, int[] dst, int[] result) { + result[0] = src[0] == 255 ? 255 : Math.min(255, dst[0] * dst[0] / (255 - src[0])); + result[1] = src[1] == 255 ? 255 : Math.min(255, dst[1] * dst[1] / (255 - src[1])); + result[2] = src[2] == 255 ? 255 : Math.min(255, dst[2] * dst[2] / (255 - src[2])); + result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + + GLOW { + @Override + void blend(int[] src, int[] dst, int[] result) { + result[0] = dst[0] == 255 ? 255 : Math.min(255, src[0] * src[0] / (255 - dst[0])); + result[1] = dst[1] == 255 ? 255 : Math.min(255, src[1] * src[1] / (255 - dst[1])); + result[2] = dst[2] == 255 ? 255 : Math.min(255, src[2] * src[2] / (255 - dst[2])); + result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + + FREEZE { + @Override + void blend(int[] src, int[] dst, int[] result) { + result[0] = src[0] == 0 ? 0 : Math.max(0, 255 - (255 - dst[0]) * (255 - dst[0]) + / src[0]); + result[1] = src[1] == 0 ? 0 : Math.max(0, 255 - (255 - dst[1]) * (255 - dst[1]) + / src[1]); + result[2] = src[2] == 0 ? 0 : Math.max(0, 255 - (255 - dst[2]) * (255 - dst[2]) + / src[2]); + result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + + HEAT { + @Override + void blend(int[] src, int[] dst, int[] result) { + result[0] = dst[0] == 0 ? 0 : Math.max(0, 255 - (255 - src[0]) * (255 - src[0]) + / dst[0]); + result[1] = dst[1] == 0 ? 0 : Math.max(0, 255 - (255 - src[1]) * (255 - src[1]) + / dst[1]); + result[2] = dst[2] == 0 ? 0 : Math.max(0, 255 - (255 - src[2]) * (255 - src[2]) + / dst[2]); + result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + + /** + * The {@code Difference} blending mode highlights the differences between the blend layer + * and the base layer. The more technical explanation is that the blend color is subtracted + * from the base color--or vice-versa, depending on the brightness--and the result is the + * difference between them. When white is the blend color, the base image is inverted. When + * black is the blend color, there is no change. + */ + DIFFERENCE { + @Override + void blend(int[] src, int[] dst, int[] result) { + result[0] = Math.abs(dst[0] - src[0]); + result[1] = Math.abs(dst[1] - src[1]); + result[2] = Math.abs(dst[2] - src[2]); + result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + + /** + * The {@code Exclusion} blending mode works very much like {@link #DIFFERENCE Difference} + * but the contrast is lower. When white is the blend color, the base image is inverted. + * When black is the blend color, there is no change. + */ + EXCLUSION { + @Override + void blend(int[] src, int[] dst, int[] result) { + result[0] = dst[0] + src[0] - (dst[0] * src[0] >> 7); + result[1] = dst[1] + src[1] - (dst[1] * src[1] >> 7); + result[2] = dst[2] + src[2] - (dst[2] * src[2] >> 7); + result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + + /** + * The {@code Hue} blend mode applies the hue of the blend color to the base image while retaining + * the luminance and saturation of the base image. It gives the base image a tinted effect + * where the tinting is darkest in areas of high saturation. Where the blend color is a + * shade of gray (0% saturation), the base image is desaturated and where the base image is + * gray, the Hue blending mode has no effect. + */ + HUE { + @Override + void blend(int[] src, int[] dst, int[] result) { + float[] srcHSL = new float[3]; + ColorUtilities.RGBtoHSL(src[0], src[1], src[2], srcHSL); + float[] dstHSL = new float[3]; + ColorUtilities.RGBtoHSL(dst[0], dst[1], dst[2], dstHSL); + + ColorUtilities.HSLtoRGB(srcHSL[0], dstHSL[1], dstHSL[2], result); + result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + + /** + * The {@code Saturation} blending mode applies the saturation of the blend color to the + * base image while retaining the hue and luminance of the base image. Neutral tones (black, + * white, and gray) in the blend will desaturate the base image. Neutral areas in the base + * image will not be changed by the saturation blending mode. + */ + SATURATION { + @Override + void blend(int[] src, int[] dst, int[] result) { + float[] srcHSL = new float[3]; + ColorUtilities.RGBtoHSL(src[0], src[1], src[2], srcHSL); + float[] dstHSL = new float[3]; + ColorUtilities.RGBtoHSL(dst[0], dst[1], dst[2], dstHSL); + + ColorUtilities.HSLtoRGB(dstHSL[0], srcHSL[1], dstHSL[2], result); + result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + + /** + * The {@code Color} blending mode applies the hue and saturation of the blend color to the + * base image while retaining the luminance of the base image. Simply put, it colors the + * base image. Neutral blend colors will desaturate the base image. + */ + COLOR { + @Override + void blend(int[] src, int[] dst, int[] result) { + float[] srcHSL = new float[3]; + ColorUtilities.RGBtoHSL(src[0], src[1], src[2], srcHSL); + float[] dstHSL = new float[3]; + ColorUtilities.RGBtoHSL(dst[0], dst[1], dst[2], dstHSL); + + ColorUtilities.HSLtoRGB(srcHSL[0], srcHSL[1], dstHSL[2], result); + result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + + /** + * The {@code Luminosity} blending mode applies the luminosity (brightness) of the blend + * colors to the base image while retaining the hue and saturation of the base image. + * {@code Luminosity} is the opposite of the {@link #COLOR Color} blending mode. + */ + LUMINOSITY { + @Override + void blend(int[] src, int[] dst, int[] result) { + float[] srcHSL = new float[3]; + ColorUtilities.RGBtoHSL(src[0], src[1], src[2], srcHSL); + float[] dstHSL = new float[3]; + ColorUtilities.RGBtoHSL(dst[0], dst[1], dst[2], dstHSL); + + ColorUtilities.HSLtoRGB(dstHSL[0], dstHSL[1], srcHSL[2], result); + result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + + /** + * This one is the "opposite" of difference mode. Note that it is NOT difference mode + * inverted, because black and white return the same result, but colors between become + * brighter instead of darker. This mode can be used to invert parts of the base image, but + * NOT to compare two images. + */ + NEGATION { + @Override + void blend(int[] src, int[] dst, int[] result) { + result[0] = 255 - Math.abs(255 - dst[0] - src[0]); + result[1] = 255 - Math.abs(255 - dst[1] - src[1]); + result[2] = 255 - Math.abs(255 - dst[2] - src[2]); + result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + + /** + * Keeps the red channel from the blend image and the green and blue channels from the base + * image. + */ + RED { + @Override + void blend(int[] src, int[] dst, int[] result) { + result[0] = src[0]; + result[1] = dst[1]; + result[2] = dst[2]; + result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + + /** + * Keeps the green channel from the blend image and the red and blue channels from the base + * image. + */ + GREEN { + @Override + void blend(int[] src, int[] dst, int[] result) { + result[0] = dst[0]; + result[1] = src[1]; + result[2] = dst[2]; + result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + + /** + * Keeps the blue channel from the blend image and the red and green channels from the base + * image. + */ + BLUE { + @Override + void blend(int[] src, int[] dst, int[] result) { + result[0] = dst[0]; + result[1] = dst[1]; + result[2] = src[2]; + result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + ; + + /** + * Blends the input colors into the result. + * + * @param src + * the source RGBA + * @param dst + * the destination RGBA + * @param result + * the result RGBA + * @throws NullPointerException + * if any argument is {@code null} + */ + abstract void blend(int[] src, int[] dst, int[] result); + } + + public static final BlendComposite Average = new BlendComposite(BlendingMode.AVERAGE); + public static final BlendComposite Multiply = new BlendComposite(BlendingMode.MULTIPLY); + public static final BlendComposite Screen = new BlendComposite(BlendingMode.SCREEN); + public static final BlendComposite Darken = new BlendComposite(BlendingMode.DARKEN); + public static final BlendComposite Lighten = new BlendComposite(BlendingMode.LIGHTEN); + public static final BlendComposite Overlay = new BlendComposite(BlendingMode.OVERLAY); + public static final BlendComposite HardLight = new BlendComposite(BlendingMode.HARD_LIGHT); + public static final BlendComposite SoftLight = new BlendComposite(BlendingMode.SOFT_LIGHT); + public static final BlendComposite VividLight = new BlendComposite(BlendingMode.VIVID_LIGHT); + public static final BlendComposite LinearLight = new BlendComposite(BlendingMode.LINEAR_LIGHT); + public static final BlendComposite PinLight = new BlendComposite(BlendingMode.PIN_LIGHT); + public static final BlendComposite HardMix = new BlendComposite(BlendingMode.HARD_MIX); + public static final BlendComposite Difference = new BlendComposite(BlendingMode.DIFFERENCE); + public static final BlendComposite Negation = new BlendComposite(BlendingMode.NEGATION); + public static final BlendComposite Exclusion = new BlendComposite(BlendingMode.EXCLUSION); + public static final BlendComposite ColorDodge = new BlendComposite(BlendingMode.COLOR_DODGE); + public static final BlendComposite InverseColorDodge = new BlendComposite(BlendingMode.INVERSE_COLOR_DODGE); + public static final BlendComposite SoftDodge = new BlendComposite(BlendingMode.SOFT_DODGE); + public static final BlendComposite ColorBurn = new BlendComposite(BlendingMode.COLOR_BURN); + public static final BlendComposite InverseColorBurn = new BlendComposite(BlendingMode.INVERSE_COLOR_BURN); + public static final BlendComposite SoftBurn = new BlendComposite(BlendingMode.SOFT_BURN); + public static final BlendComposite Reflect = new BlendComposite(BlendingMode.REFLECT); + public static final BlendComposite Glow = new BlendComposite(BlendingMode.GLOW); + public static final BlendComposite Freeze = new BlendComposite(BlendingMode.FREEZE); + public static final BlendComposite Heat = new BlendComposite(BlendingMode.HEAT); + public static final BlendComposite Add = new BlendComposite(BlendingMode.ADD); + public static final BlendComposite Subtract = new BlendComposite(BlendingMode.SUBTRACT); + public static final BlendComposite Stamp = new BlendComposite(BlendingMode.STAMP); + public static final BlendComposite Red = new BlendComposite(BlendingMode.RED); + public static final BlendComposite Green = new BlendComposite(BlendingMode.GREEN); + public static final BlendComposite Blue = new BlendComposite(BlendingMode.BLUE); + public static final BlendComposite Hue = new BlendComposite(BlendingMode.HUE); + public static final BlendComposite Saturation = new BlendComposite(BlendingMode.SATURATION); + public static final BlendComposite Color = new BlendComposite(BlendingMode.COLOR); + public static final BlendComposite Luminosity = new BlendComposite(BlendingMode.LUMINOSITY); + + private final float alpha; + private final BlendingMode mode; + + private BlendComposite(BlendingMode mode) { + this(mode, 1.0f); + } + + private BlendComposite(BlendingMode mode, float alpha) { + this.mode = mode; + + if (alpha < 0.0f || alpha > 1.0f) { + throw new IllegalArgumentException( + "alpha must be comprised between 0.0f and 1.0f"); + } + this.alpha = alpha; + } + + /** + *

      Creates a new composite based on the blending mode passed + * as a parameter. A default opacity of 1.0 is applied.

      + * + * @param mode the blending mode defining the compositing rule + * @return a new BlendComposite based on the selected blending + * mode, with an opacity of 1.0 + */ + public static BlendComposite getInstance(BlendingMode mode) { + return new BlendComposite(mode); + } + + /** + *

      Creates a new composite based on the blending mode and opacity passed + * as parameters. The opacity must be a value between 0.0 and 1.0.

      + * + * @param mode the blending mode defining the compositing rule + * @param alpha the constant alpha to be multiplied with the alpha of the + * source. alpha must be a floating point between 0.0 and 1.0. + * @throws IllegalArgumentException if the opacity is less than 0.0 or + * greater than 1.0 + * @return a new BlendComposite based on the selected blending + * mode and opacity + */ + public static BlendComposite getInstance(BlendingMode mode, float alpha) { + return new BlendComposite(mode, alpha); + } + + /** + *

      Returns a BlendComposite object that uses the specified + * blending mode and this object's alpha value. If the newly specified + * blending mode is the same as this object's, this object is returned.

      + * + * @param mode the blending mode defining the compositing rule + * @return a BlendComposite object derived from this object, + * that uses the specified blending mode + */ + public BlendComposite derive(BlendingMode mode) { + return this.mode == mode ? this : new BlendComposite(mode, getAlpha()); + } + + /** + *

      Returns a BlendComposite object that uses the specified + * opacity, or alpha, and this object's blending mode. If the newly specified + * opacity is the same as this object's, this object is returned.

      + * + * @param alpha the constant alpha to be multiplied with the alpha of the + * source. alpha must be a floating point between 0.0 and 1.0. + * @throws IllegalArgumentException if the opacity is less than 0.0 or + * greater than 1.0 + * @return a BlendComposite object derived from this object, + * that uses the specified blending mode + */ + public BlendComposite derive(float alpha) { + return this.alpha == alpha ? this : new BlendComposite(getMode(), alpha); + } + + /** + *

      Returns the opacity of this composite. If no opacity has been defined, + * 1.0 is returned.

      + * + * @return the alpha value, or opacity, of this object + */ + public float getAlpha() { + return alpha; + } + + /** + *

      Returns the blending mode of this composite.

      + * + * @return the blending mode used by this object + */ + public BlendingMode getMode() { + return mode; + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return Float.floatToIntBits(alpha) * 31 + mode.ordinal(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof BlendComposite)) { + return false; + } + + BlendComposite bc = (BlendComposite) obj; + return mode == bc.mode && alpha == bc.alpha; + } + + private static boolean isRgbColorModel(ColorModel cm) { + if (cm instanceof DirectColorModel && + cm.getTransferType() == DataBuffer.TYPE_INT) { + DirectColorModel directCM = (DirectColorModel) cm; + + return directCM.getRedMask() == 0x00FF0000 && + directCM.getGreenMask() == 0x0000FF00 && + directCM.getBlueMask() == 0x000000FF && + (directCM.getNumComponents() == 3 || + directCM.getAlphaMask() == 0xFF000000); + } + + return false; + } + + private static boolean isBgrColorModel(ColorModel cm) { + if (cm instanceof DirectColorModel && + cm.getTransferType() == DataBuffer.TYPE_INT) { + DirectColorModel directCM = (DirectColorModel) cm; + + return directCM.getRedMask() == 0x000000FF && + directCM.getGreenMask() == 0x0000FF00 && + directCM.getBlueMask() == 0x00FF0000 && + (directCM.getNumComponents() == 3 || + directCM.getAlphaMask() == 0xFF000000); + } + + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public CompositeContext createContext(ColorModel srcColorModel, + ColorModel dstColorModel, + RenderingHints hints) { + if (isRgbColorModel(srcColorModel) && isRgbColorModel(dstColorModel)) { + return new BlendingRgbContext(this); + } else if (isBgrColorModel(srcColorModel) && isBgrColorModel(dstColorModel)) { + return new BlendingBgrContext(this); + } + + throw new RasterFormatException("Incompatible color models:\n " + srcColorModel + "\n " + dstColorModel); + } + + private static abstract class BlendingContext implements CompositeContext { + protected final BlendComposite composite; + + private BlendingContext(BlendComposite composite) { + this.composite = composite; + } + + @Override + public void dispose() { + } + } + + private static class BlendingRgbContext extends BlendingContext { + private BlendingRgbContext(BlendComposite composite) { + super(composite); + } + + @Override + public void compose(Raster src, Raster dstIn, WritableRaster dstOut) { + int width = Math.min(src.getWidth(), dstIn.getWidth()); + int height = Math.min(src.getHeight(), dstIn.getHeight()); + + float alpha = composite.getAlpha(); + + int[] result = new int[4]; + int[] srcPixel = new int[4]; + int[] dstPixel = new int[4]; + int[] srcPixels = new int[width]; + int[] dstPixels = new int[width]; + + for (int y = 0; y < height; y++) { + src.getDataElements(0, y, width, 1, srcPixels); + dstIn.getDataElements(0, y, width, 1, dstPixels); + for (int x = 0; x < width; x++) { + // pixels are stored as INT_ARGB + // our arrays are [R, G, B, A] + int pixel = srcPixels[x]; + srcPixel[0] = (pixel >> 16) & 0xFF; + srcPixel[1] = (pixel >> 8) & 0xFF; + srcPixel[2] = (pixel ) & 0xFF; + srcPixel[3] = (pixel >> 24) & 0xFF; + + pixel = dstPixels[x]; + dstPixel[0] = (pixel >> 16) & 0xFF; + dstPixel[1] = (pixel >> 8) & 0xFF; + dstPixel[2] = (pixel ) & 0xFF; + dstPixel[3] = (pixel >> 24) & 0xFF; + + composite.getMode().blend(srcPixel, dstPixel, result); + + // mixes the result with the opacity + dstPixels[x] = ((int) (dstPixel[3] + (result[3] - dstPixel[3]) * alpha) & 0xFF) << 24 | + ((int) (dstPixel[0] + (result[0] - dstPixel[0]) * alpha) & 0xFF) << 16 | + ((int) (dstPixel[1] + (result[1] - dstPixel[1]) * alpha) & 0xFF) << 8 | + (int) (dstPixel[2] + (result[2] - dstPixel[2]) * alpha) & 0xFF; + } + dstOut.setDataElements(0, y, width, 1, dstPixels); + } + } + } + + private static class BlendingBgrContext extends BlendingContext { + private BlendingBgrContext(BlendComposite composite) { + super(composite); + } + + @Override + public void compose(Raster src, Raster dstIn, WritableRaster dstOut) { + int width = Math.min(src.getWidth(), dstIn.getWidth()); + int height = Math.min(src.getHeight(), dstIn.getHeight()); + + float alpha = composite.getAlpha(); + + int[] result = new int[4]; + int[] srcPixel = new int[4]; + int[] dstPixel = new int[4]; + int[] srcPixels = new int[width]; + int[] dstPixels = new int[width]; + + for (int y = 0; y < height; y++) { + src.getDataElements(0, y, width, 1, srcPixels); + dstIn.getDataElements(0, y, width, 1, dstPixels); + for (int x = 0; x < width; x++) { + // pixels are stored as INT_ABGR + // our arrays are [R, G, B, A] + int pixel = srcPixels[x]; + srcPixel[0] = (pixel ) & 0xFF; + srcPixel[1] = (pixel >> 8) & 0xFF; + srcPixel[2] = (pixel >> 16) & 0xFF; + srcPixel[3] = (pixel >> 24) & 0xFF; + + pixel = dstPixels[x]; + dstPixel[0] = (pixel ) & 0xFF; + dstPixel[1] = (pixel >> 8) & 0xFF; + dstPixel[2] = (pixel >> 16) & 0xFF; + dstPixel[3] = (pixel >> 24) & 0xFF; + + composite.getMode().blend(srcPixel, dstPixel, result); + + // mixes the result with the opacity + dstPixels[x] = ((int) (dstPixel[3] + (result[3] - dstPixel[3]) * alpha) & 0xFF) << 24 | + ((int) (dstPixel[0] + (result[0] - dstPixel[0]) * alpha) & 0xFF) | + ((int) (dstPixel[1] + (result[1] - dstPixel[1]) * alpha) & 0xFF) << 8 | + ((int) (dstPixel[2] + (result[2] - dstPixel[2]) * alpha) & 0xFF) << 16; + } + dstOut.setDataElements(0, y, width, 1, dstPixels); + } + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/graphics/ColorUtilities.java b/src/main/java/org/jdesktop/swingx/graphics/ColorUtilities.java new file mode 100644 index 0000000000..c23ed180ee --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/graphics/ColorUtilities.java @@ -0,0 +1,263 @@ +/* + * $Id: ColorUtilities.java 1496 2006-10-22 03:26:24Z gfx $ + * + * Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy). + * + * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * Copyright (c) 2006 Romain Guy + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jdesktop.swingx.graphics; + +import java.awt.*; + +/** + *

      ColorUtilities contains a set of tools to perform + * common color operations easily.

      + * + * @author Romain Guy + */ +public class ColorUtilities { + private ColorUtilities() { + } + + /** + *

      Returns the HSL (Hue/Saturation/Luminance) equivalent of a given + * RGB color. All three HSL components are between 0.0 and 1.0.

      + * + * @param color the RGB color to convert + * @return a new array of 3 floats corresponding to the HSL components + */ + public static float[] RGBtoHSL(Color color) { + return RGBtoHSL(color.getRed(), color.getGreen(), color.getBlue(), null); + } + + /** + *

      Returns the HSL (Hue/Saturation/Luminance) equivalent of a given + * RGB color. All three HSL components are between 0.0 and 1.0.

      + * + * @param color the RGB color to convert + * @param hsl a pre-allocated array of floats; can be null + * @return hsl if non-null, a new array of 3 floats otherwise + * @throws IllegalArgumentException if hsl has a length lower + * than 3 + */ + public static float[] RGBtoHSL(Color color, float[] hsl) { + return RGBtoHSL(color.getRed(), color.getGreen(), color.getBlue(), hsl); + } + + /** + *

      Returns the HSL (Hue/Saturation/Luminance) equivalent of a given + * RGB color. All three HSL components are between 0.0 and 1.0.

      + * + * @param r the red component, between 0 and 255 + * @param g the green component, between 0 and 255 + * @param b the blue component, between 0 and 255 + * @return a new array of 3 floats corresponding to the HSL components + */ + public static float[] RGBtoHSL(int r, int g, int b) { + return RGBtoHSL(r, g, b, null); + } + + /** + *

      Returns the HSL (Hue/Saturation/Luminance) equivalent of a given + * RGB color. All three HSL components are floats between 0.0 and 1.0.

      + * + * @param r the red component, between 0 and 255 + * @param g the green component, between 0 and 255 + * @param b the blue component, between 0 and 255 + * @param hsl a pre-allocated array of floats; can be null + * @return hsl if non-null, a new array of 3 floats otherwise + * @throws IllegalArgumentException if hsl has a length lower + * than 3 + */ + public static float[] RGBtoHSL(int r, int g, int b, float[] hsl) { + if (hsl == null) { + hsl = new float[3]; + } else if (hsl.length < 3) { + throw new IllegalArgumentException("hsl array must have a length of" + + " at least 3"); + } + + if (r < 0) r = 0; + else if (r > 255) r = 255; + if (g < 0) g = 0; + else if (g > 255) g = 255; + if (b < 0) b = 0; + else if (b > 255) b = 255; + + float var_R = (r / 255f); + float var_G = (g / 255f); + float var_B = (b / 255f); + + float var_Min; + float var_Max; + float del_Max; + + if (var_R > var_G) { + var_Min = var_G; + var_Max = var_R; + } else { + var_Min = var_R; + var_Max = var_G; + } + if (var_B > var_Max) { + var_Max = var_B; + } + if (var_B < var_Min) { + var_Min = var_B; + } + + del_Max = var_Max - var_Min; + + float H, S, L; + L = (var_Max + var_Min) / 2f; + + if (del_Max - 0.01f <= 0.0f) { + H = 0; + S = 0; + } else { + if (L < 0.5f) { + S = del_Max / (var_Max + var_Min); + } else { + S = del_Max / (2 - var_Max - var_Min); + } + + float del_R = (((var_Max - var_R) / 6f) + (del_Max / 2f)) / del_Max; + float del_G = (((var_Max - var_G) / 6f) + (del_Max / 2f)) / del_Max; + float del_B = (((var_Max - var_B) / 6f) + (del_Max / 2f)) / del_Max; + + if (var_R == var_Max) { + H = del_B - del_G; + } else if (var_G == var_Max) { + H = (1 / 3f) + del_R - del_B; + } else { + H = (2 / 3f) + del_G - del_R; + } + if (H < 0) { + H += 1; + } + if (H > 1) { + H -= 1; + } + } + + hsl[0] = H; + hsl[1] = S; + hsl[2] = L; + + return hsl; + } + + /** + *

      Returns the RGB equivalent of a given HSL (Hue/Saturation/Luminance) + * color.

      + * + * @param h the hue component, between 0.0 and 1.0 + * @param s the saturation component, between 0.0 and 1.0 + * @param l the luminance component, between 0.0 and 1.0 + * @return a new Color object equivalent to the HSL components + */ + public static Color HSLtoRGB(float h, float s, float l) { + int[] rgb = HSLtoRGB(h, s, l, null); + return new Color(rgb[0], rgb[1], rgb[2]); + } + + /** + *

      Returns the RGB equivalent of a given HSL (Hue/Saturation/Luminance) + * color. All three RGB components are integers between 0 and 255.

      + * + * @param h the hue component, between 0.0 and 1.0 + * @param s the saturation component, between 0.0 and 1.0 + * @param l the luminance component, between 0.0 and 1.0 + * @param rgb a pre-allocated array of ints; can be null + * @return rgb if non-null, a new array of 3 ints otherwise + * @throws IllegalArgumentException if rgb has a length lower + * than 3 + */ + public static int[] HSLtoRGB(float h, float s, float l, int[] rgb) { + if (rgb == null) { + rgb = new int[3]; + } else if (rgb.length < 3) { + throw new IllegalArgumentException("rgb array must have a length of" + + " at least 3"); + } + + if (h < 0) h = 0.0f; + else if (h > 1.0f) h = 1.0f; + if (s < 0) s = 0.0f; + else if (s > 1.0f) s = 1.0f; + if (l < 0) l = 0.0f; + else if (l > 1.0f) l = 1.0f; + + int R, G, B; + + if (s - 0.01f <= 0.0f) { + R = (int) (l * 255.0f); + G = (int) (l * 255.0f); + B = (int) (l * 255.0f); + } else { + float var_1, var_2; + if (l < 0.5f) { + var_2 = l * (1 + s); + } else { + var_2 = (l + s) - (s * l); + } + var_1 = 2 * l - var_2; + + R = (int) (255.0f * hue2RGB(var_1, var_2, h + (1.0f / 3.0f))); + G = (int) (255.0f * hue2RGB(var_1, var_2, h)); + B = (int) (255.0f * hue2RGB(var_1, var_2, h - (1.0f / 3.0f))); + } + + rgb[0] = R; + rgb[1] = G; + rgb[2] = B; + + return rgb; + } + + private static float hue2RGB(float v1, float v2, float vH) { + if (vH < 0.0f) { + vH += 1.0f; + } + if (vH > 1.0f) { + vH -= 1.0f; + } + if ((6.0f * vH) < 1.0f) { + return (v1 + (v2 - v1) * 6.0f * vH); + } + if ((2.0f * vH) < 1.0f) { + return (v2); + } + if ((3.0f * vH) < 2.0f) { + return (v1 + (v2 - v1) * ((2.0f / 3.0f) - vH) * 6.0f); + } + return (v1); + } +} diff --git a/src/main/java/org/jdesktop/swingx/graphics/FilterComposite.java b/src/main/java/org/jdesktop/swingx/graphics/FilterComposite.java new file mode 100644 index 0000000000..aab26f0c8d --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/graphics/FilterComposite.java @@ -0,0 +1,119 @@ +package org.jdesktop.swingx.graphics; + +import org.jdesktop.swingx.util.Contract; + +import java.awt.*; +import java.awt.image.*; + +/** + * A {@code FilterComposite} allows the inclusion of arbitrary image filters during the paint + * processing of {@link java.awt.Graphics2D} events. By adding a filter composite, the src and + * destination images are render using a delegated {@code Composite}, then post-processed with the + * filters before returning the result back to the graphics context. This process adds overhead to + * the painting both is terms of time (the actual processing time) and memory (as a temporary raster + * must be created to store the intermediate state). Since it is possible to delegate to a filter + * composite from a filter composite, this may result slow or unresponsive painting. If you are + * attempting to render with many different filters, it may be better to have one filter composite + * with many filters (using a compound filter). + *

      + * It was decided to use {@link BufferedImageOp} as the filter because many of these filters already + * exist. This gives high reusability to code. + * + * @author Karl Schaefer + * @see org.jdesktop.swingx.image.AbstractFilter + */ +@SuppressWarnings("nls") +public class FilterComposite implements Composite { + private static class FilterContext implements CompositeContext { + private ColorModel dstModel; + private CompositeContext ctx; + private BufferedImageOp filter; + + public FilterContext(ColorModel dstModel, CompositeContext ctx, BufferedImageOp filter) { + Contract.asNotNull(dstModel, "dstModel cannot be null"); + Contract.asNotNull(ctx, "context cannot be null"); + this.dstModel = dstModel; + this.ctx = ctx; + this.filter = filter; + } + + @Override + public void compose(Raster src, Raster dstIn, WritableRaster dstOut) { + if (filter == null) { + ctx.compose(src, dstIn, dstOut); + } else { + WritableRaster tempOut = dstModel.createCompatibleWritableRaster(dstOut.getWidth(), dstOut.getHeight()); + ctx.compose(src, dstIn, tempOut); + + filter.filter(new BufferedImage(dstModel, tempOut, dstModel.isAlphaPremultiplied(), null), + new BufferedImage(dstModel, dstOut, dstModel.isAlphaPremultiplied(), null)); + } + } + + @Override + public void dispose() { + ctx = null; + } + } + + private final Composite composite; + private BufferedImageOp filter; + + /** + * Creates an empty filter composite for the specified composite. + * + * @param composite + * the composite operation to perform prior to filtering + * @throws NullPointerException + * if {@code composite} is {@code null} + */ + public FilterComposite(Composite composite) { + this(composite, null); + } + + /** + * Creates a filter for the specified composite. + * + * @param composite + * the composite operation to perform prior to filtering + * @param filter + * the filter to apply to the composite result + * @throws NullPointerException + * if {@code composite} is {@code null} + */ + public FilterComposite(Composite composite, BufferedImageOp filter) { + Contract.asNotNull(composite, "composite cannot be null"); + this.composite = composite; + this.filter = filter; + } + + /** + * The filter to apply to the graphics context. + * + * @return the current filter + */ + public BufferedImageOp getFilter() { + return filter; + } + + /** + * Sets the filter for manipulating the graphics composites. + *

      + * A {@code null} filter will result in no filtering. + * + * @param filter + * the new filter + */ + public void setFilter(BufferedImageOp filter) { + this.filter = filter; + } + + /** + * {@inheritDoc} + */ + @Override + public CompositeContext createContext(ColorModel srcColorModel, ColorModel dstColorModel, + RenderingHints hints) { + return new FilterContext(dstColorModel, composite.createContext(srcColorModel, dstColorModel, hints), filter); + } +} diff --git a/src/main/java/org/jdesktop/swingx/graphics/Graphics2DFacade.java b/src/main/java/org/jdesktop/swingx/graphics/Graphics2DFacade.java new file mode 100644 index 0000000000..ed4dfa4fd7 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/graphics/Graphics2DFacade.java @@ -0,0 +1,452 @@ +package org.jdesktop.swingx.graphics; + +import org.jdesktop.swingx.util.Contract; + +import java.awt.*; +import java.awt.RenderingHints.Key; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.awt.image.BufferedImageOp; +import java.awt.image.ImageObserver; +import java.awt.image.RenderedImage; +import java.awt.image.renderable.RenderableImage; +import java.text.AttributedCharacterIterator; +import java.util.Map; + +/** + * A simple facade that forwards all graphics calls to a delegate. + * + * @author kschaefer + */ +public abstract class Graphics2DFacade extends Graphics2D { + private Graphics2D delegate; + + public Graphics2DFacade(Graphics2D delegate) { + this.delegate = Contract.asNotNull(delegate, "delegate cannot be null"); + } + + @Override + public Graphics create() { + return delegate.create(); + } + + @Override + public Graphics create(int x, int y, int width, int height) { + return delegate.create(x, y, width, height); + } + + @Override + public Color getColor() { + return delegate.getColor(); + } + + @Override + public void setColor(Color c) { + delegate.setColor(c); + } + + @Override + public void setPaintMode() { + delegate.setPaintMode(); + } + + @Override + public void setXORMode(Color c1) { + delegate.setXORMode(c1); + } + + @Override + public Font getFont() { + return delegate.getFont(); + } + + @Override + public void setFont(Font font) { + delegate.setFont(font); + } + + @Override + public FontMetrics getFontMetrics() { + return delegate.getFontMetrics(); + } + + @Override + public FontMetrics getFontMetrics(Font f) { + return delegate.getFontMetrics(f); + } + + @Override + public Rectangle getClipBounds() { + return delegate.getClipBounds(); + } + + @Override + public void clipRect(int x, int y, int width, int height) { + delegate.clipRect(x, y, width, height); + } + + @Override + public void setClip(int x, int y, int width, int height) { + delegate.setClip(x, y, width, height); + } + + @Override + public Shape getClip() { + return delegate.getClip(); + } + + @Override + public void setClip(Shape clip) { + delegate.setClip(clip); + } + + @Override + public void copyArea(int x, int y, int width, int height, int dx, int dy) { + delegate.copyArea(x, y, width, height, dx, dy); + } + + @Override + public void drawLine(int x1, int y1, int x2, int y2) { + delegate.drawLine(x1, y1, x2, y2); + } + + @Override + public void fillRect(int x, int y, int width, int height) { + delegate.fillRect(x, y, width, height); + } + + @Override + public void drawRect(int x, int y, int width, int height) { + delegate.drawRect(x, y, width, height); + } + + @Override + public void clearRect(int x, int y, int width, int height) { + delegate.clearRect(x, y, width, height); + } + + @Override + public void draw3DRect(int x, int y, int width, int height, boolean raised) { + delegate.draw3DRect(x, y, width, height, raised); + } + + @Override + public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { + delegate.drawRoundRect(x, y, width, height, arcWidth, arcHeight); + } + + @Override + public void fill3DRect(int x, int y, int width, int height, boolean raised) { + delegate.fill3DRect(x, y, width, height, raised); + } + + @Override + public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { + delegate.fillRoundRect(x, y, width, height, arcWidth, arcHeight); + } + + @Override + public void draw(Shape s) { + delegate.draw(s); + } + + @Override + public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) { + return delegate.drawImage(img, xform, obs); + } + + @Override + public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) { + delegate.drawImage(img, op, x, y); + } + + @Override + public void drawOval(int x, int y, int width, int height) { + delegate.drawOval(x, y, width, height); + } + + @Override + public void drawRenderedImage(RenderedImage img, AffineTransform xform) { + delegate.drawRenderedImage(img, xform); + } + + @Override + public void fillOval(int x, int y, int width, int height) { + delegate.fillOval(x, y, width, height); + } + + @Override + public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) { + delegate.drawArc(x, y, width, height, startAngle, arcAngle); + } + + @Override + public void drawRenderableImage(RenderableImage img, AffineTransform xform) { + delegate.drawRenderableImage(img, xform); + } + + @Override + public void drawString(String str, int x, int y) { + delegate.drawString(str, x, y); + } + + @Override + public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) { + delegate.fillArc(x, y, width, height, startAngle, arcAngle); + } + + @Override + public void drawString(String str, float x, float y) { + delegate.drawString(str, x, y); + } + + @Override + public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) { + delegate.drawPolyline(xPoints, yPoints, nPoints); + } + + @Override + public void drawString(AttributedCharacterIterator iterator, int x, int y) { + delegate.drawString(iterator, x, y); + } + + @Override + public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) { + delegate.drawPolygon(xPoints, yPoints, nPoints); + } + + @Override + public void drawString(AttributedCharacterIterator iterator, float x, float y) { + delegate.drawString(iterator, x, y); + } + + @Override + public void drawPolygon(Polygon p) { + delegate.drawPolygon(p); + } + + @Override + public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) { + delegate.fillPolygon(xPoints, yPoints, nPoints); + } + + @Override + public void drawGlyphVector(GlyphVector g, float x, float y) { + delegate.drawGlyphVector(g, x, y); + } + + @Override + public void fillPolygon(Polygon p) { + delegate.fillPolygon(p); + } + + @Override + public void fill(Shape s) { + delegate.fill(s); + } + + @Override + public boolean hit(Rectangle rect, Shape s, boolean onStroke) { + return delegate.hit(rect, s, onStroke); + } + + @Override + public void drawChars(char[] data, int offset, int length, int x, int y) { + delegate.drawChars(data, offset, length, x, y); + } + + @Override + public GraphicsConfiguration getDeviceConfiguration() { + return delegate.getDeviceConfiguration(); + } + + @Override + public void setComposite(Composite comp) { + delegate.setComposite(comp); + } + + @Override + public void drawBytes(byte[] data, int offset, int length, int x, int y) { + delegate.drawBytes(data, offset, length, x, y); + } + + @Override + public void setPaint(Paint paint) { + delegate.setPaint(paint); + } + + @Override + public boolean drawImage(Image img, int x, int y, ImageObserver observer) { + return delegate.drawImage(img, x, y, observer); + } + + @Override + public void setStroke(Stroke s) { + delegate.setStroke(s); + } + + @Override + public void setRenderingHint(Key hintKey, Object hintValue) { + delegate.setRenderingHint(hintKey, hintValue); + } + + @Override + public Object getRenderingHint(Key hintKey) { + return delegate.getRenderingHint(hintKey); + } + + @Override + public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) { + return delegate.drawImage(img, x, y, width, height, observer); + } + + @Override + public void setRenderingHints(Map hints) { + delegate.setRenderingHints(hints); + } + + @Override + public void addRenderingHints(Map hints) { + delegate.addRenderingHints(hints); + } + + @Override + public RenderingHints getRenderingHints() { + return delegate.getRenderingHints(); + } + + @Override + public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer) { + return delegate.drawImage(img, x, y, bgcolor, observer); + } + + @Override + public void translate(int x, int y) { + delegate.translate(x, y); + } + + @Override + public void translate(double tx, double ty) { + delegate.translate(tx, ty); + } + + @Override + public void rotate(double theta) { + delegate.rotate(theta); + } + + @Override + public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, + ImageObserver observer) { + return delegate.drawImage(img, x, y, width, height, bgcolor, observer); + } + + @Override + public void rotate(double theta, double x, double y) { + delegate.rotate(theta, x, y); + } + + @Override + public void scale(double sx, double sy) { + delegate.scale(sx, sy); + } + + @Override + public void shear(double shx, double shy) { + delegate.shear(shx, shy); + } + + @Override + public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, + int sx2, int sy2, ImageObserver observer) { + return delegate.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer); + } + + @Override + public void transform(AffineTransform Tx) { + delegate.transform(Tx); + } + + @Override + public void setTransform(AffineTransform Tx) { + delegate.setTransform(Tx); + } + + @Override + public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, + int sx2, int sy2, Color bgcolor, ImageObserver observer) { + return delegate.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, bgcolor, observer); + } + + @Override + public AffineTransform getTransform() { + return delegate.getTransform(); + } + + @Override + public Paint getPaint() { + return delegate.getPaint(); + } + + @Override + public Composite getComposite() { + return delegate.getComposite(); + } + + @Override + public void setBackground(Color color) { + delegate.setBackground(color); + } + + @Override + public Color getBackground() { + return delegate.getBackground(); + } + + @Override + public Stroke getStroke() { + return delegate.getStroke(); + } + + @Override + public void clip(Shape s) { + delegate.clip(s); + } + + @Override + public void dispose() { + delegate.dispose(); + } + + @Override + public FontRenderContext getFontRenderContext() { + return delegate.getFontRenderContext(); + } + + @Override + public void finalize() { + delegate.finalize(); + } + + @Override + public String toString() { + return delegate.toString(); + } + + @Override + public Rectangle getClipRect() { + return delegate.getClipRect(); + } + + @Override + public boolean hitClip(int x, int y, int width, int height) { + return delegate.hitClip(x, y, width, height); + } + + @Override + public Rectangle getClipBounds(Rectangle r) { + return delegate.getClipBounds(r); + } +} diff --git a/src/main/java/org/jdesktop/swingx/graphics/ReflectionRenderer.java b/src/main/java/org/jdesktop/swingx/graphics/ReflectionRenderer.java new file mode 100644 index 0000000000..abefac6bdb --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/graphics/ReflectionRenderer.java @@ -0,0 +1,512 @@ +/* + * $Id: ReflectionRenderer.java 4082 2011-11-15 18:39:43Z kschaefe $ + * + * Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy). + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * Copyright (c) 2006 Romain Guy + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.jdesktop.swingx.graphics; + +import org.jdesktop.swingx.image.StackBlurFilter; +import org.jdesktop.swingx.util.GraphicsUtilities; + +import java.awt.*; +import java.awt.image.BufferedImage; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; + +/** + *

      A reflection renderer generates the reflection of a given picture. The + * result can be either the reflection itself, or an image containing both the + * source image and its reflection.

      + *

      Reflection Properties

      + *

      A reflection is defined by three properties: + *

        + *
      • opacity: the opacity of the reflection. You will usually + * change this valued according to the background color.
      • + *
      • length: the length of the reflection. The length is a fraction + * of the height of the source image.
      • + *
      • blur enabled: perfect reflections are hardly natural. You can + * blur the reflection to make it look a bit more natural.
      • + *
      + * You can set these properties using the provided mutators or the appropriate + * constructor. Here are two ways of creating a blurred reflection, with an + * opacity of 50% and a length of 30% the height of the original image: + *
      + * ReflectionRenderer renderer = new ReflectionRenderer(0.5f, 0.3f, true);
      + * // ..
      + * renderer = new ReflectionRenderer();
      + * renderer.setOpacity(0.5f);
      + * renderer.setLength(0.3f);
      + * renderer.setBlurEnabled(true);
      + * 
      + * The default constructor provides the following default values: + *
        + *
      • opacity: 35%
      • + *
      • length: 40%
      • + *
      • blur enabled: false
      • + *

      + *

      Generating Reflections

      + *

      A reflection is generated as a BufferedImage from another + * BufferedImage. Once the renderer is set up, you must call + * {@link #createReflection(BufferedImage)} to actually generate + * the reflection: + *

      + * ReflectionRenderer renderer = new ReflectionRenderer();
      + * // renderer setup
      + * BufferedImage reflection = renderer.createReflection(bufferedImage);
      + * 

      + *

      The returned image contains only the reflection. You will have to append + * it to the source image at painting time to get a realistic results. You can + * also asks the rendered to return a picture composed of both the source image + * and its reflection: + *

      + * ReflectionRenderer renderer = new ReflectionRenderer();
      + * // renderer setup
      + * BufferedImage reflection = renderer.appendReflection(bufferedImage);
      + * 

      + *

      Properties Changes

      + *

      This renderer allows to register property change listeners with + * {@link #addPropertyChangeListener}. Listening to properties changes is very + * useful when you embed the renderer in a graphical component and give the API + * user the ability to access the renderer. By listening to properties changes, + * you can easily repaint the component when needed.

      + *

      Threading Issues

      + *

      ReflectionRenderer is not guaranteed to be thread-safe.

      + * + * @author Romain Guy + */ +public class ReflectionRenderer { + /** + *

      Identifies a change to the opacity used to render the reflection.

      + */ + public static final String OPACITY_CHANGED_PROPERTY = "reflection_opacity"; + + /** + *

      Identifies a change to the length of the rendered reflection.

      + */ + public static final String LENGTH_CHANGED_PROPERTY = "reflection_length"; + + /** + *

      Identifies a change to the blurring of the rendered reflection.

      + */ + public static final String BLUR_ENABLED_CHANGED_PROPERTY = "reflection_blur"; + + // opacity of the reflection + private float opacity; + + // length of the reflection + private float length; + + // should the reflection be blurred? + private boolean blurEnabled; + + // notifies listeners of properties changes + private PropertyChangeSupport changeSupport; + private StackBlurFilter stackBlurFilter; + + /** + *

      Creates a default good looking reflections generator. + * The default reflection renderer provides the following default values: + *

        + *
      • opacity: 35%
      • + *
      • length: 40%
      • + *
      • blurring: disabled with a radius of 1 pixel
      • + *

      + *

      These properties provide a regular, good looking reflection.

      + * + * @see #getOpacity() + * @see #setOpacity(float) + * @see #getLength() + * @see #setLength(float) + * @see #isBlurEnabled() + * @see #setBlurEnabled(boolean) + * @see #getBlurRadius() + * @see #setBlurRadius(int) + */ + public ReflectionRenderer() { + this(0.35f, 0.4f, false); + } + + /** + *

      Creates a default good looking reflections generator with the + * specified opacity. The default reflection renderer provides the following + * default values: + *

        + *
      • length: 40%
      • + *
      • blurring: disabled with a radius of 1 pixel
      • + *

      + * + * @param opacity the opacity of the reflection, between 0.0 and 1.0 + * @see #getOpacity() + * @see #setOpacity(float) + * @see #getLength() + * @see #setLength(float) + * @see #isBlurEnabled() + * @see #setBlurEnabled(boolean) + * @see #getBlurRadius() + * @see #setBlurRadius(int) + */ + public ReflectionRenderer(float opacity) { + this(opacity, 0.4f, false); + } + + /** + *

      Creates a reflections generator with the specified properties. Both + * opacity and length are numbers between 0.0 (0%) and 1.0 (100%). If the + * provided numbers are outside this range, they are clamped.

      + *

      Enabling the blur generates a different kind of reflections that might + * look more natural. The default blur radius is 1 pixel

      + * + * @param opacity the opacity of the reflection + * @param length the length of the reflection + * @param blurEnabled if true, the reflection is blurred + * @see #getOpacity() + * @see #setOpacity(float) + * @see #getLength() + * @see #setLength(float) + * @see #isBlurEnabled() + * @see #setBlurEnabled(boolean) + * @see #getBlurRadius() + * @see #setBlurRadius(int) + */ + public ReflectionRenderer(float opacity, float length, boolean blurEnabled) { + //noinspection ThisEscapedInObjectConstruction + this.changeSupport = new PropertyChangeSupport(this); + this.stackBlurFilter = new StackBlurFilter(1); + + setOpacity(opacity); + setLength(length); + setBlurEnabled(blurEnabled); + } + + /** + *

      Add a PropertyChangeListener to the listener list. The listener is + * registered for all properties. The same listener object may be added + * more than once, and will be called as many times as it is added. If + * listener is null, no exception is thrown and no action + * is taken.

      + * + * @param listener the PropertyChangeListener to be added + */ + public void addPropertyChangeListener(PropertyChangeListener listener) { + changeSupport.addPropertyChangeListener(listener); + } + + /** + *

      Remove a PropertyChangeListener from the listener list. This removes + * a PropertyChangeListener that was registered for all properties. If + * listener was added more than once to the same event source, + * it will be notified one less time after being removed. If + * listener is null, or was never added, no exception is thrown + * and no action is taken.

      + * + * @param listener the PropertyChangeListener to be removed + */ + public void removePropertyChangeListener(PropertyChangeListener listener) { + changeSupport.removePropertyChangeListener(listener); + } + + /** + *

      Gets the opacity used by the factory to generate reflections.

      + *

      The opacity is comprised between 0.0f and 1.0f; 0.0f being fully + * transparent and 1.0f fully opaque.

      + * + * @return this factory's shadow opacity + * @see #getOpacity() + * @see #createReflection(BufferedImage) + * @see #appendReflection(BufferedImage) + */ + public float getOpacity() { + return opacity; + } + + /** + *

      Sets the opacity used by the factory to generate reflections.

      + *

      Consecutive calls to {@link #createReflection} will all use this + * opacity until it is set again.

      + *

      The opacity is comprised between 0.0f and 1.0f; 0.0f being fully + * transparent and 1.0f fully opaque. If you provide a value out of these + * boundaries, it will be restrained to the closest boundary.

      + * + * @param opacity the generated reflection opacity + * @see #setOpacity(float) + * @see #createReflection(BufferedImage) + * @see #appendReflection(BufferedImage) + */ + public void setOpacity(float opacity) { + float oldOpacity = this.opacity; + + if (opacity < 0.0f) { + opacity = 0.0f; + } else if (opacity > 1.0f) { + opacity = 1.0f; + } + + if (oldOpacity != opacity) { + this.opacity = opacity; + changeSupport.firePropertyChange(OPACITY_CHANGED_PROPERTY, + oldOpacity, + this.opacity); + } + } + + /** + *

      Returns the length of the reflection. The result is a number between + * 0.0 and 1.0. This number is the fraction of the height of the source + * image that is used to compute the size of the reflection.

      + * + * @return the length of the reflection, as a fraction of the source image + * height + * @see #setLength(float) + * @see #createReflection(BufferedImage) + * @see #appendReflection(BufferedImage) + */ + public float getLength() { + return length; + } + + /** + *

      Sets the length of the reflection, as a fraction of the height of the + * source image.

      + *

      Consecutive calls to {@link #createReflection} will all use this + * opacity until it is set again.

      + *

      The opacity is comprised between 0.0f and 1.0f; 0.0f being fully + * transparent and 1.0f fully opaque. If you provide a value out of these + * boundaries, it will be restrained to the closest boundary.

      + * + * @param length the length of the reflection, as a fraction of the source + * image height + * @see #getLength() + * @see #createReflection(BufferedImage) + * @see #appendReflection(BufferedImage) + */ + public void setLength(float length) { + float oldLength = this.length; + + if (length < 0.0f) { + length = 0.0f; + } else if (length > 1.0f) { + length = 1.0f; + } + + if (oldLength != length) { + this.length = length; + changeSupport.firePropertyChange(LENGTH_CHANGED_PROPERTY, + oldLength, + this.length); + } + } + + /** + *

      Returns true if the blurring of the reflection is enabled, false + * otherwise. When blurring is enabled, the reflection is blurred to look + * more natural.

      + * + * @return true if blur is enabled, false otherwise + * @see #setBlurEnabled(boolean) + * @see #createReflection(BufferedImage) + * @see #appendReflection(BufferedImage) + */ + public boolean isBlurEnabled() { + return blurEnabled; + } + + /** + *

      Setting the blur to true will enable the blurring of the reflection + * when {@link #createReflection} is invoked.

      + *

      Enabling the blurring of the reflection can yield to more natural + * results which may or may not be better looking, depending on the source + * picture.

      + *

      Consecutive calls to {@link #createReflection} will all use this + * opacity until it is set again.

      + * + * @param blurEnabled true to enable the blur, false otherwise + * @see #isBlurEnabled() + * @see #createReflection(BufferedImage) + * @see #appendReflection(BufferedImage) + */ + public void setBlurEnabled(boolean blurEnabled) { + if (blurEnabled != this.blurEnabled) { + boolean oldBlur = this.blurEnabled; + this.blurEnabled= blurEnabled; + + changeSupport.firePropertyChange(BLUR_ENABLED_CHANGED_PROPERTY, + oldBlur, + this.blurEnabled); + } + } + + /** + *

      Returns the effective radius, in pixels, of the blur used by this + * renderer when {@link #isBlurEnabled()} is true.

      + * + * @return the effective radius of the blur used when + * isBlurEnabled is true + * @see #isBlurEnabled() + * @see #setBlurEnabled(boolean) + * @see #setBlurRadius(int) + * @see #getBlurRadius() + */ + public int getEffectiveBlurRadius() { + return stackBlurFilter.getEffectiveRadius(); + } + + /** + *

      Returns the radius, in pixels, of the blur used by this renderer when + * {@link #isBlurEnabled()} is true.

      + * + * @return the radius of the blur used when isBlurEnabled + * is true + * @see #isBlurEnabled() + * @see #setBlurEnabled(boolean) + * @see #setBlurRadius(int) + * @see #getEffectiveBlurRadius() + */ + public int getBlurRadius() { + return stackBlurFilter.getRadius(); + } + + /** + *

      Sets the radius, in pixels, of the blur used by this renderer when + * {@link #isBlurEnabled()} is true. This radius changes the size of the + * generated image when blurring is applied.

      + * + * @param radius the radius, in pixels, of the blur + * @see #isBlurEnabled() + * @see #setBlurEnabled(boolean) + * @see #getBlurRadius() + */ + public void setBlurRadius(int radius) { + this.stackBlurFilter = new StackBlurFilter(radius); + } + + /** + *

      Returns the source image and its reflection. The appearance of the + * reflection is defined by the opacity, the length and the blur + * properties.

      + * *

      The width of the generated image will be augmented when + * {@link #isBlurEnabled()} is true. The generated image will have the width + * of the source image plus twice the effective blur radius (see + * {@link #getEffectiveBlurRadius()}). The default blur radius is 1 so the + * width will be augmented by 6. You might need to take this into account + * at drawing time.

      + *

      The returned image height depends on the value returned by + * {@link #getLength()} and {@link #getEffectiveBlurRadius()}. For instance, + * if the length is 0.5 (or 50%) and the source image is 480 pixels high, + * then the reflection will be 246 (480 * 0.5 + 3 * 2) pixels high.

      + *

      You can create only the reflection by calling + * {@link #createReflection(BufferedImage)}.

      + * + * @param image the source image + * @return the source image with its reflection below + * @see #createReflection(BufferedImage) + */ + public BufferedImage appendReflection(BufferedImage image) { + BufferedImage reflection = createReflection(image); + BufferedImage buffer = GraphicsUtilities.createCompatibleTranslucentImage( + reflection.getWidth(), image.getHeight() + reflection.getHeight()); + Graphics2D g2 = buffer.createGraphics(); + + try { + int effectiveRadius = isBlurEnabled() ? stackBlurFilter + .getEffectiveRadius() : 0; + g2.drawImage(image, effectiveRadius, 0, null); + g2.drawImage(reflection, 0, image.getHeight() - effectiveRadius, + null); + } finally { + g2.dispose(); + } + + reflection.flush(); + + return buffer; + } + + /** + *

      Returns the reflection of the source image. The appearance of the + * reflection is defined by the opacity, the length and the blur + * properties.

      + * *

      The width of the generated image will be augmented when + * {@link #isBlurEnabled()} is true. The generated image will have the width + * of the source image plus twice the effective blur radius (see + * {@link #getEffectiveBlurRadius()}). The default blur radius is 1 so the + * width will be augmented by 6. You might need to take this into account + * at drawing time.

      + *

      The returned image height depends on the value returned by + * {@link #getLength()} and {@link #getEffectiveBlurRadius()}. For instance, + * if the length is 0.5 (or 50%) and the source image is 480 pixels high, + * then the reflection will be 246 (480 * 0.5 + 3 * 2) pixels high.

      + *

      The returned image contains only + * the reflection. You will have to append it to the source image to produce + * the illusion of a reflective environment. The method + * {@link #appendReflection(BufferedImage)} provides an easy + * way to create an image containing both the source and the reflection.

      + * + * @param image the source image + * @return the reflection of the source image + * @see #appendReflection(BufferedImage) + */ + public BufferedImage createReflection(BufferedImage image) { + if (length == 0.0f) { + return GraphicsUtilities.createCompatibleTranslucentImage(1, 1); + } + + int blurOffset = isBlurEnabled() ? + stackBlurFilter.getEffectiveRadius() : 0; + int height = (int) (image.getHeight() * length); + + BufferedImage buffer = + GraphicsUtilities.createCompatibleTranslucentImage( + image.getWidth() + blurOffset * 2, + height + blurOffset * 2); + Graphics2D g2 = buffer.createGraphics(); + + try { + g2.translate(0, image.getHeight()); + g2.scale(1.0, -1.0); + + g2.drawImage(image, blurOffset, -blurOffset, null); + + g2.scale(1.0, -1.0); + g2.translate(0, -image.getHeight()); + + g2.setComposite(AlphaComposite.DstIn); + g2.setPaint(new GradientPaint(0.0f, 0.0f, new Color(0.0f, 0.0f, + 0.0f, getOpacity()), 0.0f, buffer.getHeight(), new Color( + 0.0f, 0.0f, 0.0f, 0.0f), true)); + g2.fillRect(0, 0, buffer.getWidth(), buffer.getHeight()); + } finally { + g2.dispose(); + } + + return isBlurEnabled() ? stackBlurFilter.filter(buffer, null) : + buffer; + } +} diff --git a/src/main/java/org/jdesktop/swingx/graphics/ShadowRenderer.java b/src/main/java/org/jdesktop/swingx/graphics/ShadowRenderer.java new file mode 100644 index 0000000000..1cfaa2a760 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/graphics/ShadowRenderer.java @@ -0,0 +1,429 @@ +/* + * $Id: ShadowRenderer.java 4082 2011-11-15 18:39:43Z kschaefe $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.graphics; + +import org.jdesktop.swingx.util.GraphicsUtilities; + +import java.awt.*; +import java.awt.image.BufferedImage; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; + +import static org.jdesktop.swingx.util.GraphicsUtilities.createCompatibleTranslucentImage; + +/** + *

      A shadow renderer generates a drop shadow for any given picture, respecting + * the transparency channel if present. The resulting picture contains the + * shadow only and to create a drop shadow effect you will need to stack the + * original picture and the shadow generated by the renderer.

      + *

      Shadow Properties

      + *

      A shadow is defined by three properties: + *

        + *
      • size: The size, in pixels, of the shadow. This property also + * defines the fuzziness.
      • + *
      • opacity: The opacity, between 0.0 and 1.0, of the shadow.
      • + *
      • color: The color of the shadow. Shadows are not meant to be + * black only.
      • + *
      + * You can set these properties using the provided mutators or the appropriate + * constructor. Here are two ways of creating a green shadow of size 10 and + * with an opacity of 50%: + *
      + * ShadowRenderer renderer = new ShadowRenderer(10, 0.5f, Color.GREEN);
      + * // ..
      + * renderer = new ShadowRenderer();
      + * renderer.setSize(10);
      + * renderer.setOpacity(0.5f);
      + * renderer.setColor(Color.GREEN);
      + * 
      + * The default constructor provides the following default values: + *
        + *
      • size: 5 pixels
      • + *
      • opacity: 50%
      • + *
      • color: Black
      • + *

      + *

      Generating a Shadow

      + *

      A shadow is generated as a BufferedImage from another + * BufferedImage. Once the renderer is set up, you must call + * {@link #createShadow} to actually generate the shadow: + *

      + * ShadowRenderer renderer = new ShadowRenderer();
      + * // renderer setup
      + * BufferedImage shadow = renderer.createShadow(bufferedImage);
      + * 

      + *

      The generated image dimensions are computed as following:

      + *
      + * width  = imageWidth  + 2 * shadowSize
      + * height = imageHeight + 2 * shadowSize
      + * 
      + *

      Properties Changes

      + *

      This renderer allows to register property change listeners with + * {@link #addPropertyChangeListener}. Listening to properties changes is very + * useful when you embed the renderer in a graphical component and give the API + * user the ability to access the renderer. By listening to properties changes, + * you can easily repaint the component when needed.

      + *

      Threading Issues

      + *

      ShadowRenderer is not guaranteed to be thread-safe.

      + * + * @author Romain Guy + * @author Sebastien Petrucci + */ +public class ShadowRenderer { + /** + *

      Identifies a change to the size used to render the shadow.

      + *

      When the property change event is fired, the old value and the new + * value are provided as Integer instances.

      + */ + public static final String SIZE_CHANGED_PROPERTY = "shadow_size"; + + /** + *

      Identifies a change to the opacity used to render the shadow.

      + *

      When the property change event is fired, the old value and the new + * value are provided as Float instances.

      + */ + public static final String OPACITY_CHANGED_PROPERTY = "shadow_opacity"; + + /** + *

      Identifies a change to the color used to render the shadow.

      + */ + public static final String COLOR_CHANGED_PROPERTY = "shadow_color"; + + // size of the shadow in pixels (defines the fuzziness) + private int size = 5; + + // opacity of the shadow + private float opacity = 0.5f; + + // color of the shadow + private Color color = Color.BLACK; + + // notifies listeners of properties changes + private PropertyChangeSupport changeSupport; + + /** + *

      Creates a default good looking shadow generator. + * The default shadow renderer provides the following default values: + *

        + *
      • size: 5 pixels
      • + *
      • opacity: 50%
      • + *
      • color: Black
      • + *

      + *

      These properties provide a regular, good looking shadow.

      + */ + public ShadowRenderer() { + this(5, 0.5f, Color.BLACK); + } + + /** + *

      A shadow renderer needs three properties to generate shadows. + * These properties are:

      + *
        + *
      • size: The size, in pixels, of the shadow. This property also + * defines the fuzziness.
      • + *
      • opacity: The opacity, between 0.0 and 1.0, of the shadow.
      • + *
      • color: The color of the shadow. Shadows are not meant to be + * black only.
      • + *
      + * @param size the size of the shadow in pixels. Defines the fuzziness. + * @param opacity the opacity of the shadow. + * @param color the color of the shadow. + */ + public ShadowRenderer(final int size, final float opacity, final Color color) { + //noinspection ThisEscapedInObjectConstruction + changeSupport = new PropertyChangeSupport(this); + + setSize(size); + setOpacity(opacity); + setColor(color); + } + + /** + *

      Add a PropertyChangeListener to the listener list. The listener is + * registered for all properties. The same listener object may be added + * more than once, and will be called as many times as it is added. If + * listener is null, no exception is thrown and no action + * is taken.

      + * @param listener the PropertyChangeListener to be added + */ + public void addPropertyChangeListener(PropertyChangeListener listener) { + changeSupport.addPropertyChangeListener(listener); + } + + /** + *

      Remove a PropertyChangeListener from the listener list. This removes + * a PropertyChangeListener that was registered for all properties. If + * listener was added more than once to the same event source, + * it will be notified one less time after being removed. If + * listener is null, or was never added, no exception is thrown + * and no action is taken.

      + * @param listener the PropertyChangeListener to be removed + */ + public void removePropertyChangeListener(PropertyChangeListener listener) { + changeSupport.removePropertyChangeListener(listener); + } + + /** + *

      Gets the color used by the renderer to generate shadows.

      + * @return this renderer's shadow color + */ + public Color getColor() { + return color; + } + + /** + *

      Sets the color used by the renderer to generate shadows.

      + *

      Consecutive calls to {@link #createShadow} will all use this color + * until it is set again.

      + *

      If the color provided is null, the previous color will be retained.

      + * @param shadowColor the generated shadows color + */ + public void setColor(final Color shadowColor) { + if (shadowColor != null) { + Color oldColor = this.color; + this.color = shadowColor; + changeSupport.firePropertyChange(COLOR_CHANGED_PROPERTY, + oldColor, + this.color); + } + } + + /** + *

      Gets the opacity used by the renderer to generate shadows.

      + *

      The opacity is comprised between 0.0f and 1.0f; 0.0f being fully + * transparent and 1.0f fully opaque.

      + * @return this renderer's shadow opacity + */ + public float getOpacity() { + return opacity; + } + + /** + *

      Sets the opacity used by the renderer to generate shadows.

      + *

      Consecutive calls to {@link #createShadow} will all use this opacity + * until it is set again.

      + *

      The opacity is comprised between 0.0f and 1.0f; 0.0f being fully + * transparent and 1.0f fully opaque. If you provide a value out of these + * boundaries, it will be restrained to the closest boundary.

      + * @param shadowOpacity the generated shadows opacity + */ + public void setOpacity(final float shadowOpacity) { + float oldOpacity = this.opacity; + + if (shadowOpacity < 0.0) { + this.opacity = 0.0f; + } else if (shadowOpacity > 1.0f) { + this.opacity = 1.0f; + } else { + this.opacity = shadowOpacity; + } + + changeSupport.firePropertyChange(OPACITY_CHANGED_PROPERTY, + oldOpacity, + this.opacity); + } + + /** + *

      Gets the size in pixel used by the renderer to generate shadows.

      + * @return this renderer's shadow size + */ + public int getSize() { + return size; + } + + /** + *

      Sets the size, in pixels, used by the renderer to generate shadows.

      + *

      The size defines the blur radius applied to the shadow to create the + * fuzziness.

      + *

      There is virtually no limit to the size. The size cannot be negative. + * If you provide a negative value, the size will be 0 instead.

      + * @param shadowSize the generated shadows size in pixels (fuzziness) + */ + public void setSize(final int shadowSize) { + int oldSize = this.size; + + if (shadowSize < 0) { + this.size = 0; + } else { + this.size = shadowSize; + } + + changeSupport.firePropertyChange(SIZE_CHANGED_PROPERTY, + oldSize, + this.size); + } + + /** + *

      Generates the shadow for a given picture and the current properties + * of the renderer.

      + *

      The generated image dimensions are computed as following:

      + *
      +     * width  = imageWidth  + 2 * shadowSize
      +     * height = imageHeight + 2 * shadowSize
      +     * 
      + * @param image the picture from which the shadow must be cast + * @return the picture containing the shadow of image + */ + public BufferedImage createShadow(final BufferedImage image) { + // Written by Sesbastien Petrucci + int shadowSize = size * 2; + + int srcWidth = image.getWidth(); + int srcHeight = image.getHeight(); + + int dstWidth = srcWidth + shadowSize; + int dstHeight = srcHeight + shadowSize; + + int left = size; + int right = shadowSize - left; + + int yStop = dstHeight - right; + + int shadowRgb = color.getRGB() & 0x00FFFFFF; + int[] aHistory = new int[shadowSize]; + int historyIdx; + + int aSum; + + BufferedImage dst = createCompatibleTranslucentImage(dstWidth, dstHeight); + + int[] dstBuffer = new int[dstWidth * dstHeight]; + int[] srcBuffer = new int[srcWidth * srcHeight]; + + GraphicsUtilities.getPixels(image, 0, 0, srcWidth, srcHeight, srcBuffer); + + int lastPixelOffset = right * dstWidth; + float hSumDivider = 1.0f / shadowSize; + float vSumDivider = opacity / shadowSize; + + int[] hSumLookup = new int[256 * shadowSize]; + for (int i = 0; i < hSumLookup.length; i++) { + hSumLookup[i] = (int) (i * hSumDivider); + } + + int[] vSumLookup = new int[256 * shadowSize]; + for (int i = 0; i < vSumLookup.length; i++) { + vSumLookup[i] = (int) (i * vSumDivider); + } + + int srcOffset; + + // horizontal pass : extract the alpha mask from the source picture and + // blur it into the destination picture + for (int srcY = 0, dstOffset = left * dstWidth; srcY < srcHeight; srcY++) { + + // first pixels are empty + for (historyIdx = 0; historyIdx < shadowSize; ) { + aHistory[historyIdx++] = 0; + } + + aSum = 0; + historyIdx = 0; + srcOffset = srcY * srcWidth; + + // compute the blur average with pixels from the source image + for (int srcX = 0; srcX < srcWidth; srcX++) { + + int a = hSumLookup[aSum]; + dstBuffer[dstOffset++] = a << 24; // store the alpha value only + // the shadow color will be added in the next pass + + aSum -= aHistory[historyIdx]; // substract the oldest pixel from the sum + + // extract the new pixel ... + a = srcBuffer[srcOffset + srcX] >>> 24; + aHistory[historyIdx] = a; // ... and store its value into history + aSum += a; // ... and add its value to the sum + + if (++historyIdx >= shadowSize) { + historyIdx -= shadowSize; + } + } + + // blur the end of the row - no new pixels to grab + for (int i = 0; i < shadowSize; i++) { + + int a = hSumLookup[aSum]; + dstBuffer[dstOffset++] = a << 24; + + // substract the oldest pixel from the sum ... and nothing new to add ! + aSum -= aHistory[historyIdx]; + + if (++historyIdx >= shadowSize) { + historyIdx -= shadowSize; + } + } + } + + // vertical pass + for (int x = 0, bufferOffset = 0; x < dstWidth; x++, bufferOffset = x) { + + aSum = 0; + + // first pixels are empty + for (historyIdx = 0; historyIdx < left;) { + aHistory[historyIdx++] = 0; + } + + // and then they come from the dstBuffer + for (int y = 0; y < right; y++, bufferOffset += dstWidth) { + int a = dstBuffer[bufferOffset] >>> 24; // extract alpha + aHistory[historyIdx++] = a; // store into history + aSum += a; // and add to sum + } + + bufferOffset = x; + historyIdx = 0; + + // compute the blur avera`ge with pixels from the previous pass + for (int y = 0; y < yStop; y++, bufferOffset += dstWidth) { + + int a = vSumLookup[aSum]; + dstBuffer[bufferOffset] = a << 24 | shadowRgb; // store alpha value + shadow color + + aSum -= aHistory[historyIdx]; // substract the oldest pixel from the sum + + a = dstBuffer[bufferOffset + lastPixelOffset] >>> 24; // extract the new pixel ... + aHistory[historyIdx] = a; // ... and store its value into history + aSum += a; // ... and add its value to the sum + + if (++historyIdx >= shadowSize) { + historyIdx -= shadowSize; + } + } + + // blur the end of the column - no pixels to grab anymore + for (int y = yStop; y < dstHeight; y++, bufferOffset += dstWidth) { + + int a = vSumLookup[aSum]; + dstBuffer[bufferOffset] = a << 24 | shadowRgb; + + aSum -= aHistory[historyIdx]; // substract the oldest pixel from the sum + + if (++historyIdx >= shadowSize) { + historyIdx -= shadowSize; + } + } + } + + GraphicsUtilities.setPixels(dst, 0, 0, dstWidth, dstHeight, dstBuffer); + return dst; + } +} diff --git a/src/main/java/org/jdesktop/swingx/graphics/package-info.java b/src/main/java/org/jdesktop/swingx/graphics/package-info.java new file mode 100644 index 0000000000..ef3e217756 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/graphics/package-info.java @@ -0,0 +1,25 @@ +/* + * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +/** + * Contains graphic utilities and effects for working with images. + */ +package org.jdesktop.swingx.graphics; + diff --git a/src/main/java/org/jdesktop/swingx/hyperlink/AbstractHyperlinkAction.java b/src/main/java/org/jdesktop/swingx/hyperlink/AbstractHyperlinkAction.java new file mode 100644 index 0000000000..575581d2da --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/hyperlink/AbstractHyperlinkAction.java @@ -0,0 +1,144 @@ +/* + * $Id: AbstractHyperlinkAction.java 3484 2009-09-03 15:55:34Z kleopatra $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.hyperlink; + +import org.jdesktop.swingx.action.AbstractActionExt; + +import java.awt.event.ItemEvent; + +/** + * Convenience implementation to simplify {@link org.jdesktop.swingx.JXHyperlink} configuration and + * provide minimal api.

      + * + * @author Jeanette Winzenburg + */ +public abstract class AbstractHyperlinkAction extends AbstractActionExt { + + /** + * Key for the visited property value. + */ + public static final String VISITED_KEY = "visited"; + /** + * the object the actionPerformed can act on. + */ + protected T target; + + + /** + * Instantiates a LinkAction with null target. + * + */ + public AbstractHyperlinkAction () { + this(null); } + + /** + * Instantiates a LinkAction with a target of type targetClass. + * The visited property is initialized as defined by + * {@link AbstractHyperlinkAction#installTarget()} + * + * @param target the target this action should act on. + */ + public AbstractHyperlinkAction(T target) { + setTarget(target); + } + + /** + * Set the visited property. + * + * @param visited + */ + public void setVisited(boolean visited) { + putValue(VISITED_KEY, visited); + } + + /** + * + * @return visited state + */ + public boolean isVisited() { + Boolean visited = (Boolean) getValue(VISITED_KEY); + return Boolean.TRUE.equals(visited); + } + + + public T getTarget() { + return target; + } + + /** + * PRE: isTargetable(target) + * @param target + */ + public void setTarget(T target) { + T oldTarget = getTarget(); + uninstallTarget(); + this.target = target; + installTarget(); + firePropertyChange("target", oldTarget, getTarget()); + + } + + /** + * hook for subclasses to update internal state after + * a new target has been set.

      + * + * Subclasses are free to decide the details. + * Here: + *

        + *
      • the text property is set to target.toString or empty String if + * the target is null + *
      • visited is set to false. + *
      + */ + protected void installTarget() { + setName(target != null ? target.toString() : "" ); + setVisited(false); + } + + /** + * hook for subclasses to cleanup before the old target + * is overwritten.

      + * + * Subclasses are free to decide the details. + * Here: does nothing. + */ + protected void uninstallTarget() { + + } + + @Override + public void itemStateChanged(ItemEvent e) { + // do nothing + } + + /** + * Set the state property. + * Overridden to to nothing. + * PENDING: really? + * @param state if true then this action will fire ItemEvents + */ + @Override + public void setStateAction(boolean state) { + } + + + +} diff --git a/src/main/java/org/jdesktop/swingx/hyperlink/EditorPaneLinkVisitor.java b/src/main/java/org/jdesktop/swingx/hyperlink/EditorPaneLinkVisitor.java new file mode 100644 index 0000000000..eee6b3c517 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/hyperlink/EditorPaneLinkVisitor.java @@ -0,0 +1,137 @@ +/* + * $Id: EditorPaneLinkVisitor.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.hyperlink; + +import org.jdesktop.swingx.JXEditorPane; + +import javax.swing.*; +import javax.swing.event.HyperlinkEvent; +import javax.swing.event.HyperlinkListener; +import javax.swing.text.Document; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.IOException; +import java.net.URL; + + +/** + * A ActionListener using a JXEditorPane to "visit" a LinkModel. + * + * adds an internal HyperlinkListener to visit links contained + * in the document. + * + * @author Jeanette Winzenburg + */ +public class EditorPaneLinkVisitor implements ActionListener { + private JXEditorPane editorPane; + private HyperlinkListener hyperlinkListener; + private LinkModel internalLink; + + public EditorPaneLinkVisitor() { + this(null); + } + + public EditorPaneLinkVisitor(JXEditorPane pane) { + if (pane == null) { + pane = createDefaultEditorPane(); + } + this.editorPane = pane; + pane.addHyperlinkListener(getHyperlinkListener()); + } + + + public JXEditorPane getOutputComponent() { + return editorPane; + } + + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource() instanceof LinkModel) { + final LinkModel link = (LinkModel) e.getSource(); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + visit(link); + + } + }); + } + + } + + public void visit(LinkModel link) { + try { + // make sure to reload + editorPane.getDocument().putProperty(Document.StreamDescriptionProperty, null); + // JW: editorPane defaults to asynchronous loading + // no need to explicitly start a thread - really? + editorPane.setPage(link.getURL()); + link.setVisited(true); + } catch (IOException e1) { + editorPane.setText("Error 404: couldn't show " + link.getURL() + " "); + } + } + + protected JXEditorPane createDefaultEditorPane() { + final JXEditorPane editorPane = new JXEditorPane(); + editorPane.setEditable(false); + editorPane.setContentType("text/html"); + return editorPane; + } + + protected HyperlinkListener getHyperlinkListener() { + if (hyperlinkListener == null) { + hyperlinkListener = createHyperlinkListener(); + } + return hyperlinkListener; + } + + protected HyperlinkListener createHyperlinkListener() { + return new HyperlinkListener() { + @Override + public void hyperlinkUpdate(HyperlinkEvent e) { + if (HyperlinkEvent.EventType.ACTIVATED == e.getEventType()) { + visitInternal(e.getURL()); + } + + } + + }; + } + + protected LinkModel getInternalLink() { + if (internalLink == null) { + internalLink = new LinkModel("internal"); + } + return internalLink; + } + + protected void visitInternal(URL url) { + try { + getInternalLink().setURL(url); + visit(getInternalLink()); + } catch (Exception e) { + // todo: error feedback + } + } + + +} diff --git a/src/main/java/org/jdesktop/swingx/hyperlink/HyperlinkAction.java b/src/main/java/org/jdesktop/swingx/hyperlink/HyperlinkAction.java new file mode 100644 index 0000000000..7e29e52398 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/hyperlink/HyperlinkAction.java @@ -0,0 +1,310 @@ +/* + * $Id$ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.hyperlink; + +import java.awt.*; +import java.awt.Desktop.Action; +import java.awt.event.ActionEvent; +import java.io.IOException; +import java.net.URI; +import java.util.logging.Logger; + +/** + * A implementation wrapping Desktop actions BROWSE and MAIL, that is + * URI-related. + * + * @author Jeanette Winzenburg + */ +public class HyperlinkAction extends AbstractHyperlinkAction { + @SuppressWarnings("unused") + private static final Logger LOG = Logger.getLogger(HyperlinkAction.class + .getName()); + + private Action desktopAction; + private URIVisitor visitor; + + /** + * Factory method to create and return a HyperlinkAction for the given uri. Tries + * to guess the appropriate type from the uri. If uri is not null and has a + * scheme of mailto, create one of type Mail. In all other cases, creates one + * for BROWSE. + * + * @param uri to uri to create a HyperlinkAction for, maybe null. + * @return a HyperlinkAction for the given URI. + * @throws HeadlessException if {@link + * GraphicsEnvironment#isHeadless()} returns {@code true} + * @throws UnsupportedOperationException if the current platform doesn't support + * Desktop + */ + public static HyperlinkAction createHyperlinkAction(URI uri) { + Action type = isMailURI(uri) ? Action.MAIL : Action.BROWSE; + return createHyperlinkAction(uri, type); + } + + /** + * Creates and returns a HyperlinkAction with the given target and action type. + * @param uri the target uri, maybe null. + * @param desktopAction the type of desktop action this class should perform, must be + * BROWSE or MAIL + * @return a HyperlinkAction + * @throws HeadlessException if {@link + * GraphicsEnvironment#isHeadless()} returns {@code true} + * @throws UnsupportedOperationException if the current platform doesn't support + * Desktop + * @throws IllegalArgumentException if unsupported action type + */ + public static HyperlinkAction createHyperlinkAction(URI uri, Action type) { + return new HyperlinkAction(uri, type); + } + + /** + * @param uri + * @return + */ + private static boolean isMailURI(URI uri) { + return uri != null && "mailto".equalsIgnoreCase(uri.getScheme()); + } + + /** + * Instantiates a HyperlinkAction with action type BROWSE. + * + * @throws HeadlessException if {@link + * GraphicsEnvironment#isHeadless()} returns {@code true} + * @throws UnsupportedOperationException if the current platform doesn't support + * Desktop + * @throws IllegalArgumentException if unsupported action type + */ + public HyperlinkAction() { + this(Action.BROWSE); + } + + /** + * Instantiates a HyperlinkAction with the given action type. + * + * @param desktopAction the type of desktop action this class should perform, must be + * BROWSE or MAIL + * @throws HeadlessException if {@link + * GraphicsEnvironment#isHeadless()} returns {@code true} + * @throws UnsupportedOperationException if the current platform doesn't support + * Desktop + * @throws IllegalArgumentException if unsupported action type + */ + public HyperlinkAction(Action desktopAction) { + this(null, desktopAction); + } + + /** + * + * @param uri the target uri, maybe null. + * @param desktopAction the type of desktop action this class should perform, must be + * BROWSE or MAIL + * @throws HeadlessException if {@link + * GraphicsEnvironment#isHeadless()} returns {@code true} + * @throws UnsupportedOperationException if the current platform doesn't support + * Desktop + * @throws IllegalArgumentException if unsupported action type + */ + public HyperlinkAction(URI uri, Action desktopAction) { + super(); + if (!Desktop.isDesktopSupported()) { + throw new UnsupportedOperationException("Desktop API is not " + + "supported on the current platform"); + } + if (desktopAction != Action.BROWSE && desktopAction != Action.MAIL) { + throw new IllegalArgumentException("Illegal action type: " + desktopAction + + ". Must be BROWSE or MAIL"); + } + this.desktopAction = desktopAction; + getURIVisitor(); + setTarget(uri); + } + + /** + * {@inheritDoc}

      + * + * Implemented to perform the appropriate Desktop action if supported on the current + * target. Sets the visited property to true if the desktop action doesn't throw + * an exception or to false if it did. + * + * Does nothing if the action isn't supported. + */ + @Override + public void actionPerformed(ActionEvent e) { + if (!getURIVisitor().isEnabled(getTarget())) return; + try { + getURIVisitor().visit(getTarget()); + setVisited(true); + } catch (IOException e1) { + setVisited(false); + LOG.fine("cant visit Desktop " + e); + } + } + + /** + * @return + */ + public Action getDesktopAction() { + return desktopAction; + } + + + @Override + protected void installTarget() { + // doohh ... this is called from super's constructor before we are + // fully initialized + if (visitor == null) return; + super.installTarget(); + updateEnabled(); + } + + /** + * + */ + private void updateEnabled() { + setEnabled(getURIVisitor().isEnabled(getTarget())); + } + + /** + * @return + */ + private URIVisitor getURIVisitor() { + if (visitor == null) { + visitor = createURIVisitor(); + } + return visitor; + } + + /** + * @return + */ + private URIVisitor createURIVisitor() { + return getDesktopAction() == Action.BROWSE ? + new BrowseVisitor() : new MailVisitor(); + } + + /** + * Thin wrapper around Desktop functionality to allow uniform handling of + * different actions in HyperlinkAction. + * + */ + private abstract class URIVisitor { + protected boolean desktopSupported = Desktop.isDesktopSupported(); + + /** + * Returns a boolean indicating whether the action is supported on the + * given URI. This implementation returns true if both the Desktop is + * generally supported and isActionSupported(). + * + * PENDING JW: hmm ... which class exactly has to check for valid combination + * of Action and URI? + * + * @param uri + * @return + * + * @see #isActionSupported() + */ + public boolean isEnabled(URI uri) { + return desktopSupported && isActionSupported(); + } + + /** + * Visits the given URI via Desktop functionality. Must not be called if not + * enabled. + * + * @param uri the URI to visit + * @throws IOException if the Desktop method throws IOException. + * + */ + public abstract void visit(URI uri) throws IOException; + + /** + * Returns a boolean indicating if the action is supported by the current + * Desktop. + * + * @return true if the Action is supported by the current desktop, false + * otherwise. + */ + protected abstract boolean isActionSupported(); + } + + private class BrowseVisitor extends URIVisitor { + + /** + * {@inheritDoc}

      + * + * Implemented to message the browse method of Desktop. + */ + @Override + public void visit(URI uri) throws IOException { + Desktop.getDesktop().browse(uri); + } + + /** + * {@inheritDoc}

      + * + * Implemented to query the Desktop for support of BROWSE action. + */ + @Override + protected boolean isActionSupported() { + return Desktop.getDesktop().isSupported(Action.BROWSE); + } + + /** + * {@inheritDoc}

      + * + * Implemented to guard against null URI in addition to super. + */ + @Override + public boolean isEnabled(URI uri) { + return uri != null && super.isEnabled(uri); + } + + + } + + private class MailVisitor extends URIVisitor { + + /** + * {@inheritDoc}

      + * + * Implemented to message the mail function of Desktop. + */ + @Override + public void visit(URI uri) throws IOException { + if (uri == null) { + Desktop.getDesktop().mail(); + } else { + Desktop.getDesktop().mail(uri); + } + } + /** + * {@inheritDoc}

      + * + * Implemented to query the Desktop for support of MAIL action. + */ + @Override + protected boolean isActionSupported() { + return Desktop.getDesktop().isSupported(Action.MAIL); + } + + } +} diff --git a/src/main/java/org/jdesktop/swingx/hyperlink/LinkModel.java b/src/main/java/org/jdesktop/swingx/hyperlink/LinkModel.java new file mode 100644 index 0000000000..4c3fc35c7e --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/hyperlink/LinkModel.java @@ -0,0 +1,317 @@ +/* + * $Id: LinkModel.java 2951 2008-06-17 10:07:49Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.hyperlink; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.logging.Logger; + +/** + * An bean which represents an URL link. + * + * Text, URL and visited are bound properties. Compares by Text. + * + * @author Mark Davidson + * @author Jeanette Winzenburg + */ +public class LinkModel implements Comparable { + + private static final Logger LOG = Logger.getLogger(LinkModel.class + .getName()); + + private String text; // display text + + private URL url; // url of the link + + private String target; // url target frame + + private boolean visited = false; + + private PropertyChangeSupport propertyChangeSupport; + + public static final String VISITED_PROPERTY = "visited"; + + // hack - this class assumes that the url always != null + // need to cleanup + private static String defaultURLString = "https://jdnc.dev.java.net"; + + private static URL defaultURL; + + /** + * + * @param text + * @param target + * @param url + */ + public LinkModel(String text, String target, URL url) { + setText(text); + setTarget(target); + setURL(url != null ? url : getDefaultURL()); + } + + public LinkModel() { + this(" ", null, null); + } + + public LinkModel(String text) { + this(text, null, null); + } + + /** + * @param text text to that a renderer would display + * @param target the target that a URL should load into. + * @param template a string that represents a URL with + * &{N} place holders for string substitution + * @param args an array of strings which will be used for substitition + */ + public LinkModel(String text, String target, String template, String[] args) { + setText(text); + setTarget(target); + setURL(createURL(template, args)); + } + + /** + * Set the display text. + */ + public void setText(String text) { + String old = getText(); + this.text = text; + firePropertyChange("text", old, getText()); + } + + public String getText() { + if (text != null) { + return text; + } else if (url != null) { + return getURL().toString(); + } + return null; + } + + public void setURLString(String howToURLString) { + URL url = null; + try { + url = new URL(howToURLString); + } catch (MalformedURLException e) { + url = getDefaultURL(); + LOG.warning("the given urlString is malformed: " + howToURLString + + "\n falling back to default url: " + url); + } + setURL(url); + } + + private URL getDefaultURL() { + if (defaultURL == null) { + try { + defaultURL = new URL(defaultURLString); + } catch (MalformedURLException e) { + LOG.fine("should not happen - defaultURL is wellFormed: " + + defaultURLString); + } + } + return defaultURL; + } + + /** + * Set the url and resets the visited flag. + * + * Think: keep list of visited urls here? + */ + public void setURL(URL url) { + if (url == null) { + throw new IllegalArgumentException("URL for link cannot be null"); + } + if (url.equals(getURL())) + return; + URL old = getURL(); + this.url = url; + firePropertyChange("URL", old, url); + setVisited(false); + } + + public URL getURL() { + return url; + } + + /** + * Create a URL from a template string that has place holders and an array + * of strings which will be substituted into the place holders. The place + * holders are represented as + * + * @{N} where N = { 1..n } + *

      + * For example, if the template contains a string like: + * http://bugz.sfbay/cgi-bin/showbug?cat=@{1}&sub_cat=@{2} and a two + * arg array contains: java, classes_swing The resulting URL will be: + * http://bugz.sfbay/cgi-bin/showbug?cat=java&sub_cat=classes_swing + *

      + * @param template a url string that contains the placeholders + * @param args an array of strings that will be substituted + */ + private URL createURL(String template, String[] args) { + URL url = null; + try { + String urlStr = template; + for (int i = 0; i < args.length; i++) { + urlStr = urlStr.replaceAll("@\\{" + (i + 1) + "\\}", args[i]); + } + url = new URL(urlStr); + } catch (MalformedURLException ex) { + // + } + return url; + } + + /** + * Set the target that the URL should load into. This can be a uri + * representing another control or the name of a window or special targets. + * See: http://www.w3c.org/TR/html401/present/frames.html#adef-target + */ + public void setTarget(String target) { + this.target = target; + } + + /** + * Return the target for the URL. + * + * @return value of the target. If null then "_blank" will be returned. + */ + public String getTarget() { + if (target != null) { + return target; + } else { + return "_blank"; + } + } + + /** + * Sets a flag to indicate if the link has been visited. The state of this + * flag can be used to render the color of the link. + */ + public void setVisited(boolean visited) { + boolean old = getVisited(); + this.visited = visited; + firePropertyChange(VISITED_PROPERTY, old, getVisited()); + } + + public boolean getVisited() { + return visited; + } + + // ---------------------- property change notification + + public void addPropertyChangeListener(PropertyChangeListener l) { + getPropertyChangeSupport().addPropertyChangeListener(l); + + } + + public void removePropertyChangeListener(PropertyChangeListener l) { + if (propertyChangeSupport == null) + return; + propertyChangeSupport.removePropertyChangeListener(l); + } + + protected void firePropertyChange(String property, Object oldValue, + Object newValue) { + if (propertyChangeSupport == null) + return; + propertyChangeSupport.firePropertyChange(property, oldValue, newValue); + } + + protected void firePropertyChange(String property, boolean oldValue, + boolean newValue) { + if (propertyChangeSupport == null) + return; + propertyChangeSupport.firePropertyChange(property, oldValue, newValue); + + } + + private PropertyChangeSupport getPropertyChangeSupport() { + if (propertyChangeSupport == null) { + propertyChangeSupport = new PropertyChangeSupport(this); + } + return propertyChangeSupport; + } + + // Comparable interface for sorting. + public int compareTo(Object obj) { + if (obj == null) { + return 1; + } + if (obj == this) { + return 0; + } + return text.compareTo(((LinkModel) obj).text); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj != null && obj instanceof LinkModel) { + LinkModel other = (LinkModel) obj; + if (!getText().equals(other.getText())) { + return false; + } + + if (!getTarget().equals(other.getTarget())) { + return false; + } + + return getURL().equals(other.getURL()); + } + return false; + } + + @Override + public int hashCode() { + int result = 7; + + result = 37 * result + ((getText() == null) ? 0 : getText().hashCode()); + result = 37 * result + + ((getTarget() == null) ? 1 : getTarget().hashCode()); + result = 37 * result + ((getURL() == null) ? 2 : getURL().hashCode()); + + return result; + } + + @Override + public String toString() { + + StringBuffer buffer = new StringBuffer("["); + // RG: Fix for J2SE 5.0; Can't cascade append() calls because + // return type in StringBuffer and AbstractStringBuilder are different + buffer.append("url="); + buffer.append(url); + buffer.append(", target="); + buffer.append(target); + buffer.append(", text="); + buffer.append(text); + buffer.append("]"); + + return buffer.toString(); + } + +} diff --git a/src/main/java/org/jdesktop/swingx/hyperlink/LinkModelAction.java b/src/main/java/org/jdesktop/swingx/hyperlink/LinkModelAction.java new file mode 100644 index 0000000000..b697479a32 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/hyperlink/LinkModelAction.java @@ -0,0 +1,153 @@ +/* + * $Id: LinkModelAction.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.hyperlink; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + + +/** + * Specialized LinkAction for a target of type {@link LinkModel}. + *

      + * + * This action delegates actionPerformed to vistingDelegate. + * + * PENDING: move to swingx package? + * + * @author Jeanette Winzenburg + */ +public class LinkModelAction extends AbstractHyperlinkAction { + + private ActionListener delegate; + public static final String VISIT_ACTION = "visit"; + private PropertyChangeListener linkListener; + + + public LinkModelAction() { + this((T) null); + } + + public LinkModelAction(ActionListener visitingDelegate) { + this(null, visitingDelegate); + } + + public LinkModelAction(T target) { + this(target, null); + } + + public LinkModelAction(T target, ActionListener visitingDelegate) { + super(target); + setVisitingDelegate(visitingDelegate); + } + + /** + * The delegate to invoke on actionPerformed. + *

      + * The delegates actionPerformed is invoked with an ActionEvent + * having the target as source. Delegates are expected to + * cope gracefully with the T. + *

      + * + * PENDING: JW - How to formalize? + * + * @param delegate the action invoked on the target. + */ + public void setVisitingDelegate(ActionListener delegate) { + this.delegate = delegate; + } + + /** + * This action delegates to the visitingDelegate if both + * delegate and target are != null, does nothing otherwise. + * The actionEvent carries the target as source. + * + * PENDING: pass through a null target? - most probably! + * + * + * + */ + @Override + public void actionPerformed(ActionEvent e) { + if ((delegate != null) && (getTarget() != null)) { + delegate.actionPerformed(new ActionEvent(getTarget(), ActionEvent.ACTION_PERFORMED, VISIT_ACTION)); + } + + } + + /** + * installs a propertyChangeListener on the target and + * updates the visual properties from the target. + */ + @Override + protected void installTarget() { + if (getTarget() != null) { + getTarget().addPropertyChangeListener(getTargetListener()); + } + updateFromTarget(); + } + + /** + * removes the propertyChangeListener.

      + * + * Implementation NOTE: this does not clean-up internal state! There is + * no need to because updateFromTarget handles both null and not-null + * targets. Hmm... + * + */ + @Override + protected void uninstallTarget() { + if (getTarget() == null) return; + getTarget().removePropertyChangeListener(getTargetListener()); + } + + protected void updateFromTarget() { + if (getTarget() != null) { + putValue(Action.NAME, getTarget().getText()); + putValue(Action.SHORT_DESCRIPTION, getTarget().getURL().toString()); + putValue(VISITED_KEY, getTarget().getVisited()); + } else { + Object[] keys = getKeys(); + if (keys == null) return; + for (int i = 0; i < keys.length; i++) { + putValue(keys[i].toString(), null); + } + } + } + + private PropertyChangeListener getTargetListener() { + if (linkListener == null) { + linkListener = new PropertyChangeListener() { + + @Override + public void propertyChange(PropertyChangeEvent evt) { + updateFromTarget(); + } + + }; + } + return linkListener; + } + + +} diff --git a/src/main/java/org/jdesktop/swingx/icon/ColumnControlIcon.java b/src/main/java/org/jdesktop/swingx/icon/ColumnControlIcon.java new file mode 100644 index 0000000000..443e222eef --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/icon/ColumnControlIcon.java @@ -0,0 +1,84 @@ +/* + * $Id: ColumnControlIcon.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.icon; + +import javax.swing.*; +import javax.swing.plaf.UIResource; +import java.awt.*; + +/** + * Icon class for rendering icon which indicates user control of + * column visibility. + * @author Amy Fowler + * @version 1.0 + */ +public class ColumnControlIcon implements Icon, UIResource { + private int width = 10; + private int height = 10; + + /** TODO: need to support small, medium, large */ + public ColumnControlIcon() { + } + + @Override + public int getIconWidth() { + return width; + } + + @Override + public int getIconHeight() { + return height; + } + + @Override + public void paintIcon(Component c, Graphics g, int x, int y) { + Color color = c.getForeground(); + g.setColor(color); + + // draw horizontal lines + g.drawLine(x, y, x+8, y); + g.drawLine(x, y+2, x+8, y+2); + g.drawLine(x, y+8, x+2, y+8); + + // draw vertical lines + g.drawLine(x, y+1, x, y+7); + g.drawLine(x+4, y+1, x+4, y+4); + g.drawLine(x+8, y+1, x+8, y+4); + + // draw arrow + g.drawLine(x+3, y+6, x+9, y+6); + g.drawLine(x+4, y+7, x+8, y+7); + g.drawLine(x+5, y+8, x+7, y+8); + g.drawLine(x+6, y+9, x+6, y+9); + + } + + public static void main(String args[]) { + JFrame frame = new JFrame(); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + JLabel label = new JLabel(new ColumnControlIcon()); + frame.getContentPane().add(BorderLayout.CENTER, label); + frame.pack(); + frame.setVisible(true); + } + +} diff --git a/src/main/java/org/jdesktop/swingx/icon/EmptyIcon.java b/src/main/java/org/jdesktop/swingx/icon/EmptyIcon.java new file mode 100644 index 0000000000..7cd2e26e47 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/icon/EmptyIcon.java @@ -0,0 +1,59 @@ +/* + * $Id: EmptyIcon.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.icon; + +import javax.swing.*; +import java.awt.*; +import java.io.Serializable; + +/** + * An empty icon with arbitrary width and height. + */ +public final class EmptyIcon implements Icon, Serializable { + + private int width; + + private int height; + + public EmptyIcon() { + this(0, 0); + } + + public EmptyIcon(int width, int height) { + this.width = width; + this.height = height; + } + + @Override + public int getIconHeight() { + return height; + } + + @Override + public int getIconWidth() { + return width; + } + + @Override + public void paintIcon(Component c, Graphics g, int x, int y) { + } + +} diff --git a/src/main/java/org/jdesktop/swingx/icon/PainterIcon.java b/src/main/java/org/jdesktop/swingx/icon/PainterIcon.java new file mode 100644 index 0000000000..38cca8decf --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/icon/PainterIcon.java @@ -0,0 +1,69 @@ +/* + * $Id: PainterIcon.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.icon; + +import org.jdesktop.swingx.painter.Painter; + +import javax.swing.*; +import java.awt.*; + +public class PainterIcon implements Icon { + Dimension size; + private Painter painter; + public PainterIcon(Dimension size) { + this.size = size; + } + + @Override + public int getIconHeight() { + return size.height; + } + + @Override + public int getIconWidth() { + return size.width; + } + + + @Override + public void paintIcon(Component c, Graphics g, int x, int y) { + if (getPainter() != null && g instanceof Graphics2D) { + g = g.create(); + + try { + g.translate(x, y); + getPainter().paint((Graphics2D) g, c, size.width, size.height); + g.translate(-x, -y); + } finally { + g.dispose(); + } + } + } + + public Painter getPainter() { + return painter; + } + + public void setPainter(Painter painter) { + this.painter = painter; + } +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/icon/package-info.java b/src/main/java/org/jdesktop/swingx/icon/package-info.java new file mode 100644 index 0000000000..41cdce7bbb --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/icon/package-info.java @@ -0,0 +1,36 @@ +/* + * $Id: package-info.java 3165 2009-01-02 13:26:07Z rah003 $ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +/**Contains Swing Icon classes used by JDNC's Swing Extensions. +

      Package Specification

      + + + +

      Related Documentation

      + + + +*/ +package org.jdesktop.swingx.icon; \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/image/AbstractFilter.java b/src/main/java/org/jdesktop/swingx/image/AbstractFilter.java new file mode 100644 index 0000000000..ed4614a57e --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/image/AbstractFilter.java @@ -0,0 +1,97 @@ +/* + * $Id: AbstractFilter.java 3863 2010-10-26 02:53:32Z kschaefe $ + * + * Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy). + * + * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * Copyright (c) 2006 Romain Guy + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jdesktop.swingx.image; + +import org.jdesktop.beans.AbstractBean; + +import java.awt.*; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.BufferedImageOp; +import java.awt.image.ColorModel; + +/** + *

      Provides an abstract implementation of the BufferedImageOp + * interface. This class can be used to created new image filters based + * on BufferedImageOp.

      + * + * @author Romain Guy + */ + +public abstract class AbstractFilter extends AbstractBean implements BufferedImageOp { + @Override + public abstract BufferedImage filter(BufferedImage src, BufferedImage dest); + + /** + * {@inheritDoc} + */ + @Override + public Rectangle2D getBounds2D(BufferedImage src) { + return new Rectangle(0, 0, src.getWidth(), src.getHeight()); + } + + /** + * {@inheritDoc} + */ + @Override + public BufferedImage createCompatibleDestImage(BufferedImage src, + ColorModel destCM) { + if (destCM == null) { + destCM = src.getColorModel(); + } + + return new BufferedImage(destCM, + destCM.createCompatibleWritableRaster( + src.getWidth(), src.getHeight()), + destCM.isAlphaPremultiplied(), null); + } + + /** + * {@inheritDoc} + */ + @Override + public Point2D getPoint2D(Point2D srcPt, Point2D dstPt) { + return (Point2D) srcPt.clone(); + } + + /** + * {@inheritDoc} + */ + @Override + public RenderingHints getRenderingHints() { + return null; + } +} diff --git a/src/main/java/org/jdesktop/swingx/image/ColorTintFilter.java b/src/main/java/org/jdesktop/swingx/image/ColorTintFilter.java new file mode 100644 index 0000000000..ebf7bceb93 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/image/ColorTintFilter.java @@ -0,0 +1,160 @@ +/* + * $Id: ColorTintFilter.java 4082 2011-11-15 18:39:43Z kschaefe $ + * + * Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy). + * + * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * Copyright (c) 2006 Romain Guy + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jdesktop.swingx.image; + +import org.jdesktop.swingx.util.GraphicsUtilities; + +import java.awt.*; +import java.awt.image.BufferedImage; + +/** + *

      A color tint filter can be used to mix a solid color to an image. The + * result is an image tinted by the specified color. The force of the effect + * can be controlled with the mixValue, a number between 0.0 and + * 1.0 that can be seen as the percentage of the mix (0.0 does not affect the + * source image and 1.0 replaces all the pixels by the solid color).

      + *

      The color of the pixels in the resulting image is computed as follows:

      + *
      + * cR = cS * (1 - mixValue) + cM * mixValue
      + * 
      + *

      Definition of the parameters:

      + *
        + *
      • cR: color of the resulting pixel
      • + *
      • cS: color of the source pixel
      • + *
      • cM: the solid color to mix with the source image
      • + *
      • mixValue: strength of the mix, a value between 0.0 and 1.0
      • + *
      + * + * @author Romain Guy + */ + +public class ColorTintFilter extends AbstractFilter { + private final Color mixColor; + private final float mixValue; + + private int[] preMultipliedRed; + private int[] preMultipliedGreen; + private int[] preMultipliedBlue; + + /** + *

      Creates a new color mixer filter. The specified color will be used + * to tint the source image, with a mixing strength defined by + * mixValue.

      + * + * @param mixColor the solid color to mix with the source image + * @param mixValue the strength of the mix, between 0.0 and 1.0; if the + * specified value lies outside this range, it is clamped + * @throws IllegalArgumentException if mixColor is null + */ + public ColorTintFilter(Color mixColor, float mixValue) { + if (mixColor == null) { + throw new IllegalArgumentException("mixColor cannot be null"); + } + + this.mixColor = mixColor; + if (mixValue < 0.0f) { + mixValue = 0.0f; + } else if (mixValue > 1.0f) { + mixValue = 1.0f; + } + this.mixValue = mixValue; + + int mix_r = (int) (mixColor.getRed() * mixValue); + int mix_g = (int) (mixColor.getGreen() * mixValue); + int mix_b = (int) (mixColor.getBlue() * mixValue); + + // Since we use only lookup tables to apply the filter, this filter + // could be implemented as a LookupOp. + float factor = 1.0f - mixValue; + preMultipliedRed = new int[256]; + preMultipliedGreen = new int[256]; + preMultipliedBlue = new int[256]; + + for (int i = 0; i < 256; i++) { + int value = (int) (i * factor); + preMultipliedRed[i] = value + mix_r; + preMultipliedGreen[i] = value + mix_g; + preMultipliedBlue[i] = value + mix_b; + } + } + + /** + *

      Returns the mix value of this filter.

      + * + * @return the mix value, between 0.0 and 1.0 + */ + public float getMixValue() { + return mixValue; + } + + /** + *

      Returns the solid mix color of this filter.

      + * + * @return the solid color used for mixing + */ + public Color getMixColor() { + return mixColor; + } + + /** + * {@inheritDoc} + */ + @Override + public BufferedImage filter(BufferedImage src, BufferedImage dst) { + if (dst == null) { + dst = createCompatibleDestImage(src, null); + } + + int width = src.getWidth(); + int height = src.getHeight(); + + int[] pixels = new int[width * height]; + GraphicsUtilities.getPixels(src, 0, 0, width, height, pixels); + mixColor(pixels); + GraphicsUtilities.setPixels(dst, 0, 0, width, height, pixels); + + return dst; + } + + private void mixColor(int[] pixels) { + for (int i = 0; i < pixels.length; i++) { + int argb = pixels[i]; + pixels[i] = (argb & 0xFF000000) | + preMultipliedRed[(argb >> 16) & 0xFF] << 16 | + preMultipliedGreen[(argb >> 8) & 0xFF] << 8 | + preMultipliedBlue[argb & 0xFF]; + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/image/FastBlurFilter.java b/src/main/java/org/jdesktop/swingx/image/FastBlurFilter.java new file mode 100644 index 0000000000..c5881c9e54 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/image/FastBlurFilter.java @@ -0,0 +1,216 @@ +/* + * $Id: FastBlurFilter.java 4082 2011-11-15 18:39:43Z kschaefe $ + * + * Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy). + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * Copyright (c) 2006 Romain Guy + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jdesktop.swingx.image; + +import org.jdesktop.swingx.util.GraphicsUtilities; + +import java.awt.image.BufferedImage; + +/** + *

      A fast blur filter can be used to blur pictures quickly. This filter is an + * implementation of the box blur algorithm. The blurs generated by this + * algorithm might show square artifacts, especially on pictures containing + * straight lines (rectangles, text, etc.) On most pictures though, the + * result will look very good.

      + *

      The force of the blur can be controlled with a radius and the + * default radius is 3. Since the blur clamps values on the edges of the + * source picture, you might need to provide a picture with empty borders + * to avoid artifacts at the edges. The performance of this filter are + * independent from the radius.

      + * + * @author Romain Guy + */ +public class FastBlurFilter extends AbstractFilter { + private final int radius; + + /** + *

      Creates a new blur filter with a default radius of 3.

      + */ + public FastBlurFilter() { + this(3); + } + + /** + *

      Creates a new blur filter with the specified radius. If the radius + * is lower than 1, a radius of 1 will be used automatically.

      + * + * @param radius the radius, in pixels, of the blur + */ + public FastBlurFilter(int radius) { + if (radius < 1) { + radius = 1; + } + + this.radius = radius; + } + + /** + *

      Returns the radius used by this filter, in pixels.

      + * + * @return the radius of the blur + */ + public int getRadius() { + return radius; + } + + /** + * {@inheritDoc} + */ + @Override + public BufferedImage filter(BufferedImage src, BufferedImage dst) { + int width = src.getWidth(); + int height = src.getHeight(); + + if (dst == null) { + dst = createCompatibleDestImage(src, null); + } + + int[] srcPixels = new int[width * height]; + int[] dstPixels = new int[width * height]; + + GraphicsUtilities.getPixels(src, 0, 0, width, height, srcPixels); + // horizontal pass + blur(srcPixels, dstPixels, width, height, radius); + // vertical pass + //noinspection SuspiciousNameCombination + blur(dstPixels, srcPixels, height, width, radius); + // the result is now stored in srcPixels due to the 2nd pass + GraphicsUtilities.setPixels(dst, 0, 0, width, height, srcPixels); + + return dst; + } + + /** + *

      Blurs the source pixels into the destination pixels. The force of + * the blur is specified by the radius which must be greater than 0.

      + *

      The source and destination pixels arrays are expected to be in the + * INT_ARGB format.

      + *

      After this method is executed, dstPixels contains a transposed and + * filtered copy of srcPixels.

      + * + * @param srcPixels the source pixels + * @param dstPixels the destination pixels + * @param width the width of the source picture + * @param height the height of the source picture + * @param radius the radius of the blur effect + */ + static void blur(int[] srcPixels, int[] dstPixels, + int width, int height, int radius) { + final int windowSize = radius * 2 + 1; + final int radiusPlusOne = radius + 1; + + int sumAlpha; + int sumRed; + int sumGreen; + int sumBlue; + + int srcIndex = 0; + int dstIndex; + int pixel; + + int[] sumLookupTable = new int[256 * windowSize]; + for (int i = 0; i < sumLookupTable.length; i++) { + sumLookupTable[i] = i / windowSize; + } + + int[] indexLookupTable = new int[radiusPlusOne]; + if (radius < width) { + for (int i = 0; i < indexLookupTable.length; i++) { + indexLookupTable[i] = i; + } + } else { + for (int i = 0; i < width; i++) { + indexLookupTable[i] = i; + } + for (int i = width; i < indexLookupTable.length; i++) { + indexLookupTable[i] = width - 1; + } + } + + for (int y = 0; y < height; y++) { + sumAlpha = sumRed = sumGreen = sumBlue = 0; + dstIndex = y; + + pixel = srcPixels[srcIndex]; + sumAlpha += radiusPlusOne * ((pixel >> 24) & 0xFF); + sumRed += radiusPlusOne * ((pixel >> 16) & 0xFF); + sumGreen += radiusPlusOne * ((pixel >> 8) & 0xFF); + sumBlue += radiusPlusOne * ( pixel & 0xFF); + + for (int i = 1; i <= radius; i++) { + pixel = srcPixels[srcIndex + indexLookupTable[i]]; + sumAlpha += (pixel >> 24) & 0xFF; + sumRed += (pixel >> 16) & 0xFF; + sumGreen += (pixel >> 8) & 0xFF; + sumBlue += pixel & 0xFF; + } + + for (int x = 0; x < width; x++) { + dstPixels[dstIndex] = sumLookupTable[sumAlpha] << 24 | + sumLookupTable[sumRed] << 16 | + sumLookupTable[sumGreen] << 8 | + sumLookupTable[sumBlue]; + dstIndex += height; + + int nextPixelIndex = x + radiusPlusOne; + if (nextPixelIndex >= width) { + nextPixelIndex = width - 1; + } + + int previousPixelIndex = x - radius; + if (previousPixelIndex < 0) { + previousPixelIndex = 0; + } + + int nextPixel = srcPixels[srcIndex + nextPixelIndex]; + int previousPixel = srcPixels[srcIndex + previousPixelIndex]; + + sumAlpha += (nextPixel >> 24) & 0xFF; + sumAlpha -= (previousPixel >> 24) & 0xFF; + + sumRed += (nextPixel >> 16) & 0xFF; + sumRed -= (previousPixel >> 16) & 0xFF; + + sumGreen += (nextPixel >> 8) & 0xFF; + sumGreen -= (previousPixel >> 8) & 0xFF; + + sumBlue += nextPixel & 0xFF; + sumBlue -= previousPixel & 0xFF; + } + + srcIndex += width; + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/image/GaussianBlurFilter.java b/src/main/java/org/jdesktop/swingx/image/GaussianBlurFilter.java new file mode 100644 index 0000000000..4f83c00f6d --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/image/GaussianBlurFilter.java @@ -0,0 +1,191 @@ +/* + * $Id: GaussianBlurFilter.java 4219 2012-08-07 04:11:12Z kschaefe $ + * + * Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy). + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * Copyright (c) 2006 Romain Guy + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jdesktop.swingx.image; + +import org.jdesktop.swingx.util.GraphicsUtilities; + +import java.awt.image.BufferedImage; + +public class GaussianBlurFilter extends AbstractFilter { + private final int radius; + + /** + *

      Creates a new blur filter with a default radius of 3.

      + */ + public GaussianBlurFilter() { + this(3); + } + + /** + *

      Creates a new blur filter with the specified radius. If the radius + * is lower than 1, a radius of 1 will be used automatically.

      + * + * @param radius the radius, in pixels, of the blur + */ + public GaussianBlurFilter(int radius) { + if (radius < 1) { + radius = 1; + } + + this.radius = radius; + } + + /** + *

      Returns the radius used by this filter, in pixels.

      + * + * @return the radius of the blur + */ + public int getRadius() { + return radius; + } + + /** + * {@inheritDoc} + */ + @Override + public BufferedImage filter(BufferedImage src, BufferedImage dst) { + int width = src.getWidth(); + int height = src.getHeight(); + + if (dst == null) { + dst = createCompatibleDestImage(src, null); + } + + int[] srcPixels = new int[width * height]; + int[] dstPixels = new int[width * height]; + + float[] kernel = createGaussianKernel(radius); + + GraphicsUtilities.getPixels(src, 0, 0, width, height, srcPixels); + // horizontal pass + blur(srcPixels, dstPixels, width, height, kernel, radius); + // vertical pass + blur(dstPixels, srcPixels, height, width, kernel, radius); + // the result is now stored in srcPixels due to the 2nd pass + GraphicsUtilities.setPixels(dst, 0, 0, width, height, srcPixels); + + return dst; + } + + /** + *

      Blurs the source pixels into the destination pixels. The force of + * the blur is specified by the radius which must be greater than 0.

      + *

      The source and destination pixels arrays are expected to be in the + * INT_ARGB format.

      + *

      After this method is executed, dstPixels contains a transposed and + * filtered copy of srcPixels.

      + * + * @param srcPixels the source pixels + * @param dstPixels the destination pixels + * @param width the width of the source picture + * @param height the height of the source picture + * @param kernel the kernel of the blur effect + * @param radius the radius of the blur effect + */ + static void blur(int[] srcPixels, int[] dstPixels, + int width, int height, + float[] kernel, int radius) { + float a; + float r; + float g; + float b; + + int ca; + int cr; + int cg; + int cb; + + for (int y = 0; y < height; y++) { + int index = y; + int offset = y * width; + + for (int x = 0; x < width; x++) { + a = r = g = b = 0.0f; + + for (int i = -radius; i <= radius; i++) { + int subOffset = x + i; + if (subOffset < 0 || subOffset >= width) { + subOffset = (x + width) % width; + } + + int pixel = srcPixels[offset + subOffset]; + float blurFactor = kernel[radius + i]; + + a += blurFactor * ((pixel >> 24) & 0xFF); + r += blurFactor * ((pixel >> 16) & 0xFF); + g += blurFactor * ((pixel >> 8) & 0xFF); + b += blurFactor * ((pixel ) & 0xFF); + } + + ca = (int) (a + 0.5f); + cr = (int) (r + 0.5f); + cg = (int) (g + 0.5f); + cb = (int) (b + 0.5f); + + dstPixels[index] = ((ca > 255 ? 255 : ca) << 24) | + ((cr > 255 ? 255 : cr) << 16) | + ((cg > 255 ? 255 : cg) << 8) | + (cb > 255 ? 255 : cb); + index += height; + } + } + } + + static float[] createGaussianKernel(int radius) { + if (radius < 1) { + throw new IllegalArgumentException("Radius must be >= 1"); + } + + float[] data = new float[radius * 2 + 1]; + + float sigma = radius / 3.0f; + float twoSigmaSquare = 2.0f * sigma * sigma; + float sigmaRoot = (float) Math.sqrt(twoSigmaSquare * Math.PI); + float total = 0.0f; + + for (int i = -radius; i <= radius; i++) { + float distance = i * i; + int index = i + radius; + data[index] = (float) Math.exp(-distance / twoSigmaSquare) / sigmaRoot; + total += data[index]; + } + + for (int i = 0; i < data.length; i++) { + data[i] /= total; + } + + return data; + } +} diff --git a/src/main/java/org/jdesktop/swingx/image/StackBlurFilter.java b/src/main/java/org/jdesktop/swingx/image/StackBlurFilter.java new file mode 100644 index 0000000000..31834d485a --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/image/StackBlurFilter.java @@ -0,0 +1,154 @@ +/* + * $Id: StackBlurFilter.java 4082 2011-11-15 18:39:43Z kschaefe $ + * + * Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy). + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * Copyright (c) 2006 Romain Guy + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jdesktop.swingx.image; + +import org.jdesktop.swingx.util.GraphicsUtilities; + +import java.awt.image.BufferedImage; + +/** + *

      A stack blur filter can be used to create an approximation of a + * Gaussian blur. The approximation is controlled by the number of times the + * {@link FastBlurFilter} is applied onto the source + * picture. The default number of iterations, 3, provides a decent compromise + * between speed and rendering quality.

      + *

      The force of the blur can be controlled with a radius and the + * default radius is 3. Since the blur clamps values on the edges of the + * source picture, you might need to provide a picture with empty borders + * to avoid artifacts at the edges. The performance of this filter are + * independent from the radius.

      + * + * @author Romain Guy +*/ +public class StackBlurFilter extends AbstractFilter { + private final int radius; + private final int iterations; + + /** + *

      Creates a new blur filter with a default radius of 3 and 3 iterations.

      + */ + public StackBlurFilter() { + this(3, 3); + } + + /** + *

      Creates a new blur filter with the specified radius and 3 iterations. + * If the radius is lower than 1, a radius of 1 will be used automatically.

      + * + * @param radius the radius, in pixels, of the blur + */ + public StackBlurFilter(int radius) { + this(radius, 3); + } + + /** + *

      Creates a new blur filter with the specified radius. If the radius + * is lower than 1, a radius of 1 will be used automatically. The number + * of iterations controls the approximation to a Gaussian blur. If the + * number of iterations is lower than 1, one iteration will be used + * automatically.

      + * + * @param radius the radius, in pixels, of the blur + * @param iterations the number of iterations to approximate a Gaussian blur + */ + public StackBlurFilter(int radius, int iterations) { + if (radius < 1) { + radius = 1; + } + if (iterations < 1) { + iterations = 1; + } + + this.radius = radius; + this.iterations = iterations; + } + + /** + *

      Returns the effective radius of the stack blur. If the radius of the + * blur is 1 and the stack iterations count is 3, then the effective blur + * radius is 1 * 3 = 3.

      + * @return the number of iterations times the blur radius + */ + public int getEffectiveRadius() { + return getIterations() * getRadius(); + } + + /** + *

      Returns the radius used by this filter, in pixels.

      + * + * @return the radius of the blur + */ + public int getRadius() { + return radius; + } + + /** + *

      Returns the number of iterations used to approximate a Gaussian + * blur.

      + * + * @return the number of iterations used by this blur + */ + public int getIterations() { + return iterations; + } + + /** + * {@inheritDoc} + */ + @Override + public BufferedImage filter(BufferedImage src, BufferedImage dst) { + int width = src.getWidth(); + int height = src.getHeight(); + + if (dst == null) { + dst = createCompatibleDestImage(src, null); + } + + int[] srcPixels = new int[width * height]; + int[] dstPixels = new int[width * height]; + + GraphicsUtilities.getPixels(src, 0, 0, width, height, srcPixels); + for (int i = 0; i < iterations; i++) { + // horizontal pass + FastBlurFilter.blur(srcPixels, dstPixels, width, height, radius); + // vertical pass + FastBlurFilter.blur(dstPixels, srcPixels, height, width, radius); + } + // the result is now stored in srcPixels due to the 2nd pass + GraphicsUtilities.setPixels(dst, 0, 0, width, height, srcPixels); + + return dst; + } +} diff --git a/src/main/java/org/jdesktop/swingx/image/package-info.java b/src/main/java/org/jdesktop/swingx/image/package-info.java new file mode 100644 index 0000000000..83344bfbe8 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/image/package-info.java @@ -0,0 +1,25 @@ +/* + * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +/** + * Contains set of useful filters for image operations like Gausian or Fast or Star Blur or Color Tint filter. + */ +package org.jdesktop.swingx.image; + diff --git a/src/main/java/org/jdesktop/swingx/multislider/AbstractMultiThumbModel.java b/src/main/java/org/jdesktop/swingx/multislider/AbstractMultiThumbModel.java new file mode 100644 index 0000000000..46ad640b00 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/multislider/AbstractMultiThumbModel.java @@ -0,0 +1,91 @@ +/* + * $Id: AbstractMultiThumbModel.java 3259 2009-02-17 21:06:19Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.multislider; + +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author jm158417 + */ +public abstract class AbstractMultiThumbModel implements MultiThumbModel { + /** Creates a new instance of AbstractMultiThumbModel */ + public AbstractMultiThumbModel() { + } + + protected float maximumValue = 1.0f; + protected float minimumValue = 0.0f; + + public float getMaximumValue() { + return maximumValue; + } + + public float getMinimumValue() { + return minimumValue; + } + + public void setMaximumValue(float maximumValue) { + this.maximumValue = maximumValue; + } + + public void setMinimumValue(float minimumValue) { + this.minimumValue = minimumValue; + } + + protected List thumbDataListeners = new ArrayList(); + + public void addThumbDataListener(ThumbDataListener listener) { + thumbDataListeners.add(listener); + } + + public void removeThumbDataListener(ThumbDataListener listener) { + thumbDataListeners.remove(listener); + } + + + + public void thumbPositionChanged(Thumb thumb) { + fireThumbPositionChanged(thumb); + } + + protected void fireThumbPositionChanged(Thumb thumb) { + if(getThumbIndex(thumb) >= 0) { + ThumbDataEvent evt = new ThumbDataEvent(this,-1,getThumbIndex(thumb),thumb); + for(ThumbDataListener l : thumbDataListeners) { + l.positionChanged(evt); + } + } + } + public void thumbValueChanged(Thumb thumb) { + fireThumbValueChanged(thumb); + } + + protected void fireThumbValueChanged(Thumb thumb) { + ThumbDataEvent evt = new ThumbDataEvent(this,-1,getThumbIndex(thumb),thumb); + for(ThumbDataListener l : thumbDataListeners) { + l.valueChanged(evt); + } + } + +} + diff --git a/src/main/java/org/jdesktop/swingx/multislider/DefaultMultiThumbModel.java b/src/main/java/org/jdesktop/swingx/multislider/DefaultMultiThumbModel.java new file mode 100644 index 0000000000..f6f3bfbbd0 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/multislider/DefaultMultiThumbModel.java @@ -0,0 +1,116 @@ +/* + * $Id: DefaultMultiThumbModel.java 3935 2011-03-02 19:06:41Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.multislider; + + +import java.util.*; + +/** + * + * @author joshy + */ +public class DefaultMultiThumbModel extends AbstractMultiThumbModel { + + protected List>thumbs = new ArrayList>(); + + /** Creates a new instance of DefaultMultiThumbModel */ + public DefaultMultiThumbModel() { + setMinimumValue(0.0f); + setMaximumValue(1.0f); + } + // returns the index of the newly added thumb + @Override + public int addThumb(float value, E obj) { + Thumb thumb = new Thumb(this); + thumb.setPosition(value); + thumb.setObject(obj); + thumbs.add(thumb); + int n = thumbs.size(); + ThumbDataEvent evt = new ThumbDataEvent(this,-1,thumbs.size()-1,thumb); + for(ThumbDataListener tdl : thumbDataListeners) { + tdl.thumbAdded(evt); + } + return n-1; + } + + @Override + public void insertThumb(float value, E obj, int index) { + Thumb thumb = new Thumb(this); + thumb.setPosition(value); + thumb.setObject(obj); + thumbs.add(index,thumb); + ThumbDataEvent evt = new ThumbDataEvent(this,-1,index,thumb); + for(ThumbDataListener tdl : thumbDataListeners) { + tdl.thumbAdded(evt); + } + } + + @Override + public void removeThumb(int index) { + Thumb thumb = thumbs.remove(index); + ThumbDataEvent evt = new ThumbDataEvent(this,-1,index,thumb); + for(ThumbDataListener tdl : thumbDataListeners) { + tdl.thumbRemoved(evt); + } + } + + @Override + public int getThumbCount() { + return thumbs.size(); + } + + @Override + public Thumb getThumbAt(int index) { + return thumbs.get(index); + } + + @Override + public List> getSortedThumbs() { + List> list = new ArrayList>(); + list.addAll(thumbs); + Collections.sort(list, new Comparator>() { + @Override + public int compare(Thumb o1, Thumb o2) { + float f1 = o1.getPosition(); + float f2 = o2.getPosition(); + if(f1f2) { + return 1; + } + return 0; + } + }); + return list; + } + + @Override + public Iterator> iterator() { + return thumbs.iterator(); + } + + @Override + public int getThumbIndex(Thumb thumb) { + return thumbs.indexOf(thumb); + } +} diff --git a/src/main/java/org/jdesktop/swingx/multislider/MultiThumbModel.java b/src/main/java/org/jdesktop/swingx/multislider/MultiThumbModel.java new file mode 100644 index 0000000000..debf826196 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/multislider/MultiThumbModel.java @@ -0,0 +1,50 @@ +/* + * $Id: MultiThumbModel.java 3259 2009-02-17 21:06:19Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.multislider; + + +import java.util.List; + +/** + * + * @author joshy + */ +public interface MultiThumbModel extends Iterable> { + + public float getMinimumValue(); + public void setMinimumValue(float minimumValue); + public float getMaximumValue(); + public void setMaximumValue(float maximumValue); + + public int addThumb(float value, E obj); + public void insertThumb(float value, E obj, int index); + public void removeThumb(int index); + public int getThumbCount(); + public Thumb getThumbAt(int index); + public int getThumbIndex(Thumb thumb); + public List> getSortedThumbs(); + public void thumbPositionChanged(Thumb thumb); + public void thumbValueChanged(Thumb thumb); + + public void addThumbDataListener(ThumbDataListener listener); + public void removeThumbDataListener(ThumbDataListener listener); +} diff --git a/src/main/java/org/jdesktop/swingx/multislider/Thumb.java b/src/main/java/org/jdesktop/swingx/multislider/Thumb.java new file mode 100644 index 0000000000..c5eb64bc0f --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/multislider/Thumb.java @@ -0,0 +1,55 @@ +/* + * $Id: Thumb.java 3259 2009-02-17 21:06:19Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.multislider; + +/** + * + * @author jm158417 + */ +public class Thumb { + private float position; + private E object; + private MultiThumbModel model; + + /** Creates a new instance of Thumb */ + public Thumb(MultiThumbModel model) { + this.model = model; + } + + public float getPosition() { + return position; + } + + public void setPosition(float position) { + this.position = position; + model.thumbPositionChanged(this); + } + + public E getObject() { + return object; + } + + public void setObject(E object) { + this.object = object; + model.thumbValueChanged(this); + } +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/multislider/ThumbDataEvent.java b/src/main/java/org/jdesktop/swingx/multislider/ThumbDataEvent.java new file mode 100644 index 0000000000..d3286339f5 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/multislider/ThumbDataEvent.java @@ -0,0 +1,57 @@ +/* + * $Id: ThumbDataEvent.java 3475 2009-08-28 08:30:47Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.multislider; + +import java.util.EventObject; + +/** + * + * @author jm158417 + */ +public class ThumbDataEvent extends EventObject { + private int type, index; + private Thumb thumb; + + /** Creates a new instance of ThumbDataEvent */ + public ThumbDataEvent(Object source, int type, int index, Thumb thumb) { + super(source); + this.type = type; + this.thumb = thumb; + this.index = index; + } + + public int getType() { + return type; + } + + public int getIndex() { + return index; + } + + public Thumb getThumb() { + return thumb; + } + + @Override + public String toString() { + return this.getClass().getName() + " : " + type + " " + index + " " + thumb; + } +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/multislider/ThumbDataListener.java b/src/main/java/org/jdesktop/swingx/multislider/ThumbDataListener.java new file mode 100644 index 0000000000..b66ab1718a --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/multislider/ThumbDataListener.java @@ -0,0 +1,33 @@ +/* + * $Id: ThumbDataListener.java 957 2006-03-29 02:44:44Z rbair $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.multislider; + +/** + * + * @author jm158417 + */ +public interface ThumbDataListener { + public void valueChanged(ThumbDataEvent e); + public void positionChanged(ThumbDataEvent e); + public void thumbAdded(ThumbDataEvent e); + public void thumbRemoved(ThumbDataEvent e); +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/multislider/ThumbListener.java b/src/main/java/org/jdesktop/swingx/multislider/ThumbListener.java new file mode 100644 index 0000000000..abca22996a --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/multislider/ThumbListener.java @@ -0,0 +1,29 @@ +/* + * $Id: ThumbListener.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.multislider; + +import java.awt.event.MouseEvent; + +public interface ThumbListener { + public void thumbMoved(int thumb, float pos); + public void thumbSelected(int thumb); + public void mousePressed(MouseEvent evt); +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/multislider/ThumbRenderer.java b/src/main/java/org/jdesktop/swingx/multislider/ThumbRenderer.java new file mode 100644 index 0000000000..446fdef02e --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/multislider/ThumbRenderer.java @@ -0,0 +1,29 @@ +/* + * $Id: ThumbRenderer.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.multislider; + +import org.jdesktop.swingx.JXMultiThumbSlider; + +import javax.swing.*; + +public interface ThumbRenderer { + public JComponent getThumbRendererComponent(JXMultiThumbSlider slider, int index, boolean selected); +} diff --git a/src/main/java/org/jdesktop/swingx/multislider/TrackRenderer.java b/src/main/java/org/jdesktop/swingx/multislider/TrackRenderer.java new file mode 100644 index 0000000000..e23000b29e --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/multislider/TrackRenderer.java @@ -0,0 +1,30 @@ +/* + * $Id: TrackRenderer.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.multislider; + +import org.jdesktop.swingx.JXMultiThumbSlider; + +import javax.swing.*; + + +public interface TrackRenderer { + public JComponent getRendererComponent(JXMultiThumbSlider slider); +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/multislider/package-info.java b/src/main/java/org/jdesktop/swingx/multislider/package-info.java new file mode 100644 index 0000000000..e7a4644f47 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/multislider/package-info.java @@ -0,0 +1,25 @@ +/* + * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +/** + * Contains classes used by slider classes, such as {@code JXMultiThumbSlider}. + */ +package org.jdesktop.swingx.multislider; + diff --git a/src/main/java/org/jdesktop/swingx/multisplitpane/DefaultSplitPaneModel.java b/src/main/java/org/jdesktop/swingx/multisplitpane/DefaultSplitPaneModel.java new file mode 100644 index 0000000000..4344e5db61 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/multisplitpane/DefaultSplitPaneModel.java @@ -0,0 +1,52 @@ +/* + * $Id: DefaultSplitPaneModel.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.multisplitpane; + +import org.jdesktop.swingx.MultiSplitLayout.Divider; +import org.jdesktop.swingx.MultiSplitLayout.Leaf; +import org.jdesktop.swingx.MultiSplitLayout.Split; +/** + * A simplified SplitPaneLayout for common split pane needs. A common multi splitpane + * need is: + * + * +-----------+-----------+ + * | | | + * | +-----------+ + * | | | + * +-----------+-----------+ + * + * @author rbair + */ +public class DefaultSplitPaneModel extends Split { + public static final String LEFT = "left"; + public static final String TOP = "top"; + public static final String BOTTOM = "bottom"; + + /** Creates a new instance of DefaultSplitPaneLayout */ + public DefaultSplitPaneModel() { + Split row = new Split(); + Split col = new Split(); + col.setRowLayout(false); + setChildren(new Leaf(LEFT), new Divider(), col); + col.setChildren(new Leaf(TOP), new Divider(), new Leaf(BOTTOM)); + } + +} diff --git a/src/main/java/org/jdesktop/swingx/multisplitpane/package-info.java b/src/main/java/org/jdesktop/swingx/multisplitpane/package-info.java new file mode 100644 index 0000000000..75b034d606 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/multisplitpane/package-info.java @@ -0,0 +1,24 @@ +/* + * $Id: package-info.java 3165 2009-01-02 13:26:07Z rah003 $ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +/** Contains support classes for the MultiSplitLayout layout manager. +*/ +package org.jdesktop.swingx.multisplitpane; \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/package-info.java b/src/main/java/org/jdesktop/swingx/package-info.java new file mode 100644 index 0000000000..bb9e62daee --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/package-info.java @@ -0,0 +1,144 @@ +/* + * $Id: package-info.java 3933 2011-03-02 19:02:29Z kschaefe $ + * + * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +/** + * Contains extensions to the Swing GUI toolkit, including new and enhanced + * components that provide functionality commonly required by rich, + * data-centric client applications. Many of these features will eventually + * be incorporated into the Swing toolkit, although API compatibility will + * not be guaranteed. + *

      + * + *

      New or Enhanced Functionality

      + * + *

      Auto-completion for TextFields and ComboBoxes

      + * + * For more information, see the + * AutoComplete documentation. + * + *

      Enhanced Rendering Support for Collection Components

      + * + *

      Built-In Search Support for Collection Components and JXEditorPane

      + * + *

      Login/Authentication Framework

      + * + *

      Painter-Enabled Components

      + * + * Components that use painters for background rendering alter the functionality + * of how {@link java.awt.Component#setBackground(java.awt.Color)} works. + * Setting the background color of a painter-enabled component effectively sets + * the background painter to paint the requested color. + *

      + * Look and Feel implementors should note that setting a + * {@link java.swing.plaf.UIResource} to {@code setBackground} will cause a + * {@code Painter} {@code UIResource} to be installed. This means that + * implementors should set the background before setting the painter as the last + * one set wins. + * + *

      New and Enhanced components

      + * + *

      Buttons and Labels

      + *
        + *
      • {@link org.jdesktop.swingx.JXButton} + *
      • {@link org.jdesktop.swingx.JXHyperlink Hyperlink} + *
      • {@link org.jdesktop.swingx.JXLabel} + *
      • {@link org.jdesktop.swingx.JXBusyLabel} + *
      • {@link org.jdesktop.swingx.JXRadioGroup} + *
      + * + * + *

      Collection Components

      + * + * These are sortable/filterable (with the exception of hierarchical + * components) with consistent and uniform SwingX rendering, highlighting, + * searching and rollover support. + *
        + *
      • {@link org.jdesktop.swingx.JXTable Table} uses the enhanced + * {@link org.jdesktop.swingx.JXTableHeader TableHeader} + *
      • {@link org.jdesktop.swingx.JXList List} - rollover and sort/filter + * functionality is disabled by default + *
      • {@link org.jdesktop.swingx.JXTree Tree} + *
      • {@link org.jdesktop.swingx.JXTreeTable TreeTable} - a new + * hierarchical component with support of tabular node properties + *
      + * + *

      Top-level Windows, General and Special Purpose Containers

      + *
        + *
      • Enhanced {@link org.jdesktop.swingx.JXFrame Frame} using an extended + * {@link org.jdesktop.swingx.JXRootPane RootPane RootPane} to support a + * {@link org.jdesktop.swingx.JXStatusBar StatusBar} + *
      • {@link org.jdesktop.swingx.JXDialog Dialog} + *
      • {@link org.jdesktop.swingx.JXPanel Panel} + *
      • {@link org.jdesktop.swingx.JXErrorPane ErrorPane} + *
      • {@link org.jdesktop.swingx.JXLoginPane LoginPane} + * + *
      • Search components: {@link org.jdesktop.swingx.JXFindBar FindBar} used + * for incremental search (similar to FireFox), + * {@link org.jdesktop.swingx.JXFindPanel FindPanel} used in a find dialog, + * and {@link org.jdesktop.swingx.JXSearchPanel SearchPanel} used for what + * was it? + *
      • Nested SplitPane {@link org.jdesktop.swingx.JXMultiSplitPane + * MultiSplitPane} + *
      • Vertical collapsing/expansion functionality is provided by a + * {@link org.jdesktop.swingx.JXCollapsiblePane CollapsiblePane}. A special + * purpose collapsible is the {@link org.jdesktop.swingx.JXTaskPane + * TaskPane} which typically is used to group buttons/hyperlinks which + * perform related tasks. A special + * {@link org.jdesktop.swingx.JXTaskPaneContainer TaskPaneContainer} is + * responsible for the layout of several TaskPanes. + *
      • Easily configurable {@link org.jdesktop.swingx.JXTipOfTheDay + * TipOfTheDay} + *
      • {@link org.jdesktop.swingx.JXTitledPanel TitledPanel} + * + *
      + * + *

      Miscellaneous Components

      + * + *
        + *
      • New calendar components: the {@link org.jdesktop.swingx.JXDatePicker + * DatePicker} allows to select a single Date and a + * {@link org.jdesktop.swingx.JXMonthView MonthView} showing the overview of + * one or more months. + * + *
      • {@link org.jdesktop.swingx.JXHeader Header} + *
      • {@link org.jdesktop.swingx.JXTitledSeparator TitledSeparator} + * + *
      • {@link org.jdesktop.swingx.JXColorSelectionButton} + *
      • {@link org.jdesktop.swingx.JXEditorPane} + *
      • {@link org.jdesktop.swingx.JXGradientChooser} + *
      • {@link org.jdesktop.swingx.JXGraph} + *
      • Image containers {@link org.jdesktop.swingx.JXImageView ImageView} + * and {@link org.jdesktop.swingx.JXImagePanel ImagePanel} (PENDING JW: + * merge/remove one?) + *
      • {@link org.jdesktop.swingx.JXMultiThumbSlider MultiThumbSlider} + * + *
      + * + *

      External Information Sources

      + * + * SwingX Twiki + * Change History + * SwingLabs User and + * Developer Discussion Forum + */ +package org.jdesktop.swingx; + diff --git a/src/main/java/org/jdesktop/swingx/painter/AbstractAreaPainter.java b/src/main/java/org/jdesktop/swingx/painter/AbstractAreaPainter.java new file mode 100644 index 0000000000..f53f541b45 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/painter/AbstractAreaPainter.java @@ -0,0 +1,267 @@ +/* + * $Id: AbstractAreaPainter.java 4082 2011-11-15 18:39:43Z kschaefe $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.painter; + +import org.jdesktop.swingx.painter.effects.AreaEffect; +import org.jdesktop.swingx.util.PaintUtils; + +import java.awt.*; + +/** + * The abstract base class for all painters that fill a vector path area. This + * includes Shapes, Rectangles, Text, and the MattePainter + * which fills in the entire background of a component. The defining + * feature of AbstractAreaPainter subclasses + * is that they implement the provideShape() method which returns + * the outline shape of the area that this + * painter will fill. Subclasses must implement the provideShape() method. + * + * The AbstractAreaPainter provides support for the following common painting properties + * + *
        + *
      • fillPaint
      • + *
      • paintStretched
      • + *
      • borderPaint
      • + *
      • borderWidth
      • + *
      • style
      • + *
      + * + * The AbstractAreaPainter also provides support for path effects like dropshadows and glows. + * + * @author joshua@marinacci.org + */ +@SuppressWarnings("nls") +public abstract class AbstractAreaPainter extends AbstractLayoutPainter { + + /** + * Different available fill styles. BOTH indicates that both the outline, + * and the fill should be painted. This is the default. FILLED indicates that + * the shape should be filled, but no outline painted. OUTLINE specifies that + * the shape should be outlined, but not filled. NONE indicates that neither + * the fill area nor the outline should be painted. + */ + public enum Style {BOTH, FILLED, OUTLINE, + /** + * {@code NONE} has different semantics that {@link AbstractAreaPainter#setVisible(boolean) + * setVisible(false)}. With {@code setVisible(false)}, nothing is painted. With style + * {@code NONE}, any {@link AreaEffect effects} are still painted. + */ + NONE} + + // controls if the paint should be stretched to fill the available area + private boolean stretchPaint; + + private AreaEffect[] areaEffects = new AreaEffect[0]; + + private Style style = Style.BOTH; + /** + * The stroke width to use when painting. If null, the default Stroke for + * the Graphics2D is used + */ + private float borderWidth; + + /** + * The paint to use when filling the shape + */ + private Paint fillPaint; + + /** + * The Paint to use when stroking the shape (drawing the outline). If null, + * then the component foreground color is used + */ + private Paint borderPaint; + + /** + * Creates a new instance of AbstractAreaPainter + */ + public AbstractAreaPainter() { + fillPaint = Color.RED; + } + + /** + * Creates a new instance of AbstractAreaPainter + * @param paint the default paint to fill this area painter with + */ + public AbstractAreaPainter(Paint paint) { + this.fillPaint = paint; + } + + /** + * Gets the current fill paint. This is the Paint object that will be used to fill the path area. + * @return Gets the Paint being used. May be null + */ + public Paint getFillPaint() { + return fillPaint; + } + + /** + * Sets the Paint to use. This is the Paint object that will be used to fill the path area. If null, nothing is painted + * @param p the Paint to use + */ + public void setFillPaint(Paint p) { + Paint old = getFillPaint(); + this.fillPaint = p; + setDirty(true); + firePropertyChange("fillPaint", old, getFillPaint()); + } + + /** + * Indicates if the paint will be snapped. This means that the paint will be scaled and aligned along the 4 axis of (horizontal, vertical, + * and both diagonals). Snapping allows the paint to be stretched across the component when it is drawn, even if the component is + * resized. This setting is only used for gradient paints. It will have no effect on Color or Texture paints. + * @return the current value of the snapPaint property + */ + public boolean isPaintStretched() { + return stretchPaint; + } + + /** + * Specifies whether this Painter should attempt to resize the Paint to fit the area being painted. + * For example, if true, then a gradient specified as (0, 0), (1, 0) would stretch horizontally such that + * the beginning of the gradient is on the left edge of the painted region, and the end of the gradient + * is at the right edge of the painted region. + * Specifically, if true, the resizePaint method will be called to perform the actual resizing of the Paint + * @param paintStretched true if the paint should be stretched, false otherwise. + */ + public void setPaintStretched(boolean paintStretched) { + boolean old = this.isPaintStretched(); + this.stretchPaint = paintStretched; + setDirty(true); + firePropertyChange("paintStretched", old, isPaintStretched()); + } + + /** + * The Paint to use for stroking the shape (painting the outline). + * Can be a Color, GradientPaint, TexturePaint, or any other kind of Paint. + * If null, the component foreground is used. + * + * @param p the Paint to use for stroking the shape. May be null. + */ + public void setBorderPaint(Paint p) { + Paint old = getBorderPaint(); + this.borderPaint = p; + setDirty(true); + firePropertyChange("borderPaint", old, getBorderPaint()); + } + + /** + * Gets the current Paint to use for stroking the shape (painting the outline). + * Can be a Color, GradientPaint, TexturePaint, or any other kind of Paint. + * If null, the component foreground is used. + * @return the Paint used when stroking the shape. May be null + */ + public Paint getBorderPaint() { + return borderPaint; + } + + /** + * The shape can be filled or simply stroked (outlined), or both or none. By default, + * the shape is both filled and stroked. This property specifies the strategy to + * use. + * @param s the Style to use. If null, Style.BOTH is used + */ + public void setStyle(Style s) { + Style old = getStyle(); + this.style = s == null ? Style.BOTH : s; + setDirty(true); + firePropertyChange("style", old, getStyle()); + } + + /** + * Gets the current Style. The shape can be filled or simply stroked (outlined), or both or none. By default, + * the shape is both filled and stroked. This property specifies the strategy to + * use. + * @return the Style used + */ + public Style getStyle() { + return style; + } + + /** + * Sets the border width to use for painting. If null, then the default Graphics2D + * stroke will be used. The stroke will be centered on the actual shape outline. + * @param s the Stroke to fillPaint with + */ + public void setBorderWidth(float s) { + float old = getBorderWidth(); + this.borderWidth = s; + setDirty(true); + firePropertyChange("borderWidth", old, getBorderWidth()); + } + + /** + * Gets the current border width. + * @return the Stroke to use for painting + */ + public float getBorderWidth() { + return borderWidth; + } + + /** + * Resizes the given Paint. By default, only Gradients, LinearGradients, and RadialGradients are resized + * in this method. If you have special resizing needs, override this method. This + * method is mainly used to make gradient paints resize with the component this + * painter is attached to. This method is internal to the painter api and should + * not be called elsewhere. It is used by the paintStretched property and + * painter subclasses. In the future it may be made public for use by other classes. + * If this happens it should probably be turned into a static utility method. + */ + Paint calculateSnappedPaint(Paint p, int width, int height) { + return PaintUtils.resizeGradient(p, width, height); + } + + /** + * Returns the outline shape of this painter. Subclasses must implement this method. This shape + * will be used for filling, stroking, and clipping. + * @return the outline shape of this painter + * @param g graphics + * @param comp The Object this painter will be painted on. + * @param width the width to paint + * @param height the height to paint + */ + protected abstract Shape provideShape(Graphics2D g, T comp, int width, int height); + + /** + * Sets the path effects to be drawn on this painter. Set this to null in order to remove all installed effects. + * @param areaEffects the effects to apply to this painter + */ + public void setAreaEffects(AreaEffect... areaEffects) { + AreaEffect[] old = getAreaEffects(); + this.areaEffects = new AreaEffect[areaEffects == null ? 0 : areaEffects.length]; + if (areaEffects != null) { + System.arraycopy(areaEffects, 0, this.areaEffects, 0, this.areaEffects.length); + } + setDirty(true); + firePropertyChange("areaEffects", old, getAreaEffects()); + } + + /** + * Gets the current set of path effects applied to this painter. Returned array is guarantied to be not null. + * @return the effects applied to this path painter + */ + public AreaEffect[] getAreaEffects() { + AreaEffect[] results = new AreaEffect[areaEffects.length]; + System.arraycopy(areaEffects, 0, results, 0, results.length); + return results; + } + +} diff --git a/src/main/java/org/jdesktop/swingx/painter/AbstractLayoutPainter.java b/src/main/java/org/jdesktop/swingx/painter/AbstractLayoutPainter.java new file mode 100644 index 0000000000..48cb37addb --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/painter/AbstractLayoutPainter.java @@ -0,0 +1,270 @@ +/* + * $Id: AbstractLayoutPainter.java 4079 2011-11-15 16:05:13Z kschaefe $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.painter; + +import java.awt.*; + +/** + * An abstract base class for any painter which can be positioned. This means + * the painter has some intrinsic size to what it is drawing and + * can be stretched or aligned both horizontally and vertically. + * + * + * The AbstractLayoutPainter class provides the following configuraable properties: + * + *
        + *
      • horizonalAlignment - the horizonal alignment (left, center, and right)
      • + *
      • verticalAlignment - the verticalAlignment alignment (top, center, and bottom)
      • + *
      • fillHorizontal - indicates if the painter should stretch to fill the available space horizontally
      • + *
      • fillVertical - indicates if the painter should stretch to fill the available space vertically
      • + *
      • insets - whitespace on the top, bottom, left, and right. + *
      + * + * By combining these five properties any AbstractLayoutPainter subclass can position it's content + * within the paintable area. For example, an ImagePainter has an intrinsic size based on the image + * it is painting. If you wanted to paint the image in the lower right hand corner of the paintable + * area, but inset by 5 pixels, you could do the following: + * + *
      
      + *     ImagePainter p = new ImagePainter(null);
      + *     p.setVerticalAlignment(AbstractLayoutPainter.VerticalAlignment.BOTTOM);
      + *     p.setHorizontalAlignment(AbstractLayoutPainter.HorizontalAlignment.RIGHT);
      + *     p.setInsets(new Insets(0,0,5,5));
      + * 
      + * + * + * For something which is resizable, like a RectanglePainter, you can use the fill properties + * to make it resize along with the paintable area. For example, to make a rectangle with 20 px + * rounded corners, and which resizes with the paintable area but is inset + * by 10 pixels on all sides, you could do + * the following: + * + *
      
      + *     RectanglePainter p = new RectanglePainter();
      + *     p.setRoundHeight(20);
      + *     p.setRoundWidth(20);
      + *     p.setInsets(new Insets(10,10,10,10));
      + *     p.setFillHorizontal(true);
      + *     p.setFillVertical(true);
      + * 
      + * + * + * @author joshua@marinacci.org + */ +@SuppressWarnings("nls") +public abstract class AbstractLayoutPainter extends AbstractPainter { + + /** + * Specifies how to draw the image, i.e. what kind of Style to use + * when drawing + */ + private VerticalAlignment verticalAlignment = VerticalAlignment.CENTER; + private HorizontalAlignment horizontalAlignment = HorizontalAlignment.CENTER; + private Insets insets = new Insets(0,0,0,0); + private boolean fillVertical = false; + private boolean fillHorizontal = false; + + /** + * Creates a new instance of AbstractLayoutPainter + */ + public AbstractLayoutPainter() { + } + + /** + * An enum which controls horizontalAlignment alignment + */ + public static enum HorizontalAlignment { LEFT, CENTER, RIGHT } + + /** + * An enum which controls verticalAlignment alignment + */ + public static enum VerticalAlignment { TOP, CENTER, BOTTOM } + + /** + * Gets the current horizontalAlignment alignment. + * + * @return the current horizontalAlignment alignment + */ + public HorizontalAlignment getHorizontalAlignment() { + return horizontalAlignment; + } + + /** + * Gets the current whitespace insets. + * @return the current insets + */ + public Insets getInsets() { + return insets; + } + + /** + * gets the current verticalAlignment alignment + * + * @return current verticalAlignment alignment + */ + public VerticalAlignment getVerticalAlignment() { + return verticalAlignment; + } + + /** + * indicates if the painter content is stretched horizontally + * + * @return the current horizontalAlignment stretch value + */ + public boolean isFillHorizontal() { + return fillHorizontal; + } + + /** + * indicates if the painter content is stretched vertically + * + * @return the current verticalAlignment stretch value + */ + public boolean isFillVertical() { + return fillVertical; + } + + /** + * Sets a new horizontalAlignment alignment. Used to position the content at the left, right, or center. + * + * @param horizontal new horizontalAlignment alignment + */ + public void setHorizontalAlignment(HorizontalAlignment horizontal) { + HorizontalAlignment old = this.getHorizontalAlignment(); + this.horizontalAlignment = horizontal; + setDirty(true); + firePropertyChange("horizontalAlignment", old, getHorizontalAlignment()); + } + + /** + * Sets if the content should be stretched horizontally to fill all available horizontalAlignment + * space (minus the left and right insets). + * + * @param fillHorizontal new horizontal stretch value + */ + public void setFillHorizontal(boolean fillHorizontal) { + boolean old = this.isFillHorizontal(); + this.fillHorizontal = fillHorizontal; + setDirty(true); + firePropertyChange("fillHorizontal", old, isFillHorizontal()); + } + + /** + * Sets the current whitespace insets. + * @param insets new insets + */ + public void setInsets(Insets insets) { + Insets old = this.getInsets(); + this.insets = insets; + setDirty(true); + firePropertyChange("insets", old, getInsets()); + } + + /** + * Sets a new verticalAlignment alignment. Used to position the content at the top, bottom, or center. + * + * @param vertical new verticalAlignment alignment + */ + public void setVerticalAlignment(VerticalAlignment vertical) { + VerticalAlignment old = this.getVerticalAlignment(); + this.verticalAlignment = vertical; + setDirty(true); + firePropertyChange("verticalAlignment", old, getVerticalAlignment()); + } + + /** + * Sets if the content should be stretched vertically to fill all available verticalAlignment + * space (minus the top and bottom insets). + * + * @param verticalStretch new verticalAlignment stretch value + */ + public void setFillVertical(boolean verticalStretch) { + boolean old = this.isFillVertical(); + this.fillVertical = verticalStretch; + setDirty(true); + firePropertyChange("fillVertical", old, isFillVertical()); + } + + /** + * A protected method used by subclasses to calculate the final position of the + * content. This will position the content using the fillHorizontal, fillVertical + * horizontalAlignment, and verticalAlignment properties. This method + * is typically called by subclasses in their doPaint() methods. + * + * @param contentWidth The width of the content to be painted + * @param contentHeight The height of the content to be painted + * @param width the width of the area that the content will be positioned in + * @param height the height of the area that the content will be positioned in + * @return the rectangle for the content to be painted in + */ + protected final Rectangle calculateLayout(final int contentWidth, final int contentHeight, + final int width, final int height) { + + Rectangle rect = new Rectangle(); + rect.width = contentWidth; + rect.height = contentHeight; + + if(isFillHorizontal()) { + rect.width = width - insets.left - insets.right; + } + + if(isFillVertical()) { + rect.height = height - insets.top - insets.bottom; + } + rect.x = calculateX(rect.width, width); + rect.y = calculateY(rect.height, height); + return rect; + } + + private int calculateY(final int imgHeight, final int height) { + int y = 0; + if(getVerticalAlignment() == VerticalAlignment.TOP) { + y = 0; + y+= insets.top; + } + if(getVerticalAlignment() == VerticalAlignment.CENTER) { + y = (height-imgHeight)/2; + y += insets.top; + } + if(getVerticalAlignment() == VerticalAlignment.BOTTOM) { + y = height-imgHeight; + y-= insets.bottom; + } + return y; + } + + private int calculateX(final int imgWidth, final int width) { + int x = 0; + if(getHorizontalAlignment() == HorizontalAlignment.LEFT) { + x = 0; + x+= insets.left; + } + if(getHorizontalAlignment() == HorizontalAlignment.CENTER) { + x = (width-imgWidth)/2; + x += insets.left; + } + if(getHorizontalAlignment() == HorizontalAlignment.RIGHT) { + x = width-imgWidth; + x-= insets.right; + } + return x; + } +} diff --git a/src/main/java/org/jdesktop/swingx/painter/AbstractPainter.java b/src/main/java/org/jdesktop/swingx/painter/AbstractPainter.java new file mode 100644 index 0000000000..58751cf343 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/painter/AbstractPainter.java @@ -0,0 +1,438 @@ +/* + * $Id: AbstractPainter.java 4082 2011-11-15 18:39:43Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.painter; + +import org.jdesktop.beans.AbstractBean; +import org.jdesktop.swingx.util.GraphicsUtilities; + +import java.awt.*; +import java.awt.image.BufferedImage; +import java.awt.image.BufferedImageOp; +import java.lang.ref.SoftReference; + +/** + *

      A convenient base class from which concrete {@link Painter} implementations may + * extend. It extends {@link AbstractBean} as a convenience for + * adding property change notification support. In addition, AbstractPainter + * provides subclasses with the ability to cacheable painting operations, configure the + * drawing surface with common settings (such as antialiasing and interpolation), and + * toggle whether a subclass paints or not via the visibility property.

      + * + *

      Subclasses of AbstractPainter generally need only override the + * {@link #doPaint(Graphics2D, Object, int, int)} method. If a subclass requires more control + * over whether caching is enabled, or for configuring the graphics state, then it + * may override the appropriate protected methods to interpose its own behavior.

      + * + *

      For example, here is the doPaint method of a simple Painter that + * paints an opaque rectangle: + *

      
      + *  public void doPaint(Graphics2D g, T obj, int width, int height) {
      + *      g.setPaint(Color.BLUE);
      + *      g.fillRect(0, 0, width, height);
      + *  }
      + * 

      + * + * @author rbair + */ +@SuppressWarnings("nls") +public abstract class AbstractPainter extends AbstractBean implements Painter { + /** + * An enum representing the possible interpolation values of Bicubic, Bilinear, and + * Nearest Neighbor. These map to the underlying RenderingHints, + * but are easier to use and serialization safe. + */ + public enum Interpolation { + /** + * use bicubic interpolation + */ + Bicubic(RenderingHints.VALUE_INTERPOLATION_BICUBIC), + /** + * use bilinear interpolation + */ + Bilinear(RenderingHints.VALUE_INTERPOLATION_BILINEAR), + /** + * use nearest neighbor interpolation + */ + NearestNeighbor(RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); + + private Object value; + + Interpolation(Object value) { + this.value = value; + } + + private void configureGraphics(Graphics2D g) { + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, value); + } + } + + //--------------------------------------------------- Instance Variables + /** + * The cached image, if shouldUseCache() returns true + */ + private transient SoftReference cachedImage; + private boolean cacheCleared = true; + private boolean cacheable = false; + private boolean dirty = false; + private BufferedImageOp[] filters = new BufferedImageOp[0]; + private boolean antialiasing = true; + private Interpolation interpolation = Interpolation.NearestNeighbor; + private boolean visible = true; + private boolean inPaintContext; + + /** + * Creates a new instance of AbstractPainter. + */ + public AbstractPainter() { } + + /** + * Creates a new instance of AbstractPainter. + * @param cacheable indicates if this painter should be cacheable + */ + public AbstractPainter(boolean cacheable) { + setCacheable(cacheable); + } + + /** + * A defensive copy of the Effects to apply to the results + * of the AbstractPainter's painting operation. The array may + * be empty but it will never be null. + * @return the array of filters applied to this painter + */ + public final BufferedImageOp[] getFilters() { + BufferedImageOp[] results = new BufferedImageOp[filters.length]; + System.arraycopy(filters, 0, results, 0, results.length); + return results; + } + + /** + *

      A convenience method for specifying the filters to use based on + * BufferedImageOps. These will each be individually wrapped by an ImageFilter + * and then setFilters(Effect... filters) will be called with the resulting + * array

      + * + * + * @param effects the BufferedImageOps to wrap as filters + */ + public void setFilters(BufferedImageOp ... effects) { + if (effects == null) effects = new BufferedImageOp[0]; + BufferedImageOp[] old = getFilters(); + this.filters = new BufferedImageOp[effects.length]; + System.arraycopy(effects, 0, this.filters, 0, this.filters.length); + setDirty(true); + firePropertyChange("filters", old, getFilters()); + } + + /** + * Returns if antialiasing is turned on or not. The default value is true. + * This is a bound property. + * @return the current antialiasing setting + */ + public boolean isAntialiasing() { + return antialiasing; + } + /** + * Sets the antialiasing setting. This is a bound property. + * @param value the new antialiasing setting + */ + public void setAntialiasing(boolean value) { + boolean old = isAntialiasing(); + antialiasing = value; + if (old != value) setDirty(true); + firePropertyChange("antialiasing", old, isAntialiasing()); + } + + /** + * Gets the current interpolation setting. This property determines if interpolation will + * be used when drawing scaled images. @see java.awt.RenderingHints.KEY_INTERPOLATION. + * @return the current interpolation setting + */ + public Interpolation getInterpolation() { + return interpolation; + } + + /** + * Sets a new value for the interpolation setting. This setting determines if interpolation + * should be used when drawing scaled images. @see java.awt.RenderingHints.KEY_INTERPOLATION. + * @param value the new interpolation setting + */ + public void setInterpolation(Interpolation value) { + Object old = getInterpolation(); + this.interpolation = value == null ? Interpolation.NearestNeighbor : value; + if (old != value) setDirty(true); + firePropertyChange("interpolation", old, getInterpolation()); + } + + /** + * Gets the visible property. This controls if the painter should + * paint itself. It is true by default. Setting visible to false + * is good when you want to temporarily turn off a painter. An example + * of this is a painter that you only use when a button is highlighted. + * + * @return current value of visible property + */ + public boolean isVisible() { + return this.visible; + } + + /** + *

      Sets the visible property. This controls if the painter should + * paint itself. It is true by default. Setting visible to false + * is good when you want to temporarily turn off a painter. An example + * of this is a painter that you only use when a button is highlighted.

      + * + * @param visible New value of visible property. + */ + public void setVisible(boolean visible) { + boolean old = isVisible(); + this.visible = visible; + if (old != visible) setDirty(true); //not the most efficient, but I must do this otherwise a CompoundPainter + //or other aggregate painter won't know that it is now invalid + //there might be a tricky solution but that is a performance optimization + firePropertyChange("visible", old, isVisible()); + } + + /** + *

      Gets whether this AbstractPainter can be cached as an image. + * If caching is enabled, then it is the responsibility of the developer to + * invalidate the painter (via {@link #clearCache}) if external state has + * changed in such a way that the painter is invalidated and needs to be + * repainted.

      + * + * @return whether this is cacheable + */ + public boolean isCacheable() { + return cacheable; + } + + /** + *

      Sets whether this AbstractPainter can be cached as an image. + * If true, this is treated as a hint. That is, a cacheable may or may not be used. + * The {@link #shouldUseCache} method actually determines whether the cacheable is used. + * However, if false, then this is treated as an absolute value. That is, no + * cacheable will be used.

      + * + *

      If set to false, then #clearCache is called to free system resources.

      + * + * @param cacheable + */ + public void setCacheable(boolean cacheable) { + boolean old = isCacheable(); + this.cacheable = cacheable; + firePropertyChange("cacheable", old, isCacheable()); + if (!isCacheable()) { + clearCache(); + } + } + + /** + *

      Call this method to clear the cacheable. This may be called whether there is + * a cacheable being used or not. If cleared, on the next call to paint, + * the painting routines will be called.

      + * + *

      SubclassesIf overridden in subclasses, you + * must call super.clearCache, or physical + * resources (such as an Image) may leak.

      + */ + public void clearCache() { + BufferedImage cache = cachedImage == null ? null : cachedImage.get(); + if (cache != null) { + cache.flush(); + } + cacheCleared = true; + if (!isCacheable()) { + cachedImage = null; + } + } + + /** + * Only made package private for testing. Don't call this method outside + * of this class! This is NOT a bound property + */ + boolean isCacheCleared() { + return cacheCleared; + } + + /** + *

      Called to allow Painter subclasses a chance to see if any state + * in the given object has changed from the last paint operation. If it has, then + * the Painter has a chance to mark itself as dirty, thus causing a + * repaint, even if cached.

      + * + * @param object + */ + protected void validate(T object) { } + + /** + * Ye olde dirty bit. If true, then the painter is considered dirty and in need of + * being repainted. This is a bound property. + * + * @return true if the painter state has changed and the painter needs to be + * repainted. + */ + protected boolean isDirty() { + return dirty; + } + + /** + * Sets the dirty bit. If true, then the painter is considered dirty, and the cache + * will be cleared. This property is bound. + * + * @param d whether this Painter is dirty. + */ + protected void setDirty(boolean d) { + boolean old = isDirty(); + this.dirty = d; + firePropertyChange("dirty", old, isDirty()); + if (isDirty()) { + clearCache(); + } + } + + boolean isInPaintContext() { + return inPaintContext; + } + + void setInPaintContext(boolean inPaintContext) { + this.inPaintContext = inPaintContext; + } + + /** + *

      Returns true if the painter should use caching. This method allows subclasses to + * specify the heuristics regarding whether to cache or not. If a Painter + * has intelligent rules regarding painting times, and can more accurately indicate + * whether it should be cached, it could implement that logic in this method.

      + * + * @return whether or not a cache should be used + */ + protected boolean shouldUseCache() { + return isCacheable() || filters.length > 0; //NOTE, I can only do this because getFilters() is final + } + + /** + *

      This method is called by the paint method prior to + * any drawing operations to configure the drawing surface. The default + * implementation sets the rendering hints that have been specified for + * this AbstractPainter.

      + * + *

      This method can be overridden by subclasses to modify the drawing + * surface before any painting happens.

      + * + * @param g the graphics surface to configure. This will never be null. + * @see #paint(Graphics2D, Object, int, int) + */ + protected void configureGraphics(Graphics2D g) { + //configure antialiasing + if(isAntialiasing()) { + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + } else { + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_OFF); + } + + getInterpolation().configureGraphics(g); + } + + /** + * Subclasses must implement this method and perform custom painting operations + * here. + * @param width + * @param height + * @param g The Graphics2D object in which to paint + * @param object + */ + protected abstract void doPaint(Graphics2D g, T object, int width, int height); + + /** + * @inheritDoc + */ + @Override + public final void paint(Graphics2D g, T obj, int width, int height) { + if (g == null) { + throw new NullPointerException("The Graphics2D must be supplied"); + } + + if(!isVisible() || width < 1 || height < 1) { + return; + } + + configureGraphics(g); + + //paint to a temporary image if I'm caching, or if there are filters to apply + if (shouldUseCache() || filters.length > 0) { + validate(obj); + BufferedImage cache = cachedImage == null ? null : cachedImage.get(); + boolean invalidCache = null == cache || + cache.getWidth() != width || + cache.getHeight() != height; + + if (cacheCleared || invalidCache || isDirty()) { + //rebuild the cacheable. I do this both if a cacheable is needed, and if any + //filters exist. I only *save* the resulting image if caching is turned on + if (invalidCache) { + cache = GraphicsUtilities.createCompatibleTranslucentImage(width, height); + } + Graphics2D gfx = cache.createGraphics(); + + try { + gfx.setClip(0, 0, width, height); + + if (!invalidCache) { + // If we are doing a repaint, but we didn't have to + // recreate the image, we need to clear it back + // to a fully transparent background. + Composite composite = gfx.getComposite(); + gfx.setComposite(AlphaComposite.Clear); + gfx.fillRect(0, 0, width, height); + gfx.setComposite(composite); + } + + configureGraphics(gfx); + doPaint(gfx, obj, width, height); + } finally { + gfx.dispose(); + } + + if (!isInPaintContext()) { + for (BufferedImageOp f : getFilters()) { + cache = f.filter(cache, null); + } + } + + //only save the temporary image as the cacheable if I'm caching + if (shouldUseCache()) { + cachedImage = new SoftReference(cache); + cacheCleared = false; + } + } + + g.drawImage(cache, 0, 0, null); + } else { + //can't use the cacheable, so just paint + doPaint(g, obj, width, height); + } + + //painting has occured, so restore the dirty bit to false + setDirty(false); + } +} diff --git a/src/main/java/org/jdesktop/swingx/painter/AlphaPainter.java b/src/main/java/org/jdesktop/swingx/painter/AlphaPainter.java new file mode 100644 index 0000000000..f3dd03cd93 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/painter/AlphaPainter.java @@ -0,0 +1,102 @@ +/* + * $Id: AlphaPainter.java 4147 2012-02-01 17:13:24Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +package org.jdesktop.swingx.painter; + +import org.jdesktop.beans.JavaBean; + +import java.awt.*; + +/** + * Applies an alpha value to an entire stack of painters. + * + * @author joshy + */ +@JavaBean +@SuppressWarnings("nls") +public class AlphaPainter extends CompoundPainter { + private float alpha = 1.0f; + + /** + * {@inheritDoc} + */ + @Override + protected void doPaint(Graphics2D g, T component, int width, int height) { + Graphics2D g2 = (Graphics2D) g.create(); + + try { + if(getTransform() != null) { + g2.setTransform(getTransform()); + } + if(alpha < 1) { + g2.setComposite(AlphaComposite.getInstance( + AlphaComposite.SRC_OVER, alpha)); + } + + super.doPaint(g2, component, width, height); + } finally { + g2.dispose(); + } + } + /* + public static void main(String ... args) { + JXPanel panel = new JXPanel(); + AlphaPainter alpha = new AlphaPainter(); + alpha.setAlpha(1f); + alpha.setPainters(new PinstripePainter(new Color(255,255,255,125),45,20,20)); + + panel.setBackgroundPainter(new CompoundPainter( + new MattePainter(Color.RED), + alpha + )); + + JFrame frame = new JFrame(); + frame.add(panel); + frame.pack(); + frame.setSize(200,200); + frame.setVisible(true); + }*/ + + /** + * Returns the current alpha value for this painter. This is the alpha value that will be applied + * to all painters set inside this painter. Alpha values will be multiplied. This means if you set an + * alpha of 0.5 on this painter and you nest a painter inside which uses an alpha of 0.5 then the final + * pixels drawn will have an alpha of 0.25. + * @return the current value of alpha property + */ + public float getAlpha() { + return alpha; + } + + /** + * Sets the current alpha value for this painter. This is the alpha value that will be applied + * to all painters set inside this painter. Alpha values will be multiplied. This means if you set an + * alpha of 0.5 on this painter and you nest a painter inside which uses an alpha of 0.5 then the final + * pixels drawn will have an alpha of 0.25. + * @param alpha the new value of the alpha property + */ + public void setAlpha(float alpha) { + float old = getAlpha(); + this.alpha = alpha; + firePropertyChange("alpha", old, getAlpha()); + } +} diff --git a/src/main/java/org/jdesktop/swingx/painter/BusyPainter.java b/src/main/java/org/jdesktop/swingx/painter/BusyPainter.java new file mode 100644 index 0000000000..cb11c6fd3e --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/painter/BusyPainter.java @@ -0,0 +1,622 @@ +/* + * $Id: BusyPainter.java 4156 2012-02-02 19:54:38Z kschaefe $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.painter; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.util.PaintUtils; + +import java.awt.*; +import java.awt.geom.Ellipse2D; +import java.awt.geom.PathIterator; +import java.awt.geom.Point2D.Float; +import java.awt.geom.RoundRectangle2D; +import java.util.ArrayList; +import java.util.List; +import java.util.NoSuchElementException; + +/** + * A specific painter that paints an "infinite progress" like animation. + */ +@JavaBean +@SuppressWarnings("nls") +public class BusyPainter extends AbstractPainter { + + /** + * Direction is used to set the initial direction in which the + * animation starts. + * + * @see BusyPainter#setDirection(Direction) + */ + public static enum Direction { + /** + * cycle proceeds forward + */ + RIGHT, + /** cycle proceeds backward */ + LEFT, + } + + private int frame = -1; + + private int points = 8; + + private Color baseColor = new Color(200, 200, 200); + + private Color highlightColor = Color.BLACK; + + private int trailLength = 4; + + private Shape pointShape; + + private Shape trajectory; + + private Direction direction = Direction.RIGHT; + + private boolean paintCentered; + + /** + * Creates new busy painter initialized to the shape of circle and bounds size 26x26 points. + */ + public BusyPainter() { + this(26); + } + + /** + * Creates new painter initialized to the shape of circle and bounds of square of specified height. + * @param height Painter height. + */ + public BusyPainter(int height) { + this(getScaledDefaultPoint(height), getScaledDefaultTrajectory(height)); + } + + /** + * Initializes painter to the specified trajectory and and point shape. Bounds are dynamically calculated to so the specified trajectory fits in. + * @param point Point shape. + * @param trajectory Trajectory shape. + */ + public BusyPainter(Shape point, Shape trajectory) { + init(point, trajectory, Color.LIGHT_GRAY, Color.BLACK); + } + + protected static Shape getScaledDefaultTrajectory(int height) { + return new Ellipse2D.Float(((height * 8) / 26) / 2, ((height * 8) / 26) / 2, height + - ((height * 8) / 26), height - ((height * 8) / 26)); + } + + protected static Shape getScaledDefaultPoint(int height) { + return new RoundRectangle2D.Float(0, 0, (height * 8) / 26, 4, + 4, 4); + } + + /** + * Initializes painter to provided shapes and default colors. + * @param point Point shape. + * @param trajectory Trajectory shape. + */ + protected void init(Shape point, Shape trajectory, Color baseColor, Color highlightColor) { + this.baseColor = baseColor; + this.highlightColor = highlightColor; + this.pointShape = point; + this.trajectory = trajectory; + } + + /** + * @inheritDoc + */ + @Override + protected void doPaint(Graphics2D g, Object t, int width, int height) { + Rectangle r = getTrajectory().getBounds(); + int tw = width - r.width - 2*r.x; + int th = height - r.height - 2*r.y; + if (isPaintCentered()) { + g.translate(tw/2, th/2); + } + + PathIterator pi = trajectory.getPathIterator(null); + float[] coords = new float[6]; + Float cp = new Float(); + Float sp = new Float(); + int ret; + float totalDist = 0; + List segStack = new ArrayList(); + do { + try { + ret = pi.currentSegment(coords); + } catch (NoSuchElementException e) { + // invalid object definition - one of the bounds is zero or less + return; + } + if (ret == PathIterator.SEG_LINETO || (ret == PathIterator.SEG_CLOSE && (sp.x != cp.x || sp.y != cp.y))) { + //close by line + float c = calcLine(coords, cp); + totalDist += c; + // move the point to the end (just so it is same for all of them + segStack.add(new float[] { c, 0, 0, 0, 0, coords[0], coords[1], ret }); + cp.x = coords[0]; + cp.y = coords[1]; + } + if (ret == PathIterator.SEG_MOVETO) { + sp.x = cp.x = coords[0]; + sp.y = cp.y = coords[1]; + + } + if (ret == PathIterator.SEG_CUBICTO) { + float c = calcCube(coords, cp); + totalDist += c; + segStack.add(new float[] { c, coords[0], coords[1], coords[2], + coords[3], coords[4], coords[5], ret }); + cp.x = coords[4]; + cp.y = coords[5]; + } + if (ret == PathIterator.SEG_QUADTO) { + float c = calcLengthOfQuad(coords, cp); + totalDist += c; + segStack.add(new float[] { c, coords[0], coords[1], 0 ,0 , coords[2], + coords[3], ret }); + cp.x = coords[2]; + cp.y = coords[3]; + } + // got a starting point, center point on it. + pi.next(); + } while (!pi.isDone()); + float nxtP = totalDist / getPoints(); + List pList = new ArrayList(); + pList.add(new Float(sp.x, sp.y)); + int sgIdx = 0; + float[] sgmt = segStack.get(sgIdx); + float len = sgmt[0]; + float travDist = nxtP; + Float center = new Float(sp.x, sp.y); + for (int i = 1; i < getPoints(); i++) { + while (len < nxtP) { + sgIdx++; + // Be carefull when messing around with points. + sp.x = sgmt[5]; + sp.y = sgmt[6]; + sgmt = segStack.get(sgIdx); + travDist = nxtP - len; + len += sgmt[0]; + } + len -= nxtP; + Float p = calcPoint(travDist, sp, sgmt, width, height); + pList.add(p); + center.x += p.x; + center.y += p.y; + travDist += nxtP; + } + // calculate center + center.x = ((float) width) / 2; + center.y = ((float) height) / 2; + + // draw the stuff + int i = 0; + g.translate(center.x, center.y); + for (Float p : pList) { + drawAt(g, i++, p, center); + } + g.translate(-center.x, -center.y); + + if (isPaintCentered()) { + g.translate(-tw/2, -th/2); + } + } + + /** + * Gets value of centering hint. If true, shape will be positioned in the center of painted area. + * @return Whether shape will be centered over painting area or not. + */ + public boolean isPaintCentered() { + return this.paintCentered; + } + + /** + * Centers shape in the area covered by the painter. + * @param paintCentered Centering hint. + */ + public void setPaintCentered(boolean paintCentered) { + boolean old = isPaintCentered(); + this.paintCentered = paintCentered; + firePropertyChange("paintCentered", old, isPaintCentered()); + } + + private void drawAt(Graphics2D g, int i, Float p, Float c) { + g.setColor(calcFrameColor(i)); + paintRotatedCenteredShapeAtPoint(p, c, g); + } + + private void paintRotatedCenteredShapeAtPoint(Float p, Float c, Graphics2D g) { + Shape s = getPointShape(); + double hh = s.getBounds().getHeight() / 2; + double wh = s.getBounds().getWidth() / 2; + double t, x, y; + double a = c.y - p.y; + double b = p.x - c.x; + double sa = Math.signum(a); + double sb = Math.signum(b); + sa = sa == 0 ? 1 : sa; + sb = sb == 0 ? 1 : sb; + a = Math.abs(a); + b = Math.abs(b); + t = Math.atan(a / b); + t = sa > 0 ? sb > 0 ? -t : -Math.PI + t : sb > 0 ? t : Math.PI - t; + x = Math.sqrt(a * a + b * b) - wh; + y = -hh; + g.rotate(t); + g.translate(x, y); + g.fill(s); + g.translate(-x, -y); + g.rotate(-t); + + } + + private Float calcPoint(float dist2go, Float startPoint, + float[] sgmt, int w, int h) { + Float f = new Float(); + if (sgmt[7] == PathIterator.SEG_LINETO) { + // linear + float a = sgmt[5] - startPoint.x; + float b = sgmt[6] - startPoint.y; + float pathLen = sgmt[0]; + f.x = startPoint.x + a * dist2go / pathLen; + f.y = startPoint.y + b * dist2go / pathLen; + } else if (sgmt[7] == PathIterator.SEG_QUADTO) { + // quadratic curve + Float ctrl = new Float(sgmt[1]/w, sgmt[2]/h); + Float end = new Float(sgmt[5]/w, sgmt[6]/h); + Float start = new Float(startPoint.x/w, startPoint.y/h); + + // trans coords from abs to rel + f = getXY(dist2go / sgmt[0], start, ctrl, end); + f.x *= w; + f.y *= h; + + } else if (sgmt[7] == PathIterator.SEG_CUBICTO) { + // bezier curve + float x = Math.abs(startPoint.x - sgmt[5]); + float y = Math.abs(startPoint.y - sgmt[6]); + + // trans coords from abs to rel + float c1rx = Math.abs(startPoint.x - sgmt[1]) / x; + float c1ry = Math.abs(startPoint.y - sgmt[2]) / y; + float c2rx = Math.abs(startPoint.x - sgmt[3]) / x; + float c2ry = Math.abs(startPoint.y - sgmt[4]) / y; + f = getXY(dist2go / sgmt[0], c1rx, c1ry, c2rx, c2ry); + + float a = startPoint.x - sgmt[5]; + float b = startPoint.y - sgmt[6]; + + f.x = startPoint.x - f.x * a; + f.y = startPoint.y - f.y * b; + } + return f; + } + + + /** + * Calculates length of the linear segment. + * @param coords Segment coordinates. + * @param cp Start point. + * @return Length of the segment. + */ + private float calcLine(float[] coords, Float cp) { + float a = cp.x - coords[0]; + float b = cp.y - coords[1]; + float c = (float) Math.sqrt(a * a + b * b); + return c; + } + + /** + * Claclulates length of the cubic segment. + * @param coords Segment coordinates. + * @param cp Start point. + * @return Length of the segment. + */ + private float calcCube(float[] coords, Float cp) { + float x = Math.abs(cp.x - coords[4]); + float y = Math.abs(cp.y - coords[5]); + + // trans coords from abs to rel + float c1rx = Math.abs(cp.x - coords[0]) / x; + float c1ry = Math.abs(cp.y - coords[1]) / y; + float c2rx = Math.abs(cp.x - coords[2]) / x; + float c2ry = Math.abs(cp.y - coords[3]) / y; + float prevLength = 0, prevX = 0, prevY = 0; + for (float t = 0.01f; t <= 1.0f; t += .01f) { + Float xy = getXY(t, c1rx, c1ry, c2rx, c2ry); + prevLength += (float) Math.sqrt((xy.x - prevX) * (xy.x - prevX) + + (xy.y - prevY) * (xy.y - prevY)); + prevX = xy.x; + prevY = xy.y; + } + // prev len is a fraction num of the real path length + float z = ((Math.abs(x) + Math.abs(y)) / 2) * prevLength; + return z; + } + + /** + * Calculates length of the quadratic segment + * @param coords Segment coordinates + * @param cp Start point. + * @return Length of the segment. + */ + private float calcLengthOfQuad(float[] coords, Float cp) { + Float ctrl = new Float(coords[0], coords[1]); + Float end = new Float(coords[2], coords[3]); + // get abs values + // ctrl1 + float c1ax = Math.abs(cp.x - ctrl.x) ; + float c1ay = Math.abs(cp.y - ctrl.y) ; + // end1 + float e1ax = Math.abs(cp.x - end.x) ; + float e1ay = Math.abs(cp.y - end.y) ; + // get max value on each axis + float maxX = Math.max(c1ax, e1ax); + float maxY = Math.max(c1ay, e1ay); + + // trans coords from abs to rel + // ctrl1 + ctrl.x = c1ax / maxX; + ctrl.y = c1ay / maxY; + // end1 + end.x = e1ax / maxX; + end.y = e1ay / maxY; + + // claculate length + float prevLength = 0, prevX = 0, prevY = 0; + for (float t = 0.01f; t <= 1.0f; t += .01f) { + Float xy = getXY(t, new Float(0,0), ctrl, end); + prevLength += (float) Math.sqrt((xy.x - prevX) * (xy.x - prevX) + + (xy.y - prevY) * (xy.y - prevY)); + prevX = xy.x; + prevY = xy.y; + } + // prev len is a fraction num of the real path length + float a = Math.abs(coords[2] - cp.x); + float b = Math.abs(coords[3] - cp.y); + float dist = (float) Math.sqrt(a*a+b*b); + return prevLength * dist; + } + + /** + * Calculates the XY point for a given t value. + * + * The general spline equation is: x = b0*x0 + b1*x1 + b2*x2 + b3*x3 y = + * b0*y0 + b1*y1 + b2*y2 + b3*y3 where: b0 = (1-t)^3 b1 = 3 * t * (1-t)^2 b2 = + * 3 * t^2 * (1-t) b3 = t^3 We know that (x0,y0) == (0,0) and (x1,y1) == + * (1,1) for our splines, so this simplifies to: x = b1*x1 + b2*x2 + b3 y = + * b1*x1 + b2*x2 + b3 + * + * @author chet + * + * @param t parametric value for spline calculation + */ + private Float getXY(float t, float x1, float y1, float x2, float y2) { + Float xy; + float invT = (1 - t); + float b1 = 3 * t * (invT * invT); + float b2 = 3 * (t * t) * invT; + float b3 = t * t * t; + xy = new Float((b1 * x1) + (b2 * x2) + b3, (b1 * y1) + + (b2 * y2) + b3); + return xy; + } + + /** + * Calculates relative position of the point on the quad curve in time t<0,1>. + * @param t distance on the curve + * @param ctrl Control point in rel coords + * @param end End point in rel coords + * @return Solution of the quad equation for time T in non complex space in rel coords. + */ + public static Float getXY(float t, Float begin, Float ctrl, Float end) { + /* + * P1 = (x1, y1) - start point of curve + * P2 = (x2, y2) - end point of curve + * Pc = (xc, yc) - control point + * + * Pq(t) = P1*(1 - t)^2 + 2*Pc*t*(1 - t) + P2*t^2 = + * = (P1 - 2*Pc + P2)*t^2 + 2*(Pc - P1)*t + P1 + * t = [0:1] + * // thx Jim ... + * + * b0 = (1 -t)^2, b1 = 2*t*(1-t), b2 = t^2 + */ + Float xy; + float invT = (1 - t); + float b0 = invT * invT; + float b1 = 2 * t * invT ; + float b2 = t * t; + xy = new Float(b0 * begin.x + (b1 * ctrl.x) + b2* end.x, b0 * begin.y + (b1 * ctrl.y) + b2* end.y); + + return xy; + } + + /** + * Selects appropriate color for given frame based on the frame position and gradient difference. + * @param i Frame. + * @return Frame color. + */ + private Color calcFrameColor(final int i) { + if (frame == -1) { + return getBaseColor(); + } + + for (int t = 0; t < getTrailLength(); t++) { + if (direction == Direction.RIGHT + && i == (frame - t + getPoints()) % getPoints()) { + float terp = 1 - ((float) (getTrailLength() - t)) + / (float) getTrailLength(); + return PaintUtils.interpolate(getBaseColor(), + getHighlightColor(), terp); + } else if (direction == Direction.LEFT + && i == (frame + t) % getPoints()) { + float terp = ((float) (t)) / (float) getTrailLength(); + return PaintUtils.interpolate(getBaseColor(), + getHighlightColor(), terp); + } + } + return getBaseColor(); + } + + /** + * Gets current frame. + * @return Current frame. + */ + public int getFrame() { + return frame; + } + + /**Sets current frame. + * @param frame Current frame. + */ + public void setFrame(int frame) { + int old = getFrame(); + this.frame = frame; + firePropertyChange("frame", old, getFrame()); + } + + /** + * Gets base color. + * @return Base color. + */ + public Color getBaseColor() { + return baseColor; + } + + /** + * Sets new base color. Bound property. + * @param baseColor Base color. + */ + public void setBaseColor(Color baseColor) { + Color old = getBaseColor(); + this.baseColor = baseColor; + firePropertyChange("baseColor", old, getBaseColor()); + } + + /** + * Gets highlight color. + * @return Current highlight color. + */ + public Color getHighlightColor() { + return highlightColor; + } + + /** + * Sets new highlight color. Bound property. + * @param highlightColor New highlight color. + */ + public void setHighlightColor(Color highlightColor) { + Color old = getHighlightColor(); + this.highlightColor = highlightColor; + firePropertyChange("highlightColor", old, getHighlightColor()); + } + + /** + * Gets total amount of distinct points in spinner. + * @return Total amount of points. + */ + public int getPoints() { + return points; + } + + /** + * Sets total amount of points in spinner. Bound property. + * @param points Total amount of points. + */ + public void setPoints(int points) { + int old = getPoints(); + this.points = points; + firePropertyChange("points", old, getPoints()); + } + + /** + * Gets length of trail in number of points. + * @return Trail lenght. + */ + public int getTrailLength() { + return trailLength; + } + + /** + * Sets length of the trail in points. Bound property. + * @param trailLength Trail length in points. + */ + public void setTrailLength(int trailLength) { + int old = getTrailLength(); + this.trailLength = trailLength; + firePropertyChange("trailLength", old, getTrailLength()); + } + + /** + * Gets shape of current point. + * @return Shape of the point. + */ + public final Shape getPointShape() { + return pointShape; + } + + /** + * Sets new point shape. Bound property. + * @param pointShape new Shape. + */ + public final void setPointShape(Shape pointShape) { + Shape old = getPointShape(); + this.pointShape = pointShape; + firePropertyChange("pointShape", old, getPointShape()); + } + + /** + * Gets current trajectory. + * @return Current spinner trajectory . + */ + public final Shape getTrajectory() { + return trajectory; + } + + /** + * Sets new trajectory. Expected trajectory have to be closed shape. Bound property. + * @param trajectory New trajectory. + */ + public final void setTrajectory(Shape trajectory) { + Shape old = getTrajectory(); + this.trajectory = trajectory; + firePropertyChange("trajectory", old, getTrajectory()); + } + + /** + * Gets current direction of spinning. + * @return Current spinning direction. + */ + public Direction getDirection() { + return direction; + } + + /** + * Sets new spinning direction. + * @param dir Spinning direction. + */ + public void setDirection(Direction dir) { + Direction old = getDirection(); + this.direction = dir; + firePropertyChange("direction", old, getDirection()); + } +} diff --git a/src/main/java/org/jdesktop/swingx/painter/CheckerboardPainter.java b/src/main/java/org/jdesktop/swingx/painter/CheckerboardPainter.java new file mode 100644 index 0000000000..bd9d7c0980 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/painter/CheckerboardPainter.java @@ -0,0 +1,197 @@ +/* + * $Id: CheckerboardPainter.java 4147 2012-02-01 17:13:24Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.painter; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.util.PaintUtils; + +import java.awt.*; + +/** + *

      A Painter implementation that paints a checkerboard pattern. The light + * and dark colors (Paint instances) are configurable, as are the size of the + * squares (squareSize).

      + * + *

      To configure a checkerboard pattern that used a gradient for the dark + * tiles and Color.WHITE for the light tiles, you could: + *

      
      + *  GradientPaint gp = new GradientPaint(
      + *      new Point2D.Double(0, 0),
      + *      Color.BLACK,
      + *      new Point2D.Double(0, 32),
      + *      Color.GRAY);
      + *  CheckerboardPainter p = new CheckerboardPainter();
      + *  p.setDarkPaint(gp);
      + *  p.setLightPaint(Color.WHITE);
      + *  p.setSquareSize(32);
      + *  panel.seBackgroundPainter(p);
      + * 

      + * + *

      Note that in this example, the "32" in the GradientPaint matches the "32" + * set for the squareSize. This is necessary because GradientPaints don't + * readjust themselves for the size of the square. They are fixed and immutable + * at the time of creation.

      + * + * @author rbair + */ +@JavaBean +@SuppressWarnings("nls") +public class CheckerboardPainter extends AbstractPainter { + private transient Paint checkerPaint; + + private Paint darkPaint = new Color(204, 204, 204); + private Paint lightPaint = Color.WHITE; + private double squareSize = 8; + + /** + * Create a new CheckerboardPainter. By default the light color is Color.WHITE, + * the dark color is a light gray, and the square length is 8. + */ + public CheckerboardPainter() { + } + + /** + * Create a new CheckerboardPainter with the specified light and dark paints. + * By default the square length is 8. + * + * @param darkPaint the paint used to draw the dark squares + * @param lightPaint the paint used to draw the light squares + */ + public CheckerboardPainter(Paint darkPaint, Paint lightPaint) { + this(darkPaint, lightPaint, 8); + } + + /** + * Create a new CheckerboardPainter with the specified light and dark paints + * and the specified square size. + * + * @param darkPaint the paint used to draw the dark squares + * @param lightPaint the paint used to draw the light squares + * @param squareSize the squareSize of the checker board squares + */ + //TODO squareSize should become int? only ever treated as one + public CheckerboardPainter(Paint darkPaint, Paint lightPaint, double squareSize) { + this.darkPaint = darkPaint; + this.lightPaint = lightPaint; + this.squareSize = squareSize; + } + + + /** + * Specifies the squareSize of the squares. By default, it is 8. A squareSize of <= + * 0 will cause an IllegalArgumentException to be thrown. + * + * @param squareSize the squareSize of one side of a square tile. Must be > 0. + */ + public void setSquareSize(double squareSize) { + if (squareSize <= 0) { + throw new IllegalArgumentException("Length must be > 0"); + } + + double old = getSquareSize(); + this.squareSize = squareSize; + checkerPaint = null; + setDirty(true); + firePropertyChange("squareSize", old, getSquareSize()); + } + + /** + * Gets the current square length. + * + * @return the squareSize. Will be > 0 + */ + public double getSquareSize() { + return squareSize; + } + + /** + * Specifies the paint to use for dark tiles. This is a Paint and + * may be anything, including a TexturePaint for painting images. If null, + * the background color of the component is used. + * + * @param color the Paint to use for painting the "dark" tiles. May be null. + */ + public void setDarkPaint(Paint color) { + Paint old = getDarkPaint(); + this.darkPaint = color; + checkerPaint = null; + setDirty(true); + firePropertyChange("darkPaint", old, getDarkPaint()); + } + + /** + * + * Gets the current dark paint. + * @return the Paint used for painting the "dark" tiles. May be null + */ + public Paint getDarkPaint() { + return darkPaint; + } + + /** + * Specifies the paint to use for light tiles. This is a Paint and + * may be anything, including a TexturePaint for painting images. If null, + * the foreground color of the component is used. + * + * @param color the Paint to use for painting the "light" tiles. May be null. + */ + public void setLightPaint(Paint color) { + Paint old = getLightPaint(); + this.lightPaint = color; + checkerPaint = null; + setDirty(true); + firePropertyChange("lightPaint", old, getLightPaint()); + } + + /** + * + * gets the current light paint + * @return the Paint used for painting the "light" tiles. May be null + */ + public Paint getLightPaint() { + return lightPaint; + } + + /** + * Helper method that creates and returns the Paint that incorporates the + * sizes and light and dark Paints in one TexturePaint. I may want to cache + * this value in the future for performance reasons + */ + private Paint getCheckerPaint(Object c) { + if (checkerPaint == null) { + Paint p1 = PainterUtils.getForegroundPaint(getLightPaint(), c); + Paint p2 = PainterUtils.getBackgroundPaint(getDarkPaint(), c); + + checkerPaint = PaintUtils.getCheckerPaint(p1, p2, (int)(getSquareSize() * 2)); + } + return checkerPaint; + } + + /** + * {@inheritDoc} + */ + @Override + protected void doPaint(Graphics2D g, Object t, int width, int height) { + g.setPaint(getCheckerPaint(t)); + g.fillRect(0, 0, width, height); + } +} diff --git a/src/main/java/org/jdesktop/swingx/painter/CompoundPainter.java b/src/main/java/org/jdesktop/swingx/painter/CompoundPainter.java new file mode 100644 index 0000000000..74ae90a950 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/painter/CompoundPainter.java @@ -0,0 +1,389 @@ +/* + * $Id: CompoundPainter.java 4156 2012-02-02 19:54:38Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.painter; + +import org.jdesktop.beans.JavaBean; + +import java.awt.*; +import java.awt.geom.AffineTransform; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.lang.ref.WeakReference; + +/** + *

      A {@link Painter} implementation composed of an array of Painters. + * CompoundPainter provides a means for combining several individual + * Painters, or groups of them, into one logical unit. Each of the + * Painters are executed in order. BufferedImageOp filter effects can + * be applied to them together as a whole. The entire set of painting operations + * may be cached together.

      + * + *

      + * + *

      For example, if I want to create a CompoundPainter that started with a blue + * background, had pinstripes on it running at a 45 degree angle, and those + * pinstripes appeared to "fade in" from left to right, I would write the following: + *

      
      + *  Color blue = new Color(0x417DDD);
      + *  Color translucent = new Color(blue.getRed(), blue.getGreen(), blue.getBlue(), 0);
      + *  panel.setBackground(blue);
      + *  panel.setForeground(Color.LIGHT_GRAY);
      + *  GradientPaint blueToTranslucent = new GradientPaint(
      + *    new Point2D.Double(.4, 0),
      + *    blue,
      + *    new Point2D.Double(1, 0),
      + *    translucent);
      + *  MattePainter veil = new MattePainter(blueToTranslucent);
      + *  veil.setPaintStretched(true);
      + *  Painter pinstripes = new PinstripePainter(45);
      + *  Painter backgroundPainter = new RectanglePainter(this.getBackground(), null);
      + *  Painter p = new CompoundPainter(backgroundPainter, pinstripes, veil);
      + *  panel.setBackgroundPainter(p);
      + * 

      + * + * @author rbair + */ +@JavaBean +@SuppressWarnings("nls") +public class CompoundPainter extends AbstractPainter { + private static class Handler implements PropertyChangeListener { + private final WeakReference> ref; + + public Handler(CompoundPainter painter) { + ref = new WeakReference>(painter); + } + + /** + * {@inheritDoc} + */ + @Override + public void propertyChange(PropertyChangeEvent evt) { + CompoundPainter painter = ref.get(); + + if (painter == null) { + AbstractPainter src = (AbstractPainter) evt.getSource(); + src.removePropertyChangeListener(this); + } else { + String property = evt.getPropertyName(); + + if ("dirty".equals(property) && evt.getNewValue() == Boolean.FALSE) { + return; + } + + painter.setDirty(true); + } + } + } + + private Handler handler; + + private Painter[] painters = new Painter[0]; + private AffineTransform transform; + private boolean clipPreserved = false; + + private boolean checkForDirtyChildPainters = true; + + /** Creates a new instance of CompoundPainter */ + public CompoundPainter() { + this((Painter[]) null); + } + + /** + * Convenience constructor for creating a CompoundPainter for an array + * of painters. A defensive copy of the given array is made, so that future + * modification to the array does not result in changes to the CompoundPainter. + * + * @param painters array of painters, which will be painted in order + */ + public CompoundPainter(Painter... painters) { + handler = new Handler(this); + + setPainters(painters); + } + + /** + * Sets the array of Painters to use. These painters will be executed in + * order. A null value will be treated as an empty array. To prevent unexpected + * behavior all values in provided array are copied to internally held array. + * Any changes to the original array will not be reflected. + * + * @param painters array of painters, which will be painted in order + */ + public void setPainters(Painter... painters) { + Painter[] old = getPainters(); + + for (Painter p : old) { + if (p instanceof AbstractPainter) { + ((AbstractPainter) p).removePropertyChangeListener(handler); + } + } + + this.painters = new Painter[painters == null ? 0 : painters.length]; + if (painters != null) { + System.arraycopy(painters, 0, this.painters, 0, this.painters.length); + } + + for (Painter p : this.painters) { + if (p instanceof AbstractPainter) { + ((AbstractPainter) p).addPropertyChangeListener(handler); + } + } + + setDirty(true); + firePropertyChange("painters", old, getPainters()); + } + + /** + * Gets the array of painters used by this CompoundPainter + * @return a defensive copy of the painters used by this CompoundPainter. + * This will never be null. + */ + public final Painter[] getPainters() { + Painter[] results = new Painter[painters.length]; + System.arraycopy(painters, 0, results, 0, results.length); + return results; + } + + + /** + * Indicates if the clip produced by any painter is left set once it finishes painting. + * Normally the clip will be reset between each painter. Setting clipPreserved to + * true can be used to let one painter mask other painters that come after it. + * @return if the clip should be preserved + * @see #setClipPreserved(boolean) + */ + public boolean isClipPreserved() { + return clipPreserved; + } + + /** + * Sets if the clip should be preserved. + * Normally the clip will be reset between each painter. Setting clipPreserved to + * true can be used to let one painter mask other painters that come after it. + * + * @param shouldRestoreState new value of the clipPreserved property + * @see #isClipPreserved() + */ + public void setClipPreserved(boolean shouldRestoreState) { + boolean oldShouldRestoreState = isClipPreserved(); + this.clipPreserved = shouldRestoreState; + setDirty(true); + firePropertyChange("clipPreserved",oldShouldRestoreState,shouldRestoreState); + } + + /** + * Gets the current transform applied to all painters in this CompoundPainter. May be null. + * @return the current AffineTransform + */ + public AffineTransform getTransform() { + return transform; + } + + /** + * Set a transform to be applied to all painters contained in this CompoundPainter + * @param transform a new AffineTransform + */ + public void setTransform(AffineTransform transform) { + AffineTransform old = getTransform(); + this.transform = transform; + setDirty(true); + firePropertyChange("transform",old,transform); + } + + /** + *

      Iterates over all child Painters and gives them a chance + * to validate themselves. If any of the child painters are dirty, then + * this CompoundPainter marks itself as dirty.

      + * + * {@inheritDoc} + */ + @Override + protected void validate(T object) { + boolean dirty = false; + for (Painter p : painters) { + if (p instanceof AbstractPainter) { + AbstractPainter ap = (AbstractPainter) p; + ap.validate(object); + if (ap.isDirty()) { + dirty = true; + break; + } + } + } + clearLocalCacheOnly = true; + setDirty(dirty); //super will call clear cache + clearLocalCacheOnly = false; + } + + //indicates whether the local cache should be cleared only, as opposed to the + //cache's of all of the children. This is needed to optimize the caching strategy + //when, during validate, the CompoundPainter is marked as dirty + private boolean clearLocalCacheOnly = false; + + /** + * Used by {@link #isDirty()} to check if the child Painters + * should be checked for their dirty flag as part of + * processing.
      + * Default value is: true
      + * This should be set to false if the cacheable state + * of the child Painters are different from each other. This + * will allow the cacheable == true Painters to + * keep their cached image during regular repaints. In this case, + * client code should call {@link #clearCache()} manually when the cacheable + * Painters should be updated. + * + * + * @see #isDirty() + */ + public boolean isCheckingDirtyChildPainters() { + return checkForDirtyChildPainters; + } + /** + * Set the flag used by {@link #isDirty()} to check if the + * child Painters should be checked for their + * dirty flag as part of processing. + * + * @see #isCheckingDirtyChildPainters() + * @see #isDirty() + */ + public void setCheckingDirtyChildPainters(boolean b) { + boolean old = isCheckingDirtyChildPainters(); + this.checkForDirtyChildPainters = b; + firePropertyChange("checkingDirtyChildPainters",old, isCheckingDirtyChildPainters()); + } + + /** + * {@inheritDoc} + * + * @impl This CompoundPainter is dirty if it, or (optionally) any of its children, + * are dirty. If the super implementation returns true, we return + * true. Otherwise, if {@link #isCheckingDirtyChildPainters()} is + * true, we iterate over all child Painters and query them to + * see if they are dirty. If so, then true is returned. Otherwise, we return + * false. + * @see #isCheckingDirtyChildPainters() + */ + @Override + protected boolean isDirty() { + boolean dirty = super.isDirty(); + if (dirty) { + return true; + } + else if (isCheckingDirtyChildPainters()) { + for (Painter p : painters) { + if (p instanceof AbstractPainter) { + AbstractPainter ap = (AbstractPainter) p; + if (ap.isDirty()) { + return true; + } + } + } + } + return false; + } + + /** + * {@inheritDoc} + */ + @Override + protected void setDirty(boolean d) { + boolean old = super.isDirty(); + boolean ours = isDirty(); + + super.setDirty(d); + + //must perform this check to ensure we do not double notify + if (d != old && d == ours) { + firePropertyChange("dirty", old, isDirty()); + } + } + + /** + *

      Clears the cache of this Painter, and all child + * Painters. This is done to ensure that resources + * are collected, even if clearCache is called by some framework + * or other code that doesn't realize this is a CompoundPainter.

      + * + *

      Call #clearLocalCache if you only want to clear the cache of this + * CompoundPainter + * + * {@inheritDoc} + */ + @Override + public void clearCache() { + if (!clearLocalCacheOnly) { + for (Painter p : painters) { + if (p instanceof AbstractPainter) { + AbstractPainter ap = (AbstractPainter) p; + ap.clearCache(); + } + } + } + super.clearCache(); + } + + /** + *

      Clears the cache of this painter only, and not of any of the children.

      + */ + public void clearLocalCache() { + super.clearCache(); + } + + /** + * {@inheritDoc} + */ + @Override + protected void doPaint(Graphics2D g, T component, int width, int height) { + for (Painter p : getPainters()) { + Graphics2D temp = (Graphics2D) g.create(); + + try { + p.paint(temp, component, width, height); + if(isClipPreserved()) { + g.setClip(temp.getClip()); + } + } finally { + temp.dispose(); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void configureGraphics(Graphics2D g) { + //applies the transform + AffineTransform tx = getTransform(); + if (tx != null) { + g.setTransform(tx); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected boolean shouldUseCache() { + return super.shouldUseCache(); // || (painters != null && painters.length > 1); + } +} diff --git a/src/main/java/org/jdesktop/swingx/painter/GlossPainter.java b/src/main/java/org/jdesktop/swingx/painter/GlossPainter.java new file mode 100644 index 0000000000..855ff3175b --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/painter/GlossPainter.java @@ -0,0 +1,171 @@ +/* + * $Id: GlossPainter.java 4147 2012-02-01 17:13:24Z kschaefe $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.painter; + +import org.jdesktop.beans.JavaBean; + +import java.awt.*; +import java.awt.geom.Area; +import java.awt.geom.Ellipse2D; + +/** + *

      A Painter implementation that simulates a gloss effect. The gloss can + * be positioned at the top or bottom of the drawing area. To fill the gloss, + * this painter uses a Paint instance which can be used to fill with a color + * (opaque or translucent), a texture, a gradient...

      + *

      The following example creates a white gloss at the top of the drawing + * area:

      + *
      + *  GlossPainter p = new GlossPainter();
      + *  p.setPaint(new Color(1.0f, 1.0f, 1.0f, 0.2f);
      + *  p.setPosition(GlossPainter.GlossPosition.TOP);
      + *  panel.setBackgroundPainter(p);
      + * 
      + *

      The values shown in this examples are the values used by default if + * they are not specified.

      + * + * @author Romain Guy + */ +@JavaBean +@SuppressWarnings("nls") +public class GlossPainter extends AbstractPainter { + /** + *

      Used to define the position of the gloss on the painted area.

      + */ + public enum GlossPosition { + TOP, BOTTOM + } + + private Paint paint; + private GlossPosition position; + + /** + *

      Creates a new gloss painter positioned at the top of the painted + * area with a 20% translucent white color.

      + */ + public GlossPainter() { + this(new Color(1.0f, 1.0f, 1.0f, 0.2f), GlossPosition.TOP); + } + + /** + *

      Creates a new gloss painter positioned at the top of the painted + * area with the specified paint.

      + * + * @param paint The paint to be used when filling the gloss + */ + public GlossPainter(Paint paint) { + this(paint, GlossPosition.TOP); + } + + /** + *

      Creates a new gloss painter positioned at the specified position + * and using a white, 20% translucent paint.

      + * + * @param position The position of the gloss on the painted area + */ + public GlossPainter(GlossPosition position) { + this(new Color(1.0f, 1.0f, 1.0f, 0.2f), position); + } + + /** + *

      Creates a new gloss painter positioned at the specified position + * and painted with the specified paint.

      + * + * @param paint The paint to be used when filling the gloss + * @param position The position of the gloss on the painted area + */ + public GlossPainter(Paint paint, GlossPosition position) { + this.setPaint(paint); + this.setPosition(position); + } + + /** + * {@inheritDoc} + */ + @Override + protected void doPaint(Graphics2D g, Object component, int width, int height) { + if (getPaint() != null) { + Ellipse2D ellipse = new Ellipse2D.Double(-width / 2.0, + height / 2.7, width * 2.0, + height * 2.0); + + Area gloss = new Area(ellipse); + if (getPosition() == GlossPosition.TOP) { + Area area = new Area(new Rectangle(0, 0, + width, height)); + area.subtract(new Area(ellipse)); + gloss = area; + } + /* + if(getClip() != null) { + gloss.intersect(new Area(getClip())); + }*/ + g.setPaint(getPaint()); + g.fill(gloss); + } + } + + /** + *

      Returns the paint currently used by the painter to fill the gloss.

      + * + * @return the current Paint instance used by the painter + */ + public Paint getPaint() { + return paint; + } + + /** + *

      Changes the paint to be used to fill the gloss. When the specified + * paint is null, nothing is painted. A paint can be an instance of + * Color.

      + * + * @param paint The Paint instance to be used to fill the gloss + */ + public void setPaint(Paint paint) { + Paint old = this.paint; + this.paint = paint; + setDirty(true); + firePropertyChange("paint", old, getPaint()); + } + + /** + *

      Returns the position at which the gloss is painted.

      + * + * @return the position of the gloss in the painted area + */ + public GlossPosition getPosition() { + return position; + } + + /** + *

      Changes the position of the gloss in the painted area. Only the + * values defined in the GlossPosition enum are valid.

      + * + * @param position The position at which the gloss is painted + */ + public void setPosition(GlossPosition position) { + GlossPosition old = this.position; + this.position = position; + setDirty(true); + firePropertyChange("position", old, getPosition()); + } +} diff --git a/src/main/java/org/jdesktop/swingx/painter/ImagePainter.java b/src/main/java/org/jdesktop/swingx/painter/ImagePainter.java new file mode 100644 index 0000000000..b21f8e3580 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/painter/ImagePainter.java @@ -0,0 +1,400 @@ +/* + * $Id: ImagePainter.java 4261 2012-11-19 18:38:46Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.painter; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.painter.effects.AreaEffect; + +import java.awt.*; +import java.awt.geom.Area; +import java.awt.image.BufferedImage; +import java.util.logging.Logger; + +/** + *

      A Painter instance that paints an image. Any Image is acceptable. This + * Painter also allows the developer to specify a "Style" -- CENTERED, TILED, + * SCALED, POSITIONED, and CSS_POSITIONED; with the following meanings:

      + * + *
        + *
      • CENTERED: draws the image unscaled and positioned in the center of + * the component
      • + *
      • TILED: draws the image repeatedly across the component, filling the + * entire background.
      • + *
      • SCALED: draws the image stretched large enough (or small enough) to + * cover the entire component. The stretch may not preserve the aspect ratio of the + * original image.
      • + *
      • POSITIONED: draws the image at the location specified by the imageLocation + * property. This style of drawing will respect the imageScale property.
      • + *
      • CSS_POSITIONED: draws the image using CSS style background positioning. + *It will use the location specified by the imageLocation property. This property should + *contain a point with the x and y values between 0 and 1. 0,0 will put the image in the + *upper left hand corner, 1,1 in the lower right, and 0.5,0.5 in the center. All other values + *will be interpolated accordingly. For a more + * complete definition of the positioning algorithm see the + * CSS 2.1 spec. + *
      • + *
      + * + * @author Richard + */ +@JavaBean +@SuppressWarnings("nls") +public class ImagePainter extends AbstractAreaPainter { + public enum ScaleType { InsideFit, OutsideFit, Distort } + + /** + * Logger to use + */ + @SuppressWarnings("unused") + private static final Logger LOG = Logger.getLogger(ImagePainter.class.getName()); + + /** + * The image to draw + */ + private transient BufferedImage img; + + private boolean horizontalRepeat; + private boolean verticalRepeat; + + private boolean scaleToFit = false; + private ScaleType scaleType = ScaleType.InsideFit; + + private double imageScale = 1.0; + + /** + * Create a new ImagePainter. By default there is no image, and the alignment + * is centered. + */ + public ImagePainter() { + this((BufferedImage)null); + } + + /** + * Create a new ImagePainter with the specified image and the Style + * Style.CENTERED + * + * @param image the image to be painted + */ + public ImagePainter(BufferedImage image) { + this(image,HorizontalAlignment.CENTER, VerticalAlignment.CENTER); + } + + /** + * Create a new ImagePainter with the specified image and alignment. + * @param horizontal the horizontal alignment + * @param vertical the vertical alignment + * @param image the image to be painted + */ + public ImagePainter(BufferedImage image, HorizontalAlignment horizontal, VerticalAlignment vertical) { + super(); + setCacheable(true); + this.img = image; + this.setVerticalAlignment(vertical); + this.setHorizontalAlignment(horizontal); + this.setFillPaint(null); + this.setBorderPaint(null); + this.setDirty(false); + } + + /** + * Sets the image to paint with. + * @param image if null, clears the image. Otherwise, this will set the + * image to be painted. + */ + public void setImage(BufferedImage image) { + if (image != img) { + Image oldImage = img; + img = image; + setDirty(true); + firePropertyChange("image", oldImage, img); + } + } + + /** + * Gets the current image used for painting. + * @return the image used for painting + */ + public BufferedImage getImage() { + return img; + } + + /** + * {@inheritDoc} + */ + @Override + protected void doPaint(Graphics2D g, Object component, int width, int height) { + Shape shape = provideShape(g, component,width,height); + + switch (getStyle()) { + case BOTH: + drawBackground(g,shape,width,height); + drawBorder(g,shape,width,height); + break; + case FILLED: + drawBackground(g,shape,width,height); + break; + case OUTLINE: + drawBorder(g,shape,width,height); + break; + case NONE: + break; + default: + break; + } + } + + private void drawBackground(Graphics2D g, Shape shape, int width, int height) { + Paint p = getFillPaint(); + + if(p != null) { + if(isPaintStretched()) { + p = calculateSnappedPaint(p, width, height); + } + g.setPaint(p); + g.fill(shape); + } + + if(getAreaEffects() != null) { + for(AreaEffect ef : getAreaEffects()) { + ef.apply(g, shape, width, height); + } + } + + + if (img != null) { + int imgWidth = img.getWidth(null); + int imgHeight = img.getHeight(null); + if (imgWidth == -1 || imgHeight == -1) { + //image hasn't completed loading, do nothing + } else { + Rectangle rect = shape.getBounds(); + + if(verticalRepeat || horizontalRepeat) { + Shape oldClip = g.getClip(); + Shape clip = g.getClip(); + if(clip == null) { + clip = new Rectangle(0,0,width,height); + } + Area area = new Area(clip); + Insets insets = getInsets(); + area.intersect(new Area(new Rectangle(insets.left, insets.top, width - insets.left - insets.right, height - insets.top - insets.bottom))); + + if (verticalRepeat && horizontalRepeat) { + area.intersect(new Area(new Rectangle(0, 0, width, height))); + g.setClip(area); + } else if (verticalRepeat) { + area.intersect(new Area(new Rectangle(rect.x, 0, rect.width, height))); + g.setClip(area); + } else { + area.intersect(new Area(new Rectangle(0, rect.y, width, rect.height))); + g.setClip(area); + } + + TexturePaint tp = new TexturePaint(img, rect); + g.setPaint(tp); + g.fillRect(0,0,width,height); + g.setClip(oldClip); + } else { + if(scaleToFit) { + int sw = imgWidth; + int sh = imgHeight; + if(scaleType == ScaleType.InsideFit) { + if(sw > width) { + float scale = (float)width/(float)sw; + sw = (int)(sw * scale); + sh = (int)(sh * scale); + } + if(sh > height) { + float scale = (float)height/(float)sh; + sw = (int)(sw * scale); + sh = (int)(sh * scale); + } + } + if(scaleType == ScaleType.OutsideFit) { + if(sw > width) { + float scale = (float)width/(float)sw; + sw = (int)(sw * scale); + sh = (int)(sh * scale); + } + if(sh < height) { + float scale = (float)height/(float)sh; + sw = (int)(sw * scale); + sh = (int)(sh * scale); + } + } + if(scaleType == ScaleType.Distort) { + sw = width; + sh = height; + } + int x=0; + int y=0; + switch(getHorizontalAlignment()) { + case CENTER: + x=(width/2)-(sw/2); + break; + case RIGHT: + x=width-sw; + break; + case LEFT: + break; + default: + break; + } + switch(getVerticalAlignment()) { + case CENTER: + y=(height/2)-(sh/2); + break; + case BOTTOM: + y=height-sh; + break; + case TOP: + break; + default: + break; + } + g.drawImage(img, x, y, sw, sh, null); + } else { + int sw = rect.width; + int sh = rect.height; + if(imageScale != 1.0) { + sw = (int)(sw * imageScale); + sh = (int)(sh * imageScale); + } + g.drawImage(img, rect.x, rect.y, sw, sh, null); + } + } + } + } + + } + + private void drawBorder(Graphics2D g, Shape shape, int width, int height) { + if(getBorderPaint() != null) { + g.setPaint(getBorderPaint()); + g.setStroke(new BasicStroke(getBorderWidth())); + g.draw(shape); + } + } + + public boolean isScaleToFit() { + return scaleToFit; + } + + public void setScaleToFit(boolean scaleToFit) { + boolean old = isScaleToFit(); + this.scaleToFit = scaleToFit; + setDirty(true); + firePropertyChange("scaleToFit", old, isScaleToFit()); + } + + public ScaleType getScaleType() { + return scaleType; + } + + public void setScaleType(ScaleType scaleType) { + ScaleType old = getScaleType(); + this.scaleType = scaleType; + setDirty(true); + firePropertyChange("scaleType", old, getScaleType()); + } + + /** + * Gets the current scaling factor used when drawing an image. + * @return the current scaling factor + */ + public double getImageScale() { + return imageScale; + } + + /** + * Sets the scaling factor used when drawing the image + * @param imageScale the new image scaling factor + */ + public void setImageScale(double imageScale) { + double old = getImageScale(); + this.imageScale = imageScale; + setDirty(true); + firePropertyChange("imageScale",old,this.imageScale); + } + + /** + * Indicates if the image will be repeated horizontally. + * @return if the image will be repeated horizontally + */ + public boolean isHorizontalRepeat() { + return horizontalRepeat; + } + + /** + * Sets if the image should be repeated horizontally. + * @param horizontalRepeat the new horizontal repeat value + */ + public void setHorizontalRepeat(boolean horizontalRepeat) { + boolean old = this.isHorizontalRepeat(); + this.horizontalRepeat = horizontalRepeat; + setDirty(true); + firePropertyChange("horizontalRepeat",old,this.horizontalRepeat); + } + + /** + * Indicates if the image will be repeated vertically. + * @return if the image will be repeated vertically + */ + public boolean isVerticalRepeat() { + return verticalRepeat; + } + + /** + * Sets if the image should be repeated vertically. + * @param verticalRepeat new value for the vertical repeat + */ + public void setVerticalRepeat(boolean verticalRepeat) { + boolean old = this.isVerticalRepeat(); + this.verticalRepeat = verticalRepeat; + setDirty(true); + firePropertyChange("verticalRepeat",old,this.verticalRepeat); + } + + /** + * + */ + @Override + protected Shape provideShape(Graphics2D g, Object comp, int width, int height) { + if(getImage() != null) { + BufferedImage bi = getImage(); + int imgWidth = bi.getWidth(); + int imgHeight = bi.getHeight(); + + return calculateLayout(imgWidth, imgHeight, width, height); + } + return new Rectangle(0,0,0,0); + + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return getClass().getSimpleName() + "[img=" + img + "]"; + } +} diff --git a/src/main/java/org/jdesktop/swingx/painter/MattePainter.java b/src/main/java/org/jdesktop/swingx/painter/MattePainter.java new file mode 100644 index 0000000000..fe5027d437 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/painter/MattePainter.java @@ -0,0 +1,114 @@ +/* + * $Id: MattePainter.java 4147 2012-02-01 17:13:24Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.painter; + +import org.jdesktop.beans.JavaBean; + +import java.awt.*; + +/** + * A Painter implementation that uses a Paint to fill the entire background + * area. For example, if I wanted to paint the entire background of a panel green, I would: + *
      
      + *  MattePainter p = new MattePainter(Color.GREEN);
      + *  panel.setBackgroundPainter(p);
      + * 

      + * + *

      Since it accepts a Paint, it is also possible to paint a texture or use other + * more exotic Paint implementations. To paint a BufferedImage texture as the + * background: + *

      
      + *  TexturePaint paint = new TexturePaint(bufferedImage,
      + *      new Rectangle2D.Double(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight()));
      + *  MattePainter p = new MattePainter(paint);
      + *  panel.setBackgroundPainter(p);
      + * 

      + * + *

      If no paint is specified, then nothing is painted

      + * @author rbair + */ +@JavaBean +public class MattePainter extends AbstractAreaPainter { + + /** + * Creates a new MattePainter with "null" as the paint used + */ + public MattePainter() { + } + + /** + * Create a new MattePainter for the given Paint. This can be a GradientPaint + * (the gradient will not grow when the component becomes larger unless + * you use the paintStretched boolean property), + * TexturePaint, Color, or other Paint instance. + * + * @param paint Paint to fill with + */ + public MattePainter(Paint paint) { + super(paint); + } + + /** + * Create a new MattePainter for the given Paint. This can be a GradientPaint + * (the gradient will not grow when the component becomes larger unless + * you use the paintStretched boolean property), + * TexturePaint, Color, or other Paint instance. + * + * @param paint Paint to fill with + * @param paintStretched indicates if the paint should be stretched + */ + public MattePainter(Paint paint, boolean paintStretched) { + super(paint); + this.setPaintStretched(paintStretched); + } + + /** + * {@inheritDoc} + */ + @Override + protected void doPaint(Graphics2D g, Object component, int width, int height) { + Paint p = getFillPaint(); + + if (p != null) { + Insets insets = getInsets(); + int w = width - insets.left - insets.right; + int h = height - insets.top - insets.bottom; + + if (isPaintStretched()) { + p = calculateSnappedPaint(p, w, h); + } + + g.translate(insets.left, insets.top); + g.setPaint(p); + g.fill(provideShape(g, component, w, h)); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected Shape provideShape(Graphics2D g, Object comp, int width, int height) { + return new Rectangle(0,0,width,height); + } + +} diff --git a/src/main/java/org/jdesktop/swingx/painter/Painter.java b/src/main/java/org/jdesktop/swingx/painter/Painter.java new file mode 100644 index 0000000000..9bc8439c96 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/painter/Painter.java @@ -0,0 +1,103 @@ +/* + * $Id: Painter.java 3860 2010-10-26 01:14:53Z kschaefe $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.painter; + +import java.awt.*; + +/** + *

      A painting delegate. The Painter interface defines exactly one method, + * paint. It is used in situations where the developer can change + * the painting routine of a component without having to resort to subclassing + * the component.

      + * + *

      Painters are simply encapsulations of Java2D code and make + * it fairly trivial to reuse existing Painters or to combine + * them together. Implementations of this interface are also trivial to write, + * such that if you can't find a Painter that does what you need, + * you can write one with minimal effort. Writing a Painter requires + * knowledge of Java2D.

      + * + *

      A Painter may be created with a type parameter. This type will be + * expected in the paint method. For example, you may wish to write a + * Painter that only works with subclasses of {@link java.awt.Component}. + * In that case, when the Painter is declared, you may declare that + * it requires a Component, allowing the paint method to be type safe. Ex: + *

      
      + *     Painter<Component> p = new Painter<Component>() {
      + *         public void paint(Graphics2D g, Component c, int width, int height) {
      + *             g.setColor(c.getBackground());
      + *             //and so forth
      + *         }
      + *     }
      + * 

      + * + *

      This class is not threadsafe.

      + * + * @author rbair + * @see AbstractPainter + * @see CompoundPainter + */ +public interface Painter { + /** + *

      Renders to the given {@link Graphics2D} object. Implementations + * of this method may modify state on the Graphics2D, and are not + * required to restore that state upon completion. In most cases, it is recommended + * that the caller pass in a scratch graphics object. The Graphics2D + * must never be null.

      + * + *

      State on the graphics object may be honored by the paint method, + * but may not be. For instance, setting the antialiasing rendering hint on the + * graphics may or may not be respected by the Painter implementation.

      + * + *

      The supplied object parameter acts as an optional configuration argument. + * For example, it could be of type Component. A Painter + * that expected it could then read state from that Component and + * use the state for painting. For example, an implementation may read the + * backgroundColor and use that.

      + * + *

      Generally, to enhance reusability, most standard Painters ignore + * this parameter. They can thus be reused in any context. The object + * may be null. Implementations must not throw a NullPointerException if the object + * parameter is null.

      + * + *

      Finally, the width and height arguments specify the + * width and height that the Painter should paint into. More + * specifically, the specified width and height instruct the painter that it should + * paint fully within this width and height. Any specified clip on the + * g param will further constrain the region.

      + * + *

      For example, suppose I have a Painter implementation that draws + * a gradient. The gradient goes from white to black. It "stretches" to fill the + * painted region. Thus, if I use this Painter to paint a 500 x 500 + * region, the far left would be black, the far right would be white, and a smooth + * gradient would be painted between. I could then, without modification, reuse the + * Painter to paint a region that is 20x20 in size. This region would + * also be black on the left, white on the right, and a smooth gradient painted + * between.

      + * + * @param g The Graphics2D to render to. This must not be null. + * @param object an optional configuration parameter. This may be null. + * @param width width of the area to paint. + * @param height height of the area to paint. + */ + public void paint(Graphics2D g, T object, int width, int height); +} diff --git a/src/main/java/org/jdesktop/swingx/painter/PainterPaint.java b/src/main/java/org/jdesktop/swingx/painter/PainterPaint.java new file mode 100644 index 0000000000..9d848240c7 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/painter/PainterPaint.java @@ -0,0 +1,116 @@ +/* + * $Id: PainterPaint.java 4082 2011-11-15 18:39:43Z kschaefe $ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.painter; + +import org.jdesktop.swingx.util.GraphicsUtilities; + +import java.awt.*; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.Raster; + +/** + * + * @author Karl George Schaefer + */ +public class PainterPaint implements Paint { + protected static class PainterPaintContext implements PaintContext { + private Painter painter; + private T object; + private BufferedImage saved; + + public PainterPaintContext(Painter painter, T object) { + painter.getClass(); // null check + this.painter = painter; + this.object = object; + } + + /** + * {@inheritDoc} + */ + @Override + public void dispose() { + //does nothing + } + + /** + * {@inheritDoc} + */ + @Override + public ColorModel getColorModel() { + if (saved == null) { + return GraphicsUtilities.createCompatibleImage(1, 1).getColorModel(); + } + + return saved.getColorModel(); + } + + /** + * {@inheritDoc} + */ + @Override + public Raster getRaster(int x, int y, int w, int h) { + if (saved == null || saved.getWidth() != w || saved.getHeight() != h) { + saved = GraphicsUtilities.createCompatibleImage(w, h); + Graphics2D g2d = saved.createGraphics(); + + try { + if (painter instanceof AbstractPainter) { + ((AbstractPainter) painter).setInPaintContext(true); + } + painter.paint(g2d, object, w, h); + } finally { + g2d.dispose(); + if (painter instanceof AbstractPainter) { + ((AbstractPainter) painter).setInPaintContext(false); + } + } + } + + return saved.getData(); + } + } + + private final PainterPaintContext context; + + public PainterPaint(Painter painter, T object) { + context = new PainterPaintContext(painter, object); + } + + /** + * {@inheritDoc} + */ + @Override + public PaintContext createContext(ColorModel cm, Rectangle deviceBounds, + Rectangle2D userBounds, AffineTransform xform, RenderingHints hints) { + return context; + } + + /** + * {@inheritDoc} + */ + @Override + public int getTransparency() { + return Transparency.BITMASK; + } +} diff --git a/src/main/java/org/jdesktop/swingx/painter/PainterUtils.java b/src/main/java/org/jdesktop/swingx/painter/PainterUtils.java new file mode 100644 index 0000000000..2d417a3e8a --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/painter/PainterUtils.java @@ -0,0 +1,39 @@ +package org.jdesktop.swingx.painter; + +import java.awt.*; + +final class PainterUtils { + private PainterUtils() { + //prevent instantiation + } + + static Paint getForegroundPaint(Paint current, Object o) { + if (current == null) { + if (o instanceof Component) { + return ((Component) o).getForeground(); + } + } + + return current; + } + + static Paint getBackgroundPaint(Paint current, Object o) { + if (current == null) { + if (o instanceof Component) { + return ((Component) o).getBackground(); + } + } + + return current; + } + + static Font getComponentFont(Font current, Object o) { + if (current == null) { + if (o instanceof Component) { + return ((Component) o).getFont(); + } + } + + return current; + } +} diff --git a/src/main/java/org/jdesktop/swingx/painter/Painters.java b/src/main/java/org/jdesktop/swingx/painter/Painters.java new file mode 100644 index 0000000000..6f50ca006c --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/painter/Painters.java @@ -0,0 +1,40 @@ +/* + * $Id: Painters.java 3590 2010-01-12 19:19:45Z kschaefe $ + * + * Copyright 2010 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.painter; + +import java.awt.*; + +/** + * A collection of static painters. These painters do not store state and are safe to reuse. + * @author kschaefer + */ +public final class Painters { + public static final Painter EMPTY_PAINTER = new Painter() { + @Override + public void paint(Graphics2D g, Object object, int width, int height) { + //does nothing + } + }; + + private Painters() { + //prevent instantiation + } +} diff --git a/src/main/java/org/jdesktop/swingx/painter/PinstripePainter.java b/src/main/java/org/jdesktop/swingx/painter/PinstripePainter.java new file mode 100644 index 0000000000..d8b8b15dae --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/painter/PinstripePainter.java @@ -0,0 +1,277 @@ +/* + * $Id: PinstripePainter.java 4259 2012-11-14 20:07:00Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.painter; + +import org.jdesktop.beans.JavaBean; + +import java.awt.*; +import java.awt.geom.Area; +import java.awt.geom.Line2D; + +import static java.lang.Math.*; +import static org.jdesktop.swingx.painter.PainterUtils.getForegroundPaint; + +/** + *

      + * A fun Painter that paints pinstripes. You can specify the Paint to paint those pinstripes in + * (could even be a texture paint!), the angle at which to paint the pinstripes, and the spacing + * between stripes. + *

      + * + *

      + * The default PinstripePainter configuration will paint the pinstripes using the foreground color + * of the component (the default behavior if a Paint is not specified) at a 45 degree angle with 8 + * pixels between stripes + *

      + * + *

      + * Here is a custom code snippet that paints Color.GRAY pinstripes at a 135 degree angle: + * + *

      + * 
      + *  PinstripePainter p = new PinstripePainter();
      + *  p.setAngle(135);
      + *  p.setPaint(Color.GRAY);
      + * 
      + * 
      + * + * @author rbair + */ +@JavaBean +public class PinstripePainter extends AbstractPainter { + /** + * The angle in degrees to paint the pinstripes at. The default value is 45. The value will be + * between 0 and 360 inclusive. The setAngle method will ensure this. + */ + private double angle; + + /** + * The spacing between pinstripes + */ + private double spacing; + + /** + * The stroke width of the pinstripes + */ + private double stripeWidth; + + /** + * The Paint to use when drawing the pinstripes + */ + private Paint paint; + + /** + * Create a new PinstripePainter. By default the angle with be 45 degrees, the spacing will be 8 + * pixels, and the color will be the Component foreground color. + */ + public PinstripePainter() { + this(null); + } + + /** + * Create a new PinstripePainter using an angle of 45, 8 pixel spacing, and the given Paint. + * + * @param paint + * the paint used when drawing the stripes + */ + public PinstripePainter(Paint paint) { + this(paint, 45); + } + + /** + * Create a new PinstripePainter using the given angle, 8 pixel spacing, and the foreground + * color of the Component + * + * @param angle + * the angle, in degrees, in which to paint the pinstripes + */ + public PinstripePainter(double angle) { + this(null, angle); + } + + /** + * Create a new PinstripePainter using the given angle, 8 pixel spacing, and the given Paint + * + * @param paint + * the paint used when drawing the stripes + * @param angle + * the angle, in degrees, in which to paint the pinstripes + */ + public PinstripePainter(Paint paint, double angle) { + this(paint, angle, 1, 8); + } + + /** + * Create a new PinstripePainter with the specified paint, angle, stripe width, and stripe + * spacing. + * + * @param paint + * @param angle + * @param stripeWidth + * @param spacing + */ + public PinstripePainter(Paint paint, double angle, double stripeWidth, double spacing) { + this.paint = paint; + this.angle = angle; + this.stripeWidth = stripeWidth; + this.spacing = spacing; + } + + /** + * Get the current paint used for drawing the pinstripes + * + * @return the Paint to use to draw the pinstripes + */ + public Paint getPaint() { + return paint; + } + + /** + * Set the paint to use for drawing the pinstripes + * + * @param p + * the Paint to use. May be a Color. + */ + public void setPaint(Paint p) { + Paint old = getPaint(); + this.paint = p; + firePropertyChange("paint", old, getPaint()); + } + + /** + * Gets the current angle of the pinstripes + * + * @return the angle, in degrees, at which the pinstripes are painted + */ + public double getAngle() { + return angle; + } + + /** + * Sets the angle, in degrees, at which to paint the pinstripes. If the given angle is < 0 or > + * 360, it will be appropriately constrained. For example, if a value of 365 is given, it will + * result in 5 degrees. The conversion is not perfect, but "a man on a galloping horse won't be + * able to tell the difference". + * + * @param angle + * the Angle in degrees at which to paint the pinstripes + */ + public void setAngle(double angle) { + if (angle > 360) { + angle = angle % 360; + } + + if (angle < 0) { + angle = 360 - ((angle * -1) % 360); + } + + double old = getAngle(); + this.angle = angle; + firePropertyChange("angle", old, getAngle()); + } + + /** + * Gets the current width of the pinstripes + * + * @return the current pinstripe width + */ + public double getStripeWidth() { + return stripeWidth; + } + + /** + * Set the width of the pinstripes + * + * @param stripeWidth + * a new width for the pinstripes + */ + public void setStripeWidth(double stripeWidth) { + double oldSripeWidth = getStripeWidth(); + this.stripeWidth = stripeWidth; + firePropertyChange("stripeWidth", oldSripeWidth, getStripeWidth()); + } + + /** + * Get the current spacing between the stripes + * + * @return the spacing between pinstripes + */ + public double getSpacing() { + return spacing; + } + + /** + * Sets the spacing between pinstripes + * + * @param spacing + * spacing between pinstripes + */ + public void setSpacing(double spacing) { + double old = getSpacing(); + this.spacing = spacing; + firePropertyChange("spacing", old, getSpacing()); + } + + /** + * {@inheritDoc} + */ + @Override + protected void doPaint(Graphics2D g, Object component, int width, int height) { + // draws pinstripes at the angle specified in this class and at the given distance apart + Shape oldClip = g.getClip(); + Shape newClip; + + if (oldClip == null) { + newClip = new Rectangle(width, height); + } else { + Rectangle r = oldClip.getBounds(); + r.width = width; + r.height = height; + + Area a = new Area(r); + a.intersect(new Area(oldClip)); + + newClip = a; + } + + int startX = newClip.getBounds().x; + int startY = newClip.getBounds().y; + + g.setClip(newClip); + g.setPaint(getForegroundPaint(getPaint(), component)); + g.setStroke(new BasicStroke((float) getStripeWidth())); + g.rotate(toRadians(getAngle())); + + double hypLength = hypot(width, height); + double gap = getSpacing() + getStripeWidth(); + + int numLines = (int) round(hypLength / gap); + + for (int i = 0; i < numLines; i++) { + double x = i * gap; + + g.draw(new Line2D.Double(startX + x, startY - hypLength, startX + x, startY + hypLength)); + } + + g.setClip(oldClip); + } +} diff --git a/src/main/java/org/jdesktop/swingx/painter/RectanglePainter.java b/src/main/java/org/jdesktop/swingx/painter/RectanglePainter.java new file mode 100644 index 0000000000..0f1321e220 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/painter/RectanglePainter.java @@ -0,0 +1,265 @@ +/* + * $Id: RectanglePainter.java 4147 2012-02-01 17:13:24Z kschaefe $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.painter; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.painter.effects.AreaEffect; +import org.jdesktop.swingx.util.ShapeUtils; + +import java.awt.*; +import java.awt.geom.Rectangle2D; +import java.awt.geom.RectangularShape; +import java.awt.geom.RoundRectangle2D; + +/** + * A painter which paints square and rounded rectangles + * + * @author joshua.marinacci@sun.com + */ +@JavaBean +@SuppressWarnings("nls") +public class RectanglePainter extends AbstractAreaPainter { + private boolean rounded = false; + //private Insets insets = new Insets(0,0,0,0); + private int roundWidth = 20; + private int roundHeight = 20; + private int width = -1; + private int height = -1; + //private double strokeWidth = 1; + + /** Creates a new instance of RectanglePainter */ + public RectanglePainter() { + this(0, 0, 0, 0); + } + + public RectanglePainter(int top, int left, int bottom, int right) { + this(top, left, bottom, right, 0, 0); + } + + public RectanglePainter(int top, int left, int bottom, int right, + int roundWidth, int roundHeight) { + this(top, left, bottom, right, roundWidth, roundHeight, roundWidth != 0 || roundHeight != 0, Color.RED, 1f, Color.BLACK); + } + + public RectanglePainter(int top, int left, int bottom, int right, int roundWidth, + int roundHeight, boolean rounded, Paint fillPaint, float strokeWidth, Paint borderPaint) { + this(new Insets(top, left, bottom, right), -1, -1, roundWidth, roundHeight, rounded, fillPaint, strokeWidth, borderPaint); + } + + public RectanglePainter(Color fillPaint, Color borderPaint) { + this(fillPaint, borderPaint, 1f, null); + } + + public RectanglePainter(Paint fillPaint, Paint borderPaint, float borderWidth, Style style) { + this(new Insets(0, 0, 0, 0), -1, -1, 0, 0, false, fillPaint, borderWidth, borderPaint); + setStyle(style); + setDirty(false); + } + + public RectanglePainter(int width, int height, int cornerRadius, Paint fillPaint) { + this(new Insets(0, 0, 0, 0), width, height, cornerRadius, cornerRadius, true, fillPaint, 1f, Color.BLACK); + } + + public RectanglePainter(Insets insets, int width, int height, int roundWidth, int roundHeight, + boolean rounded, Paint fillPaint, float strokeWidth, Paint borderPaint) { + this.width = width; + this.height = height; + setFillHorizontal(width < 0); + setFillVertical(height < 0); + setInsets(insets); + this.roundWidth = roundWidth; + this.roundHeight = roundHeight; + this.rounded = rounded; + this.setFillPaint(fillPaint); + this.setBorderWidth(strokeWidth); + this.setBorderPaint(borderPaint); + this.setDirty(false); + } + + /** + * Indicates if the rectangle is rounded + * @return if the rectangle is rounded + */ + public boolean isRounded() { + return rounded; + } + + /** + * sets if the rectangle should be rounded + * @param rounded if the rectangle should be rounded + */ + public void setRounded(boolean rounded) { + boolean oldRounded = isRounded(); + this.rounded = rounded; + setDirty(true); + firePropertyChange("rounded",oldRounded,rounded); + } + + /** + * gets the round width of the rectangle + * @return the current round width + */ + public int getRoundWidth() { + return roundWidth; + } + + /** + * sets the round width of the rectangle + * @param roundWidth a new round width + */ + public void setRoundWidth(int roundWidth) { + int oldRoundWidth = getRoundWidth(); + this.roundWidth = roundWidth; + setDirty(true); + firePropertyChange("roundWidth",oldRoundWidth,roundWidth); + } + + /** + * gets the round height of the rectangle + * @return the current round height + */ + public int getRoundHeight() { + return roundHeight; + } + + /** + * sets the round height of the rectangle + * @param roundHeight a new round height + */ + public void setRoundHeight(int roundHeight) { + int oldRoundHeight = getRoundHeight(); + this.roundHeight = roundHeight; + setDirty(true); + firePropertyChange("roundHeight",oldRoundHeight,roundHeight); + } + + + /* ======== drawing code ============ */ + protected RectangularShape calculateShape(int width, int height) { + Insets insets = getInsets(); + int x = insets.left; + int y = insets.top; + + // use the position calcs from the super class + Rectangle bounds = calculateLayout(this.width, this.height, width, height); + if(this.width != -1 && !isFillHorizontal()) { + width = this.width; + x = bounds.x; + } + if(this.height != -1 && !isFillVertical()) { + height = this.height; + y = bounds.y; + } + + if(isFillHorizontal()) { + width = width - insets.left - insets.right; + } + if(isFillVertical()) { + height = height - insets.top - insets.bottom; + } + + + RectangularShape shape = new Rectangle2D.Double(x, y, width, height); + if(rounded) { + shape = new RoundRectangle2D.Double(x, y, width, height, roundWidth, roundHeight); + } + return shape; + } + + + + @Override + protected void doPaint(Graphics2D g, Object component, int width, int height) { + Shape shape = provideShape(g, component, width, height); + switch (getStyle()) { + case BOTH: + drawBackground(g,shape,width,height); + drawBorder(g,shape,width,height); + break; + case FILLED: + drawBackground(g,shape,width,height); + break; + case OUTLINE: + drawBorder(g,shape,width,height); + break; + } + + // background + // border + // leave the clip to support masking other painters + ShapeUtils.mergeClip(g,shape); + /* + Area area = new Area(g.getClip()); + area.intersect(new Area(shape));//new Rectangle(0,0,width,height))); + g.setClip(area);*/ + //g.setClip(shape); + } + + private void drawBorder(Graphics2D g, Shape shape, int width, int height) { + Paint p = getBorderPaint(); + if(isPaintStretched()) { + p = calculateSnappedPaint(p, width, height); + } + + g.setPaint(p); + + g.setStroke(new BasicStroke(getBorderWidth())); + + // shrink the border by 1 px + if(shape instanceof Rectangle2D) { + Rectangle2D rect = (Rectangle2D) shape; + + g.draw(new Rectangle2D.Double(rect.getX(), rect.getY(), + rect.getWidth()-1, rect.getHeight()-1)); + } else if(shape instanceof RoundRectangle2D) { + RoundRectangle2D rect = (RoundRectangle2D) shape; + + g.draw(new RoundRectangle2D.Double(rect.getX(), rect.getY(), + rect.getWidth()-1, rect.getHeight()-1, + rect.getArcWidth(), rect.getArcHeight())); + } else { + g.draw(shape); + } + } + + private void drawBackground(Graphics2D g, Shape shape, int width, int height) { + Paint p = getFillPaint(); + if(isPaintStretched()) { + p = calculateSnappedPaint(p, width, height); + } + + g.setPaint(p); + + g.fill(shape); + if(getAreaEffects() != null) { + for(AreaEffect ef : getAreaEffects()) { + ef.apply(g, shape, width, height); + } + } + } + + @Override + protected Shape provideShape(Graphics2D g, Object comp, int width, int height) { + return calculateShape(width,height); + } +} + diff --git a/src/main/java/org/jdesktop/swingx/painter/ShapePainter.java b/src/main/java/org/jdesktop/swingx/painter/ShapePainter.java new file mode 100644 index 0000000000..a4c18ed9d4 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/painter/ShapePainter.java @@ -0,0 +1,211 @@ +/* + * $Id: ShapePainter.java 4147 2012-02-01 17:13:24Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.painter; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.painter.effects.AreaEffect; + +import java.awt.*; +import java.awt.geom.Ellipse2D; + +import static org.jdesktop.swingx.painter.PainterUtils.getBackgroundPaint; +import static org.jdesktop.swingx.painter.PainterUtils.getForegroundPaint; + + +/** + *

      A Painter that paints java.awt.Shapes. It uses a stroke and a fillPaint to do so. The + * shape is painted as is, at a specific location. If no Shape is specified, nothing + * will be painted. If no stroke is specified, the default for the Graphics2D + * will be used. If no fillPaint is specified, the component background color + * will be used. The shape can be positioned using the insets, horizontal, and + * vertical properties.

      + * + *

      Here is an example that draws a rectangle aligned on the center right: + *

      
      + *  Rectangle2D.Double rect = new Rectangle2D.Double(0, 0, 50, 50);
      + *  ShapePainter p = new ShapePainter(rect);
      + * p.setHorizontal(HorizontalAlignment.RIGHT);
      + * p.setVertical(VerticalAlignment.CENTER);
      + * 
      + * @author rbair + */ +@JavaBean +@SuppressWarnings("nls") +public class ShapePainter extends AbstractAreaPainter { + /** + * The Shape to fillPaint. If null, nothing is painted. + */ + private Shape shape; + + /** + * Create a new ShapePainter + */ + public ShapePainter() { + super(); + this.shape = new Ellipse2D.Double(0,0,100,100); + this.setBorderWidth(3); + this.setFillPaint(Color.RED); + this.setBorderPaint(Color.BLACK); + } + + /** + * Create a new ShapePainter with the specified shape. + * + * + * @param shape the shape to fillPaint + */ + public ShapePainter(Shape shape) { + super(); + this.shape = shape; + } + + /** + * Create a new ShapePainter with the specified shape and fillPaint. + * + * + * @param shape the shape to fillPaint + * @param paint the fillPaint to be used to fillPaint the shape + */ + public ShapePainter(Shape shape, Paint paint) { + super(); + this.shape = shape; + this.setFillPaint(paint); + } + + /** + * Create a new ShapePainter with the specified shape and fillPaint. The shape + * can be filled or stroked (only the outline is painted). + * + * + * @param shape the shape to fillPaint + * @param paint the fillPaint to be used to fillPaint the shape + * @param style specifies the ShapePainter.Style to use for painting this shape. + * If null, then Style.BOTH is used + */ + public ShapePainter(Shape shape, Paint paint, Style style) { + super(); + this.shape = shape; + this.setFillPaint(paint); + this.setStyle(style == null ? Style.BOTH : style); + } + + /** + * Sets the shape to fillPaint. This shape is not resized when the component + * bounds are. To do that, create a custom shape that is bound to the + * component width/height + * + * + * @param s the Shape to fillPaint. May be null + */ + public void setShape(Shape s) { + Shape old = getShape(); + this.shape = s; + setDirty(true); + firePropertyChange("shape", old, getShape()); + } + + /** + * Gets the current shape + * @return the Shape to fillPaint. May be null + */ + public Shape getShape() { + return shape; + } + + /** + * {@inheritDoc} + */ + @Override + protected void doPaint(Graphics2D g, Object component, int w, int h) { + g.setStroke(new BasicStroke(this.getBorderWidth())); + + if(getShape() != null) { + Shape s = provideShape(g,component, w, h); + Rectangle bounds = s.getBounds(); + Rectangle rect = calculateLayout(bounds.width, bounds.height, w, h); + //u.p("rect = " + rect); + g = (Graphics2D)g.create(); + + try { + g.translate(rect.x, rect.y); + //draw/fill the shape + drawPathEffects(g, s, rect.width, rect.height); + switch (getStyle()) { + case BOTH: + drawShape(g, s, component, rect.width, rect.height); + fillShape(g, s, component, rect.width, rect.height); + break; + case FILLED: + fillShape(g, s, component, rect.width, rect.height); + break; + case OUTLINE: + drawShape(g, s, component, rect.width, rect.height); + break; + } + } finally { + g.dispose(); + } + } + } + + private void drawShape(Graphics2D g, Shape s, Object component, int w, int h) { + g.setPaint(calculateStrokePaint(component, w, h)); + g.draw(s); + } + + private void fillShape(Graphics2D g, Shape s, Object component, int w, int h) { + g.setPaint(calculateFillPaint(component, w, h)); + g.fill(s); + } + + // shape effect stuff + @Override + protected Shape provideShape(Graphics2D g, Object comp, int width, int height) { + return getShape(); + } + + private Paint calculateStrokePaint(Object component, int width, int height) { + Paint p = getForegroundPaint(getBorderPaint(), component); + if(isPaintStretched()) { + p = calculateSnappedPaint(p, width, height); + } + return p; + } + + private Paint calculateFillPaint(Object component, int width, int height) { + //set the fillPaint + Paint p = getBackgroundPaint(getFillPaint(), component); + if(isPaintStretched()) { + p = calculateSnappedPaint(p, width, height); + } + return p; + } + + private void drawPathEffects(Graphics2D g, Shape s, int w, int h) { + if(getAreaEffects() != null) { + //Paint pt = calculateFillPaint(component, w, h); + for(AreaEffect ef : getAreaEffects()) { + ef.apply(g, s, w, h); + } + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/painter/TextPainter.java b/src/main/java/org/jdesktop/swingx/painter/TextPainter.java new file mode 100644 index 0000000000..02e8a056ef --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/painter/TextPainter.java @@ -0,0 +1,208 @@ +/* + * $Id: TextPainter.java 4147 2012-02-01 17:13:24Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.painter; + +import org.jdesktop.beans.JavaBean; +import org.jdesktop.swingx.painter.effects.AreaEffect; + +import javax.swing.*; +import javax.swing.text.JTextComponent; +import java.awt.*; +import java.awt.font.GlyphVector; + +import static org.jdesktop.swingx.painter.PainterUtils.getComponentFont; +import static org.jdesktop.swingx.painter.PainterUtils.getForegroundPaint; + +/** + * A painter which draws text. If the font, text, and paint are not provided they will be + * obtained from the object being painted if it is a Swing text component. + * + * @author rbair + */ +@JavaBean +@SuppressWarnings("nls") +public class TextPainter extends AbstractAreaPainter { + private String text = ""; + private Font font = null; + + /** Creates a new instance of TextPainter */ + public TextPainter() { + this(""); + } + + /** + * Create a new TextPainter which will paint the specified text + * @param text the text to paint + */ + public TextPainter(String text) { + this(text, null, null); + } + + /** + * Create a new TextPainter which will paint the specified text with the specified font. + * @param text the text to paint + * @param font the font to paint the text with + */ + public TextPainter(String text, Font font) { + this(text, font, null); + } + + /** + * Create a new TextPainter which will paint the specified text with the specified paint. + * @param text the text to paint + * @param paint the paint to paint with + */ + public TextPainter(String text, Paint paint) { + this(text, null, paint); + } + + /** + * Create a new TextPainter which will paint the specified text with the specified font and paint. + * @param text the text to paint + * @param font the font to paint the text with + * @param paint the paint to paint with + */ + public TextPainter(String text, Font font, Paint paint) { + this.text = text; + this.font = font; + setFillPaint(paint); + } + + /** + * Set the font (and font size and style) to be used when drawing the text + * @param f the new font + */ + public void setFont(Font f) { + Font old = getFont(); + this.font = f; + setDirty(true); + firePropertyChange("font", old, getFont()); + } + + /** + * gets the font (and font size and style) to be used when drawing the text + * @return the current font + */ + public Font getFont() { + return font; + } + + /** + * Sets the text to draw + * @param text the text to draw + */ + public void setText(String text) { + String old = getText(); + this.text = text == null ? "" : text; + setDirty(true); + firePropertyChange("text", old, getText()); + } + + /** + * gets the text currently used to draw + * @return the text to be drawn + */ + public String getText() { + return text; + } + + /** + * {@inheritDoc} + */ + @Override + protected void doPaint(Graphics2D g, Object component, int width, int height) { + Font f = calculateFont(component); + if (f != null) { + g.setFont(f); + } + + Paint paint = getForegroundPaint(getFillPaint(), component); + String t = calculateText(component); + + // get the font metrics + FontMetrics metrics = g.getFontMetrics(g.getFont()); + //Rectangle2D rect = metrics.getStringBounds(text,g); + + int tw = metrics.stringWidth(t); + int th = metrics.getHeight(); + Rectangle res = calculateLayout(tw, th, width, height); + + g.translate(res.x, res.y); + + if(isPaintStretched()) { + paint = calculateSnappedPaint(paint, res.width, res.height); + } + + if (paint != null) { + g.setPaint(paint); + } + + g.drawString(t, 0, 0 + metrics.getAscent()); + if(getAreaEffects() != null) { + Shape shape = provideShape(g, component, width, height); + for(AreaEffect ef : getAreaEffects()) { + ef.apply(g, shape, width, height); + } + } + g.translate(-res.x,-res.y); + } + + private String calculateText(final Object component) { + // prep the text + String t = getText(); + //make components take priority if(text == null || text.trim().equals("")) { + if(t != null && !t.trim().equals("")) { + return t; + } + if(component instanceof JTextComponent) { + t = ((JTextComponent)component).getText(); + } + if(component instanceof JLabel) { + t = ((JLabel)component).getText(); + } + if(component instanceof AbstractButton) { + t = ((AbstractButton)component).getText(); + } + return t; + } + + private Font calculateFont(final Object component) { + // prep the various text attributes + Font f = getComponentFont(getFont(), component); + if (f == null) { + f = new Font("Dialog", Font.PLAIN, 18); + } + return f; + } + + /** + * {@inheritDoc} + */ + @Override + protected Shape provideShape(Graphics2D g2, Object comp, int width, int height) { + Font f = calculateFont(comp); + String t = calculateText(comp); + FontMetrics metrics = g2.getFontMetrics(f); + GlyphVector vect = f.createGlyphVector(g2.getFontRenderContext(),t); + return vect.getOutline(0f,0f+ metrics.getAscent()); + } +} diff --git a/src/main/java/org/jdesktop/swingx/painter/effects/AbstractAreaEffect.java b/src/main/java/org/jdesktop/swingx/painter/effects/AbstractAreaEffect.java new file mode 100644 index 0000000000..75d1d85f33 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/painter/effects/AbstractAreaEffect.java @@ -0,0 +1,399 @@ +/* + * $Id: AbstractAreaEffect.java 4082 2011-11-15 18:39:43Z kschaefe $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +package org.jdesktop.swingx.painter.effects; + +import java.awt.*; +import java.awt.geom.Area; +import java.awt.geom.Point2D; +import java.awt.image.BufferedImage; + +import static org.jdesktop.swingx.util.GraphicsUtilities.createCompatibleTranslucentImage; + +/** + * The abstract base class for path effects. It takes care + * of soft clipping and interpolating brush sizes and colors. Subclasses + * can change these values to provide prefab effect behavior, like + * dropshadows and glows. + * @author joshy + */ +@SuppressWarnings("nls") +public class AbstractAreaEffect implements AreaEffect { + private static final boolean debug = false; + /** + * Creates a new instance of AreaEffect + */ + public AbstractAreaEffect() { + setBrushColor(Color.BLACK); + setBrushSteps(10); + setEffectWidth(8); + setRenderInsideShape(false); + setOffset(new Point(4,4)); + setShouldFillShape(true); + setShapeMasked(true); + } + + @Override + public void apply(Graphics2D g, Shape clipShape, int width, int height) { + // create a rect to hold the bounds + width = (int)(clipShape.getBounds2D().getWidth() + clipShape.getBounds2D().getX()); + height = (int)(clipShape.getBounds2D().getHeight() + clipShape.getBounds2D().getY()); + Rectangle effectBounds = new Rectangle(0,0, + width + getEffectWidth()*2 + 1, + height + getEffectWidth()*2 + 1); + + // Apply the border glow effect + if (isShapeMasked()) { + BufferedImage clipImage = getClipImage(effectBounds); + Graphics2D g2 = clipImage.createGraphics(); + + try { + // clear the buffer + g2.setPaint(Color.BLACK); + g2.setComposite(AlphaComposite.Clear); + g2.fillRect(0, 0, effectBounds.width, effectBounds.height); + + if (debug) { + g2.setPaint(Color.WHITE); + g2.setComposite(AlphaComposite.SrcOver); + g2.drawRect(0, 0, effectBounds.width - 1, + effectBounds.height - 1); + } + + // turn on smoothing + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + g2.translate(getEffectWidth() - getOffset().getX(), + getEffectWidth() - getOffset().getY()); + paintBorderGlow(g2, clipShape, width, height); + + // clip out the parts we don't want + g2.setComposite(AlphaComposite.Clear); + g2.setColor(Color.WHITE); + if (isRenderInsideShape()) { + // clip the outside + Area area = new Area(effectBounds); + area.subtract(new Area(clipShape)); + g2.fill(area); + } else { + // clip the inside + g2.fill(clipShape); + } + } finally { + // draw the final image + g2.dispose(); + } + + g.drawImage(clipImage, -getEffectWidth() + (int) getOffset().getX(), -getEffectWidth() + (int) getOffset().getY(), null); + } else { + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + paintBorderGlow(g, clipShape, width, height); + } + + //g.setColor(Color.MAGENTA); + //g.draw(clipShape.getBounds2D()); + //g.drawRect(0,0,width,height); + + } + + BufferedImage _clipImage = null; + private BufferedImage getClipImage(final Rectangle effectBounds) { + // set up a temp buffer + if(_clipImage == null || + _clipImage.getWidth() != effectBounds.width || + _clipImage.getHeight() != effectBounds.height) { + _clipImage = createCompatibleTranslucentImage(effectBounds.width, effectBounds.height); + } + _clipImage.getGraphics().clearRect(0,0,_clipImage.getWidth(), _clipImage.getHeight()); + return _clipImage; + } + + + /* + private BufferedImage createClipImage(Shape s, Graphics2D g, int width, int height) { + // Create a translucent intermediate image in which we can perform + // the soft clipping + + GraphicsConfiguration gc = g.getDeviceConfiguration(); + BufferedImage img = gc.createCompatibleImage(width, height, Transparency.TRANSLUCENT); + Graphics2D g2 = img.createGraphics(); + + // Clear the image so all pixels have zero alpha + g2.setComposite(AlphaComposite.Clear); + g2.fillRect(0, 0, width, height); + + // Render our clip shape into the image. Note that we enable + // antialiasing to achieve the soft clipping effect. Try + // commenting out the line that enables antialiasing, and + // you will see that you end up with the usual hard clipping. + g2.setComposite(AlphaComposite.Src); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2.setColor(Color.WHITE); + g2.fill(s); + g2.dispose(); + + return img; + }*/ + + + /* draws the actual shaded border to the specified graphics + */ + /** + * Paints the border glow + * @param g2 + * @param clipShape + * @param width + * @param height + */ + protected void paintBorderGlow(Graphics2D g2, + Shape clipShape, int width, int height) { + + int steps = getBrushSteps(); + float brushAlpha = 1f/steps; + + boolean inside = isRenderInsideShape(); + + g2.setPaint(getBrushColor()); + + g2.translate(offset.getX(), offset.getY()); + + if(isShouldFillShape()) { + // set the inside/outside mode + if(inside) { + g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 1f)); + Area a1 = new Area(new Rectangle( + (int)-offset.getX()-20, + (int)-offset.getY()-20, + width+40,height+40)); + Area a2 = new Area(clipShape); + a1.subtract(a2); + g2.fill(a1); + } else { + g2.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_OVER, 1f)); + g2.fill(clipShape); + } + + } + + // set the inside/outside mode + /* + if(inside) { + g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, brushAlpha)); + } else { + g2.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_OVER, brushAlpha)); + }*/ + g2.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_OVER, brushAlpha)); + + // draw the effect + for(float i=0; i=0; i=i-1f) { + float brushWidth = i * getEffectWidth()/steps; + gfx.setPaint(interpolateColor(i/steps,edgeColor,centerColor)); + gfx.setStroke(new BasicStroke(brushWidth, + BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); + gfx.draw(clipShape); + }*/ + + /* // an interesting outline effect. stroke the shape with a wide brush + * // then stroke again with slightly less wide one, then don't fill the middle + for(int i=0; i<2; i++) { + float brushWidth = (2-i)*5; + p("widdth = " + brushWidth); + gfx.setPaint(interpolateColor((float)(1-i), Color.BLACK, Color.WHITE)); + gfx.setStroke(new BasicStroke(brushWidth)); + gfx.draw(clipShape); + } + */ + gfx.translate(getOffset().getX(), getOffset().getY()); + gfx.setComposite(AlphaComposite.SrcOver); + int steps = getEffectWidth(); + if(borderPosition == BorderPosition.Centered) { + steps = steps/2; + } + for(int i=0; i + * + * @author Frederic Lavigne + * @author Karl Schaefer + */ +@SuppressWarnings("nls") +public abstract class AbstractComponentAddon implements ComponentAddon { + + private String name; + + protected AbstractComponentAddon(String name) { + this.name = name; + } + + @Override + public final String getName() { + return name; + } + + @Override + public void initialize(LookAndFeelAddons addon) { + addon.loadDefaults(getDefaults(addon)); + } + + @Override + public void uninitialize(LookAndFeelAddons addon) { + // commented after Issue 446. Maybe addon should keep track of its + // added defaults to correctly remove them on uninitialize + // addon.unloadDefaults(getDefaults(addon)); + } + + /** + * Adds default key/value pairs to the given list. + * + * @param addon + * @param defaults + */ + protected void addBasicDefaults(LookAndFeelAddons addon, + DefaultsList defaults) { + } + + /** + * Default implementation calls + * {@link #addBasicDefaults(LookAndFeelAddons, DefaultsList)} + * + * @param addon + * @param defaults + */ + protected void addMacDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + addBasicDefaults(addon, defaults); + } + + /** + * Default implementation calls + * {@link #addBasicDefaults(LookAndFeelAddons, DefaultsList)} + * + * @param addon + * @param defaults + */ + protected void addMetalDefaults(LookAndFeelAddons addon, + DefaultsList defaults) { + addBasicDefaults(addon, defaults); + } + + /** + * Default implementation calls + * {@link #addBasicDefaults(LookAndFeelAddons, DefaultsList)} + * + * @param addon + * @param defaults + */ + protected void addMotifDefaults(LookAndFeelAddons addon, + DefaultsList defaults) { + addBasicDefaults(addon, defaults); + } + + /** + * Default implementation calls + * {@link #addBasicDefaults(LookAndFeelAddons, DefaultsList)} + * + * @param addon + * @param defaults + */ + protected void addWindowsDefaults(LookAndFeelAddons addon, + DefaultsList defaults) { + addBasicDefaults(addon, defaults); + } + + /** + * Default implementation calls + * {@link #addBasicDefaults(LookAndFeelAddons, DefaultsList)} + * + * @param addon + * @param defaults + */ + protected void addLinuxDefaults(LookAndFeelAddons addon, + DefaultsList defaults) { + addBasicDefaults(addon, defaults); + } + + /** + * Default implementation calls + * {@link #addBasicDefaults(LookAndFeelAddons, DefaultsList)} + * + * @param addon + * @param defaults + */ + protected void addNimbusDefaults(LookAndFeelAddons addon, + DefaultsList defaults) { + addBasicDefaults(addon, defaults); + } + + /** + * Gets the defaults for the given addon. + * + * Based on the addon, it calls + * {@link #addMacDefaults(LookAndFeelAddons, DefaultsList)} if isMac() or + * {@link #addMetalDefaults(LookAndFeelAddons, DefaultsList)} if isMetal() + * or {@link #addMotifDefaults(LookAndFeelAddons, DefaultsList)} if + * isMotif() or {@link #addWindowsDefaults(LookAndFeelAddons, DefaultsList)} + * if isWindows() or + * {@link #addBasicDefaults(LookAndFeelAddons, DefaultsList)} if none of the + * above was called. + * + * @param addon + * @return an array of key/value pairs. For example: + * + *
      +     * Object[] uiDefaults = { "Font", new Font("Dialog", Font.BOLD, 12), "Color",
      +     *         Color.red, "five", new Integer(5) };
      +     * 
      + */ + private Object[] getDefaults(LookAndFeelAddons addon) { + DefaultsList defaults = new DefaultsList(); + if (isWindows(addon)) { + addWindowsDefaults(addon, defaults); + } else if (isMetal(addon)) { + addMetalDefaults(addon, defaults); + } else if (isMac(addon)) { + addMacDefaults(addon, defaults); + } else if (isMotif(addon)) { + addMotifDefaults(addon, defaults); + // PENDING JW: the separation line here looks fishy + // what about Nimbus on Linux systems? + } else if (isLinux(addon)) { + addLinuxDefaults(addon, defaults); + } else if (isNimbus(addon)) { + addNimbusDefaults(addon, defaults); + } else { + // at least add basic defaults + addBasicDefaults(addon, defaults); + } + return defaults.toArray(); + } + + // + // Helper methods to make ComponentAddon developer life easier + // + + /** + * @return true if the addon is the Windows addon or its subclasses + */ + protected boolean isWindows(LookAndFeelAddons addon) { + return addon instanceof WindowsLookAndFeelAddons; + } + + /** + * @return true if the addon is the Metal addon or its subclasses + */ + protected boolean isMetal(LookAndFeelAddons addon) { + return addon instanceof MetalLookAndFeelAddons; + } + + /** + * @return true if the addon is the Mac OS X addon or its subclasses + */ + protected boolean isMac(LookAndFeelAddons addon) { + return addon instanceof MacOSXLookAndFeelAddons; + } + + /** + * @return true if the addon is the Motif addon or its subclasses + */ + protected boolean isMotif(LookAndFeelAddons addon) { + return addon instanceof MotifLookAndFeelAddons; + } + + /** + * @return true if the current look and feel is Linux + */ + protected boolean isLinux(LookAndFeelAddons addon) { + return addon instanceof LinuxLookAndFeelAddons; + } + + /** + * @return true if the current look and feel is Nimbus + */ + protected boolean isNimbus(LookAndFeelAddons addon) { + return addon instanceof NimbusLookAndFeelAddons; + } + + /** + * @return true if the current look and feel is one of JGoodies Plastic l&fs + */ + protected boolean isPlastic() { + return UIManager.getLookAndFeel().getClass().getName() + .contains("Plastic"); + } + + /** + * @return true if the current look and feel is Synth l&f + */ + protected boolean isSynth() { + return UIManager.getLookAndFeel().getClass().getName().contains("ynth"); + } + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/AbstractUIChangeHandler.java b/src/main/java/org/jdesktop/swingx/plaf/AbstractUIChangeHandler.java new file mode 100644 index 0000000000..f491d15f6a --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/AbstractUIChangeHandler.java @@ -0,0 +1,30 @@ +package org.jdesktop.swingx.plaf; + +import javax.swing.*; +import java.beans.PropertyChangeListener; +import java.util.Map; +import java.util.WeakHashMap; + +@SuppressWarnings("nls") +public abstract class AbstractUIChangeHandler implements PropertyChangeListener { + //prevent double installation. + private final Map installed = new WeakHashMap(); + + public void install(JComponent c){ + if(isInstalled(c)){ + return; + } + + c.addPropertyChangeListener("UI", this); + installed.put(c, Boolean.FALSE); + } + + public boolean isInstalled(JComponent c) { + return installed.containsKey(c); + } + + public void uninstall(JComponent c){ + c.removePropertyChangeListener("UI", this); + installed.remove(c); + } +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/plaf/BuddyLayoutAndBorder.java b/src/main/java/org/jdesktop/swingx/plaf/BuddyLayoutAndBorder.java new file mode 100644 index 0000000000..b9a60ec2d1 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/BuddyLayoutAndBorder.java @@ -0,0 +1,264 @@ +package org.jdesktop.swingx.plaf; + +import org.jdesktop.swingx.prompt.BuddySupport; +import org.jdesktop.swingx.prompt.BuddySupport.Position; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.plaf.UIResource; +import javax.swing.plaf.basic.BasicBorders.MarginBorder; +import java.awt.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +public class BuddyLayoutAndBorder implements LayoutManager, Border, PropertyChangeListener, UIResource { + private JTextField textField; + + private Border borderDelegate; + + /** + * Installs a {@link BuddyLayoutAndBorder} as a layout and border of the + * given text field. Registers a {@link PropertyChangeListener} to wrap any + * subsequently set border on the text field. + */ + protected void install(JTextField textField) { + uninstall(); + this.textField = textField; + + textField.setLayout(this); + + replaceBorderIfNecessary(); + textField.addPropertyChangeListener("border", this); + } + + public Border getBorderDelegate() { + return borderDelegate; + } + + /** + * Wraps and replaces the text fields default border with this object, to + * honor the button margins and sizes of the search, clear and popup buttons + * and the layout style. + */ + protected void replaceBorderIfNecessary() { + Border original = textField.getBorder(); + + if (!(original instanceof BuddyLayoutAndBorder)) { + borderDelegate = original; + textField.setBorder(this); + } + } + + /** + * Does nothing. + * + * @see BuddySupport#add(javax.swing.JComponent, Position, JTextField) + */ + @Override + public void addLayoutComponent(String name, Component comp) { + } + + @Override + public Dimension minimumLayoutSize(Container parent) { + return preferredLayoutSize(parent); + } + + @Override + public Dimension preferredLayoutSize(Container parent) { + Dimension d = new Dimension(); + + // height of highest buddy. + for (Component c : BuddySupport.getLeft(textField)) { + d.height = Math.max(d.height, c.getPreferredSize().height); + } + for (Component c : BuddySupport.getRight(textField)) { + d.height = Math.max(d.height, c.getPreferredSize().height); + } + + Insets insets = getRealBorderInsets(); + d.height += insets.top + insets.bottom; + d.width += insets.left + insets.right; + + Insets outerMargin = BuddySupport.getOuterMargin(textField); + if (outerMargin != null) { + d.width += outerMargin.left + outerMargin.right; + d.height += outerMargin.bottom + outerMargin.top; + } + + return d; + } + + /** + * Does nothing. + * + * @see BuddySupport#remove(javax.swing.JComponent, JTextField) + */ + @Override + public void removeLayoutComponent(Component comp) { + } + + @Override + public void layoutContainer(Container parent) { + Rectangle visibleRect = getVisibleRect(); + Dimension size; + + for (Component comp : BuddySupport.getLeft(textField)) { + if (!comp.isVisible()) { + continue; + } + size = comp.getPreferredSize(); + comp.setBounds(visibleRect.x, centerY(visibleRect, size), size.width, size.height); + + visibleRect.x += size.width; + visibleRect.width -= size.width; + } + + for (Component comp : BuddySupport.getRight(textField)) { + if (!comp.isVisible()) { + continue; + } + + size = comp.getPreferredSize(); + comp.setBounds(visibleRect.x + visibleRect.width - size.width, centerY(visibleRect, size), size.width, + size.height); + visibleRect.width -= size.width; + } + } + + protected int centerY(Rectangle rect, Dimension size) { + return (int) (rect.getCenterY() - (size.height / 2)); + } + + /** + * @return the rectangle allocated by the text field, including the space + * allocated by the child components left and right, the text fields + * original border insets and the outer margin. + * + */ + protected Rectangle getVisibleRect() { + Rectangle alloc = SwingUtilities.getLocalBounds(textField); + + substractInsets(alloc, getRealBorderInsets()); + substractInsets(alloc, BuddySupport.getOuterMargin(textField)); + + return alloc; + } + + private void substractInsets(Rectangle alloc, Insets insets) { + if (insets != null) { + alloc.x += insets.left; + alloc.y += insets.top; + alloc.width -= insets.left + insets.right; + alloc.height -= insets.top + insets.bottom; + } + } + + /** + * Returns the {@link Insets} of the original {@link Border} plus the space + * required by the child components. + * + * @see Border#getBorderInsets(Component) + */ + @Override + public Insets getBorderInsets(Component c) { + Insets insets = null; + if (borderDelegate != null) { + // Original insets are cloned to make it work in Mac OS X Aqua LnF. + // Seems that this LnF uses a shared insets instance which should + // not be modified. + // Include margin here + insets = (Insets) borderDelegate.getBorderInsets(textField).clone(); + } else { + insets = new Insets(0, 0, 0, 0); + } + //somehow this happens sometimes + if (textField == null) { + return insets; + } + + for (Component comp : BuddySupport.getLeft(textField)) { + insets.left += comp.isVisible() ? comp.getPreferredSize().width : 0; + } + for (Component comp : BuddySupport.getRight(textField)) { + insets.right += comp.isVisible() ? comp.getPreferredSize().width : 0; + } + + Insets outerMargin = BuddySupport.getOuterMargin(textField); + if (outerMargin != null) { + insets.left += outerMargin.left; + insets.right += outerMargin.right; + insets.top += outerMargin.top; + insets.bottom += outerMargin.bottom; + } + + return insets; + } + + /** + * Returns the insets of the original border (without the margin! Beware of + * {@link MarginBorder}!). + * + * @return the insets of the border delegate + */ + public Insets getRealBorderInsets() { + if (borderDelegate == null) { + //SwingX 1287: null borders are possible and give no insets + return new Insets(0, 0, 0, 0); + } + + Insets insets = borderDelegate.getBorderInsets(textField); + + // for some reason, all LnFs add the margin to the insets. + // we want the insets without the margin, so substract the margin here!! + // TODO: consider checking, if the current border really includes the + // margin. Consider: + // 1. Not only MarginBorder adds margin + // 2. Calling getBorderInsets(null) is not appropriate, since some + // Borders can't handle null values. + Insets margin = textField.getMargin(); + if (margin != null) { + insets.left -= margin.left; + insets.right -= margin.right; + insets.top -= margin.top; + insets.bottom -= margin.bottom; + } + + return insets; + } + + @Override + public boolean isBorderOpaque() { + if (borderDelegate == null) { + return false; + } + return borderDelegate.isBorderOpaque(); + } + + @Override + public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { + if (borderDelegate != null) { + borderDelegate.paintBorder(c, g, x, y, width, height); + } + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + replaceBorderIfNecessary(); + } + + public void uninstall() { + if (textField != null) { + textField.removePropertyChangeListener("border", this); + if (textField.getBorder() == this) { + textField.setBorder(borderDelegate); + } + textField.setLayout(null); + textField = null; + } + } + + @Override + public String toString() { + return String.format("%s (%s): %s", getClass().getName(), getBorderInsets(null), borderDelegate); + } +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/plaf/BuddyTextFieldUI.java b/src/main/java/org/jdesktop/swingx/plaf/BuddyTextFieldUI.java new file mode 100644 index 0000000000..af72700cd1 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/BuddyTextFieldUI.java @@ -0,0 +1,88 @@ +package org.jdesktop.swingx.plaf; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.plaf.TextUI; +import java.awt.*; + +/** + *

      + * TODO: queries the text components layout manager for the preferred size. + *

      + * + * @author Peter Weishapl + * + */ +public class BuddyTextFieldUI extends PromptTextFieldUI { + protected BuddyLayoutAndBorder layoutAndBorder; + + // Bad hacking: FIXME when know how to get the real margin. + private static final Insets MAC_MARGIN = new Insets(0, 2, 1, 2); + + @Override + public void paint(Graphics g, JComponent c) { + // yet another dirty mac hack to prevent painting background outside of + // border. + if (hasMacTextFieldBorder(c)) { + Insets borderInsets = layoutAndBorder.getRealBorderInsets(); + + borderInsets.left -= MAC_MARGIN.left; + int height = c.getHeight() - borderInsets.bottom - borderInsets.top + MAC_MARGIN.bottom + MAC_MARGIN.top; + int width = c.getWidth() - borderInsets.left - borderInsets.right + MAC_MARGIN.right; + g.clipRect(borderInsets.left, borderInsets.top, width, height); + } + super.paint(g, c); + } + + private boolean hasMacTextFieldBorder(JComponent c) { + Border border = c.getBorder(); + if (border == layoutAndBorder) { + border = layoutAndBorder.getBorderDelegate(); + } + return border != null && border.getClass().getName().equals("apple.laf.CUIAquaTextFieldBorder"); + } + + /** + * Creates a new {@link BuddyTextFieldUI} which delegates most work to + * another {@link TextUI}. + * + * @param delegate + */ + public BuddyTextFieldUI(TextUI delegate) { + super(delegate); + } + + @Override + public void installUI(JComponent c) { + super.installUI(c); + layoutAndBorder = createBuddyLayoutAndBorder(); + layoutAndBorder.install((JTextField) c); + } + + protected BuddyLayoutAndBorder createBuddyLayoutAndBorder() { + return new BuddyLayoutAndBorder(); + } + + @Override + public void uninstallUI(JComponent c) { + layoutAndBorder.uninstall(); + super.uninstallUI(c); + } + + /** + * TODO: comment + * + * @see javax.swing.plaf.ComponentUI#getPreferredSize(JComponent) + */ + @Override + public Dimension getPreferredSize(JComponent c) { + Dimension d = new Dimension(); + Dimension cd = super.getPreferredSize(c); + Dimension ld = c.getLayout().preferredLayoutSize(c); + + d.height = Math.max(cd.height, ld.height); + d.width = Math.max(cd.width, ld.width); + + return d; + } +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/plaf/BusyLabelAddon.java b/src/main/java/org/jdesktop/swingx/plaf/BusyLabelAddon.java new file mode 100644 index 0000000000..d8abf83e1f --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/BusyLabelAddon.java @@ -0,0 +1,59 @@ +/* + * $Id: BusyLabelAddon.java 2565 2008-01-03 19:08:32Z rah003 $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf; + +import org.jdesktop.swingx.JXBusyLabel; + +import javax.swing.plaf.ColorUIResource; +import java.awt.*; +import java.awt.geom.Ellipse2D; +import java.awt.geom.RoundRectangle2D; + +/** + * Addon for JXBusyLabel.
      + * + * @author rah003 + */ +public class BusyLabelAddon extends AbstractComponentAddon { + + public BusyLabelAddon() { + super("JXBusyLabel"); + } + + @Override + protected void addBasicDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + defaults.add(JXBusyLabel.uiClassID, "org.jdesktop.swingx.plaf.basic.BasicBusyLabelUI"); + defaults.add("JXBusyLabel.delay", 100); + defaults.add("JXBusyLabel.baseColor", new ColorUIResource(Color.LIGHT_GRAY)); + defaults.add("JXBusyLabel.highlightColor", new ColorUIResource(UIManagerExt.getSafeColor("Label.foreground", Color.BLACK))); + float barLength = 8; + float barWidth = 4; + float height = 26; + defaults.add("JXBusyLabel.pointShape", new ShapeUIResource( + new RoundRectangle2D.Float(0, 0, barLength, barWidth, + barWidth, barWidth))); + defaults.add("JXBusyLabel.trajectoryShape", new ShapeUIResource( + new Ellipse2D.Float(barLength / 2, barLength / 2, height + - barLength, height - barLength))); + + + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/BusyLabelUI.java b/src/main/java/org/jdesktop/swingx/plaf/BusyLabelUI.java new file mode 100644 index 0000000000..ee9c5f15b6 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/BusyLabelUI.java @@ -0,0 +1,44 @@ +/* + * $Id: BusyLabelUI.java 3964 2011-03-17 19:12:29Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.plaf; + +import org.jdesktop.swingx.painter.BusyPainter; + +import java.awt.*; + +/** + * + * @author rah003 + */ +public interface BusyLabelUI { + /** + * @return The BusyPainter for the JXBusyLabel. If + * this method returns null, then no progress indication will be shown by busy label. + */ + public BusyPainter getBusyPainter(Dimension dim); + + /** + * Delay between moving from one point to another. The exact timing will be close to the selected value but is not guaranteed to be precise (subject to the timing precision of underlaying jvm). + * @return Delay in ms. + */ + public int getDelay(); +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/ColumnControlButtonAddon.java b/src/main/java/org/jdesktop/swingx/plaf/ColumnControlButtonAddon.java new file mode 100644 index 0000000000..1b396fc0a1 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/ColumnControlButtonAddon.java @@ -0,0 +1,50 @@ +/* + * $Id: ColumnControlButtonAddon.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf; + +import org.jdesktop.swingx.icon.ColumnControlIcon; + +import javax.swing.plaf.InsetsUIResource; + +/** + * Addon to load LF specific properties for the ColumnControlButton. + * + * @author Jeanette Winzenburg + */ +public class ColumnControlButtonAddon extends AbstractComponentAddon { + + /** + * Instantiates the addon for ColumnControlButton. + */ + public ColumnControlButtonAddon() { + super("ColumnControlButton"); + } + + @Override + protected void addBasicDefaults(LookAndFeelAddons addon, + DefaultsList defaults) { + super.addBasicDefaults(addon, defaults); + defaults.add("ColumnControlButton.actionIcon", new ColumnControlIcon()); + defaults.add("ColumnControlButton.margin", new InsetsUIResource(1, 2, 2, 1)); + } + + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/ComponentAddon.java b/src/main/java/org/jdesktop/swingx/plaf/ComponentAddon.java new file mode 100644 index 0000000000..142f132511 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/ComponentAddon.java @@ -0,0 +1,55 @@ +/* + * $Id: ComponentAddon.java 4028 2011-06-03 19:32:19Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf; + +/** + * Each new component type of the library will contribute an addon to + * the LookAndFeelAddons. A ComponentAddon is the + * equivalent of a {@link javax.swing.LookAndFeel}but focused on one + * component.
      + * + * @author Frederic Lavigne + */ +public interface ComponentAddon { + + /** + * @return the name of this addon + */ + String getName(); + + /** + * Initializes this addon (i.e register UI classes, colors, fonts, + * borders, any UIResource used by the component class). When + * initializing, the addon can register different resources based on + * the addon or the current look and feel. + * + * @param addon the current addon + */ + void initialize(LookAndFeelAddons addon); + + /** + * Uninitializes this addon. + * + * @param addon + */ + void uninitialize(LookAndFeelAddons addon); + +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/plaf/DatePickerAddon.java b/src/main/java/org/jdesktop/swingx/plaf/DatePickerAddon.java new file mode 100644 index 0000000000..f9b35a0162 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/DatePickerAddon.java @@ -0,0 +1,133 @@ +/* + * $Id: DatePickerAddon.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf; + +import org.jdesktop.swingx.JXDatePicker; +import org.jdesktop.swingx.plaf.basic.BasicDatePickerUI; +import org.jdesktop.swingx.util.OS; + +import javax.swing.*; +import javax.swing.border.LineBorder; +import javax.swing.plaf.BorderUIResource; + +/** + * @author Joshua Outwater + */ +public class DatePickerAddon extends AbstractComponentAddon { + public DatePickerAddon() { + super("JXDatePicker"); + } + + /** + * {@inheritDoc} + */ + @Override + protected void addBasicDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addBasicDefaults(addon, defaults); + + defaults.add(JXDatePicker.uiClassID, BasicDatePickerUI.class.getName()); + defaults.add("JXDatePicker.border", + new BorderUIResource(BorderFactory.createCompoundBorder( + LineBorder.createGrayLineBorder(), + BorderFactory.createEmptyBorder(3, 3, 3, 3)))); + + UIManagerExt.addResourceBundle( + "org.jdesktop.swingx.plaf.basic.resources.DatePicker"); + } + + /** + * {@inheritDoc} + */ + @Override + protected void addWindowsDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addWindowsDefaults(addon, defaults); + if (OS.isWindowsXP() && OS.isUsingWindowsVisualStyles()) { + defaults.add("JXDatePicker.arrowIcon", + LookAndFeel.makeIcon(DatePickerAddon.class, "windows/resources/combo-xp.png")); + } else { + defaults.add("JXDatePicker.arrowIcon", + LookAndFeel.makeIcon(DatePickerAddon.class, "windows/resources/combo-w2k.png")); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void addLinuxDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addLinuxDefaults(addon, defaults); + + defaults.add("JXDatePicker.arrowIcon", + LookAndFeel.makeIcon(DatePickerAddon.class, "linux/resources/combo-gtk.png")); + + if (isGTK()) { + // Issue #667-swingx: ugly border in GTK + // remove the border which was installed in addBasicDefaults + defaults.add("JXDatePicker.border", null); + } + } + + /** + * + * @return true if the LF is GTK. + */ + private boolean isGTK() { + return "GTK".equals(UIManager.getLookAndFeel().getID()); + } + + /** + * {@inheritDoc} + */ + @Override + protected void addMacDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addMacDefaults(addon, defaults); + + defaults.add("JXDatePicker.arrowIcon", + LookAndFeel.makeIcon(DatePickerAddon.class, "macosx/resources/combo-osx.png")); + + defaults.add("JXDatePicker.border", "none"); + + } + + /** {@inheritDoc} */ + @Override + protected void addNimbusDefaults (LookAndFeelAddons addon, + DefaultsList defaults) { + super.addNimbusDefaults (addon, defaults); + + // Issue #913-swingx: ugly in Nimbus + // TODO: don't use an image here, Nimbus uses Painters for everything + // => e.g. reuse the +// com.sun.java.swing.plaf.nimbus.ComboBoxComboBoxArrowButtonPainter + // (at the moment the OS-X icon looks most similar, it's much better + // than no icon...) + defaults.add ("JXDatePicker.arrowIcon", + LookAndFeel.makeIcon (DatePickerAddon.class, + "macosx/resources/combo-osx.png")); + + // Issue #913-swingx: ugly in Nimbus + // remove the border which was installed in addBasicDefaults + // this is done by Nimbus + defaults.add ("JXDatePicker.border", null); + } + +} + diff --git a/src/main/java/org/jdesktop/swingx/plaf/DatePickerUI.java b/src/main/java/org/jdesktop/swingx/plaf/DatePickerUI.java new file mode 100644 index 0000000000..c4a3730017 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/DatePickerUI.java @@ -0,0 +1,73 @@ +/* + * $Id: DatePickerUI.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf; + +import javax.swing.plaf.ComponentUI; +import java.beans.PropertyVetoException; +import java.util.Date; + +/** + * The ComponentUI for a JXDatePicker. + *

      + * + * Responsible for keeping the date property of all participants synchronized at + * all "stable" points in their life-cycle. That is the following invariant is + * guaranteed: + * + *

      
      + * Date selected = datePicker.getMonthView().getSelectedDate();
      + * assertEquals(selected, datePicker.getDate());
      + * assertEquals(selected, datePicker.getEditor().getValue());
      + * 
      + * + * @author Joshua Outwater + * @author Jeanette Winzenburg + */ +public abstract class DatePickerUI extends ComponentUI { + /** + * Get the baseline for the specified component, or a value less + * than 0 if the baseline can not be determined. The baseline is measured + * from the top of the component. + * + * @param width Width of the component to determine baseline for. + * @param height Height of the component to determine baseline for. + * @return baseline for the specified component + */ + public int getBaseline(int width, int height) { + return -1; + } + + /** + * Checks the given date for validity for selection. If valid, + * returns the date as appropriate in the picker's context, otherwise + * throws a propertyVetoException. Note that the returned date might + * be different from the input date, f.i. the time fields might be + * cleared. The input date is guaranteed to be unchanged. + * + * @param date date to check + * @return the date as allowed in the context of the picker. + * + * @throws PropertyVetoException if the given date is not valid for + * selection + */ + public abstract Date getSelectableDate(Date date) throws PropertyVetoException; + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/DefaultsList.java b/src/main/java/org/jdesktop/swingx/plaf/DefaultsList.java new file mode 100644 index 0000000000..3a7bf64ce5 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/DefaultsList.java @@ -0,0 +1,142 @@ +/* + * $Id: DefaultsList.java 4047 2011-07-19 18:51:12Z kschaefe $ + * + * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf; + +import org.jdesktop.swingx.painter.Painter; +import org.jdesktop.swingx.util.Contract; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.plaf.UIResource; +import java.awt.*; +import java.util.ArrayList; +import java.util.List; + +/** + * A specialty "list" for working with UI defaults. Requires adds to be done + * using key/value pairs. The purpose of this list is to enforce additions as + * pairs. + * + * @author Karl George Schaefer + */ +@SuppressWarnings("nls") +public final class DefaultsList { + private List delegate; + + /** + * Creates a {@code DefaultsList}. + */ + public DefaultsList() { + delegate = new ArrayList(); + } + + /** + * Adds a key/value pair to the defaults list. This implementation defers to + * {@link #add(Object, Object, boolean)} with {@code enableChecking} set to + * {@code true}. + * + * @param key + * the key that will be used to query {@code UIDefaults} + * @param value + * the value associated with the key + * @throws NullPointerException + * if {@code key} is {@code null} + * @throws IllegalArgumentException + * if {@code value} is a type that should be a + * {@code UIResource} but is not. For instance, passing in a + * {@code Border} that is not a {@code UIResource} will + * cause an exception. This checking must be enabled. + */ + public void add(Object key, Object value) { + add(key, value, true); + } + + /** + * Adds a key/value pair to the defaults list. A pair with a {@code null} + * value is treated specially. A {@code null}-value pair is never added to + * the list and, furthermore, if a key/value pair exists in this list with + * the same key as the newly added one, it is removed. + * + * @param key + * the key that will be used to query {@code UIDefaults} + * @param value + * the value associated with the key + * @param enableChecking + * if {@code true} then the value is checked to ensure that + * it is a {@code UIResource}, if appropriate + * @throws NullPointerException + * if {@code key} is {@code null} + * @throws IllegalArgumentException + * if {@code value} is a type that should be a + * {@code UIResource} but is not. For instance, passing in a + * {@code Border} that is not a {@code UIResource} will + * cause an exception. This checking must be enabled. + */ + public void add(Object key, Object value, boolean enableChecking) { + if (enableChecking) { + asUIResource(value, value + " must be a UIResource"); + } + + if (value == null && delegate.contains(key)) { + int i = delegate.indexOf(key); + + delegate.remove(i + 1); + delegate.remove(i); + } else if (value != null) { + delegate.add(Contract.asNotNull(key, "key cannot be null")); + delegate.add(value); + } + } + + //TODO move to Contract? + private static T asUIResource(T value, String message) { + if (!(value instanceof UIResource)) { + boolean shouldThrow = false; + + shouldThrow |= value instanceof ActionMap; + shouldThrow |= value instanceof Border; + shouldThrow |= value instanceof Color; + shouldThrow |= value instanceof Dimension; + shouldThrow |= value instanceof Font; + shouldThrow |= value instanceof Icon; + shouldThrow |= value instanceof InputMap; + shouldThrow |= value instanceof Insets; + shouldThrow |= value instanceof Painter; + //FIXME how to handle UIResource testing +// shouldThrow |= value instanceof StringValue; + + if (shouldThrow) { + throw new IllegalArgumentException(message); + } + } + + return value; + } + + /** + * Gets a copy of this list as an array. + * + * @return an array containing all of the key/value pairs added to this list + */ + public Object[] toArray() { + return delegate.toArray(); + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/ErrorPaneAddon.java b/src/main/java/org/jdesktop/swingx/plaf/ErrorPaneAddon.java new file mode 100644 index 0000000000..d9d55a8b00 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/ErrorPaneAddon.java @@ -0,0 +1,62 @@ +/* + * $Id: ErrorPaneAddon.java 3475 2009-08-28 08:30:47Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.plaf; + +import org.jdesktop.swingx.JXErrorPane; + +/** + * + * @author rbair + */ +public class ErrorPaneAddon extends AbstractComponentAddon { + + /** Creates a new instance of ErrorPaneAddon */ + public ErrorPaneAddon() { + super("JXErrorPane"); + } + + /** + * {@inheritDoc} + */ + @Override + protected void addBasicDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addBasicDefaults(addon, defaults); + + defaults.add(JXErrorPane.uiClassID, "org.jdesktop.swingx.plaf.basic.BasicErrorPaneUI"); + + UIManagerExt.addResourceBundle( + "org.jdesktop.swingx.plaf.basic.resources.ErrorPane"); + } + + /** + * {@inheritDoc} + */ + @Override + protected void addMacDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addMacDefaults(addon, defaults); + + defaults.add(JXErrorPane.uiClassID, "org.jdesktop.swingx.plaf.macosx.MacOSXErrorPaneUI"); + + UIManagerExt.addResourceBundle( + "org.jdesktop.swingx.plaf.macosx.resources.ErrorPane"); + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/ErrorPaneUI.java b/src/main/java/org/jdesktop/swingx/plaf/ErrorPaneUI.java new file mode 100644 index 0000000000..f9c8a092bf --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/ErrorPaneUI.java @@ -0,0 +1,60 @@ +/* + * $Id: ErrorPaneUI.java 3105 2008-10-16 21:09:43Z rah003 $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.plaf; + +import javax.swing.*; +import javax.swing.plaf.PanelUI; +import java.awt.*; + +/** + * The ComponentUI for a JXErrorPane. + *

      + * + * @author rbair + */ +public abstract class ErrorPaneUI extends PanelUI { + /** + * Creates new ErrorPane wrapped in the frame window centered at provided owner component. + * @param owner component to center created error frame at. + * @return New ErrorPane instance wrapped in JFrame. + */ + public abstract JFrame getErrorFrame(Component owner); + + /** + * Creates new ErrorPane wrapped in the dialog window centered at provided owner component. + * @param owner component to center created error dialog at. + * @return New ErrorPane instance wrapped in JDialog. + */ + public abstract JDialog getErrorDialog(Component owner); + + /** + * Creates new ErrorPane wrapped in the internal frame window centered at provided owner component. + * @param owner component to center created error frame at. + * @return New ErrorPane instance wrapped in JInternalFrame. + */ + public abstract JInternalFrame getErrorInternalFrame(Component owner); + /** + * Calculates default prefered size for JXErrorPane on given platform/LAF. + * @return Preferred size. + */ + public abstract Dimension calculatePreferredSize(); +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/HeaderAddon.java b/src/main/java/org/jdesktop/swingx/plaf/HeaderAddon.java new file mode 100644 index 0000000000..91c2e6df99 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/HeaderAddon.java @@ -0,0 +1,81 @@ +/* + * $Id: HeaderAddon.java 2474 2007-11-21 17:32:04Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf; + +import org.jdesktop.swingx.JXHeader; + +import javax.swing.*; +import javax.swing.plaf.ColorUIResource; +import javax.swing.plaf.FontUIResource; +import java.awt.*; + +/** + * Addon for JXHeader.
      + * + */ +public class HeaderAddon extends AbstractComponentAddon { + + public HeaderAddon() { + super("JXHeader"); + } + + /** + * {@inheritDoc} + */ + @Override + protected void addBasicDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addBasicDefaults(addon, defaults); + + defaults.add(JXHeader.uiClassID, "org.jdesktop.swingx.plaf.basic.BasicHeaderUI"); + //TODO image is missing + defaults.add("JXHeader.defaultIcon", + LookAndFeel.makeIcon(HeaderAddon.class, "basic/resources/header-default.png")); + //TODO use safe methods + defaults.add("JXHeader.titleFont", new FontUIResource(UIManager.getFont("Label.font").deriveFont(Font.BOLD))); + defaults.add("JXHeader.titleForeground", UIManager.getColor("Label.foreground")); + defaults.add("JXHeader.descriptionFont", UIManager.getFont("Label.font")); + defaults.add("JXHeader.descriptionForeground", UIManager.getColor("Label.foreground")); + defaults.add("JXHeader.background", + UIManagerExt.getSafeColor("control", new ColorUIResource(Color.decode("#C0C0C0")))); + defaults.add("JXHeader.startBackground", new ColorUIResource(Color.WHITE)); + } + + /** + * {@inheritDoc} + */ + @Override + protected void addMacDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addMacDefaults(addon, defaults); + + defaults.add("JXHeader.background", new ColorUIResource(new Color(218, 218, 218))); + defaults.add("JXHeader.startBackground", new ColorUIResource(new Color(235, 235, 235))); + } + + /** + * {@inheritDoc} + */ + @Override + protected void addNimbusDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addNimbusDefaults(addon, defaults); + + defaults.add("JXHeader.background", new ColorUIResource(new Color(214, 217, 223, 255))); + } +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/plaf/HeaderUI.java b/src/main/java/org/jdesktop/swingx/plaf/HeaderUI.java new file mode 100644 index 0000000000..0bf490bc46 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/HeaderUI.java @@ -0,0 +1,31 @@ +/* + * $Id: HeaderUI.java 1422 2006-09-25 23:06:11Z rbair $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.plaf; + +import javax.swing.plaf.PanelUI; + +/** + * + * @author rbair + */ +public abstract class HeaderUI extends PanelUI { +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/HyperlinkAddon.java b/src/main/java/org/jdesktop/swingx/plaf/HyperlinkAddon.java new file mode 100644 index 0000000000..7652c8c935 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/HyperlinkAddon.java @@ -0,0 +1,47 @@ +/* + * $Id: HyperlinkAddon.java 3745 2010-08-06 03:02:52Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf; + + +import org.jdesktop.swingx.JXHyperlink; + +import javax.swing.plaf.ColorUIResource; + +/** + * Addon for JXHyperlink.
      + */ +public class HyperlinkAddon extends AbstractComponentAddon { + public HyperlinkAddon() { + super("JXHyperlink"); + } + + @Override + protected void addBasicDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addBasicDefaults(addon, defaults); + + defaults.add(JXHyperlink.uiClassID, "org.jdesktop.swingx.plaf.basic.BasicHyperlinkUI"); + //using CSS pseudo classes for Color types + defaults.add("Hyperlink.linkColor", new ColorUIResource(0, 0x33, 0xFF)); + defaults.add("Hyperlink.visitedColor", new ColorUIResource(0x99, 0, 0x99)); + defaults.add("Hyperlink.hoverColor", new ColorUIResource(0xFF, 0x33, 0)); + defaults.add("Hyperlink.activeColor", new ColorUIResource(0xFF, 0x33, 0)); + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/LoginPaneAddon.java b/src/main/java/org/jdesktop/swingx/plaf/LoginPaneAddon.java new file mode 100644 index 0000000000..f091ac21c8 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/LoginPaneAddon.java @@ -0,0 +1,98 @@ +/* + * $Id: LoginPaneAddon.java 3033 2008-08-12 05:05:44Z rah003 $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.plaf; + +import org.jdesktop.swingx.JXLoginPane; + +import javax.swing.*; +import javax.swing.plaf.BorderUIResource; +import javax.swing.plaf.ColorUIResource; +import javax.swing.plaf.FontUIResource; +import javax.swing.plaf.metal.MetalLookAndFeel; +import java.awt.*; + +/** + * + * @author rbair + */ +public class LoginPaneAddon extends AbstractComponentAddon { + + /** Creates a new instance of LoginPaneAddon */ + public LoginPaneAddon() { + super("JXLoginPane"); + } + + @Override + protected void addBasicDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addBasicDefaults(addon, defaults); + Color errorBG = new Color(255, 215, 215); + + defaults.add(JXLoginPane.uiClassID, "org.jdesktop.swingx.plaf.basic.BasicLoginPaneUI"); + defaults.add("JXLoginPane.errorIcon", + LookAndFeel.makeIcon(LoginPaneAddon.class, "basic/resources/error16.png")); + defaults.add("JXLoginPane.bannerFont", new FontUIResource("Arial Bold", Font.PLAIN, 36)); + //#911 Not every LAF has Label.font defined ... + Font labelFont = UIManager.getFont("Label.font"); + Font boldLabel = labelFont != null ? labelFont.deriveFont(Font.BOLD) : new Font("SansSerif", Font.BOLD, 12); + defaults.add("JXLoginPane.pleaseWaitFont", + new FontUIResource(boldLabel)); + defaults.add("JXLoginPane.bannerForeground", new ColorUIResource(Color.WHITE)); + defaults.add("JXLoginPane.bannerDarkBackground", new ColorUIResource(Color.GRAY)); + defaults.add("JXLoginPane.bannerLightBackground", new ColorUIResource(Color.LIGHT_GRAY)); + defaults.add("JXLoginPane.errorBackground", new ColorUIResource(errorBG)); + defaults.add("JXLoginPane.errorBorder", + new BorderUIResource(BorderFactory.createCompoundBorder( + BorderFactory.createEmptyBorder(0, 36, 0, 11), + BorderFactory.createCompoundBorder( + BorderFactory.createLineBorder(Color.GRAY.darker()), + BorderFactory.createMatteBorder(5, 7, 5, 5, errorBG))))); + + UIManagerExt.addResourceBundle( + "org.jdesktop.swingx.plaf.basic.resources.LoginPane"); + } + + @Override + protected void addMetalDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addMetalDefaults(addon, defaults); + + if (isPlastic()) { + defaults.add("JXLoginPane.bannerForeground", new ColorUIResource(Color.WHITE)); + defaults.add("JXLoginPane.bannerDarkBackground", new ColorUIResource(Color.GRAY)); + defaults.add("JXLoginPane.bannerLightBackground", new ColorUIResource(Color.LIGHT_GRAY)); + } else { + defaults.add("JXLoginPane.bannerForeground", new ColorUIResource(Color.WHITE)); + defaults.add("JXLoginPane.bannerDarkBackground", + MetalLookAndFeel.getCurrentTheme().getPrimaryControlDarkShadow()); + defaults.add("JXLoginPane.bannerLightBackground", + MetalLookAndFeel.getCurrentTheme().getPrimaryControl()); + } + } + + @Override + protected void addWindowsDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addWindowsDefaults(addon, defaults); + + defaults.add("JXLoginPane.bannerForeground", new ColorUIResource(Color.WHITE)); + defaults.add("JXLoginPane.bannerDarkBackground", new ColorUIResource(49, 121, 242)); + defaults.add("JXLoginPane.bannerLightBackground", new ColorUIResource(198, 211, 247)); + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/LoginPaneUI.java b/src/main/java/org/jdesktop/swingx/plaf/LoginPaneUI.java new file mode 100644 index 0000000000..c7ef73e420 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/LoginPaneUI.java @@ -0,0 +1,37 @@ +/* + * $Id: LoginPaneUI.java 3472 2009-08-27 13:12:42Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.plaf; + +import javax.swing.plaf.PanelUI; +import java.awt.*; + +/** + * + * @author rbair + */ +public abstract class LoginPaneUI extends PanelUI { + /** + * @return The Image to use as the banner for the JXLoginPane. If + * this method returns null, then no banner will be shown. + */ + public abstract Image getBanner(); +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/LookAndFeelAddons.java b/src/main/java/org/jdesktop/swingx/plaf/LookAndFeelAddons.java new file mode 100644 index 0000000000..f896ec7240 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/LookAndFeelAddons.java @@ -0,0 +1,533 @@ +/* + * $Id: LookAndFeelAddons.java 4250 2012-11-13 18:15:34Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf; + +import org.jdesktop.swingx.BackgroundPaintable; +import org.jdesktop.swingx.painter.Painter; + +import javax.swing.*; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.UIResource; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.ServiceLoader; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Provides additional pluggable UI for new components added by the library. By default, the library + * uses the pluggable UI returned by {@link #getBestMatchAddonClassName()}. + *

      + * The default addon can be configured using the swing.addon system property as follow: + *

        + *
      • on the command line, java -Dswing.addon=ADDONCLASSNAME ...
      • + *
      • at runtime and before using the library components + * System.getProperties().put("swing.addon", ADDONCLASSNAME);
      • + *
      + *

      + * The default {@link #getCrossPlatformAddonClassName() cross platform addon} can be configured + * using the swing.crossplatformlafaddon system property as follow: + *

        + *
      • on the command line, java -Dswing.crossplatformlafaddon=ADDONCLASSNAME ...
      • + *
      • at runtime and before using the library components + * System.getProperties().put("swing.crossplatformlafaddon", ADDONCLASSNAME);
        + * Note: changing this property after the UI has been initialized may result in unexpected behavior. + *
      • + *
      + *

      + * The addon can also be installed directly by calling the {@link #setAddon(String)}method. For + * example, to install the Windows addons, add the following statement + * LookAndFeelAddons.setAddon("org.jdesktop.swingx.plaf.windows.WindowsLookAndFeelAddons");. + * + * @author Frederic Lavigne + * @author Karl Schaefer + */ +@SuppressWarnings("nls") +public abstract class LookAndFeelAddons { + + private static List contributedComponents = new ArrayList(); + + /** + * Key used to ensure the current UIManager has been populated by the LookAndFeelAddons. + */ + private static final Object APPCONTEXT_INITIALIZED = new Object(); + + private static boolean trackingChanges = false; + private static PropertyChangeListener changeListener; + + static { + // load the default addon + String addonClassname = getBestMatchAddonClassName(); + + try { + addonClassname = System.getProperty("swing.addon", addonClassname); + } catch (SecurityException e) { + // security exception may arise in Java Web Start + } + + try { + setAddon(addonClassname); + } catch (Exception e) { + // PENDING(fred) do we want to log an error and continue with a default + // addon class or do we just fail? + throw new ExceptionInInitializerError(e); + } + + setTrackingLookAndFeelChanges(true); + } + + private static LookAndFeelAddons currentAddon; + + /** + * Determines if the addon is a match for the {@link UIManager#getLookAndFeel() current Look and + * Feel}. + * + * @return {@code true} if this addon matches (is compatible); {@code false} otherwise + */ + protected boolean matches() { + return false; + } + + /** + * Determines if the addon is a match for the system Look and Feel. + * + * @return {@code true} if this addon matches (is compatible with) the system Look and Feel; + * {@code false} otherwise + */ + protected boolean isSystemAddon() { + return false; + } + + /** + * Initializes the look and feel addon. This method is + * + * @see #uninitialize + * @see UIManager#setLookAndFeel + */ + public void initialize() { + for (Iterator iter = contributedComponents.iterator(); iter.hasNext();) { + ComponentAddon addon = iter.next(); + addon.initialize(this); + } + } + + public void uninitialize() { + for (Iterator iter = contributedComponents.iterator(); iter.hasNext();) { + ComponentAddon addon = iter.next(); + addon.uninitialize(this); + } + } + + /** + * Adds the given defaults in UIManager. + * + * Note: the values are added only if they do not exist in the existing look and feel defaults. + * This makes it possible for look and feel implementors to override SwingX defaults. + * + * Note: the array is traversed in reverse order. If a key is found twice in the array, the + * key/value with the highest position in the array gets precedence over the other key in the + * array + * + * @param keysAndValues + */ + public void loadDefaults(Object[] keysAndValues) { + // Go in reverse order so the most recent keys get added first... + for (int i = keysAndValues.length - 2; i >= 0; i = i - 2) { + if (UIManager.getLookAndFeelDefaults().get(keysAndValues[i]) == null) { + UIManager.getLookAndFeelDefaults().put(keysAndValues[i], keysAndValues[i + 1]); + } + } + } + + public void unloadDefaults(@SuppressWarnings("unused") Object[] keysAndValues) { + // commented after Issue 446. + /* + * for (int i = 0, c = keysAndValues.length; i < c; i = i + 2) { + * UIManager.getLookAndFeelDefaults().put(keysAndValues[i], null); } + */ + } + + public static void setAddon(String addonClassName) throws InstantiationException, + IllegalAccessException, ClassNotFoundException { + setAddon(Class.forName(addonClassName, true, getClassLoader())); + } + + public static void setAddon(Class addonClass) throws InstantiationException, + IllegalAccessException { + LookAndFeelAddons addon = (LookAndFeelAddons) addonClass.newInstance(); + setAddon(addon); + } + + public static void setAddon(LookAndFeelAddons addon) { + if (currentAddon != null) { + currentAddon.uninitialize(); + } + + addon.initialize(); + currentAddon = addon; + // JW: we want a marker to discover if the LookAndFeelDefaults have been + // swept from under our feet. The following line looks suspicious, + // as it is setting a user default instead of a LF default. User defaults + // are not touched when resetting a LF + UIManager.put(APPCONTEXT_INITIALIZED, Boolean.TRUE); + // trying to fix #784-swingx: frequent NPE on getUI + // JW: we want a marker to discover if the LookAndFeelDefaults have been + // swept from under our feet. + UIManager.getLookAndFeelDefaults().put(APPCONTEXT_INITIALIZED, Boolean.TRUE); + } + + public static LookAndFeelAddons getAddon() { + return currentAddon; + } + + private static ClassLoader getClassLoader() { + ClassLoader cl = null; + + try { + cl = AccessController.doPrivileged(new PrivilegedAction() { + @Override + public ClassLoader run() { + return LookAndFeelAddons.class.getClassLoader(); + } + }); + } catch (SecurityException ignore) { } + + if (cl == null) { + final Thread t = Thread.currentThread(); + + try { + cl = AccessController.doPrivileged(new PrivilegedAction() { + @Override + public ClassLoader run() { + return t.getContextClassLoader(); + } + }); + } catch (SecurityException ignore) { } + } + + if (cl == null) { + try { + cl = AccessController.doPrivileged(new PrivilegedAction() { + @Override + public ClassLoader run() { + return ClassLoader.getSystemClassLoader(); + } + }); + } catch (SecurityException ignore) { } + } + + return cl; + } + + /** + * Based on the current look and feel (as returned by UIManager.getLookAndFeel()), + * this method returns the name of the closest LookAndFeelAddons to use. + * + * @return the addon matching the currently installed look and feel + */ + public static String getBestMatchAddonClassName() { + LookAndFeel laf = UIManager.getLookAndFeel(); + String className = null; + + if (UIManager.getCrossPlatformLookAndFeelClassName().equals(laf.getClass().getName())) { + className = getCrossPlatformAddonClassName(); + } else if (UIManager.getSystemLookAndFeelClassName().equals(laf.getClass().getName())) { + className = getSystemAddonClassName(); + } else { + ServiceLoader addonLoader = ServiceLoader.load(LookAndFeelAddons.class, + getClassLoader()); + + for (LookAndFeelAddons addon : addonLoader) { + if (addon.matches()) { + className = addon.getClass().getName(); + break; + } + } + } + + if (className == null) { + className = getSystemAddonClassName(); + } + + return className; + } + + public static String getCrossPlatformAddonClassName() { + try { + return AccessController.doPrivileged(new PrivilegedAction() { + @Override + public String run() { + return System.getProperty("swing.crossplatformlafaddon", + "org.jdesktop.swingx.plaf.metal.MetalLookAndFeelAddons"); + } + }); + } catch (SecurityException ignore) { + } + + return "org.jdesktop.swing.plaf.metal.MetalLookAndFeelAddons"; + } + + /** + * Gets the addon best suited for the operating system where the virtual machine is running. + * + * @return the addon matching the native operating system platform. + */ + public static String getSystemAddonClassName() { + ServiceLoader addonLoader = ServiceLoader.load(LookAndFeelAddons.class, + getClassLoader()); + String className = null; + + for (LookAndFeelAddons addon : addonLoader) { + if (addon.isSystemAddon()) { + className = addon.getClass().getName(); + break; + } + } + + if (className == null) { + className = getCrossPlatformAddonClassName(); + } + + return className; + } + + /** + * Each new component added by the library will contribute its default UI classes, colors and + * fonts to the LookAndFeelAddons. See {@link ComponentAddon}. + * + * @param component + */ + public static void contribute(ComponentAddon component) { + contributedComponents.add(component); + + if (currentAddon != null) { + // make sure to initialize any addons added after the + // LookAndFeelAddons has been installed + component.initialize(currentAddon); + } + } + + /** + * Removes the contribution of the given addon + * + * @param component + */ + public static void uncontribute(ComponentAddon component) { + contributedComponents.remove(component); + + if (currentAddon != null) { + component.uninitialize(currentAddon); + } + } + + /** + * Workaround for IDE mixing up with classloaders and Applets environments. Consider this method + * as API private. It must not be called directly. + * + * @param component + * @param expectedUIClass + * @return an instance of expectedUIClass + */ + public static ComponentUI getUI(JComponent component, Class expectedUIClass) { + maybeInitialize(); + + // solve issue with ClassLoader not able to find classes + String uiClassname = (String) UIManager.get(component.getUIClassID()); + // possible workaround and more debug info on #784 + if (uiClassname == null) { + Logger logger = Logger.getLogger("LookAndFeelAddons"); + logger.warning("Failed to retrieve UI for " + component.getClass().getName() + + " with UIClassID " + component.getUIClassID()); + if (logger.isLoggable(Level.FINE)) { + logger.fine("Existing UI defaults keys: " + + new ArrayList(UIManager.getDefaults().keySet())); + } + // really ugly hack. Should be removed as soon as we figure out what is causing the + // issue + uiClassname = "org.jdesktop.swingx.plaf.basic.Basic" + expectedUIClass.getSimpleName(); + } + try { + Class uiClass = Class.forName(uiClassname); + UIManager.put(uiClassname, uiClass); + } catch (ClassNotFoundException e) { + // we ignore the ClassNotFoundException + } + + ComponentUI ui = UIManager.getUI(component); + + if (expectedUIClass.isInstance(ui)) { + return ui; + } else if (ui == null) { + barkOnUIError("no ComponentUI class for: " + component); + } else { + String realUI = ui.getClass().getName(); + Class realUIClass = null; + + try { + realUIClass = expectedUIClass.getClassLoader().loadClass(realUI); + } catch (ClassNotFoundException e) { + barkOnUIError("failed to load class " + realUI); + } + + if (realUIClass != null) { + try { + Method createUIMethod = realUIClass.getMethod("createUI", + new Class[] { JComponent.class }); + + return (ComponentUI) createUIMethod.invoke(null, new Object[] { component }); + } catch (NoSuchMethodException e) { + barkOnUIError("static createUI() method not found in " + realUIClass); + } catch (Exception e) { + barkOnUIError("createUI() failed for " + component + " " + e); + } + } + } + + return null; + } + + // this is how core UIDefaults yells about bad components; we do the same + private static void barkOnUIError(String message) { + System.err.println(message); + new Error().printStackTrace(); + } + + /** + * With applets, if you reload the current applet, the UIManager will be reinitialized (entries + * previously added by LookAndFeelAddons will be removed) but the addon will not reinitialize + * because addon initialize itself through the static block in components and the classes do not + * get reloaded. This means component.updateUI will fail because it will not find its UI. + * + * This method ensures LookAndFeelAddons get re-initialized if needed. It must be called in + * every component updateUI methods. + */ + private static synchronized void maybeInitialize() { + if (currentAddon != null) { + // this is to ensure "UIManager#maybeInitialize" gets called and the + // LAFState initialized + UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + // if (!UIManager.getBoolean(APPCONTEXT_INITIALIZED)) { + // JW: trying to fix #784-swingx: frequent NPE in getUI + // moved the "marker" property into the LookAndFeelDefaults + if (!defaults.getBoolean(APPCONTEXT_INITIALIZED)) { + setAddon(currentAddon); + } + } + } + + // + // TRACKING OF THE CURRENT LOOK AND FEEL + // + private static class UpdateAddon implements PropertyChangeListener { + @Override + public void propertyChange(PropertyChangeEvent evt) { + try { + setAddon(getBestMatchAddonClassName()); + } catch (Exception e) { + // should not happen + throw new RuntimeException(e); + } + } + } + + /** + * If true, everytime the Swing look and feel is changed, the addon which best matches the + * current look and feel will be automatically selected. + * + * @param tracking + * true to automatically update the addon, false to not automatically track the + * addon. Defaults to false. + * @see #getBestMatchAddonClassName() + */ + public static synchronized void setTrackingLookAndFeelChanges(boolean tracking) { + if (trackingChanges != tracking) { + if (tracking) { + if (changeListener == null) { + changeListener = new UpdateAddon(); + } + UIManager.addPropertyChangeListener(changeListener); + } else { + if (changeListener != null) { + UIManager.removePropertyChangeListener(changeListener); + } + changeListener = null; + } + trackingChanges = tracking; + } + } + + /** + * @return true if the addon will be automatically change to match the current look and feel + * @see #setTrackingLookAndFeelChanges(boolean) + */ + public static synchronized boolean isTrackingLookAndFeelChanges() { + return trackingChanges; + } + + /** + * Convenience method for setting a component's background painter property with a value from + * the defaults. The painter is only set if the painter is {@code null} or an instance of + * {@code UIResource}. + * + * @param c + * component to set the painter on + * @param painter + * key specifying the painter + * @throws NullPointerException + * if the component or painter is {@code null} + * @throws IllegalArgumentException + * if the component does not contain the "backgroundPainter" property or the + * property cannot be set + */ + public static void installBackgroundPainter(T c, String painter) { + Painter p = c.getBackgroundPainter(); + + if (p == null || p instanceof UIResource) { + c.setBackgroundPainter(UIManagerExt.getPainter(painter)); + } + } + + /** + * Convenience method for uninstalling a background painter. If the painter of the component is + * a {@code UIResource}, it is set to {@code null}. + * + * @param c + * component to uninstall the painter on + * @throws NullPointerException + * if {@code c} is {@code null} + * @throws IllegalArgumentException + * if the component does not contain the "backgroundPainter" property or the + * property cannot be set + */ + public static void uninstallBackgroundPainter(T c) { + Painter p = c.getBackgroundPainter(); + + if (p == null || p instanceof UIResource) { + c.setBackgroundPainter(null); + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/LookAndFeelUtils.java b/src/main/java/org/jdesktop/swingx/plaf/LookAndFeelUtils.java new file mode 100644 index 0000000000..af37b9eaf3 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/LookAndFeelUtils.java @@ -0,0 +1,72 @@ +/* + * $Id$ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.plaf; + +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.UIResource; +import java.awt.*; + +/** + * Collection of helpers. Could move to LookAndFeelAddon? + * + * @author Jeanette Winzenburg + */ +public class LookAndFeelUtils { + + /** + * Returns the ui that is of type klass, or null if + * one can not be found. + */ + public static Object getUIOfType(ComponentUI ui, Class klass) { + if (klass.isInstance(ui)) { + return ui; + } + return null; + } + + /** + * Returns a Font based on the param which is not of type UIResource. + * + * @param font the base font + * @return a font not of type UIResource, may be null. + */ + public static Font getAsNotUIResource(Font font) { + if (!(font instanceof UIResource)) return font; + // PENDING JW: correct way to create another font instance? + return font.deriveFont(font.getAttributes()); + } + + /** + * Returns a Color based on the param which is not of type UIResource. + * + * @param color the base color + * @return a color not of type UIResource, may be null. + */ + public static Color getAsNotUIResource(Color color) { + if (!(color instanceof UIResource)) return color; + // PENDING JW: correct way to create another color instance? + float[] rgb = color.getRGBComponents(null); + return new Color(rgb[0], rgb[1], rgb[2], rgb[3]); + } + + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/MonthViewAddon.java b/src/main/java/org/jdesktop/swingx/plaf/MonthViewAddon.java new file mode 100644 index 0000000000..f83a121613 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/MonthViewAddon.java @@ -0,0 +1,60 @@ +/* + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf; + +import org.jdesktop.swingx.JXMonthView; + +import javax.swing.*; +import javax.swing.plaf.ColorUIResource; +import javax.swing.plaf.FontUIResource; +import java.awt.*; + +public class MonthViewAddon extends AbstractComponentAddon { + public MonthViewAddon() { + super("JXMonthView"); + } + + /** + * {@inheritDoc} + */ + @Override + protected void addBasicDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addBasicDefaults(addon, defaults); + + defaults.add(JXMonthView.uiClassID, "org.jdesktop.swingx.plaf.basic.BasicMonthViewUI"); + defaults.add("JXMonthView.background", new ColorUIResource(Color.WHITE)); + defaults.add("JXMonthView.monthStringBackground", new ColorUIResource(138, 173, 209)); + defaults.add("JXMonthView.monthStringForeground", new ColorUIResource(68, 68, 68)); + defaults.add("JXMonthView.daysOfTheWeekForeground", new ColorUIResource(68, 68, 68)); + defaults.add("JXMonthView.weekOfTheYearForeground", new ColorUIResource(68, 68, 68)); + defaults.add("JXMonthView.unselectableDayForeground", new ColorUIResource(Color.RED)); + defaults.add("JXMonthView.selectedBackground", new ColorUIResource(197, 220, 240)); + defaults.add("JXMonthView.flaggedDayForeground", new ColorUIResource(Color.RED)); + defaults.add("JXMonthView.leadingDayForeground", new ColorUIResource(Color.LIGHT_GRAY)); + defaults.add("JXMonthView.trailingDayForeground", new ColorUIResource(Color.LIGHT_GRAY)); + defaults.add("JXMonthView.font", UIManagerExt.getSafeFont("Button.font", + new FontUIResource("Dialog", Font.PLAIN, 12))); + defaults.add("JXMonthView.monthDownFileName", + LookAndFeel.makeIcon(MonthViewAddon.class, "basic/resources/month-down.png")); + defaults.add("JXMonthView.monthUpFileName", + LookAndFeel.makeIcon(MonthViewAddon.class, "basic/resources/month-up.png")); + defaults.add("JXMonthView.boxPaddingX", 3); + defaults.add("JXMonthView.boxPaddingY", 3); + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/MonthViewUI.java b/src/main/java/org/jdesktop/swingx/plaf/MonthViewUI.java new file mode 100644 index 0000000000..1b615ad916 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/MonthViewUI.java @@ -0,0 +1,67 @@ +/* + * $Id: MonthViewUI.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf; + +import javax.swing.plaf.ComponentUI; +import java.util.Date; + +public abstract class MonthViewUI extends ComponentUI { + + /** + * Returns an array of String to use as names for the days of the week. + * + * @return array of names for the days of the week. + */ + public abstract String[] getDaysOfTheWeek(); + + + /** + * Returns the Date at the given location. May be null if the + * coordinates don't map to a day in the month which contains the + * coordinates. Specifically: hitting leading/trailing dates returns null. + * + * Mapping pixel to calendar day. + * + * @param x the x position of the location in pixel + * @param y the y position of the location in pixel + * @return the day at the given location or null if the location + * doesn't map to a day in the month which contains the coordinates. + */ + public abstract Date getDayAtLocation(int x, int y); + + + + + /** + * Returns the last possible date that can be displayed. + * This is implemented by the UI since it is in control of layout + * and may possibly yeild different results based on implementation.

      + * + * It's up to the UI to keep this property, based on internal state and + * the firstDisplayed as controlled by the JXMonthView. + * + * @return Date The date. + */ + public abstract Date getLastDisplayedDay(); + + + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/MultiThumbSliderAddon.java b/src/main/java/org/jdesktop/swingx/plaf/MultiThumbSliderAddon.java new file mode 100644 index 0000000000..b977e7e39c --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/MultiThumbSliderAddon.java @@ -0,0 +1,44 @@ +/* + * $Id: MultiThumbSliderAddon.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf; + +import org.jdesktop.swingx.JXMultiThumbSlider; + +/** + * + * @author jm158417 + */ +public class MultiThumbSliderAddon extends AbstractComponentAddon { + + /** Creates a new instance of MultiThumbSliderAddon */ + public MultiThumbSliderAddon() { + super("JXMultiThumbSlider"); + } + + @Override + protected void addBasicDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addBasicDefaults(addon, defaults); + + defaults.add(JXMultiThumbSlider.uiClassID, + "org.jdesktop.swingx.plaf.basic.BasicMultiThumbSliderUI"); + } + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/MultiThumbSliderUI.java b/src/main/java/org/jdesktop/swingx/plaf/MultiThumbSliderUI.java new file mode 100644 index 0000000000..33253e311b --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/MultiThumbSliderUI.java @@ -0,0 +1,31 @@ +/* + * $Id: MultiThumbSliderUI.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf; + +import javax.swing.plaf.ComponentUI; + +/** + * + * @author Joshua Marinacci + */ +public abstract class MultiThumbSliderUI extends ComponentUI { + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/PainterUIResource.java b/src/main/java/org/jdesktop/swingx/plaf/PainterUIResource.java new file mode 100644 index 0000000000..5f26a65b70 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/PainterUIResource.java @@ -0,0 +1,61 @@ +/* + * $Id: PainterUIResource.java 4046 2011-07-19 18:45:40Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.plaf; + +import org.jdesktop.swingx.painter.Painter; + +import javax.swing.*; +import javax.swing.plaf.UIResource; +import java.awt.*; + +/** + * An implementation of Painter as a UIResource. UI classes that create Painters + * should use this class. + * + * @author rbair + * @author Karl George Schaefer + * @param a subclass of JComponent + */ +public class PainterUIResource implements Painter, UIResource { + private Painter p; + + /** + * Creates a new instance of PainterUIResource with the specified delegate + * painter. + * + * @param p + * the delegate painter + */ + public PainterUIResource(Painter p) { + this.p = p; + } + + /** + * {@inheritDoc} + */ + @Override + public void paint(Graphics2D g, T component, int width, int height) { + if (p != null) { + p.paint(g, component, width, height); + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/PromptTextAreaUI.java b/src/main/java/org/jdesktop/swingx/plaf/PromptTextAreaUI.java new file mode 100644 index 0000000000..590ed709ef --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/PromptTextAreaUI.java @@ -0,0 +1,52 @@ +package org.jdesktop.swingx.plaf; + +import javax.swing.*; +import javax.swing.plaf.TextUI; +import javax.swing.text.JTextComponent; + +/** + * {@link PromptTextUI} implementation for rendering prompts on + * {@link JTextArea}s and uses a {@link JTextArea} as a prompt component. + * + * @author Peter Weishapl + * + */ +public class PromptTextAreaUI extends PromptTextUI { + /** + * Shared prompt renderer. + */ + private final static JTextArea txt = new JTextArea(); + + /** + * Creates a new {@link PromptTextAreaUI}. + * + * @param delegate + */ + public PromptTextAreaUI(TextUI delegate) { + super(delegate); + } + + /** + * Overrides {@link #getPromptComponent(JTextComponent)} to additionally + * update {@link JTextArea} specific properties. + */ + @Override + public JTextComponent getPromptComponent(JTextComponent txt) { + JTextArea lbl = (JTextArea) super.getPromptComponent(txt); + JTextArea txtArea = (JTextArea) txt; + + lbl.setColumns(txtArea.getColumns()); + lbl.setRows(txtArea.getRows()); + + return lbl; + } + + /** + * Returns a shared {@link JTextArea}. + */ + @Override + protected JTextComponent createPromptComponent() { + txt.updateUI(); + return txt; + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/PromptTextFieldUI.java b/src/main/java/org/jdesktop/swingx/plaf/PromptTextFieldUI.java new file mode 100644 index 0000000000..b7ac3cb070 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/PromptTextFieldUI.java @@ -0,0 +1,187 @@ +package org.jdesktop.swingx.plaf; + +import org.jdesktop.swingx.search.NativeSearchFieldSupport; +import org.jdesktop.swingx.util.OS; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.plaf.TextUI; +import javax.swing.text.JTextComponent; +import java.awt.*; + +import static javax.swing.BorderFactory.createEmptyBorder; + +/** + * {@link PromptTextUI} implementation for rendering prompts on + * {@link JTextField}s and uses a {@link JTextField} as a prompt component. + * + * @author Peter Weishapl + * + */ +public class PromptTextFieldUI extends PromptTextUI { + /** + * Creates a new {@link PromptTextFieldUI}. + * + * @param delegate + */ + public PromptTextFieldUI(TextUI delegate) { + super(delegate); + } + + /** + * Overrides {@link #getPromptComponent(JTextComponent)} to additionally + * update {@link JTextField} specific properties. + */ + @Override + public JTextComponent getPromptComponent(JTextComponent txt) { + LabelField lbl = (LabelField) super.getPromptComponent(txt); + JTextField txtField = (JTextField) txt; + + lbl.setHorizontalAlignment(txtField.getHorizontalAlignment()); + lbl.setColumns(txtField.getColumns()); + + // Make search field in Leopard paint focused border. + lbl.hasFocus = txtField.hasFocus() + && NativeSearchFieldSupport.isNativeSearchField(txtField); + + // leopard client properties. see + // http://developer.apple.com/technotes/tn2007/tn2196.html#JTEXTFIELD_VARIANT + NativeSearchFieldSupport.setSearchField(lbl, NativeSearchFieldSupport + .isSearchField(txtField)); + NativeSearchFieldSupport.setFindPopupMenu(lbl, NativeSearchFieldSupport + .getFindPopupMenu(txtField)); + + // here we need to copy the border again for Mac OS X, because the above + // calls may have replaced it. + Border b = txt.getBorder(); + + if (b == null) { + lbl.setBorder(txt.getBorder()); + } else { + Insets insets = b.getBorderInsets(txt); + lbl.setBorder( + createEmptyBorder(insets.top, insets.left, insets.bottom, insets.right)); + } + // lbl.setBorder(txtField.getBorder()); + + // buddy support: not needed, because BuddyLayoutAndBorder queries + // original text field + // BuddySupport.setOuterMargin(lbl, + // BuddySupport.getOuterMargin(txtField)); + // BuddySupport.setLeft(lbl, BuddySupport.getLeft(txtField)); + // BuddySupport.setRight(lbl, BuddySupport.getRight(txtField)); + + return lbl; + } + + /** + * Returns a shared {@link JTextField}. + */ + @Override + protected JTextComponent createPromptComponent() { + return new LabelField(); + } + + private static final class LabelField extends JTextField { + boolean hasFocus; + + @Override + public boolean hasFocus() { + return hasFocus; + } + + /** + * {@inheritDoc}

      + * + * Overridden to not automatically de/register itself from/to the ToolTipManager. + * As rendering component it is not considered to be active in any way, so the + * manager must not listen. + */ + @Override + public void setToolTipText(String text) { + putClientProperty(TOOL_TIP_TEXT_KEY, text); + } + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + * + * @since 1.5 + */ + @Override + public void invalidate() {} + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + */ + @Override + public void validate() {} + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + */ + @Override + public void revalidate() {} + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + */ + @Override + public void repaint(long tm, int x, int y, int width, int height) {} + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + */ + @Override + public void repaint(Rectangle r) { } + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + * + * @since 1.5 + */ + @Override + public void repaint() { + } + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + */ + @Override + protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { + if (OS.isMacOSX()) { + super.firePropertyChange(propertyName, oldValue, newValue); + } else { + // Strings get interned... + if ("document".equals(propertyName)) { + super.firePropertyChange(propertyName, oldValue, newValue); + } + } + } + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + */ + @Override + public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { + if (OS.isMacOSX()) { + super.firePropertyChange(propertyName, oldValue, newValue); + } + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/PromptTextUI.java b/src/main/java/org/jdesktop/swingx/plaf/PromptTextUI.java new file mode 100644 index 0000000000..5079b68ec6 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/PromptTextUI.java @@ -0,0 +1,457 @@ +package org.jdesktop.swingx.plaf; + +import org.jdesktop.swingx.painter.Painter; +import org.jdesktop.swingx.prompt.PromptSupport; +import org.jdesktop.swingx.prompt.PromptSupport.FocusBehavior; + +import javax.accessibility.Accessible; +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.TextUI; +import javax.swing.text.*; +import javax.swing.text.DefaultHighlighter.DefaultHighlightPainter; +import javax.swing.text.Position.Bias; +import java.awt.Component.BaselineResizeBehavior; +import java.awt.*; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.lang.reflect.Method; + +import static javax.swing.BorderFactory.createEmptyBorder; + +/** + *

      + * Abstract {@link TextUI} class that delegates most work to another + * {@link TextUI} and additionally renders a prompt text as specified in the + * {@link JTextComponent}s client properties by {@link PromptSupport}. + *

      + * Subclasses of this class must provide a prompt component used for rendering + * the prompt text. + *

      + * + * @author Peter Weishapl + * + */ +public abstract class PromptTextUI extends TextUI { + protected class PainterHighlighter implements Highlighter { + private final Painter painter; + + private JTextComponent c; + + public PainterHighlighter(Painter painter) { + this.painter = painter; + } + + /** + * {@inheritDoc} + */ + @Override + public Object addHighlight(int p0, int p1, HighlightPainter p) + throws BadLocationException { + return new Object(); + } + + /** + * {@inheritDoc} + */ + @Override + public void changeHighlight(Object tag, int p0, int p1) + throws BadLocationException { + + } + + /** + * {@inheritDoc} + */ + @Override + public void deinstall(JTextComponent c) { + c = null; + } + + /** + * {@inheritDoc} + */ + @Override + public Highlight[] getHighlights() { + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public void install(JTextComponent c) { + this.c = c; + } + + /** + * {@inheritDoc} + */ + @Override + public void paint(Graphics g) { + Graphics2D g2d = (Graphics2D) g.create(); + + try { + painter.paint(g2d, c, c.getWidth(), c.getHeight()); + } finally { + g2d.dispose(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void removeAllHighlights() { + // TODO Auto-generated method stub + + } + + /** + * {@inheritDoc} + */ + @Override + public void removeHighlight(Object tag) { + // TODO Auto-generated method stub + + } + } + + static final FocusHandler focusHandler = new FocusHandler(); + + /** + * Delegate the hard work to this object. + */ + protected final TextUI delegate; + + /** + * This component ist painted when rendering the prompt text. + */ + protected JTextComponent promptComponent; + + /** + * Creates a new {@link PromptTextUI} which delegates most work to another + * {@link TextUI}. + * + * @param delegate + */ + public PromptTextUI(TextUI delegate) { + this.delegate = delegate; + } + + /** + * Creates a component which should be used to render the prompt text. + * + * @return + */ + protected abstract JTextComponent createPromptComponent(); + + /** + * Calls TextUI#installUI(JComponent) on the delegate and installs a focus + * listener on c which repaints the component when it gains or + * loses the focus. + */ + @Override + public void installUI(JComponent c) { + delegate.installUI(c); + + JTextComponent txt = (JTextComponent) c; + + // repaint to correctly highlight text if FocusBehavior is + // HIGHLIGHT_LABEL in Metal and Windows LnF + txt.addFocusListener(focusHandler); + } + + /** + * Delegates, then uninstalls the focus listener. + */ + @Override + public void uninstallUI(JComponent c) { + delegate.uninstallUI(c); + c.removeFocusListener(focusHandler); + promptComponent = null; + } + + /** + * Creates a label component, if none has already been created. Sets the + * prompt components properties to reflect the given {@link JTextComponent}s + * properties and returns it. + * + * @param txt + * @return the adjusted prompt component + */ + public JTextComponent getPromptComponent(JTextComponent txt) { + if (promptComponent == null) { + promptComponent = createPromptComponent(); + } + if (txt.isFocusOwner() + && PromptSupport.getFocusBehavior(txt) == FocusBehavior.HIDE_PROMPT) { + promptComponent.setText(null); + } else { + promptComponent.setText(PromptSupport.getPrompt(txt)); + } + + promptComponent.getHighlighter().removeAllHighlights(); + if (txt.isFocusOwner() + && PromptSupport.getFocusBehavior(txt) == FocusBehavior.HIGHLIGHT_PROMPT) { + promptComponent.setForeground(txt.getSelectedTextColor()); + try { + promptComponent.getHighlighter().addHighlight(0, + promptComponent.getText().length(), + new DefaultHighlightPainter(txt.getSelectionColor())); + } catch (BadLocationException e) { + e.printStackTrace(); + } + } else { + promptComponent.setForeground(PromptSupport.getForeground(txt)); + } + + if (PromptSupport.getFontStyle(txt) == null) { + promptComponent.setFont(txt.getFont()); + } else { + promptComponent.setFont(txt.getFont().deriveFont( + PromptSupport.getFontStyle(txt))); + } + + promptComponent.setBackground(PromptSupport.getBackground(txt)); + promptComponent.setHighlighter(new PainterHighlighter(PromptSupport + .getBackgroundPainter(txt))); + promptComponent.setEnabled(txt.isEnabled()); + promptComponent.setOpaque(txt.isOpaque()); + promptComponent.setBounds(txt.getBounds()); + Border b = txt.getBorder(); + + if (b == null) { + promptComponent.setBorder(txt.getBorder()); + } else { + Insets insets = b.getBorderInsets(txt); + promptComponent.setBorder( + createEmptyBorder(insets.top, insets.left, insets.bottom, insets.right)); + } + + promptComponent.setSelectedTextColor(txt.getSelectedTextColor()); + promptComponent.setSelectionColor(txt.getSelectionColor()); + promptComponent.setEditable(txt.isEditable()); + promptComponent.setMargin(txt.getMargin()); + + return promptComponent; + } + + /** + * When {@link #shouldPaintPrompt(JTextComponent)} returns true, the prompt + * component is retrieved by calling + * {@link #getPromptComponent(JTextComponent)} and it's preferred size is + * returned. Otherwise super{@link #getPreferredSize(JComponent)} is called. + */ + @Override + public Dimension getPreferredSize(JComponent c) { + JTextComponent txt = (JTextComponent) c; + if (shouldPaintPrompt(txt)) { + return getPromptComponent(txt).getPreferredSize(); + } + return delegate.getPreferredSize(c); + } + + /** + * Delegates painting when {@link #shouldPaintPrompt(JTextComponent)} + * returns false. Otherwise the prompt component is retrieved by calling + * {@link #getPromptComponent(JTextComponent)} and painted. Then the caret + * of the given text component is painted. + */ + @Override + public void paint(Graphics g, final JComponent c) { + JTextComponent txt = (JTextComponent) c; + + if (shouldPaintPrompt(txt)) { + paintPromptComponent(g, txt); + } else { + delegate.paint(g, c); + } + } + + protected void paintPromptComponent(Graphics g, JTextComponent txt) { + JTextComponent lbl = getPromptComponent(txt); + SwingUtilities.paintComponent(g, lbl, txt, 0, 0, txt.getWidth(), txt.getHeight()); + + if (txt.getCaret() != null) { + txt.getCaret().paint(g); + } + } + + /** + * Returns if the prompt or the text field should be painted, depending on + * the state of txt. + * + * @param txt + * @return true when txt contains not text, otherwise false + */ + public boolean shouldPaintPrompt(JTextComponent txt) { + return txt.getText() == null || txt.getText().length() == 0; + } + + /** + * Calls super.{@link #update(Graphics, JComponent)}, which in turn calls + * the paint method of this object. + */ + @Override + public void update(Graphics g, JComponent c) { + if (shouldPaintPrompt((JTextComponent) c)) { + super.update(g, c); + } else { + delegate.update(g, c); + } + } + + /** + * Delegate when {@link #shouldPaintPrompt(JTextComponent)} returns false. + * Otherwise get the prompt component's UI and delegate to it. This ensures + * that the {@link Caret} is painted on the correct position (this is + * important when the text is centered, so that the caret will not be + * painted inside the label text) + */ + @Override + public Rectangle modelToView(JTextComponent t, int pos, Bias bias) + throws BadLocationException { + if (shouldPaintPrompt(t)) { + return getPromptComponent(t).getUI().modelToView(t, pos, bias); + } else { + return delegate.modelToView(t, pos, bias); + } + } + + /** + * Calls {@link #modelToView(JTextComponent, int, Bias)} with + * {@link Bias#Forward}. + */ + @Override + public Rectangle modelToView(JTextComponent t, int pos) + throws BadLocationException { + return modelToView(t, pos, Bias.Forward); + } + + // ********************* Delegate methods *************************/// + // ****************************************************************/// + + @Override + public boolean contains(JComponent c, int x, int y) { + return delegate.contains(c, x, y); + } + + @Override + public void damageRange(JTextComponent t, int p0, int p1, Bias firstBias, + Bias secondBias) { + delegate.damageRange(t, p0, p1, firstBias, secondBias); + } + + @Override + public void damageRange(JTextComponent t, int p0, int p1) { + delegate.damageRange(t, p0, p1); + } + + @Override + public boolean equals(Object obj) { + return delegate.equals(obj); + } + + @Override + public Accessible getAccessibleChild(JComponent c, int i) { + return delegate.getAccessibleChild(c, i); + } + + @Override + public int getAccessibleChildrenCount(JComponent c) { + return delegate.getAccessibleChildrenCount(c); + } + + @Override + public EditorKit getEditorKit(JTextComponent t) { + return delegate.getEditorKit(t); + } + + @Override + public Dimension getMaximumSize(JComponent c) { + return delegate.getMaximumSize(c); + } + + @Override + public Dimension getMinimumSize(JComponent c) { + return delegate.getMinimumSize(c); + } + + @Override + public int getNextVisualPositionFrom(JTextComponent t, int pos, Bias b, + int direction, Bias[] biasRet) throws BadLocationException { + return delegate + .getNextVisualPositionFrom(t, pos, b, direction, biasRet); + } + + @Override + public View getRootView(JTextComponent t) { + return delegate.getRootView(t); + } + + @Override + public String getToolTipText(JTextComponent t, Point pt) { + return delegate.getToolTipText(t, pt); + } + + @Override + public int hashCode() { + return delegate.hashCode(); + } + + @Override + public String toString() { + return String.format("%s (%s)", getClass().getName(), delegate + .toString()); + } + + @Override + public int viewToModel(JTextComponent t, Point pt, Bias[] biasReturn) { + return delegate.viewToModel(t, pt, biasReturn); + } + + @Override + public int viewToModel(JTextComponent t, Point pt) { + return delegate.viewToModel(t, pt); + } + + /** + * Tries to call {@link ComponentUI#getBaseline(int, int)} on the delegate + * via Reflection. Workaround to maintain compatibility with Java 5. Ideally + * we should also override {@link #getBaselineResizeBehavior(JComponent)}, + * but that's impossible since the {@link BaselineResizeBehavior} class, + * which does not exist in Java 5, is involved. + * + * @return the baseline, or -2 if getBaseline could not be + * invoked on the delegate. + */ + @Override + public int getBaseline(JComponent c, int width, int height) { + try { + Method m = delegate.getClass().getMethod("getBaseline", + JComponent.class, int.class, int.class); + Object o = m.invoke(delegate, new Object[] { c, width, height }); + return (Integer) o; + } catch (Exception ex) { + // ignore + return -2; + } + } + + /** + * Repaint the {@link TextComponent} when it loses or gains the focus. + */ + private static final class FocusHandler extends FocusAdapter { + @Override + public void focusGained(FocusEvent e) { + e.getComponent().repaint(); + } + + @Override + public void focusLost(FocusEvent e) { + e.getComponent().repaint(); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/plaf/SafeBorder.java b/src/main/java/org/jdesktop/swingx/plaf/SafeBorder.java new file mode 100644 index 0000000000..4bfa0e18f3 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/SafeBorder.java @@ -0,0 +1,95 @@ +/* + * Created on 04.11.2010 + * + */ +package org.jdesktop.swingx.plaf; + +import javax.swing.border.AbstractBorder; +import javax.swing.plaf.UIResource; +import java.awt.*; +import java.awt.Component.BaselineResizeBehavior; + +/** + * Wrapper around a delegate with the same behaviour as the delegate except that + * it catches null insets (hack around Issue 1297-swingx which is core bug + * 6739738) + */ +public class SafeBorder extends AbstractBorder implements UIResource { + + private AbstractBorder delegate; + + public SafeBorder(AbstractBorder delegate) { + this.delegate = delegate; + } + + /** + * {@inheritDoc} + */ + @Override + public int getBaseline(Component c, int width, int height) { + return delegate.getBaseline(c, width, height); + } + + /** + * {@inheritDoc} + */ + @Override + public BaselineResizeBehavior getBaselineResizeBehavior(Component c) { + return delegate.getBaselineResizeBehavior(c); + } + + /** + * {@inheritDoc} + */ + @Override + public Insets getBorderInsets(Component c, Insets insets) { + Insets result = delegate.getBorderInsets(c, safeInsets(insets)); + return safeInsets(result); + } + + /** + * @param insets + * the insets to query + * @return the insets supplied or an empty insets if the value is {@code null} + */ + private Insets safeInsets(Insets insets) { + return insets != null ? insets : new Insets(0, 0, 0, 0); + } + + /** + * {@inheritDoc} + */ + @Override + public Insets getBorderInsets(Component c) { + Insets result = delegate.getBorderInsets(c); + return safeInsets(result); + } + + /** + * {@inheritDoc} + */ + @Override + public Rectangle getInteriorRectangle(Component c, int x, int y, int width, + int height) { + return delegate.getInteriorRectangle(c, x, y, width, height); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isBorderOpaque() { + return delegate.isBorderOpaque(); + } + + /** + * {@inheritDoc} + */ + @Override + public void paintBorder(Component c, Graphics g, int x, int y, int width, + int height) { + delegate.paintBorder(c, g, x, y, width, height); + } + + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/SearchFieldAddon.java b/src/main/java/org/jdesktop/swingx/plaf/SearchFieldAddon.java new file mode 100644 index 0000000000..b64b24cf67 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/SearchFieldAddon.java @@ -0,0 +1,123 @@ +package org.jdesktop.swingx.plaf; + +import org.jdesktop.swingx.JXSearchField.LayoutStyle; + +import javax.swing.*; +import javax.swing.plaf.IconUIResource; +import javax.swing.plaf.InsetsUIResource; +import java.awt.*; +import java.net.URL; + +@SuppressWarnings("nls") +public class SearchFieldAddon extends AbstractComponentAddon { + public static final String SEARCH_FIELD_SOURCE = "searchField"; + public static final String BUTTON_SOURCE = "button"; + + public SearchFieldAddon() { + super("JXSearchField"); + } + + @Override + protected void addBasicDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addBasicDefaults(addon, defaults); + defaults.add("SearchField.layoutStyle", LayoutStyle.MAC); + defaults.add("SearchField.icon", getIcon("basic/resources/search.gif")); + defaults.add("SearchField.rolloverIcon", getIcon("basic/resources/search_rollover.gif")); + defaults.add("SearchField.pressedIcon", getIcon("basic/resources/search.gif")); + defaults.add("SearchField.popupIcon", getIcon("basic/resources/search_popup.gif")); + defaults.add("SearchField.popupRolloverIcon", getIcon("basic/resources/search_popup_rollover.gif")); + defaults.add("SearchField.clearIcon", getIcon("basic/resources/clear.gif")); + defaults.add("SearchField.clearRolloverIcon", getIcon("basic/resources/clear_rollover.gif")); + defaults.add("SearchField.clearPressedIcon", getIcon("basic/resources/clear_pressed.gif")); + defaults.add("SearchField.buttonMargin", new InsetsUIResource(1, 1, 1, 1)); + defaults.add("SearchField.popupSource", BUTTON_SOURCE); + + //webstart fix + UIManagerExt.addResourceBundle("org.jdesktop.swingx.plaf.basic.resources.SearchField"); +// UIManager.getDefaults().addResourceBundle("org.jdesktop.swingx.plaf.basic.resources.SearchField"); + } + + @Override + protected void addMetalDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addMetalDefaults(addon, defaults); + + defaults.add("SearchField.buttonMargin", new InsetsUIResource(0, 0, 1, 1)); + } + + @Override + protected void addWindowsDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addWindowsDefaults(addon, defaults); + + defaults.add("SearchField.promptFontStyle", Font.ITALIC); + defaults.add("SearchField.layoutStyle", LayoutStyle.VISTA); + defaults.add("SearchField.icon", getIcon("windows/resources/search.gif")); + defaults.add("SearchField.rolloverIcon", getIcon("windows/resources/search_rollover.gif")); + defaults.add("SearchField.pressedIcon", getIcon("windows/resources/search_pressed.gif")); + defaults.add("SearchField.popupIcon", getIcon("windows/resources/search_popup.gif")); + defaults.add("SearchField.popupRolloverIcon", getIcon("windows/resources/search_popup_rollover.gif")); + defaults.add("SearchField.popupPressedIcon", getIcon("windows/resources/search_popup_pressed.gif")); + defaults.add("SearchField.clearIcon", getIcon("windows/resources/clear.gif")); + defaults.add("SearchField.clearRolloverIcon", getIcon("windows/resources/clear_rollover.gif")); + defaults.add("SearchField.clearPressedIcon", getIcon("windows/resources/clear_pressed.gif")); + defaults.add("SearchField.useSeperatePopupButton", Boolean.TRUE); + defaults.add("SearchField.popupOffset", -1); + + // Do it like 'Windows Media Player' in XP: + // Replace the border line with the search button line on rollover. + // But not in classic mode! + if (UIManager.getLookAndFeel().getClass().getName().indexOf("Classic") == -1) { + defaults.add("SearchField.buttonMargin", new InsetsUIResource(0, -1, 0, -1)); + } else { + defaults.add("SearchField.buttonMargin", new InsetsUIResource(0, 0, 0, 0)); + } + } + + @Override + protected void addMotifDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addMotifDefaults(addon, defaults); + + defaults.add("SearchField.icon", getIcon("macosx/resources/search.png")); + defaults.add("SearchField.rolloverIcon", getIcon("macosx/resources/search.png")); + defaults.add("SearchField.pressedIcon", getIcon("macosx/resources/search.png")); + defaults.add("SearchField.popupIcon", getIcon("macosx/resources/search_popup.png")); + defaults.add("SearchField.popupRolloverIcon", getIcon("macosx/resources/search_popup.png")); + defaults.add("SearchField.popupPressedIcon", getIcon("macosx/resources/search_popup.png")); + defaults.add("SearchField.clearIcon", getIcon("macosx/resources/clear.png")); + defaults.add("SearchField.clearRolloverIcon", getIcon("macosx/resources/clear_rollover.png")); + defaults.add("SearchField.clearPressedIcon", getIcon("macosx/resources/clear_pressed.png")); + } + + @Override + protected void addMacDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addMacDefaults(addon, defaults); + + defaults.add("SearchField.icon", getIcon("macosx/resources/search.png")); + defaults.add("SearchField.rolloverIcon", getIcon("macosx/resources/search.png")); + defaults.add("SearchField.pressedIcon", getIcon("macosx/resources/search.png")); + defaults.add("SearchField.popupIcon", getIcon("macosx/resources/search_popup.png")); + defaults.add("SearchField.popupRolloverIcon", getIcon("macosx/resources/search_popup.png")); + defaults.add("SearchField.popupPressedIcon", getIcon("macosx/resources/search_popup.png")); + defaults.add("SearchField.clearIcon", getIcon("macosx/resources/clear.png")); + defaults.add("SearchField.clearRolloverIcon", getIcon("macosx/resources/clear_rollover.png")); + defaults.add("SearchField.clearPressedIcon", getIcon("macosx/resources/clear_pressed.png")); + defaults.add("SearchField.buttonMargin", new InsetsUIResource(0, 0, 0, 0)); + defaults.add("SearchField.popupSource", SEARCH_FIELD_SOURCE); + } + + // Workaround: Only return true, when the current LnF is Windows or PlasticXP. + @Override + protected boolean isWindows(LookAndFeelAddons addon) { + return super.isWindows(addon) + || UIManager.getLookAndFeel().getClass().getName().indexOf("Windows") != -1 + || UIManager.getLookAndFeel().getClass().getName().indexOf("PlasticXP") != -1; + } + + private IconUIResource getIcon(String resourceName) { + URL url = getClass().getResource(resourceName); + if (url == null) { + return null; + } else { + return new IconUIResource(new ImageIcon(url)); + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/SearchFieldUI.java b/src/main/java/org/jdesktop/swingx/plaf/SearchFieldUI.java new file mode 100644 index 0000000000..9280e5b768 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/SearchFieldUI.java @@ -0,0 +1,482 @@ +package org.jdesktop.swingx.plaf; + +import org.jdesktop.swingx.JXSearchField; +import org.jdesktop.swingx.JXSearchField.LayoutStyle; +import org.jdesktop.swingx.prompt.BuddySupport; +import org.jdesktop.swingx.search.NativeSearchFieldSupport; + +import javax.swing.*; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.plaf.TextUI; +import javax.swing.plaf.UIResource; +import javax.swing.text.Document; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +/** + * The default {@link JXSearchField} UI delegate. + * + * @author Peter Weishapl + * + */ +public class SearchFieldUI extends BuddyTextFieldUI { + /** + * The search field that we're a UI delegate for. Initialized by the + * installUI method, and reset to null by + * uninstallUI. + * + * @see #installUI + * @see #uninstallUI + */ + protected JXSearchField searchField; + + private Handler handler; + + public static final Insets NO_INSETS = new Insets(0, 0, 0, 0); + + public SearchFieldUI(TextUI delegate) { + super(delegate); + } + + private Handler getHandler() { + if (handler == null) { + handler = new Handler(); + } + return handler; + } + + /** + * Calls {@link #installDefaults()}, adds the search, clear and popup button + * to the search field and registers a {@link PropertyChangeListener} ad + * {@link DocumentListener} and an {@link ActionListener} on the popup + * button. + */ + @Override + public void installUI(JComponent c) { + searchField = (JXSearchField) c; + + super.installUI(c); + + installDefaults(); + layoutButtons(); + + configureListeners(); + } + + private void configureListeners() { + if (isNativeSearchField()) { + popupButton().removeActionListener(getHandler()); + searchField.removePropertyChangeListener(getHandler()); + } else { + popupButton().addActionListener(getHandler()); + searchField.addPropertyChangeListener(getHandler()); + } + + // add support for instant search mode in any case. + searchField.getDocument().addDocumentListener(getHandler()); + } + + private boolean isNativeSearchField() { + return NativeSearchFieldSupport.isNativeSearchField(searchField); + } + + @Override + protected BuddyLayoutAndBorder createBuddyLayoutAndBorder() { + return new BuddyLayoutAndBorder() { + /** + * This does nothing, if the search field is rendered natively on + * Leopard. + */ + @Override + protected void replaceBorderIfNecessary() { + if (!isNativeSearchField()) { + super.replaceBorderIfNecessary(); + } + } + + /** + * Return zero, when the search field is rendered natively on + * Leopard, to make painting work correctly. + */ + @Override + public Dimension preferredLayoutSize(Container parent) { + if (isNativeSearchField()) { + return new Dimension(); + } else { + return super.preferredLayoutSize(parent); + } + } + + /** + * Prevent 'jumping' when text is entered: Include the clear button, + * when layout style is Mac. When layout style is Vista: Take the + * clear button's preferred width if its either greater than the + * search button's pref. width or greater than the popup button's + * pref. width when a popup menu is installed and not using a + * seperate popup button. + */ + @Override + public Insets getBorderInsets(Component c) { + Insets insets = super.getBorderInsets(c); + if (searchField != null && !isNativeSearchField()) { + if (isMacLayoutStyle()) { + if (!clearButton().isVisible()) { + insets.right += clearButton().getPreferredSize().width; + } + } else { + JButton refButton = popupButton(); + if (searchField.getFindPopupMenu() == null + ^ searchField.isUseSeperatePopupButton()) { + refButton = searchButton(); + } + + int clearWidth = clearButton().getPreferredSize().width; + int refWidth = refButton.getPreferredSize().width; + int overSize = clearButton().isVisible() ? refWidth + - clearWidth : clearWidth - refWidth; + if (overSize > 0) { + insets.right += overSize; + } + } + + } + return insets; + } + }; + } + + private void layoutButtons() { + BuddySupport.removeAll(searchField); + + if (isNativeSearchField()) { + return; + } + + if (isMacLayoutStyle()) { + BuddySupport.addLeft(searchButton(), searchField); + } else { + BuddySupport.addRight(searchButton(), searchField); + } + + BuddySupport.addRight(clearButton(), searchField); + + if (usingSeperatePopupButton()) { + BuddySupport.addRight(BuddySupport.createGap(getPopupOffset()), + searchField); + } + + if (usingSeperatePopupButton() || !isMacLayoutStyle()) { + BuddySupport.addRight(popupButton(), searchField); + } else { + BuddySupport.addLeft(popupButton(), searchField); + } + } + + private boolean isMacLayoutStyle() { + return searchField.getLayoutStyle() == LayoutStyle.MAC; + } + + /** + * Initialize the search fields various properties based on the + * corresponding "SearchField.*" properties from defaults table. The + * {@link JXSearchField}s layout is set to the value returned by + * createLayout. Also calls {@link #replaceBorderIfNecessary()} + * and {@link #updateButtons()}. This method is called by + * {@link #installUI(JComponent)}. + * + * @see #installUI + * @see #createLayout + * @see JXSearchField#customSetUIProperty(String, Object) + */ + protected void installDefaults() { + if (isNativeSearchField()) { + return; + } + + if (UIManager.getBoolean("SearchField.useSeperatePopupButton")) { + searchField.customSetUIProperty("useSeperatePopupButton", + Boolean.TRUE); + } else { + searchField.customSetUIProperty("useSeperatePopupButton", + Boolean.FALSE); + } + + searchField.customSetUIProperty("layoutStyle", UIManager + .get("SearchField.layoutStyle")); + searchField.customSetUIProperty("promptFontStyle", UIManager + .get("SearchField.promptFontStyle")); + + if (shouldReplaceResource(searchField.getOuterMargin())) { + searchField.setOuterMargin(UIManager + .getInsets("SearchField.buttonMargin")); + } + + updateButtons(); + + if (shouldReplaceResource(clearButton().getIcon())) { + clearButton().setIcon(UIManager.getIcon("SearchField.clearIcon")); + } + if (shouldReplaceResource(clearButton().getPressedIcon())) { + clearButton().setPressedIcon( + UIManager.getIcon("SearchField.clearPressedIcon")); + } + if (shouldReplaceResource(clearButton().getRolloverIcon())) { + clearButton().setRolloverIcon( + UIManager.getIcon("SearchField.clearRolloverIcon")); + } + + searchButton().setIcon( + getNewIcon(searchButton().getIcon(), "SearchField.icon")); + + popupButton().setIcon( + getNewIcon(popupButton().getIcon(), "SearchField.popupIcon")); + popupButton().setRolloverIcon( + getNewIcon(popupButton().getRolloverIcon(), + "SearchField.popupRolloverIcon")); + popupButton().setPressedIcon( + getNewIcon(popupButton().getPressedIcon(), + "SearchField.popupPressedIcon")); + } + + /** + * Removes all installed listeners, the layout and resets the search field + * original border and removes all children. + */ + @Override + public void uninstallUI(JComponent c) { + super.uninstallUI(c); + + searchField.removePropertyChangeListener(getHandler()); + searchField.getDocument().removeDocumentListener(getHandler()); + popupButton().removeActionListener(getHandler()); + + searchField.setLayout(null); + searchField.removeAll(); + searchField = null; + } + + /** + * Returns true if o is null or of instance + * {@link UIResource}. + * + * @param o an object + * @return true if o is null or of instance + * {@link UIResource} + */ + protected boolean shouldReplaceResource(Object o) { + return o == null || o instanceof UIResource; + } + + /** + * Convience method for only replacing icons if they have not been + * customized by the user. Returns the icon from the defaults table + * belonging to resKey, if + * {@link #shouldReplaceResource(Object)} with the icon as a + * parameter returns true. Otherwise returns icon. + * + * @param icon the current icon + * @param resKey the resource key identifying the default icon + * @return the new icon + */ + protected Icon getNewIcon(Icon icon, String resKey) { + Icon uiIcon = UIManager.getIcon(resKey); + if (shouldReplaceResource(icon)) { + return uiIcon; + } + return icon; + } + + /** + * Convienence method. + * + * @see JXSearchField#getCancelButton() + * @return the clear button + */ + protected final JButton clearButton() { + return searchField.getCancelButton(); + } + + /** + * Convienence method. + * + * @see JXSearchField#getFindButton() + * @return the search button + */ + protected final JButton searchButton() { + return searchField.getFindButton(); + } + + /** + * Convienence method. + * + * @see JXSearchField#getPopupButton() + * @return the popup button + */ + protected final JButton popupButton() { + return searchField.getPopupButton(); + } + + /** + * Returns true if + * {@link JXSearchField#isUseSeperatePopupButton()} is true and + * a search popup menu has been set. + * + * @return the popup button is used in addition to the search button + */ + public boolean usingSeperatePopupButton() { + return searchField.isUseSeperatePopupButton() + && searchField.getFindPopupMenu() != null; + } + + /** + * Returns the number of pixels between the popup button and the clear (or + * search) button as specified in the default table by + * 'SearchField.popupOffset'. Returns 0 if + * {@link #usingSeperatePopupButton()} returns false + * + * @return number of pixels between the popup button and the clear (or + * search) button + */ + protected int getPopupOffset() { + if (usingSeperatePopupButton()) { + return UIManager.getInt("SearchField.popupOffset"); + } + return 0; + } + + /** + * Sets the visibility of the search, clear and popup buttons depending on + * the search mode, layout stye, search text, search popup menu and the use + * of a seperate popup button. Also resets the search buttons pressed and + * rollover icons if the search field is in regular search mode or clears + * the icons when the search field is in instant search mode. + */ + protected void updateButtons() { + clearButton().setVisible( + (!searchField.isRegularSearchMode() || searchField + .isMacLayoutStyle()) + && hasText()); + + boolean clearNotHere = (searchField.isMacLayoutStyle() || !clearButton() + .isVisible()); + + searchButton() + .setVisible( + (searchField.getFindPopupMenu() == null || usingSeperatePopupButton()) + && clearNotHere); + popupButton().setVisible( + searchField.getFindPopupMenu() != null + && (clearNotHere || usingSeperatePopupButton())); + + if (searchField.isRegularSearchMode()) { + searchButton().setRolloverIcon( + getNewIcon(searchButton().getRolloverIcon(), + "SearchField.rolloverIcon")); + searchButton().setPressedIcon( + getNewIcon(searchButton().getPressedIcon(), + "SearchField.pressedIcon")); + } else { + // no action, therefore no rollover icon. + if (shouldReplaceResource(searchButton().getRolloverIcon())) { + searchButton().setRolloverIcon(null); + } + if (shouldReplaceResource(searchButton().getPressedIcon())) { + searchButton().setPressedIcon(null); + } + } + } + + private boolean hasText() { + return searchField.getText() != null + && searchField.getText().length() > 0; + } + + class Handler implements PropertyChangeListener, ActionListener, + DocumentListener { + @Override + public void propertyChange(PropertyChangeEvent evt) { + String prop = evt.getPropertyName(); + Object src = evt.getSource(); + + if (src.equals(searchField)) { + if ("findPopupMenu".equals(prop) || "searchMode".equals(prop) + || "useSeperatePopupButton".equals(prop) + || "searchMode".equals(prop) + || "layoutStyle".equals(prop)) { + layoutButtons(); + updateButtons(); + } else if ("document".equals(prop)) { + Document doc = (Document) evt.getOldValue(); + if (doc != null) { + doc.removeDocumentListener(this); + } + doc = (Document) evt.getNewValue(); + if (doc != null) { + doc.addDocumentListener(this); + } + } + } + } + + /** + * Shows the search popup menu, if installed. + */ + @Override + public void actionPerformed(ActionEvent e) { + if (searchField.getFindPopupMenu() != null) { + Component src = SearchFieldAddon.SEARCH_FIELD_SOURCE + .equals(UIManager.getString("SearchField.popupSource")) ? searchField + : (Component) e.getSource(); + + Rectangle r = SwingUtilities.getLocalBounds(src); + int popupWidth = searchField.getFindPopupMenu() + .getPreferredSize().width; + int x = searchField.isVistaLayoutStyle() + || usingSeperatePopupButton() ? r.x + r.width + - popupWidth : r.x; + searchField.getFindPopupMenu().show(src, x, r.y + r.height); + } + } + + @Override + public void changedUpdate(DocumentEvent e) { + update(); + } + + @Override + public void insertUpdate(DocumentEvent e) { + update(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + update(); + } + + /** + * Called when the search text changes. Calls + * {@link JXSearchField#postActionEvent()} In instant search mode or + * starts the search field instant search timer if the instant search + * delay is greater 0. + */ + private void update() { + if (searchField.isInstantSearchMode()) { + searchField.getInstantSearchTimer().stop(); + // only use timer when delay greater 0. + if (searchField.getInstantSearchDelay() > 0) { + searchField.getInstantSearchTimer().setInitialDelay( + searchField.getInstantSearchDelay()); + searchField.getInstantSearchTimer().start(); + } else { + searchField.postActionEvent(); + } + } + + updateButtons(); + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/ShapeUIResource.java b/src/main/java/org/jdesktop/swingx/plaf/ShapeUIResource.java new file mode 100644 index 0000000000..f3321c8d93 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/ShapeUIResource.java @@ -0,0 +1,94 @@ +/* + * $Id: ShapeUIResource.java 4028 2011-06-03 19:32:19Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.plaf; + +import javax.swing.plaf.UIResource; +import java.awt.*; +import java.awt.geom.AffineTransform; +import java.awt.geom.PathIterator; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +/** + * An implementation of Shape that implements UIResource. UI + * classes that create Shapes should use this class. + * + * @author rah003 + */ +public class ShapeUIResource implements Shape, UIResource { + private Shape s; + + /** Creates a new instance of PainterUIResource */ + public ShapeUIResource(Shape p) { + this.s = p; + } + + @Override + public boolean contains(Point2D p) { + return s.contains(p); + } + + @Override + public boolean contains(Rectangle2D r) { + return s.contains(r); + } + + @Override + public boolean contains(double x, double y) { + return s.contains(x, y); + } + + @Override + public boolean contains(double x, double y, double w, double h) { + return s.contains(x, y, w, h); + } + + @Override + public Rectangle getBounds() { + return s.getBounds(); + } + + @Override + public Rectangle2D getBounds2D() { + return s.getBounds2D(); + } + + @Override + public PathIterator getPathIterator(AffineTransform at) { + return s.getPathIterator(at); + } + + @Override + public PathIterator getPathIterator(AffineTransform at, double flatness) { + return s.getPathIterator(at, flatness); + } + + @Override + public boolean intersects(Rectangle2D r) { + return s.intersects(r); + } + + @Override + public boolean intersects(double x, double y, double w, double h) { + return s.intersects(x, y, w, h); + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/StatusBarAddon.java b/src/main/java/org/jdesktop/swingx/plaf/StatusBarAddon.java new file mode 100644 index 0000000000..1e3d8c925b --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/StatusBarAddon.java @@ -0,0 +1,85 @@ +/* + * $Id: StatusBarAddon.java 3475 2009-08-28 08:30:47Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf; + +import org.jdesktop.swingx.JXStatusBar; +import org.jdesktop.swingx.plaf.windows.WindowsLookAndFeelAddons; +import org.jdesktop.swingx.util.OS; + +/** + * Addon for JXStatusBar.
      + * + */ +public class StatusBarAddon extends AbstractComponentAddon { + + public StatusBarAddon() { + super("JXStatusBar"); + } + + @Override + protected void addBasicDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addBasicDefaults(addon, defaults); + + defaults.add(JXStatusBar.uiClassID, + "org.jdesktop.swingx.plaf.basic.BasicStatusBarUI"); + } + + @Override + protected void addMacDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addMacDefaults(addon, defaults); + + defaults.add(JXStatusBar.uiClassID, + "org.jdesktop.swingx.plaf.macosx.MacOSXStatusBarUI"); + } + + @Override + protected void addMetalDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addMetalDefaults(addon, defaults); + + defaults.add(JXStatusBar.uiClassID, + "org.jdesktop.swingx.plaf.metal.MetalStatusBarUI"); + } + + @Override + protected void addWindowsDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addWindowsDefaults(addon, defaults); + if (OS.isUsingWindowsVisualStyles()) { + defaults.add(JXStatusBar.uiClassID, + "org.jdesktop.swingx.plaf.windows.WindowsStatusBarUI"); + + String xpStyle = OS.getWindowsVisualStyle(); + + if (WindowsLookAndFeelAddons.SILVER_VISUAL_STYLE.equalsIgnoreCase(xpStyle) + || WindowsLookAndFeelAddons.VISTA_VISUAL_STYLE.equalsIgnoreCase(xpStyle)) { + defaults.add("StatusBar.leftImage", "resources/silver-statusbar-left.png"); + defaults.add("StatusBar.middleImage", "resources/silver-statusbar-middle.png"); + defaults.add("StatusBar.rightImage", "resources/silver-statusbar-right.png"); + } else { + defaults.add("StatusBar.leftImage", "resources/statusbar-left.png"); + defaults.add("StatusBar.middleImage", "resources/statusbar-middle.png"); + defaults.add("StatusBar.rightImage", "resources/statusbar-right.png"); + } + } else { + defaults.add(JXStatusBar.uiClassID, + "org.jdesktop.swingx.plaf.windows.WindowsClassicStatusBarUI"); + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/StatusBarUI.java b/src/main/java/org/jdesktop/swingx/plaf/StatusBarUI.java new file mode 100644 index 0000000000..456c3812dd --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/StatusBarUI.java @@ -0,0 +1,32 @@ +/* + * $Id: StatusBarUI.java 3246 2009-02-04 15:43:14Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.plaf; + +import javax.swing.plaf.ComponentUI; + +/** + * Pluggable look and feel interface for StatusBar. + * + * @author rbair + */ +public abstract class StatusBarUI extends ComponentUI { +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/TableAddon.java b/src/main/java/org/jdesktop/swingx/plaf/TableAddon.java new file mode 100644 index 0000000000..c030478adc --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/TableAddon.java @@ -0,0 +1,121 @@ +/* + * $Id$ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.plaf; + +import javax.swing.*; +import javax.swing.border.AbstractBorder; +import javax.swing.border.Border; +import javax.swing.plaf.UIResource; +import java.awt.*; +import java.util.logging.Logger; + +/** + * TODO add type doc + * + * @author Jeanette Winzenburg + */ +public class TableAddon extends AbstractComponentAddon { + + @SuppressWarnings("unused") + private static final Logger LOG = Logger.getLogger(TableAddon.class + .getName()); + + /** + * @param name + */ + public TableAddon() { + super("JXTable"); + } + + + + @Override + protected void addNimbusDefaults(LookAndFeelAddons addon, + DefaultsList defaults) { + super.addNimbusDefaults(addon, defaults); + // JW: Hacking around core issue #6882917 + // which is the underlying reason for issue #1180-swingx + // (SwingX vs Nimbus table striping) + if (Boolean.TRUE.equals(UIManager.get("Nimbus.keepAlternateRowColor"))) return; + Object value = UIManager.getLookAndFeelDefaults().remove("Table.alternateRowColor"); + if (value instanceof Color) { + defaults.add("UIColorHighlighter.stripingBackground", value, false); + } + } + + + + /** + * @inherited

      + * + * PENDING JW: move to addLinuxDefaults after testing + */ + @Override + protected void addBasicDefaults(LookAndFeelAddons addon, + DefaultsList defaults) { + super.addBasicDefaults(addon, defaults); + if (isGTK()) { + replaceListTableBorders(addon, defaults); + } + } + + private void replaceListTableBorders(LookAndFeelAddons addon, + DefaultsList defaults) { + replaceBorder(defaults, "Table.", "focusCellHighlightBorder"); + replaceBorder(defaults, "Table.", "focusSelectedCellHighlightBorder"); + replaceBorder(defaults, "Table.", "noFocusBorder"); + } + + + + /** + * @param defaults + * @param componentPrefix + * @param borderKey + */ + private void replaceBorder(DefaultsList defaults, String componentPrefix, + String borderKey) { + String key = componentPrefix + borderKey; + Border border = UIManager.getBorder(componentPrefix + borderKey); + if (border instanceof AbstractBorder && border instanceof UIResource + && border.getClass().getName().contains("ListTable")) { + border = new SafeBorder((AbstractBorder) border); + // PENDING JW: this is fishy ... adding to lookAndFeelDefaults is taken + UIManager.getLookAndFeelDefaults().put(key, border); + // adding to defaults is not +// defaults.add(key, border); + + } + } + + + + /** + * + * @return true if the LF is GTK. + */ + private boolean isGTK() { + return "GTK".equals(UIManager.getLookAndFeel().getID()); + } + + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/TableHeaderAddon.java b/src/main/java/org/jdesktop/swingx/plaf/TableHeaderAddon.java new file mode 100644 index 0000000000..6fcf451b18 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/TableHeaderAddon.java @@ -0,0 +1,64 @@ +/* + * $Id$ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.plaf; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.plaf.BorderUIResource; +import javax.swing.plaf.metal.MetalBorders; + +/** + * Addon for JXTableHeader. + * + * Implemented to hack around core issue ??: Metal header renderer appears squeezed. + * + * @author Jeanette Winzenburg + */ +public class TableHeaderAddon extends AbstractComponentAddon { + + /** + * @param name + */ + public TableHeaderAddon() { + super("JXTableHeader"); + } + + @Override + protected void addMetalDefaults(LookAndFeelAddons addon, + DefaultsList defaults) { + super.addMetalDefaults(addon, defaults); + String key = "TableHeader.cellBorder"; + Border border = UIManager.getBorder(key); + if (border instanceof MetalBorders.TableHeaderBorder) { + border = new BorderUIResource.CompoundBorderUIResource(border, + BorderFactory.createEmptyBorder()); + // PENDING JW: this is fishy ... adding to lookAndFeelDefaults is taken + UIManager.getLookAndFeelDefaults().put(key, border); + // adding to defaults is not +// defaults.add(key, border); + } + + } + + + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/TaskPaneAddon.java b/src/main/java/org/jdesktop/swingx/plaf/TaskPaneAddon.java new file mode 100644 index 0000000000..f1c472050b --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/TaskPaneAddon.java @@ -0,0 +1,233 @@ +/* + * $Id: TaskPaneAddon.java 3622 2010-02-25 04:34:55Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf; + +import org.jdesktop.swingx.JXTaskPane; +import org.jdesktop.swingx.plaf.windows.WindowsClassicLookAndFeelAddons; +import org.jdesktop.swingx.plaf.windows.WindowsLookAndFeelAddons; +import org.jdesktop.swingx.util.OS; + +import javax.swing.*; +import javax.swing.plaf.ColorUIResource; +import javax.swing.plaf.FontUIResource; +import javax.swing.plaf.metal.MetalLookAndFeel; +import javax.swing.plaf.metal.OceanTheme; +import java.awt.*; + +/** + * Addon for JXTaskPane.
      + * + * @author Frederic Lavigne + * @author Karl Schaefer + */ +public class TaskPaneAddon extends AbstractComponentAddon { + + public TaskPaneAddon() { + super("JXTaskPane"); + } + + @Override + protected void addBasicDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + Font taskPaneFont = UIManagerExt.getSafeFont("Label.font", new Font( + "Dialog", Font.PLAIN, 12)); + taskPaneFont = taskPaneFont.deriveFont(Font.BOLD); + + Color menuBackground = new ColorUIResource(SystemColor.menu); + + defaults.add(JXTaskPane.uiClassID, "org.jdesktop.swingx.plaf.basic.BasicTaskPaneUI"); + defaults.add("TaskPane.font", new FontUIResource(taskPaneFont)); + defaults.add("TaskPane.background", UIManagerExt.getSafeColor("List.background", + new ColorUIResource(Color.decode("#005C5C")))); + defaults.add("TaskPane.specialTitleBackground", new ColorUIResource(menuBackground.darker())); + defaults.add("TaskPane.titleBackgroundGradientStart", menuBackground); + defaults.add("TaskPane.titleBackgroundGradientEnd", menuBackground); + defaults.add("TaskPane.titleForeground", new ColorUIResource(SystemColor.menuText)); + defaults.add("TaskPane.specialTitleForeground", new ColorUIResource(SystemColor.menuText.brighter())); + defaults.add("TaskPane.animate", Boolean.TRUE); + defaults.add("TaskPane.focusInputMap", new UIDefaults.LazyInputMap(new Object[] { + "ENTER", "toggleCollapsed", + "SPACE", "toggleCollapsed"})); + } + + @Override + protected void addLinuxDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + addMetalDefaults(addon, defaults); + } + + @Override + protected void addMetalDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addMetalDefaults(addon, defaults); + + if (MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme) { + defaults.add(JXTaskPane.uiClassID, "org.jdesktop.swingx.plaf.misc.GlossyTaskPaneUI"); + } else { + defaults.add(JXTaskPane.uiClassID, "org.jdesktop.swingx.plaf.metal.MetalTaskPaneUI"); + } + + //TODO use safe methods + defaults.add("TaskPane.foreground", UIManager.getColor("activeCaptionText")); + defaults.add("TaskPane.background", MetalLookAndFeel.getControl()); + defaults.add("TaskPane.specialTitleBackground", MetalLookAndFeel.getPrimaryControl()); + defaults.add("TaskPane.titleBackgroundGradientStart", MetalLookAndFeel.getPrimaryControl()); + defaults.add("TaskPane.titleBackgroundGradientEnd", MetalLookAndFeel.getPrimaryControlHighlight()); + defaults.add("TaskPane.titleForeground", MetalLookAndFeel.getControlTextColor()); + defaults.add("TaskPane.specialTitleForeground", MetalLookAndFeel.getControlTextColor()); + defaults.add("TaskPane.borderColor", MetalLookAndFeel.getPrimaryControl()); + defaults.add("TaskPane.titleOver", new ColorUIResource(MetalLookAndFeel.getControl().darker())); + defaults.add("TaskPane.specialTitleOver", MetalLookAndFeel.getPrimaryControlHighlight()); + } + + @Override + protected void addWindowsDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addWindowsDefaults(addon, defaults); + + if (addon instanceof WindowsLookAndFeelAddons) { + defaults.add(JXTaskPane.uiClassID, "org.jdesktop.swingx.plaf.windows.WindowsTaskPaneUI"); + + String xpStyle = OS.getWindowsVisualStyle(); + if (WindowsLookAndFeelAddons.HOMESTEAD_VISUAL_STYLE + .equalsIgnoreCase(xpStyle)) { + defaults.add("TaskPane.foreground", new ColorUIResource(86, 102, 45)); + defaults.add("TaskPane.background", new ColorUIResource(246, 246, 236)); + defaults.add("TaskPane.specialTitleBackground", new ColorUIResource(224, 231, 184)); + defaults.add("TaskPane.titleBackgroundGradientStart", new ColorUIResource(Color.WHITE)); + defaults.add("TaskPane.titleBackgroundGradientEnd", new ColorUIResource(224, 231, 184)); + defaults.add("TaskPane.titleForeground", new ColorUIResource(86, 102, 45)); + defaults.add("TaskPane.titleOver", new ColorUIResource(114, 146, 29)); + defaults.add("TaskPane.specialTitleForeground", new ColorUIResource(86, 102, 45)); + defaults.add("TaskPane.specialTitleOver", new ColorUIResource(114, 146, 29)); + defaults.add("TaskPane.borderColor", new ColorUIResource(Color.WHITE)); + } else if (WindowsLookAndFeelAddons.SILVER_VISUAL_STYLE + .equalsIgnoreCase(xpStyle)) { + defaults.add("TaskPane.foreground", new ColorUIResource(Color.BLACK)); + defaults.add("TaskPane.background", new ColorUIResource(240, 241, 245)); + defaults.add("TaskPane.specialTitleBackground", new ColorUIResource(222, 222, 222)); + defaults.add("TaskPane.titleBackgroundGradientStart", new ColorUIResource(Color.WHITE)); + defaults.add("TaskPane.titleBackgroundGradientEnd", new ColorUIResource(214, 215, 224)); + defaults.add("TaskPane.titleForeground", new ColorUIResource(Color.BLACK)); + defaults.add("TaskPane.titleOver", new ColorUIResource(126, 124, 124)); + defaults.add("TaskPane.specialTitleForeground", new ColorUIResource(Color.BLACK)); + defaults.add("TaskPane.specialTitleOver", new ColorUIResource(126, 124, 124)); + defaults.add("TaskPane.borderColor", new ColorUIResource(Color.WHITE)); + } else if (OS.isWindowsVista()) { + //do not need to use safe method since the properties can never return null + final Toolkit toolkit = Toolkit.getDefaultToolkit(); + + defaults.add("TaskPane.foreground", new ColorUIResource(Color.WHITE)); + defaults.add("TaskPane.background", + new ColorUIResource((Color)toolkit.getDesktopProperty("win.3d.backgroundColor"))); + defaults.add("TaskPane.specialTitleBackground", new ColorUIResource(33, 89, 201)); + defaults.add("TaskPane.titleBackgroundGradientStart", new ColorUIResource(Color.WHITE)); + defaults.add("TaskPane.titleBackgroundGradientEnd", + new ColorUIResource((Color)toolkit.getDesktopProperty("win.frame.inactiveCaptionColor"))); + defaults.add("TaskPane.titleForeground", + new ColorUIResource((Color)toolkit.getDesktopProperty("win.frame.inactiveCaptionTextColor"))); + defaults.add("TaskPane.specialTitleForeground", new ColorUIResource(Color.WHITE)); + defaults.add("TaskPane.borderColor", new ColorUIResource(Color.WHITE)); + } else { + defaults.add("TaskPane.foreground", new ColorUIResource(Color.WHITE)); + defaults.add("TaskPane.background", new ColorUIResource(214, 223, 247)); + defaults.add("TaskPane.specialTitleBackground", new ColorUIResource(33, 89, 201)); + defaults.add("TaskPane.titleBackgroundGradientStart", new ColorUIResource(Color.WHITE)); + defaults.add("TaskPane.titleBackgroundGradientEnd", new ColorUIResource(199, 212, 247)); + defaults.add("TaskPane.titleForeground", new ColorUIResource(33, 89, 201)); + defaults.add("TaskPane.specialTitleForeground", new ColorUIResource(Color.WHITE)); + defaults.add("TaskPane.borderColor", new ColorUIResource(Color.WHITE)); + } + } + + if (addon instanceof WindowsClassicLookAndFeelAddons) { + defaults.add(JXTaskPane.uiClassID, "org.jdesktop.swingx.plaf.windows.WindowsClassicTaskPaneUI"); + defaults.add("TaskPane.foreground", new ColorUIResource(Color.BLACK)); + defaults.add("TaskPane.background", new ColorUIResource(Color.WHITE)); + defaults.add("TaskPane.specialTitleBackground", new ColorUIResource(10, 36, 106)); + defaults.add("TaskPane.titleBackgroundGradientStart", new ColorUIResource(212, 208, 200)); + defaults.add("TaskPane.titleBackgroundGradientEnd", new ColorUIResource(212, 208, 200)); + defaults.add("TaskPane.titleForeground", new ColorUIResource(Color.BLACK)); + defaults.add("TaskPane.specialTitleForeground", new ColorUIResource(Color.WHITE)); + defaults.add("TaskPane.borderColor", new ColorUIResource(212, 208, 200)); + } + } + + @Override + protected void addMacDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addMacDefaults(addon, defaults); + + defaults.add(JXTaskPane.uiClassID, "org.jdesktop.swingx.plaf.misc.GlossyTaskPaneUI"); + defaults.add("TaskPane.background", new ColorUIResource(245, 245, 245)); + defaults.add("TaskPane.titleForeground", new ColorUIResource(Color.BLACK)); + defaults.add("TaskPane.specialTitleBackground", new ColorUIResource(188,188,188)); + defaults.add("TaskPane.specialTitleForeground", new ColorUIResource(Color.BLACK)); + defaults.add("TaskPane.titleBackgroundGradientStart", new ColorUIResource(250,250,250)); + defaults.add("TaskPane.titleBackgroundGradientEnd", new ColorUIResource(188,188,188)); + defaults.add("TaskPane.borderColor", new ColorUIResource(97, 97, 97)); + defaults.add("TaskPane.titleOver", new ColorUIResource(125, 125, 97)); + defaults.add("TaskPane.specialTitleOver", new ColorUIResource(125, 125, 97)); + } + + @Override + protected void addNimbusDefaults(LookAndFeelAddons addon, + DefaultsList defaults) { + super.addNimbusDefaults(addon, defaults); + + defaults.add(JXTaskPane.uiClassID, + "org.jdesktop.swingx.plaf.nimbus.NimbusTaskPaneUI"); + // dynamically changing the LaF to Nimbus does not refresh correctly the + // control colors if they are not hard-coded due to Nimbus DerivedColors + // lazy initialization + // defaults.add("TaskPane.foreground", new + // ColorUIResource(UIManager.getColor("activeCaption"))); + defaults.add("TaskPane.foreground", new ColorUIResource(186, 190, 198)); + // defaults.add("TaskPane.background", new + // ColorUIResource(UIManager.getColor("control"))); + defaults.add("TaskPane.background", new ColorUIResource(214, 217, 223)); + // defaults.add("TaskPane.specialTitleBackground", new + // ColorUIResource(UIManager.getColor("nimbusBlueGrey"))); + defaults.add("TaskPane.specialTitleBackground", new ColorUIResource( + 169, 176, 190)); + // defaults.add("TaskPane.titleBackgroundGradientStart", new + // ColorUIResource(UIManager.getColor("background"))); + defaults.add("TaskPane.titleBackgroundGradientStart", + new ColorUIResource(214, 217, 223)); + // defaults.add("TaskPane.titleBackgroundGradientEnd", new + // ColorUIResource(UIManager.getColor("controlLHighlight"))); + defaults.add("TaskPane.titleBackgroundGradientEnd", + new ColorUIResource(247, 248, 250)); + defaults.add("TaskPane.titleForeground", new ColorUIResource( + Color.BLACK)); + defaults.add("TaskPane.specialTitleForeground", new ColorUIResource( + Color.BLACK)); + // defaults.add("TaskPane.borderColor", new + // ColorUIResource(UIManager.getColor("nimbusBorder"))); + defaults + .add("TaskPane.borderColor", new ColorUIResource(146, 151, 161)); + // defaults.add("TaskPane.titleOver", new + // ColorUIResource(UIManager.getColor("nimbusSelection"))); + defaults.add("TaskPane.titleOver", new ColorUIResource(57, 105, 138)); + // defaults.add("TaskPane.specialTitleOver", new + // ColorUIResource(UIManager.getColor("nimbusSelection"))); + defaults.add("TaskPane.specialTitleOver", new ColorUIResource(57, 105, + 138)); + + } + + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/TaskPaneContainerAddon.java b/src/main/java/org/jdesktop/swingx/plaf/TaskPaneContainerAddon.java new file mode 100644 index 0000000000..397e452da2 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/TaskPaneContainerAddon.java @@ -0,0 +1,143 @@ +/* + * $Id: TaskPaneContainerAddon.java 3500 2009-09-10 10:28:24Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf; + +import org.jdesktop.swingx.JXTaskPaneContainer; +import org.jdesktop.swingx.painter.MattePainter; +import org.jdesktop.swingx.plaf.windows.WindowsClassicLookAndFeelAddons; +import org.jdesktop.swingx.plaf.windows.WindowsLookAndFeelAddons; +import org.jdesktop.swingx.util.OS; + +import javax.swing.*; +import javax.swing.plaf.BorderUIResource; +import javax.swing.plaf.ColorUIResource; +import javax.swing.plaf.metal.MetalLookAndFeel; +import java.awt.*; + +/** + * Addon for JXTaskPaneContainer. This addon defines the following properties: + * + * + * + * + * + * + *
      TaskPaneContainer.backgroundbackground color
      TaskPaneContainer.backgroundPainterbackground painter
      TaskPaneContainer.bordercontainer border
      TaskPaneContainer.fontfont (currently unused)
      TaskPaneContainer.foregroundforeground color (currently unused)
      + * + * @author Frederic Lavigne + * @author Karl Schaefer + */ +public class TaskPaneContainerAddon extends AbstractComponentAddon { + + public TaskPaneContainerAddon() { + super("JXTaskPaneContainer"); + } + + @Override + protected void addBasicDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addBasicDefaults(addon, defaults); + + defaults.add(JXTaskPaneContainer.uiClassID, "org.jdesktop.swingx.plaf.basic.BasicTaskPaneContainerUI"); + defaults.add("TaskPaneContainer.background", UIManagerExt.getSafeColor("Desktop.background", + new ColorUIResource(Color.decode("#005C5C")))); + defaults.add("TaskPaneContainer.border", new BorderUIResource(BorderFactory.createEmptyBorder(10, 10, 0, 10))); + } + + /** + * {@inheritDoc} + */ + @Override + protected void addMetalDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addMetalDefaults(addon, defaults); + + defaults.add("TaskPaneContainer.background", MetalLookAndFeel.getDesktopColor()); + } + + /** + * {@inheritDoc} + */ + @Override + protected void addWindowsDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addWindowsDefaults(addon, defaults); + if (addon instanceof WindowsClassicLookAndFeelAddons) { + defaults.add("TaskPaneContainer.background", UIManagerExt.getSafeColor("List.background", + new ColorUIResource(Color.decode("#005C5C")))); + } else if (addon instanceof WindowsLookAndFeelAddons) { + String xpStyle = OS.getWindowsVisualStyle(); + ColorUIResource background; + Color backgroundGradientStart; + Color backgroundGradientEnd; + + if (WindowsLookAndFeelAddons.HOMESTEAD_VISUAL_STYLE + .equalsIgnoreCase(xpStyle)) { + background = new ColorUIResource(201, 215, 170); + backgroundGradientStart = new Color(204, 217, 173); + backgroundGradientEnd = new Color(165, 189, 132); + } else if (WindowsLookAndFeelAddons.SILVER_VISUAL_STYLE + .equalsIgnoreCase(xpStyle)) { + background = new ColorUIResource(192, 195, 209); + backgroundGradientStart = new Color(196, 200, 212); + backgroundGradientEnd = new Color(177, 179, 200); + } else { + if (OS.isWindowsVista()) { + final Toolkit toolkit = Toolkit.getDefaultToolkit(); + background = new ColorUIResource((Color)toolkit.getDesktopProperty("win.3d.backgroundColor")); + backgroundGradientStart = (Color)toolkit.getDesktopProperty("win.frame.activeCaptionColor"); + backgroundGradientEnd = (Color)toolkit.getDesktopProperty("win.frame.inactiveCaptionColor"); + } else { + background = new ColorUIResource(117, 150, 227); + backgroundGradientStart = new ColorUIResource(123, 162, 231); + backgroundGradientEnd = new ColorUIResource(99, 117, 214); + } + } + + defaults.add("TaskPaneContainer.backgroundPainter", new PainterUIResource( + new MattePainter(new GradientPaint( + 0f, 0f, backgroundGradientStart, + 0f, 1f, backgroundGradientEnd), + true))); + defaults.add("TaskPaneContainer.background", background); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void addMacDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addMacDefaults(addon, defaults); + + defaults.add("TaskPaneContainer.background", new ColorUIResource(238, 238, 238)); + } + + @Override + protected void addNimbusDefaults(LookAndFeelAddons addon, + DefaultsList defaults) { + super.addNimbusDefaults(addon, defaults); + // dynamically changing the LaF to Nimbus does not refresh correctly the + // control colors if they are not hard-coded due to Nimbus DerivedColors + // lazy initialization +// defaults.add("TaskPaneContainer.background", new ColorUIResource( +// UIManager.getColor("control"))); + defaults.add("TaskPaneContainer.background", new ColorUIResource(214,217,223)); + } + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/TaskPaneContainerUI.java b/src/main/java/org/jdesktop/swingx/plaf/TaskPaneContainerUI.java new file mode 100644 index 0000000000..cc5770de0d --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/TaskPaneContainerUI.java @@ -0,0 +1,32 @@ +/* + * $Id: TaskPaneContainerUI.java 2363 2007-10-31 13:24:06Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf; + +import javax.swing.plaf.PanelUI; + +/** + * Pluggable UI for JXTaskPaneContainer. + * + * @author Frederic Lavigne + */ +public abstract class TaskPaneContainerUI extends PanelUI { + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/TaskPaneUI.java b/src/main/java/org/jdesktop/swingx/plaf/TaskPaneUI.java new file mode 100644 index 0000000000..b6a5702f6e --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/TaskPaneUI.java @@ -0,0 +1,45 @@ +/* + * $Id: TaskPaneUI.java 2365 2007-11-01 13:38:15Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf; + +import javax.swing.*; +import javax.swing.plaf.PanelUI; +import java.awt.*; + +/** + * Pluggable UI for JXTaskPane. + * + * @author Frederic Lavigne + */ +public abstract class TaskPaneUI extends PanelUI { + + /** + * Called by the component when an action is added to the component through + * the {@link org.jdesktop.swingx.JXTaskPane#add(Action)} method. + * + * @param action + * @return a component built from the action. + */ + public Component createAction(Action action) { + return new JButton(action); + } + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/TextUIWrapper.java b/src/main/java/org/jdesktop/swingx/plaf/TextUIWrapper.java new file mode 100644 index 0000000000..5edb374cf3 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/TextUIWrapper.java @@ -0,0 +1,164 @@ +package org.jdesktop.swingx.plaf; + +import org.jdesktop.swingx.JXSearchField; +import org.jdesktop.swingx.prompt.BuddySupport; + +import javax.swing.*; +import javax.swing.plaf.TextUI; +import javax.swing.plaf.basic.BasicTextUI; +import javax.swing.text.JTextComponent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +/** + * TODO: + * + * @author Peter Weishapl + * + * @param + */ +public abstract class TextUIWrapper { + private static final DefaultWrapper defaultWrapper = new DefaultWrapper(); + + public static final TextUIWrapper getDefaultWrapper() { + return defaultWrapper; + } + + private Class wrapperClass; + + protected TextUIWrapper(Class wrapperClass) { + this.wrapperClass = wrapperClass; + } + + /** + *

      + * Wraps and replaces the current UI of the given textComponent, by calling + * {@link #wrapUI(JTextComponent)} if necessary. + *

      + * + * @param textComponent + * @param stayOnUIChange + * if true, a {@link PropertyChangeListener} is registered, which + * listens for UI changes and wraps any new UI object. + */ + public final void install(final JTextComponent textComponent, boolean stayOnUIChange) { + replaceUIIfNeeded(textComponent); + if (stayOnUIChange) { + uiChangeHandler.install(textComponent); + } + } + + /** + * Wraps and replaces the text components current UI by calling {@link #wrapUI(TextUI)}, if the + * text components current UI is not an instance of the given wrapper class. + * + * @param textComponent + * @return true if the UI has been replaced + */ + protected boolean replaceUIIfNeeded(JTextComponent textComponent) { + if (wrapperClass.isAssignableFrom(textComponent.getUI().getClass())) { + return false; + } + + textComponent.setUI(wrapUI(textComponent)); + + return true; + } + + /** + * Override to return the appropriate UI wrapper object for the given {@link TextUI}. + * + * @param textUI + * @return the wrapping UI + */ + public abstract UI wrapUI(JTextComponent textComponent); + + /** + * Returns the wrapper class. + * + * @return the wrapper class + */ + public Class getWrapperClass() { + return wrapperClass; + } + + /** + *

      + * Removes the {@link PropertyChangeListener}, which listens for "UI" property changes (if + * installed) and then calls {@link JComponent#updateUI()} on the textComponent to + * set the UI object provided by the current {@link UIDefaults}. + *

      + * + * @param textComponent + */ + public final void uninstall(final JTextComponent textComponent) { + uiChangeHandler.uninstall(textComponent); + textComponent.updateUI(); + } + + private final TextUIChangeHandler uiChangeHandler = new TextUIChangeHandler(); + + private final class TextUIChangeHandler extends AbstractUIChangeHandler { + @Override + public void propertyChange(PropertyChangeEvent evt) { + JTextComponent txt = (JTextComponent) evt.getSource(); + + replaceUIIfNeeded(txt); + } + } + + public static final class DefaultWrapper extends TextUIWrapper { + private DefaultWrapper() { + super(PromptTextUI.class); + } + + /** + *

      + * Creates a new {@link PromptTextUI}, which wraps the given textComponents UI. + *

      + *

      + * If the UI is already of type {@link PromptTextUI}, it will be returned. If + * textComponent is of type {@link JXSearchField} a new {@link SearchFieldUI} + * object will be returned. If textComponent is of type {@link JTextField} or + * {@link JTextArea} a {@link BuddyTextFieldUI} or {@link PromptTextAreaUI} will be + * returned, respectively. If the UI is of any other type, a + * {@link IllegalArgumentException} will be thrown. + *

      + * + * @param textComponent + * wrap this components UI + * @return a {@link PromptTextUI} which wraps the textComponents UI. + */ + @Override + public PromptTextUI wrapUI(JTextComponent textComponent) { + TextUI textUI = textComponent.getUI(); + + if (textUI instanceof PromptTextUI) { + return (PromptTextUI) textUI; + } else if (textComponent instanceof JXSearchField) { + return new SearchFieldUI(textUI); + } else if (textComponent instanceof JTextField) { + return new BuddyTextFieldUI(textUI); + } else if (textComponent instanceof JTextArea) { + return new PromptTextAreaUI(textUI); + } + throw new IllegalArgumentException("ui implementation not supported: " + + textUI.getClass()); + } + + /** + * Every time the UI needs to be replaced we also need to make sure, that all buddy + * components are also in the component hierarchy. (That's because {@link BasicTextUI} + * removes all our buddies upon UI changes). + */ + @Override + protected boolean replaceUIIfNeeded(JTextComponent textComponent) { + boolean replaced = super.replaceUIIfNeeded(textComponent); + + if (replaced && textComponent instanceof JTextField) { + BuddySupport.ensureBuddiesAreInComponentHierarchy((JTextField) textComponent); + } + return replaced; + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/TipOfTheDayAddon.java b/src/main/java/org/jdesktop/swingx/plaf/TipOfTheDayAddon.java new file mode 100644 index 0000000000..83bc94bca1 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/TipOfTheDayAddon.java @@ -0,0 +1,87 @@ +/* + * $Id: TipOfTheDayAddon.java 2474 2007-11-21 17:32:04Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf; + +import org.jdesktop.swingx.JXTipOfTheDay; +import org.jdesktop.swingx.plaf.basic.BasicTipOfTheDayUI; +import org.jdesktop.swingx.plaf.windows.WindowsTipOfTheDayUI; + +import javax.swing.*; +import javax.swing.plaf.BorderUIResource; +import javax.swing.plaf.ColorUIResource; +import javax.swing.plaf.FontUIResource; +import java.awt.*; + +/** + * Addon for JXTipOfTheDay.
      + * + * @author Frederic Lavigne + */ +public class TipOfTheDayAddon extends AbstractComponentAddon { + + public TipOfTheDayAddon() { + super("JXTipOfTheDay"); + } + + @Override + protected void addBasicDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addBasicDefaults(addon, defaults); + + Font font = UIManagerExt.getSafeFont("Label.font", new Font("Dialog", Font.PLAIN, 12)); + font = font.deriveFont(Font.BOLD, 13f); + + defaults.add(JXTipOfTheDay.uiClassID, BasicTipOfTheDayUI.class.getName()); + defaults.add("TipOfTheDay.font", UIManagerExt.getSafeFont("TextPane.font", + new FontUIResource("Serif", Font.PLAIN, 12))); + defaults.add("TipOfTheDay.tipFont", new FontUIResource(font)); + defaults.add("TipOfTheDay.background", new ColorUIResource(Color.WHITE)); + defaults.add("TipOfTheDay.icon", + LookAndFeel.makeIcon(BasicTipOfTheDayUI.class, "resources/TipOfTheDay24.gif")); + defaults.add("TipOfTheDay.border", new BorderUIResource( + BorderFactory.createLineBorder(new Color(117, 117, 117)))); + + UIManagerExt.addResourceBundle( + "org.jdesktop.swingx.plaf.basic.resources.TipOfTheDay"); + } + + /** + * {@inheritDoc} + */ + @Override + protected void addWindowsDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addWindowsDefaults(addon, defaults); + + Font font = UIManagerExt.getSafeFont("Label.font", + new Font("Dialog", Font.PLAIN, 12)); + font = font.deriveFont(13f); + + defaults.add(JXTipOfTheDay.uiClassID, WindowsTipOfTheDayUI.class.getName()); + defaults.add("TipOfTheDay.background", new ColorUIResource(Color.GRAY)); + defaults.add("TipOfTheDay.font", new FontUIResource(font)); + defaults.add("TipOfTheDay.icon", + LookAndFeel.makeIcon(WindowsTipOfTheDayUI.class, "resources/tipoftheday.png")); + defaults.add("TipOfTheDay.border" ,new BorderUIResource(new WindowsTipOfTheDayUI.TipAreaBorder())); + + UIManagerExt.addResourceBundle( + "org.jdesktop.swingx.plaf.windows.resources.TipOfTheDay"); + } + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/TipOfTheDayUI.java b/src/main/java/org/jdesktop/swingx/plaf/TipOfTheDayUI.java new file mode 100644 index 0000000000..5854622511 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/TipOfTheDayUI.java @@ -0,0 +1,48 @@ +/* + * $Id: TipOfTheDayUI.java 542 2005-10-10 18:03:15Z rbair $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf; + +import org.jdesktop.swingx.JXTipOfTheDay; + +import javax.swing.*; +import javax.swing.plaf.PanelUI; +import java.awt.*; + +/** + * Pluggable UI for JXTipOfTheDay. + * + * @author Frederic Lavigne + */ +public abstract class TipOfTheDayUI extends PanelUI { + + /** + * Creates a new JDialog to display a JXTipOfTheDay panel. If + * choice is not null then the window will offer a way for the + * end-user to not show the tip of the day dialog. + * + * @param parentComponent + * @param choice + * @return a new JDialog to display a JXTipOfTheDay panel + */ + public abstract JDialog createDialog(Component parentComponent, + JXTipOfTheDay.ShowOnStartupChoice choice); + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/TitledPanelAddon.java b/src/main/java/org/jdesktop/swingx/plaf/TitledPanelAddon.java new file mode 100644 index 0000000000..ee53e2acf4 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/TitledPanelAddon.java @@ -0,0 +1,107 @@ +/* + * $Id: TitledPanelAddon.java 3288 2009-03-10 14:36:28Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf; + + +import org.jdesktop.swingx.JXTitledPanel; +import org.jdesktop.swingx.painter.MattePainter; + +import javax.swing.plaf.ColorUIResource; +import javax.swing.plaf.FontUIResource; +import javax.swing.plaf.InsetsUIResource; +import javax.swing.plaf.metal.MetalLookAndFeel; +import java.awt.*; + +/** + * Addon for JXTitledPanel.
      + * + */ +public class TitledPanelAddon extends AbstractComponentAddon { + + public TitledPanelAddon() { + super("JXTitledPanel"); + } + + @Override + protected void addBasicDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addBasicDefaults(addon, defaults); + + defaults.add(JXTitledPanel.uiClassID, "org.jdesktop.swingx.plaf.basic.BasicTitledPanelUI"); + defaults.add("JXTitledPanel.titleFont", UIManagerExt.getSafeFont("Button.font", + new FontUIResource("Dialog", Font.PLAIN, 12))); + defaults.add("JXTitledPanel.titleForeground", new ColorUIResource(Color.WHITE)); + defaults.add("JXTitledPanel.titlePainter", new PainterUIResource( + new MattePainter( + new GradientPaint(0, 0, Color.LIGHT_GRAY, 0, 1, Color.GRAY), true))); + defaults.add("JXTitledPanel.captionInsets", new InsetsUIResource(4, 12, 4, 12)); + defaults.add("JXTitledPanel.rightDecorationInsets", new InsetsUIResource(1,1,1,1)); + defaults.add("JXTitledPanel.leftDecorationInsets", new InsetsUIResource(1,1,1,1)); + } + + /** + * {@inheritDoc} + */ + @Override + protected void addLinuxDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + addMetalDefaults(addon, defaults); + } + + /** + * {@inheritDoc} + */ + @Override + protected void addMetalDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addMetalDefaults(addon, defaults); + + if (isPlastic()) { + defaults.add("JXTitledPanel.titleForeground", new ColorUIResource(Color.WHITE)); + defaults.add("JXTitledPanel.titlePainter", new PainterUIResource( + new MattePainter(new GradientPaint(0, 0, new Color(49, 121, 242), + 0, 1, new Color(198, 211, 247)), true))); + } else { + defaults.add("JXTitledPanel.titleForeground", new ColorUIResource(Color.WHITE)); + defaults.add("JXTitledPanel.titlePainter", new PainterUIResource( + new MattePainter(new GradientPaint(0, 0, + MetalLookAndFeel.getCurrentTheme().getPrimaryControl(), 0, 1, + MetalLookAndFeel.getCurrentTheme().getPrimaryControlDarkShadow()),true))); + } + } + + @Override + protected void addWindowsDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addWindowsDefaults(addon, defaults); + + // JW: hot fix for #291-swingx + // was tracked down by Neil Weber - the requested colors are not available in + // all LFs, so changed to fall-back to something real + // don't understand why this has blown when trying to toggle to Metal... + // definitely needs deeper digging + // kgs: moved to using getSafeXXX from UIManagerExt + defaults.add("JXTitledPanel.titleForeground", UIManagerExt.getSafeColor( + "InternalFrame.activeTitleForeground", new ColorUIResource(Color.WHITE))); + defaults.add("JXTitledPanel.titlePainter", new PainterUIResource( + new MattePainter(new GradientPaint(0, 0, UIManagerExt.getSafeColor( + "InternalFrame.inactiveTitleGradient", new ColorUIResource(49, 121, 242)), + 0, 1, UIManagerExt.getSafeColor( "InternalFrame.activeTitleBackground", + new ColorUIResource(198, 211, 247))), true))); + + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/TitledPanelUI.java b/src/main/java/org/jdesktop/swingx/plaf/TitledPanelUI.java new file mode 100644 index 0000000000..128c36688d --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/TitledPanelUI.java @@ -0,0 +1,50 @@ +/* + * $Id: TitledPanelUI.java 997 2006-04-11 20:51:31Z rbair $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.plaf; + +import javax.swing.*; +import javax.swing.plaf.PanelUI; +import java.awt.*; + +/** + * + * @author rbair + */ +public abstract class TitledPanelUI extends PanelUI { + /** + * Adds the given JComponent as a decoration on the right of the title + * @param decoration + */ + public abstract void setRightDecoration(JComponent decoration); + public abstract JComponent getRightDecoration(); + + /** + * Adds the given JComponent as a decoration on the left of the title + * @param decoration + */ + public abstract void setLeftDecoration(JComponent decoration); + public abstract JComponent getLeftDecoration(); + /** + * @return the Container acting as the title bar for this component + */ + public abstract Container getTitleBar(); +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/UIAction.java b/src/main/java/org/jdesktop/swingx/plaf/UIAction.java new file mode 100644 index 0000000000..32bca5a6b2 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/UIAction.java @@ -0,0 +1,111 @@ +/* + * $Id: UIAction.java 4028 2011-06-03 19:32:19Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.plaf; + +import javax.swing.*; +import java.beans.PropertyChangeListener; + +/** + * UIAction is the basis of all of basic's action classes that are used in + * an ActionMap. Subclasses need to override actionPerformed. + *

      + * A typical subclass will look like: + *

      + *    private static class Actions extends UIAction {
      + *        Actions(String name) {
      + *            super(name);
      + *        }
      + *
      + *        public void actionPerformed(ActionEvent ae) {
      + *            if (getName() == "selectAll") {
      + *                selectAll();
      + *            }
      + *            else if (getName() == "cancelEditing") {
      + *                cancelEditing();
      + *            }
      + *        }
      + *    }
      + * 
      + *

      + * Subclasses that wish to conditionalize the enabled state should override + * isEnabled(Component), and be aware that the passed in + * Component may be null. + *

      + * This is based on sun.swing.UIAction in J2SE 1.5 + * + * @see Action + * @author Scott Violet + */ +public abstract class UIAction implements Action { + private String name; + + public UIAction(String name) { + this.name = name; + } + + public final String getName() { + return name; + } + + @Override + public Object getValue(String key) { + return NAME.equals(key) ? name : null; + } + + // UIAction is not mutable, this does nothing. + @Override + public void putValue(String key, Object value) { + } + + // UIAction is not mutable, this does nothing. + @Override + public void setEnabled(boolean b) { + } + + /** + * Cover method for isEnabled(null). + */ + @Override + public final boolean isEnabled() { + return isEnabled(null); + } + + /** + * Subclasses that need to conditionalize the enabled state should + * override this. Be aware that sender may be null. + * + * @param sender Widget enabled state is being asked for, may be null. + */ + public boolean isEnabled(Object sender) { + return true; + } + + // UIAction is not mutable, this does nothing. + @Override + public void addPropertyChangeListener(PropertyChangeListener listener) { + } + + // UIAction is not mutable, this does nothing. + @Override + public void removePropertyChangeListener(PropertyChangeListener listener) { + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/UIColorHighlighterAddon.java b/src/main/java/org/jdesktop/swingx/plaf/UIColorHighlighterAddon.java new file mode 100644 index 0000000000..59184500f2 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/UIColorHighlighterAddon.java @@ -0,0 +1,131 @@ +/* + * $Id: UIColorHighlighterAddon.java 3513 2009-09-22 11:18:09Z kleopatra $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf; + +import org.jdesktop.swingx.plaf.windows.WindowsLookAndFeelAddons; +import org.jdesktop.swingx.util.OS; + +import javax.swing.*; +import javax.swing.plaf.ColorUIResource; +import javax.swing.plaf.metal.MetalLookAndFeel; +import javax.swing.plaf.metal.OceanTheme; +import java.awt.*; +import java.util.logging.Logger; + +/** + * Loads LF specific background striping colors. + * + * The colors are based on the LF selection colors for certain + * LFs and themes, for unknown LFs/themes a generic grey is used. + * + * + * @author Jeanette Winzenburg + * @author Karl Schaefer + */ +public class UIColorHighlighterAddon extends AbstractComponentAddon { + + @SuppressWarnings("unused") + private static final Logger LOG = Logger + .getLogger(UIColorHighlighterAddon.class.getName()); + + public UIColorHighlighterAddon() { + super("UIColorHighlighter"); + } + + /** + * {@inheritDoc} + */ + @Override + protected void addBasicDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addBasicDefaults(addon, defaults); + + defaults.add("UIColorHighlighter.stripingBackground", new ColorUIResource(229, 229, 229)); + } + + /** + * {@inheritDoc} + */ + @Override + protected void addMacDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addMacDefaults(addon, defaults); + + defaults.add("UIColorHighlighter.stripingBackground", new ColorUIResource(237, 243, 254)); + } + + /** + * {@inheritDoc} + */ + @Override + protected void addMetalDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addMetalDefaults(addon, defaults); + + if (MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme) { + defaults.add("UIColorHighlighter.stripingBackground", new ColorUIResource(230, 238, 246)); + } else { + defaults.add("UIColorHighlighter.stripingBackground", new ColorUIResource(235, 235, 255)); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void addWindowsDefaults(LookAndFeelAddons addon, DefaultsList defaults) { + super.addWindowsDefaults(addon, defaults); + + if (OS.isUsingWindowsVisualStyles()) { + String xpStyle = OS.getWindowsVisualStyle(); + + if (WindowsLookAndFeelAddons.HOMESTEAD_VISUAL_STYLE + .equalsIgnoreCase(xpStyle)) { + defaults.add("UIColorHighlighter.stripingBackground", new ColorUIResource(228, 231, 219)); + } else if (WindowsLookAndFeelAddons.SILVER_VISUAL_STYLE + .equalsIgnoreCase(xpStyle)) { + defaults.add("UIColorHighlighter.stripingBackground", new ColorUIResource(235, 235, 236)); + } else { + // default blue + defaults.add("UIColorHighlighter.stripingBackground", new ColorUIResource(224, 233, 246)); + } + + } else { + defaults.add("UIColorHighlighter.stripingBackground", new ColorUIResource(218, 222, 233)); + } + } + + @Override + protected void addNimbusDefaults(LookAndFeelAddons addon, + DefaultsList defaults) { + super.addNimbusDefaults(addon, defaults); + // JW: Hacking around core issue #6882917 + // which is the underlying reason for issue #1180-swingx + // (SwingX vs Nimbus table striping) + if (Boolean.TRUE.equals(UIManager.get("Nimbus.keepAlternateRowColor"))) return; + // PENDING JW: not entirely sure if it is safe to really grab the color here + // the Nimbus (Derived)Color is not yet fully installed at this moment + // so without a table to instantiate may be rgb = 0,0,0 + Object value = UIManager.getLookAndFeelDefaults().remove("Table.alternateRowColor"); + if (value instanceof Color) { + defaults.add("UIColorHighlighter.stripingBackground", value, false); + } + } + + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/UIDependent.java b/src/main/java/org/jdesktop/swingx/plaf/UIDependent.java new file mode 100644 index 0000000000..143856464b --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/UIDependent.java @@ -0,0 +1,36 @@ +/* + * $Id: UIDependent.java 4028 2011-06-03 19:32:19Z kschaefe $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf; + +/** + * Encapsulates state that depends on the UI and needs + * to be updated on LookAndFeel change. + * + * @author Jeanette Winzenburg + */ +public interface UIDependent { + /** + * Updates all internal visuals after changing a UI-delegate. + * + * @see javax.swing.JComponent#updateUI() + */ + void updateUI(); +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/UIManagerExt.java b/src/main/java/org/jdesktop/swingx/plaf/UIManagerExt.java new file mode 100644 index 0000000000..b2620df348 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/UIManagerExt.java @@ -0,0 +1,721 @@ +/* + * $Id: UIManagerExt.java 4028 2011-06-03 19:32:19Z kschaefe $ + * + * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf; + +import org.jdesktop.swingx.painter.Painter; +import org.jdesktop.swingx.util.Contract; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.plaf.*; +import java.awt.*; +import java.util.*; + +/** + * A utility class for obtaining configuration properties from the + * {@code UIDefaults}. This class handles SwingX-specific L&F needs, such as + * the installation of painters and shapes. There are several categories of + * utility methods: + *

        + *
      • Support for the safe creation of {@code UIResource}s.
      • + *
      • Support for new {@code UIResource} types, such as + * {@code PainterUIResource}.
      • + *
      • Support for the dynamic localization of {@code UIDefaults}.
      • + *
      • Support for returning non-{@code String} localizations from + * {@code ResourceBundle}s.
      • + *
      + *

      Safe Methods

      + *

      + * The {@code getSafeXXX} methods are designed for use with + * {@code LookAndFeelAddon}s. Any addon that attempts to obtain a property + * defined in the defaults (available from {@code UIManager.get}) to set a + * property that will be added to the defaults for the addon should use the + * "safe" methods. The methods ensure that a valid value is always returned and + * that value is a {@code UIResource}. + *

      + *

      Support for New Types

      + *

      + * {@code UIManagerExt} supports the retrieval of new {@code UIResource} types. + * There is a {@code getXXX} method for every {@code UIResource} subtype in the + * {@code org.jdesktop.swingx.plaf} package. + *

      + *

      Support for Dynamic Localization

      + *

      + * {@code UIManagerExt} enables dynamic localization by supporting + * {@code ResourceBundle}s. The + * {@linkplain UIDefaults#addResourceBundle(String)} allows resource bundles to + * be added to the {@code UIDefaults}. While there is support for this feature + * in core, there is a bug with the class loader that prevents user added + * bundles from working correctly when used via Web Start. Therefore, + * {@code UIManagerExt} defines methods to add and remove resource bundles. + * These are the only methods that SwingX classes should use when adding + * resource bundles to the defaults. Since {@code UIManagerExt} is maintaining + * the bundles, any localized {@code String}s must be retrieved from + * the {@code getString} methods in this class. + *

      + *

      Support for Non-{@code String} Localization Values

      + *

      + * All methods work by first determining if the value is present + * {@code UIDefaults}. If the value is not present, then the installed + * {@code ResourceBundle}s are queried. {@code UIManagerExt} will attempt to + * convert any returned value to the appropriate type. For instance, + * {@code getInt} uses {@code Integer.decode} to convert {@code String}s + * returned from the bundle into {@code int}s. + *

      + * + * @author Karl George Schaefer + * + * @see UIManager + * @see UIDefaults + */ +@SuppressWarnings("nls") +public class UIManagerExt { + /** + * Used to replicate the resource bundle behavior from the + * {@code UIDefaults}. + */ + private static class UIDefaultsExt { + //use vector; we want synchronization + private Vector resourceBundles; + + /** + * Maps from a Locale to a cached Map of the ResourceBundle. This is done + * so as to avoid an exception being thrown when a value is asked for. + * Access to this should be done while holding a lock on the + * UIDefaults, eg synchronized(this). + */ + private Map> resourceCache; + + UIDefaultsExt() { + resourceCache = new HashMap>(); + } + + //should this just return String? + private Object getFromResourceBundle(Object key, Locale l) { + + if( resourceBundles == null || + resourceBundles.isEmpty() || + !(key instanceof String) ) { + return null; + } + + // A null locale means use the default locale. + if( l == null ) { + l = Locale.getDefault(); + } + + synchronized(this) { + return getResourceCache(l).get(key); + } + } + + /** + * Returns a Map of the known resources for the given locale. + */ + private Map getResourceCache(Locale l) { + Map values = resourceCache.get(l); + + if (values == null) { + values = new HashMap(); + for (int i=resourceBundles.size()-1; i >= 0; i--) { + String bundleName = resourceBundles.get(i); + + try { + ResourceBundle b = ResourceBundle. + getBundle(bundleName, l, UIManagerExt.class.getClassLoader()); + Enumeration keys = b.getKeys(); + + while (keys.hasMoreElements()) { + String key = keys.nextElement(); + + if (values.get(key) == null) { + Object value = b.getObject(key); + + values.put(key, (String) value); + } + } + } catch( MissingResourceException mre ) { + // Keep looking + } + } + resourceCache.put(l, values); + } + return values; + } + + public synchronized void addResourceBundle(String bundleName) { + if( bundleName == null ) { + return; + } + if( resourceBundles == null ) { + resourceBundles = new Vector(5); + } + if (!resourceBundles.contains(bundleName)) { + resourceBundles.add( bundleName ); + resourceCache.clear(); + } + } + + public synchronized void removeResourceBundle( String bundleName ) { + if( resourceBundles != null ) { + resourceBundles.remove( bundleName ); + } + resourceCache.clear(); + } + } + + private static UIDefaultsExt uiDefaultsExt = new UIDefaultsExt(); + + private UIManagerExt() { + //does nothing + } + + /** + * Adds a resource bundle to the list of resource bundles that are searched + * for localized values. Resource bundles are searched in the reverse order + * they were added. In other words, the most recently added bundle is + * searched first. + * + * @param bundleName + * the base name of the resource bundle to be added + * @see ResourceBundle + * @see #removeResourceBundle + */ + public static void addResourceBundle(String bundleName) { + uiDefaultsExt.addResourceBundle(bundleName); + } + + /** + * Removes a resource bundle from the list of resource bundles that are + * searched for localized defaults. + * + * @param bundleName + * the base name of the resource bundle to be removed + * @see ResourceBundle + * @see #addResourceBundle + */ + public static void removeResourceBundle(String bundleName) { + uiDefaultsExt.removeResourceBundle(bundleName); + } + + /** + * Returns a string from the defaults. If the value for {@code key} is not a + * {@code String}, {@code null} is returned. + * + * @param key + * an {@code Object} specifying the string + * @return the {@code String} object + * @throws NullPointerException + * if {@code key} is {@code null} + */ + public static String getString(Object key) { + return getString(key, null); + } + + /** + * Returns a string from the defaults. If the value for {@code key} is not a + * {@code String}, {@code null} is returned. + * + * @param key + * an {@code Object} specifying the string + * @param l + * the {@code Locale} for which the string is desired; refer + * to {@code UIDefaults} for details on how a {@code null} + * {@code Locale} is handled + * @return the {@code String} object + * @throws NullPointerException + * if {@code key} is {@code null} + */ + public static String getString(Object key, Locale l) { + Object value = UIManager.get(key, l); + + if (value instanceof String) { + return (String) value; + } + + //only return resource bundle if not in UIDefaults + if (value == null) { + value = uiDefaultsExt.getFromResourceBundle(key, l); + + if (value instanceof String) { + return (String) value; + } + } + + return null; + } + + /** + * Returns an integer from the defaults. If the value for {@code key} is not + * an {@code int}, {@code 0} is returned. + * + * @param key + * an {@code Object} specifying the integer + * @return the {@code int} + * @throws NullPointerException + * if {@code key} is {@code null} + */ + public static int getInt(Object key) { + return getInt(key, null); + } + + /** + * Returns an integer from the defaults. If the value for {@code key} is not + * an {@code int}, {@code 0} is returned. + * + * @param key + * an {@code Object} specifying the integer + * @param l + * the {@code Locale} for which the integer is desired; refer + * to {@code UIDefaults} for details on how a {@code null} + * {@code Locale} is handled + * @return the {@code int} + * @throws NullPointerException + * if {@code key} is {@code null} + */ + public static int getInt(Object key, Locale l) { + Object value = UIManager.get(key, l); + + if (value instanceof Integer) { + return (Integer) value; + } + + if (value == null) { + value = uiDefaultsExt.getFromResourceBundle(key, l); + + if (value instanceof Integer) { + return (Integer) value; + } + + if (value instanceof String) { + try { + return Integer.decode((String) value); + } catch (NumberFormatException e) { + // ignore - the entry was not parseable, can't do anything + // JW: should we log it? + } + } + } + + return 0; + } + + /** + * Returns an Boolean from the defaults. If the value for {@code key} is not + * a {@code boolean}, {@code false} is returned. + * + * @param key + * an {@code Object} specifying the Boolean + * @return the {@code boolean} + * @throws NullPointerException + * if {@code key} is {@code null} + */ + public static boolean getBoolean(Object key) { + return getBoolean(key, null); + } + + /** + * Returns an Boolean from the defaults. If the value for {@code key} is not + * a {@code boolean}, {@code false} is returned. + * + * @param key + * an {@code Object} specifying the Boolean + * @param l + * the {@code Locale} for which the Boolean is desired; refer + * to {@code UIDefaults} for details on how a {@code null} + * {@code Locale} is handled + * @return the {@code boolean} + * @throws NullPointerException + * if {@code key} is {@code null} + */ + public static boolean getBoolean(Object key, Locale l) { + Object value = UIManager.get(key, l); + + if (value instanceof Boolean) { + return (Boolean) value; + } + + //only return resource bundle if not in UIDefaults + if (value == null) { + value = uiDefaultsExt.getFromResourceBundle(key, l); + + if (value instanceof Boolean) { + return (Boolean) value; + } + + if (value instanceof String) { + return Boolean.valueOf((String) value); + } + } + + return false; + } + + /** + * Returns a color from the defaults. If the value for {@code key} is not + * a {@code Color}, {@code null} is returned. + * + * @param key + * an {@code Object} specifying the color + * @return the {@code Color} object + * @throws NullPointerException + * if {@code key} is {@code null} + */ + public static Color getColor(Object key) { + return getColor(key, null); + } + + /** + * Returns a color from the defaults. If the value for {@code key} is not + * a {@code Color}, {@code null} is returned. + * + * @param key + * an {@code Object} specifying the color + * @param l + * the {@code Locale} for which the color is desired; refer + * to {@code UIDefaults} for details on how a {@code null} + * {@code Locale} is handled + * @return the {@code Color} object + * @throws NullPointerException + * if {@code key} is {@code null} + */ + public static Color getColor(Object key, Locale l) { + Object value = UIManager.get(key, l); + + if (value instanceof Color) { + return (Color) value; + } + + //only return resource bundle if not in UIDefaults + if (value == null) { + value = uiDefaultsExt.getFromResourceBundle(key, l); + + if (value instanceof Color) { + return (Color) value; + } + + if (value instanceof String) { + try { + return Color.decode((String) value); + } catch (NumberFormatException e) { + // incorrect format; does nothing + } + } + } + + return null; + } + + //TODO: Font.decode always returns a valid font. This is not acceptable for UIManager +// /** +// * Returns a font from the defaults. If the value for {@code key} is not +// * a {@code Font}, {@code null} is returned. +// * +// * @param key +// * an {@code Object} specifying the font +// * @return the {@code Font} object +// * @throws NullPointerException +// * if {@code key} is {@code null} +// */ +// public static Font getFont(Object key) { +// return getFont(key, null); +// } +// +// /** +// * Returns a font from the defaults. If the value for {@code key} is not +// * a {@code Font}, {@code null} is returned. +// * +// * @param key +// * an {@code Object} specifying the font +// * @param l +// * the {@code Locale} for which the font is desired; refer +// * to {@code UIDefaults} for details on how a {@code null} +// * {@code Locale} is handled +// * @return the {@code Font} object +// * @throws NullPointerException +// * if {@code key} is {@code null} +// */ +// public static Font getFont(Object key, Locale l) { +// Object value = UIManager.get(key, l); +// +// if (value instanceof Font) { +// return (Font) value; +// } +// +// //only return resource bundle if not in UIDefaults +// if (value == null) { +// value = uiDefaultsExt.getFromResourceBundle(key, l); +// +// if (value instanceof Font) { +// return (Font) value; +// } +// +// if (value instanceof String) { +// return Font.decode((String) value); +// } +// } +// +// return null; +// } + + /** + * Returns a shape from the defaults. If the value for {@code key} is not a + * {@code Shape}, {@code null} is returned. + * + * @param key an {@code Object} specifying the shape + * @return the {@code Shape} object + * @throws NullPointerException if {@code key} is {@code null} + */ + public static Shape getShape(Object key) { + Object value = UIManager.getDefaults().get(key); + return (value instanceof Shape) ? (Shape) value : null; + } + + /** + * Returns a shape from the defaults that is appropriate for the given + * locale. If the value for {@code key} is not a {@code Shape}, + * {@code null} is returned. + * + * @param key + * an {@code Object} specifying the shape + * @param l + * the {@code Locale} for which the shape is desired; refer + * to {@code UIDefaults} for details on how a {@code null} + * {@code Locale} is handled + * @return the {@code Shape} object + * @throws NullPointerException + * if {@code key} is {@code null} + */ + public static Shape getShape(Object key, Locale l) { + Object value = UIManager.getDefaults().get(key, l); + return (value instanceof Shape) ? (Shape) value : null; + } + + /** + * Returns a painter from the defaults. If the value for {@code key} is not + * a {@code Painter}, {@code null} is returned. + * + * @param key + * an {@code Object} specifying the painter + * @return the {@code Painter} object + * @throws NullPointerException + * if {@code key} is {@code null} + */ + public static Painter getPainter(Object key) { + Object value = UIManager.getDefaults().get(key); + return (value instanceof Painter) ? (Painter) value : null; + } + + /** + * Returns a painter from the defaults that is appropriate for the given + * locale. If the value for {@code key} is not a {@code Painter}, + * {@code null} is returned. + * + * @param key + * an {@code Object} specifying the painter + * @param l + * the {@code Locale} for which the painter is desired; refer + * to {@code UIDefaults} for details on how a {@code null} + * {@code Locale} is handled + * @return the {@code Painter} object + * @throws NullPointerException + * if {@code key} is {@code null} + */ + public static Painter getPainter(Object key, Locale l) { + Object value = UIManager.getDefaults().get(key, l); + return (value instanceof Painter) ? (Painter) value : null; + } + + /** + * Returns a border from the defaults. If the value for {@code key} is not a + * {@code Border}, {@code defaultBorder} is returned. + * + * @param key + * an {@code Object} specifying the border + * @param defaultBorder + * the border to return if the border specified by + * {@code key} does not exist + * @return the {@code Border} object + * @throws NullPointerException + * if {@code key} or {@code defaultBorder} is {@code null} + */ + public static Border getSafeBorder(Object key, Border defaultBorder) { + Contract.asNotNull(defaultBorder, "defaultBorder cannot be null"); + + Border safeBorder = UIManager.getBorder(key); + + if (safeBorder == null) { + safeBorder = defaultBorder; + } + + if (!(safeBorder instanceof UIResource)) { + safeBorder = new BorderUIResource(safeBorder); + } + + return safeBorder; + } + + /** + * Returns a color from the defaults. If the value for {@code key} is not a + * {@code Color}, {@code defaultColor} is returned. + * + * @param key + * an {@code Object} specifying the color + * @param defaultColor + * the color to return if the color specified by {@code key} + * does not exist + * @return the {@code Color} object + * @throws NullPointerException + * if {@code key} or {@code defaultColor} is {@code null} + */ + public static Color getSafeColor(Object key, Color defaultColor) { + Contract.asNotNull(defaultColor, "defaultColor cannot be null"); + + Color safeColor = UIManager.getColor(key); + + if (safeColor == null) { + safeColor = defaultColor; + } + + if (!(safeColor instanceof UIResource)) { + safeColor = new ColorUIResource(safeColor); + } + + return safeColor; + } + + /** + * Returns a dimension from the defaults. If the value for {@code key} is + * not a {@code Dimension}, {@code defaultDimension} is returned. + * + * @param key + * an {@code Object} specifying the dimension + * @param defaultDimension + * the dimension to return if the dimension specified by + * {@code key} does not exist + * @return the {@code Dimension} object + * @throws NullPointerException + * if {@code key} or {@code defaultColor} is {@code null} + */ + public static Dimension getSafeDimension(Object key, Dimension defaultDimension) { + Contract.asNotNull(defaultDimension, "defaultDimension cannot be null"); + + Dimension safeDimension = UIManager.getDimension(key); + + if (safeDimension == null) { + safeDimension = defaultDimension; + } + + if (!(safeDimension instanceof UIResource)) { + safeDimension = new DimensionUIResource(safeDimension.width, safeDimension.height); + } + + return safeDimension; + } + + /** + * Returns a font from the defaults. If the value for {@code key} is not a + * {@code Font}, {@code defaultFont} is returned. + * + * @param key + * an {@code Object} specifying the font + * @param defaultFont + * the font to return if the font specified by {@code key} + * does not exist + * @return the {@code Font} object + * @throws NullPointerException + * if {@code key} or {@code defaultFont} is {@code null} + */ + public static Font getSafeFont(Object key, Font defaultFont) { + Contract.asNotNull(defaultFont, "defaultFont cannot be null"); + + Font safeFont = UIManager.getFont(key); + + if (safeFont == null) { + safeFont = defaultFont; + } + + if (!(safeFont instanceof UIResource)) { + safeFont = new FontUIResource(safeFont); + } + + return safeFont; + } + + /** + * Returns an icon from the defaults. If the value for {@code key} is not a + * {@code Icon}, {@code defaultIcon} is returned. + * + * @param key + * an {@code Object} specifying the icon + * @param defaultIcon + * the icon to return if the icon specified by {@code key} + * does not exist + * @return the {@code Icon} object + * @throws NullPointerException + * if {@code key} or {@code defaultIcon} is {@code null} + */ + public static Icon getSafeIcon(Object key, Icon defaultIcon) { + Contract.asNotNull(defaultIcon, "defaultIcon cannot be null"); + + Icon safeIcon = UIManager.getIcon(key); + + if (safeIcon == null) { + safeIcon = defaultIcon; + } + + if (!(safeIcon instanceof UIResource)) { + safeIcon = new IconUIResource(safeIcon); + } + + return safeIcon; + } + + /** + * Returns an insets from the defaults. If the value for {@code key} is not + * a {@code Insets}, {@code defaultInsets} is returned. + * + * @param key + * an {@code Object} specifying the insets + * @param defaultInsets + * the insets to return if the insets specified by + * {@code key} does not exist + * @return the {@code Insets} object + * @throws NullPointerException + * if {@code key} or {@code defaultInsets} is {@code null} + */ + public static Insets getSafeInsets(Object key, Insets defaultInsets) { + Contract.asNotNull(defaultInsets, "defaultInsets cannot be null"); + + Insets safeInsets = UIManager.getInsets(key); + + if (safeInsets == null) { + safeInsets = defaultInsets; + } + + if (!(safeInsets instanceof UIResource)) { + safeInsets = new InsetsUIResource(safeInsets.top, safeInsets.left, + safeInsets.bottom, safeInsets.right); + } + + return safeInsets; + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/XListAddon.java b/src/main/java/org/jdesktop/swingx/plaf/XListAddon.java new file mode 100644 index 0000000000..8096cc6f80 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/XListAddon.java @@ -0,0 +1,100 @@ +/* + * $Id: BusyLabelAddon.java 2565 2008-01-03 19:08:32Z rah003 $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf; + +import org.jdesktop.swingx.JXList; + +import javax.swing.*; +import javax.swing.border.AbstractBorder; +import javax.swing.border.Border; +import javax.swing.plaf.UIResource; + +/** + * Addon for JXList. + *

      + * + * Install a custom ui to support sorting/filtering in JXList. + */ +public class XListAddon extends AbstractComponentAddon { + + public XListAddon() { + super("JXList"); + } + + @Override + protected void addBasicDefaults(LookAndFeelAddons addon, + DefaultsList defaults) { + defaults.add(JXList.uiClassID, + "org.jdesktop.swingx.plaf.basic.core.BasicXListUI"); + if (isGTK()) { + replaceListTableBorders(addon, defaults); + } + } + + @Override + protected void addNimbusDefaults(LookAndFeelAddons addon, + DefaultsList defaults) { + defaults.add(JXList.uiClassID, + "org.jdesktop.swingx.plaf.synth.SynthXListUI"); + } + + + private void replaceListTableBorders(LookAndFeelAddons addon, + DefaultsList defaults) { + replaceBorder(defaults, "List.", "focusCellHighlightBorder"); + replaceBorder(defaults, "List.", "focusSelectedCellHighlightBorder"); + replaceBorder(defaults, "List.", "noFocusBorder"); + } + + + + /** + * @param defaults + * @param componentPrefix + * @param borderKey + */ + private void replaceBorder(DefaultsList defaults, String componentPrefix, + String borderKey) { + String key = componentPrefix + borderKey; + Border border = UIManager.getBorder(componentPrefix + borderKey); + if (border instanceof AbstractBorder && border instanceof UIResource + && border.getClass().getName().contains("ListTable")) { + border = new SafeBorder((AbstractBorder) border); + // PENDING JW: this is fishy ... adding to lookAndFeelDefaults is taken + UIManager.getLookAndFeelDefaults().put(key, border); + // adding to defaults is not +// defaults.add(key, border); + + } + } + + + + /** + * + * @return true if the LF is GTK. + */ + private boolean isGTK() { + return "GTK".equals(UIManager.getLookAndFeel().getID()); + } + + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicBusyLabelUI.java b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicBusyLabelUI.java new file mode 100644 index 0000000000..4f7d0f11b0 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicBusyLabelUI.java @@ -0,0 +1,68 @@ +/* + * $Id: BasicBusyLabelUI.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf.basic; + +import org.jdesktop.swingx.JXBusyLabel; +import org.jdesktop.swingx.painter.BusyPainter; +import org.jdesktop.swingx.plaf.BusyLabelUI; +import org.jdesktop.swingx.plaf.UIManagerExt; + +import javax.swing.*; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicLabelUI; +import java.awt.*; + +/** + * Base implementation of the JXBusyLabel UI. + * + * @author rah003 + */ +public class BasicBusyLabelUI extends BasicLabelUI implements BusyLabelUI { + + /** Creates a new instance of BasicBusyLabelUI */ + public BasicBusyLabelUI(JXBusyLabel lbl) { + } + + public static ComponentUI createUI(JComponent c) { + return new BasicBusyLabelUI((JXBusyLabel)c); + } + + @Override + public BusyPainter getBusyPainter(final Dimension dim) { + BusyPainter p = new BusyPainter() { + @Override + protected void init(Shape point, Shape trajectory, Color b, Color h) { + super.init(dim == null ? UIManagerExt.getShape("JXBusyLabel.pointShape") : getScaledDefaultPoint(dim.height), + dim == null ? UIManagerExt.getShape("JXBusyLabel.trajectoryShape") : getScaledDefaultTrajectory(dim.height), + UIManagerExt.getSafeColor("JXBusyLabel.baseColor", Color.LIGHT_GRAY), + UIManagerExt.getSafeColor("JXBusyLabel.highlightColor", Color.BLACK)); + } + }; + return p; + } + + @Override + public int getDelay() { + return UIManager.getInt("JXBusyLabel.delay"); + } + + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicCalendarHeaderHandler.java b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicCalendarHeaderHandler.java new file mode 100644 index 0000000000..a3754c505b --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicCalendarHeaderHandler.java @@ -0,0 +1,262 @@ +/* + * $Id: BasicCalendarHeaderHandler.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.plaf.basic; + +import org.jdesktop.swingx.JXHyperlink; +import org.jdesktop.swingx.JXMonthView; +import org.jdesktop.swingx.JXPanel; +import org.jdesktop.swingx.hyperlink.AbstractHyperlinkAction; +import org.jdesktop.swingx.renderer.StringValue; +import org.jdesktop.swingx.renderer.StringValues; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.text.DateFormatSymbols; +import java.util.Calendar; +import java.util.Locale; + + +/** + * Custom implementation of a CalendarHeaderHandler in preparation of a vista-style + * calendar. Does nothing yet. + * + * @author Jeanette Winzenburg + */ +public class BasicCalendarHeaderHandler extends CalendarHeaderHandler { + + + @Override + public void install(JXMonthView monthView) { + super.install(monthView); + getHeaderComponent().setActions(monthView.getActionMap().get("previousMonth"), + monthView.getActionMap().get("nextMonth"), + monthView.getActionMap().get("zoomOut")); + + } + + + + @Override + protected void installNavigationActions() { + // TODO Auto-generated method stub + super.installNavigationActions(); + ZoomOutAction zoomOutAction = new ZoomOutAction(); + zoomOutAction.setTarget(monthView); + monthView.getActionMap().put("zoomOut", zoomOutAction); + } + + + + @Override + public void uninstall(JXMonthView monthView) { + getHeaderComponent().setActions(null, null, null); + super.uninstall(monthView); + } + + + @Override + public BasicCalendarHeader getHeaderComponent() { + // TODO Auto-generated method stub + return (BasicCalendarHeader) super.getHeaderComponent(); + } + + @Override + protected BasicCalendarHeader createCalendarHeader() { + return new BasicCalendarHeader(); + } + + /** + * Quick fix for Issue #1046-swingx: header text not updated if zoomable. + * + */ + protected static class ZoomOutAction extends AbstractHyperlinkAction { + + private PropertyChangeListener linkListener; + // Formatters/state used by Providers. + /** Localized month strings used in title. */ + private String[] monthNames; + private StringValue tsv ; + + public ZoomOutAction() { + super(); + tsv = new StringValue() { + + @Override + public String getString(Object value) { + if (value instanceof Calendar) { + String month = monthNames[((Calendar) value) + .get(Calendar.MONTH)]; + return month + " " + + ((Calendar) value).get(Calendar.YEAR); + } + return StringValues.TO_STRING.getString(value); + } + + }; + } + + @Override + public void actionPerformed(ActionEvent e) { + // TODO Auto-generated method stub + + } + + + /** + * installs a propertyChangeListener on the target and + * updates the visual properties from the target. + */ + @Override + protected void installTarget() { + if (getTarget() != null) { + getTarget().addPropertyChangeListener(getTargetListener()); + } + updateLocale(); + updateFromTarget(); + } + + /** + * + */ + private void updateLocale() { + Locale current = getTarget() != null ? getTarget().getLocale() : Locale.getDefault(); + monthNames = DateFormatSymbols.getInstance(current).getMonths(); + } + + /** + * removes the propertyChangeListener.

      + * + * Implementation NOTE: this does not clean-up internal state! There is + * no need to because updateFromTarget handles both null and not-null + * targets. Hmm... + * + */ + @Override + protected void uninstallTarget() { + if (getTarget() == null) return; + getTarget().removePropertyChangeListener(getTargetListener()); + } + + protected void updateFromTarget() { + // this happens on construction with null target + if (tsv == null) return; + Calendar calendar = getTarget() != null ? getTarget().getCalendar() : null; + setName(tsv.getString(calendar)); + } + + private PropertyChangeListener getTargetListener() { + if (linkListener == null) { + linkListener = new PropertyChangeListener() { + + @Override + public void propertyChange(PropertyChangeEvent evt) { + if ("firstDisplayedDay".equals(evt.getPropertyName())) { + updateFromTarget(); + } else if ("locale".equals(evt.getPropertyName())) { + updateLocale(); + updateFromTarget(); + } + } + + }; + } + return linkListener; + } + + + } + + + /** + * Active header for a JXMonthView in zoomable mode.

      + * + * PENDING JW: very much work-in-progress. + */ + static class BasicCalendarHeader extends JXPanel { + + protected AbstractButton prevButton; + protected AbstractButton nextButton; + protected JXHyperlink zoomOutLink; + + public BasicCalendarHeader() { + setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS)); + prevButton = createNavigationButton(); + nextButton = createNavigationButton(); + zoomOutLink = createZoomLink(); + add(prevButton); + add(Box.createHorizontalGlue()); + add(zoomOutLink); + add(Box.createHorizontalGlue()); + add(nextButton); + setBorder(BorderFactory.createEmptyBorder(2, 4, 2, 4)); + } + + /** + * Sets the actions for backward, forward and zoom out navigation. + * + * @param prev + * @param next + * @param zoomOut + */ + public void setActions(Action prev, Action next, Action zoomOut) { + prevButton.setAction(prev); + nextButton.setAction(next); + zoomOutLink.setAction(zoomOut); + } + + + /** + * {@inheritDoc}

      + * + * Overridden to set the font of the zoom hyperlink. + */ + @Override + public void setFont(Font font) { + super.setFont(font); + if (zoomOutLink != null) + zoomOutLink.setFont(font); + } + + private JXHyperlink createZoomLink() { + JXHyperlink zoomOutLink = new JXHyperlink(); + Color textColor = new Color(16, 66, 104); + zoomOutLink.setUnclickedColor(textColor); + zoomOutLink.setClickedColor(textColor); + zoomOutLink.setFocusable(false); + return zoomOutLink; + } + + private AbstractButton createNavigationButton() { + JXHyperlink b = new JXHyperlink(); + b.setContentAreaFilled(false); + b.setBorder(BorderFactory.createEmptyBorder()); + b.setRolloverEnabled(true); + b.setFocusable(false); + return b; + } + } + +} + diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicCalendarRenderingHandler.java b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicCalendarRenderingHandler.java new file mode 100644 index 0000000000..b5acf10d03 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicCalendarRenderingHandler.java @@ -0,0 +1,346 @@ +package org.jdesktop.swingx.plaf.basic; + +import org.jdesktop.swingx.JXMonthView; +import org.jdesktop.swingx.decorator.*; +import org.jdesktop.swingx.plaf.UIManagerExt; +import org.jdesktop.swingx.renderer.*; + +import javax.swing.*; +import java.awt.*; +import java.text.DateFormat; +import java.text.DateFormatSymbols; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +/** + * The RenderingHandler responsible for text rendering. It provides + * and configures a rendering component for the given cell of + * a JXMonthView.

      + * + * Note: exposing the createXXStringValue methods is an emergency workaround for + * Issue #1062-swingx (core doesn't use arabic digits where appropriate) to allow + * subclasses to do better than core. So beware of future changes! + * + */ +class BasicCalendarRenderingHandler implements CalendarRenderingHandler { + /** The CellContext for content and default visual config. */ + private CalendarCellContext cellContext; + /** The providers to use per DayState. */ + private Map> providers; + //-------- Highlight properties + /** The Painter used for highlighting unselectable dates. */ + private TextCrossingPainter textCross; + /** The foreground color for unselectable date highlight. */ + private Color unselectableDayForeground; + + /** + * Instantiates a RenderingHandler and installs default state. + */ + public BasicCalendarRenderingHandler() { + install(); + } + + private void install() { + unselectableDayForeground = UIManagerExt.getColor("JXMonthView.unselectableDayForeground"); + textCross = new TextCrossingPainter(); + cellContext = new CalendarCellContext(); + installProviders(); + } + + /** + * Creates and stores ComponentProviders for all DayStates. + */ + private void installProviders() { + providers = new HashMap>(); + + StringValue sv = createDayStringValue(null); + ComponentProvider provider = new LabelProvider(sv, JLabel.RIGHT); + providers.put(CalendarState.IN_MONTH, provider); + providers.put(CalendarState.TODAY, provider); + providers.put(CalendarState.TRAILING, provider); + providers.put(CalendarState.LEADING, provider); + + StringValue wsv = createWeekOfYearStringValue(null); + ComponentProvider weekOfYearProvider = new LabelProvider(wsv, + JLabel.RIGHT); + providers.put(CalendarState.WEEK_OF_YEAR, weekOfYearProvider); + + ComponentProvider dayOfWeekProvider = new LabelProvider(JLabel.CENTER) { + + @Override + protected String getValueAsString(CellContext context) { + Object value = context.getValue(); + // PENDING JW: this is breaking provider's contract in its + // role as StringValue! Don't in the general case. + if (value instanceof Calendar) { + int day = ((Calendar) value).get(Calendar.DAY_OF_WEEK); + return ((JXMonthView) context.getComponent()).getDayOfTheWeek(day); + } + return super.getValueAsString(context); + } + + }; + providers.put(CalendarState.DAY_OF_WEEK, dayOfWeekProvider); + + StringValue tsv = createMonthHeaderStringValue(null); + ComponentProvider titleProvider = new LabelProvider(tsv, + JLabel.CENTER); + providers.put(CalendarState.TITLE, titleProvider); + } + + /** + * Creates and returns a StringValue used for rendering the title of a month box. + * The input they are assumed to handle is a Calendar configured to a day of + * the month to render. + * + * @param locale the Locale to use, might be null to indicate usage of the default + * Locale + * @return a StringValue appropriate for rendering month title. + */ + protected StringValue createMonthHeaderStringValue(Locale locale) { + if (locale == null) { + locale = Locale.getDefault(); + } + final String[] monthNames = DateFormatSymbols.getInstance(locale).getMonths(); + StringValue tsv = new StringValue() { + + @Override + public String getString(Object value) { + if (value instanceof Calendar) { + String month = monthNames[((Calendar) value) + .get(Calendar.MONTH)]; + return month + " " + + ((Calendar) value).get(Calendar.YEAR); + } + return StringValues.TO_STRING.getString(value); + } + + }; + return tsv; + } + + /** + * Creates and returns a StringValue used for rendering the week of year. + * The input they are assumed to handle is a Calendar configured to a day of + * the week to render. + * + * @param locale the Locale to use, might be null to indicate usage of the default + * Locale + * @return a StringValue appropriate for rendering week of year. + */ + protected StringValue createWeekOfYearStringValue(Locale locale) { + StringValue wsv = new StringValue() { + + @Override + public String getString(Object value) { + if (value instanceof Calendar) { + value = ((Calendar) value).get(Calendar.WEEK_OF_YEAR); + } + return StringValues.TO_STRING.getString(value); + } + + }; + return wsv; + } + + /** + * Creates and returns a StringValue used for rendering days in a month. + * The input they are assumed to handle is a Calendar configured to the day. + * + * @param locale the Locale to use, might be null to indicate usage of the default + * Locale + * @return a StringValue appropriate for rendering days in a month + */ + protected StringValue createDayStringValue(Locale locale) { + if (locale == null) { + locale = Locale.getDefault(); + } + FormatStringValue sv = new FormatStringValue(new SimpleDateFormat("d", locale)) { + + @Override + public String getString(Object value) { + if (value instanceof Calendar) { + ((DateFormat) getFormat()).setTimeZone(((Calendar) value).getTimeZone()); + value = ((Calendar) value).getTime(); + } + return super.getString(value); + } + + }; + return sv; + } + + + /** + * Updates internal state to the given Locale. + * + * @param locale the new Locale. + */ + @Override + public void setLocale(Locale locale) { + StringValue dayValue = createDayStringValue(locale); + providers.get(CalendarState.IN_MONTH).setStringValue(dayValue); + providers.get(CalendarState.TODAY).setStringValue(dayValue); + providers.get(CalendarState.TRAILING).setStringValue(dayValue); + providers.get(CalendarState.LEADING).setStringValue(dayValue); + + providers.get(CalendarState.WEEK_OF_YEAR).setStringValue(createWeekOfYearStringValue(locale)); + providers.get(CalendarState.TITLE).setStringValue(createMonthHeaderStringValue(locale)); + } + + /** + * Configures and returns a component for rendering of the given monthView cell. + * + * @param monthView the JXMonthView to render onto + * @param calendar the cell value + * @param dayState the DayState of the cell + * @return a component configured for rendering the given cell + */ + @Override + public JComponent prepareRenderingComponent(JXMonthView monthView, Calendar calendar, CalendarState dayState) { + cellContext.installContext(monthView, calendar, + isSelected(monthView, calendar, dayState), + isFocused(monthView, calendar, dayState), + dayState); + JComponent comp = providers.get(dayState).getRendererComponent(cellContext); + return highlight(comp, monthView, calendar, dayState); + } + + + /** + * + * NOTE: it's the responsibility of the CalendarCellContext to detangle + * all "default" (that is: which could be queried from the comp and/or UIManager) + * foreground/background colors based on the given state! Moved out off here. + *

      + * PENDING JW: replace hard-coded logic by giving over to highlighters. + * + * @param monthView the JXMonthView to render onto + * @param calendar the cell value + * @param dayState the DayState of the cell + * @param dayState + */ + private JComponent highlight(JComponent comp, JXMonthView monthView, + Calendar calendar, CalendarState dayState) { + CalendarAdapter adapter = getCalendarAdapter(monthView, calendar, dayState); + return (JComponent) getHighlighter().highlight(comp, adapter); + } + + /** + * @return + */ + private Highlighter getHighlighter() { + if (highlighter == null) { + highlighter = new CompoundHighlighter(); + installHighlighters(); + } + return highlighter; + } + + /** + * + */ + private void installHighlighters() { + HighlightPredicate boldPredicate = new HighlightPredicate() { + + @Override + public boolean isHighlighted(Component renderer, + ComponentAdapter adapter) { + if (!(adapter instanceof CalendarAdapter)) + return false; + CalendarAdapter ca = (CalendarAdapter) adapter; + return CalendarState.DAY_OF_WEEK == ca.getCalendarState() || + CalendarState.TITLE == ca.getCalendarState(); + } + + }; + Highlighter font = new AbstractHighlighter(boldPredicate) { + + @Override + protected Component doHighlight(Component component, + ComponentAdapter adapter) { + component.setFont(getDerivedFont(component.getFont())); + return component; + } + + }; + highlighter.addHighlighter(font); + + HighlightPredicate unselectable = new HighlightPredicate() { + + @Override + public boolean isHighlighted(Component renderer, + ComponentAdapter adapter) { + if (!(adapter instanceof CalendarAdapter)) + return false; + return ((CalendarAdapter) adapter).isUnselectable(); + } + + }; + textCross.setForeground(unselectableDayForeground); + Highlighter painterHL = new PainterHighlighter(unselectable, textCross); + highlighter.addHighlighter(painterHL); + + } + + /** + * @param monthView + * @param calendar + * @param dayState + * @return + */ + private CalendarAdapter getCalendarAdapter(JXMonthView monthView, + Calendar calendar, CalendarState dayState) { + if (calendarAdapter == null) { + calendarAdapter = new CalendarAdapter(monthView); + } + return calendarAdapter.install(calendar, dayState); + } + + private CalendarAdapter calendarAdapter; + private CompoundHighlighter highlighter; + + /** + * @param font + * @return + */ + private Font getDerivedFont(Font font) { + return font.deriveFont(Font.BOLD); + } + + /** + * @param monthView + * @param calendar + * @param dayState + * @return + */ + private boolean isFocused(JXMonthView monthView, Calendar calendar, + CalendarState dayState) { + return false; + } + + /** + * @param monthView the JXMonthView to render onto + * @param calendar the cell value + * @param dayState the DayState of the cell + * @return + */ + private boolean isSelected(JXMonthView monthView, Calendar calendar, + CalendarState dayState) { + if (!isSelectable(dayState)) return false; + return monthView.isSelected(calendar.getTime()); + } + + + /** + * @param dayState + * @return + */ + private boolean isSelectable(CalendarState dayState) { + return (CalendarState.IN_MONTH == dayState) || (CalendarState.TODAY == dayState); + } + +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicDatePickerUI.java b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicDatePickerUI.java new file mode 100644 index 0000000000..c1b5c5d157 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicDatePickerUI.java @@ -0,0 +1,1670 @@ +/* + * $Id: BasicDatePickerUI.java 4107 2012-01-19 14:24:15Z kleopatra $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf.basic; + +import org.jdesktop.swingx.JXDatePicker; +import org.jdesktop.swingx.JXMonthView; +import org.jdesktop.swingx.SwingXUtilities; +import org.jdesktop.swingx.calendar.CalendarUtils; +import org.jdesktop.swingx.calendar.DatePickerFormatter; +import org.jdesktop.swingx.calendar.DatePickerFormatter.DatePickerFormatterUIResource; +import org.jdesktop.swingx.calendar.DateSelectionModel; +import org.jdesktop.swingx.event.DateSelectionEvent; +import org.jdesktop.swingx.event.DateSelectionEvent.EventType; +import org.jdesktop.swingx.event.DateSelectionListener; +import org.jdesktop.swingx.plaf.DatePickerUI; + +import javax.swing.*; +import javax.swing.JFormattedTextField.AbstractFormatter; +import javax.swing.JFormattedTextField.AbstractFormatterFactory; +import javax.swing.border.Border; +import javax.swing.event.PopupMenuEvent; +import javax.swing.event.PopupMenuListener; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.UIResource; +import javax.swing.text.DefaultFormatterFactory; +import javax.swing.text.View; +import java.awt.*; +import java.awt.event.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyVetoException; +import java.text.DateFormat; +import java.text.ParseException; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; +import java.util.TimeZone; +import java.util.logging.Logger; + +/** + * The basic implementation of a DatePickerUI. + *

      + * + * + * @author Joshua Outwater + * @author Jeanette Winzenburg + */ +public class BasicDatePickerUI extends DatePickerUI { + + @SuppressWarnings("all") + private static final Logger LOG = Logger.getLogger(BasicDatePickerUI.class + .getName()); + + protected JXDatePicker datePicker; + private JButton popupButton; + private BasicDatePickerPopup popup; + private Handler handler; + /* + * shared listeners + */ + protected PropertyChangeListener propertyChangeListener; + private FocusListener focusListener; + + /* + * listener's for the arrow button + */ + protected MouseListener mouseListener; + protected MouseMotionListener mouseMotionListener; + + /* + * listeners for the picker's editor + */ + private ActionListener editorActionListener; + private EditorCancelAction editorCancelAction; + private PropertyChangeListener editorPropertyListener; + + /** + * listeners for the picker's monthview + */ + private DateSelectionListener monthViewSelectionListener; + private ActionListener monthViewActionListener; + private PropertyChangeListener monthViewPropertyListener; + + private PopupRemover popupRemover; + + private PopupMenuListener popupMenuListener; + + + @SuppressWarnings({"UnusedDeclaration"}) + public static ComponentUI createUI(JComponent c) { + return new BasicDatePickerUI(); + } + + @Override + public void installUI(JComponent c) { + datePicker = (JXDatePicker)c; + datePicker.setLayout(createLayoutManager()); + installComponents(); + installDefaults(); + installKeyboardActions(); + installListeners(); + } + + @Override + public void uninstallUI(JComponent c) { + uninstallListeners(); + uninstallKeyboardActions(); + uninstallDefaults(); + uninstallComponents(); + datePicker.setLayout(null); + datePicker = null; + } + + protected void installComponents() { + + JFormattedTextField editor = datePicker.getEditor(); + if (SwingXUtilities.isUIInstallable(editor)) { + DateFormat[] formats = getCustomFormats(editor); + // we are not yet listening ... + datePicker.setEditor(createEditor()); + if (formats != null) { + datePicker.setFormats(formats); + } + } + updateFromEditorChanged(null, false); + + popupButton = createPopupButton(); + if (popupButton != null) { + // this is a trick to get hold of the client prop which + // prevents closing of the popup + JComboBox box = new JComboBox(); + Object preventHide = box.getClientProperty("doNotCancelPopup"); + popupButton.putClientProperty("doNotCancelPopup", preventHide); + datePicker.add(popupButton); + popupButton.setEnabled(datePicker.isEnabled()); + popupButton.setInheritsPopupMenu(true); + } + updateChildLocale(datePicker.getLocale()); + + } + + /** + * Checks and returns custom formats on the editor, if any. + * + * @param editor the editor to check + * @return the custom formats uses in the editor or null if it had + * used defaults as defined in the datepicker properties + */ + private DateFormat[] getCustomFormats(JFormattedTextField editor) { + DateFormat[] formats = null; + if (editor != null) { + AbstractFormatterFactory factory = editor.getFormatterFactory(); + if (factory != null) { + AbstractFormatter formatter = factory.getFormatter(editor); + // fix for #1144: classCastException for custom formatters + // PENDING JW: revisit for #1138 + if ((formatter instanceof DatePickerFormatter) && !(formatter instanceof UIResource)) { +// if (!(formatter instanceof DatePickerFormatterUIResource)) { + formats = ((DatePickerFormatter) formatter).getFormats(); + } + } + + } + return formats; + } + + protected void uninstallComponents() { + JFormattedTextField editor = datePicker.getEditor(); + if (editor != null) { + datePicker.remove(editor); + } + + if (popupButton != null) { + datePicker.remove(popupButton); + popupButton = null; + } + } + + /** + * Installs DatePicker default properties. + */ + protected void installDefaults() { + // PENDING JW: currently this is for testing only. + boolean zoomable = Boolean.TRUE.equals(UIManager.get("JXDatePicker.forceZoomable")); + if (zoomable) { + datePicker.getMonthView().setZoomable(true); + } + } + + protected void uninstallDefaults() { + + } + + protected void installKeyboardActions() { + // install picker's actions + ActionMap pickerMap = datePicker.getActionMap(); + pickerMap.put(JXDatePicker.CANCEL_KEY, createCancelAction()); + pickerMap.put(JXDatePicker.COMMIT_KEY, createCommitAction()); + pickerMap.put(JXDatePicker.HOME_NAVIGATE_KEY, createHomeAction(false)); + pickerMap.put(JXDatePicker.HOME_COMMIT_KEY, createHomeAction(true)); + TogglePopupAction popupAction = createTogglePopupAction(); + pickerMap.put("TOGGLE_POPUP", popupAction); + + InputMap pickerInputMap = datePicker.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); + pickerInputMap.put(KeyStroke.getKeyStroke("ENTER"), JXDatePicker.COMMIT_KEY); + pickerInputMap.put(KeyStroke.getKeyStroke("ESCAPE"), JXDatePicker.CANCEL_KEY); + // PENDING: get from LF + pickerInputMap.put(KeyStroke.getKeyStroke("F5"), JXDatePicker.HOME_COMMIT_KEY); + pickerInputMap.put(KeyStroke.getKeyStroke("shift F5"), JXDatePicker.HOME_NAVIGATE_KEY); + pickerInputMap.put(KeyStroke.getKeyStroke("alt DOWN"), "TOGGLE_POPUP"); + + installLinkPanelKeyboardActions(); + } + + protected void uninstallKeyboardActions() { + uninstallLinkPanelKeyboardActions(datePicker.getLinkPanel()); + } + + + /** + * Installs actions and key bindings on the datePicker's linkPanel. Does + * nothing if the linkPanel is null. + * + * PRE: keybindings installed on picker. + */ + protected void installLinkPanelKeyboardActions() { + if (datePicker.getLinkPanel() == null) + return; + ActionMap map = datePicker.getLinkPanel().getActionMap(); + map.put(JXDatePicker.HOME_COMMIT_KEY, datePicker.getActionMap().get( + JXDatePicker.HOME_COMMIT_KEY)); + map.put(JXDatePicker.HOME_NAVIGATE_KEY, datePicker.getActionMap().get( + JXDatePicker.HOME_NAVIGATE_KEY)); + InputMap inputMap = datePicker.getLinkPanel().getInputMap( + JComponent.WHEN_IN_FOCUSED_WINDOW); + // PENDING: get from LF + inputMap.put(KeyStroke.getKeyStroke("F5"), + JXDatePicker.HOME_COMMIT_KEY); + inputMap.put(KeyStroke.getKeyStroke("shift F5"), + JXDatePicker.HOME_NAVIGATE_KEY); + } + + + /** + * Uninstalls actions and key bindings from linkPanel. Does nothing if the + * linkPanel is null. + * + * @param panel the component to uninstall + * + */ + protected void uninstallLinkPanelKeyboardActions(JComponent panel) { + if (panel == null) return; + ActionMap map = panel.getActionMap(); + map.remove(JXDatePicker.HOME_COMMIT_KEY); + map.remove(JXDatePicker.HOME_NAVIGATE_KEY); + InputMap inputMap = panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); + // PENDING: get from LF + inputMap.remove(KeyStroke.getKeyStroke("F5")); + inputMap.remove(KeyStroke.getKeyStroke("shift F5")); + + } + + /** + * Creates and installs all listeners to all components. + * + */ + protected void installListeners() { + /* + * create the listeners. + */ + // propertyListener for datePicker + propertyChangeListener = createPropertyChangeListener(); + + // mouseListener (for popup button only) ? + mouseListener = createMouseListener(); + mouseMotionListener = createMouseMotionListener(); + + // shared focuslistener (installed to picker and editor) + focusListener = createFocusListener(); + + // editor related listeners + editorActionListener = createEditorActionListener(); + editorPropertyListener = createEditorPropertyListener(); + + // montheView related listeners + monthViewSelectionListener = createMonthViewSelectionListener(); + monthViewActionListener = createMonthViewActionListener(); + monthViewPropertyListener = createMonthViewPropertyListener(); + + popupRemover = new PopupRemover(); + /* + * install the listeners + */ + // picker + datePicker.addPropertyChangeListener(propertyChangeListener); + datePicker.addFocusListener(focusListener); + + if (popupButton != null) { + // JW: which property do we want to monitor? + popupButton.addPropertyChangeListener(propertyChangeListener); + popupButton.addMouseListener(mouseListener); + popupButton.addMouseMotionListener(mouseMotionListener); + } + + updateEditorListeners(null); + // JW the following does more than installing the listeners .. + // synchs properties of datepicker to monthView's + // prepares monthview for usage in popup + // synch the date + // Relies on being the last thing done in the install .. + // + updateFromMonthViewChanged(null); + } + /** + * Uninstalls and nulls all listeners which had been installed + * by this delegate. + * + */ + protected void uninstallListeners() { + // datePicker + datePicker.removePropertyChangeListener(propertyChangeListener); + datePicker.removeFocusListener(focusListener); + + // monthView + datePicker.getMonthView().getSelectionModel().removeDateSelectionListener(monthViewSelectionListener); + datePicker.getMonthView().removeActionListener(monthViewActionListener); + datePicker.getMonthView().removePropertyChangeListener(propertyChangeListener); + + // JW: when can that be null? + // maybe in the very beginning? if some code calls ui.uninstall + // before ui.install? The editor is created by the ui. + if (datePicker.getEditor() != null) { + uninstallEditorListeners(datePicker.getEditor()); + } + if (popupButton != null) { + popupButton.removePropertyChangeListener(propertyChangeListener); + popupButton.removeMouseListener(mouseListener); + popupButton.removeMouseMotionListener(mouseMotionListener); + } + + popupRemover.unload(); + + popupRemover = null; + propertyChangeListener = null; + mouseListener = null; + mouseMotionListener = null; + + editorActionListener = null; + editorPropertyListener = null; + + monthViewSelectionListener = null; + monthViewActionListener = null; + monthViewPropertyListener = null; + + handler = null; + } + +// --------------------- wiring listeners + /** + * Wires the picker's monthView related listening. Removes all + * listeners from the given old view and adds the listeners to + * the current monthView.

      + * + * @param oldMonthView + */ + protected void updateMonthViewListeners(JXMonthView oldMonthView) { + DateSelectionModel oldModel = null; + if (oldMonthView != null) { + oldMonthView.removePropertyChangeListener(monthViewPropertyListener); + oldMonthView.removeActionListener(monthViewActionListener); + oldModel = oldMonthView.getSelectionModel(); + } + datePicker.getMonthView().addPropertyChangeListener(monthViewPropertyListener); + datePicker.getMonthView().addActionListener(monthViewActionListener); + updateSelectionModelListeners(oldModel); + } + + + /** + * Wires the picker's editor related listening and actions. Removes + * listeners/actions from the old editor and adds them to + * the new editor.

      + * + * @param oldEditor the pickers editor before the change + */ + protected void updateEditorListeners(JFormattedTextField oldEditor) { + if (oldEditor != null) { + uninstallEditorListeners(oldEditor); + } + datePicker.getEditor().addPropertyChangeListener(editorPropertyListener); + datePicker.getEditor().addActionListener(editorActionListener); + datePicker.getEditor().addFocusListener(focusListener); + editorCancelAction = new EditorCancelAction(datePicker.getEditor()); + } + + /** + * Uninstalls all listeners and actions which have been installed + * by this delegate from the given editor. + * + * @param oldEditor the editor to uninstall. + */ + private void uninstallEditorListeners(JFormattedTextField oldEditor) { + oldEditor.removePropertyChangeListener(editorPropertyListener); + oldEditor.removeActionListener(editorActionListener); + oldEditor.removeFocusListener(focusListener); + if (editorCancelAction != null) { + editorCancelAction.uninstall(); + editorCancelAction = null; + } + } + + /** + * Wires monthView's selection model listening. Removes the + * selection listener from the old model and add to the new model. + * + * @param oldModel the dateSelectionModel before the change, may be null. + */ + protected void updateSelectionModelListeners(DateSelectionModel oldModel) { + if (oldModel != null) { + oldModel.removeDateSelectionListener(monthViewSelectionListener); + } + datePicker.getMonthView().getSelectionModel() + .addDateSelectionListener(monthViewSelectionListener); + + } + + + // ---------------- component creation + /** + * Creates the editor used to edit the date selection. The editor is + * configured with the default DatePickerFormatter marked as UIResource. + * + * @return an instance of a JFormattedTextField + */ + protected JFormattedTextField createEditor() { + JFormattedTextField f = new DefaultEditor( + new DatePickerFormatterUIResource(datePicker.getLocale())); + f.setName("dateField"); + // this produces a fixed pref widths, looking a bit funny + // int columns = UIManagerExt.getInt("JXDatePicker.numColumns", null); + // if (columns > 0) { + // f.setColumns(columns); + // } + // that's always 0 as it comes from the resourcebundle + // f.setColumns(UIManager.getInt("JXDatePicker.numColumns")); + Border border = UIManager.getBorder("JXDatePicker.border"); + if (border != null) { + f.setBorder(border); + } + return f; + } + + protected JButton createPopupButton() { + JButton b = new JButton(); + b.setName("popupButton"); + b.setRolloverEnabled(false); + b.setMargin(new Insets(0, 3, 0, 3)); + + Icon icon = UIManager.getIcon("JXDatePicker.arrowIcon"); + if (icon == null) { + icon = (Icon)UIManager.get("Tree.expandedIcon"); + } + b.setIcon(icon); + b.setFocusable(false); + return b; + } + + /** + * + * A subclass of JFormattedTextField which calculates a "reasonable" + * minimum preferred size, independent of value/text.

      + * + * Note: how to find the "reasonable" width is open to discussion. + * This implementation creates another datepicker, feeds it with + * the formats and asks its prefWidth.

      + * + * PENDING: there's a resource property JXDatePicker.numColumns - why + * don't we use it? + */ + private class DefaultEditor extends JFormattedTextField implements UIResource { + + + public DefaultEditor(AbstractFormatter formatter) { + super(formatter); + } + + /** + * {@inheritDoc}

      + * + * Overridden to return a preferred size which has a reasonable lower bound. + */ + @Override + public Dimension getPreferredSize() { + Dimension preferredSize = super.getPreferredSize(); + if (getColumns() <= 0) { + Dimension compare = getCompareMinimumSize(); + if (preferredSize.width < compare.width) { + return compare; + } + } + return preferredSize; + } + + /** + * {@inheritDoc}

      + * + * Overridden to return the preferred size. + */ + @Override + public Dimension getMinimumSize() { + return getPreferredSize(); + } + + private Dimension getCompareMinimumSize() { + JFormattedTextField field = new JFormattedTextField(getFormatter()); + field.setMargin(getMargin()); + field.setBorder(getBorder()); + field.setFont(getFont()); + field.setValue(new Date()); + Dimension min = field.getPreferredSize(); + field.setValue(null); + min.width += Math.max(field.getPreferredSize().width, 4); + return min; + } + + + } + +// ---------------- Layout + /** + * {@inheritDoc} + */ + @Override + public Dimension getMinimumSize(JComponent c) { + return getPreferredSize(c); + } + + /** + * {@inheritDoc} + */ + @Override + public Dimension getPreferredSize(JComponent c) { + Dimension dim = datePicker.getEditor().getPreferredSize(); + if (popupButton != null) { + dim.width += popupButton.getPreferredSize().width; + } + Insets insets = datePicker.getInsets(); + dim.width += insets.left + insets.right; + dim.height += insets.top + insets.bottom; + return (Dimension)dim.clone(); + } + + + @Override + public int getBaseline(int width, int height) { + JFormattedTextField editor = datePicker.getEditor(); + View rootView = editor.getUI().getRootView(editor); + if (rootView.getViewCount() > 0) { + Insets insets = editor.getInsets(); + Insets insetsOut = datePicker.getInsets(); + int nh = height - insets.top - insets.bottom + - insetsOut.top - insetsOut.bottom; + int y = insets.top + insetsOut.top; + View fieldView = rootView.getView(0); + int vspan = (int) fieldView.getPreferredSpan(View.Y_AXIS); + if (nh != vspan) { + int slop = nh - vspan; + y += slop / 2; + } + FontMetrics fm = editor.getFontMetrics(editor.getFont()); + y += fm.getAscent(); + return y; + } + return -1; + } + + +//------------------------------- controller methods/classes + + /** + * {@inheritDoc} + */ + @Override + public Date getSelectableDate(Date date) throws PropertyVetoException { + Date cleaned = date == null ? null : + datePicker.getMonthView().getSelectionModel().getNormalizedDate(date); + if (CalendarUtils.areEqual(cleaned, datePicker.getDate())) { + // one place to interrupt the update spiral + throw new PropertyVetoException("date not selectable", null); + } + if (cleaned == null) return cleaned; + if (datePicker.getMonthView().isUnselectableDate(cleaned)) { + throw new PropertyVetoException("date not selectable", null); + } + return cleaned; + } + +//-------------------- update methods called from listeners + /** + * Updates internals after picker's date property changed. + */ + protected void updateFromDateChanged() { + Date visibleHook = datePicker.getDate() != null ? + datePicker.getDate() : datePicker.getLinkDay(); + datePicker.getMonthView().ensureDateVisible(visibleHook); + datePicker.getEditor().setValue(datePicker.getDate()); + } + + /** + * Updates date related properties in picker/monthView + * after a change in the editor's value. Reverts the + * value if the new date is unselectable. + * + * @param oldDate the editor value before the change + * @param newDate the editor value after the change + */ + protected void updateFromValueChanged(Date oldDate, Date newDate) { + if ((newDate != null) && datePicker.getMonthView().isUnselectableDate(newDate)) { + revertValue(oldDate); + return; + } + // the other place to interrupt the update spiral + if (!CalendarUtils.areEqual(newDate, datePicker.getMonthView().getSelectionDate())) { + datePicker.getMonthView().setSelectionDate(newDate); + } + datePicker.setDate(newDate); + } + + /** + * PENDING: currently this resets at once - but it's a no-no, + * because it happens during notification + * + * @param oldDate the old date to revert to + */ + private void revertValue(Date oldDate) { + datePicker.getEditor().setValue(oldDate); + } + /** + * Updates date related properties picker/editor + * after a change in the monthView's + * selection. + * + * Here: does nothing if the change is intermediate. + * + * PENDNG JW: shouldn't we listen to actionEvents then? + * + * @param eventType the type of the selection change + * @param adjusting flag to indicate whether the the selection change + * is intermediate + */ + protected void updateFromSelectionChanged(EventType eventType, boolean adjusting) { + if (adjusting) return; + updateEditorValue(); + } + + /** + * Updates internals after the picker's monthView has changed.

      + * + * Cleans to popup. Wires the listeners. Updates date. + * Updates formats' timezone. + * + * @param oldMonthView the picker's monthView before the change, + * may be null. + */ + protected void updateFromMonthViewChanged(JXMonthView oldMonthView) { + uninstallPopup(); + updateMonthViewListeners(oldMonthView); + TimeZone oldTimeZone = null; + if (oldMonthView != null) { + oldMonthView.setComponentInputMapEnabled(false); + oldTimeZone = oldMonthView.getTimeZone(); + } + datePicker.getMonthView().setComponentInputMapEnabled(true); + updateTimeZone(oldTimeZone); + updateEditorValue(); + } + + + /** + * Updates internals after the picker's editor property + * has changed.

      + * + * Updates the picker's children. Removes the old editor and + * adds the new editor. Wires the editor listeners, it the flag + * set. Typically, this method is called during installing the + * componentUI with the flag set to false and true at all other + * moments. + * + * + * @param oldEditor the picker's editor before the change, + * may be null. + * @param updateListeners a flag to indicate whether the listeners + * are ready for usage. + */ + protected void updateFromEditorChanged(JFormattedTextField oldEditor, + boolean updateListeners) { + if (oldEditor != null) { + datePicker.remove(oldEditor); + oldEditor.putClientProperty("doNotCancelPopup", null); + } + datePicker.add(datePicker.getEditor()); + // this is a trick to get hold of the client prop which + // prevents closing of the popup + JComboBox box = new JComboBox(); + Object preventHide = box.getClientProperty("doNotCancelPopup"); + datePicker.getEditor().putClientProperty("doNotCancelPopup", preventHide); + datePicker.getEditor().setInheritsPopupMenu(true); + + updateEditorValue(); + updateEditorProperties(); + if (updateListeners) { + updateEditorListeners(oldEditor); + datePicker.revalidate(); + } + } + + + /** + * Synchronizes the properties of the current editor to the properties of + * the JXDatePicker. + */ + private void updateEditorProperties() { + datePicker.getEditor().setEnabled(datePicker.isEnabled()); + datePicker.getEditor().setEditable(datePicker.isEditable()); + } + + /** + * Updates internals after the selection model changed. + * + * @param oldModel the model before the change. + */ + protected void updateFromSelectionModelChanged(DateSelectionModel oldModel) { + updateSelectionModelListeners(oldModel); + updateEditorValue(); + } + + /** + * Sets the editor value to the model's selectedDate. + */ + private void updateEditorValue() { + datePicker.getEditor().setValue(datePicker.getMonthView().getSelectionDate()); + } + + //---------------------- updating other properties + + + /** + * Updates properties which depend on the picker's editable.

      + * + */ + protected void updateFromEditableChanged() { + boolean isEditable = datePicker.isEditable(); + // PENDING JW: revisit - align with combo's editable? + datePicker.getMonthView().setEnabled(isEditable); + datePicker.getEditor().setEditable(isEditable); + /* + * PatrykRy: Commit today date is not allowed if datepicker is not editable! + */ + setActionEnabled(JXDatePicker.HOME_COMMIT_KEY, isEditable); + // for consistency, synch navigation as well + setActionEnabled(JXDatePicker.HOME_NAVIGATE_KEY, isEditable); + } + + /** + * Update properties which depend on the picker's enabled. + */ + protected void updateFromEnabledChanged() { + boolean isEnabled = datePicker.isEnabled(); + popupButton.setEnabled(isEnabled); + datePicker.getEditor().setEnabled(isEnabled); + } + + + /** + * + * @param key + * @param enabled + */ + private void setActionEnabled(String key, boolean enabled) { + Action action = datePicker.getActionMap().get(key); + if (action != null) { + action.setEnabled(enabled); + } + } + + /** + * Updates the picker's formats to the given TimeZone. + * @param zone the timezone to set on the formats. + */ + protected void updateFormatsFromTimeZone(TimeZone zone) { + for (DateFormat format : datePicker.getFormats()) { + format.setTimeZone(zone); + } + } + + /** + * Updates picker's timezone dependent properties on change notification + * from the associated monthView. + * + * PENDING JW: DatePicker needs to send notification on timezone change? + * + * @param old the timezone before the change. + */ + protected void updateTimeZone(TimeZone old) { + updateFormatsFromTimeZone(datePicker.getTimeZone()); + updateLinkDate(); + } + + /** + * Updates the picker's linkDate to be in synch with monthView's today. + */ + protected void updateLinkDate() { + datePicker.setLinkDay(datePicker.getMonthView().getToday()); + } + + /** + * Called form property listener, updates all components locale, formats + * etc. + * + * @author PeS + */ + protected void updateLocale() { + Locale locale = datePicker.getLocale(); + updateFormatLocale(locale); + updateChildLocale(locale); + } + + private void updateFormatLocale(Locale locale) { + if (locale != null) { + // PENDING JW: timezone? + if (getCustomFormats(datePicker.getEditor()) == null) { + datePicker.getEditor().setFormatterFactory( + new DefaultFormatterFactory( + new DatePickerFormatterUIResource(locale))); + } + } + } + + private void updateChildLocale(Locale locale) { + if (locale != null) { + datePicker.getEditor().setLocale(locale); + datePicker.getLinkPanel().setLocale(locale); + datePicker.getMonthView().setLocale(locale); + } + } + + /** + * @param oldLinkPanel + * + */ + protected void updateLinkPanel(JComponent oldLinkPanel) { + if (oldLinkPanel != null) { + uninstallLinkPanelKeyboardActions(oldLinkPanel); + } + installLinkPanelKeyboardActions(); + if (popup != null) { + popup.updateLinkPanel(oldLinkPanel); + } + } + + +//------------------- methods called by installed actions + + /** + * + */ + protected void commit() { + hidePopup(); + try { + datePicker.commitEdit(); + } catch (ParseException ex) { + // can't help it + } + } + + /** + * + */ + protected void cancel() { + if (isPopupVisible()) { + popup.putClientProperty("JPopupMenu.firePopupMenuCanceled", Boolean.TRUE); + } + hidePopup(); + datePicker.cancelEdit(); + } + + /** + * PENDING: widened access for debugging - need api to + * control popup visibility? + */ + public void hidePopup() { + if (popup != null) popup.setVisible(false); + } + + public boolean isPopupVisible() { + if (popup != null) { + return popup.isVisible(); + } + return false; + } + /** + * Navigates to linkDate. If commit, the linkDate is selected + * and committed. If not commit, the linkDate is scrolled to visible, if the + * monthview is open, does nothing for invisible monthView. + * + * @param commit boolean to indicate whether the linkDate should be + * selected and committed + */ + protected void home(boolean commit) { + if (commit) { + Calendar cal = datePicker.getMonthView().getCalendar(); + cal.setTime(datePicker.getLinkDay()); + datePicker.getMonthView().setSelectionDate(cal.getTime()); + datePicker.getMonthView().commitSelection(); + } else { + datePicker.getMonthView().ensureDateVisible(datePicker.getLinkDay()); + } + } + +//---------------------- other stuff + + /** + * Creates and returns the action for committing the picker's + * input. + * + * @return + */ + private Action createCommitAction() { + Action action = new AbstractAction() { + + @Override + public void actionPerformed(ActionEvent e) { + commit(); + } + + }; + return action; + } + + /** + * Creates and returns the action for cancel the picker's + * edit. + * + * @return + */ + private Action createCancelAction() { + Action action = new AbstractAction() { + + @Override + public void actionPerformed(ActionEvent e) { + cancel(); + } + + }; + return action; + } + + private Action createHomeAction(final boolean commit) { + Action action = new AbstractAction( ) { + + @Override + public void actionPerformed(ActionEvent e) { + home(commit); + + } + + }; + return action ; + } + /** + * The wrapper for the editor cancel action. + * + * PENDING: Need to extend TestAction? + * + */ + public class EditorCancelAction extends AbstractAction { + private JFormattedTextField editor; + private Action cancelAction; + public static final String TEXT_CANCEL_KEY = "reset-field-edit"; + + public EditorCancelAction(JFormattedTextField field) { + install(field); + } + + /** + * Resets the contained editors actionMap to original and + * nulls all fields.

      + * NOTE: after calling this method the action must not be + * used! Create a new one for the same or another editor. + * + */ + public void uninstall() { + editor.getActionMap().remove(TEXT_CANCEL_KEY); + cancelAction = null; + editor = null; + } + + /** + * @param editor + */ + private void install(JFormattedTextField editor) { + this.editor = editor; + cancelAction = editor.getActionMap().get(TEXT_CANCEL_KEY); + editor.getActionMap().put(TEXT_CANCEL_KEY, this); + } + + @Override + public void actionPerformed(ActionEvent e) { + cancelAction.actionPerformed(null); + cancel(); + } + + } + + /** + * Creates and returns the action which toggles the visibility of the popup. + * + * @return the action which toggles the visibility of the popup. + */ + protected TogglePopupAction createTogglePopupAction() { + return new TogglePopupAction(); + } + + /** + * Toggles the popups visibility after preparing internal state. + * + * + */ + public void toggleShowPopup() { + if (popup == null) { + installPopup(); + } + if (popup.isVisible()) { + popup.setVisible(false); + } else { + // PENDING JW: Issue 757-swing - datePicker firing focusLost on + // opening + // not with following line - but need to run tests + datePicker.getEditor().requestFocusInWindow(); +// datePicker.requestFocusInWindow(); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { +// if (datePicker.getParent() == null) { +// // Tracking #1372-swingx - parent is null if used as +// // DatePickerCellEditor, +// // two different editors, clickCountToStart == 1 and +// // Metal +// // as a first hot fix, we back out +// LOG.info("couldn't show popup for: " + datePicker.getName()); +// return; +// } + popup.show(datePicker, 0, datePicker.getHeight()); + } + }); + } + } + + /** + * Creates the popup and registers the popup listener. All internal + * methods must use this method instead of calling createPopup directly. + */ + protected void installPopup() { + popup = createMonthViewPopup(); + popup.addPopupMenuListener(getPopupMenuListener()); + } + + /** + * Removes the popup listener from the popup and null it, if + * it was not null. All internal popup removal/replacement must + * use this method instead of nulling directly. + * + */ + protected void uninstallPopup() { + if (popup != null) { + popup.removePopupMenuListener(getPopupMenuListener()); + } + popup = null; + } + + /** + * Returns the PopupMenuListener for the MonthView popup. Lazily created. + * + * @return the popupuMenuListener to install on the popup + */ + protected PopupMenuListener getPopupMenuListener() { + if (popupMenuListener == null) { + popupMenuListener = createPopupMenuListener(); + } + return popupMenuListener; + } + + /** + * Creates and returns a PopupMenuListener. + * + * PENDING JW: the listener management assumes a stateless implementation + * relative to the popup/picker. Custom implementations should take care + * to not keep any references. + * + * @return + */ + protected PopupMenuListener createPopupMenuListener() { + PopupMenuListener l= new PopupMenuListener() { + + @Override + public void popupMenuCanceled(PopupMenuEvent e) { + PopupMenuListener[] ls = datePicker.getPopupMenuListeners(); + PopupMenuEvent retargeted = null; + for (PopupMenuListener listener : ls) { + if (retargeted == null) { + retargeted = new PopupMenuEvent(datePicker); + } + listener.popupMenuCanceled(retargeted); + } + } + + @Override + public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { + PopupMenuListener[] ls = datePicker.getPopupMenuListeners(); + PopupMenuEvent retargeted = null; + for (PopupMenuListener listener : ls) { + if (retargeted == null) { + retargeted = new PopupMenuEvent(datePicker); + } + listener.popupMenuWillBecomeInvisible(retargeted); + } + } + + @Override + public void popupMenuWillBecomeVisible(PopupMenuEvent e) { + PopupMenuListener[] ls = datePicker.getPopupMenuListeners(); + PopupMenuEvent retargeted = null; + for (PopupMenuListener listener : ls) { + if (retargeted == null) { + retargeted = new PopupMenuEvent(datePicker); + } + listener.popupMenuWillBecomeVisible(retargeted); + } + } + + }; + return l; + } + + + /** + * + */ + private BasicDatePickerPopup createMonthViewPopup() { + BasicDatePickerPopup popup = new BasicDatePickerPopup(); + popup.setLightWeightPopupEnabled(datePicker.isLightWeightPopupEnabled()); + return popup; + } + /** + * Action used to commit the current value in the JFormattedTextField. + * This action is used by the keyboard bindings. + */ + private class TogglePopupAction extends AbstractAction { + public TogglePopupAction() { + super("TogglePopup"); + } + + @Override + public void actionPerformed(ActionEvent ev) { + toggleShowPopup(); + } + } + + + /** + * Popup component that shows a JXMonthView component along with controlling + * buttons to allow traversal of the months. Upon selection of a date the + * popup will automatically hide itself and enter the selection into the + * editable field of the JXDatePicker. + * + */ + protected class BasicDatePickerPopup extends JPopupMenu { + + public BasicDatePickerPopup() { + setLayout(new BorderLayout()); + add(datePicker.getMonthView(), BorderLayout.CENTER); + updateLinkPanel(null); + } + + /** + * @param oldLinkPanel + */ + public void updateLinkPanel(JComponent oldLinkPanel) { + if (oldLinkPanel != null) { + remove(oldLinkPanel); + } + if (datePicker.getLinkPanel() != null) { + add(datePicker.getLinkPanel(), BorderLayout.SOUTH); + } + + } + } + + /** + * PENDING: JW - I really hate the one-in-all. Wont touch + * it for now, maybe later. As long as we have it, the new + * listeners (dateSelection) are here too, for consistency. + * Adding the Layout here as well is ... , IMO. + */ + private class Handler implements LayoutManager, MouseListener, MouseMotionListener, + PropertyChangeListener, DateSelectionListener, ActionListener, FocusListener { + +//------------- implement Mouse/MotionListener + private boolean _forwardReleaseEvent = false; + + @Override + public void mouseClicked(MouseEvent ev) { + } + + @Override + public void mousePressed(MouseEvent ev) { + if (!datePicker.isEnabled() || !SwingUtilities.isLeftMouseButton(ev)) { + return; + } + // PENDING JW: why do we need a mouseListener? the + // arrowbutton should have the toggleAction installed? + // Hmm... maybe doesn't ... check! + // reason might be that we want to open on pressed + // typically (or LF-dependent?), + // the button's action is invoked on released. +// LOG.info("opening on mousePressed?"); + toggleShowPopup(); + } + + @Override + public void mouseReleased(MouseEvent ev) { + if (!datePicker.isEnabled() || !datePicker.isEditable()) { + return; + } + + // Retarget mouse event to the month view. + if (_forwardReleaseEvent) { + JXMonthView monthView = datePicker.getMonthView(); + ev = SwingUtilities.convertMouseEvent(popupButton, ev, + monthView); + monthView.dispatchEvent(ev); + _forwardReleaseEvent = false; + } + } + + @Override + public void mouseEntered(MouseEvent ev) { + } + + @Override + public void mouseExited(MouseEvent ev) { + } + + @Override + public void mouseDragged(MouseEvent ev) { + if (!datePicker.isEnabled() || !datePicker.isEditable()) { + return; + } + + _forwardReleaseEvent = true; + + if (!popup.isShowing()) { + return; + } + + // Retarget mouse event to the month view. + JXMonthView monthView = datePicker.getMonthView(); + ev = SwingUtilities.convertMouseEvent(popupButton, ev, monthView); + monthView.dispatchEvent(ev); + } + + @Override + public void mouseMoved(MouseEvent ev) { + } +//------------------ implement DateSelectionListener + + @Override + public void valueChanged(DateSelectionEvent ev) { + updateFromSelectionChanged(ev.getEventType(), ev.isAdjusting()); + } + +//------------------ implement propertyChangeListener + /** + * {@inheritDoc} + */ + @Override + public void propertyChange(PropertyChangeEvent e) { + if (e.getSource() == datePicker) { + datePickerPropertyChange(e); + } else + if (e.getSource() == datePicker.getEditor()) { + editorPropertyChange(e); + } else + if (e.getSource() == datePicker.getMonthView()) { + monthViewPropertyChange(e); + } else + if (e.getSource() == popupButton) { + buttonPropertyChange(e); + } else + // PENDING - move back, ... + if ("value".equals(e.getPropertyName())) { + throw new IllegalStateException( + "editor listening is moved to dedicated propertyChangeLisener"); + } + } + + /** + * Handles property changes from datepicker's editor. + * + * @param e the PropertyChangeEvent object describing the event source + * and the property that has changed + */ + private void editorPropertyChange(PropertyChangeEvent evt) { + if ("value".equals(evt.getPropertyName())) { + updateFromValueChanged((Date) evt.getOldValue(), (Date) evt + .getNewValue()); + } + + } + + /** + * Handles property changes from DatePicker. + * @param e the PropertyChangeEvent object describing the + * event source and the property that has changed + */ + private void datePickerPropertyChange(PropertyChangeEvent e) { + String property = e.getPropertyName(); + if ("date".equals(property)) { + updateFromDateChanged(); + } else if ("enabled".equals(property)) { + updateFromEnabledChanged(); + } else if ("editable".equals(property)) { + updateFromEditableChanged(); + } else if (JComponent.TOOL_TIP_TEXT_KEY.equals(property)) { + String tip = datePicker.getToolTipText(); + datePicker.getEditor().setToolTipText(tip); + popupButton.setToolTipText(tip); + } else if (JXDatePicker.MONTH_VIEW.equals(property)) { + updateFromMonthViewChanged((JXMonthView) e.getOldValue()); + } else if (JXDatePicker.LINK_PANEL.equals(property)) { + updateLinkPanel((JComponent) e.getOldValue()); + } else if (JXDatePicker.EDITOR.equals(property)) { + updateFromEditorChanged((JFormattedTextField) e.getOldValue(), true); + } else if ("componentOrientation".equals(property)) { + datePicker.revalidate(); + } else if ("lightWeightPopupEnabled".equals(property)) { + // Force recreation of the popup when this property changes. + if (popup != null) { + popup.setVisible(false); + } + uninstallPopup(); + } else if ("formats".equals(property)) { + updateFormatsFromTimeZone(datePicker.getTimeZone()); + } + else if ("locale".equals(property)) { + updateLocale(); + } + } + + /** + * Handles propertyChanges from the picker's monthView. + * + * @param e the PropertyChangeEvent object describing the event source + * and the property that has changed + */ + private void monthViewPropertyChange(PropertyChangeEvent e) { + if ("selectionModel".equals(e.getPropertyName())) { + updateFromSelectionModelChanged((DateSelectionModel) e.getOldValue()); + } else if ("timeZone".equals(e.getPropertyName())) { + updateTimeZone((TimeZone) e.getOldValue()); + } else if ("today".equals(e.getPropertyName())) { + updateLinkDate(); + } + } + + /** + * Handles propertyChanges from the picker's popupButton. + * + * PENDING: does nothing, kept while refactoring .. which + * properties from the button do we want to handle? + * + * @param e the PropertyChangeEvent object describing the event source + * and the property that has changed. + */ + private void buttonPropertyChange(PropertyChangeEvent e) { + } + +//-------------- implement LayoutManager + + @Override + public void addLayoutComponent(String name, Component comp) { } + + @Override + public void removeLayoutComponent(Component comp) { } + + @Override + public Dimension preferredLayoutSize(Container parent) { + return parent.getPreferredSize(); + } + + @Override + public Dimension minimumLayoutSize(Container parent) { + return parent.getMinimumSize(); + } + + @Override + public void layoutContainer(Container parent) { + Insets insets = datePicker.getInsets(); + int width = datePicker.getWidth() - insets.left - insets.right; + int height = datePicker.getHeight() - insets.top - insets.bottom; + + int popupButtonWidth = popupButton != null ? popupButton.getPreferredSize().width : 0; + + boolean ltr = datePicker.getComponentOrientation().isLeftToRight(); + + datePicker.getEditor().setBounds(ltr ? insets.left : insets.left + popupButtonWidth, + insets.top, + width - popupButtonWidth, + height); + + if (popupButton != null) { + popupButton.setBounds(ltr ? width - popupButtonWidth + insets.left : insets.left, + insets.top, + popupButtonWidth, + height); + } + } + +// ------------- implement actionListener (listening to monthView actionEvent) + + @Override + public void actionPerformed(ActionEvent e) { + if (e == null) return; + if (e.getSource() == datePicker.getMonthView()) { + monthViewActionPerformed(e); + } else if (e.getSource() == datePicker.getEditor()) { + editorActionPerformed(e); + } + } + + /** + * Listening to actionEvents fired by the picker's editor. + * + * @param e + */ + private void editorActionPerformed(ActionEvent e) { + // pass the commit on to the picker. + commit(); + } + + /** + * Listening to actionEvents fired by the picker's monthView. + * + * @param e + */ + private void monthViewActionPerformed(ActionEvent e) { + if (JXMonthView.CANCEL_KEY.equals(e.getActionCommand())) { + cancel(); + } else if (JXMonthView.COMMIT_KEY.equals(e.getActionCommand())) { + commit(); + } + } + +//------------------- focusListener + + /** + * Issue #573-swingx - F2 in table doesn't focus the editor. + * + * Do the same as combo: manually pass-on the focus to the editor. + * + */ + @Override + public void focusGained(FocusEvent e) { + if (e.isTemporary()) return; + popupRemover.load(); + if (e.getSource() == datePicker) { + datePicker.getEditor().requestFocusInWindow(); + } + } + + /** + * #565-swingx: popup not hidden if clicked into combo. + * The problem is that the combo uses the same trick as + * this datepicker to prevent auto-closing of the popup + * if focus is transfered back to the picker's editor. + * + * The idea is to hide the popup manually when the + * permanentFocusOwner changes to somewhere else. + * + * JW: doesn't work - we only get the temporary lost, + * but no permanent loss if the focus is transfered from + * the focusOwner to a new permanentFocusOwner. + * + * OOOkaay ... looks like exclusively related to a combo: + * we do get the expected focusLost if the focus is + * transferred permanently from the temporary focusowner + * to a new "normal" permanentFocusOwner (like a textfield), + * we don't get it if transfered to a tricksing owner (like + * a combo or picker). So can't do anything here. + * + * listen to keyboardFocusManager? + */ + @Override + public void focusLost(FocusEvent e) { + + } + } + + public class PopupRemover implements PropertyChangeListener { + + private KeyboardFocusManager manager; + private boolean loaded; + + public void load() { + if (manager != KeyboardFocusManager.getCurrentKeyboardFocusManager()) { + unload(); + manager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); + } + if (!loaded) { + manager.addPropertyChangeListener("permanentFocusOwner", this); + loaded = true; + } + } + + /** + * @param b + */ + private void unload(boolean nullManager) { + if (manager != null) { + manager.removePropertyChangeListener("permanentFocusOwner", this); + if (nullManager) { + manager = null; + } + } + loaded = false; + } + + public void unload() { + unload(true); + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (!isPopupVisible()) { + unload(false); + return; + } + Component comp = manager.getPermanentFocusOwner(); + if ((comp != null) && !SwingXUtilities.isDescendingFrom(comp, datePicker)) { + unload(false); + // on hiding the popup the focusmanager transfers + // focus back to the old permanentFocusOwner + // before showing the popup, that is the picker + // or the editor. So we have to force it back ... + hidePopup(); + comp.requestFocusInWindow(); + // this has no effect as focus changes are asynchronous +// inHide = false; + } + } + + + } + + +// ------------------ listener creation + + /** + * Creates and returns the property change listener for the + * picker's monthView + * @return the listener for monthView properties + */ + protected PropertyChangeListener createMonthViewPropertyListener() { + return getHandler(); + } + + /** + * Creates and returns the focuslistener for picker and editor. + * @return the focusListener + */ + protected FocusListener createFocusListener() { + return getHandler(); + } + + + /** + * Creates and returns the ActionListener for the picker's editor. + * @return the Actionlistener for the editor. + */ + protected ActionListener createEditorActionListener() { + return getHandler(); + } + + /** + * Creates and returns the ActionListener for the picker's monthView. + * + * @return the Actionlistener for the monthView. + */ + protected ActionListener createMonthViewActionListener() { + return getHandler(); + } + +/** + * Returns the listener for the dateSelection. + * + * @return the date selection listener + */ + protected DateSelectionListener createMonthViewSelectionListener() { + return getHandler(); + } + + /** + * @return a propertyChangeListener listening to + * editor property changes + */ + protected PropertyChangeListener createEditorPropertyListener() { + return getHandler(); + } + + /** + * Lazily creates and returns the shared all-mighty listener of everything + * + * @return the shared listener. + */ + private Handler getHandler() { + if (handler == null) { + handler = new Handler(); + } + return handler; + } + + protected PropertyChangeListener createPropertyChangeListener() { + return getHandler(); + } + + protected LayoutManager createLayoutManager() { + return getHandler(); + } + + protected MouseListener createMouseListener() { + return getHandler(); + } + + protected MouseMotionListener createMouseMotionListener() { + return getHandler(); + } + + +//------------ utility methods + + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicErrorPaneUI.java b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicErrorPaneUI.java new file mode 100644 index 0000000000..c59e403024 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicErrorPaneUI.java @@ -0,0 +1,1098 @@ +/* + * $Id: BasicErrorPaneUI.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf.basic; + +import org.jdesktop.swingx.JXEditorPane; +import org.jdesktop.swingx.JXErrorPane; +import org.jdesktop.swingx.action.AbstractActionExt; +import org.jdesktop.swingx.error.ErrorInfo; +import org.jdesktop.swingx.error.ErrorLevel; +import org.jdesktop.swingx.error.ErrorReporter; +import org.jdesktop.swingx.plaf.ErrorPaneUI; +import org.jdesktop.swingx.plaf.UIManagerExt; +import org.jdesktop.swingx.util.WindowUtils; + +import javax.swing.*; +import javax.swing.border.EmptyBorder; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.UIResource; +import javax.swing.plaf.basic.BasicHTML; +import javax.swing.text.JTextComponent; +import javax.swing.text.StyledEditorKit; +import javax.swing.text.html.HTMLEditorKit; +import java.awt.*; +import java.awt.datatransfer.StringSelection; +import java.awt.datatransfer.Transferable; +import java.awt.event.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.logging.Level; + +/** + * Base implementation of the JXErrorPane UI. + * + * @author rbair + * @author rah003 + */ +public class BasicErrorPaneUI extends ErrorPaneUI { + /** + * Used as a prefix when pulling data out of UIManager for i18n + */ + protected static final String CLASS_NAME = "JXErrorPane"; + + /** + * The error pane this UI is for + */ + protected JXErrorPane pane; + /** + * Error message text area + */ + protected JEditorPane errorMessage; + + /** + * Error message text scroll pane wrapper. + */ + protected JScrollPane errorScrollPane; + /** + * details text area + */ + protected JXEditorPane details; + /** + * detail button + */ + protected AbstractButton detailButton; + /** + * ok/close button + */ + protected JButton closeButton; + /** + * label used to display the warning/error icon + */ + protected JLabel iconLabel; + /** + * report an error button + */ + protected AbstractButton reportButton; + /** + * details panel + */ + protected JPanel detailsPanel; + protected JScrollPane detailsScrollPane; + protected JButton copyToClipboardButton; + + /** + * Property change listener for the error pane ensures that the pane's UI + * is reinitialized. + */ + protected PropertyChangeListener errorPaneListener; + + /** + * Action listener for the detail button. + */ + protected ActionListener detailListener; + + /** + * Action listener for the copy to clipboard button. + */ + protected ActionListener copyToClipboardListener; + + //------------------------------------------------------ private helpers + /** + * The height of the window when collapsed. This value is stashed when the + * dialog is expanded + */ + private int collapsedHeight = 0; + /** + * The height of the window when last expanded. This value is stashed when + * the dialog is collapsed + */ + private int expandedHeight = 0; + + //---------------------------------------------------------- constructor + + /** + * {@inheritDoc} + */ + public static ComponentUI createUI(JComponent c) { + return new BasicErrorPaneUI(); + } + + /** + * {@inheritDoc} + */ + @Override + public void installUI(JComponent c) { + super.installUI(c); + + this.pane = (JXErrorPane)c; + + installDefaults(); + installComponents(); + installListeners(); + + //if the report action needs to be defined, do so + Action a = c.getActionMap().get(JXErrorPane.REPORT_ACTION_KEY); + if (a == null) { + final JXErrorPane pane = (JXErrorPane)c; + AbstractActionExt reportAction = new AbstractActionExt() { + @Override + public void actionPerformed(ActionEvent e) { + ErrorReporter reporter = pane.getErrorReporter(); + if (reporter != null) { + reporter.reportError(pane.getErrorInfo()); + } + } + }; + configureReportAction(reportAction); + c.getActionMap().put(JXErrorPane.REPORT_ACTION_KEY, reportAction); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void uninstallUI(JComponent c) { + super.uninstallUI(c); + + uninstallListeners(); + uninstallComponents(); + uninstallDefaults(); + } + + /** + * Installs the default colors, and default font into the Error Pane + */ + protected void installDefaults() { + } + + + /** + * Uninstalls the default colors, and default font into the Error Pane. + */ + protected void uninstallDefaults() { + LookAndFeel.uninstallBorder(pane); + } + + + /** + * Create and install the listeners for the Error Pane. + * This method is called when the UI is installed. + */ + protected void installListeners() { + //add a listener to the pane so I can reinit() whenever the + //bean properties change (particularly error info) + errorPaneListener = new ErrorPaneListener(); + pane.addPropertyChangeListener(errorPaneListener); + } + + + /** + * Remove the installed listeners from the Error Pane. + * The number and types of listeners removed and in this method should be + * the same that was added in installListeners + */ + protected void uninstallListeners() { + //remove the property change listener from the pane + pane.removePropertyChangeListener(errorPaneListener); + } + + + // =============================== + // begin Sub-Component Management + // + + /** + * Creates and initializes the components which make up the + * aggregate combo box. This method is called as part of the UI + * installation process. + */ + protected void installComponents() { + iconLabel = new JLabel(pane.getIcon()); + + errorMessage = new JEditorPane(); + errorMessage.setEditable(false); + errorMessage.setContentType("text/html"); + errorMessage.setEditorKitForContentType("text/plain", new StyledEditorKit()); + errorMessage.setEditorKitForContentType("text/html", new HTMLEditorKit()); + + errorMessage.setOpaque(false); + errorMessage.putClientProperty(JXEditorPane.HONOR_DISPLAY_PROPERTIES, Boolean.TRUE); + + closeButton = new JButton(UIManagerExt.getString( + CLASS_NAME + ".ok_button_text", errorMessage.getLocale())); + + reportButton = new EqualSizeJButton(pane.getActionMap().get(JXErrorPane.REPORT_ACTION_KEY)); + + detailButton = new EqualSizeJButton(UIManagerExt.getString( + CLASS_NAME + ".details_expand_text", errorMessage.getLocale())); + + details = new JXEditorPane(); + details.setContentType("text/html"); + details.putClientProperty(JXEditorPane.HONOR_DISPLAY_PROPERTIES, Boolean.TRUE); + details.setTransferHandler(createDetailsTransferHandler(details)); + detailsScrollPane = new JScrollPane(details); + detailsScrollPane.setPreferredSize(new Dimension(10, 250)); + details.setEditable(false); + detailsPanel = new JPanel(); + detailsPanel.setVisible(false); + copyToClipboardButton = new JButton(UIManagerExt.getString( + CLASS_NAME + ".copy_to_clipboard_button_text", errorMessage.getLocale())); + copyToClipboardListener = new ActionListener() { + @Override + public void actionPerformed(ActionEvent ae) { + details.copy(); + } + }; + copyToClipboardButton.addActionListener(copyToClipboardListener); + + detailsPanel.setLayout(createDetailPanelLayout()); + detailsPanel.add(detailsScrollPane); + detailsPanel.add(copyToClipboardButton); + + // Create error scroll pane. Make sure this happens before call to createErrorPaneLayout() in case any extending + // class wants to manipulate the component there. + errorScrollPane = new JScrollPane(errorMessage); + errorScrollPane.setBorder(new EmptyBorder(0,0,5,0)); + errorScrollPane.setOpaque(false); + errorScrollPane.getViewport().setOpaque(false); + + //initialize the gui. Most of this code is similar between Mac and PC, but + //where they differ protected methods have been written allowing the + //mac implementation to alter the layout of the dialog. + pane.setLayout(createErrorPaneLayout()); + + //An empty border which constitutes the padding from the edge of the + //dialog to the content. All content that butts against this border should + //not be padded. + Insets borderInsets = new Insets(16, 24, 16, 17); + pane.setBorder(BorderFactory.createEmptyBorder(borderInsets.top, borderInsets.left, borderInsets.bottom, borderInsets.right)); + + //add the JLabel responsible for displaying the icon. + //TODO: in the future, replace this usage of a JLabel with a JXImagePane, + //which may add additional "coolness" such as allowing the user to drag + //the image off the dialog onto the desktop. This kind of coolness is common + //in the mac world. + pane.add(iconLabel); + pane.add(errorScrollPane); + pane.add(closeButton); + pane.add(reportButton); + reportButton.setVisible(false); // not visible by default + pane.add(detailButton); + pane.add(detailsPanel); + + //make the buttons the same size + EqualSizeJButton[] buttons = new EqualSizeJButton[] { + (EqualSizeJButton)detailButton, (EqualSizeJButton)reportButton }; + ((EqualSizeJButton)reportButton).setGroup(buttons); + ((EqualSizeJButton)detailButton).setGroup(buttons); + + reportButton.setMinimumSize(reportButton.getPreferredSize()); + detailButton.setMinimumSize(detailButton.getPreferredSize()); + + //set the event handling + detailListener = new DetailsClickEvent(); + detailButton.addActionListener(detailListener); + } + + /** + * The aggregate components which compise the combo box are + * unregistered and uninitialized. This method is called as part of the + * UI uninstallation process. + */ + protected void uninstallComponents() { + iconLabel = null; + errorMessage = null; + closeButton = null; + reportButton = null; + + detailButton.removeActionListener(detailListener); + detailButton = null; + + details.setTransferHandler(null); + details = null; + + detailsScrollPane.removeAll(); + detailsScrollPane = null; + + detailsPanel.setLayout(null); + detailsPanel.removeAll(); + detailsPanel = null; + + copyToClipboardButton.removeActionListener(copyToClipboardListener); + copyToClipboardButton = null; + + pane.removeAll(); + pane.setLayout(null); + pane.setBorder(null); + } + + // + // end Sub-Component Management + // =============================== + + /** + * @inheritDoc + */ + @Override + public JFrame getErrorFrame(Component owner) { + reinit(); + expandedHeight = 0; + collapsedHeight = 0; + JXErrorFrame frame = new JXErrorFrame(pane); + centerWindow(frame, owner); + return frame; + } + + /** + * @inheritDoc + */ + @Override + public JDialog getErrorDialog(Component owner) { + reinit(); + expandedHeight = 0; + collapsedHeight = 0; + Window w = WindowUtils.findWindow(owner); + JXErrorDialog dlg = null; + if (w instanceof Dialog) { + dlg = new JXErrorDialog((Dialog)w, pane); + } else if (w instanceof Frame) { + dlg = new JXErrorDialog((Frame)w, pane); + } else { + // default fallback to null + dlg = new JXErrorDialog(JOptionPane.getRootFrame(), pane); + } + centerWindow(dlg, owner); + return dlg; + } + + /** + * @inheritDoc + */ + @Override + public JInternalFrame getErrorInternalFrame(Component owner) { + reinit(); + expandedHeight = 0; + collapsedHeight = 0; + JXInternalErrorFrame frame = new JXInternalErrorFrame(pane); + centerWindow(frame, owner); + return frame; + } + + /** + * Create and return the LayoutManager to use with the error pane. + */ + protected LayoutManager createErrorPaneLayout() { + return new ErrorPaneLayout(); + } + + protected LayoutManager createDetailPanelLayout() { + GridBagLayout layout = new GridBagLayout(); + layout.addLayoutComponent(detailsScrollPane, new GridBagConstraints(0,0,1,1,1.0,1.0,GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(6,0,0,0),0,0)); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.LINE_END; + gbc.fill = GridBagConstraints.NONE; + gbc.gridwidth = 1; + gbc.gridx = 0; + gbc.gridy = 1; + gbc.weighty = 0.0; + gbc.weightx = 1.0; + gbc.insets = new Insets(6, 0, 6, 0); + layout.addLayoutComponent(copyToClipboardButton, gbc); + return layout; + } + + @Override + public Dimension calculatePreferredSize() { + //TODO returns a Dimension that is either X wide, or as wide as necessary + //to show the title. It is Y high. + return new Dimension(iconLabel.getPreferredSize().width + errorMessage.getPreferredSize().width, 206); + } + + protected int getDetailsHeight() { + return 300; + } + + protected void configureReportAction(AbstractActionExt reportAction) { + reportAction.setName(UIManagerExt.getString(CLASS_NAME + ".report_button_text", pane.getLocale())); + } + + //----------------------------------------------- private helper methods + + /** + * Creates and returns a TransferHandler which can be used to copy the details + * from the details component. It also disallows pasting into the component, or + * cutting from the component. + * + * @return a TransferHandler for the details area + */ + private TransferHandler createDetailsTransferHandler(JTextComponent detailComponent) { + return new DetailsTransferHandler(detailComponent); + } + + /** + * @return the default error icon + */ + protected Icon getDefaultErrorIcon() { + try { + Icon icon = UIManager.getIcon(CLASS_NAME + ".errorIcon"); + return icon == null ? UIManager.getIcon("OptionPane.errorIcon") : icon; + } catch (Exception e) { + return null; + } + } + + /** + * @return the default warning icon + */ + protected Icon getDefaultWarningIcon() { + try { + Icon icon = UIManager.getIcon(CLASS_NAME + ".warningIcon"); + return icon == null ? UIManager.getIcon("OptionPane.warningIcon") : icon; + } catch (Exception e) { + return null; + } + } + + /** + * Set the details section of the error dialog. If the details are either + * null or an empty string, then hide the details button and hide the detail + * scroll pane. Otherwise, just set the details section. + * + * @param details Details to be shown in the detail section of the dialog. + * This can be null if you do not want to display the details section of the + * dialog. + */ + private void setDetails(String details) { + if (details == null || details.equals("")) { + detailButton.setVisible(false); + } else { + this.details.setText(details); + detailButton.setVisible(true); + } + } + + protected void configureDetailsButton(boolean expanded) { + if (expanded) { + detailButton.setText(UIManagerExt.getString( + CLASS_NAME + ".details_contract_text", detailButton.getLocale())); + } else { + detailButton.setText(UIManagerExt.getString( + CLASS_NAME + ".details_expand_text", detailButton.getLocale())); + } + } + + /** + * Set the details section to be either visible or invisible. Set the + * text of the Details button accordingly. + * @param b if true details section will be visible + */ + private void setDetailsVisible(boolean b) { + if (b) { + collapsedHeight = pane.getHeight(); + pane.setSize(pane.getWidth(), expandedHeight == 0 ? collapsedHeight + getDetailsHeight() : expandedHeight); + detailsPanel.setVisible(true); + configureDetailsButton(true); + detailsPanel.applyComponentOrientation(detailButton.getComponentOrientation()); + + // workaround for bidi bug, if the text is not set "again" and the component orientation has changed + // then the text won't be aligned correctly. To reproduce this (in JDK 1.5) show two dialogs in one + // use LTOR orientation and in the second use RTOL orientation and press "details" in both. + // Text in the text box should be aligned to right/left respectively, without this line this doesn't + // occure I assume because bidi properties are tested when the text is set and are not updated later + // on when setComponentOrientation is invoked. + details.setText(details.getText()); + details.setCaretPosition(0); + } else if (collapsedHeight != 0) { //only collapse if the dialog has been expanded + expandedHeight = pane.getHeight(); + detailsPanel.setVisible(false); + configureDetailsButton(false); + // Trick to force errorMessage JTextArea to resize according + // to its columns property. + errorMessage.setSize(0, 0); + errorMessage.setSize(errorMessage.getPreferredSize()); + pane.setSize(pane.getWidth(), collapsedHeight); + } else { + detailsPanel.setVisible(false); + } + + pane.doLayout(); + } + + /** + * Set the error message for the dialog box + * @param errorMessage Message for the error dialog + */ + private void setErrorMessage(String errorMessage) { + if(BasicHTML.isHTMLString(errorMessage)) { + this.errorMessage.setContentType("text/html"); + } else { + this.errorMessage.setContentType("text/plain"); + } + this.errorMessage.setText(errorMessage); + this.errorMessage.setCaretPosition(0); + } + + /** + * Reconfigures the dialog if settings have changed, such as the + * errorInfo, errorIcon, warningIcon, etc + */ + protected void reinit() { + setDetailsVisible(false); + Action reportAction = pane.getActionMap().get(JXErrorPane.REPORT_ACTION_KEY); + reportButton.setAction(reportAction); + reportButton.setVisible(reportAction != null && reportAction.isEnabled() && pane.getErrorReporter() != null); + reportButton.setEnabled(reportButton.isVisible()); + ErrorInfo errorInfo = pane.getErrorInfo(); + if (errorInfo == null) { + iconLabel.setIcon(pane.getIcon()); + setErrorMessage(""); + closeButton.setText(UIManagerExt.getString( + CLASS_NAME + ".ok_button_text", closeButton.getLocale())); + setDetails(""); + //TODO Does this ever happen? It seems like if errorInfo is null and + //this is called, it would be an IllegalStateException. + } else { + //change the "closeButton"'s text to either the default "ok"/"close" text + //or to the "fatal" text depending on the error level of the incident info + if (errorInfo.getErrorLevel() == ErrorLevel.FATAL) { + closeButton.setText(UIManagerExt.getString( + CLASS_NAME + ".fatal_button_text", closeButton.getLocale())); + } else { + closeButton.setText(UIManagerExt.getString( + CLASS_NAME + ".ok_button_text", closeButton.getLocale())); + } + + //if the icon for the pane has not been specified by the developer, + //then set it to the default icon based on the error level + Icon icon = pane.getIcon(); + if (icon == null || icon instanceof UIResource) { + if (errorInfo.getErrorLevel().intValue() <= Level.WARNING.intValue()) { + icon = getDefaultWarningIcon(); + } else { + icon = getDefaultErrorIcon(); + } + } + iconLabel.setIcon(icon); + setErrorMessage(errorInfo.getBasicErrorMessage()); + String details = errorInfo.getDetailedErrorMessage(); + if(details == null) { + details = getDetailsAsHTML(errorInfo); + } + setDetails(details); + } + } + + /** + * Creates and returns HTML representing the details of this incident info. This + * method is only called if the details needs to be generated: ie: the detailed + * error message property of the incident info is null. + */ + protected String getDetailsAsHTML(ErrorInfo errorInfo) { + if(errorInfo.getErrorException() != null) { + //convert the stacktrace into a more pleasent bit of HTML + StringBuffer html = new StringBuffer(""); + html.append("

      " + escapeXml(errorInfo.getTitle()) + "

      "); + html.append("
      "); + html.append("
      "); + html.append("Message:"); + html.append("
      ");
      +            html.append("    " + escapeXml(errorInfo.getErrorException().toString()));
      +            html.append("
      "); + html.append("Level:"); + html.append("
      ");
      +            html.append("    " + errorInfo.getErrorLevel());
      +            html.append("
      "); + html.append("Stack Trace:"); + Throwable ex = errorInfo.getErrorException(); + while(ex != null) { + html.append("

      "+ex.getMessage()+"

      "); + html.append("
      ");
      +                for (StackTraceElement el : ex.getStackTrace()) {
      +                    html.append("    " + el.toString().replace("", "<init>") + "\n");
      +                }
      +                html.append("
      "); + ex = ex.getCause(); + } + html.append(""); + return html.toString(); + } else { + return null; + } + } + + //------------------------------------------------ actions/inner classes + + /** + * Default action for closing the JXErrorPane's enclosing window + * (JDialog, JFrame, or JInternalFrame) + */ + private static final class CloseAction extends AbstractAction { + private Window w; + + /** + * @param w cannot be null + */ + private CloseAction(Window w) { + if (w == null) { + throw new NullPointerException("Window cannot be null"); + } + this.w = w; + } + + /** + * @inheritDoc + */ + @Override + public void actionPerformed(ActionEvent e) { + w.setVisible(false); + w.dispose(); + } + } + + + /** + * Listener for Details click events. Alternates whether the details section + * is visible or not. + * + * @author rbair + */ + private final class DetailsClickEvent implements ActionListener { + + /* (non-Javadoc) + * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) + */ + @Override + public void actionPerformed(ActionEvent e) { + setDetailsVisible(!detailsPanel.isVisible()); + } + } + + private final class ResizeWindow implements ActionListener { + private Window w; + private ResizeWindow(Window w) { + if (w == null) { + throw new NullPointerException(); + } + this.w = w; + } + + @Override + public void actionPerformed(ActionEvent ae) { + Dimension contentSize = null; + if (w instanceof JDialog) { + contentSize = ((JDialog)w).getContentPane().getSize(); + } else { + contentSize = ((JFrame)w).getContentPane().getSize(); + } + + Dimension dialogSize = w.getSize(); + int ydiff = dialogSize.height - contentSize.height; + Dimension paneSize = pane.getSize(); + w.setSize(new Dimension(dialogSize.width, paneSize.height + ydiff)); + w.validate(); + w.repaint(); + } + } + + /** + * This is a button that maintains the size of the largest button in the button + * group by returning the largest size from the getPreferredSize method. + * This is better than using setPreferredSize since this will work regardless + * of changes to the text of the button and its language. + */ + private static final class EqualSizeJButton extends JButton { + + public EqualSizeJButton(String text) { + super(text); + } + + public EqualSizeJButton(Action a) { + super(a); + } + + /** + * Buttons whose size should be taken into consideration + */ + private EqualSizeJButton[] group; + + public void setGroup(EqualSizeJButton[] group) { + this.group = group; + } + + /** + * Returns the actual preferred size on a different instance of this button + */ + private Dimension getRealPreferredSize() { + return super.getPreferredSize(); + } + + /** + * If the preferredSize has been set to a + * non-null value just returns it. + * If the UI delegate's getPreferredSize + * method returns a non null value then return that; + * otherwise defer to the component's layout manager. + * + * @return the value of the preferredSize property + * @see #setPreferredSize + * @see ComponentUI + */ + @Override + public Dimension getPreferredSize() { + int width = 0; + int height = 0; + for(int iter = 0 ; iter < group.length ; iter++) { + Dimension size = group[iter].getRealPreferredSize(); + width = Math.max(size.width, width); + height = Math.max(size.height, height); + } + + return new Dimension(width, height); + } + + } + + /** + * Returns the text as non-HTML in a COPY operation, and disabled CUT/PASTE + * operations for the Details pane. + */ + private static final class DetailsTransferHandler extends TransferHandler { + private JTextComponent details; + private DetailsTransferHandler(JTextComponent detailComponent) { + if (detailComponent == null) { + throw new NullPointerException("detail component cannot be null"); + } + this.details = detailComponent; + } + + @Override + protected Transferable createTransferable(JComponent c) { + String text = details.getSelectedText(); + if (text == null || text.equals("")) { + details.selectAll(); + text = details.getSelectedText(); + details.select(-1, -1); + } + return new StringSelection(text); + } + + @Override + public int getSourceActions(JComponent c) { + return TransferHandler.COPY; + } + + } + + private final class JXErrorDialog extends JDialog { + public JXErrorDialog(Frame parent, JXErrorPane p) { + super(parent, true); + init(p); + } + + public JXErrorDialog(Dialog parent, JXErrorPane p) { + super(parent, true); + init(p); + } + + protected void init(JXErrorPane p) { + // FYI: info can be null + setTitle(p.getErrorInfo() == null ? null : p.getErrorInfo().getTitle()); + initWindow(this, p); + } + } + + private final class JXErrorFrame extends JFrame { + public JXErrorFrame(JXErrorPane p) { + setTitle(p.getErrorInfo().getTitle()); + initWindow(this, p); + } + } + + private final class JXInternalErrorFrame extends JInternalFrame { + public JXInternalErrorFrame(JXErrorPane p) { + setTitle(p.getErrorInfo().getTitle()); + + setLayout(new BorderLayout()); + add(p, BorderLayout.CENTER); + final Action closeAction = new AbstractAction() { + @Override + public void actionPerformed(ActionEvent evt) { + setVisible(false); + dispose(); + } + }; + closeButton.addActionListener(closeAction); + addComponentListener(new ComponentAdapter() { + @Override + public void componentHidden(ComponentEvent e) { + //remove the action listener + closeButton.removeActionListener(closeAction); + exitIfFatal(); + } + }); + + getRootPane().setDefaultButton(closeButton); + setResizable(false); + setDefaultCloseOperation(JInternalFrame.DISPOSE_ON_CLOSE); + KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); + getRootPane().registerKeyboardAction(closeAction, ks, JComponent.WHEN_IN_FOCUSED_WINDOW); + //setPreferredSize(calculatePreferredDialogSize()); + } + } + + /** + * Utility method for initializing a Window for displaying a JXErrorPane. + * This is particularly useful because the differences between JFrame and + * JDialog are so minor. + * removed. + */ + private void initWindow(final Window w, final JXErrorPane pane) { + w.setLayout(new BorderLayout()); + w.add(pane, BorderLayout.CENTER); + final Action closeAction = new CloseAction(w); + closeButton.addActionListener(closeAction); + final ResizeWindow resizeListener = new ResizeWindow(w); + //make sure this action listener is last (or, oddly, the first in the list) + ActionListener[] list = detailButton.getActionListeners(); + for (ActionListener a : list) { + detailButton.removeActionListener(a); + } + detailButton.addActionListener(resizeListener); + for (ActionListener a : list) { + detailButton.addActionListener(a); + } + + if (w instanceof JFrame) { + final JFrame f = (JFrame)w; + f.getRootPane().setDefaultButton(closeButton); + f.setResizable(true); + f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); + f.getRootPane().registerKeyboardAction(closeAction, ks, JComponent.WHEN_IN_FOCUSED_WINDOW); + } else if (w instanceof JDialog) { + final JDialog d = (JDialog)w; + d.getRootPane().setDefaultButton(closeButton); + d.setResizable(true); + d.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); + d.getRootPane().registerKeyboardAction(closeAction, ks, JComponent.WHEN_IN_FOCUSED_WINDOW); + } + + w.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + //remove the action listener + closeButton.removeActionListener(closeAction); + detailButton.removeActionListener(resizeListener); + exitIfFatal(); + } + }); + w.pack(); + } + + private void exitIfFatal() { + ErrorInfo info = pane.getErrorInfo(); + // FYI: info can be null + if (info != null && info.getErrorLevel() == ErrorLevel.FATAL) { + Action fatalAction = pane.getActionMap().get(JXErrorPane.FATAL_ACTION_KEY); + if (fatalAction == null) { + System.exit(1); + } else { + ActionEvent ae = new ActionEvent(closeButton, -1, "fatal"); + fatalAction.actionPerformed(ae); + } + } + } + + private final class ErrorPaneListener implements PropertyChangeListener { + @Override + public void propertyChange(PropertyChangeEvent evt) { + reinit(); + } + } + + /** + * Lays out the BasicErrorPaneUI components. + */ + private final class ErrorPaneLayout implements LayoutManager { + private JEditorPane dummy = new JEditorPane(); + + @Override + public void addLayoutComponent(String name, Component comp) {} + @Override + public void removeLayoutComponent(Component comp) {} + + /** + * The preferred size is: + * The width of the parent container + * The height necessary to show the entire message text + * (as long as said height does not go off the screen) + * plus the buttons + * + * The preferred height changes depending on whether the details + * are visible, or not. + */ + @Override + public Dimension preferredLayoutSize(Container parent) { + int prefWidth = parent.getWidth(); + int prefHeight = parent.getHeight(); + final Insets insets = parent.getInsets(); + int pw = detailButton.isVisible() ? detailButton.getPreferredSize().width : 0; + pw += detailButton.isVisible() ? detailButton.getPreferredSize().width : 0; + pw += reportButton.isVisible() ? (5 + reportButton.getPreferredSize().width) : 0; + pw += closeButton.isVisible() ? (5 + closeButton.getPreferredSize().width) : 0; + prefWidth = Math.max(prefWidth, pw) + insets.left + insets.right; + if (errorMessage != null) { + //set a temp editor to a certain size, just to determine what its + //pref height is + dummy.setContentType(errorMessage.getContentType()); + dummy.setEditorKit(errorMessage.getEditorKit()); + dummy.setText(errorMessage.getText()); + dummy.setSize(prefWidth, 20); + int errorMessagePrefHeight = dummy.getPreferredSize().height; + + prefHeight = + //the greater of the error message height or the icon height + Math.max(errorMessagePrefHeight, iconLabel.getPreferredSize().height) + + //the space between the error message and the button + 10 + + //the button preferred height + closeButton.getPreferredSize().height; + + if (detailsPanel.isVisible()) { + prefHeight += getDetailsHeight(); + } + + } + + if (iconLabel != null && iconLabel.getIcon() != null) { + prefWidth += iconLabel.getIcon().getIconWidth(); + prefHeight += 10; // top of icon is positioned 10px above the text + } + + return new Dimension( + prefWidth + insets.left + insets.right, + prefHeight + insets.top + insets.bottom); + } + + @Override + public Dimension minimumLayoutSize(Container parent) { + return preferredLayoutSize(parent); + } + + @Override + public void layoutContainer(Container parent) { + final Insets insets = parent.getInsets(); + int x = insets.left; + int y = insets.top; + + //place the icon + if (iconLabel != null) { + Dimension dim = iconLabel.getPreferredSize(); + iconLabel.setBounds(x, y, dim.width, dim.height); + x += dim.width + 17; + int leftEdge = x; + + //place the error message + dummy.setContentType(errorMessage.getContentType()); + dummy.setText(errorMessage.getText()); + dummy.setSize(parent.getWidth() - leftEdge - insets.right, 20); + dim = dummy.getPreferredSize(); + int spx = x; + int spy = y; + Dimension spDim = new Dimension (parent.getWidth() - leftEdge - insets.right, dim.height); + y += dim.height + 10; + int rightEdge = parent.getWidth() - insets.right; + x = rightEdge; + dim = detailButton.getPreferredSize(); //all buttons should be the same height! + int buttonY = y + 5; + if (detailButton.isVisible()) { + dim = detailButton.getPreferredSize(); + x -= dim.width; + detailButton.setBounds(x, buttonY, dim.width, dim.height); + } + if (detailButton.isVisible()) { + detailButton.setBounds(x, buttonY, dim.width, dim.height); + } + errorScrollPane.setBounds(spx, spy, spDim.width, buttonY - spy); + if (reportButton.isVisible()) { + dim = reportButton.getPreferredSize(); + x -= dim.width; + x -= 5; + reportButton.setBounds(x, buttonY, dim.width, dim.height); + } + + dim = closeButton.getPreferredSize(); + x -= dim.width; + x -= 5; + closeButton.setBounds(x, buttonY, dim.width, dim.height); + + //if the dialog is expanded... + if (detailsPanel.isVisible()) { + //layout the details + y = buttonY + dim.height + 6; + x = leftEdge; + int width = rightEdge - x; + detailsPanel.setBounds(x, y, width, parent.getHeight() - (y + insets.bottom) ); + } + } + } + } + + private static void centerWindow(Window w, Component owner) { + //center based on the owner component, if it is not null + //otherwise, center based on the center of the screen + if (owner != null) { + Point p = owner.getLocation(); + p.x += owner.getWidth()/2; + p.y += owner.getHeight()/2; + SwingUtilities.convertPointToScreen(p, owner); + w.setLocation(p); + } else { + w.setLocation(WindowUtils.getPointForCentering(w)); + } + } + + private static void centerWindow(JInternalFrame w, Component owner) { + //center based on the owner component, if it is not null + //otherwise, center based on the center of the screen + if (owner != null) { + Point p = owner.getLocation(); + p.x += owner.getWidth()/2; + p.y += owner.getHeight()/2; + SwingUtilities.convertPointToScreen(p, owner); + w.setLocation(p); + } else { + w.setLocation(WindowUtils.getPointForCentering(w)); + } + } + + /** + * Converts the incoming string to an escaped output string. This method + * is far from perfect, only escaping <, > and & characters + */ + private static String escapeXml(String input) { + String s = input == null ? "" : input.replace("&", "&"); + s = s.replace("<", "<"); + return s = s.replace(">", ">"); + } +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicHeaderUI.java b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicHeaderUI.java new file mode 100644 index 0000000000..85edcccfb0 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicHeaderUI.java @@ -0,0 +1,454 @@ +/* + * $Id: BasicHeaderUI.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.plaf.basic; + +import org.jdesktop.swingx.JXHeader; +import org.jdesktop.swingx.JXHeader.IconPosition; +import org.jdesktop.swingx.JXLabel; +import org.jdesktop.swingx.JXLabel.MultiLineSupport; +import org.jdesktop.swingx.painter.MattePainter; +import org.jdesktop.swingx.painter.Painter; +import org.jdesktop.swingx.plaf.HeaderUI; +import org.jdesktop.swingx.plaf.PainterUIResource; +import org.jdesktop.swingx.plaf.UIManagerExt; + +import javax.swing.*; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.UIResource; +import javax.swing.plaf.basic.BasicHTML; +import javax.swing.text.View; +import java.awt.*; +import java.awt.event.HierarchyBoundsAdapter; +import java.awt.event.HierarchyBoundsListener; +import java.awt.event.HierarchyEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.logging.Logger; + +/** + * Base implementation of Header UI.

      + * + * PENDING JW: This implementation is unusual in that it does not keep a reference + * to the component it controls. Typically, such is only the case if the ui is + * shared between instances. Historical? A consequence is that the un/install methods + * need to carry the header as parameter. Which looks funny when at the same time + * the children of the header are instance fields in this. Should think about cleanup: + * either get rid off the instance fields here, or reference the header and remove + * the param (would break subclasses).

      + * + * PENDING JW: keys for uidefaults are inconsistent - most have prefix "JXHeader." while + * defaultIcon has prefix "Header."

      + * + * @author rbair + * @author rah003 + * @author Jeanette Winzenburg + */ +public class BasicHeaderUI extends HeaderUI { + @SuppressWarnings("unused") + private static final Logger LOG = Logger.getLogger(BasicHeaderUI.class + .getName()); + // Implementation detail. Neeeded to expose getMultiLineSupport() method to allow restoring view + // lost after LAF switch + protected class DescriptionPane extends JXLabel { + @Override + public void paint(Graphics g) { + // switch off jxlabel default antialiasing + // JW: that cost me dearly to track down - it's the default foreground painter + // which is an AbstractPainter which has _global_ antialiased on by default + // and here the _text_ antialiased is turned off + // changed JXLabel default foregroundPainter to have antialiasing false by + // default, so remove interference here + // part of fix for #920 - the other part is in JXLabel, fix 1164 +// ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, +// RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); + super.paint(g); + } + + @Override + public MultiLineSupport getMultiLineSupport() { + return super.getMultiLineSupport(); + } + } + + protected JLabel titleLabel; + protected DescriptionPane descriptionPane; + protected JLabel imagePanel; + private PropertyChangeListener propListener; + private HierarchyBoundsListener boundsListener; + private Color gradientLightColor; + private Color gradientDarkColor; + + /** Creates a new instance of BasicHeaderUI */ + public BasicHeaderUI() { + } + + /** + * Returns an instance of the UI delegate for the specified component. + * Each subclass must provide its own static createUI + * method that returns an instance of that UI delegate subclass. + * If the UI delegate subclass is stateless, it may return an instance + * that is shared by multiple components. If the UI delegate is + * stateful, then it should return a new instance per component. + * The default implementation of this method throws an error, as it + * should never be invoked. + */ + public static ComponentUI createUI(JComponent c) { + return new BasicHeaderUI(); + } + + /** + * Configures the specified component appropriate for the look and feel. + * This method is invoked when the ComponentUI instance is being installed + * as the UI delegate on the specified component. This method should + * completely configure the component for the look and feel, + * including the following: + *

        + *
      1. Install any default property values for color, fonts, borders, + * icons, opacity, etc. on the component. Whenever possible, + * property values initialized by the client program should not + * be overridden. + *
      2. Install a LayoutManager on the component if necessary. + *
      3. Create/add any required sub-components to the component. + *
      4. Create/install event listeners on the component. + *
      5. Create/install a PropertyChangeListener on the component in order + * to detect and respond to component property changes appropriately. + *
      6. Install keyboard UI (mnemonics, traversal, etc.) on the component. + *
      7. Initialize any appropriate instance data. + *
      + * @param c the component where this UI delegate is being installed + * + * @see #uninstallUI + * @see JComponent#setUI + * @see JComponent#updateUI + */ + @Override + public void installUI(JComponent c) { + super.installUI(c); + assert c instanceof JXHeader; + JXHeader header = (JXHeader)c; + + installDefaults(header); + installComponents(header); + installListeners(header); + } + + /** + * Reverses configuration which was done on the specified component during + * installUI. This method is invoked when this + * UIComponent instance is being removed as the UI delegate + * for the specified component. This method should undo the + * configuration performed in installUI, being careful to + * leave the JComponent instance in a clean state (no + * extraneous listeners, look-and-feel-specific property objects, etc.). + * This should include the following: + *
        + *
      1. Remove any UI-set borders from the component. + *
      2. Remove any UI-set layout managers on the component. + *
      3. Remove any UI-added sub-components from the component. + *
      4. Remove any UI-added event/property listeners from the component. + *
      5. Remove any UI-installed keyboard UI from the component. + *
      6. Nullify any allocated instance data objects to allow for GC. + *
      + * @param c the component from which this UI delegate is being removed; + * this argument is often ignored, + * but might be used if the UI object is stateless + * and shared by multiple components + * + * @see #installUI + * @see JComponent#updateUI + */ + @Override + public void uninstallUI(JComponent c) { + assert c instanceof JXHeader; + JXHeader header = (JXHeader)c; + + uninstallListeners(header); + uninstallComponents(header); + uninstallDefaults(header); + } + + /** + * Installs default header properties. + *

      + * + * NOTE: this method is called before the children are created, so must not + * try to access any of those!. + * + * @param header the header to install. + */ + protected void installDefaults(JXHeader header) { + gradientLightColor = UIManagerExt.getColor("JXHeader.startBackground"); + if (gradientLightColor == null) { + // fallback to white + gradientLightColor = Color.WHITE; + } + gradientDarkColor = UIManagerExt.getColor("JXHeader.background"); + // for backwards compatibility (mostly for substance and synthetica, + // I suspect) I'll fall back on the "control" color if + // JXHeader.background + // isn't specified. + if (gradientDarkColor == null) { + gradientDarkColor = UIManagerExt.getColor("control"); + } + + if (isUIInstallable(header.getBackgroundPainter())) { + header.setBackgroundPainter(createBackgroundPainter()); + } + + // title properties + if (isUIInstallable(header.getTitleFont())) { + Font titleFont = UIManager.getFont("JXHeader.titleFont"); + // fallback to label font + header.setTitleFont(titleFont != null ? titleFont : UIManager + .getFont("Label.font")); + } + if (isUIInstallable(header.getTitleForeground())) { + Color titleForeground = UIManagerExt + .getColor("JXHeader.titleForeground"); + // fallback to label foreground + header.setTitleForeground(titleForeground != null ? titleForeground + : UIManagerExt.getColor("Label.foreground")); + } + + // description properties + if (isUIInstallable(header.getDescriptionFont())) { + Font descFont = UIManager.getFont("JXHeader.descriptionFont"); + // fallback to label font + header.setDescriptionFont(descFont != null ? descFont : UIManager + .getFont("Label.font")); + } + if (isUIInstallable(header.getDescriptionForeground())) { + Color descForeground = UIManagerExt + .getColor("JXHeader.descriptionForeground"); + // fallback to label foreground + header.setDescriptionForeground(descForeground != null ? descForeground + : UIManagerExt.getColor("Label.foreground")); + } + + // icon label properties + if (isUIInstallable(header.getIcon())) { + header.setIcon(UIManager.getIcon("Header.defaultIcon")); + } + } + + /** + * Uninstalls the given header's default properties. This implementation + * does nothing. + * + * @param h the header to ininstall the properties from. + */ + protected void uninstallDefaults(JXHeader h) { + } + + /** + * Creates, configures, adds contained components. + * PRE: header's default properties must be set before calling this. + * + * @param header the header to install the components into. + */ + protected void installComponents(JXHeader header) { + titleLabel = new JLabel(); + descriptionPane = new DescriptionPane(); + imagePanel = new JLabel(); + installComponentDefaults(header); + header.setLayout(new GridBagLayout()); + resetLayout(header); + } + + /** + * Unconfigures, removes and nulls contained components. + * + * @param header the header to install the components into. + */ + protected void uninstallComponents(JXHeader header) { + uninstallComponentDefaults(header); + header.remove(titleLabel); + header.remove(descriptionPane); + header.remove(imagePanel); + titleLabel = null; + descriptionPane = null; + imagePanel = null; + } + + /** + * Configures the component default properties from the given header. + * + * @param header the header to install the components into. + */ + protected void installComponentDefaults(JXHeader header) { + // JW: force a not UIResource for properties which have ui default values + // like color, font, ?? + titleLabel.setFont(getAsNotUIResource(header.getTitleFont())); + titleLabel.setForeground(getAsNotUIResource(header.getTitleForeground())); + titleLabel.setText(header.getTitle()); + descriptionPane.setFont(getAsNotUIResource(header.getDescriptionFont())); + descriptionPane.setForeground(getAsNotUIResource(header.getDescriptionForeground())); + descriptionPane.setOpaque(false); + descriptionPane.setText(header.getDescription()); + descriptionPane.setLineWrap(true); + + imagePanel.setIcon(header.getIcon()); + + } + + /** + * Returns a Font based on the param which is not of type UIResource. + * + * @param font the base font + * @return a font not of type UIResource, may be null. + */ + private Font getAsNotUIResource(Font font) { + if (!(font instanceof UIResource)) return font; + // PENDING JW: correct way to create another font instance? + return font.deriveFont(font.getAttributes()); + } + + /** + * Returns a Color based on the param which is not of type UIResource. + * + * @param color the base color + * @return a color not of type UIResource, may be null. + */ + private Color getAsNotUIResource(Color color) { + if (!(color instanceof UIResource)) return color; + // PENDING JW: correct way to create another color instance? + float[] rgb = color.getRGBComponents(null); + return new Color(rgb[0], rgb[1], rgb[2], rgb[3]); + } + + /** + * Checks and returns whether the given property should be replaced + * by the UI's default value.

      + * + * PENDING JW: move as utility method ... where? + * + * @param property the property to check. + * @return true if the given property should be replaced by the UI#s + * default value, false otherwise. + */ + private boolean isUIInstallable(Object property) { + return (property == null) || (property instanceof UIResource); + } + + /** + * Uninstalls component defaults. This implementation does nothing. + * + * @param header the header to uninstall from. + */ + protected void uninstallComponentDefaults(JXHeader header) { + } + + + protected void installListeners(final JXHeader header) { + propListener = new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + onPropertyChange(header, evt.getPropertyName(), evt.getOldValue(), evt.getNewValue()); + } + }; + boundsListener = new HierarchyBoundsAdapter() { + @Override + public void ancestorResized(HierarchyEvent e) { + if (header == e.getComponent()) { + View v = (View) descriptionPane.getClientProperty(BasicHTML.propertyKey); + // view might get lost on LAF change ... + if (v == null) { + descriptionPane.putClientProperty(BasicHTML.propertyKey, + MultiLineSupport.createView(descriptionPane)); + v = (View) descriptionPane.getClientProperty(BasicHTML.propertyKey); + } + if (v != null) { + Container tla = header.getTopLevelAncestor(); + if (tla == null) { + tla = header.getParent(); + while (tla.getParent() != null) { + tla = tla.getParent(); + } + } + int h = Math.max(descriptionPane.getHeight(), tla.getHeight()); + int w = Math.min(tla.getWidth(), header.getParent().getWidth()); + // 35 = description pane insets, TODO: obtain dynamically + w -= 35 + header.getInsets().left + header.getInsets().right + descriptionPane.getInsets().left + descriptionPane.getInsets().right + imagePanel.getInsets().left + imagePanel.getInsets().right + imagePanel.getWidth() + descriptionPane.getBounds().x; + v.setSize(w, h); + descriptionPane.setSize(w, (int) Math.ceil(v.getPreferredSpan(View.Y_AXIS))); + } + } + }}; + header.addPropertyChangeListener(propListener); + header.addHierarchyBoundsListener(boundsListener); + } + + protected void uninstallListeners(JXHeader h) { + h.removePropertyChangeListener(propListener); + h.removeHierarchyBoundsListener(boundsListener); + } + + protected void onPropertyChange(JXHeader h, String propertyName, Object oldValue, final Object newValue) { + if ("title".equals(propertyName)) { + titleLabel.setText(h.getTitle()); + } else if ("description".equals(propertyName)) { + descriptionPane.setText(h.getDescription()); + } else if ("icon".equals(propertyName)) { + imagePanel.setIcon(h.getIcon()); + } else if ("enabled".equals(propertyName)) { + boolean enabled = h.isEnabled(); + titleLabel.setEnabled(enabled); + descriptionPane.setEnabled(enabled); + imagePanel.setEnabled(enabled); + } else if ("titleFont".equals(propertyName)) { + titleLabel.setFont((Font)newValue); + } else if ("descriptionFont".equals(propertyName)) { + descriptionPane.setFont((Font)newValue); + } else if ("titleForeground".equals(propertyName)) { + titleLabel.setForeground((Color)newValue); + } else if ("descriptionForeground".equals(propertyName)) { + descriptionPane.setForeground((Color)newValue); + } else if ("iconPosition".equals(propertyName)) { + resetLayout(h); + } + } + + private void resetLayout(JXHeader h) { + h.remove(titleLabel); + h.remove(descriptionPane); + h.remove(imagePanel); + if (h.getIconPosition() == null || h.getIconPosition() == IconPosition.RIGHT) { + h.add(titleLabel, new GridBagConstraints(0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(12, 12, 0, 11), 0, 0)); + h.add(descriptionPane, new GridBagConstraints(0, 1, 1, 1, 1.0, 1.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.BOTH, new Insets(0, 24, 12, 11), 0, 0)); + h.add(imagePanel, new GridBagConstraints(1, 0, 1, 2, 0.0, 1.0, GridBagConstraints.FIRST_LINE_END, GridBagConstraints.NONE, new Insets(12, 0, 11, 11), 0, 0)); + } else { + h.add(titleLabel, new GridBagConstraints(1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(12, 12, 0, 11), 0, 0)); + h.add(descriptionPane, new GridBagConstraints(1, 1, 1, 1, 1.0, 1.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.BOTH, new Insets(0, 24, 12, 11), 0, 0)); + h.add(imagePanel, new GridBagConstraints(0, 0, 1, 2, 0.0, 1.0, GridBagConstraints.FIRST_LINE_END, GridBagConstraints.NONE, new Insets(12, 11, 0, 11), 0, 0)); + } + } + + + + protected Painter createBackgroundPainter() { + MattePainter p = new MattePainter(new GradientPaint(0, 0, gradientLightColor, 1, 0, gradientDarkColor)); + p.setPaintStretched(true); + return new PainterUIResource(p); + } + + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicHyperlinkUI.java b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicHyperlinkUI.java new file mode 100644 index 0000000000..ac94f78d31 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicHyperlinkUI.java @@ -0,0 +1,869 @@ +/* + * $Id: BasicHyperlinkUI.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf.basic; + +import org.jdesktop.swingx.JXHyperlink; +import org.jdesktop.swingx.SwingXUtilities; + +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.plaf.BorderUIResource; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicButtonListener; +import javax.swing.plaf.basic.BasicButtonUI; +import javax.swing.plaf.basic.BasicGraphicsUtils; +import javax.swing.plaf.basic.BasicHTML; +import javax.swing.text.*; +import javax.swing.text.html.HTMLDocument; +import javax.swing.text.html.HTMLEditorKit; +import javax.swing.text.html.ImageView; +import javax.swing.text.html.StyleSheet; +import java.awt.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.Reader; +import java.io.StringReader; +import java.net.URL; +import java.util.logging.Logger; + +/** + * Basic implementation of the JXHyperlink UI.
      + * This is copied from org.jdesktop.jdnc.plaf.basic.BasicLinkButtonUI + */ +public class BasicHyperlinkUI extends BasicButtonUI { + + @SuppressWarnings("unused") + private static final Logger LOG = Logger.getLogger(BasicHyperlinkUI.class + .getName()); + + public static ComponentUI createUI(JComponent c) { + return new BasicHyperlinkUI(); + } + + private static Rectangle viewRect = new Rectangle(); + + private static Rectangle textRect = new Rectangle(); + + private static Rectangle iconRect = new Rectangle(); + +// private static MouseListener handCursorListener = new HandCursor(); + + protected int dashedRectGapX; + + protected int dashedRectGapY; + + protected int dashedRectGapWidth; + + protected int dashedRectGapHeight; + + private Color focusColor; + + private View ulv; + + private PropertyChangeListener pcListener = new PropertyChangeListener() { + + @Override + public void propertyChange(PropertyChangeEvent evt) { + // this method is called from the edt. only other place where ulv is used is in + // painting which also happens on edt so it should be safe even without synchronization + // sole purpose of this call is to reinitialize view on every property change + ulv = null; + }}; + + @Override + protected void installDefaults(AbstractButton b) { + super.installDefaults(b); + + JXHyperlink link = (JXHyperlink) b; + + LookAndFeel.installProperty(b, "opaque", false); + + if (SwingXUtilities.isUIInstallable(link.getUnclickedColor())) { + link.setUnclickedColor(UIManager.getColor("Hyperlink.linkColor")); + } + + if (SwingXUtilities.isUIInstallable(link.getClickedColor())) { + link.setClickedColor(UIManager.getColor("Hyperlink.visitedColor")); + } + + b.setBorderPainted(false); + b.setRolloverEnabled(true); + + if (SwingXUtilities.isUIInstallable(b.getBorder())) { + b.setBorder(new BorderUIResource(BorderFactory.createEmptyBorder(0, 1, 0, 0))); + } + + dashedRectGapX = UIManager.getInt("ButtonUI.dashedRectGapX"); + dashedRectGapY = UIManager.getInt("ButtonUI.dashedRectGapY"); + dashedRectGapWidth = UIManager.getInt("ButtonUI.dashedRectGapWidth"); + dashedRectGapHeight = UIManager.getInt("ButtonUI.dashedRectGapHeight"); + focusColor = UIManager.getColor("ButtonUI.focus"); + + b.setHorizontalAlignment(SwingConstants.LEADING); + } + + @Override + protected void installListeners(AbstractButton b) { + super.installListeners(b); +// b.addMouseListener(handCursorListener); + b.addPropertyChangeListener(pcListener); + } + + @Override + protected void uninstallListeners(AbstractButton b) { + super.uninstallListeners(b); +// b.removeMouseListener(handCursorListener); + b.removePropertyChangeListener(pcListener); + } + + protected Color getFocusColor() { + return focusColor; + } + + @Override + public void paint(Graphics g, JComponent c) { + AbstractButton b = (AbstractButton) c; + ButtonModel model = b.getModel(); + + FontMetrics fm = g.getFontMetrics(); + + Insets i = c.getInsets(); + + viewRect.x = i.left; + viewRect.y = i.top; + viewRect.width = b.getWidth() - (i.right + viewRect.x); + viewRect.height = b.getHeight() - (i.bottom + viewRect.y); + + textRect.x = textRect.y = textRect.width = textRect.height = 0; + iconRect.x = iconRect.y = iconRect.width = iconRect.height = 0; + + Font f = c.getFont(); + g.setFont(f); + + // layout the text and icon + String text = SwingUtilities.layoutCompoundLabel(c, fm, b.getText(), b + .getIcon(), b.getVerticalAlignment(), b + .getHorizontalAlignment(), b.getVerticalTextPosition(), b + .getHorizontalTextPosition(), viewRect, iconRect, textRect, b + .getText() == null ? 0 : b.getIconTextGap()); + + clearTextShiftOffset(); + + // perform UI specific press action, e.g. Windows L&F shifts text + if (model.isArmed() && model.isPressed()) { + paintButtonPressed(g, b); + } + + // Paint the Icon + if (b.getIcon() != null) { + paintIcon(g, c, iconRect); + } + +// Composite oldComposite = ((Graphics2D) g).getComposite(); +// +// if (model.isRollover()) { +// ((Graphics2D) g).setComposite(AlphaComposite.getInstance( +// AlphaComposite.SRC_OVER, 0.5f)); +// } + + if (text != null && !text.equals("")) { + View v = (View) c.getClientProperty(BasicHTML.propertyKey); + if (v != null) { + paintHTMLText(g, b, textRect, text, v); + } else { + paintText(g, b, textRect, text); + } + } + + if (b.isFocusPainted() && b.hasFocus()) { + // paint UI specific focus + paintFocus(g, b, viewRect, textRect, iconRect); + } + +// ((Graphics2D) g).setComposite(oldComposite); + } + + /** + * Method which renders the text of the current button if html. + *

      + * @param g Graphics context + * @param b Current button to render + * @param textRect Bounding rectangle to render the text. + * @param text String to render + * @param v the View to use. + */ + protected void paintHTMLText(Graphics g, AbstractButton b, + Rectangle textRect, String text, View v) { + textRect.x += getTextShiftOffset(); + textRect.y += getTextShiftOffset(); + // fix #441-swingx - underline not painted for html + if (b.getModel().isRollover()) { + //paintUnderline(g, b, textRect, text); + if (ulv == null) { + ulv = ULHtml.createHTMLView(b, text); + } + ulv.paint(g, textRect); + } else { + v.paint(g, textRect); + } + textRect.x -= getTextShiftOffset(); + textRect.y -= getTextShiftOffset(); + } + + /** + * {@inheritDoc}

      + * Overridden to paint the underline on rollover. + */ + @Override + protected void paintText(Graphics g, AbstractButton b, Rectangle textRect, + String text) { + //kgs -- SwingX #415: pixel-shift when disabled + //BasicButtonUI shifts disabled text to the left by 1 pixel + //we compensate for that here, so that all Hyperlinks paint + //at the same location regardless of state + if (!b.getModel().isEnabled()) { + textRect.x += 1; + } + + super.paintText(g, b, textRect, text); + if (b.getModel().isRollover()) { + paintUnderline(g, b, textRect, text); + } + } + + private void paintUnderline(Graphics g, AbstractButton b, Rectangle rect, + String text) { + // JW: copied from JXTable.LinkRenderer + FontMetrics fm = g.getFontMetrics(); + int descent = fm.getDescent(); + + // REMIND(aim): should we be basing the underline on + // the font's baseline instead of the text bounds? + g.drawLine(rect.x + getTextShiftOffset(), + (rect.y + rect.height) - descent + 1 + getTextShiftOffset(), + rect.x + rect.width + getTextShiftOffset(), + (rect.y + rect.height) - descent + 1 + getTextShiftOffset()); + } + + @Override + protected void paintFocus(Graphics g, AbstractButton b, Rectangle viewRect, + Rectangle textRect, Rectangle iconRect) { + if (b.getParent() instanceof JToolBar) { + // Windows doesn't draw the focus rect for buttons in a toolbar. + return; + } + + // focus painted same color as text + g.setColor(getFocusColor()); + // paint the focus rect around the union of text rect and icon rect + // PENDING JW: duplicated to handle insets + Rectangle iconTextRect = getIconTextRect(b); + // PENDING JW: better factor handling of insets - the bare union doesn't respect insets +// Rectangle iconTextRect = textRect.union(iconRect); + BasicGraphicsUtils.drawDashedRect(g, iconTextRect.x, iconTextRect.y, + iconTextRect.width, iconTextRect.height); + // pre-#167-swingx: active area too large +// int width = b.getWidth(); +// int height = b.getHeight(); +// BasicGraphicsUtils.drawDashedRect(g, dashedRectGapX, dashedRectGapY, +// width - dashedRectGapWidth, height - dashedRectGapHeight); + } + + @Override + protected void paintButtonPressed(Graphics g, AbstractButton b) { + setTextShiftOffset(); + } + + + @Override + protected BasicButtonListener createButtonListener(AbstractButton b) { + return new BasicHyperlinkListener(b); + } + + /** + * {@inheritDoc}

      + * + * Overridden to return true if the position is inside the union of the + * text and icon rectangle, false otherwise. + */ + @Override + public boolean contains(JComponent c, int x, int y) { + AbstractButton button = (AbstractButton) c; + return isInside(getIconTextRect(button), x, y); + } + + /** + * @param iconTextRect + * @param point + * @return + */ + private boolean isInside(Rectangle iconTextRect, int x, int y) { + if (iconTextRect == null) return false; + return iconTextRect.contains(x, y); + } + + /** + * C&p'ed from BasicGraphicsUtils (getPreferredButtonSize). + * + * @param b the button to analyse. + * @return the union of the text and icon rectangle of the AbstractButton + * or null if the button has children (??) + */ + protected Rectangle getIconTextRect(AbstractButton b) { + if (b.getComponentCount() > 0) { + return null; + } + + Icon icon = b.getIcon(); + String text = b.getText(); + + Font font = b.getFont(); + FontMetrics fm = b.getFontMetrics(font); + + Rectangle iconR = new Rectangle(); + Rectangle textR = new Rectangle(); + Rectangle viewR = new Rectangle(b.getSize()); + + SwingUtilities.layoutCompoundLabel(b, fm, text, icon, + b.getVerticalAlignment(), b.getHorizontalAlignment(), b + .getVerticalTextPosition(), b + .getHorizontalTextPosition(), viewR, iconR, textR, + (text == null ? 0 : b.getIconTextGap())); + + /* + * The preferred size of the button is the size of the text and icon + * rectangles plus the buttons insets. + */ + + Rectangle r = iconR.union(textR); + + Insets insets = b.getInsets(); + r.width += insets.left + insets.right; + r.height += insets.top + insets.bottom; + // PENDING JW: why not? +// r.x -= insets.left; + r.y -= insets.top; + return r; + } + + /** + * A BasicButtonListener specialized to the needs of a Hyperlink. + * + * @author Jeanette Winzenburg + */ + public static class BasicHyperlinkListener extends BasicButtonListener { + + /** + * @param b + */ + public BasicHyperlinkListener(AbstractButton b) { + super(b); + } + + + @Override + public void stateChanged(ChangeEvent e) { + AbstractButton button = (AbstractButton) e.getSource(); + if (button.isRolloverEnabled()) { + button.setCursor(button.getModel().isRollover() ? + // PENDING JW: support customizable cursor + Cursor.getPredefinedCursor(Cursor.HAND_CURSOR) : null); + } + super.stateChanged(e); + } + } + + static class ULHtml extends BasicHTML { + /** + * Create an html renderer for the given component and + * string of html. + */ + public static View createHTMLView(JComponent c, String html) { + BasicEditorKit kit = getFactory(); + Document doc = kit.createDefaultDocument(c.getFont(), + c.getForeground()); + Object base = c.getClientProperty(documentBaseKey); + if (base instanceof URL) { + ((HTMLDocument)doc).setBase((URL)base); + } + Reader r = new StringReader(html); + try { + kit.read(r, doc, 0); + } catch (Throwable e) { + } + ViewFactory f = kit.getViewFactory(); + View hview = f.create(doc.getDefaultRootElement()); + View v = new Renderer(c, f, hview); + return v; + } + + static BasicEditorKit getFactory() { + if (basicHTMLFactory == null) { + basicHTMLViewFactory = new BasicHTMLViewFactory(); + basicHTMLFactory = new BasicEditorKit(); + } + return basicHTMLFactory; + } + + /** + * The source of the html renderers + */ + private static BasicEditorKit basicHTMLFactory; + + /** + * Creates the Views that visually represent the model. + */ + private static ViewFactory basicHTMLViewFactory; + + /** + * Overrides to the default stylesheet. Should consider + * just creating a completely fresh stylesheet. + */ + private static final String styleChanges = + "p { margin-top: 0; margin-bottom: 0; margin-left: 0; margin-right: 0; text-decoration: underline }" + + "body { margin-top: 0; margin-bottom: 0; margin-left: 0; margin-right: 0; text-decoration: underline }"+ + "font {text-decoration: underline}"; + + static class BasicEditorKit extends HTMLEditorKit { + /** Shared base style for all documents created by us use. */ + private static StyleSheet defaultStyles; + + /** + * Overriden to return our own slimmed down style sheet. + */ + @Override + public StyleSheet getStyleSheet() { + if (defaultStyles == null) { + defaultStyles = new StyleSheet(); + StringReader r = new StringReader(styleChanges); + try { + defaultStyles.loadRules(r, null); + } catch (Throwable e) { + // don't want to die in static initialization... + // just display things wrong. + } + r.close(); + defaultStyles.addStyleSheet(super.getStyleSheet()); + } + return defaultStyles; + } + + /** + * Sets the async policy to flush everything in one chunk, and + * to not display unknown tags. + */ + public Document createDefaultDocument(Font defaultFont, + Color foreground) { + StyleSheet styles = getStyleSheet(); + StyleSheet ss = new StyleSheet(); + ss.addStyleSheet(styles); + BasicDocument doc = new BasicDocument(ss, defaultFont, foreground); + doc.setAsynchronousLoadPriority(Integer.MAX_VALUE); + doc.setPreservesUnknownTags(false); + return doc; + } + + /** + * Returns the ViewFactory that is used to make sure the Views don't + * load in the background. + */ + @Override + public ViewFactory getViewFactory() { + return basicHTMLViewFactory; + } + } + + + /** + * BasicHTMLViewFactory extends HTMLFactory to force images to be loaded + * synchronously. + */ + static class BasicHTMLViewFactory extends HTMLEditorKit.HTMLFactory { + @Override + public View create(Element elem) { + View view = super.create(elem); + + if (view instanceof ImageView) { + ((ImageView)view).setLoadsSynchronously(true); + } + return view; + } + } + + + /** + * The subclass of HTMLDocument that is used as the model. getForeground + * is overridden to return the foreground property from the Component this + * was created for. + */ + static class BasicDocument extends HTMLDocument { + + private static String displayPropertiesToCSS(Font f, Color c) { + StringBuilder rule = new StringBuilder("body {"); + if (f != null) { + rule.append(" font-family: "); + rule.append(f.getFamily()); + rule.append(" ; "); + rule.append(" font-size: "); + rule.append(f.getSize()); + rule.append("pt ;"); + if (f.isBold()) { + rule.append(" font-weight: 700 ; "); + } + if (f.isItalic()) { + rule.append(" font-style: italic ; "); + } + } + if (c != null) { + rule.append(" color: #"); + if (c.getRed() < 16) { + rule.append('0'); + } + rule.append(Integer.toHexString(c.getRed())); + if (c.getGreen() < 16) { + rule.append('0'); + } + rule.append(Integer.toHexString(c.getGreen())); + if (c.getBlue() < 16) { + rule.append('0'); + } + rule.append(Integer.toHexString(c.getBlue())); + rule.append(" ; "); + } + rule.append(" }"); + return rule.toString(); + } + + // --------- EO 1.5 x 1.6 incompatibility handling .... + + BasicDocument(StyleSheet s, Font defaultFont, Color foreground) { + super(s); + setPreservesUnknownTags(false); + setFontAndColor(defaultFont, foreground); + } + + /** + * Sets the default font and default color. These are set by + * adding a rule for the body that specifies the font and color. + * This allows the html to override these should it wish to have + * a custom font or color. + */ + private void setFontAndColor(Font font, Color fg) { + getStyleSheet().addRule(displayPropertiesToCSS(font,fg)); + } + } + + + /** + * Root text view that acts as an HTML renderer. + */ + static class Renderer extends View { + + Renderer(JComponent c, ViewFactory f, View v) { + super(null); + host = c; + factory = f; + view = v; + view.setParent(this); + // initially layout to the preferred size + setSize(view.getPreferredSpan(X_AXIS), view.getPreferredSpan(Y_AXIS)); + } + + /** + * Fetches the attributes to use when rendering. At the root + * level there are no attributes. If an attribute is resolved + * up the view hierarchy this is the end of the line. + */ + @Override + public AttributeSet getAttributes() { + return null; + } + + /** + * Determines the preferred span for this view along an axis. + * + * @param axis may be either X_AXIS or Y_AXIS + * @return the span the view would like to be rendered into. + * Typically the view is told to render into the span + * that is returned, although there is no guarantee. + * The parent may choose to resize or break the view. + */ + @Override + public float getPreferredSpan(int axis) { + if (axis == X_AXIS) { + // width currently laid out to + return width; + } + return view.getPreferredSpan(axis); + } + + /** + * Determines the minimum span for this view along an axis. + * + * @param axis may be either X_AXIS or Y_AXIS + * @return the span the view would like to be rendered into. + * Typically the view is told to render into the span + * that is returned, although there is no guarantee. + * The parent may choose to resize or break the view. + */ + @Override + public float getMinimumSpan(int axis) { + return view.getMinimumSpan(axis); + } + + /** + * Determines the maximum span for this view along an axis. + * + * @param axis may be either X_AXIS or Y_AXIS + * @return the span the view would like to be rendered into. + * Typically the view is told to render into the span + * that is returned, although there is no guarantee. + * The parent may choose to resize or break the view. + */ + @Override + public float getMaximumSpan(int axis) { + return Integer.MAX_VALUE; + } + + /** + * Specifies that a preference has changed. + * Child views can call this on the parent to indicate that + * the preference has changed. The root view routes this to + * invalidate on the hosting component. + *

      + * This can be called on a different thread from the + * event dispatching thread and is basically unsafe to + * propagate into the component. To make this safe, + * the operation is transferred over to the event dispatching + * thread for completion. It is a design goal that all view + * methods be safe to call without concern for concurrency, + * and this behavior helps make that true. + * + * @param child the child view + * @param width true if the width preference has changed + * @param height true if the height preference has changed + */ + @Override + public void preferenceChanged(View child, boolean width, boolean height) { + host.revalidate(); + host.repaint(); + } + + /** + * Determines the desired alignment for this view along an axis. + * + * @param axis may be either X_AXIS or Y_AXIS + * @return the desired alignment, where 0.0 indicates the origin + * and 1.0 the full span away from the origin + */ + @Override + public float getAlignment(int axis) { + return view.getAlignment(axis); + } + + /** + * Renders the view. + * + * @param g the graphics context + * @param allocation the region to render into + */ + @Override + public void paint(Graphics g, Shape allocation) { + Rectangle alloc = allocation.getBounds(); + view.setSize(alloc.width, alloc.height); + view.paint(g, allocation); + } + + /** + * Sets the view parent. + * + * @param parent the parent view + */ + @Override + public void setParent(View parent) { + throw new Error("Can't set parent on root view"); + } + + /** + * Returns the number of views in this view. Since + * this view simply wraps the root of the view hierarchy + * it has exactly one child. + * + * @return the number of views + * @see #getView + */ + @Override + public int getViewCount() { + return 1; + } + + /** + * Gets the n-th view in this container. + * + * @param n the number of the view to get + * @return the view + */ + @Override + public View getView(int n) { + return view; + } + + /** + * Provides a mapping from the document model coordinate space + * to the coordinate space of the view mapped to it. + * + * @param pos the position to convert + * @param a the allocated region to render into + * @return the bounding box of the given position + */ + @Override + public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException { + return view.modelToView(pos, a, b); + } + + /** + * Provides a mapping from the document model coordinate space + * to the coordinate space of the view mapped to it. + * + * @param p0 the position to convert >= 0 + * @param b0 the bias toward the previous character or the + * next character represented by p0, in case the + * position is a boundary of two views. + * @param p1 the position to convert >= 0 + * @param b1 the bias toward the previous character or the + * next character represented by p1, in case the + * position is a boundary of two views. + * @param a the allocated region to render into + * @return the bounding box of the given position is returned + * @exception BadLocationException if the given position does + * not represent a valid location in the associated document + * @exception IllegalArgumentException for an invalid bias argument + * @see View#viewToModel + */ + @Override + public Shape modelToView(int p0, Position.Bias b0, int p1, + Position.Bias b1, Shape a) throws BadLocationException { + return view.modelToView(p0, b0, p1, b1, a); + } + + /** + * Provides a mapping from the view coordinate space to the logical + * coordinate space of the model. + * + * @param x x coordinate of the view location to convert + * @param y y coordinate of the view location to convert + * @param a the allocated region to render into + * @return the location within the model that best represents the + * given point in the view + */ + @Override + public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) { + return view.viewToModel(x, y, a, bias); + } + + /** + * Returns the document model underlying the view. + * + * @return the model + */ + @Override + public Document getDocument() { + return view.getDocument(); + } + + /** + * Returns the starting offset into the model for this view. + * + * @return the starting offset + */ + @Override + public int getStartOffset() { + return view.getStartOffset(); + } + + /** + * Returns the ending offset into the model for this view. + * + * @return the ending offset + */ + @Override + public int getEndOffset() { + return view.getEndOffset(); + } + + /** + * Gets the element that this view is mapped to. + * + * @return the view + */ + @Override + public Element getElement() { + return view.getElement(); + } + + /** + * Sets the view size. + * + * @param width the width + * @param height the height + */ + @Override + public void setSize(float width, float height) { + this.width = (int) width; + view.setSize(width, height); + } + + /** + * Fetches the container hosting the view. This is useful for + * things like scheduling a repaint, finding out the host + * components font, etc. The default implementation + * of this is to forward the query to the parent view. + * + * @return the container + */ + @Override + public Container getContainer() { + return host; + } + + /** + * Fetches the factory to be used for building the + * various view fragments that make up the view that + * represents the model. This is what determines + * how the model will be represented. This is implemented + * to fetch the factory provided by the associated + * EditorKit. + * + * @return the factory + */ + @Override + public ViewFactory getViewFactory() { + return factory; + } + + private int width; + private View view; + private ViewFactory factory; + private JComponent host; + + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicLoginPaneUI.java b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicLoginPaneUI.java new file mode 100644 index 0000000000..e1fac1cf59 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicLoginPaneUI.java @@ -0,0 +1,159 @@ +/* + * $Id: BasicLoginPaneUI.java 4082 2011-11-15 18:39:43Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf.basic; + +import org.jdesktop.swingx.JXLoginPane; +import org.jdesktop.swingx.plaf.LoginPaneUI; +import org.jdesktop.swingx.plaf.UIManagerExt; +import org.jdesktop.swingx.util.GraphicsUtilities; + +import javax.swing.*; +import javax.swing.plaf.ComponentUI; +import java.awt.*; +import java.awt.geom.GeneralPath; +import java.awt.image.BufferedImage; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +/** + * Base implementation of the JXLoginPane UI. + * + * @author rbair + */ +public class BasicLoginPaneUI extends LoginPaneUI { + private class LocaleHandler implements PropertyChangeListener { + /** + * {@inheritDoc} + */ + @Override + public void propertyChange(PropertyChangeEvent evt) { + Object src = evt.getSource(); + + if (src instanceof JComponent) { + ((JComponent) src).updateUI(); + } + } + } + + private JXLoginPane dlg; + + /** Creates a new instance of BasicLoginDialogUI */ + public BasicLoginPaneUI(JXLoginPane dlg) { + this.dlg = dlg; +// dlg.addPropertyChangeListener("locale", new LocaleHandler()); + } + + public static ComponentUI createUI(JComponent c) { + return new BasicLoginPaneUI((JXLoginPane)c); + } + + @Override + public void installUI(JComponent c) { + installDefaults(); + } + + protected void installDefaults() { + String s = dlg.getBannerText(); + if (s == null || s.equals("")) { + dlg.setBannerText(UIManagerExt.getString("JXLoginPane.bannerString", dlg.getLocale())); + } + + s = dlg.getErrorMessage(); + if (s == null || s.equals("")) { + dlg.setErrorMessage(UIManagerExt.getString("JXLoginPane.errorMessage", dlg.getLocale())); + } + } + + /** + * Creates default 400x60 banner for the login panel. + * @see LoginPaneUI#getBanner() + */ + @Override + public Image getBanner() { + int w = 400; + int h = 60; + float loginStringX = w * .05f; + float loginStringY = h * .75f; + + BufferedImage img = GraphicsUtilities.createCompatibleImage(w, h); + Graphics2D g2 = img.createGraphics(); + try { + Font font = UIManager.getFont("JXLoginPane.bannerFont"); + g2.setFont(font); + Graphics2D originalGraphics = g2; + + try { + if (!dlg.getComponentOrientation().isLeftToRight()) { + originalGraphics = (Graphics2D) g2.create(); + g2.scale(-1, 1); + g2.translate(-w, 0); + loginStringX = w + - (((float) font.getStringBounds( + dlg.getBannerText(), + originalGraphics.getFontRenderContext()) + .getWidth()) + w * .05f); + } + + g2.setRenderingHint(RenderingHints.KEY_RENDERING, + RenderingHints.VALUE_RENDER_QUALITY); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, + RenderingHints.VALUE_FRACTIONALMETRICS_ON); + + // draw a big square + g2.setColor(UIManager + .getColor("JXLoginPane.bannerDarkBackground")); + g2.fillRect(0, 0, w, h); + + // create the curve shape + GeneralPath curveShape = new GeneralPath( + GeneralPath.WIND_NON_ZERO); + curveShape.moveTo(0, h * .6f); + curveShape.curveTo(w * .167f, h * 1.2f, w * .667f, h * -.5f, w, + h * .75f); + curveShape.lineTo(w, h); + curveShape.lineTo(0, h); + curveShape.lineTo(0, h * .8f); + curveShape.closePath(); + + // draw into the buffer a gradient (bottom to top), and the text + // "Login" + GradientPaint gp = new GradientPaint(0, h, UIManager + .getColor("JXLoginPane.bannerDarkBackground"), 0, 0, + UIManager.getColor("JXLoginPane.bannerLightBackground")); + g2.setPaint(gp); + g2.fill(curveShape); + + originalGraphics.setColor(UIManager + .getColor("JXLoginPane.bannerForeground")); + originalGraphics.drawString(dlg.getBannerText(), loginStringX, + loginStringY); + } finally { + originalGraphics.dispose(); + } + } finally { + g2.dispose(); + } + return img; + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicLookAndFeelAddons.java b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicLookAndFeelAddons.java new file mode 100644 index 0000000000..3b0afb5464 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicLookAndFeelAddons.java @@ -0,0 +1,53 @@ +/* + * $Id: BasicLookAndFeelAddons.java 4034 2011-07-19 17:18:03Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf.basic; + +import org.jdesktop.swingx.plaf.LookAndFeelAddons; +import org.jdesktop.swingx.plaf.UIManagerExt; + +/** + * Install simple pluggable UI. Usually not used directly, subclasses should be + * preferred as this addon may not provide complete implementation of the + * additional pluggable UIs. + */ +public abstract class BasicLookAndFeelAddons extends LookAndFeelAddons { + /** + * {@inheritDoc} + */ + @Override + public void initialize() { + super.initialize(); + //must add resource bundle after adding component values + UIManagerExt.addResourceBundle( + "org.jdesktop.swingx.plaf.basic.resources.swingx"); + } + + /** + * {@inheritDoc} + */ + @Override + public void uninitialize() { + //must remove resource bundle before adding component values + UIManagerExt.removeResourceBundle( + "org.jdesktop.swingx.plaf.basic.resources.swingx"); + super.uninitialize(); + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicMonthViewUI.java b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicMonthViewUI.java new file mode 100644 index 0000000000..c3b9c2e918 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicMonthViewUI.java @@ -0,0 +1,2323 @@ +/* + * $Id: BasicMonthViewUI.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf.basic; + +import org.jdesktop.swingx.JXMonthView; +import org.jdesktop.swingx.SwingXUtilities; +import org.jdesktop.swingx.action.AbstractActionExt; +import org.jdesktop.swingx.calendar.CalendarUtils; +import org.jdesktop.swingx.calendar.DateSelectionModel; +import org.jdesktop.swingx.calendar.DateSelectionModel.SelectionMode; +import org.jdesktop.swingx.event.DateSelectionEvent; +import org.jdesktop.swingx.event.DateSelectionListener; +import org.jdesktop.swingx.plaf.MonthViewUI; +import org.jdesktop.swingx.plaf.UIManagerExt; + +import javax.swing.*; +import javax.swing.plaf.ComponentUI; +import java.awt.*; +import java.awt.event.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.text.DateFormatSymbols; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; +import java.util.SortedSet; +import java.util.logging.Logger; + +/** + * Base implementation of the JXMonthView UI.

      + * + * Note: The api changed considerably between releases 0.9.4 and 0.9.5. + *

      + * + * The general drift of the change was to delegate all text rendering to a dedicated + * rendering controller (currently named RenderingHandler), similar to + * the collection view rendering. The UI itself keeps layout and positioning of + * the rendering components. Plus updating on property changes received from the + * monthView.

      + * + * + *

      + * Painting: coordinate systems. + * + *

        + *
      • Screen coordinates of months/days, accessible via the getXXBounds() methods. These + * coordinates are absolute in the system of the monthView. + *
      • The grid of visible months with logical row/column coordinates. The logical + * coordinates are adjusted to ComponentOrientation. + *
      • The grid of days in a month with logical row/column coordinates. The logical + * coordinates are adjusted to ComponentOrientation. The columns + * are the (optional) week header and the days of the week. The rows are the day header + * and the weeks in a month. The day header shows the localized names of the days and + * has the row coordinate DAY_HEADER_ROW. It is shown always. + * The row header shows the week number in the year and has the column coordinate WEEK_HEADER_COLUMN. It + * is shown only if the showingWeekNumber property is true. + *
      + * + * On the road to "zoomable" date range views (Vista-style).

      + * + * Added support (doesn't do anything yet, zoom-logic must yet be defined) + * by way of an active calendar header which is added to the monthView if zoomable. + * It is disabled by default. In this mode, the view is always + * traversable and shows exactly one calendar. It is orthogonal to the classic + * mode, that is client code should not be effected in any way as long as the mode + * is not explicitly enabled.

      + * + * NOTE to LAF implementors: the active calendar header is very, very, very raw and + * sure to change without much notice. Better not yet to support it right now. + * + * @author dmouse + * @author rbair + * @author rah003 + * @author Jeanette Winzenburg + */ +public class BasicMonthViewUI extends MonthViewUI { + @SuppressWarnings("all") + private static final Logger LOG = Logger.getLogger(BasicMonthViewUI.class + .getName()); + + private static final int CALENDAR_SPACING = 10; + + /** Return value used to identify when the month down button is pressed. */ + public static final int MONTH_DOWN = 1; + /** Return value used to identify when the month up button is pressed. */ + public static final int MONTH_UP = 2; + + // constants for day columns + protected static final int WEEK_HEADER_COLUMN = 0; + protected static final int DAYS_IN_WEEK = 7; + protected static final int FIRST_DAY_COLUMN = WEEK_HEADER_COLUMN + 1; + protected static final int LAST_DAY_COLUMN = FIRST_DAY_COLUMN + DAYS_IN_WEEK -1; + + // constants for day rows (aka: weeks) + protected static final int DAY_HEADER_ROW = 0; + protected static final int WEEKS_IN_MONTH = 6; + protected static final int FIRST_WEEK_ROW = DAY_HEADER_ROW + 1; + protected static final int LAST_WEEK_ROW = FIRST_WEEK_ROW + WEEKS_IN_MONTH - 1; + + + /** the component we are installed for. */ + protected JXMonthView monthView; + // listeners + private PropertyChangeListener propertyChangeListener; + private MouseListener mouseListener; + private MouseMotionListener mouseMotionListener; + private Handler handler; + + // fields related to visible date range + /** end of day of the last visible month. */ + private Date lastDisplayedDate; + /** + + //---------- fields related to selection/navigation + + + /** flag indicating keyboard navigation. */ + private boolean usingKeyboard = false; + /** For interval selections we need to record the date we pivot around. */ + private Date pivotDate = null; + /** + * Date span used by the keyboard actions to track the original selection. + */ + private SortedSet originalDateSpan; + + //------------------ visuals + + protected boolean isLeftToRight; + protected Icon monthUpImage; + protected Icon monthDownImage; + + + /** + * The padding for month traversal icons. + * PENDING JW: decouple rendering and hit-detection. + */ + private int arrowPaddingX = 3; + private int arrowPaddingY = 3; + + + /** height of month header including the monthView's box padding. */ + private int fullMonthBoxHeight; + /** + * width of a "day" box including the monthView's box padding + * this is the same for days-of-the-week, weeks-of-the-year and days + */ + private int fullBoxWidth; + /** + * height of a "day" box including the monthView's box padding + * this is the same for days-of-the-week, weeks-of-the-year and days + */ + private int fullBoxHeight; + /** the width of a single month display. */ + private int calendarWidth; + /** the height of a single month display. */ + private int calendarHeight; + /** the height of a single month grid cell, including padding. */ + private int fullCalendarHeight; + /** the width of a single month grid cell, including padding. */ + private int fullCalendarWidth; + /** The number of calendars displayed vertically. */ + private int calendarRowCount = 1; + /** The number of calendars displayed horizontally. */ + private int calendarColumnCount = 1; + + /** + * The bounding box of the grid of visible months. + */ + protected Rectangle calendarGrid = new Rectangle(); + + /** + * The Strings used for the day headers. This is the fall-back for + * the monthView if no custom strings are set. + * PENDING JW: delegate to RenderingHandler? + */ + private String[] daysOfTheWeek; + + /** + * Provider of configured components for text rendering. + */ + private CalendarRenderingHandler renderingHandler; + /** + * The CellRendererPane for stamping rendering comps. + */ + private CellRendererPane rendererPane; + + /** + * The CalendarHeaderHandler which provides the header component if zoomable. + */ + private CalendarHeaderHandler calendarHeaderHandler; + + + @SuppressWarnings({"UnusedDeclaration"}) + public static ComponentUI createUI(JComponent c) { + return new BasicMonthViewUI(); + } + + /** + * Installs the component as appropriate for the current lf. + * + * PENDING JW: clarify sequence of installXX methods. + */ + @Override + public void installUI(JComponent c) { + monthView = (JXMonthView)c; + monthView.setLayout(createLayoutManager()); + + // PENDING JW: move to installDefaults or installComponents? + installRenderingHandler(); + + installDefaults(); + installDelegate(); + installKeyboardActions(); + installComponents(); + updateLocale(false); + updateZoomable(); + installListeners(); + } + + + @Override + public void uninstallUI(JComponent c) { + uninstallRenderingHandler(); + uninstallListeners(); + uninstallKeyboardActions(); + uninstallDefaults(); + uninstallComponents(); + monthView.setLayout(null); + monthView = null; + } + + /** + * Creates and installs the calendar header handler. + */ + protected void installComponents() { + setCalendarHeaderHandler(createCalendarHeaderHandler()); + getCalendarHeaderHandler().install(monthView); + } + + /** + * Uninstalls the calendar header handler. + */ + protected void uninstallComponents() { + getCalendarHeaderHandler().uninstall(monthView); + setCalendarHeaderHandler(null); + } + + /** + * Installs default values.

      + * + * This is refactored to only install default properties on the monthView. + * Extracted install of this delegate's properties into installDelegate. + * + */ + protected void installDefaults() { + LookAndFeel.installProperty(monthView, "opaque", Boolean.TRUE); + + // @KEEP JW: do not use the core install methods (might have classloader probs) + // instead access all properties via the UIManagerExt .. + // BasicLookAndFeel.installColorsAndFont(monthView, +// "JXMonthView.background", "JXMonthView.foreground", "JXMonthView.font"); + + if (SwingXUtilities.isUIInstallable(monthView.getBackground())) { + monthView.setBackground(UIManagerExt.getColor("JXMonthView.background")); + } + if (SwingXUtilities.isUIInstallable(monthView.getForeground())) { + monthView.setForeground(UIManagerExt.getColor("JXMonthView.foreground")); + } + if (SwingXUtilities.isUIInstallable(monthView.getFont())) { + // PENDING JW: missing in managerExt? Or not applicable anyway? + monthView.setFont(UIManager.getFont("JXMonthView.font")); + } + if (SwingXUtilities.isUIInstallable(monthView.getMonthStringBackground())) { + monthView.setMonthStringBackground(UIManagerExt.getColor("JXMonthView.monthStringBackground")); + } + if (SwingXUtilities.isUIInstallable(monthView.getMonthStringForeground())) { + monthView.setMonthStringForeground(UIManagerExt.getColor("JXMonthView.monthStringForeground")); + } + if (SwingXUtilities.isUIInstallable(monthView.getDaysOfTheWeekForeground())) { + monthView.setDaysOfTheWeekForeground(UIManagerExt.getColor("JXMonthView.daysOfTheWeekForeground")); + } + if (SwingXUtilities.isUIInstallable(monthView.getSelectionBackground())) { + monthView.setSelectionBackground(UIManagerExt.getColor("JXMonthView.selectedBackground")); + } + if (SwingXUtilities.isUIInstallable(monthView.getSelectionForeground())) { + monthView.setSelectionForeground(UIManagerExt.getColor("JXMonthView.selectedForeground")); + } + if (SwingXUtilities.isUIInstallable(monthView.getFlaggedDayForeground())) { + monthView.setFlaggedDayForeground(UIManagerExt.getColor("JXMonthView.flaggedDayForeground")); + } + + monthView.setBoxPaddingX(UIManagerExt.getInt("JXMonthView.boxPaddingX")); + monthView.setBoxPaddingY(UIManagerExt.getInt("JXMonthView.boxPaddingY")); + } + + /** + * Installs this ui delegate's properties. + */ + protected void installDelegate() { + isLeftToRight = monthView.getComponentOrientation().isLeftToRight(); + // PENDING JW: remove here if rendererHandler takes over control completely + // as is, some properties are duplicated + monthDownImage = UIManager.getIcon("JXMonthView.monthDownFileName"); + monthUpImage = UIManager.getIcon("JXMonthView.monthUpFileName"); + // install date related state + setFirstDisplayedDay(monthView.getFirstDisplayedDay()); + } + + + protected void uninstallDefaults() {} + + protected void installKeyboardActions() { + // Setup the keyboard handler. + // JW: changed (0.9.6) to when-ancestor just to be on the safe side + // if the title contain active comps + installKeyBindings(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); + // JW: removed the automatic keybindings in WHEN_IN_FOCUSED + // which caused #555-swingx (binding active if not focused) + ActionMap actionMap = monthView.getActionMap(); + KeyboardAction acceptAction = new KeyboardAction(KeyboardAction.ACCEPT_SELECTION); + actionMap.put("acceptSelection", acceptAction); + KeyboardAction cancelAction = new KeyboardAction(KeyboardAction.CANCEL_SELECTION); + actionMap.put("cancelSelection", cancelAction); + + actionMap.put("selectPreviousDay", new KeyboardAction(KeyboardAction.SELECT_PREVIOUS_DAY)); + actionMap.put("selectNextDay", new KeyboardAction(KeyboardAction.SELECT_NEXT_DAY)); + actionMap.put("selectDayInPreviousWeek", new KeyboardAction(KeyboardAction.SELECT_DAY_PREVIOUS_WEEK)); + actionMap.put("selectDayInNextWeek", new KeyboardAction(KeyboardAction.SELECT_DAY_NEXT_WEEK)); + + actionMap.put("adjustSelectionPreviousDay", new KeyboardAction(KeyboardAction.ADJUST_SELECTION_PREVIOUS_DAY)); + actionMap.put("adjustSelectionNextDay", new KeyboardAction(KeyboardAction.ADJUST_SELECTION_NEXT_DAY)); + actionMap.put("adjustSelectionPreviousWeek", new KeyboardAction(KeyboardAction.ADJUST_SELECTION_PREVIOUS_WEEK)); + actionMap.put("adjustSelectionNextWeek", new KeyboardAction(KeyboardAction.ADJUST_SELECTION_NEXT_WEEK)); + + + actionMap.put(JXMonthView.COMMIT_KEY, acceptAction); + actionMap.put(JXMonthView.CANCEL_KEY, cancelAction); + + // PENDING JW: complete (year-, decade-, ?? ) and consolidate with KeyboardAction + // additional navigation actions + AbstractActionExt prev = new AbstractActionExt() { + + @Override + public void actionPerformed(ActionEvent e) { + previousMonth(); + } + + }; + monthView.getActionMap().put("scrollToPreviousMonth", prev); + AbstractActionExt next = new AbstractActionExt() { + + @Override + public void actionPerformed(ActionEvent e) { + nextMonth(); + } + + }; + monthView.getActionMap().put("scrollToNextMonth", next); + + } + + + /** + * @param inputMap + */ + private void installKeyBindings(int type) { + InputMap inputMap = monthView.getInputMap(type); + inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, false), "acceptSelection"); + inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false), "cancelSelection"); + + // @KEEP quickly check #606-swingx: keybindings not working in internalframe + // eaten somewhere +// inputMap.put(KeyStroke.getKeyStroke("F1"), "selectPreviousDay"); + + inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), "selectPreviousDay"); + inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), "selectNextDay"); + inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false), "selectDayInPreviousWeek"); + inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), "selectDayInNextWeek"); + + inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, InputEvent.SHIFT_MASK, false), "adjustSelectionPreviousDay"); + inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, InputEvent.SHIFT_MASK, false), "adjustSelectionNextDay"); + inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, InputEvent.SHIFT_MASK, false), "adjustSelectionPreviousWeek"); + inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, InputEvent.SHIFT_MASK, false), "adjustSelectionNextWeek"); + } + + /** + * @param inputMap + */ + private void uninstallKeyBindings(int type) { + InputMap inputMap = monthView.getInputMap(type); + inputMap.clear(); + } + + protected void uninstallKeyboardActions() {} + + protected void installListeners() { + propertyChangeListener = createPropertyChangeListener(); + mouseListener = createMouseListener(); + mouseMotionListener = createMouseMotionListener(); + + monthView.addPropertyChangeListener(propertyChangeListener); + monthView.addMouseListener(mouseListener); + monthView.addMouseMotionListener(mouseMotionListener); + + monthView.getSelectionModel().addDateSelectionListener(getHandler()); + } + + protected void uninstallListeners() { + monthView.getSelectionModel().removeDateSelectionListener(getHandler()); + monthView.removeMouseMotionListener(mouseMotionListener); + monthView.removeMouseListener(mouseListener); + monthView.removePropertyChangeListener(propertyChangeListener); + + mouseMotionListener = null; + mouseListener = null; + propertyChangeListener = null; + } + + /** + * Creates and installs the renderingHandler and infrastructure to use it. + */ + protected void installRenderingHandler() { + setRenderingHandler(createRenderingHandler()); + if (getRenderingHandler() != null) { + rendererPane = new CellRendererPane(); + monthView.add(rendererPane); + } + } + + /** + * Uninstalls the renderingHandler and infrastructure that used it. + */ + protected void uninstallRenderingHandler() { + if (getRenderingHandler() == null) return; + monthView.remove(rendererPane); + rendererPane = null; + setRenderingHandler(null); + } + + /** + * Returns the CalendarRenderingHandler to use. Subclasses may override to + * plug-in custom implementations.

      + * + * This implementation returns an instance of RenderingHandler. + * + * @return the endering handler to use for painting, must not be null + */ + protected CalendarRenderingHandler createRenderingHandler() { + return new RenderingHandler(); + } + + /** + * @param renderingHandler the renderingHandler to set + */ + protected void setRenderingHandler(CalendarRenderingHandler renderingHandler) { + this.renderingHandler = renderingHandler; + } + + /** + * @return the renderingHandler + */ + protected CalendarRenderingHandler getRenderingHandler() { + return renderingHandler; + } + + /** + * + * Empty subclass for backward compatibility. The original implementation was + * extracted as standalone class and renamed to BasicCalendarRenderingHandler.

      + * + * This will be available for extension by LAF providers until all collaborators + * in the new rendering pipeline are ready for public exposure. + */ + protected static class RenderingHandler extends BasicCalendarRenderingHandler { + + } + /** + * Binds/clears the keystrokes in the component input map, + * based on the monthView's componentInputMap enabled property. + * + * @see JXMonthView#isComponentInputMapEnabled() + */ + protected void updateComponentInputMap() { + if (monthView.isComponentInputMapEnabled()) { + installKeyBindings(JComponent.WHEN_IN_FOCUSED_WINDOW); + } else { + uninstallKeyBindings(JComponent.WHEN_IN_FOCUSED_WINDOW); + } + } + + + + /** + * Updates internal state according to monthView's locale. Revalidates the + * monthView if the boolean parameter is true. + * + * @param revalidate a boolean indicating whether the monthView should be + * revalidated after the change. + */ + protected void updateLocale(boolean revalidate) { + Locale locale = monthView.getLocale(); + if (getRenderingHandler() != null) { + getRenderingHandler().setLocale(locale); + } + + // fixed JW: respect property in UIManager if available + // PENDING JW: what to do if weekdays had been set + // with JXMonthView method? how to detect? + daysOfTheWeek = (String[]) UIManager.get("JXMonthView.daysOfTheWeek"); + + if (daysOfTheWeek == null) { + daysOfTheWeek = new String[7]; + String[] dateFormatSymbols = DateFormatSymbols.getInstance(locale) + .getShortWeekdays(); + daysOfTheWeek = new String[JXMonthView.DAYS_IN_WEEK]; + for (int i = Calendar.SUNDAY; i <= Calendar.SATURDAY; i++) { + daysOfTheWeek[i - 1] = dateFormatSymbols[i]; + } + } + if (revalidate) { + monthView.invalidate(); + monthView.validate(); + } + } + + @Override + public String[] getDaysOfTheWeek() { + String[] days = new String[daysOfTheWeek.length]; + System.arraycopy(daysOfTheWeek, 0, days, 0, days.length); + return days; + } + + +//---------------------- listener creation + protected PropertyChangeListener createPropertyChangeListener() { + return getHandler(); + } + + protected LayoutManager createLayoutManager() { + return getHandler(); + } + + protected MouseListener createMouseListener() { + return getHandler(); + } + + protected MouseMotionListener createMouseMotionListener() { + return getHandler(); + } + + private Handler getHandler() { + if (handler == null) { + handler = new Handler(); + } + + return handler; + } + + public boolean isUsingKeyboard() { + return usingKeyboard; + } + + public void setUsingKeyboard(boolean val) { + usingKeyboard = val; + } + + + + // ----------------------- mapping day coordinates + + /** + * Returns the bounds of the day in the grid of days which contains the + * given location. The bounds are in monthView screen coordinate system. + *

      + * + * Note: this is a pure geometric mapping. The returned rectangle need not + * necessarily map to a date in the month which contains the location, it + * can represent a week-number/column header or a leading/trailing date. + * + * @param x the x position of the location in pixel + * @param y the y position of the location in pixel + * @return the bounds of the day which contains the location, or null if + * outside + */ + protected Rectangle getDayBoundsAtLocation(int x, int y) { + Rectangle monthDetails = getMonthDetailsBoundsAtLocation(x, y); + if ((monthDetails == null) || (!monthDetails.contains(x, y))) + return null; + // calculate row/column in absolute grid coordinates + int row = (y - monthDetails.y) / fullBoxHeight; + int column = (x - monthDetails.x) / fullBoxWidth; + return new Rectangle(monthDetails.x + column * fullBoxWidth, monthDetails.y + + row * fullBoxHeight, fullBoxWidth, fullBoxHeight); + } + + /** + * Returns the bounds of the day box at logical coordinates in the given month. + * The row's range is from DAY_HEADER_ROW to LAST_WEEK_ROW. Column's range is from + * WEEK_HEADER_COLUMN to LAST_DAY_COLUMN. + * + * @param month the month containing the day box + * @param row the logical row (== week) coordinate in the day grid + * @param column the logical column (== day) coordinate in the day grid + * @return the bounds of the daybox or null if not showing + * @throws IllegalArgumentException if row or column are out off range. + * + * @see #getDayGridPositionAtLocation(int, int) + */ + protected Rectangle getDayBoundsInMonth(Date month, int row, final int column) { + checkValidRow(row, column); + if ((WEEK_HEADER_COLUMN == column) && !monthView.isShowingWeekNumber()) return null; + Rectangle monthBounds = getMonthBounds(month); + if (monthBounds == null) return null; + // dayOfWeek header is shown always + monthBounds.y += getMonthHeaderHeight() + (row - DAY_HEADER_ROW) * fullBoxHeight; + // PENDING JW: still looks fishy ... + int absoluteColumn = column - FIRST_DAY_COLUMN; + if (monthView.isShowingWeekNumber()) { + absoluteColumn++; + } + if (isLeftToRight) { + monthBounds.x += absoluteColumn * fullBoxWidth; + } else { + int leading = monthBounds.x + monthBounds.width - fullBoxWidth; + monthBounds.x = leading - absoluteColumn * fullBoxWidth; + } + monthBounds.width = fullBoxWidth; + monthBounds.height = fullBoxHeight; + return monthBounds; + } + + + /** + * Returns the logical coordinates of the day which contains the given + * location. The p.x of the returned value represents the week header or the + * day of week, ranging from WEEK_HEADER_COLUMN to LAST_DAY_COLUMN. The + * p.y represents the day header or week of the month, ranging from DAY_HEADER_ROW + * to LAST_WEEK_ROW. The transformation takes care of + * ComponentOrientation. + *

      + * + * Note: The returned grid position need not + * necessarily map to a date in the month which contains the location, it + * can represent a week-number/column header or a leading/trailing date. + * + * @param x the x position of the location in pixel + * @param y the y position of the location in pixel + * @return the logical coordinates of the day in the grid of days in a month + * or null if outside. + * + * @see #getDayBoundsInMonth(Date, int, int) + */ + protected Point getDayGridPositionAtLocation(int x, int y) { + Rectangle monthDetailsBounds = getMonthDetailsBoundsAtLocation(x, y); + if ((monthDetailsBounds == null) ||(!monthDetailsBounds.contains(x, y))) return null; + int calendarRow = (y - monthDetailsBounds.y) / fullBoxHeight + DAY_HEADER_ROW; + int absoluteColumn = (x - monthDetailsBounds.x) / fullBoxWidth; + int calendarColumn = absoluteColumn + FIRST_DAY_COLUMN; + if (!isLeftToRight) { + int leading = monthDetailsBounds.x + monthDetailsBounds.width; + calendarColumn = (leading - x) / fullBoxWidth + FIRST_DAY_COLUMN; + } + if (monthView.isShowingWeekNumber()) { + calendarColumn -= 1; + } + return new Point(calendarColumn, calendarRow); + } + + /** + * Returns the Date defined by the logical + * grid coordinates relative to the given month. May be null if the + * logical coordinates represent a header in the day grid or is outside of the + * given month. + * + * Mapping logical day grid coordinates to Date.

      + * + * PENDING JW: relax the startOfMonth pre? Why did I require it? + * + * @param month a calendar representing the first day of the month, must not + * be null. + * @param row the logical row index in the day grid of the month + * @param column the logical column index in the day grid of the month + * @return the day at the logical grid coordinates in the given month or null + * if the coordinates are day/week header or leading/trailing dates + * @throws IllegalStateException if the month is not the start of the month. + * + * @see #getDayGridPosition(Date) + */ + protected Date getDayInMonth(Date month, int row, int column) { + if ((row == DAY_HEADER_ROW) || (column == WEEK_HEADER_COLUMN)) return null; + Calendar calendar = getCalendar(month); + int monthField = calendar.get(Calendar.MONTH); + if (!CalendarUtils.isStartOfMonth(calendar)) + throw new IllegalStateException("calendar must be start of month but was: " + month.getTime()); + CalendarUtils.startOfWeek(calendar); + // PENDING JW: correctly mapped now? + calendar.add(Calendar.DAY_OF_MONTH, + (row - FIRST_WEEK_ROW) * DAYS_IN_WEEK + (column - FIRST_DAY_COLUMN)); + if (calendar.get(Calendar.MONTH) == monthField) { + return calendar.getTime(); + } + return null; + + } + + /** + * Returns the given date's position in the grid of the month it is contained in. + * + * @param date the Date to get the logical position for, must not be null. + * @return the logical coordinates of the day in the grid of days in a + * month or null if the Date is not visible. + * + * @see #getDayInMonth(Date, int, int) + */ + protected Point getDayGridPosition(Date date) { + if (!isVisible(date)) return null; + Calendar calendar = getCalendar(date); + Date startOfDay = CalendarUtils.startOfDay(calendar, date); + // there must be a less ugly way? + // columns + CalendarUtils.startOfWeek(calendar); + int column = FIRST_DAY_COLUMN; + while (calendar.getTime().before(startOfDay)) { + column++; + calendar.add(Calendar.DAY_OF_MONTH, 1); + } + + Date startOfWeek = CalendarUtils.startOfWeek(calendar, date); + calendar.setTime(date); + CalendarUtils.startOfMonth(calendar); + int row = FIRST_WEEK_ROW; + while (calendar.getTime().before(startOfWeek)) { + row++; + calendar.add(Calendar.WEEK_OF_YEAR, 1); + } + return new Point(column, row); + } + + + /** + * Returns the Date at the given location. May be null if the + * coordinates don't map to a day in the month which contains the + * coordinates. Specifically: hitting leading/trailing dates returns null. + * + * Mapping pixel to calendar day. + * + * @param x the x position of the location in pixel + * @param y the y position of the location in pixel + * @return the day at the given location or null if the location + * doesn't map to a day in the month which contains the coordinates. + * + * @see #getDayBounds(Date) + */ + @Override + public Date getDayAtLocation(int x, int y) { + Point dayInGrid = getDayGridPositionAtLocation(x, y); + if ((dayInGrid == null) + || (dayInGrid.x == WEEK_HEADER_COLUMN) || (dayInGrid.y == DAY_HEADER_ROW)) return null; + Date month = getMonthAtLocation(x, y); + return getDayInMonth(month, dayInGrid.y, dayInGrid.x); + } + + /** + * Returns the bounds of the given day. + * The bounds are in monthView coordinate system.

      + * + * PENDING JW: this most probably should be public as it is the logical + * reverse of getDayAtLocation

      + * + * @param date the Date to return the bounds for. Must not be null. + * @return the bounds of the given date or null if not visible. + * + * @see #getDayAtLocation(int, int) + */ + protected Rectangle getDayBounds(Date date) { + if (!isVisible(date)) return null; + Point position = getDayGridPosition(date); + Rectangle monthBounds = getMonthBounds(date); + monthBounds.y += getMonthHeaderHeight() + (position.y - DAY_HEADER_ROW) * fullBoxHeight; + if (monthView.isShowingWeekNumber()) { + position.x++; + } + position.x -= FIRST_DAY_COLUMN; + if (isLeftToRight) { + monthBounds.x += position.x * fullBoxWidth; + } else { + int start = monthBounds.x + monthBounds.width - fullBoxWidth; + monthBounds.x = start - position.x * fullBoxWidth; + } + monthBounds.width = fullBoxWidth; + monthBounds.height = fullBoxHeight; + return monthBounds; + } + + /** + * @param row + */ + private void checkValidRow(int row, int column) { + if ((column < WEEK_HEADER_COLUMN) || (column > LAST_DAY_COLUMN)) + throw new IllegalArgumentException("illegal column in day grid " + column); + if ((row < DAY_HEADER_ROW) || (row > LAST_WEEK_ROW)) + throw new IllegalArgumentException("illegal row in day grid" + row); + } + + /** + * Returns a boolean indicating if the given Date is visible. Trailing/leading + * dates of the last/first displayed month are considered to be invisible. + * + * @param date the Date to check for visibility. Must not be null. + * @return true if the date is visible, false otherwise. + */ + private boolean isVisible(Date date) { + if (getFirstDisplayedDay().after(date) || getLastDisplayedDay().before(date)) return false; + return true; + } + + + // ------------------- mapping month parts + + + /** + * Mapping pixel to bounds.

      + * + * PENDING JW: define the "action grid". Currently this replaces the old + * version to remove all internal usage of deprecated methods. + * + * @param x the x position of the location in pixel + * @param y the y position of the location in pixel + * @return the bounds of the active header area in containing the location + * or null if outside. + */ + protected int getTraversableGridPositionAtLocation(int x, int y) { + Rectangle headerBounds = getMonthHeaderBoundsAtLocation(x, y); + if (headerBounds == null) return -1; + if (y < headerBounds.y + arrowPaddingY) return -1; + if (y > headerBounds.y + headerBounds.height - arrowPaddingY) return -1; + headerBounds.setBounds(headerBounds.x + arrowPaddingX, y, + headerBounds.width - 2 * arrowPaddingX, headerBounds.height); + if (!headerBounds.contains(x, y)) return -1; + Rectangle hitArea = new Rectangle(headerBounds.x, headerBounds.y, monthUpImage.getIconWidth(), monthUpImage.getIconHeight()); + if (hitArea.contains(x, y)) { + return isLeftToRight ? MONTH_DOWN : MONTH_UP; + } + hitArea.translate(headerBounds.width - monthUpImage.getIconWidth(), 0); + if (hitArea.contains(x, y)) { + return isLeftToRight ? MONTH_UP : MONTH_DOWN; + } + return -1; + } + + /** + * Returns the bounds of the month header which contains the + * given location. The bounds are in monthView coordinate system. + * + *

      + * + * @param x the x position of the location in pixel + * @param y the y position of the location in pixel + * @return the bounds of the month which contains the location, + * or null if outside + */ + protected Rectangle getMonthHeaderBoundsAtLocation(int x, int y) { + Rectangle header = getMonthBoundsAtLocation(x, y); + if (header == null) return null; + header.height = getMonthHeaderHeight(); + return header; + } + + /** + * Returns the bounds of the month details which contains the + * given location. The bounds are in monthView coordinate system. + * + * @param x the x position of the location in pixel + * @param y the y position of the location in pixel + * @return the bounds of the details grid in the month at + * location or null if outside. + */ + protected Rectangle getMonthDetailsBoundsAtLocation(int x, int y) { + Rectangle month = getMonthBoundsAtLocation(x, y); + if (month == null) return null; + int startOfDaysY = month.y + getMonthHeaderHeight(); + if (y < startOfDaysY) return null; + month.y = startOfDaysY; + month.height = month.height - getMonthHeaderHeight(); + return month; + } + + + // ---------------------- mapping month coordinates + + /** + * Returns the bounds of the month which contains the + * given location. The bounds are in monthView coordinate system. + * + *

      + * + * Mapping pixel to bounds. + * + * @param x the x position of the location in pixel + * @param y the y position of the location in pixel + * @return the bounds of the month which contains the location, + * or null if outside + */ + protected Rectangle getMonthBoundsAtLocation(int x, int y) { + if (!calendarGrid.contains(x, y)) return null; + int calendarRow = (y - calendarGrid.y) / fullCalendarHeight; + int calendarColumn = (x - calendarGrid.x) / fullCalendarWidth; + return new Rectangle( + calendarGrid.x + calendarColumn * fullCalendarWidth, + calendarGrid.y + calendarRow * fullCalendarHeight, + calendarWidth, calendarHeight); + } + + + /** + * + * Returns the logical coordinates of the month which contains + * the given location. The p.x of the returned value represents the column, the + * p.y represents the row the month is shown in. The transformation takes + * care of ComponentOrientation.

      + * + * Mapping pixel to logical grid coordinates. + * + * @param x the x position of the location in pixel + * @param y the y position of the location in pixel + * @return the logical coordinates of the month in the grid of month shown by + * this monthView or null if outside. + */ + protected Point getMonthGridPositionAtLocation(int x, int y) { + if (!calendarGrid.contains(x, y)) return null; + int calendarRow = (y - calendarGrid.y) / fullCalendarHeight; + int calendarColumn = (x - calendarGrid.x) / fullCalendarWidth; + if (!isLeftToRight) { + int start = calendarGrid.x + calendarGrid.width; + calendarColumn = (start - x) / fullCalendarWidth; + + } + return new Point(calendarColumn, calendarRow); + } + + /** + * Returns the Date representing the start of the month which + * contains the given location.

      + * + * Mapping pixel to calendar day. + * + * @param x the x position of the location in pixel + * @param y the y position of the location in pixel + * @return the start of the month which contains the given location or + * null if the location is outside the grid of months. + */ + protected Date getMonthAtLocation(int x, int y) { + Point month = getMonthGridPositionAtLocation(x, y); + if (month == null) return null; + return getMonth(month.y, month.x); + } + + /** + * Returns the Date representing the start of the month at the given + * logical position in the grid of months.

      + * + * Mapping logical grid coordinates to Calendar. + * + * @param row the rowIndex in the grid of months. + * @param column the columnIndex in the grid months. + * @return a Date representing the start of the month at the given + * logical coordinates. + * + * @see #getMonthGridPosition(Date) + */ + protected Date getMonth(int row, int column) { + Calendar calendar = getCalendar(); + calendar.add(Calendar.MONTH, + row * calendarColumnCount + column); + return calendar.getTime(); + + } + + /** + * Returns the logical grid position of the month containing the given date. + * The Point's x value is the column in the grid of months, the y value + * is the row in the grid of months. + * + * Mapping Date to logical grid position, this is the reverse of getMonth(int, int). + * + * @param date the Date to return the bounds for. Must not be null. + * @return the postion of the month that contains the given date or null if not visible. + * + * @see #getMonth(int, int) + * @see #getMonthBounds(int, int) + */ + protected Point getMonthGridPosition(Date date) { + if (!isVisible(date)) return null; + // start of grid + Calendar calendar = getCalendar(); + int firstMonth = calendar.get(Calendar.MONTH); + int firstYear = calendar.get(Calendar.YEAR); + + // + calendar.setTime(date); + int month = calendar.get(Calendar.MONTH); + int year = calendar.get(Calendar.YEAR); + + int diffMonths = month - firstMonth + + ((year - firstYear) * JXMonthView.MONTHS_IN_YEAR); + + int row = diffMonths / calendarColumnCount; + int column = diffMonths % calendarColumnCount; + + return new Point(column, row); + } + + /** + * Returns the bounds of the month at the given logical coordinates + * in the grid of visible months.

      + * + * Mapping logical grip position to pixel. + * + * @param row the rowIndex in the grid of months. + * @param column the columnIndex in the grid months. + * @return the bounds of the month at the given logical logical position. + * + * @see #getMonthGridPositionAtLocation(int, int) + * @see #getMonthBoundsAtLocation(int, int) + */ + protected Rectangle getMonthBounds(int row, int column) { + int startY = calendarGrid.y + row * fullCalendarHeight; + int startX = calendarGrid.x + column * fullCalendarWidth; + if (!isLeftToRight) { + startX = calendarGrid.x + (calendarColumnCount - 1 - column) * fullCalendarWidth; + } + return new Rectangle(startX, startY, calendarWidth, calendarHeight); + } + + /** + * Returns the bounds of the month containing the given date. + * The bounds are in monthView coordinate system.

      + * + * Mapping Date to pixel. + * + * @param date the Date to return the bounds for. Must not be null. + * @return the bounds of the month that contains the given date or null if not visible. + * + * @see #getMonthAtLocation(int, int) + */ + protected Rectangle getMonthBounds(Date date) { + Point position = getMonthGridPosition(date); + return position != null ? getMonthBounds(position.y, position.x) : null; + } + + /** + * Returns the bounds of the month containing the given date. + * The bounds are in monthView coordinate system.

      + * + * Mapping Date to pixel. + * + * @param date the Date to return the bounds for. Must not be null. + * @return the bounds of the month that contains the given date or null if not visible. + * + * @see #getMonthAtLocation(int, int) + */ + protected Rectangle getMonthHeaderBounds(Date date, boolean includeInsets) { + Point position = getMonthGridPosition(date); + if (position == null) return null; + Rectangle bounds = getMonthBounds(position.y, position.x); + bounds.height = getMonthHeaderHeight(); + if (!includeInsets) { + + } + return bounds; + } + + + //---------------- accessors for sizes + + /** + * Returns the size of a month. + * @return the size of a month. + */ + protected Dimension getMonthSize() { + return new Dimension(calendarWidth, calendarHeight); + } + + /** + * Returns the size of a day including the padding. + * @return the size of a month. + */ + protected Dimension getDaySize() { + return new Dimension(fullBoxWidth, fullBoxHeight); + } + /** + * Returns the height of the month header. + * + * @return the height of the month header. + */ + protected int getMonthHeaderHeight() { + return fullMonthBoxHeight; + } + + + + //------------------- layout + + /** + * Called from layout: calculates properties + * of grid of months. + */ + private void calculateMonthGridLayoutProperties() { + calculateMonthGridRowColumnCount(); + calculateMonthGridBounds(); + } + + /** + * Calculates the bounds of the grid of months. + * + * CalendarRow/ColumnCount and calendarWidth/Height must be + * initialized before calling this. + */ + private void calculateMonthGridBounds() { + calendarGrid.setBounds(calculateCalendarGridX(), + calculateCalendarGridY(), + calculateCalendarGridWidth(), + calculateCalendarGridHeight()); + } + + + private int calculateCalendarGridY() { + return (monthView.getHeight() - calculateCalendarGridHeight()) / 2; + } + + private int calculateCalendarGridX() { + return (monthView.getWidth() - calculateCalendarGridWidth()) / 2; + } + + private int calculateCalendarGridHeight() { + return ((calendarHeight * calendarRowCount) + + (CALENDAR_SPACING * (calendarRowCount - 1 ))); + } + + private int calculateCalendarGridWidth() { + return ((calendarWidth * calendarColumnCount) + + (CALENDAR_SPACING * (calendarColumnCount - 1))); + } + + /** + * Calculates and updates the numCalCols/numCalRows that determine the + * number of calendars that can be displayed. Updates the last displayed + * date if appropriate. + * + */ + private void calculateMonthGridRowColumnCount() { + int oldNumCalCols = calendarColumnCount; + int oldNumCalRows = calendarRowCount; + + calendarRowCount = 1; + calendarColumnCount = 1; + if (!isZoomable()) { + // Determine how many columns of calendars we want to paint. + int addColumns = (monthView.getWidth() - calendarWidth) + / (calendarWidth + CALENDAR_SPACING); + // happens if used as renderer in a tree.. don't know yet why + if (addColumns > 0) { + calendarColumnCount += addColumns; + } + + // Determine how many rows of calendars we want to paint. + int addRows = (monthView.getHeight() - calendarHeight) + / (calendarHeight + CALENDAR_SPACING); + if (addRows > 0) { + calendarRowCount += addRows; + } + } + if (oldNumCalCols != calendarColumnCount + || oldNumCalRows != calendarRowCount) { + updateLastDisplayedDay(getFirstDisplayedDay()); + } + } + + /** + * @return true if the month view can be zoomed, false otherwise + */ + protected boolean isZoomable() { + return monthView.isZoomable(); + } + + + + +//-------------------- painting + + + /** + * Overridden to extract the background painting for ease-of-use of + * subclasses. + */ + @Override + public void update(Graphics g, JComponent c) { + paintBackground(g); + paint(g, c); + } + + /** + * Paints the background of the component. This implementation fill the + * monthView's area with its background color if opaque, does nothing + * if not opaque. Subclasses can override but must comply to opaqueness + * contract. + * + * @param g the Graphics to fill. + * + */ + protected void paintBackground(Graphics g) { + if (monthView.isOpaque()) { + g.setColor(monthView.getBackground()); + g.fillRect(0, 0, monthView.getWidth(), monthView.getHeight()); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void paint(Graphics g, JComponent c) { + Rectangle clip = g.getClipBounds(); + // Get a calender set to the first displayed date + Calendar cal = getCalendar(); + // loop through grid of months + for (int row = 0; row < calendarRowCount; row++) { + for (int column = 0; column < calendarColumnCount; column++) { + // get the bounds of the current month. + Rectangle bounds = getMonthBounds(row, column); + // Check if this row falls in the clip region. + if (bounds.intersects(clip)) { + paintMonth(g, cal); + } + cal.add(Calendar.MONTH, 1); + } + } + + } + + /** + * Paints the month represented by the given Calendar. + * + * Note: the given calendar must not be changed. + * @param g the graphics to paint into + * @param month the calendar specifying the first day of the month to + * paint, must not be null + */ + protected void paintMonth(Graphics g, Calendar month) { + paintMonthHeader(g, month); + paintDayHeader(g, month); + paintWeekHeader(g, month); + paintDays(g, month); + } + + /** + * Paints the header of a month. + * + * Note: the given calendar must not be changed. + * @param g the graphics to paint into + * @param month the calendar specifying the first day of the month to + * paint, must not be null + */ + protected void paintMonthHeader(Graphics g, Calendar month) { + Rectangle page = getMonthHeaderBounds(month.getTime(), false); + paintDayOfMonth(g, page, month, CalendarState.TITLE); + } + + /** + * Paints the day column header. + * + * Note: the given calendar must not be changed. + * @param g the graphics to paint into + * @param month the calendar specifying the first day of the month to + * paint, must not be null + */ + protected void paintDayHeader(Graphics g, Calendar month) { + paintDaysOfWeekSeparator(g, month); + Calendar cal = (Calendar) month.clone(); + CalendarUtils.startOfWeek(cal); + for (int i = FIRST_DAY_COLUMN; i <= LAST_DAY_COLUMN; i++) { + Rectangle dayBox = getDayBoundsInMonth(month.getTime(), DAY_HEADER_ROW, i); + paintDayOfMonth(g, dayBox, cal, CalendarState.DAY_OF_WEEK); + cal.add(Calendar.DATE, 1); + } + } + + /** + * Paints the day column header. + * + * Note: the given calendar must not be changed. + * @param g the graphics to paint into + * @param month the calendar specifying the first day of the month to + * paint, must not be null + */ + protected void paintWeekHeader(Graphics g, Calendar month) { + if (!monthView.isShowingWeekNumber()) + return; + paintWeekOfYearSeparator(g, month); + + int weeks = getWeeks(month); + // the calendar passed to the renderers + Calendar weekCalendar = (Calendar) month.clone(); + // we loop by logical row (== week in month) coordinates + for (int week = FIRST_WEEK_ROW; week < FIRST_WEEK_ROW + weeks; week++) { + // get the day bounds based on logical row/column coordinates + Rectangle dayBox = getDayBoundsInMonth(month.getTime(), week, WEEK_HEADER_COLUMN); + // NOTE: this can be set to any day in the week to render the weeknumber of + // categorized by CalendarState + paintDayOfMonth(g, dayBox, weekCalendar, CalendarState.WEEK_OF_YEAR); + weekCalendar.add(Calendar.WEEK_OF_YEAR, 1); + } + } + + /** + * Paints the days of the given month. + * + * Note: the given calendar must not be changed. + * @param g the graphics to paint into + * @param month the calendar specifying the first day of the month to + * paint, must not be null + */ + protected void paintDays(Graphics g, Calendar month) { + Calendar clonedCal = (Calendar) month.clone(); + CalendarUtils.startOfMonth(clonedCal); + Date startOfMonth = clonedCal.getTime(); + CalendarUtils.endOfMonth(clonedCal); + Date endOfMonth = clonedCal.getTime(); + // reset the clone + clonedCal.setTime(month.getTime()); + // adjust to start of week + clonedCal.setTime(month.getTime()); + CalendarUtils.startOfWeek(clonedCal); + for (int week = FIRST_WEEK_ROW; week <= LAST_WEEK_ROW; week++) { + for (int day = FIRST_DAY_COLUMN; day <= LAST_DAY_COLUMN; day++) { + CalendarState state = null; + if (clonedCal.getTime().before(startOfMonth)) { + if (monthView.isShowingLeadingDays()) { + state = CalendarState.LEADING; + } + } else if (clonedCal.getTime().after(endOfMonth)) { + if (monthView.isShowingTrailingDays()) { + state = CalendarState.TRAILING; + } + + } else { + state = isToday(clonedCal.getTime()) ? CalendarState.TODAY : CalendarState.IN_MONTH; + } + if (state != null) { + Rectangle bounds = getDayBoundsInMonth(startOfMonth, week, day); + paintDayOfMonth(g, bounds, clonedCal, state); + } + clonedCal.add(Calendar.DAY_OF_MONTH, 1); + } + } + } + + + /** + * Paints a day which is of the current month with the given state.

      + * + * PENDING JW: mis-nomer - this is in fact called for rendering any day-related + * state (including weekOfYear, dayOfWeek headers) and for rendering + * the month header as well, that is from everywhere. + * Rename to paintSomethingGeneral. Think about impact for subclasses + * (what do they really need? feedback please!) + * + * @param g the graphics to paint into. + * @param bounds the rectangle to paint the day into + * @param calendar the calendar representing the day to paint + * @param state the calendar state + */ + protected void paintDayOfMonth(Graphics g, Rectangle bounds, Calendar calendar, CalendarState state) { + JComponent comp = getRenderingHandler().prepareRenderingComponent(monthView, calendar, + state); + rendererPane.paintComponent(g, comp, monthView, bounds.x, bounds.y, + bounds.width, bounds.height, true); + } + + /** + * Paints the separator between row header (weeks of year) and days. + * + * Note: the given calendar must not be changed. + * @param g the graphics to paint into + * @param month the calendar specifying the first day of the month to + * paint, must not be null + */ + protected void paintWeekOfYearSeparator(Graphics g, Calendar month) { + Rectangle r = getSeparatorBounds(month, FIRST_WEEK_ROW, WEEK_HEADER_COLUMN); + if (r == null) return; + g.setColor(monthView.getForeground()); + g.drawLine(r.x, r.y, r.x, r.y + r.height); + } + + /** + * Paints the separator between column header (days of week) and days. + * + * Note: the given calendar must not be changed. + * @param g the graphics to paint into + * @param month the calendar specifying the the first day of the month to + * paint, must not be null + */ + protected void paintDaysOfWeekSeparator(Graphics g, Calendar month) { + Rectangle r = getSeparatorBounds(month, DAY_HEADER_ROW, FIRST_DAY_COLUMN); + if (r == null) return; + g.setColor(monthView.getForeground()); + g.drawLine(r.x, r.y, r.x + r.width, r.y); + } + + /** + * @param month + * @param row + * @param column + * @return + */ + private Rectangle getSeparatorBounds(Calendar month, int row, int column) { + Rectangle separator = getDayBoundsInMonth(month.getTime(), row, column); + if (separator == null) return null; + if (column == WEEK_HEADER_COLUMN) { + separator.height *= WEEKS_IN_MONTH; + if (isLeftToRight) { + separator.x += separator.width - 1; + } + separator.width = 1; + } else if (row == DAY_HEADER_ROW) { + int oldWidth = separator.width; + separator.width *= DAYS_IN_WEEK; + if (!isLeftToRight) { + separator.x -= separator.width - oldWidth; + } + separator.y += separator.height - 1; + separator.height = 1; + } + return separator; + } + + /** + * Returns the number of weeks to paint in the current month, as represented + * by the given calendar. The calendar is expected to be set to the first + * of the month. + * + * Note: the given calendar must not be changed. + * + * @param month the calendar specifying the the first day of the month to + * paint, must not be null + * @return the number of weeks of this month. + */ + protected int getWeeks(Calendar month) { + Calendar cloned = (Calendar) month.clone(); + // the calendar is set to the first of month, get date for last + CalendarUtils.endOfMonth(cloned); + // marker for end + Date last = cloned.getTime(); + // start again + cloned.setTime(month.getTime()); + CalendarUtils.startOfWeek(cloned); + int weeks = 0; + while (last.after(cloned.getTime())) { + weeks++; + cloned.add(Calendar.WEEK_OF_MONTH, 1); + } + return weeks; + } + + + + private void traverseMonth(int arrowType) { + if (arrowType == MONTH_DOWN) { + previousMonth(); + } else if (arrowType == MONTH_UP) { + nextMonth(); + } + } + + private void nextMonth() { + Date upperBound = monthView.getUpperBound(); + if (upperBound == null + || upperBound.after(getLastDisplayedDay()) ){ + Calendar cal = getCalendar(); + cal.add(Calendar.MONTH, 1); + monthView.setFirstDisplayedDay(cal.getTime()); + } + } + + private void previousMonth() { + Date lowerBound = monthView.getLowerBound(); + if (lowerBound == null + || lowerBound.before(getFirstDisplayedDay())){ + Calendar cal = getCalendar(); + cal.add(Calendar.MONTH, -1); + monthView.setFirstDisplayedDay(cal.getTime()); + } + } + +//--------------------------- displayed dates, calendar + + + /** + * Returns the monthViews calendar configured to the firstDisplayedDate. + * + * NOTE: it's safe to change the calendar state without resetting because + * it's JXMonthView's responsibility to protect itself. + * + * @return the monthView's calendar, configured with the firstDisplayedDate. + */ + protected Calendar getCalendar() { + return getCalendar(getFirstDisplayedDay()); + } + + /** + * Returns the monthViews calendar configured to the given time. + * + * NOTE: it's safe to change the calendar state without resetting because + * it's JXMonthView's responsibility to protect itself. + * + * @param date the date to configure the calendar with + * @return the monthView's calendar, configured with the given date. + */ + protected Calendar getCalendar(Date date) { + Calendar calendar = monthView.getCalendar(); + calendar.setTime(date); + return calendar; + } + + + + /** + * Updates the lastDisplayedDate property based on the given first and + * visible # of months. + * + * @param first the date of the first visible day. + */ + private void updateLastDisplayedDay(Date first) { + Calendar cal = getCalendar(first); + cal.add(Calendar.MONTH, ((calendarColumnCount * calendarRowCount) - 1)); + CalendarUtils.endOfMonth(cal); + lastDisplayedDate = cal.getTime(); + } + + + /** + * {@inheritDoc} + */ + @Override + public Date getLastDisplayedDay() { + return lastDisplayedDate; + } + + /*-------------- refactored: encapsulate aliased fields + */ + + /** + * Updates internal state that depends on the MonthView's firstDisplayedDay + * property.

      + * + * Here: updates lastDisplayedDay. + *

      + * + * + * @param firstDisplayedDay the firstDisplayedDate to set + */ + protected void setFirstDisplayedDay(Date firstDisplayedDay) { + updateLastDisplayedDay(firstDisplayedDay); + } + + /** + * Returns the first displayed day. Convenience delegate to + * + * @return the firstDisplayed + */ + protected Date getFirstDisplayedDay() { + return monthView.getFirstDisplayedDay(); + } + + /** + * @return the firstDisplayedMonth + */ + protected int getFirstDisplayedMonth() { + return getCalendar().get(Calendar.MONTH); + } + + + /** + * @return the firstDisplayedYear + */ + protected int getFirstDisplayedYear() { + return getCalendar().get(Calendar.YEAR); + } + + + /** + * @return the selection + */ + protected SortedSet getSelection() { + return monthView.getSelection(); + } + + + /** + * @return the start of today. + */ + protected Date getToday() { + return monthView.getToday(); + } + + /** + * Returns true if the date passed in is the same as today. + * + * PENDING JW: really want the exact test? + * + * @param date long representing the date you want to compare to today. + * @return true if the date passed is the same as today. + */ + protected boolean isToday(Date date) { + return date.equals(getToday()); + } + + +//-----------------------end encapsulation + + +//------------------ Handler implementation +// + /** + * temporary: removed SelectionMode.NO_SELECTION, replaced + * all access by this method to enable easy re-adding, if we want it. + * If not - remove. + */ + private boolean canSelectByMode() { + return true; + } + + + private class Handler implements + MouseListener, MouseMotionListener, LayoutManager, + PropertyChangeListener, DateSelectionListener { + private boolean armed; + private Date startDate; + private Date endDate; + + @Override + public void mouseClicked(MouseEvent e) {} + + @Override + public void mousePressed(MouseEvent e) { + // If we were using the keyboard we aren't anymore. + setUsingKeyboard(false); + + if (!monthView.isEnabled()) { + return; + } + + if (!monthView.hasFocus() && monthView.isFocusable()) { + monthView.requestFocusInWindow(); + } + + // Check if one of the month traverse buttons was pushed. + if (monthView.isTraversable()) { + int arrowType = getTraversableGridPositionAtLocation(e.getX(), e.getY()); + if (arrowType != -1) { + traverseMonth(arrowType); + return; + } + } + + if (!canSelectByMode()) { + return; + } + + Date cal = getDayAtLocation(e.getX(), e.getY()); + if (cal == null) { + return; + } + + // Update the selected dates. + startDate = cal; + endDate = cal; + + if (monthView.getSelectionMode() == SelectionMode.SINGLE_INTERVAL_SELECTION || +// selectionMode == SelectionMode.WEEK_INTERVAL_SELECTION || + monthView.getSelectionMode() == SelectionMode.MULTIPLE_INTERVAL_SELECTION) { + pivotDate = startDate; + } + + monthView.getSelectionModel().setAdjusting(true); + + if (monthView.getSelectionMode() == SelectionMode.MULTIPLE_INTERVAL_SELECTION && e.isControlDown()) { + monthView.addSelectionInterval(startDate, endDate); + } else { + monthView.setSelectionInterval(startDate, endDate); + } + + // Arm so we fire action performed on mouse release. + armed = true; + } + + + @Override + public void mouseReleased(MouseEvent e) { + // If we were using the keyboard we aren't anymore. + setUsingKeyboard(false); + + if (!monthView.isEnabled()) { + return; + } + + if (!monthView.hasFocus() && monthView.isFocusable()) { + monthView.requestFocusInWindow(); + } + + if (armed) { + monthView.commitSelection(); + } + armed = false; + } + + @Override + public void mouseEntered(MouseEvent e) {} + + @Override + public void mouseExited(MouseEvent e) {} + + @Override + public void mouseDragged(MouseEvent e) { + // If we were using the keyboard we aren't anymore. + setUsingKeyboard(false); + if (!monthView.isEnabled() || !canSelectByMode()) { + return; + } + Date cal = getDayAtLocation(e.getX(), e.getY()); + if (cal == null) { + return; + } + + Date selected = cal; + Date oldStart = startDate; + Date oldEnd = endDate; + + if (monthView.getSelectionMode() == SelectionMode.SINGLE_SELECTION) { + if (selected.equals(oldStart)) { + return; + } + startDate = selected; + endDate = selected; + } else if (pivotDate != null){ + if (selected.before(pivotDate)) { + startDate = selected; + endDate = pivotDate; + } else if (selected.after(pivotDate)) { + startDate = pivotDate; + endDate = selected; + } + } else { // pivotDate had not yet been initialiased + // might happen on first click into leading/trailing dates + // JW: fix of #996-swingx: NPE when dragging + startDate = selected; + endDate = selected; + pivotDate = selected; + } + + if (startDate.equals(oldStart) && endDate.equals(oldEnd)) { + return; + } + + if (monthView.getSelectionMode() == SelectionMode.MULTIPLE_INTERVAL_SELECTION && e.isControlDown()) { + monthView.addSelectionInterval(startDate, endDate); + } else { + monthView.setSelectionInterval(startDate, endDate); + } + + // Set trigger. + armed = true; + } + + @Override + public void mouseMoved(MouseEvent e) {} + +//------------------------ layout + + + private Dimension preferredSize = new Dimension(); + + @Override + public void addLayoutComponent(String name, Component comp) {} + + @Override + public void removeLayoutComponent(Component comp) {} + + @Override + public Dimension preferredLayoutSize(Container parent) { + layoutContainer(parent); + return new Dimension(preferredSize); + } + + @Override + public Dimension minimumLayoutSize(Container parent) { + return preferredLayoutSize(parent); + } + + @Override + public void layoutContainer(Container parent) { + + int maxMonthWidth = 0; + int maxMonthHeight = 0; + Calendar calendar = getCalendar(); + for (int i = calendar.getMinimum(Calendar.MONTH); i <= calendar.getMaximum(Calendar.MONTH); i++) { + calendar.set(Calendar.MONTH, i); + CalendarUtils.startOfMonth(calendar); + JComponent comp = getRenderingHandler().prepareRenderingComponent(monthView, calendar, CalendarState.TITLE); + Dimension pref = comp.getPreferredSize(); + maxMonthWidth = Math.max(maxMonthWidth, pref.width); + maxMonthHeight = Math.max(maxMonthHeight, pref.height); + } + + int maxBoxWidth = 0; + int maxBoxHeight = 0; + calendar = getCalendar(); + CalendarUtils.startOfWeek(calendar); + for (int i = 0; i < JXMonthView.DAYS_IN_WEEK; i++) { + JComponent comp = getRenderingHandler().prepareRenderingComponent(monthView, calendar, CalendarState.DAY_OF_WEEK); + Dimension pref = comp.getPreferredSize(); + maxBoxWidth = Math.max(maxBoxWidth, pref.width); + maxBoxHeight = Math.max(maxBoxHeight, pref.height); + calendar.add(Calendar.DATE, 1); + } + + calendar = getCalendar(); + for (int i = 0; i < calendar.getMaximum(Calendar.DAY_OF_MONTH); i++) { + JComponent comp = getRenderingHandler().prepareRenderingComponent(monthView, calendar, CalendarState.IN_MONTH); + Dimension pref = comp.getPreferredSize(); + maxBoxWidth = Math.max(maxBoxWidth, pref.width); + maxBoxHeight = Math.max(maxBoxHeight, pref.height); + calendar.add(Calendar.DATE, 1); + } + + int dayColumns = JXMonthView.DAYS_IN_WEEK; + if (monthView.isShowingWeekNumber()) { + dayColumns++; + } + + if (maxMonthWidth > maxBoxWidth * dayColumns) { + // monthHeader pref > sum of box widths + // handle here: increase day box width accordingly + double diff = maxMonthWidth - (maxBoxWidth * dayColumns); + maxBoxWidth += Math.ceil(diff/(double) dayColumns); + + } + + fullBoxWidth = maxBoxWidth; + fullBoxHeight = maxBoxHeight; + // PENDING JW: huuh? what we doing here? + int boxHeight = maxBoxHeight - 2 * monthView.getBoxPaddingY(); + fullMonthBoxHeight = Math.max(boxHeight, maxMonthHeight) ; + + // Keep track of calendar width and height for use later. + calendarWidth = fullBoxWidth * JXMonthView.DAYS_IN_WEEK; + if (monthView.isShowingWeekNumber()) { + calendarWidth += fullBoxWidth; + } + fullCalendarWidth = calendarWidth + CALENDAR_SPACING; + + calendarHeight = (fullBoxHeight * 7) + fullMonthBoxHeight; + fullCalendarHeight = calendarHeight + CALENDAR_SPACING; + // Calculate minimum width/height for the component. + int prefRows = getPreferredRows(); + preferredSize.height = (calendarHeight * prefRows) + + (CALENDAR_SPACING * (prefRows - 1)); + + int prefCols = getPreferredColumns(); + preferredSize.width = (calendarWidth * prefCols) + + (CALENDAR_SPACING * (prefCols - 1)); + + // Add insets to the dimensions. + Insets insets = monthView.getInsets(); + preferredSize.width += insets.left + insets.right; + preferredSize.height += insets.top + insets.bottom; + + calculateMonthGridLayoutProperties(); + + if (isZoomable()) { + getCalendarHeaderHandler().getHeaderComponent().setBounds(getMonthHeaderBounds(monthView.getFirstDisplayedDay(), false)); + } + } + + /** + * @return + */ + private int getPreferredColumns() { + return isZoomable() ? 1 : monthView.getPreferredColumnCount(); + } + + /** + * @return + */ + private int getPreferredRows() { + return isZoomable() ? 1 : monthView.getPreferredRowCount(); + } + + + + @Override + public void propertyChange(PropertyChangeEvent evt) { + String property = evt.getPropertyName(); + + if ("componentOrientation".equals(property)) { + isLeftToRight = monthView.getComponentOrientation().isLeftToRight(); + monthView.revalidate(); + monthView.repaint(); + } else if (JXMonthView.SELECTION_MODEL.equals(property)) { + DateSelectionModel selectionModel = (DateSelectionModel) evt.getOldValue(); + selectionModel.removeDateSelectionListener(getHandler()); + selectionModel = (DateSelectionModel) evt.getNewValue(); + selectionModel.addDateSelectionListener(getHandler()); + } else if ("firstDisplayedDay".equals(property)) { + setFirstDisplayedDay(((Date) evt.getNewValue())); + monthView.repaint(); + } else if (JXMonthView.BOX_PADDING_X.equals(property) + || JXMonthView.BOX_PADDING_Y.equals(property) + || JXMonthView.TRAVERSABLE.equals(property) + || JXMonthView.DAYS_OF_THE_WEEK.equals(property) + || "border".equals(property) + || "showingWeekNumber".equals(property) + || "traversable".equals(property) + + ) { + monthView.revalidate(); + monthView.repaint(); + } else if ("zoomable".equals(property)) { + updateZoomable(); +// } else if ("font".equals(property)) { +// calendarHeaderHandler.getHeaderComponent().setFont(getAsNotUIResource(createDerivedFont())); +// monthView.revalidate(); + } else if ("componentInputMapEnabled".equals(property)) { + updateComponentInputMap(); + } else if ("locale".equals(property)) { // "locale" is bound property + updateLocale(true); + } else { + monthView.repaint(); +// LOG.info("got propertyChange:" + property); + } + } + + @Override + public void valueChanged(DateSelectionEvent ev) { + monthView.repaint(); + } + + + } + + /** + * Class that supports keyboard traversal of the JXMonthView component. + */ + private class KeyboardAction extends AbstractAction { + public static final int ACCEPT_SELECTION = 0; + public static final int CANCEL_SELECTION = 1; + public static final int SELECT_PREVIOUS_DAY = 2; + public static final int SELECT_NEXT_DAY = 3; + public static final int SELECT_DAY_PREVIOUS_WEEK = 4; + public static final int SELECT_DAY_NEXT_WEEK = 5; + public static final int ADJUST_SELECTION_PREVIOUS_DAY = 6; + public static final int ADJUST_SELECTION_NEXT_DAY = 7; + public static final int ADJUST_SELECTION_PREVIOUS_WEEK = 8; + public static final int ADJUST_SELECTION_NEXT_WEEK = 9; + + private int action; + + public KeyboardAction(int action) { + this.action = action; + } + + @Override + public void actionPerformed(ActionEvent ev) { + if (!canSelectByMode()) + return; + if (!isUsingKeyboard()) { + originalDateSpan = getSelection(); + } + // JW: removed the isUsingKeyboard from the condition + // need to fire always. + if (action >= ACCEPT_SELECTION && action <= CANCEL_SELECTION) { + // refactor the logic ... + if (action == CANCEL_SELECTION) { + // Restore the original selection. + if ((originalDateSpan != null) + && !originalDateSpan.isEmpty()) { + monthView.setSelectionInterval( + originalDateSpan.first(), originalDateSpan + .last()); + } else { + monthView.clearSelection(); + } + monthView.cancelSelection(); + } else { + // Accept the keyboard selection. + monthView.commitSelection(); + } + setUsingKeyboard(false); + } else if (action >= SELECT_PREVIOUS_DAY + && action <= SELECT_DAY_NEXT_WEEK) { + setUsingKeyboard(true); + monthView.getSelectionModel().setAdjusting(true); + pivotDate = null; + traverse(action); + } else if (isIntervalMode() + && action >= ADJUST_SELECTION_PREVIOUS_DAY + && action <= ADJUST_SELECTION_NEXT_WEEK) { + setUsingKeyboard(true); + monthView.getSelectionModel().setAdjusting(true); + addToSelection(action); + } + } + + + /** + * @return + */ + private boolean isIntervalMode() { + return !(monthView.getSelectionMode() == SelectionMode.SINGLE_SELECTION); + } + + private void traverse(int action) { + Date oldStart = monthView.isSelectionEmpty() ? + monthView.getToday() : monthView.getFirstSelectionDate(); + Calendar cal = getCalendar(oldStart); + switch (action) { + case SELECT_PREVIOUS_DAY: + cal.add(Calendar.DAY_OF_MONTH, -1); + break; + case SELECT_NEXT_DAY: + cal.add(Calendar.DAY_OF_MONTH, 1); + break; + case SELECT_DAY_PREVIOUS_WEEK: + cal.add(Calendar.DAY_OF_MONTH, -JXMonthView.DAYS_IN_WEEK); + break; + case SELECT_DAY_NEXT_WEEK: + cal.add(Calendar.DAY_OF_MONTH, JXMonthView.DAYS_IN_WEEK); + break; + } + + Date newStartDate = cal.getTime(); + if (!newStartDate.equals(oldStart)) { + monthView.setSelectionInterval(newStartDate, newStartDate); + monthView.ensureDateVisible(newStartDate); + } + } + + /** + * If we are in a mode that allows for range selection this method + * will extend the currently selected range. + * + * NOTE: This may not be the expected behavior for the keyboard controls + * and we ay need to update this code to act in a way that people expect. + * + * @param action action for adjusting selection + */ + private void addToSelection(int action) { + Date newStartDate; + Date newEndDate; + Date selectionStart; + Date selectionEnd; + if (!monthView.isSelectionEmpty()) { + newStartDate = selectionStart = monthView.getFirstSelectionDate(); + newEndDate = selectionEnd = monthView.getLastSelectionDate(); + } else { + newStartDate = selectionStart = monthView.getToday(); + newEndDate = selectionEnd = newStartDate; + } + + if (pivotDate == null) { + pivotDate = newStartDate; + } + + // want a copy to play with - each branch sets and reads the time + // actually don't care about the pre-set time. + Calendar cal = getCalendar(); + boolean isStartMoved; + switch (action) { + case ADJUST_SELECTION_PREVIOUS_DAY: + if (newEndDate.after(pivotDate)) { + newEndDate = previousDay(cal, newEndDate); + isStartMoved = false; + } else { + newStartDate = previousDay(cal, newStartDate); + newEndDate = pivotDate; + isStartMoved = true; + } + break; + case ADJUST_SELECTION_NEXT_DAY: + if (newStartDate.before(pivotDate)) { + newStartDate = nextDay(cal, newStartDate); + isStartMoved = true; + } else { + newEndDate = nextDay(cal, newEndDate); + isStartMoved = false; + newStartDate = pivotDate; + } + break; + case ADJUST_SELECTION_PREVIOUS_WEEK: + if (newEndDate.after(pivotDate)) { + Date newTime = previousWeek(cal, newEndDate); + if (newTime.after(pivotDate)) { + newEndDate = newTime; + isStartMoved = false; + } else { + newStartDate = newTime; + newEndDate = pivotDate; + isStartMoved = true; + } + } else { + newStartDate = previousWeek(cal, newStartDate); + isStartMoved = true; + } + break; + case ADJUST_SELECTION_NEXT_WEEK: + if (newStartDate.before(pivotDate)) { + Date newTime = nextWeek(cal, newStartDate); + if (newTime.before(pivotDate)) { + newStartDate = newTime; + isStartMoved = true; + } else { + newStartDate = pivotDate; + newEndDate = newTime; + isStartMoved = false; + } + } else { + newEndDate = nextWeek(cal, newEndDate); + isStartMoved = false; + } + break; + default : throw new IllegalArgumentException("invalid adjustment action: " + action); + } + + if (!newStartDate.equals(selectionStart) || !newEndDate.equals(selectionEnd)) { + monthView.setSelectionInterval(newStartDate, newEndDate); + monthView.ensureDateVisible(isStartMoved ? newStartDate : newEndDate); + } + + } + + /** + * @param cal + * @param date + * @return + */ + private Date nextWeek(Calendar cal, Date date) { + cal.setTime(date); + cal.add(Calendar.DAY_OF_MONTH, JXMonthView.DAYS_IN_WEEK); + return cal.getTime(); + } + + /** + * @param cal + * @param date + * @return + */ + private Date previousWeek(Calendar cal, Date date) { + cal.setTime(date); + cal.add(Calendar.DAY_OF_MONTH, -JXMonthView.DAYS_IN_WEEK); + return cal.getTime(); + } + + /** + * @param cal + * @param date + * @return + */ + private Date nextDay(Calendar cal, Date date) { + cal.setTime(date); + cal.add(Calendar.DAY_OF_MONTH, 1); + return cal.getTime(); + } + + /** + * @param cal + * @param date + * @return + */ + private Date previousDay(Calendar cal, Date date) { + cal.setTime(date); + cal.add(Calendar.DAY_OF_MONTH, -1); + return cal.getTime(); + } + + + } + +//--------------------- zoomable + + /** + * Updates state after the monthView's zoomable property has been changed. + * This implementation adds/removes the header component if zoomable is true/false + * respectively. + */ + protected void updateZoomable() { + if (monthView.isZoomable()) { + monthView.add(getCalendarHeaderHandler().getHeaderComponent()); + } else { + monthView.remove(getCalendarHeaderHandler().getHeaderComponent()); + } + monthView.revalidate(); + monthView.repaint(); + } + + /** + * Creates and returns a calendar header handler which provides and configures + * a component for use in a zoomable monthView. Subclasses may override to return + * a custom handler.

      + * + * This implementation first queries the UIManager for class to use and returns + * that if available, returns a BasicCalendarHeaderHandler if not. + * + * @return a calendar header handler providing a component for use in zoomable + * monthView. + * + * @see #getHeaderFromUIManager() + * @see CalendarHeaderHandler + * @see BasicCalendarHeaderHandler + */ + protected CalendarHeaderHandler createCalendarHeaderHandler() { + CalendarHeaderHandler handler = getHeaderFromUIManager(); + return handler != null ? handler : new BasicCalendarHeaderHandler(); + } + + + /** + * Returns a CalendarHeaderHandler looked up in the UIManager. This implementation + * looks for a String registered with a key of CalendarHeaderHandler.uiControllerID. If + * found it assumes that the value is the class name of the handler and tries + * to instantiate the handler. + * + * @return a CalendarHeaderHandler from the UIManager or null if none + * available or instantiation failed. + */ + protected CalendarHeaderHandler getHeaderFromUIManager() { + Object handlerClass = UIManager.get(CalendarHeaderHandler.uiControllerID); + if (handlerClass instanceof String) { + return instantiateClass((String) handlerClass); + } + return null; + } + + /** + * @param handlerClassName + * @return + */ + private CalendarHeaderHandler instantiateClass(String handlerClassName) { + Class handler = null; + try { + handler = Class.forName(handlerClassName); + return instantiateClass(handler); + } catch (ClassNotFoundException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return null; + } + + /** + * @param handlerClass + * @return + */ + private CalendarHeaderHandler instantiateClass(Class handlerClass) { + Constructor constructor = null; + try { + constructor = handlerClass.getConstructor(); + } catch (SecurityException e) { + LOG.finer("cant instantiate CalendarHeaderHandler (security) " + handlerClass); + } catch (NoSuchMethodException e) { + LOG.finer("cant instantiate CalendarHeaderHandler (missing parameterless constructo?)" + handlerClass); + } + if (constructor != null) { + try { + return (CalendarHeaderHandler) constructor.newInstance(); + } catch (IllegalArgumentException e) { + LOG.finer("cant instantiate CalendarHeaderHandler (missing parameterless constructo?)" + handlerClass); + } catch (InstantiationException e) { + LOG.finer("cant instantiate CalendarHeaderHandler (not instantiable) " + handlerClass); + } catch (IllegalAccessException e) { + LOG.finer("cant instantiate CalendarHeaderHandler (constructor not public) " + handlerClass); + } catch (InvocationTargetException e) { + LOG.finer("cant instantiate CalendarHeaderHandler (Invocation target)" + handlerClass); + } + } + return null; + } + + /** + * @param calendarHeaderHandler the calendarHeaderHandler to set + */ + protected void setCalendarHeaderHandler(CalendarHeaderHandler calendarHeaderHandler) { + this.calendarHeaderHandler = calendarHeaderHandler; + } + + /** + * @return the calendarHeaderHandler + */ + protected CalendarHeaderHandler getCalendarHeaderHandler() { + return calendarHeaderHandler; + } + + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicMultiThumbSliderUI.java b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicMultiThumbSliderUI.java new file mode 100644 index 0000000000..a1c99f16f4 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicMultiThumbSliderUI.java @@ -0,0 +1,95 @@ +/* + * $Id: BasicMultiThumbSliderUI.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf.basic; + +import org.jdesktop.swingx.JXMultiThumbSlider; +import org.jdesktop.swingx.multislider.ThumbRenderer; +import org.jdesktop.swingx.multislider.TrackRenderer; +import org.jdesktop.swingx.plaf.MultiThumbSliderUI; + +import javax.swing.*; +import javax.swing.plaf.ComponentUI; +import java.awt.*; + +/** + * + * @author Joshua Marinacci + */ +public class BasicMultiThumbSliderUI extends MultiThumbSliderUI { + + protected JXMultiThumbSlider slider; + + public static ComponentUI createUI(JComponent c) { + return new BasicMultiThumbSliderUI(); + } + + @Override + public void installUI(JComponent c) { + slider = (JXMultiThumbSlider)c; + slider.setThumbRenderer(new BasicThumbRenderer()); + slider.setTrackRenderer(new BasicTrackRenderer()); + } + @Override + public void uninstallUI(JComponent c) { + slider = null; + } + + private class BasicThumbRenderer extends JComponent implements ThumbRenderer { + public BasicThumbRenderer() { + setPreferredSize(new Dimension(14,14)); + } + + @Override + protected void paintComponent(Graphics g) { + g.setColor(Color.green); + Polygon poly = new Polygon(); + JComponent thumb = this; + poly.addPoint(thumb.getWidth()/2,0); + poly.addPoint(0,thumb.getHeight()/2); + poly.addPoint(thumb.getWidth()/2,thumb.getHeight()); + poly.addPoint(thumb.getWidth(),thumb.getHeight()/2); + g.fillPolygon(poly); + } + + @Override + public JComponent getThumbRendererComponent(JXMultiThumbSlider slider, int index, boolean selected) { + return this; + } + } + + private class BasicTrackRenderer extends JComponent implements TrackRenderer { + private JXMultiThumbSlider slider; + @Override + public void paintComponent(Graphics g) { + g.setColor(slider.getBackground()); + g.fillRect(0, 0, slider.getWidth(), slider.getHeight()); + g.setColor(Color.black); + g.drawLine(0,slider.getHeight()/2,slider.getWidth(),slider.getHeight()/2); + g.drawLine(0,slider.getHeight()/2+1,slider.getWidth(),slider.getHeight()/2+1); + } + + @Override + public JComponent getRendererComponent(JXMultiThumbSlider slider) { + this.slider = slider; + return this; + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicStatusBarUI.java b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicStatusBarUI.java new file mode 100644 index 0000000000..fd614c622b --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicStatusBarUI.java @@ -0,0 +1,625 @@ +/* + * $Id: BasicStatusBarUI.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.plaf.basic; + +import org.jdesktop.swingx.JXStatusBar; +import org.jdesktop.swingx.JXStatusBar.Constraint; +import org.jdesktop.swingx.plaf.StatusBarUI; +import org.jdesktop.swingx.plaf.UIManagerExt; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.plaf.BorderUIResource; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.UIResource; +import java.awt.*; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.HashMap; +import java.util.Map; + +/** + * + * @author rbair + * @author Karl Schaefer + */ +public class BasicStatusBarUI extends StatusBarUI { + private class Handler implements MouseListener, MouseMotionListener, PropertyChangeListener { + private Window window = SwingUtilities.getWindowAncestor(statusBar); + private int handleBoundary = getHandleBoundary(); + private boolean validPress = false; + private Point startingPoint; + + private int getHandleBoundary() { + Border border = statusBar.getBorder(); + + if (border == null || !statusBar.isResizeHandleEnabled()) { + return 0; + } + + if (statusBar.getComponentOrientation().isLeftToRight()) { + return border.getBorderInsets(statusBar).right; + } else { + return border.getBorderInsets(statusBar).left; + } + } + + private boolean isHandleAreaPoint(Point point) { + if (window == null || window.isMaximumSizeSet()) { + return false; + } + + if (statusBar.getComponentOrientation().isLeftToRight()) { + return point.x >= statusBar.getWidth() - handleBoundary; + } else { + return point.x <= handleBoundary; + } + } + + /** + * {@inheritDoc} + */ + @Override + public void mouseClicked(MouseEvent e) { + //does nothing + } + + /** + * {@inheritDoc} + */ + @Override + public void mouseEntered(MouseEvent e) { + if (isHandleAreaPoint(e.getPoint())) { + if (statusBar.getComponentOrientation().isLeftToRight()) { + window.setCursor(Cursor.getPredefinedCursor( + Cursor.SE_RESIZE_CURSOR)); + } else { + window.setCursor(Cursor.getPredefinedCursor( + Cursor.SW_RESIZE_CURSOR)); + } + } else { + window.setCursor(null); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void mouseExited(MouseEvent e) { + if (!validPress) { + window.setCursor(null); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void mousePressed(MouseEvent e) { + validPress = SwingUtilities.isLeftMouseButton(e) && isHandleAreaPoint(e.getPoint()); + startingPoint = e.getPoint(); + SwingUtilities.convertPointToScreen(startingPoint, statusBar); + } + + /** + * {@inheritDoc} + */ + @Override + public void mouseReleased(MouseEvent e) { + validPress = !SwingUtilities.isLeftMouseButton(e); + window.validate(); + window.setCursor(null); + } + + /** + * {@inheritDoc} + */ + @Override + public void mouseDragged(MouseEvent e) { + if (validPress) { + Rectangle wb = window.getBounds(); + Point p = e.getPoint(); + SwingUtilities.convertPointToScreen(p, statusBar); + + wb.height += (p.y - startingPoint.y); + if (statusBar.getComponentOrientation().isLeftToRight()) { + wb.width += (p.x - startingPoint.x); + } else { + wb.x += (p.x - startingPoint.x); + wb.width += (startingPoint.x - p.x); + } + + window.setBounds(wb); + window.validate(); + startingPoint = p; + } + } + + /** + * {@inheritDoc} + */ + @Override + public void mouseMoved(MouseEvent e) { + if (isHandleAreaPoint(e.getPoint())) { + if (statusBar.getComponentOrientation().isLeftToRight()) { + window.setCursor(Cursor.getPredefinedCursor( + Cursor.SE_RESIZE_CURSOR)); + } else { + window.setCursor(Cursor.getPredefinedCursor( + Cursor.SW_RESIZE_CURSOR)); + } + } else { + window.setCursor(null); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void propertyChange(PropertyChangeEvent evt) { + if ("ancestor".equals(evt.getPropertyName())) { + window = SwingUtilities.getWindowAncestor(statusBar); + + boolean useResizeHandle = statusBar.getParent() != null + && statusBar.getRootPane() != null + && (statusBar.getParent() == statusBar.getRootPane() + || statusBar.getParent() == statusBar.getRootPane().getContentPane()); + statusBar.setResizeHandleEnabled(useResizeHandle); + } else if ("border".equals(evt.getPropertyName())) { + handleBoundary = getHandleBoundary(); + } else if ("componentOrientation".equals(evt.getPropertyName())) { + handleBoundary = getHandleBoundary(); + } else if ("resizeHandleEnabled".equals(evt.getPropertyName())) { + //TODO disable handle display + handleBoundary = getHandleBoundary(); + } + } + } + + public static final String AUTO_ADD_SEPARATOR = new StringBuffer("auto-add-separator").toString(); + /** + * Used to help reduce the amount of trash being generated + */ + private static Insets TEMP_INSETS; + /** + * The one and only JXStatusBar for this UI delegate + */ + protected JXStatusBar statusBar; + + protected MouseListener mouseListener; + + protected MouseMotionListener mouseMotionListener; + + protected PropertyChangeListener propertyChangeListener; + + private Handler handler; + + /** Creates a new instance of BasicStatusBarUI */ + public BasicStatusBarUI() { + } + + /** + * Returns an instance of the UI delegate for the specified component. + * Each subclass must provide its own static createUI + * method that returns an instance of that UI delegate subclass. + * If the UI delegate subclass is stateless, it may return an instance + * that is shared by multiple components. If the UI delegate is + * stateful, then it should return a new instance per component. + * The default implementation of this method throws an error, as it + * should never be invoked. + */ + public static ComponentUI createUI(JComponent c) { + return new BasicStatusBarUI(); + } + + /** + * {@inheritDoc} + */ + @Override + public void installUI(JComponent c) { + assert c instanceof JXStatusBar; + statusBar = (JXStatusBar)c; + + installDefaults(statusBar); + installListeners(statusBar); + + // only set the layout manager if the layout manager of the component is + // null or a UIResource. Do not replace custom layout managers. + LayoutManager m = statusBar.getLayout(); + if (m == null || m instanceof UIResource) { + statusBar.setLayout(createLayout()); + } + } + + protected void installDefaults(JXStatusBar sb) { + //only set the border if it is an instanceof UIResource + //In other words, only replace the border if it has not been + //set by the developer. UIResource is the flag we use to indicate whether + //the value was set by the UIDelegate, or by the developer. + Border b = statusBar.getBorder(); + if (b == null || b instanceof UIResource) { + statusBar.setBorder(createBorder()); + } + + LookAndFeel.installProperty(sb, "opaque", Boolean.TRUE); + } + + private Handler getHandler() { + if (handler == null) { + handler = new Handler(); + } + + return handler; + } + + /** + * Creates a {@code MouseListener} which will be added to the + * status bar. If this method returns null then it will not + * be added to the status bar. + *

      + * Subclasses may override this method to return instances of their own + * MouseEvent handlers. + * + * @return an instance of a {@code MouseListener} or null + */ + protected MouseListener createMouseListener() { + return getHandler(); + } + + /** + * Creates a {@code MouseMotionListener} which will be added to the + * status bar. If this method returns null then it will not + * be added to the status bar. + *

      + * Subclasses may override this method to return instances of their own + * MouseEvent handlers. + * + * @return an instance of a {@code MouseMotionListener} or null + */ + protected MouseMotionListener createMouseMotionListener() { + return getHandler(); + } + + /** + * Creates a {@code PropertyChangeListener} which will be added to the + * status bar. If this method returns null then it will not + * be added to the status bar. + *

      + * Subclasses may override this method to return instances of their own + * PropertyChangeEvent handlers. + * + * @return an instance of a {@code PropertyChangeListener} or null + */ + protected PropertyChangeListener createPropertyChangeListener() { + return getHandler(); + } + + /** + * Create and install the listeners for the status bar. + * This method is called when the UI is installed. + */ + protected void installListeners(JXStatusBar sb) { + if ((mouseListener = createMouseListener()) != null) { + statusBar.addMouseListener(mouseListener); + } + + if ((mouseMotionListener = createMouseMotionListener()) != null) { + statusBar.addMouseMotionListener(mouseMotionListener); + } + + if ((propertyChangeListener = createPropertyChangeListener()) != null) { + statusBar.addPropertyChangeListener(propertyChangeListener); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void uninstallUI(JComponent c) { + assert c instanceof JXStatusBar; + + uninstallDefaults(statusBar); + uninstallListeners(statusBar); + + if (statusBar.getLayout() instanceof UIResource) { + statusBar.setLayout(null); + } + } + + protected void uninstallDefaults(JXStatusBar sb) { + if (sb.getBorder() instanceof UIResource) { + sb.setBorder(null); + } + } + + /** + * Remove the installed listeners from the status bar. + * The number and types of listeners removed in this method should be + * the same that were added in installListeners + */ + protected void uninstallListeners(JXStatusBar sb) { + if (mouseListener != null) { + statusBar.removeMouseListener(mouseListener); + } + + if (mouseMotionListener != null) { + statusBar.removeMouseMotionListener(mouseMotionListener); + } + + if (propertyChangeListener != null) { + statusBar.removePropertyChangeListener(propertyChangeListener); + } + } + + @Override + public void paint(Graphics g, JComponent c) { + //paint the background if opaque + if (statusBar.isOpaque()) { + Graphics2D g2 = (Graphics2D)g; + paintBackground(g2, statusBar); + } + + if (includeSeparators()) { + //now paint the separators + TEMP_INSETS = getSeparatorInsets(TEMP_INSETS); + for (int i=0; i constraints = new HashMap(); + + @Override + public void addLayoutComponent(String name, Component comp) {addLayoutComponent(comp, null);} + @Override + public void removeLayoutComponent(Component comp) {constraints.remove(comp);} + @Override + public Dimension minimumLayoutSize(Container parent) {return preferredLayoutSize(parent);} + @Override + public Dimension maximumLayoutSize(Container target) {return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);} + @Override + public float getLayoutAlignmentX(Container target) {return .5f;} + @Override + public float getLayoutAlignmentY(Container target) {return .5f;} + @Override + public void invalidateLayout(Container target) {} + + @Override + public void addLayoutComponent(Component comp, Object constraint) { + //we accept an Insets, a ResizeBehavior, or a Constraint. + if (constraint instanceof Insets) { + constraint = new Constraint((Insets)constraint); + } else if (constraint instanceof Constraint.ResizeBehavior) { + constraint = new Constraint((Constraint.ResizeBehavior)constraint); + } + + constraints.put(comp, (Constraint)constraint); + } + + @Override + public Dimension preferredLayoutSize(Container parent) { + Dimension prefSize = new Dimension(); + int count = 0; + for (Component comp : constraints.keySet()) { + Constraint c = constraints.get(comp); + Dimension d = comp.getPreferredSize(); + int prefWidth = 0; + if (c != null) { + Insets i = c.getInsets(); + d.width += i.left + i.right; + d.height += i.top + i.bottom; + prefWidth = c.getFixedWidth(); + } + prefSize.height = Math.max(prefSize.height, d.height); + prefSize.width += Math.max(d.width, prefWidth); + + //If this is not the last component, add extra space between each + //component (for the separator). + count++; + if (includeSeparators() && constraints.size() < count) { + prefSize.width += getSeparatorWidth(); + } + } + + Insets insets = parent.getInsets(); + prefSize.height += insets.top + insets.bottom; + prefSize.width += insets.left + insets.right; + return prefSize; + } + + @Override + public void layoutContainer(Container parent) { + /* + * Layout algorithm: + * If the parent width is less than the sum of the preferred + * widths of the components (including separators), where + * preferred width means either the component preferred width + + * constraint insets, or fixed width + constraint insets, then + * simply layout the container from left to right and let the + * right hand components flow off the parent. + * + * Otherwise, lay out each component according to its preferred + * width except for components with a FILL constraint. For these, + * resize them evenly for each FILL constraint. + */ + + //the insets of the parent component. + Insets parentInsets = parent.getInsets(); + //the available width for putting components. + int availableWidth = parent.getWidth() - parentInsets.left - parentInsets.right; + if (includeSeparators()) { + //remove from availableWidth the amount of space the separators will take + availableWidth -= (parent.getComponentCount() - 1) * getSeparatorWidth(); + } + + //the preferred widths of all of the components -- where preferred + //width mean the preferred width after calculating fixed widths and + //constraint insets + int[] preferredWidths = new int[parent.getComponentCount()]; + int sumPreferredWidths = 0; + for (int i=0; i sumPreferredWidths) { + //the number of components with a fill constraint + int numFilledComponents = 0; + for (Component comp : parent.getComponents()) { + Constraint c = constraints.get(comp); + if (c != null && c.getResizeBehavior() == Constraint.ResizeBehavior.FILL) { + numFilledComponents++; + } + } + + if (numFilledComponents > 0) { + //calculate the share of free space each FILL component will take + availableWidth -= sumPreferredWidths; + double weight = 1.0 / (double)numFilledComponents; + int share = (int)(availableWidth * weight); + int remaining = numFilledComponents; + for (int i=0; i 1) { + preferredWidths[i] += share; + availableWidth -= share; + } else { + preferredWidths[i] += availableWidth; + } + remaining--; + } + } + } + } + + //now lay out the components + int nextX = parentInsets.left; + int height = parent.getHeight() - parentInsets.top - parentInsets.bottom; + for (int i=0; iJXTaskPaneContainer UI. + * + * @author Frederic Lavigne + * @author Karl Schaefer + */ +public class BasicTaskPaneContainerUI extends TaskPaneContainerUI { + /** + * A {@code UIResource} implementation of {@code VerticalLayout}. + * + * @author Karl George Schaefer + */ + protected class VerticalLayoutUIResource extends VerticalLayout implements UIResource { + /** + * The default layout. + */ + public VerticalLayoutUIResource() { + super(); + } + + /** + * Defines a layout with the specified gap. + * + * @param gap + * the gap between components + */ + public VerticalLayoutUIResource(int gap) { + super(gap); + } + } + + /** + * Returns a new instance of BasicTaskPaneContainerUI. + * BasicTaskPaneContainerUI delegates are allocated one per + * JXTaskPaneContainer. + * + * @return A new TaskPaneContainerUI implementation for the Basic look and + * feel. + */ + public static ComponentUI createUI(JComponent c) { + return new BasicTaskPaneContainerUI(); + } + + /** + * The task pane container managed by this UI delegate. + */ + protected JXTaskPaneContainer taskPane; + + /** + * {@inheritDoc} + */ + @Override +public void installUI(JComponent c) { + super.installUI(c); + taskPane = (JXTaskPaneContainer)c; + installDefaults(); + + LayoutManager manager = taskPane.getLayout(); + + if (manager == null || manager instanceof UIResource) { + taskPane.setLayout(createDefaultLayout()); + } + } + + /** + * Installs the default colors, border, and painter of the task pane + * container. + */ + protected void installDefaults() { + LookAndFeel.installColors(taskPane, "TaskPaneContainer.background", + "TaskPaneContainer.foreground"); + LookAndFeel.installBorder(taskPane, "TaskPaneContainer.border"); + LookAndFeelAddons.installBackgroundPainter(taskPane, + "TaskPaneContainer.backgroundPainter"); + LookAndFeel.installProperty(taskPane, "opaque", Boolean.TRUE); + } + + /** + * Constructs a layout manager to be used by the Look and Feel. + * @return the layout manager for the current Look and Feel + */ + protected LayoutManager createDefaultLayout() { + return new VerticalLayoutUIResource(14); + } + + /** + * {@inheritDoc} + */ + @Override + public void uninstallUI(JComponent c) { + uninstallDefaults(); + + super.uninstallUI(c); + } + + /** + * Uninstalls the default colors, border, and painter of the task pane + * container. + */ + protected void uninstallDefaults() { + LookAndFeel.uninstallBorder(taskPane); + LookAndFeelAddons.uninstallBackgroundPainter(taskPane); + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicTaskPaneUI.java b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicTaskPaneUI.java new file mode 100644 index 0000000000..11d52f4ca4 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicTaskPaneUI.java @@ -0,0 +1,874 @@ +/* + * $Id: BasicTaskPaneUI.java 4260 2012-11-14 20:59:08Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf.basic; + +import org.jdesktop.swingx.JXCollapsiblePane; +import org.jdesktop.swingx.JXHyperlink; +import org.jdesktop.swingx.JXTaskPane; +import org.jdesktop.swingx.SwingXUtilities; +import org.jdesktop.swingx.icon.EmptyIcon; +import org.jdesktop.swingx.plaf.TaskPaneUI; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.event.MouseInputAdapter; +import javax.swing.event.MouseInputListener; +import javax.swing.plaf.*; +import javax.swing.plaf.basic.BasicGraphicsUtils; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.MouseEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +import static org.jdesktop.swingx.SwingXUtilities.isUIInstallable; + +/** + * Base implementation of the JXTaskPane UI. + * + * @author Frederic Lavigne + */ +public class BasicTaskPaneUI extends TaskPaneUI { + + private static FocusListener focusListener = new RepaintOnFocus(); + + public static ComponentUI createUI(JComponent c) { + return new BasicTaskPaneUI(); + } + + protected int titleHeight = 25; + protected int roundHeight = 5; + + protected JXTaskPane group; + + protected boolean mouseOver; + protected MouseInputListener mouseListener; + + protected PropertyChangeListener propertyListener; + + /** + * {@inheritDoc} + */ + @Override + public void installUI(JComponent c) { + super.installUI(c); + group = (JXTaskPane) c; + + installDefaults(); + installListeners(); + installKeyboardActions(); + } + + /** + * Installs default properties. Following properties are installed: + *

        + *
      • TaskPane.background
      • + *
      • TaskPane.foreground
      • + *
      • TaskPane.font
      • + *
      • TaskPane.borderColor
      • + *
      • TaskPane.titleForeground
      • + *
      • TaskPane.titleBackgroundGradientStart
      • + *
      • TaskPane.titleBackgroundGradientEnd
      • + *
      • TaskPane.titleOver
      • + *
      • TaskPane.specialTitleOver
      • + *
      • TaskPane.specialTitleForeground
      • + *
      • TaskPane.specialTitleBackground
      • + *
      + */ + protected void installDefaults() { + LookAndFeel.installColorsAndFont(group, "TaskPane.background", + "TaskPane.foreground", "TaskPane.font"); + LookAndFeel.installProperty(group, "opaque", false); + + if (isUIInstallable(group.getBorder())) { + group.setBorder(createPaneBorder()); + } + + if (group.getContentPane() instanceof JComponent) { + JComponent content = (JComponent) group.getContentPane(); + + LookAndFeel.installColorsAndFont(content, + "TaskPane.background", "TaskPane.foreground", "TaskPane.font"); + + if (isUIInstallable(content.getBorder())) { + content.setBorder(createContentPaneBorder()); + } + } + } + + /** + * Installs listeners for UI delegate. + */ + protected void installListeners() { + mouseListener = createMouseInputListener(); + group.addMouseMotionListener(mouseListener); + group.addMouseListener(mouseListener); + + group.addFocusListener(focusListener); + propertyListener = createPropertyListener(); + group.addPropertyChangeListener(propertyListener); + } + + /** + * Installs keyboard actions to allow task pane to react on hot keys. + */ + protected void installKeyboardActions() { + InputMap inputMap = (InputMap) UIManager.get("TaskPane.focusInputMap"); + if (inputMap != null) { + SwingUtilities.replaceUIInputMap(group, JComponent.WHEN_FOCUSED, + inputMap); + } + + ActionMap map = getActionMap(); + if (map != null) { + SwingUtilities.replaceUIActionMap(group, map); + } + } + + ActionMap getActionMap() { + ActionMap map = new ActionMapUIResource(); + map.put("toggleCollapsed", new ToggleCollapsedAction()); + return map; + } + + @Override + public void uninstallUI(JComponent c) { + uninstallListeners(); + super.uninstallUI(c); + } + + /** + * Uninstalls previously installed listeners to free component for garbage collection. + */ + protected void uninstallListeners() { + group.removeMouseListener(mouseListener); + group.removeMouseMotionListener(mouseListener); + group.removeFocusListener(focusListener); + group.removePropertyChangeListener(propertyListener); + } + + /** + * Creates new toggle listener. + * @return MouseInputListener reacting on toggle events of task pane. + */ + protected MouseInputListener createMouseInputListener() { + return new ToggleListener(); + } + + /** + * Creates property change listener for task pane. + * @return Property change listener reacting on changes to the task pane. + */ + protected PropertyChangeListener createPropertyListener() { + return new ChangeListener(); + } + + /** + * Evaluates whenever given mouse even have occurred within borders of task pane. + * @param event Evaluated event. + * @return True if event occurred within task pane area, false otherwise. + */ + protected boolean isInBorder(MouseEvent event) { + return event.getY() < getTitleHeight(event.getComponent()); + } + + /** + * Gets current title height. Default value is 25 if not specified otherwise. Method checks + * provided component for user set font (!instanceof FontUIResource), if font is set, height + * will be calculated from font metrics instead of using internal preset height. + * @return Current title height. + */ + protected int getTitleHeight(Component c) { + if (c instanceof JXTaskPane) { + JXTaskPane taskPane = (JXTaskPane) c; + Font font = taskPane.getFont(); + int height = titleHeight; + + if (font != null && !(font instanceof FontUIResource)) { + height = Math.max(height, taskPane.getFontMetrics(font).getHeight()); + } + + Icon icon = taskPane.getIcon(); + + if (icon != null) { + height = Math.max(height, icon.getIconHeight() + 4); + } + + return height; + } + + return titleHeight; + } + + /** + * Creates new border for task pane. + * @return Fresh border on every call. + */ + protected Border createPaneBorder() { + return new PaneBorder(); + } + + @Override + public Dimension getPreferredSize(JComponent c) { + Component component = group.getComponent(0); + if (!(component instanceof JXCollapsiblePane)) { + // something wrong in this JXTaskPane + return super.getPreferredSize(c); + } + + JXCollapsiblePane collapsible = (JXCollapsiblePane) component; + Dimension dim = collapsible.getPreferredSize(); + + Border groupBorder = group.getBorder(); + if (groupBorder instanceof PaneBorder) { + ((PaneBorder) groupBorder).label.setDisplayedMnemonic(group + .getMnemonic()); + Dimension border = ((PaneBorder) groupBorder) + .getPreferredSize(group); + dim.width = Math.max(dim.width, border.width); + dim.height += border.height; + } else { + dim.height += getTitleHeight(c); + } + + return dim; + } + + /** + * Creates content pane border. + * @return Fresh content pane border initialized with current value of TaskPane.borderColor + * on every call. + */ + protected Border createContentPaneBorder() { + Color borderColor = UIManager.getColor("TaskPane.borderColor"); + return new CompoundBorder(new ContentPaneBorder(borderColor), + BorderFactory.createEmptyBorder(10, 10, 10, 10)); + } + + @Override + public Component createAction(Action action) { + JXHyperlink link = new JXHyperlink(action) { + @Override + public void updateUI() { + super.updateUI(); + // ensure the ui of this link is correctly update on l&f changes + configure(this); + } + }; + configure(link); + return link; + } + + /** + * Configures internally used hyperlink on new action creation and on every call to + * updateUI(). + * @param link Configured hyperlink. + */ + protected void configure(JXHyperlink link) { + link.setOpaque(false); + link.setBorderPainted(false); + link.setFocusPainted(true); + link.setForeground(UIManager.getColor("TaskPane.titleForeground")); + } + + /** + * Ensures expanded group is visible. Issues delayed request for scrolling to visible. + */ + protected void ensureVisible() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + group.scrollRectToVisible(new Rectangle(group.getWidth(), group + .getHeight())); + } + }); + } + + /** + * Focus listener responsible for repainting of the taskpane on focus change. + */ + static class RepaintOnFocus implements FocusListener { + @Override + public void focusGained(FocusEvent e) { + e.getComponent().repaint(); + } + + @Override + public void focusLost(FocusEvent e) { + e.getComponent().repaint(); + } + } + + /** + * Change listener responsible for change handling. + */ + class ChangeListener implements PropertyChangeListener { + @Override + public void propertyChange(PropertyChangeEvent evt) { + // if group is expanded but not animated + // or if animated has reached expanded state + // scroll to visible if scrollOnExpand is enabled + if (("collapsed".equals(evt.getPropertyName()) + && Boolean.TRUE.equals(evt.getNewValue()) && !group + .isAnimated())) { + if (group.isScrollOnExpand()) { + ensureVisible(); + } + } else if (JXTaskPane.ICON_CHANGED_KEY + .equals(evt.getPropertyName()) + || JXTaskPane.TITLE_CHANGED_KEY.equals(evt + .getPropertyName()) + || JXTaskPane.SPECIAL_CHANGED_KEY.equals(evt + .getPropertyName())) { + // icon, title, special must lead to a repaint() + group.repaint(); + } else if ("mnemonic".equals(evt.getPropertyName())) { + SwingXUtilities.updateMnemonicBinding(group, "toggleCollapsed"); + + Border b = group.getBorder(); + + if (b instanceof PaneBorder) { + int key = (Integer) evt.getNewValue(); + ((PaneBorder) b).label.setDisplayedMnemonic(key); + } + } + } + } + + /** + * Mouse listener responsible for handling of toggle events. + */ + class ToggleListener extends MouseInputAdapter { + @Override + public void mouseEntered(MouseEvent e) { + if (isInBorder(e)) { + e.getComponent().setCursor( + Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + } else { + mouseOver = false; + group.repaint(0, 0, group.getWidth(), getTitleHeight(group)); + } + } + + @Override + public void mouseExited(MouseEvent e) { + e.getComponent().setCursor(null); + mouseOver = false; + group.repaint(0, 0, group.getWidth(), getTitleHeight(group)); + } + + @Override + public void mouseMoved(MouseEvent e) { + if (isInBorder(e)) { + e.getComponent().setCursor( + Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + mouseOver = true; + } else { + e.getComponent().setCursor(null); + mouseOver = false; + } + + group.repaint(0, 0, group.getWidth(), getTitleHeight(group)); + } + + @Override + public void mouseReleased(MouseEvent e) { + if (SwingUtilities.isLeftMouseButton(e) && isInBorder(e)) { + group.setCollapsed(!group.isCollapsed()); + } + } + } + + /** + * Toggle expanded action. + */ + class ToggleCollapsedAction extends AbstractAction { + /** + * Serial version UID. + */ + private static final long serialVersionUID = 5676859881615358815L; + + public ToggleCollapsedAction() { + super("toggleCollapsed"); + } + + @Override + public void actionPerformed(ActionEvent e) { + group.setCollapsed(!group.isCollapsed()); + } + + @Override + public boolean isEnabled() { + return group.isVisible(); + } + } + + /** + * Toggle icon. + */ + protected static class ChevronIcon implements Icon { + boolean up = true; + + public ChevronIcon(boolean up) { + this.up = up; + } + + @Override + public int getIconHeight() { + return 3; + } + + @Override + public int getIconWidth() { + return 6; + } + + @Override + public void paintIcon(Component c, Graphics g, int x, int y) { + if (up) { + g.drawLine(x + 3, y, x, y + 3); + g.drawLine(x + 3, y, x + 6, y + 3); + } else { + g.drawLine(x, y, x + 3, y + 3); + g.drawLine(x + 3, y + 3, x + 6, y); + } + } + } + + /** + * The border around the content pane + */ + protected static class ContentPaneBorder implements Border, UIResource { + Color color; + + public ContentPaneBorder(Color color) { + this.color = color; + } + + @Override + public Insets getBorderInsets(Component c) { + return new Insets(0, 1, 1, 1); + } + + @Override + public boolean isBorderOpaque() { + return true; + } + + @Override + public void paintBorder(Component c, Graphics g, int x, int y, + int width, int height) { + g.setColor(color); + g.drawLine(x, y, x, y + height - 1); + g.drawLine(x, y + height - 1, x + width - 1, y + height - 1); + g.drawLine(x + width - 1, y, x + width - 1, y + height - 1); + } + } + + /** + * The border of the taskpane group paints the "text", the "icon", the + * "expanded" status and the "special" type. + * + */ + protected class PaneBorder implements Border, UIResource { + + protected Color borderColor; + protected Color titleForeground; + protected Color specialTitleBackground; + protected Color specialTitleForeground; + protected Color titleBackgroundGradientStart; + protected Color titleBackgroundGradientEnd; + + protected Color titleOver; + protected Color specialTitleOver; + + protected JLabel label; + + /** + * Creates new instance of individual pane border. + */ + public PaneBorder() { + borderColor = UIManager.getColor("TaskPane.borderColor"); + + titleForeground = UIManager.getColor("TaskPane.titleForeground"); + + specialTitleBackground = UIManager + .getColor("TaskPane.specialTitleBackground"); + specialTitleForeground = UIManager + .getColor("TaskPane.specialTitleForeground"); + + titleBackgroundGradientStart = UIManager + .getColor("TaskPane.titleBackgroundGradientStart"); + titleBackgroundGradientEnd = UIManager + .getColor("TaskPane.titleBackgroundGradientEnd"); + + titleOver = UIManager.getColor("TaskPane.titleOver"); + if (titleOver == null) { + titleOver = specialTitleBackground.brighter(); + } + specialTitleOver = UIManager.getColor("TaskPane.specialTitleOver"); + if (specialTitleOver == null) { + specialTitleOver = specialTitleBackground.brighter(); + } + + label = new JLabel(); + label.setOpaque(false); + label.setIconTextGap(8); + } + + @Override + public Insets getBorderInsets(Component c) { + return new Insets(getTitleHeight(c), 0, 0, 0); + } + + /** + * Overwritten to always return true to speed up + * painting. Don't use transparent borders unless providing UI delegate + * that provides proper return value when calling this method. + * + * @see Border#isBorderOpaque() + */ + @Override + public boolean isBorderOpaque() { + return true; + } + + /** + * Calculates the preferred border size, its size so all its content + * fits. + * + * @param group + * Selected group. + */ + public Dimension getPreferredSize(JXTaskPane group) { + // calculate the title width so it is fully visible + // it starts with the title width + configureLabel(group); + Dimension dim = label.getPreferredSize(); + // add the title left offset + dim.width += 3; + // add the controls width + dim.width += getTitleHeight(group); + // and some space between label and controls + dim.width += 3; + + dim.height = getTitleHeight(group); + return dim; + } + + /** + * Paints background of the title. This may differ based on properties + * of the group. + * + * @param group + * Selected group. + * @param g + * Target graphics. + */ + protected void paintTitleBackground(JXTaskPane group, Graphics g) { + if (group.isSpecial()) { + g.setColor(specialTitleBackground); + } else { + g.setColor(titleBackgroundGradientStart); + } + g.fillRect(0, 0, group.getWidth(), getTitleHeight(group) - 1); + } + + /** + * Paints current group title. + * + * @param group + * Selected group. + * @param g + * Target graphics. + * @param textColor + * Title color. + * @param x + * X coordinate of the top left corner. + * @param y + * Y coordinate of the top left corner. + * @param width + * Width of the box. + * @param height + * Height of the box. + */ + protected void paintTitle(JXTaskPane group, Graphics g, + Color textColor, int x, int y, int width, int height) { + configureLabel(group); + label.setForeground(textColor); + if (group.getFont() != null && ! (group.getFont() instanceof FontUIResource)) { + label.setFont(group.getFont()); + } + g.translate(x, y); + label.setBounds(0, 0, width, height); + label.paint(g); + g.translate(-x, -y); + } + + /** + * Configures label for the group using its title, font, icon and + * orientation. + * + * @param group + * Selected group. + */ + protected void configureLabel(JXTaskPane group) { + label.applyComponentOrientation(group.getComponentOrientation()); + label.setFont(group.getFont()); + label.setText(group.getTitle()); + label.setIcon(group.getIcon() == null ? new EmptyIcon() : group + .getIcon()); + } + + /** + * Paints expanded controls. Default implementation does nothing. + * + * @param group + * Expanded group. + * @param g + * Target graphics. + * @param x + * X coordinate of the top left corner. + * @param y + * Y coordinate of the top left corner. + * @param width + * Width of the box. + * @param height + * Height of the box. + */ + protected void paintExpandedControls(JXTaskPane group, Graphics g, + int x, int y, int width, int height) { + } + + /** + * Gets current paint color. + * + * @param group + * Selected group. + * @return Color to be used for painting provided group. + */ + protected Color getPaintColor(JXTaskPane group) { + Color paintColor; + if (isMouseOverBorder()) { + if (mouseOver) { + if (group.isSpecial()) { + paintColor = specialTitleOver; + } else { + paintColor = titleOver; + } + } else { + if (group.isSpecial()) { + paintColor = specialTitleForeground; + } else { + paintColor = group.getForeground() == null || group.getForeground() instanceof ColorUIResource ? titleForeground : group.getForeground(); + } + } + } else { + if (group.isSpecial()) { + paintColor = specialTitleForeground; + } else { + paintColor = group.getForeground() == null || group.getForeground() instanceof ColorUIResource ? titleForeground : group.getForeground(); + } + } + return paintColor; + } + + /* + * @see javax.swing.border.Border#paintBorder(java.awt.Component, + * java.awt.Graphics, int, int, int, int) + */ + @Override + public void paintBorder(Component c, Graphics g, int x, int y, + int width, int height) { + + JXTaskPane group = (JXTaskPane) c; + + // calculate position of title and toggle controls + int controlWidth = getTitleHeight(group) - 2 * getRoundHeight(); + int controlX = group.getWidth() - getTitleHeight(group); + int controlY = getRoundHeight() - 1; + int titleX = 3; + int titleY = 0; + int titleWidth = group.getWidth() - getTitleHeight(group) - 3; + int titleHeight = getTitleHeight(group); + + if (!group.getComponentOrientation().isLeftToRight()) { + controlX = group.getWidth() - controlX - controlWidth; + titleX = group.getWidth() - titleX - titleWidth; + } + + // paint the title background + paintTitleBackground(group, g); + + // paint the the toggles + paintExpandedControls(group, g, controlX, controlY, controlWidth, + controlWidth); + + // paint the title text and icon + Color paintColor = getPaintColor(group); + + // focus painted same color as text + if (group.hasFocus()) { + paintFocus(g, paintColor, 3, 3, width - 6, getTitleHeight(group) - 6); + } + + paintTitle(group, g, paintColor, titleX, titleY, titleWidth, + titleHeight); + } + + /** + * Paints oval 'border' area around the control itself. + * + * @param group + * Expanded group. + * @param g + * Target graphics. + * @param x + * X coordinate of the top left corner. + * @param y + * Y coordinate of the top left corner. + * @param width + * Width of the box. + * @param height + * Height of the box. + */ + protected void paintRectAroundControls(JXTaskPane group, Graphics g, + int x, int y, int width, int height, Color highColor, + Color lowColor) { + if (mouseOver) { + int x2 = x + width; + int y2 = y + height; + g.setColor(highColor); + g.drawLine(x, y, x2, y); + g.drawLine(x, y, x, y2); + g.setColor(lowColor); + g.drawLine(x2, y, x2, y2); + g.drawLine(x, y2, x2, y2); + } + } + + /** + * Paints oval 'border' area around the control itself. + * + * @param group + * Expanded group. + * @param g + * Target graphics. + * @param x + * X coordinate of the top left corner. + * @param y + * Y coordinate of the top left corner. + * @param width + * Width of the box. + * @param height + * Height of the box. + */ + protected void paintOvalAroundControls(JXTaskPane group, Graphics g, + int x, int y, int width, int height) { + if (group.isSpecial()) { + g.setColor(specialTitleBackground.brighter()); + g.drawOval(x, y, width, height); + } else { + g.setColor(titleBackgroundGradientStart); + g.fillOval(x, y, width, height); + + g.setColor(titleBackgroundGradientEnd.darker()); + g.drawOval(x, y, width, width); + } + } + + /** + * Paints controls for the group. + * + * @param group + * Expanded group. + * @param g + * Target graphics. + * @param x + * X coordinate of the top left corner. + * @param y + * Y coordinate of the top left corner. + * @param width + * Width of the box. + * @param height + * Height of the box. + */ + protected void paintChevronControls(JXTaskPane group, Graphics g, + int x, int y, int width, int height) { + ChevronIcon chevron; + if (group.isCollapsed()) { + chevron = new ChevronIcon(false); + } else { + chevron = new ChevronIcon(true); + } + int chevronX = x + width / 2 - chevron.getIconWidth() / 2; + int chevronY = y + (height / 2 - chevron.getIconHeight()); + chevron.paintIcon(group, g, chevronX, chevronY); + chevron.paintIcon(group, g, chevronX, chevronY + + chevron.getIconHeight() + 1); + } + + /** + * Paints focused group. + * + * @param g + * Target graphics. + * @param paintColor + * Focused group color. + * @param x + * X coordinate of the top left corner. + * @param y + * Y coordinate of the top left corner. + * @param width + * Width of the box. + * @param height + * Height of the box. + */ + protected void paintFocus(Graphics g, Color paintColor, int x, int y, + int width, int height) { + g.setColor(paintColor); + BasicGraphicsUtils.drawDashedRect(g, x, y, width, height); + } + + /** + * Default implementation returns false. + * + * @return true if this border wants to display things differently when + * the mouse is over it + */ + protected boolean isMouseOverBorder() { + return false; + } + } + + /** + * Gets size of arc used to round corners. + * + * @return size of arc used to round corners of the panel. + */ + protected int getRoundHeight() { + return roundHeight; + } + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicTipOfTheDayUI.java b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicTipOfTheDayUI.java new file mode 100644 index 0000000000..0e05fc5537 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicTipOfTheDayUI.java @@ -0,0 +1,352 @@ +/* + * $Id: BasicTipOfTheDayUI.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf.basic; + +import org.jdesktop.swingx.JXTipOfTheDay; +import org.jdesktop.swingx.JXTipOfTheDay.ShowOnStartupChoice; +import org.jdesktop.swingx.SwingXUtilities; +import org.jdesktop.swingx.plaf.TipOfTheDayUI; +import org.jdesktop.swingx.plaf.UIManagerExt; +import org.jdesktop.swingx.tips.TipOfTheDayModel.Tip; + +import javax.swing.*; +import javax.swing.plaf.ActionMapUIResource; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicHTML; +import javax.swing.text.html.HTMLDocument; +import java.awt.*; +import java.awt.event.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.Locale; + +/** + * Base implementation of the JXTipOfTheDay UI. + * + * @author Frederic Lavigne + */ +public class BasicTipOfTheDayUI extends TipOfTheDayUI { + + public static ComponentUI createUI(JComponent c) { + return new BasicTipOfTheDayUI((JXTipOfTheDay)c); + } + + protected JXTipOfTheDay tipPane; + protected JPanel tipArea; + protected Component currentTipComponent; + + protected Font tipFont; + protected PropertyChangeListener changeListener; + + public BasicTipOfTheDayUI(JXTipOfTheDay tipPane) { + this.tipPane = tipPane; + } + + @Override + public JDialog createDialog(Component parentComponent, + final ShowOnStartupChoice choice) { + return createDialog(parentComponent, choice, true); + } + + protected JDialog createDialog(Component parentComponent, + final ShowOnStartupChoice choice, + boolean showPreviousButton) { + Locale locale = parentComponent==null ? null : parentComponent.getLocale(); + String title = UIManagerExt.getString("TipOfTheDay.dialogTitle", locale); + + final JDialog dialog; + + Window window; + if (parentComponent == null) { + window = JOptionPane.getRootFrame(); + } else { + window = (parentComponent instanceof Window)?(Window)parentComponent + :SwingUtilities.getWindowAncestor(parentComponent); + } + + if (window instanceof Frame) { + dialog = new JDialog((Frame)window, title, true); + } else { + dialog = new JDialog((Dialog)window, title, true); + } + + dialog.getContentPane().setLayout(new BorderLayout(10, 10)); + dialog.getContentPane().add(tipPane, BorderLayout.CENTER); + ((JComponent)dialog.getContentPane()).setBorder(BorderFactory + .createEmptyBorder(10, 10, 10, 10)); + + final JCheckBox showOnStartupBox; + + // tip controls + JPanel controls = new JPanel(new BorderLayout()); + dialog.add("South", controls); + + if (choice != null) { + showOnStartupBox = new JCheckBox(UIManagerExt + .getString("TipOfTheDay.showOnStartupText", locale), choice + .isShowingOnStartup()); + controls.add(showOnStartupBox, BorderLayout.CENTER); + } else { + showOnStartupBox = null; + } + + JPanel buttons = + new JPanel(new GridLayout(1, showPreviousButton?3:2, 9, 0)); + controls.add(buttons, BorderLayout.LINE_END); + + if (showPreviousButton) { + JButton previousTipButton = new JButton(UIManagerExt + .getString("TipOfTheDay.previousTipText", locale)); + buttons.add(previousTipButton); + previousTipButton.addActionListener(getActionMap().get("previousTip")); + } + + JButton nextTipButton = new JButton(UIManagerExt + .getString("TipOfTheDay.nextTipText", locale)); + buttons.add(nextTipButton); + nextTipButton.addActionListener(getActionMap().get("nextTip")); + + JButton closeButton = new JButton(UIManagerExt + .getString("TipOfTheDay.closeText", locale)); + buttons.add(closeButton); + + final ActionListener saveChoice = new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (choice != null) { + choice.setShowingOnStartup(showOnStartupBox.isSelected()); + } + dialog.setVisible(false); + } + }; + + closeButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + dialog.setVisible(false); + saveChoice.actionPerformed(null); + } + }); + dialog.getRootPane().setDefaultButton(closeButton); + + dialog.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + saveChoice.actionPerformed(null); + } + }); + + ((JComponent)dialog.getContentPane()).registerKeyboardAction(saveChoice, + KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), + JComponent.WHEN_IN_FOCUSED_WINDOW); + + dialog.pack(); + dialog.setLocationRelativeTo(parentComponent); + + return dialog; + } + + @Override + public void installUI(JComponent c) { + super.installUI(c); + installDefaults(); + installKeyboardActions(); + installComponents(); + installListeners(); + + showCurrentTip(); + } + + protected void installKeyboardActions() { + ActionMap map = getActionMap(); + if (map != null) { + SwingUtilities.replaceUIActionMap(tipPane, map); + } + } + + ActionMap getActionMap() { + ActionMap map = new ActionMapUIResource(); + map.put("previousTip", new PreviousTipAction()); + map.put("nextTip", new NextTipAction()); + return map; + } + + protected void installListeners() { + changeListener = createChangeListener(); + tipPane.addPropertyChangeListener(changeListener); + } + + protected PropertyChangeListener createChangeListener() { + return new ChangeListener(); + } + + protected void installDefaults() { + LookAndFeel.installColorsAndFont(tipPane, "TipOfTheDay.background", + "TipOfTheDay.foreground", "TipOfTheDay.font"); + LookAndFeel.installBorder(tipPane, "TipOfTheDay.border"); + LookAndFeel.installProperty(tipPane, "opaque", Boolean.TRUE); + tipFont = UIManager.getFont("TipOfTheDay.tipFont"); + } + + protected void installComponents() { + tipPane.setLayout(new BorderLayout()); + + // tip icon + JLabel tipIcon = new JLabel(UIManagerExt + .getString("TipOfTheDay.didYouKnowText", tipPane.getLocale())); + tipIcon.setIcon(UIManager.getIcon("TipOfTheDay.icon")); + tipIcon.setBorder(BorderFactory.createEmptyBorder(22, 15, 22, 15)); + tipPane.add("North", tipIcon); + + // tip area + tipArea = new JPanel(new BorderLayout(2, 2)); + tipArea.setOpaque(false); + tipArea.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + tipPane.add("Center", tipArea); + } + + @Override + public Dimension getPreferredSize(JComponent c) { + return new Dimension(420, 175); + } + + protected void showCurrentTip() { + if (currentTipComponent != null) { + tipArea.remove(currentTipComponent); + } + + int currentTip = tipPane.getCurrentTip(); + if (currentTip == -1) { + JLabel label = new JLabel(); + label.setOpaque(true); + label.setBackground(UIManager.getColor("TextArea.background")); + currentTipComponent = label; + tipArea.add("Center", currentTipComponent); + return; + } + + // tip does not fall in current tip range + if (tipPane.getModel() == null || tipPane.getModel().getTipCount() == 0 + || (currentTip < 0 && currentTip >= tipPane.getModel().getTipCount())) { + currentTipComponent = new JLabel(); + } else { + Tip tip = tipPane.getModel().getTipAt(currentTip); + + Object tipObject = tip.getTip(); + if (tipObject instanceof Component) { + currentTipComponent = (Component)tipObject; + } else if (tipObject instanceof Icon) { + currentTipComponent = new JLabel((Icon)tipObject); + } else { + JScrollPane tipScroll = new JScrollPane(); + tipScroll.setBorder(null); + tipScroll.setOpaque(false); + tipScroll.getViewport().setOpaque(false); + tipScroll.setBorder(null); + + String text = tipObject == null?"":tipObject.toString(); + + if (BasicHTML.isHTMLString(text)) { + JEditorPane editor = new JEditorPane("text/html", text); + editor.setFont(tipPane.getFont()); +// BasicHTML.updateRenderer(editor, text); + SwingXUtilities.setHtmlFont( + (HTMLDocument) editor.getDocument(), tipPane.getFont()); + editor.setEditable(false); + editor.setBorder(null); + editor.setMargin(null); + editor.setOpaque(false); + tipScroll.getViewport().setView(editor); + } else { + JTextArea area = new JTextArea(text); + area.setFont(tipPane.getFont()); + area.setEditable(false); + area.setLineWrap(true); + area.setWrapStyleWord(true); + area.setBorder(null); + area.setMargin(null); + area.setOpaque(false); + tipScroll.getViewport().setView(area); + } + + currentTipComponent = tipScroll; + } + } + + tipArea.add("Center", currentTipComponent); + tipArea.revalidate(); + tipArea.repaint(); + } + + @Override + public void uninstallUI(JComponent c) { + uninstallListeners(); + uninstallComponents(); + uninstallDefaults(); + super.uninstallUI(c); + } + + protected void uninstallListeners() { + tipPane.removePropertyChangeListener(changeListener); + } + + protected void uninstallComponents() {} + + protected void uninstallDefaults() {} + + class ChangeListener implements PropertyChangeListener { + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (JXTipOfTheDay.CURRENT_TIP_CHANGED_KEY.equals(evt.getPropertyName())) { + showCurrentTip(); + } + } + } + + class PreviousTipAction extends AbstractAction { + public PreviousTipAction() { + super("previousTip"); + } + @Override + public void actionPerformed(ActionEvent e) { + tipPane.previousTip(); + } + @Override + public boolean isEnabled() { + return tipPane.isEnabled(); + } + } + + class NextTipAction extends AbstractAction { + public NextTipAction() { + super("nextTip"); + } + @Override + public void actionPerformed(ActionEvent e) { + tipPane.nextTip(); + } + @Override + public boolean isEnabled() { + return tipPane.isEnabled(); + } + } + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicTitledPanelUI.java b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicTitledPanelUI.java new file mode 100644 index 0000000000..64f1834481 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicTitledPanelUI.java @@ -0,0 +1,339 @@ +/* + * $Id: BasicTitledPanelUI.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.plaf.basic; + +import org.jdesktop.swingx.JXPanel; +import org.jdesktop.swingx.JXTitledPanel; +import org.jdesktop.swingx.SwingXUtilities; +import org.jdesktop.swingx.plaf.TitledPanelUI; + +import javax.swing.*; +import javax.swing.plaf.BorderUIResource; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.UIResource; +import java.awt.*; +import java.beans.*; +import java.lang.reflect.Method; +import java.util.logging.Level; +import java.util.logging.Logger; + + +/** + * All TitledPanels contain a title section and a content section. The default + * implementation for the title section relies on a Gradient background. All + * title sections can have components embedded to the "left" or + * "right" of the Title. + * + * @author Richard Bair + * @author Jeanette Winzenburg + * @author rah003 + * + */ +public class BasicTitledPanelUI extends TitledPanelUI { + private static final Logger LOG = Logger.getLogger(BasicTitledPanelUI.class.getName()); + + /** + * JLabel used for the title in the Title section of the JTitledPanel. + */ + protected JLabel caption; + /** + * The Title section panel. + */ + protected JXPanel topPanel; + /** + * Listens to changes in the title of the JXTitledPanel component + */ + protected PropertyChangeListener titleChangeListener; + + protected JComponent left; + protected JComponent right; + + /** Creates a new instance of BasicTitledPanelUI */ + public BasicTitledPanelUI() { + } + + /** + * Returns an instance of the UI delegate for the specified component. + * Each subclass must provide its own static createUI + * method that returns an instance of that UI delegate subclass. + * If the UI delegate subclass is stateless, it may return an instance + * that is shared by multiple components. If the UI delegate is + * stateful, then it should return a new instance per component. + * The default implementation of this method throws an error, as it + * should never be invoked. + */ + public static ComponentUI createUI(JComponent c) { + return new BasicTitledPanelUI(); + } + /** + * Configures the specified component appropriate for the look and feel. + * This method is invoked when the ComponentUI instance is being installed + * as the UI delegate on the specified component. This method should + * completely configure the component for the look and feel, + * including the following: + *
        + *
      1. Install any default property values for color, fonts, borders, + * icons, opacity, etc. on the component. Whenever possible, + * property values initialized by the client program should not + * be overridden. + *
      2. Install a LayoutManager on the component if necessary. + *
      3. Create/add any required sub-components to the component. + *
      4. Create/install event listeners on the component. + *
      5. Create/install a PropertyChangeListener on the component in order + * to detect and respond to component property changes appropriately. + *
      6. Install keyboard UI (mnemonics, traversal, etc.) on the component. + *
      7. Initialize any appropriate instance data. + *
      + * @param c the component where this UI delegate is being installed + * + * @see #uninstallUI + * @see JComponent#setUI + * @see JComponent#updateUI + */ + @Override + public void installUI(JComponent c) { + assert c instanceof JXTitledPanel; + JXTitledPanel titledPanel = (JXTitledPanel)c; + installDefaults(titledPanel); + + caption = createAndConfigureCaption(titledPanel); + topPanel = createAndConfigureTopPanel(titledPanel); + + installComponents(titledPanel); + installListeners(titledPanel); + } + + protected void installDefaults(JXTitledPanel titledPanel) { + installProperty(titledPanel, "titlePainter", UIManager.get("JXTitledPanel.titlePainter")); + installProperty(titledPanel, "titleForeground", UIManager.getColor("JXTitledPanel.titleForeground")); + installProperty(titledPanel, "titleFont", UIManager.getFont("JXTitledPanel.titleFont")); + LookAndFeel.installProperty(titledPanel, "opaque", false); + } + + protected void uninstallDefaults(JXTitledPanel titledPanel) { + } + + protected void installComponents(JXTitledPanel titledPanel) { + topPanel.add(caption, new GridBagConstraints(1, 0, 1, 1, 1.0, 1.0, GridBagConstraints.NORTHWEST, + GridBagConstraints.HORIZONTAL, getCaptionInsets(), 0, 0)); + if (titledPanel.getClientProperty(JXTitledPanel.RIGHT_DECORATION) instanceof JComponent) { + setRightDecoration((JComponent) titledPanel.getClientProperty(JXTitledPanel.RIGHT_DECORATION)); + } + if (titledPanel.getClientProperty(JXTitledPanel.LEFT_DECORATION) instanceof JComponent) { + setLeftDecoration((JComponent) titledPanel.getClientProperty(JXTitledPanel.LEFT_DECORATION)); + } + // swingx#500 + if (!(titledPanel.getLayout() instanceof BorderLayout)){ + titledPanel.setLayout(new BorderLayout()); + } + titledPanel.add(topPanel, BorderLayout.NORTH); + // fix #1063-swingx: must respect custom border + if (SwingXUtilities.isUIInstallable(titledPanel.getBorder())) { + // use uiresource border + // old was: BorderFactory.createRaisedBevelBorder()); + titledPanel.setBorder(BorderUIResource.getRaisedBevelBorderUIResource()); + } + } + + protected void uninstallComponents(JXTitledPanel titledPanel) { + titledPanel.remove(topPanel); + } + + protected Insets getCaptionInsets() { + return UIManager.getInsets("JXTitledPanel.captionInsets"); + } + + protected JXPanel createAndConfigureTopPanel(JXTitledPanel titledPanel) { + JXPanel topPanel = new JXPanel(); + topPanel.setBackgroundPainter(titledPanel.getTitlePainter()); + topPanel.setBorder(BorderFactory.createEmptyBorder()); + topPanel.setLayout(new GridBagLayout()); + topPanel.setOpaque(false); + return topPanel; + } + + protected JLabel createAndConfigureCaption(final JXTitledPanel titledPanel) { + JLabel caption = new JLabel(titledPanel.getTitle()){ + //#501 + @Override + public void updateUI(){ + super.updateUI(); + setForeground(titledPanel.getTitleForeground()); + setFont(titledPanel.getTitleFont()); + } + }; + caption.setFont(titledPanel.getTitleFont()); + caption.setForeground(titledPanel.getTitleForeground()); + return caption; + } + + /** + * Reverses configuration which was done on the specified component during + * installUI. This method is invoked when this + * UIComponent instance is being removed as the UI delegate + * for the specified component. This method should undo the + * configuration performed in installUI, being careful to + * leave the JComponent instance in a clean state (no + * extraneous listeners, look-and-feel-specific property objects, etc.). + * This should include the following: + *
        + *
      1. Remove any UI-set borders from the component. + *
      2. Remove any UI-set layout managers on the component. + *
      3. Remove any UI-added sub-components from the component. + *
      4. Remove any UI-added event/property listeners from the component. + *
      5. Remove any UI-installed keyboard UI from the component. + *
      6. Nullify any allocated instance data objects to allow for GC. + *
      + * @param c the component from which this UI delegate is being removed; + * this argument is often ignored, + * but might be used if the UI object is stateless + * and shared by multiple components + * + * @see #installUI + * @see JComponent#updateUI + */ + @Override + public void uninstallUI(JComponent c) { + assert c instanceof JXTitledPanel; + JXTitledPanel titledPanel = (JXTitledPanel) c; + uninstallListeners(titledPanel); + // JW: this is needed to make the gradient paint work correctly... + // LF changes will remove the left/right components... + topPanel.removeAll(); + titledPanel.remove(topPanel); + titledPanel.putClientProperty(JXTitledPanel.LEFT_DECORATION, left); + titledPanel.putClientProperty(JXTitledPanel.RIGHT_DECORATION, right); + caption = null; + topPanel = null; + titledPanel = null; + left = null; + right = null; + } + + protected void installListeners(final JXTitledPanel titledPanel) { + titleChangeListener = new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getPropertyName().equals("title")) { + caption.setText((String)evt.getNewValue()); + } else if (evt.getPropertyName().equals("titleForeground")) { + caption.setForeground((Color)evt.getNewValue()); + } else if (evt.getPropertyName().equals("titleFont")) { + caption.setFont((Font)evt.getNewValue()); + } else if ("titlePainter".equals(evt.getPropertyName())) { + topPanel.setBackgroundPainter(titledPanel.getTitlePainter()); + topPanel.repaint(); + } + } + }; + titledPanel.addPropertyChangeListener(titleChangeListener); + } + + protected void uninstallListeners(JXTitledPanel titledPanel) { + titledPanel.removePropertyChangeListener(titleChangeListener); + } + + protected void installProperty(JComponent c, String propName, Object value) { + try { + BeanInfo bi = Introspector.getBeanInfo(c.getClass()); + for (PropertyDescriptor pd : bi.getPropertyDescriptors()) { + if (pd.getName().equals(propName)) { + Method m = pd.getReadMethod(); + Object oldVal = m.invoke(c); + if (oldVal == null || oldVal instanceof UIResource) { + m = pd.getWriteMethod(); + m.invoke(c, value); + } + } + } + } catch (Exception e) { + LOG.log(Level.FINE, "Failed to install property " + propName, e); + } + } + + /** + * Paints the specified component appropriate for the look and feel. + * This method is invoked from the ComponentUI.update method when + * the specified component is being painted. Subclasses should override + * this method and use the specified Graphics object to + * render the content of the component.

      + * + * PENDING JW: we don't need this, do we - remove! + * + * @param g the Graphics context in which to paint + * @param c the component being painted; + * this argument is often ignored, + * but might be used if the UI object is stateless + * and shared by multiple components + * + * @see #update + */ + @Override + public void paint(Graphics g, JComponent c) { + super.paint(g, c); + } + + /** + * Adds the given JComponent as a decoration on the right of the title + * @param decoration + */ + @Override + public void setRightDecoration(JComponent decoration) { + if (right != null) topPanel.remove(right); + right = decoration; + if (right != null) { + topPanel.add(decoration, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, UIManager.getInsets("JXTitledPanel.rightDecorationInsets"), 0, 0)); + + } + } + + @Override + public JComponent getRightDecoration() { + return right; + } + + /** + * Adds the given JComponent as a decoration on the left of the title + * @param decoration + */ + @Override + public void setLeftDecoration(JComponent decoration) { + if (left != null) topPanel.remove(left); + left = decoration; + if (left != null) { + topPanel.add(left, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, UIManager.getInsets("JXTitledPanel.leftDecorationInsets"), 0, 0)); + } + } + + @Override + public JComponent getLeftDecoration() { + return left; + } + + /** + * @return the Container acting as the title bar for this component + */ + @Override + public Container getTitleBar() { + return topPanel; + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/CalendarAdapter.java b/src/main/java/org/jdesktop/swingx/plaf/basic/CalendarAdapter.java new file mode 100644 index 0000000000..f695a33d0d --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/CalendarAdapter.java @@ -0,0 +1,127 @@ +/* + * $Id: CalendarAdapter.java 3877 2010-11-03 11:36:33Z kleopatra $ + * + * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.plaf.basic; + +import org.jdesktop.swingx.JXMonthView; +import org.jdesktop.swingx.decorator.ComponentAdapter; + +import java.util.Calendar; + +/** + * ComponentAdapter for a JXMonthView (experimental for internal use of BasicMonthViewUI).

      + * + * For now, this effectively disables all notion of row/column coordinates. It's focused + * on an externally provided date (as Calendar) and CalendarState. Yeah, I know, that's + * tweaking too much but then, I want to use highlighters which need an adapter... + * + * + * @author Jeanette Winzenburg + */ +class CalendarAdapter extends ComponentAdapter { + + Calendar calendar; + CalendarState dayState; + + /** + * @param component + */ + public CalendarAdapter(JXMonthView component) { + super(component); + } + + /** + * @param calendar2 + * @param dayState2 + * @return + */ + public CalendarAdapter install(Calendar calendar, CalendarState dayState) { + this.calendar = calendar; + this.dayState = dayState; + return this; + } + + + @Override + public JXMonthView getComponent() { + return (JXMonthView) super.getComponent(); + } + + public CalendarState getCalendarState() { + return dayState; + } + + public boolean isFlagged() { + if (getComponent() == null || calendar == null) { + return false; + } + return getComponent().isFlaggedDate(calendar.getTime()); + } + + public boolean isUnselectable() { + if (getComponent() == null || calendar == null || !isSelectable()) { + return false; + } + return getComponent().isUnselectableDate(calendar.getTime()); + } + + /** + * @param dayState + * @return + */ + private boolean isSelectable() { + return (CalendarState.IN_MONTH == getCalendarState()) || (CalendarState.TODAY == getCalendarState()); + } + + @Override + public boolean isSelected() { + if (getComponent() == null || calendar == null) { + return false; + } + return getComponent().isSelected(calendar.getTime()); + } + + + @Override + public Object getFilteredValueAt(int row, int column) { + return getValueAt(row, column); + } + + @Override + public Object getValueAt(int row, int column) { + return calendar; + } + + @Override + public boolean hasFocus() { + return false; + } + + @Override + public boolean isCellEditable(int row, int column) { + return false; + } + + @Override + public boolean isEditable() { + return false; + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/CalendarCellContext.java b/src/main/java/org/jdesktop/swingx/plaf/basic/CalendarCellContext.java new file mode 100644 index 0000000000..0acaf32ec8 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/CalendarCellContext.java @@ -0,0 +1,214 @@ +/* + * $Id: CalendarCellContext.java 3286 2009-03-10 12:13:43Z kleopatra $ + * + * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf.basic; + +import org.jdesktop.swingx.JXMonthView; +import org.jdesktop.swingx.border.IconBorder; +import org.jdesktop.swingx.plaf.UIManagerExt; +import org.jdesktop.swingx.renderer.CellContext; + +import javax.swing.*; +import javax.swing.border.Border; +import java.awt.*; +import java.util.Calendar; + +/** + * MonthView specific CellContext. This is internally used by BasisMonthViewUI rendering. + * + * @author Jeanette Winzenburg + */ +class CalendarCellContext extends CellContext { + + /** + * The padding for month traversal icons. + * PENDING JW: decouple rendering and hit-detection. As is, these are + * hard-coded "magic numbers" which must be the same in both + * the ui-delegate (which does the hit-detection) and here (which + * returns the default title border) + * + * Added as preliminary fix for #1028-swingx: title border incorrect if box-padding 0 + */ + private int arrowPaddingX = 3; + private int arrowPaddingY = 3; + + private CalendarState dayState; + + public void installContext(JXMonthView component, Calendar value, + boolean selected, boolean focused, CalendarState dayState) { + this.component = component; + this.dayState = dayState; + installState(value, -1, -1, selected, focused, true, true); + } + + + @Override + public JXMonthView getComponent() { + return (JXMonthView) super.getComponent(); + } + + + public CalendarState getCalendarState() { + return dayState; + } + + + public Calendar getCalendar() { + return (getValue() instanceof Calendar) ? (Calendar) getValue() : null; + } + + @Override + protected Color getForeground() { + if (CalendarState.LEADING == dayState) { + return getUIColor("leadingDayForeground"); + } + if (CalendarState.TRAILING == dayState) { + return getUIColor("trailingDayForeground"); + } + if ((CalendarState.TITLE == dayState) && (getComponent() != null)) { + return getComponent().getMonthStringForeground(); + } + if (CalendarState.WEEK_OF_YEAR == dayState) { + Color weekOfTheYearForeground = getUIColor("weekOfTheYearForeground"); + if (weekOfTheYearForeground != null) { + return weekOfTheYearForeground; + } + } + if (CalendarState.DAY_OF_WEEK == dayState) { + Color daysOfTheWeekForeground = getComponent() != null + ? getComponent().getDaysOfTheWeekForeground() : null; + if (daysOfTheWeekForeground != null) { + return daysOfTheWeekForeground; + } + } + + Color flaggedOrPerDayForeground = getFlaggedOrPerDayForeground(); + return flaggedOrPerDayForeground != null ? flaggedOrPerDayForeground : super.getForeground(); + } + + /** + * @param key + * @return + */ + private Color getUIColor(String key) { + return UIManagerExt.getColor(getUIPrefix() + key); + } + + /** + * Returns the special color used for flagged days or per weekday or null if none is + * set, the component or the calendar are null. + * + * @return the special foreground color for flagged days or per dayOfWeek. + */ + protected Color getFlaggedOrPerDayForeground() { + + if (getComponent() != null && (getCalendar() != null)) { + if (getComponent().isFlaggedDate(getCalendar().getTime())) { + return getComponent().getFlaggedDayForeground(); + } else { + Color perDay = getComponent().getPerDayOfWeekForeground(getCalendar().get(Calendar.DAY_OF_WEEK)); + if (perDay != null) { + return perDay; + } + + } + } + return null; + } + + @Override + protected Color getBackground() { + if ((CalendarState.TITLE == dayState) && (getComponent() != null)) { + return getComponent().getMonthStringBackground(); + } + return super.getBackground(); + } + + @Override + protected Color getSelectionBackground() { + if (CalendarState.LEADING == dayState || CalendarState.TRAILING == dayState) return getBackground(); + return getComponent() != null ? getComponent().getSelectionBackground() : null; + } + + @Override + protected Color getSelectionForeground() { + if (CalendarState.LEADING == dayState || CalendarState.TRAILING == dayState) return getForeground(); + Color flaggedOrPerDayForeground = getFlaggedOrPerDayForeground(); + if (flaggedOrPerDayForeground != null) { + return flaggedOrPerDayForeground; + } + return getComponent() != null ? getComponent().getSelectionForeground() : null; + } + + + + @Override + protected Border getBorder() { + if (getComponent() == null) { + return super.getBorder(); + } + if (CalendarState.TITLE == dayState) { + return getTitleBorder(); + } + if (isToday()) { + int x = getComponent().getBoxPaddingX(); + int y = getComponent().getBoxPaddingY(); + Border todayBorder = BorderFactory.createLineBorder(getComponent().getTodayBackground()); + Border empty = BorderFactory.createEmptyBorder(y - 1, x - 1, y - 1, x -1); + return BorderFactory.createCompoundBorder(todayBorder, empty); + } + return BorderFactory.createEmptyBorder(getComponent().getBoxPaddingY(), getComponent().getBoxPaddingX(), getComponent().getBoxPaddingY(), getComponent().getBoxPaddingX()); + } + + /** + * @return + */ + private Border getTitleBorder() { + if (getComponent().isTraversable()) { + Icon downIcon = UIManager.getIcon("JXMonthView.monthDownFileName"); + Icon upIcon = UIManager.getIcon("JXMonthView.monthUpFileName"); + + // fix for #1028-swingx: title border whacky for boxpadding 0 + // in fact there had been a deeper issue - without using the arrowPadding here + // the hit-detection of the buttons is slightly off target + IconBorder up = new IconBorder(upIcon, SwingConstants.EAST, arrowPaddingX); + IconBorder down = new IconBorder(downIcon, SwingConstants.WEST, arrowPaddingX); + Border compound = BorderFactory.createCompoundBorder(up, down); + Border empty = BorderFactory.createEmptyBorder(2* arrowPaddingY, 0, 2*arrowPaddingY, 0); + return BorderFactory.createCompoundBorder(compound, empty); + } + + return BorderFactory.createEmptyBorder(getComponent().getBoxPaddingY(), getComponent().getBoxPaddingX(), getComponent().getBoxPaddingY(), getComponent().getBoxPaddingX()); + } + + /** + * @return + */ + protected boolean isToday() { + return CalendarState.TODAY == dayState; + } + + @Override + protected String getUIPrefix() { + return "JXMonthView."; + } + + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/CalendarHeaderHandler.java b/src/main/java/org/jdesktop/swingx/plaf/basic/CalendarHeaderHandler.java new file mode 100644 index 0000000000..a6b0d6df35 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/CalendarHeaderHandler.java @@ -0,0 +1,312 @@ +/* + * $Id: CalendarHeaderHandler.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.plaf.basic; + +import org.jdesktop.swingx.JXMonthView; +import org.jdesktop.swingx.action.AbstractActionExt; + +import javax.swing.*; +import javax.swing.plaf.UIResource; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.logging.Logger; + +/** + * Provides and wires a component appropriate as a calendar navigation header. + * The design idea is to support a pluggable header for a zoomable (PENDING JW: + * naming!) JXMonthView. Then custom implementations can be tailored to exactly + * fit their needs. + *

      + * + * To install a custom implementation, register the class name of the custom + * header handler with the key CalendarHeaderHandler.uiControllerID + * , example: + * + *

      + * 
      + *  UIManager.put(CalendarHeaderHandler.uiControllerID, "com.foo.bar.MagicHeaderHandler")
      + * 
      + * 
      + * + * Basic navigation action should (will) be defined by the ui delegate itself (PENDING + * JW: still incomplete in BasicMonthViewUI). This handler can modify/enhance + * them as appropriate for its context. + *

      + * + * PENDING JW: those icons ... who's responsible? Shouldn't we use any of the + * default arrows as defined in the laf anyway (are there any?) + *

      + * + * Note: this is work-in-progress, be prepared to change if subclassing + * for custom requirements! + * + * @author Jeanette Winzenburg + */ +public abstract class CalendarHeaderHandler { + + @SuppressWarnings("unused") + private static final Logger LOG = Logger + .getLogger(CalendarHeaderHandler.class.getName()); + + public static final String uiControllerID = "CalendarHeaderHandler"; + + protected JXMonthView monthView; + + private JComponent calendarHeader; + + protected Icon monthDownImage; + + protected Icon monthUpImage; + + private PropertyChangeListener monthViewPropertyChangeListener; + + /** + * Installs this handler to the given month view. + * + * @param monthView the target month view to install to. + */ + public void install(JXMonthView monthView) { + this.monthView = monthView; + // PENDING JW: remove here if rendererHandler takes over control + // completely + // as is, some properties are duplicated + monthDownImage = UIManager.getIcon("JXMonthView.monthDownFileName"); + monthUpImage = UIManager.getIcon("JXMonthView.monthUpFileName"); + installNavigationActions(); + installListeners(); + componentOrientationChanged(); + monthStringBackgroundChanged(); + fontChanged(); + } + + /** + * Uninstalls this handler from the given target month view. + * + * @param monthView the target month view to install from. + */ + public void uninstall(JXMonthView monthView) { + this.monthView.remove(getHeaderComponent()); + uninstallListeners(); + this.monthView = null; + } + + /** + * Returns a component to be used as header in a zoomable month view, + * guaranteed to be not null. + * + * @return a component to be used as header in a zoomable JXMonthView + */ + public JComponent getHeaderComponent() { + if (calendarHeader == null) { + calendarHeader = createCalendarHeader(); + } + return calendarHeader; + } + + /** + * Creates and registered listeners on the monthView as appropriate. This + * implementation registers a PropertyChangeListener which synchronizes + * internal state on changes of componentOrientation, font and + * monthStringBackground. + */ + protected void installListeners() { + monthView + .addPropertyChangeListener(getMonthViewPropertyChangeListener()); + } + + /** + * Unregisters listeners which had been installed to the monthView. + */ + protected void uninstallListeners() { + monthView.removePropertyChangeListener(monthViewPropertyChangeListener); + } + + /** + * Returns the propertyChangelistener for the monthView. Lazily created. + * + * @return the propertyChangeListener for the monthView. + */ + private PropertyChangeListener getMonthViewPropertyChangeListener() { + if (monthViewPropertyChangeListener == null) { + monthViewPropertyChangeListener = new PropertyChangeListener() { + + @Override + public void propertyChange(PropertyChangeEvent evt) { + if ("componentOrientation".equals(evt.getPropertyName())) { + componentOrientationChanged(); + } else if ("font".equals(evt.getPropertyName())) { + fontChanged(); + } else if ("monthStringBackground".equals(evt + .getPropertyName())) { + monthStringBackgroundChanged(); + } + + } + }; + } + return monthViewPropertyChangeListener; + } + + /** + * Synchronizes internal state which depends on the month view's + * monthStringBackground. + */ + protected void monthStringBackgroundChanged() { + getHeaderComponent().setBackground( + getAsNotUIResource(monthView.getMonthStringBackground())); + + } + + /** + * Synchronizes internal state which depends on the month view's font. + */ + protected void fontChanged() { + getHeaderComponent().setFont(getAsNotUIResource(createDerivedFont())); + monthView.revalidate(); + } + + /** + * Synchronizes internal state which depends on the month view's + * componentOrientation. + * + * This implementation updates the month navigation icons and the header + * component's orientation. + */ + protected void componentOrientationChanged() { + getHeaderComponent().applyComponentOrientation( + monthView.getComponentOrientation()); + if (monthView.getComponentOrientation().isLeftToRight()) { + updateMonthNavigationIcons(monthDownImage, monthUpImage); + } else { + updateMonthNavigationIcons(monthUpImage, monthDownImage); + } + } + + /** + * @param previous the icon to use in the previousMonth action + * @param next the icon to use on the nextMonth action + */ + private void updateMonthNavigationIcons(Icon previous, Icon next) { + updateActionIcon("previousMonth", previous); + updateActionIcon("nextMonth", next); + } + + /** + * @param previousKey + * @param previous + */ + private void updateActionIcon(String previousKey, Icon previous) { + Action action = monthView.getActionMap().get(previousKey); + if (action != null) { + action.putValue(Action.SMALL_ICON, previous); + } + } + + /** + * Creates and returns the component used as header in a zoomable monthView. + * + * @return the component used as header in a zoomable monthView, guaranteed + * to be not null. + */ + protected abstract JComponent createCalendarHeader(); + + /** + * Installs and configures navigational actions. + *

      + * + * This implementation creates and installs wrappers around the + * scrollToPrevious/-NextMonth actions installed by the ui and configures + * them with the appropriate next/previous icons. + */ + protected void installNavigationActions() { + installWrapper("scrollToPreviousMonth", "previousMonth", monthView + .getComponentOrientation().isLeftToRight() ? monthDownImage + : monthUpImage); + installWrapper("scrollToNextMonth", "nextMonth", monthView + .getComponentOrientation().isLeftToRight() ? monthUpImage + : monthDownImage); + } + + /** + * Creates an life action wrapper around the action registered with + * actionKey, sets its SMALL_ICON property to the given icon and installs + * itself with the newActionKey. + * + * @param actionKey the key of the action to wrap around + * @param newActionKey the key of the wrapper action + * @param icon the icon to use in the wrapper action + */ + private void installWrapper(final String actionKey, String newActionKey, + Icon icon) { + AbstractActionExt wrapper = new AbstractActionExt(null, icon) { + + @Override + public void actionPerformed(ActionEvent e) { + Action action = monthView.getActionMap().get(actionKey); + if (action != null) { + action.actionPerformed(e); + } + } + + }; + monthView.getActionMap().put(newActionKey, wrapper); + } + + /** + * Returns a Font based on the param which is not of type UIResource. + * + * @param font the base font + * @return a font not of type UIResource, may be null. + */ + private Font getAsNotUIResource(Font font) { + if (!(font instanceof UIResource)) + return font; + // PENDING JW: correct way to create another font instance? + return font.deriveFont(font.getAttributes()); + } + + /** + * Returns a Color based on the param which is not of type UIResource. + * + * @param color the base color + * @return a color not of type UIResource, may be null. + */ + private Color getAsNotUIResource(Color color) { + if (!(color instanceof UIResource)) + return color; + // PENDING JW: correct way to create another color instance? + float[] rgb = color.getRGBComponents(null); + return new Color(rgb[0], rgb[1], rgb[2], rgb[3]); + } + + /** + * Create a derived font used to when painting various pieces of the month + * view component. This method will be called whenever the font on the + * component is set so a new derived font can be created. + */ + protected Font createDerivedFont() { + return monthView.getFont().deriveFont(Font.BOLD); + } + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/CalendarRenderingHandler.java b/src/main/java/org/jdesktop/swingx/plaf/basic/CalendarRenderingHandler.java new file mode 100644 index 0000000000..e706d5f9f1 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/CalendarRenderingHandler.java @@ -0,0 +1,60 @@ +/* + * $Id: CalendarRenderingHandler.java 3166 2009-01-02 13:27:18Z rah003 $ + * + * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf.basic; + +import org.jdesktop.swingx.JXMonthView; + +import javax.swing.*; +import java.util.Calendar; +import java.util.Locale; + +/** + * The RenderingHandler responsible for text rendering. It provides + * and configures a rendering component for the given cell of + * a JXMonthView.

      + * + * @author Jeanette Winzenburg + */ +public interface CalendarRenderingHandler { + + /** + * Configures and returns a component for rendering of the given monthView cell. + * + * @param monthView the JXMonthView to render onto + * @param calendar the cell value + * @param state the DayState of the cell + * @return a component configured for rendering the given cell + */ + public JComponent prepareRenderingComponent(JXMonthView monthView, + Calendar calendar, CalendarState state); + + /** + * Updates internal state to the given Locale. + * + * PENDING JW: ideally, the handler should be stateless and this method + * removed. Currently needed because there is no way to get the Locale + * from a Calendar. + * + * @param locale the new Locale. + */ + public void setLocale(Locale locale); + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/CalendarState.java b/src/main/java/org/jdesktop/swingx/plaf/basic/CalendarState.java new file mode 100644 index 0000000000..41747edad4 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/CalendarState.java @@ -0,0 +1,35 @@ +/* + * $Id: CalendarState.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf.basic; + +/** + * States of a Day in a MonthView page. + * + * @author Jeanette Winzenburg + */ +public enum CalendarState { + TODAY, + IN_MONTH, + LEADING, + TRAILING, + WEEK_OF_YEAR, + DAY_OF_WEEK, TITLE +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/CapsLockSupport.java b/src/main/java/org/jdesktop/swingx/plaf/basic/CapsLockSupport.java new file mode 100644 index 0000000000..d8b4802140 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/CapsLockSupport.java @@ -0,0 +1,142 @@ +package org.jdesktop.swingx.plaf.basic; + +import org.jdesktop.beans.AbstractBean; + +import java.awt.*; +import java.awt.event.KeyEvent; + +import static java.awt.event.KeyEvent.VK_CAPS_LOCK; + +/** + * A class for determining the state of the {@link KeyEvent.VK_CAPS_LOCK CAPS LOCK + * key}. It also supports notification when the locking state changes. + *

      + * Although it is possible to use {@link Toolkit#getLockingKeyState(int)} to determine the current + * state of the CAPS LOCK key, that method is not guaranteed to work on all platforms. This class + * attempts to handle those shortfalls and provide an easy mechanism for listening to state changes. + * + *

      + * CapsLockSupport cls = CapsLockSupport.getInstance();
      + * // for get the current state of the caps lock key
      + * boolean currentState = cls.isCapsLockEnabled();
      + * // for listening to changes in the caps lock state
      + * cls.addPropertyChangeListener("capsLockEnabled", myListener);
      + * 
      + * + * There is one special case to be aware of. If {@code CapsLockSupport} is not able to determine the + * state of the CAPS LOCK key, then {@link #isInitialized()} will return {@code false} until it is + * able to introspect a {@link KeyEvent} and determine the current locking state. If + * {@code CapsLockSupport} must use delayed initialization, it will fire a property change to notify + * listeners that it is now in an accurate state. + * + * @author kschaefer + */ +public final class CapsLockSupport extends AbstractBean implements KeyEventDispatcher { + private boolean useToolkit; + private boolean capsLockeEnabled; + private boolean updateViaKeyEvent; + + private static class SingletonHolder { + private static final CapsLockSupport INSTANCE = new CapsLockSupport(); + + static { + KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(INSTANCE); + } + } + + private CapsLockSupport() { + try { + capsLockeEnabled = Toolkit.getDefaultToolkit().getLockingKeyState(VK_CAPS_LOCK); + useToolkit = true; + updateViaKeyEvent = false; + } catch (UnsupportedOperationException e) { + capsLockeEnabled = false; + useToolkit = false; + updateViaKeyEvent = true; + } + } + + /** + * Gets the only instance of {@code CapsLockSupport}. + * + * @return the {@code CapsLockSupport} instance + */ + public static CapsLockSupport getInstance() { + return SingletonHolder.INSTANCE; + } + + /** + * Determines if {@code CapsLockSupport} is accurately synchronized with the state of the CAPS + * LOCK key. When not initialized, {@link #isCapsLockEnabled()} will always return {@code false} + * . {@code CapsLockSupport} will fail to initialize only if + * {@code Toolkit#getLockingKeyState(int)} throws an exception; in that case, it will initialize + * as soon as it receives a valid key event (that can be used to determine the current locking + * state). + * + * @return {@code true} if {@code CapsLockSupport} accurately knows the state of the CAPS LOCK + * key + */ + public boolean isInitialized() { + return useToolkit || (useToolkit ^ updateViaKeyEvent); + } + + /** + * Determines the current state of the {@link KeyEvent.VK_CAPS_LOCK CAPS LOCK key}. + * + * @return {@code true} if CAPS LOCK is enabled; {@code false} otherwise + */ + public boolean isCapsLockEnabled() { + if (useToolkit) { + try { + return Toolkit.getDefaultToolkit().getLockingKeyState(VK_CAPS_LOCK); + } catch (UnsupportedOperationException shouldNeverHappen) { + return capsLockeEnabled; + } + } + + return capsLockeEnabled; + } + + void setCapsLockEnabled(boolean capsLockEnabled) { + boolean oldValue = this.capsLockeEnabled; + this.capsLockeEnabled = capsLockEnabled; + firePropertyChange("capsLockEnabled", oldValue, this.capsLockeEnabled); //$NON-NLS-1$ + } + + // updateViaKeyEvent is use to find the initial state of the CAPS LOCK key when the Toolkit does + // not support it + /** + * This is an implementation detail and should not be considered public. + */ + @Override + public boolean dispatchKeyEvent(KeyEvent e) { + if (e.getID() == KeyEvent.KEY_PRESSED) { + int keyCode = e.getKeyCode(); + + if (keyCode == VK_CAPS_LOCK) { + if (!updateViaKeyEvent) { + if (useToolkit) { + try { + setCapsLockEnabled(Toolkit.getDefaultToolkit().getLockingKeyState(VK_CAPS_LOCK)); + } catch (UnsupportedOperationException shouldNeverHappen) { + setCapsLockEnabled(!capsLockeEnabled); + } + } else { + setCapsLockEnabled(!capsLockeEnabled); + } + } + } else if (updateViaKeyEvent && Character.isLetter(keyCode)) { + if (keyCode == e.getKeyChar()) { + capsLockeEnabled = !e.isShiftDown(); + } else { + capsLockeEnabled = e.isShiftDown(); + } + + updateViaKeyEvent = false; + firePropertyChange("initialized", false, true); //$NON-NLS-1$ + } + } + + return false; + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/SpinningCalendarHeaderHandler.java b/src/main/java/org/jdesktop/swingx/plaf/basic/SpinningCalendarHeaderHandler.java new file mode 100644 index 0000000000..02f3818781 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/SpinningCalendarHeaderHandler.java @@ -0,0 +1,512 @@ +package org.jdesktop.swingx.plaf.basic; + +import org.jdesktop.swingx.JXHyperlink; +import org.jdesktop.swingx.JXMonthView; +import org.jdesktop.swingx.JXPanel; +import org.jdesktop.swingx.renderer.FormatStringValue; + +import javax.swing.*; +import javax.swing.JSpinner.DefaultEditor; +import javax.swing.JSpinner.NumberEditor; +import java.awt.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.logging.Logger; + +/** + * Custom CalendarHeaderHandler which supports year-wise navigation. + *

      + * + * The custom component used as header component of this implementation contains + * month-navigation buttons, a label with localized month text and a spinner for + * .. well ... spinning the years. There is minimal configuration control via + * the UIManager: + * + *

        + *
      • control the position of the nextMonth button: the default is at the + * trailing edge of the header. Option is to insert it directly after the month + * text, to enable set a Boolean.TRUE as value for key + * ARROWS_SURROUNDS_MONTH. + *
      • control the focusability of the spinner's text field: the default is + * false. To enable set a Boolean.TRUE as value for key + * FOCUSABLE_SPINNER_TEXT. + *
      + * + * Note: this header is not used by default. To make it the + * per-application default register it with the UIManager, like + * + *
      
      + * UIManager.put(CalendarHeaderHandler.uiControllerID, 
      + *      "org.jdesktop.swingx.plaf.basic.SpinningCalendarHeaderHandler");
      + * 
      + * 
      + * + * PENDING JW: implement and bind actions for keyboard navigation. These are + * potentially different from navigation by mouse: need to move the selection + * along with the scrolling? + * + */ +public class SpinningCalendarHeaderHandler extends CalendarHeaderHandler { + + /** + * Key for use in UIManager to control the position of the nextMonth arrow. + */ + public static final String ARROWS_SURROUND_MONTH = "SpinningCalendarHeader.arrowsSurroundMonth"; + + /** + * Key for use in UIManager to control the focusable property of the year + * spinner. + */ + public static final String FOCUSABLE_SPINNER_TEXT = "SpinningCalendarHeader.focusableSpinnerText"; + + @SuppressWarnings("unused") + private static final Logger LOG = Logger + .getLogger(SpinningCalendarHeaderHandler.class.getName()); + + /** the spinner model for year-wise navigation. */ + private SpinnerModel yearSpinnerModel; + + /** listener for property changes of the JXMonthView. */ + private PropertyChangeListener monthPropertyListener; + + /** converter for month text. */ + private FormatStringValue monthStringValue; + + // ----------------- public/protected overrides to manage custom + // creation/config + + /** + * {@inheritDoc} + *

      + * + * Overridden to configure header specifics component after calling super. + */ + @Override + public void install(JXMonthView monthView) { + super.install(monthView); + getHeaderComponent().setActions( + monthView.getActionMap().get("previousMonth"), + monthView.getActionMap().get("nextMonth"), + getYearSpinnerModel()); + componentOrientationChanged(); + monthStringBackgroundChanged(); + fontChanged(); + localeChanged(); + } + + /** + * {@inheritDoc} + *

      + * + * Overridden to cleanup the specifics before calling super. + */ + @Override + public void uninstall(JXMonthView monthView) { + getHeaderComponent().setActions(null, null, null); + getHeaderComponent().setMonthText(""); + super.uninstall(monthView); + } + + /** + * {@inheritDoc} + *

      + * + * Convenience override to the type created. + */ + @Override + public SpinningCalendarHeader getHeaderComponent() { + return (SpinningCalendarHeader) super.getHeaderComponent(); + } + + /** + * {@inheritDoc} + *

      + * + * Implemented to create and configure the custom header component. + */ + @Override + protected SpinningCalendarHeader createCalendarHeader() { + SpinningCalendarHeader header = new SpinningCalendarHeader(); + if (Boolean.TRUE.equals(UIManager.getBoolean(FOCUSABLE_SPINNER_TEXT))) { + header.setSpinnerFocusable(true); + } + if (Boolean.TRUE.equals(UIManager.getBoolean(ARROWS_SURROUND_MONTH))) { + header.setArrowsSurroundMonth(true); + } + return header; + } + + /** + * {@inheritDoc} + *

      + */ + @Override + protected void installListeners() { + super.installListeners(); + monthView.addPropertyChangeListener(getPropertyChangeListener()); + } + + /** + * {@inheritDoc} + *

      + */ + @Override + protected void uninstallListeners() { + monthView.removePropertyChangeListener(getPropertyChangeListener()); + super.uninstallListeners(); + } + + // ---------------- listening/update triggered by changes of the JXMonthView + + /** + * Updates the formatter of the month text to the JXMonthView's Locale. + */ + protected void updateFormatters() { + SimpleDateFormat monthNameFormat = (SimpleDateFormat) DateFormat + .getDateInstance(DateFormat.SHORT, monthView.getLocale()); + monthNameFormat.applyPattern("MMMM"); + monthStringValue = new FormatStringValue(monthNameFormat); + } + + /** + * Updates internal state to monthView's firstDisplayedDay. + */ + protected void firstDisplayedDayChanged() { + ((YearSpinnerModel) getYearSpinnerModel()).fireStateChanged(); + getHeaderComponent().setMonthText( + monthStringValue.getString(monthView.getFirstDisplayedDay())); + } + + /** + * Updates internal state to monthView's locale. + */ + protected void localeChanged() { + updateFormatters(); + firstDisplayedDayChanged(); + } + + /** + * Returns the property change listener for use on the monthView. This is + * lazyly created if not yet done. This implementation listens to changes of + * firstDisplayedDay and locale property and updates internal state + * accordingly. + * + * @return the property change listener for the monthView, never null. + */ + private PropertyChangeListener getPropertyChangeListener() { + if (monthPropertyListener == null) { + monthPropertyListener = new PropertyChangeListener() { + + @Override + public void propertyChange(PropertyChangeEvent evt) { + if ("firstDisplayedDay".equals(evt.getPropertyName())) { + firstDisplayedDayChanged(); + } else if ("locale".equals(evt.getPropertyName())) { + localeChanged(); + } + + } + + }; + } + return monthPropertyListener; + } + + // ---------------------- methods to back to Spinner model + + /** + * Returns the current year of the monthView. Callback for spinner model. + * + * return the current year of the monthView. + */ + private int getYear() { + Calendar cal = monthView.getCalendar(); + return cal.get(Calendar.YEAR); + } + + /** + * Returns the previous year of the monthView. Callback for spinner model. + *

      + * + * PENDING JW: check against lower bound. + * + * return the previous year of the monthView. + */ + private int getPreviousYear() { + Calendar cal = monthView.getCalendar(); + cal.add(Calendar.YEAR, -1); + return cal.get(Calendar.YEAR); + } + + /** + * Returns the next year of the monthView. Callback for spinner model. + *

      + * + * PENDING JW: check against upper bound. + * + * return the next year of the monthView. + */ + private int getNextYear() { + Calendar cal = monthView.getCalendar(); + cal.add(Calendar.YEAR, 1); + return cal.get(Calendar.YEAR); + } + + /** + * Sets the current year of the monthView to the given value. Callback for + * spinner model. + * + * @param value the new value of the year. + * @return a boolean indicating if a change actually happened. + */ + private boolean setYear(Object value) { + int year = ((Integer) value).intValue(); + Calendar cal = monthView.getCalendar(); + if (cal.get(Calendar.YEAR) == year) + return false; + cal.set(Calendar.YEAR, year); + monthView.setFirstDisplayedDay(cal.getTime()); + return true; + } + + /** + * Thin-layer implementation of a SpinnerModel which is actually backed by + * this controller. + */ + private class YearSpinnerModel extends AbstractSpinnerModel { + + @Override + public Object getNextValue() { + return getNextYear(); + } + + @Override + public Object getPreviousValue() { + return getPreviousYear(); + } + + @Override + public Object getValue() { + return getYear(); + } + + @Override + public void setValue(Object value) { + if (setYear(value)) { + fireStateChanged(); + } + } + + @Override + public void fireStateChanged() { + super.fireStateChanged(); + } + + } + + private SpinnerModel getYearSpinnerModel() { + if (yearSpinnerModel == null) { + yearSpinnerModel = new YearSpinnerModel(); + } + return yearSpinnerModel; + } + + /** + * The custom header component controlled and configured by this handler. + * + */ + protected static class SpinningCalendarHeader extends JXPanel { + private AbstractButton prevButton; + + private AbstractButton nextButton; + + private JLabel monthText; + + private JSpinner yearSpinner; + + private boolean surroundMonth; + + public SpinningCalendarHeader() { + initComponents(); + } + + /** + * Installs the actions and models to be used by this component. + * + * @param prev the action to use for the previous button + * @param next the action to use for the next button + * @param model the spinner model to use for the spinner. + */ + public void setActions(Action prev, Action next, SpinnerModel model) { + prevButton.setAction(prev); + nextButton.setAction(next); + uninstallZoomAction(); + installZoomAction(model); + } + + /** + * Sets the focusable property of the spinner's editor's text field. + * + * The default value is false. + * + * @param focusable the focusable property of the spinner's editor. + */ + public void setSpinnerFocusable(boolean focusable) { + ((DefaultEditor) yearSpinner.getEditor()).getTextField() + .setFocusable(focusable); + } + + /** + * The default value is false. + * + * @param surroundMonth + */ + public void setArrowsSurroundMonth(boolean surroundMonth) { + if (this.surroundMonth == surroundMonth) + return; + this.surroundMonth = surroundMonth; + removeAll(); + addComponents(); + } + + /** + * Sets the text to use for the month label. + * + * @param text the text to use for the month label. + */ + public void setMonthText(String text) { + monthText.setText(text); + } + + /** + * {@inheritDoc} + *

      + * + * Overridden to set the font of its child components. + */ + @Override + public void setFont(Font font) { + super.setFont(font); + if (monthText != null) { + monthText.setFont(font); + yearSpinner.setFont(font); + yearSpinner.getEditor().setFont(font); + ((DefaultEditor) yearSpinner.getEditor()).getTextField() + .setFont(font); + } + } + + /** + * {@inheritDoc} + *

      + * + * Overridden to set the background of its child compenents. + */ + @Override + public void setBackground(Color bg) { + super.setBackground(bg); + for (int i = 0; i < getComponentCount(); i++) { + getComponent(i).setBackground(bg); + } + if (yearSpinner != null) { + yearSpinner.setBackground(bg); + yearSpinner.setBorder(BorderFactory.createLineBorder(bg, 2)); + yearSpinner.getEditor().setBackground(bg); + ((DefaultEditor) yearSpinner.getEditor()).getTextField() + .setBackground(bg); + } + } + + private void installZoomAction(SpinnerModel model) { + if (model == null) + return; + yearSpinner.setModel(model); + } + + private void uninstallZoomAction() { + } + + private void initComponents() { + createComponents(); + setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS)); + setBorder(BorderFactory.createEmptyBorder(2, 4, 2, 4)); + addComponents(); + } + + /** + * + */ + private void addComponents() { + if (surroundMonth) { + add(prevButton); + add(monthText); + add(nextButton); + add(Box.createHorizontalStrut(5)); + add(yearSpinner); + } else { + add(prevButton); + add(Box.createHorizontalGlue()); + add(monthText); + add(Box.createHorizontalStrut(5)); + add(yearSpinner); + add(Box.createHorizontalGlue()); + add(nextButton); + } + } + + /** + * + */ + private void createComponents() { + prevButton = createNavigationButton(); + nextButton = createNavigationButton(); + monthText = createMonthText(); + yearSpinner = createSpinner(); + } + + private JLabel createMonthText() { + JLabel comp = new JLabel() { + + @Override + public Dimension getMaximumSize() { + Dimension dim = super.getMaximumSize(); + dim.width = Integer.MAX_VALUE; + dim.height = Integer.MAX_VALUE; + return dim; + } + + }; + comp.setHorizontalAlignment(JLabel.CENTER); + return comp; + } + + /** + * Creates and returns the JSpinner used for year navigation. + * + * @return + */ + private JSpinner createSpinner() { + JSpinner spinner = new JSpinner(); + spinner.setFocusable(false); + NumberEditor editor = new NumberEditor(spinner); + editor.getFormat().setGroupingUsed(false); + editor.getTextField().setFocusable(false); + spinner.setEditor(editor); + return spinner; + } + + private AbstractButton createNavigationButton() { + JXHyperlink b = new JXHyperlink(); + b.setContentAreaFilled(false); + b.setBorder(BorderFactory.createEmptyBorder()); + b.setRolloverEnabled(true); + b.setFocusable(false); + return b; + } + + } + +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/TextCrossingPainter.java b/src/main/java/org/jdesktop/swingx/plaf/basic/TextCrossingPainter.java new file mode 100644 index 0000000000..f64b7d8923 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/TextCrossingPainter.java @@ -0,0 +1,96 @@ +/* + * $Id: TextCrossingPainter.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf.basic; + +import org.jdesktop.swingx.painter.AbstractPainter; + +import javax.swing.*; +import java.awt.*; + +/** + * Painter used to cross-out unselectable dates. + * + * PENDING JW: subclass (or maybe even use?) one of the painter subclasses. + * + * @author Jeanette Winzenburg + */ +class TextCrossingPainter extends AbstractPainter { + Rectangle paintIconR = new Rectangle(); + Rectangle paintViewR = new Rectangle(); + Rectangle paintTextR = new Rectangle(); + Insets insetss = new Insets(0, 0, 0, 0); + Color crossColor; + /** + * {@inheritDoc}

      + * + * Paints a diagonal cross over the text if the comp is of type JLabel, + * does nothing otherwise. + */ + @Override + protected void doPaint(Graphics2D g, JComponent comp, int width, + int height) { + if (!(comp instanceof JLabel)) return; + JLabel label = (JLabel) comp; + Insets insets = label.getInsets(insetss); + 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; + SwingUtilities.layoutCompoundLabel(label, + label.getFontMetrics(label.getFont()), label.getText(), null, + label.getVerticalAlignment(), label.getHorizontalAlignment(), + label.getVerticalTextPosition(), label.getHorizontalTextPosition(), + paintViewR, paintIconR, paintTextR, label.getIconTextGap()); + doPaint(g, paintTextR); + } + + private void doPaint(Graphics2D g, Rectangle r) { + Color old = g.getColor(); + g.setColor(getForeground()); + g.drawLine(r.x, r.y, r.x + r.width, r.y + r.height); + g.drawLine(r.x + 1, r.y, r.x + r.width + 1, r.y + r.height); + g.drawLine(r.x + r.width, r.y, r.x, r.y + r.height); + g.drawLine(r.x + r.width - 1, r.y, r.x - 1, r.y + r.height); + g.setColor(old); + + } + + /** + * + * @param crossColor the color to paint the cross with + */ + public void setForeground(Color crossColor) { + Color old = getForeground(); + this.crossColor = crossColor; + firePropertyChange("foreground", old, getForeground()); + } + + /** + * Returns the color to use for painting the cross. + * + * @return the color used for painting. + */ + public Color getForeground() { + return crossColor; + } +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/core/BasicTransferable.java b/src/main/java/org/jdesktop/swingx/plaf/basic/core/BasicTransferable.java new file mode 100644 index 0000000000..d63282f0b2 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/core/BasicTransferable.java @@ -0,0 +1,283 @@ +/* + * $Id$ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.plaf.basic.core; + + +import javax.swing.plaf.UIResource; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.io.*; + +/** + * A transferable implementation for the default data transfer of some Swing + * components. + * + * @author Timothy Prinzing + * @version 1.10 11/17/05 + */ +public class BasicTransferable implements Transferable, UIResource { + + protected String plainData; + protected String htmlData; + + private static DataFlavor[] htmlFlavors; + private static DataFlavor[] stringFlavors; + private static DataFlavor[] plainFlavors; + + static { + try { + htmlFlavors = new DataFlavor[3]; + htmlFlavors[0] = new DataFlavor("text/html;class=java.lang.String"); + htmlFlavors[1] = new DataFlavor("text/html;class=java.io.Reader"); + htmlFlavors[2] = new DataFlavor("text/html;charset=unicode;class=java.io.InputStream"); + + plainFlavors = new DataFlavor[3]; + plainFlavors[0] = new DataFlavor("text/plain;class=java.lang.String"); + plainFlavors[1] = new DataFlavor("text/plain;class=java.io.Reader"); + plainFlavors[2] = new DataFlavor("text/plain;charset=unicode;class=java.io.InputStream"); + + stringFlavors = new DataFlavor[2]; + stringFlavors[0] = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType+";class=java.lang.String"); + stringFlavors[1] = DataFlavor.stringFlavor; + + } catch (ClassNotFoundException cle) { + System.err.println("error initializing javax.swing.plaf.basic.BasicTranserable"); + } + } + + public BasicTransferable(String plainData, String htmlData) { + this.plainData = plainData; + this.htmlData = htmlData; + } + + + /** + * Returns an array of DataFlavor objects indicating the flavors the data + * can be provided in. The array should be ordered according to preference + * for providing the data (from most richly descriptive to least descriptive). + * @return an array of data flavors in which this data can be transferred + */ + public DataFlavor[] getTransferDataFlavors() { + DataFlavor[] richerFlavors = getRicherFlavors(); + int nRicher = (richerFlavors != null) ? richerFlavors.length : 0; + int nHTML = (isHTMLSupported()) ? htmlFlavors.length : 0; + int nPlain = (isPlainSupported()) ? plainFlavors.length: 0; + int nString = (isPlainSupported()) ? stringFlavors.length : 0; + int nFlavors = nRicher + nHTML + nPlain + nString; + DataFlavor[] flavors = new DataFlavor[nFlavors]; + + // fill in the array + int nDone = 0; + if (nRicher > 0) { + System.arraycopy(richerFlavors, 0, flavors, nDone, nRicher); + nDone += nRicher; + } + if (nHTML > 0) { + System.arraycopy(htmlFlavors, 0, flavors, nDone, nHTML); + nDone += nHTML; + } + if (nPlain > 0) { + System.arraycopy(plainFlavors, 0, flavors, nDone, nPlain); + nDone += nPlain; + } + if (nString > 0) { + System.arraycopy(stringFlavors, 0, flavors, nDone, nString); + nDone += nString; + } + return flavors; + } + + /** + * Returns whether or not the specified data flavor is supported for + * this object. + * @param flavor the requested flavor for the data + * @return boolean indicating whether or not the data flavor is supported + */ + public boolean isDataFlavorSupported(DataFlavor flavor) { + DataFlavor[] flavors = getTransferDataFlavors(); + for (int i = 0; i < flavors.length; i++) { + if (flavors[i].equals(flavor)) { + return true; + } + } + return false; + } + + /** + * Returns an object which represents the data to be transferred. The class + * of the object returned is defined by the representation class of the flavor. + * + * @param flavor the requested flavor for the data + * @see DataFlavor#getRepresentationClass + * @exception IOException if the data is no longer available + * in the requested flavor. + * @exception UnsupportedFlavorException if the requested data flavor is + * not supported. + */ + public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { + DataFlavor[] richerFlavors = getRicherFlavors(); + if (isRicherFlavor(flavor)) { + return getRicherData(flavor); + } else if (isHTMLFlavor(flavor)) { + String data = getHTMLData(); + data = (data == null) ? "" : data; + if (String.class.equals(flavor.getRepresentationClass())) { + return data; + } else if (Reader.class.equals(flavor.getRepresentationClass())) { + return new StringReader(data); + } else if (InputStream.class.equals(flavor.getRepresentationClass())) { + return new StringBufferInputStream(data); + } + // fall through to unsupported + } else if (isPlainFlavor(flavor)) { + String data = getPlainData(); + data = (data == null) ? "" : data; + if (String.class.equals(flavor.getRepresentationClass())) { + return data; + } else if (Reader.class.equals(flavor.getRepresentationClass())) { + return new StringReader(data); + } else if (InputStream.class.equals(flavor.getRepresentationClass())) { + return new StringBufferInputStream(data); + } + // fall through to unsupported + + } else if (isStringFlavor(flavor)) { + String data = getPlainData(); + data = (data == null) ? "" : data; + return data; + } + throw new UnsupportedFlavorException(flavor); + } + + // --- richer subclass flavors ---------------------------------------------- + + protected boolean isRicherFlavor(DataFlavor flavor) { + DataFlavor[] richerFlavors = getRicherFlavors(); + int nFlavors = (richerFlavors != null) ? richerFlavors.length : 0; + for (int i = 0; i < nFlavors; i++) { + if (richerFlavors[i].equals(flavor)) { + return true; + } + } + return false; + } + + /** + * Some subclasses will have flavors that are more descriptive than HTML + * or plain text. If this method returns a non-null value, it will be + * placed at the start of the array of supported flavors. + */ + protected DataFlavor[] getRicherFlavors() { + return null; + } + + protected Object getRicherData(DataFlavor flavor) throws UnsupportedFlavorException { + return null; + } + + // --- html flavors ---------------------------------------------------------- + + /** + * Returns whether or not the specified data flavor is an HTML flavor that + * is supported. + * @param flavor the requested flavor for the data + * @return boolean indicating whether or not the data flavor is supported + */ + protected boolean isHTMLFlavor(DataFlavor flavor) { + DataFlavor[] flavors = htmlFlavors; + for (int i = 0; i < flavors.length; i++) { + if (flavors[i].equals(flavor)) { + return true; + } + } + return false; + } + + /** + * Should the HTML flavors be offered? If so, the method + * getHTMLData should be implemented to provide something reasonable. + */ + protected boolean isHTMLSupported() { + return htmlData != null; + } + + /** + * Fetch the data in a text/html format + */ + protected String getHTMLData() { + return htmlData; + } + + // --- plain text flavors ---------------------------------------------------- + + /** + * Returns whether or not the specified data flavor is an plain flavor that + * is supported. + * @param flavor the requested flavor for the data + * @return boolean indicating whether or not the data flavor is supported + */ + protected boolean isPlainFlavor(DataFlavor flavor) { + DataFlavor[] flavors = plainFlavors; + for (int i = 0; i < flavors.length; i++) { + if (flavors[i].equals(flavor)) { + return true; + } + } + return false; + } + + /** + * Should the plain text flavors be offered? If so, the method + * getPlainData should be implemented to provide something reasonable. + */ + protected boolean isPlainSupported() { + return plainData != null; + } + + /** + * Fetch the data in a text/plain format. + */ + protected String getPlainData() { + return plainData; + } + + // --- string flavorss -------------------------------------------------------- + + /** + * Returns whether or not the specified data flavor is a String flavor that + * is supported. + * @param flavor the requested flavor for the data + * @return boolean indicating whether or not the data flavor is supported + */ + protected boolean isStringFlavor(DataFlavor flavor) { + DataFlavor[] flavors = stringFlavors; + for (int i = 0; i < flavors.length; i++) { + if (flavors[i].equals(flavor)) { + return true; + } + } + return false; + } + + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/core/BasicXListUI.java b/src/main/java/org/jdesktop/swingx/plaf/basic/core/BasicXListUI.java new file mode 100644 index 0000000000..e3af7b9853 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/core/BasicXListUI.java @@ -0,0 +1,3119 @@ +/* + * $Id$ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.plaf.basic.core; + +import org.jdesktop.swingx.JXList; +import org.jdesktop.swingx.SwingXUtilities; +import org.jdesktop.swingx.plaf.LookAndFeelUtils; +import org.jdesktop.swingx.plaf.UIAction; +import org.jdesktop.swingx.plaf.basic.core.DragRecognitionSupport.BeforeDrag; + +import javax.swing.*; +import javax.swing.event.*; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.UIResource; +import javax.swing.plaf.basic.BasicListUI; +import javax.swing.text.Position; +import java.awt.*; +import java.awt.datatransfer.Transferable; +import java.awt.event.*; +import java.awt.geom.Point2D; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +//import sun.swing.DefaultLookup; +//import sun.swing.SwingUtilities2; +//import sun.swing.UIAction; + +/** + * An extensible implementation of {@code ListUI} for JXList. + * {@code BasicXListUI} instances cannot be shared between multiple + * lists.

      + * + * The heart of added functionality is to support sorting/filtering, that is keep + * model-selection and RowSorter state synchronized. The details are delegated to a ListSortUI, + * but this class is responsible to manage the sortUI on changes of list properties, model and + * view selection (same strategy as in JXTable).

      + * + * Note: this delegate is mostly a 1:1 copy of BasicListUI. The difference is that + * it accesses the list elements and list elementCount exclusively through the + * JXList api. This allows a clean implementation of sorting/filtering.

      + * + * The differences (goal was to touch as little code as possible as this needs + * to be updated on every change to core until that is changed to not access + * the list's model directly, sigh) for core functionality: + *

        + *
      • extracted method for list.getModel().getSize (for the delegate class and + * all contained static classes) and use that method exclusively + *
      • similar for remaining list.getModel(): implemented wrapping listModel + * which messages the list + *
      • rename key for shared actionMap to keep core list actions separate + * (just in case somebody wants both) - they point to the wrong delegate + *
      • replaced references to SwingUtilities2 in sun packages by references to + * c&p'ed methods in SwingXUtilities + *
      • replaced storage of shared Input/ActionMap in defaultLookup by direct + * storage in UIManager. + *
      + * + * Differences to achieve extended functionality: + *
        + *
      • added methods to un/-installSortUI and call in un/installUI(component) + *
      • changed PropertyChangeHandler to call a) hasHandledPropertyChange to + * allow this class to replace super handler functinality and + * b) updateSortUI after handling all not-sorter related properties. + *
      • changed createPropertyChangeListener to return a PropertyChangeHandler + *
      • changed ListDataHandler to check if event handled by SortUI and delegate + * to handler only if not + *
      • changed createListDataListener to return a ListDataHandler + *
      • changed ListSelectionHandler to check if event handled by SortUI and + * delegate to handler only if not + *
      changed createListSelectionListener to return a ListSelectionHandler + * + * Differences for bug fixes (due to incorrectly extending super): + *
        + *
      • Issue #1495-swingx: getBaseline throughs NPE + *
      + * + * Note: extension of core (instead of implement from scratch) is to keep + * external (?) code working which expects a ui delegate of type BasicSomething. + * LAF implementors with a custom ListUI extending BasicListUI should be able to + * add support for JXList by adding a separate CustomXListUI extending this, same + * as the default with parent changed. Beware: custom code must not + * call access the model directly or - if they insist - convert the row index to + * account for sorting/filtering! That's the whole point of this class. + * + * @version 1.127 12/02/08 + * @author Hans Muller + * @author Philip Milne + * @author Shannon Hickey (drag and drop) + */ +public class BasicXListUI extends BasicListUI +{ + private static final StringBuilder BASELINE_COMPONENT_KEY = + new StringBuilder("List.baselineComponent"); + + protected JXList list = null; + protected CellRendererPane rendererPane; + + // Listeners that this UI attaches to the JList + protected FocusListener focusListener; + protected MouseInputListener mouseInputListener; + protected ListSelectionListener listSelectionListener; + protected ListDataListener listDataListener; + protected PropertyChangeListener propertyChangeListener; + private Handler handler; + + protected int[] cellHeights = null; + protected int cellHeight = -1; + protected int cellWidth = -1; + protected int updateLayoutStateNeeded = modelChanged; + /** + * Height of the list. When asked to paint, if the current size of + * the list differs, this will update the layout state. + */ + private int listHeight; + + /** + * Width of the list. When asked to paint, if the current size of + * the list differs, this will update the layout state. + */ + private int listWidth; + + /** + * The layout orientation of the list. + */ + private int layoutOrientation; + + // Following ivars are used if the list is laying out horizontally + + /** + * Number of columns to create. + */ + private int columnCount; + /** + * Preferred height to make the list, this is only used if the + * the list is layed out horizontally. + */ + private int preferredHeight; + /** + * Number of rows per column. This is only used if the row height is + * fixed. + */ + private int rowsPerColumn; + + /** + * The time factor to treate the series of typed alphanumeric key + * as prefix for first letter navigation. + */ + private long timeFactor = 1000L; + + /** + * Local cache of JList's client property "List.isFileList" + */ + private boolean isFileList = false; + + /** + * Local cache of JList's component orientation property + */ + private boolean isLeftToRight = true; + + /* The bits below define JList property changes that affect layout. + * When one of these properties changes we set a bit in + * updateLayoutStateNeeded. The change is dealt with lazily, see + * maybeUpdateLayoutState. Changes to the JLists model, e.g. the + * models length changed, are handled similarly, see DataListener. + */ + + protected final static int modelChanged = 1 << 0; + protected final static int selectionModelChanged = 1 << 1; + protected final static int fontChanged = 1 << 2; + protected final static int fixedCellWidthChanged = 1 << 3; + protected final static int fixedCellHeightChanged = 1 << 4; + protected final static int prototypeCellValueChanged = 1 << 5; + protected final static int cellRendererChanged = 1 << 6; + private final static int layoutOrientationChanged = 1 << 7; + private final static int heightChanged = 1 << 8; + private final static int widthChanged = 1 << 9; + private final static int componentOrientationChanged = 1 << 10; + + private static final int DROP_LINE_THICKNESS = 2; + + // FIXME - JW LazyActionMap copy is in different package ... move here? + public static void loadActionMap(LazyActionMap map) { + map.put(new Actions(Actions.SELECT_PREVIOUS_COLUMN)); + map.put(new Actions(Actions.SELECT_PREVIOUS_COLUMN_EXTEND)); + map.put(new Actions(Actions.SELECT_PREVIOUS_COLUMN_CHANGE_LEAD)); + map.put(new Actions(Actions.SELECT_NEXT_COLUMN)); + map.put(new Actions(Actions.SELECT_NEXT_COLUMN_EXTEND)); + map.put(new Actions(Actions.SELECT_NEXT_COLUMN_CHANGE_LEAD)); + map.put(new Actions(Actions.SELECT_PREVIOUS_ROW)); + map.put(new Actions(Actions.SELECT_PREVIOUS_ROW_EXTEND)); + map.put(new Actions(Actions.SELECT_PREVIOUS_ROW_CHANGE_LEAD)); + map.put(new Actions(Actions.SELECT_NEXT_ROW)); + map.put(new Actions(Actions.SELECT_NEXT_ROW_EXTEND)); + map.put(new Actions(Actions.SELECT_NEXT_ROW_CHANGE_LEAD)); + map.put(new Actions(Actions.SELECT_FIRST_ROW)); + map.put(new Actions(Actions.SELECT_FIRST_ROW_EXTEND)); + map.put(new Actions(Actions.SELECT_FIRST_ROW_CHANGE_LEAD)); + map.put(new Actions(Actions.SELECT_LAST_ROW)); + map.put(new Actions(Actions.SELECT_LAST_ROW_EXTEND)); + map.put(new Actions(Actions.SELECT_LAST_ROW_CHANGE_LEAD)); + map.put(new Actions(Actions.SCROLL_UP)); + map.put(new Actions(Actions.SCROLL_UP_EXTEND)); + map.put(new Actions(Actions.SCROLL_UP_CHANGE_LEAD)); + map.put(new Actions(Actions.SCROLL_DOWN)); + map.put(new Actions(Actions.SCROLL_DOWN_EXTEND)); + map.put(new Actions(Actions.SCROLL_DOWN_CHANGE_LEAD)); + map.put(new Actions(Actions.SELECT_ALL)); + map.put(new Actions(Actions.CLEAR_SELECTION)); + map.put(new Actions(Actions.ADD_TO_SELECTION)); + map.put(new Actions(Actions.TOGGLE_AND_ANCHOR)); + map.put(new Actions(Actions.EXTEND_TO)); + map.put(new Actions(Actions.MOVE_SELECTION_TO)); + + map.put(TransferHandler.getCutAction().getValue(Action.NAME), + TransferHandler.getCutAction()); + map.put(TransferHandler.getCopyAction().getValue(Action.NAME), + TransferHandler.getCopyAction()); + map.put(TransferHandler.getPasteAction().getValue(Action.NAME), + TransferHandler.getPasteAction()); + } + +//-------------------- X-Wrapper + + private ListModel modelX; + + private ListSortUI sortUI; + /** + * Compatibility Wrapper: a synthetic model which delegates to list api and throws + * @return + */ + protected ListModel getViewModel() { + if (modelX == null) { + modelX = new ListModel() { + + @Override + public int getSize() { + return ((JXList) list).getElementCount(); + } + + @Override + public Object getElementAt(int index) { + return ((JXList) list).getElementAt(index); + } + + @Override + public void addListDataListener(ListDataListener l) { + throw new UnsupportedOperationException("this is a synthetic model wrapper"); + } + @Override + public void removeListDataListener(ListDataListener l) { + throw new UnsupportedOperationException("this is a synthetic model wrapper"); + + } + + }; + } + return modelX; + } + + /** + * @return + */ + protected int getElementCount() { + return ((JXList) list).getElementCount(); + } + + protected Object getElementAt(int viewIndex) { + return ((JXList) list).getElementAt(viewIndex); + } + + /** + * Fix for Issue #1495: NPE on getBaseline. + * + * As per contract, that methods needs to throw Exceptions on illegal + * parameters. As we by-pass super, need to do the check and throw + * ouerselves. + * + * @param c + * @param width + * @param height + * + * @throws IllegalArgumentException if width or height < 0 + * @throws NPE if c == null + */ + protected void checkBaselinePrecondition(JComponent c, int width, int height) { + if (c == null) { + throw new NullPointerException("Component must be non-null"); + } + if (width < 0 || height < 0) { + throw new IllegalArgumentException( + "Width and height must be >= 0"); + } + } + +//--------------- api to support/control sorting/filtering + + protected ListSortUI getSortUI() { + return sortUI; + } + + /** + * Installs SortUI if the list has a rowSorter. Does nothing if not. + */ + protected void installSortUI() { + if (list.getRowSorter() == null) return; + sortUI = new ListSortUI(list, list.getRowSorter()); + } + + /** + * Dispose and null's the sortUI if installed. Does nothing if not. + */ + protected void uninstallSortUI() { + if (sortUI == null) return; + sortUI.dispose(); + sortUI = null; + } + + /** + * Called from the PropertyChangeHandler. + * + * @param property the name of the changed property. + */ + protected void updateSortUI(String property) { + if ("rowSorter".equals(property)) { + updateSortUIToRowSorterProperty(); + } + } + /** + * + */ + private void updateSortUIToRowSorterProperty() { + uninstallSortUI(); + installSortUI(); + } + + /** + * Returns a boolean indicating whether or not the event has been processed + * by the sortUI. + * @param e + * @return + */ + protected boolean processedBySortUI(ListDataEvent e) { + if (sortUI == null) + return false; + sortUI.modelChanged(e); + updateLayoutStateNeeded = modelChanged; + redrawList(); + return true; + } + + /** + * Returns a boolean indicating whether or not the event has been processed + * by the sortUI. + * @param e + * @return + */ + protected boolean processedBySortUI(ListSelectionEvent e) { + if (sortUI == null) return false; + sortUI.viewSelectionChanged(e); + list.repaint(); + return true; + } + + +//--------------------- enhanced support + /** + * Invalidates the cell size cache and revalidates/-paints the list. + * + */ + public void invalidateCellSizeCache() { + updateLayoutStateNeeded |= 1; + redrawList(); + } + +//--------------------- core copy + + + /** + * Paint one List cell: compute the relevant state, get the "rubber stamp" + * cell renderer component, and then use the CellRendererPane to paint it. + * Subclasses may want to override this method rather than paint(). + * + * @see #paint + */ + protected void paintCell( + Graphics g, + int row, + Rectangle rowBounds, + ListCellRenderer cellRenderer, + ListModel dataModel, + ListSelectionModel selModel, + int leadIndex) + { + Object value = dataModel.getElementAt(row); + boolean cellHasFocus = list.hasFocus() && (row == leadIndex); + boolean isSelected = selModel.isSelectedIndex(row); + + Component rendererComponent = + cellRenderer.getListCellRendererComponent(list, value, row, isSelected, cellHasFocus); + + int cx = rowBounds.x; + int cy = rowBounds.y; + int cw = rowBounds.width; + int ch = rowBounds.height; + + if (isFileList) { + // Shrink renderer to preferred size. This is mostly used on Windows + // where selection is only shown around the file name, instead of + // across the whole list cell. + int w = Math.min(cw, rendererComponent.getPreferredSize().width + 4); + if (!isLeftToRight) { + cx += (cw - w); + } + cw = w; + } + + rendererPane.paintComponent(g, rendererComponent, list, cx, cy, cw, ch, true); + } + + + /** + * Paint the rows that intersect the Graphics objects clipRect. This + * method calls paintCell as necessary. Subclasses + * may want to override these methods. + * + * @see #paintCell + */ + public void paint(Graphics g, JComponent c) { + Shape clip = g.getClip(); + paintImpl(g, c); + g.setClip(clip); + + paintDropLine(g); + } + + private void paintImpl(Graphics g, JComponent c) + { + switch (layoutOrientation) { + case JList.VERTICAL_WRAP: + if (list.getHeight() != listHeight) { + updateLayoutStateNeeded |= heightChanged; + redrawList(); + } + break; + case JList.HORIZONTAL_WRAP: + if (list.getWidth() != listWidth) { + updateLayoutStateNeeded |= widthChanged; + redrawList(); + } + break; + default: + break; + } + maybeUpdateLayoutState(); + + ListCellRenderer renderer = list.getCellRenderer(); + ListModel dataModel = getViewModel(); + ListSelectionModel selModel = list.getSelectionModel(); + int size; + + if ((renderer == null) || (size = dataModel.getSize()) == 0) { + return; + } + + // Determine how many columns we need to paint + Rectangle paintBounds = g.getClipBounds(); + + int startColumn, endColumn; + if (c.getComponentOrientation().isLeftToRight()) { + startColumn = convertLocationToColumn(paintBounds.x, + paintBounds.y); + endColumn = convertLocationToColumn(paintBounds.x + + paintBounds.width, + paintBounds.y); + } else { + startColumn = convertLocationToColumn(paintBounds.x + + paintBounds.width, + paintBounds.y); + endColumn = convertLocationToColumn(paintBounds.x, + paintBounds.y); + } + int maxY = paintBounds.y + paintBounds.height; + int leadIndex = adjustIndex(list.getLeadSelectionIndex(), list); + int rowIncrement = (layoutOrientation == JList.HORIZONTAL_WRAP) ? + columnCount : 1; + + + for (int colCounter = startColumn; colCounter <= endColumn; + colCounter++) { + // And then how many rows in this columnn + int row = convertLocationToRowInColumn(paintBounds.y, colCounter); + int rowCount = getRowCount(colCounter); + int index = getModelIndex(colCounter, row); + Rectangle rowBounds = getCellBounds(list, index, index); + + if (rowBounds == null) { + // Not valid, bail! + return; + } + while (row < rowCount && rowBounds.y < maxY && + index < size) { + rowBounds.height = getHeight(colCounter, row); + g.setClip(rowBounds.x, rowBounds.y, rowBounds.width, + rowBounds.height); + g.clipRect(paintBounds.x, paintBounds.y, paintBounds.width, + paintBounds.height); + paintCell(g, index, rowBounds, renderer, dataModel, selModel, + leadIndex); + rowBounds.y += rowBounds.height; + index += rowIncrement; + row++; + } + } + // Empty out the renderer pane, allowing renderers to be gc'ed. + rendererPane.removeAll(); + } + + + + private void paintDropLine(Graphics g) { + JList.DropLocation loc = list.getDropLocation(); + if (loc == null || !loc.isInsert()) { + return; + } + // PENDING JW: revisit ... side-effects? +// Color c = DefaultLookup.getColor(list, this, "List.dropLineColor", null); + Color c = UIManager.getColor("List.dropLineColor"); + if (c != null) { + g.setColor(c); + Rectangle rect = getDropLineRect(loc); + g.fillRect(rect.x, rect.y, rect.width, rect.height); + } + } + + private Rectangle getDropLineRect(JList.DropLocation loc) { + int size = getElementCount(); + + if (size == 0) { + Insets insets = list.getInsets(); + if (layoutOrientation == JList.HORIZONTAL_WRAP) { + if (isLeftToRight) { + return new Rectangle(insets.left, insets.top, DROP_LINE_THICKNESS, 20); + } else { + return new Rectangle(list.getWidth() - DROP_LINE_THICKNESS - insets.right, + insets.top, DROP_LINE_THICKNESS, 20); + } + } else { + return new Rectangle(insets.left, insets.top, + list.getWidth() - insets.left - insets.right, + DROP_LINE_THICKNESS); + } + } + + Rectangle rect = null; + int index = loc.getIndex(); + boolean decr = false; + + if (layoutOrientation == JList.HORIZONTAL_WRAP) { + if (index == size) { + decr = true; + } else if (index != 0 && convertModelToRow(index) + != convertModelToRow(index - 1)) { + + Rectangle prev = getCellBounds(list, index - 1); + Rectangle me = getCellBounds(list, index); + Point p = loc.getDropPoint(); + + if (isLeftToRight) { + decr = Point2D.distance(prev.x + prev.width, + prev.y + (int)(prev.height / 2.0), + p.x, p.y) + < Point2D.distance(me.x, + me.y + (int)(me.height / 2.0), + p.x, p.y); + } else { + decr = Point2D.distance(prev.x, + prev.y + (int)(prev.height / 2.0), + p.x, p.y) + < Point2D.distance(me.x + me.width, + me.y + (int)(prev.height / 2.0), + p.x, p.y); + } + } + + if (decr) { + index--; + rect = getCellBounds(list, index); + if (isLeftToRight) { + rect.x += rect.width; + } else { + rect.x -= DROP_LINE_THICKNESS; + } + } else { + rect = getCellBounds(list, index); + if (!isLeftToRight) { + rect.x += rect.width - DROP_LINE_THICKNESS; + } + } + + if (rect.x >= list.getWidth()) { + rect.x = list.getWidth() - DROP_LINE_THICKNESS; + } else if (rect.x < 0) { + rect.x = 0; + } + + rect.width = DROP_LINE_THICKNESS; + } else if (layoutOrientation == JList.VERTICAL_WRAP) { + if (index == size) { + index--; + rect = getCellBounds(list, index); + rect.y += rect.height; + } else if (index != 0 && convertModelToColumn(index) + != convertModelToColumn(index - 1)) { + + Rectangle prev = getCellBounds(list, index - 1); + Rectangle me = getCellBounds(list, index); + Point p = loc.getDropPoint(); + if (Point2D.distance(prev.x + (int)(prev.width / 2.0), + prev.y + prev.height, + p.x, p.y) + < Point2D.distance(me.x + (int)(me.width / 2.0), + me.y, + p.x, p.y)) { + + index--; + rect = getCellBounds(list, index); + rect.y += rect.height; + } else { + rect = getCellBounds(list, index); + } + } else { + rect = getCellBounds(list, index); + } + + if (rect.y >= list.getHeight()) { + rect.y = list.getHeight() - DROP_LINE_THICKNESS; + } + + rect.height = DROP_LINE_THICKNESS; + } else { + if (index == size) { + index--; + rect = getCellBounds(list, index); + rect.y += rect.height; + } else { + rect = getCellBounds(list, index); + } + + if (rect.y >= list.getHeight()) { + rect.y = list.getHeight() - DROP_LINE_THICKNESS; + } + + rect.height = DROP_LINE_THICKNESS; + } + + return rect; + } + + /** + * Returns the baseline. + * + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + * @see JComponent#getBaseline(int, int) + * @since 1.6 + */ + public int getBaseline(JComponent c, int width, int height) { +// super.getBaseline(c, width, height); + checkBaselinePrecondition(c, width, height); + int rowHeight = list.getFixedCellHeight(); + UIDefaults lafDefaults = UIManager.getLookAndFeelDefaults(); + Component renderer = (Component)lafDefaults.get( + BASELINE_COMPONENT_KEY); + if (renderer == null) { + ListCellRenderer lcr = (ListCellRenderer)UIManager.get( + "List.cellRenderer"); + + // fix for 6711072 some LAFs like Nimbus do not provide this + // UIManager key and we should not through a NPE here because of it + if (lcr == null) { + lcr = new DefaultListCellRenderer(); + } + + renderer = lcr.getListCellRendererComponent( + list, "a", -1, false, false); + lafDefaults.put(BASELINE_COMPONENT_KEY, renderer); + } + renderer.setFont(list.getFont()); + // JList actually has much more complex behavior here. + // If rowHeight != -1 the rowHeight is either the max of all cell + // heights (layout orientation != VERTICAL), or is variable depending + // upon the cell. We assume a default size. + // We could theoretically query the real renderer, but that would + // not work for an empty model and the results may vary with + // the content. + if (rowHeight == -1) { + rowHeight = renderer.getPreferredSize().height; + } + return renderer.getBaseline(Integer.MAX_VALUE, rowHeight) + + list.getInsets().top; + } + + /** + * Returns an enum indicating how the baseline of the component + * changes as the size changes. + * + * @throws NullPointerException {@inheritDoc} + * @see JComponent#getBaseline(int, int) + * @since 1.6 + */ + public Component.BaselineResizeBehavior getBaselineResizeBehavior( + JComponent c) { + super.getBaselineResizeBehavior(c); + return Component.BaselineResizeBehavior.CONSTANT_ASCENT; + } + + /** + * The preferredSize of the list depends upon the layout orientation. + * + * + * + * + * + * + *
      Layout OrientationPreferred Size
      JList.VERTICAL + * The preferredSize of the list is total height of the rows + * and the maximum width of the cells. If JList.fixedCellHeight + * is specified then the total height of the rows is just + * (cellVerticalMargins + fixedCellHeight) * model.getSize() where + * rowVerticalMargins is the space we allocate for drawing + * the yellow focus outline. Similarly if fixedCellWidth is + * specified then we just use that. + *
      JList.VERTICAL_WRAP + * If the visible row count is greater than zero, the preferredHeight + * is the maximum cell height * visibleRowCount. If the visible row + * count is <= 0, the preferred height is either the current height + * of the list, or the maximum cell height, whichever is + * bigger. The preferred width is than the maximum cell width * + * number of columns needed. Where the number of columns needs is + * list.height / max cell height. Max cell height is either the fixed + * cell height, or is determined by iterating through all the cells + * to find the maximum height from the ListCellRenderer. + *
      JList.HORIZONTAL_WRAP + * If the visible row count is greater than zero, the preferredHeight + * is the maximum cell height * adjustedRowCount. Where + * visibleRowCount is used to determine the number of columns. + * Because this lays out horizontally the number of rows is + * then determined from the column count. For example, lets say + * you have a model with 10 items and the visible row count is 8. + * The number of columns needed to display this is 2, but you no + * longer need 8 rows to display this, you only need 5, thus + * the adjustedRowCount is 5. + *

      If the visible row + * count is <= 0, the preferred height is dictated by the + * number of columns, which will be as many as can fit in the width + * of the JList (width / max cell width), with at + * least one column. The preferred height then becomes the + * model size / number of columns * maximum cell height. + * Max cell height is either the fixed + * cell height, or is determined by iterating through all the cells + * to find the maximum height from the ListCellRenderer. + *

      + * The above specifies the raw preferred width and height. The resulting + * preferred width is the above width + insets.left + insets.right and + * the resulting preferred height is the above height + insets.top + + * insets.bottom. Where the Insets are determined from + * list.getInsets(). + * + * @param c The JList component. + * @return The total size of the list. + */ + public Dimension getPreferredSize(JComponent c) { + maybeUpdateLayoutState(); + + int lastRow = getElementCount() - 1; + if (lastRow < 0) { + return new Dimension(0, 0); + } + + Insets insets = list.getInsets(); + int width = cellWidth * columnCount + insets.left + insets.right; + int height; + + if (layoutOrientation != JList.VERTICAL) { + height = preferredHeight; + } + else { + Rectangle bounds = getCellBounds(list, lastRow); + + if (bounds != null) { + height = bounds.y + bounds.height + insets.bottom; + } + else { + height = 0; + } + } + return new Dimension(width, height); + } + + + /** + * Selected the previous row and force it to be visible. + * + * @see JList#ensureIndexIsVisible + */ + protected void selectPreviousIndex() { + int s = list.getSelectedIndex(); + if(s > 0) { + s -= 1; + list.setSelectedIndex(s); + list.ensureIndexIsVisible(s); + } + } + + + /** + * Selected the previous row and force it to be visible. + * + * @see JList#ensureIndexIsVisible + */ + protected void selectNextIndex() + { + int s = list.getSelectedIndex(); + if((s + 1) < getElementCount()) { + s += 1; + list.setSelectedIndex(s); + list.ensureIndexIsVisible(s); + } + } + + + /** + * Registers the keyboard bindings on the JList that the + * BasicXListUI is associated with. This method is called at + * installUI() time. + * + * @see #installUI + */ + protected void installKeyboardActions() { + InputMap inputMap = getInputMap(JComponent.WHEN_FOCUSED); + + SwingUtilities.replaceUIInputMap(list, JComponent.WHEN_FOCUSED, + inputMap); + + LazyActionMap.installLazyActionMap(list, BasicXListUI.class, + "XList.actionMap"); + } + + InputMap getInputMap(int condition) { + if (condition == JComponent.WHEN_FOCUSED) { + // PENDING JW: side-effect when reverting to ui manager? revisit! + InputMap keyMap = (InputMap) UIManager.get("List.focusInputMap"); +// InputMap keyMap = (InputMap)DefaultLookup.get( +// list, this, "List.focusInputMap"); + InputMap rtlKeyMap; + + if (isLeftToRight || + ((rtlKeyMap = (InputMap) UIManager.get("List.focusInputMap.RightToLeft")) + == null)) { +// ((rtlKeyMap = (InputMap)DefaultLookup.get(list, this, +// "List.focusInputMap.RightToLeft")) == null)) { + return keyMap; + } else { + rtlKeyMap.setParent(keyMap); + return rtlKeyMap; + } + } + return null; + } + + /** + * Unregisters keyboard actions installed from + * installKeyboardActions. + * This method is called at uninstallUI() time - subclassess should + * ensure that all of the keyboard actions registered at installUI + * time are removed here. + * + * @see #installUI + */ + protected void uninstallKeyboardActions() { + SwingUtilities.replaceUIActionMap(list, null); + SwingUtilities.replaceUIInputMap(list, JComponent.WHEN_FOCUSED, null); + } + + + /** + * Create and install the listeners for the JList, its model, and its + * selectionModel. This method is called at installUI() time. + * + * @see #installUI + * @see #uninstallListeners + */ + protected void installListeners() + { + TransferHandler th = list.getTransferHandler(); + if (th == null || th instanceof UIResource) { + list.setTransferHandler(defaultTransferHandler); + // default TransferHandler doesn't support drop + // so we don't want drop handling + if (list.getDropTarget() instanceof UIResource) { + list.setDropTarget(null); + } + } + + focusListener = createFocusListener(); + mouseInputListener = createMouseInputListener(); + propertyChangeListener = createPropertyChangeListener(); + listSelectionListener = createListSelectionListener(); + listDataListener = createListDataListener(); + + list.addFocusListener(focusListener); + list.addMouseListener(mouseInputListener); + list.addMouseMotionListener(mouseInputListener); + list.addPropertyChangeListener(propertyChangeListener); + list.addKeyListener(getHandler()); + // JW: here we really want the model + ListModel model = list.getModel(); + if (model != null) { + model.addListDataListener(listDataListener); + } + + ListSelectionModel selectionModel = list.getSelectionModel(); + if (selectionModel != null) { + selectionModel.addListSelectionListener(listSelectionListener); + } + } + + + /** + * Remove the listeners for the JList, its model, and its + * selectionModel. All of the listener fields, are reset to + * null here. This method is called at uninstallUI() time, + * it should be kept in sync with installListeners. + * + * @see #uninstallUI + * @see #installListeners + */ + protected void uninstallListeners() + { + list.removeFocusListener(focusListener); + list.removeMouseListener(mouseInputListener); + list.removeMouseMotionListener(mouseInputListener); + list.removePropertyChangeListener(propertyChangeListener); + list.removeKeyListener(getHandler()); + + ListModel model = list.getModel(); + if (model != null) { + model.removeListDataListener(listDataListener); + } + + ListSelectionModel selectionModel = list.getSelectionModel(); + if (selectionModel != null) { + selectionModel.removeListSelectionListener(listSelectionListener); + } + + focusListener = null; + mouseInputListener = null; + listSelectionListener = null; + listDataListener = null; + propertyChangeListener = null; + handler = null; + } + + + /** + * Initialize JList properties, e.g. font, foreground, and background, + * and add the CellRendererPane. The font, foreground, and background + * properties are only set if their current value is either null + * or a UIResource, other properties are set if the current + * value is null. + * + * @see #uninstallDefaults + * @see #installUI + * @see CellRendererPane + */ + protected void installDefaults() + { + list.setLayout(null); + + LookAndFeel.installBorder(list, "List.border"); + + LookAndFeel.installColorsAndFont(list, "List.background", "List.foreground", "List.font"); + + LookAndFeel.installProperty(list, "opaque", Boolean.TRUE); + + if (list.getCellRenderer() == null) { + list.setCellRenderer((ListCellRenderer)(UIManager.get("List.cellRenderer"))); + } + + Color sbg = list.getSelectionBackground(); + if (sbg == null || sbg instanceof UIResource) { + list.setSelectionBackground(UIManager.getColor("List.selectionBackground")); + } + + Color sfg = list.getSelectionForeground(); + if (sfg == null || sfg instanceof UIResource) { + list.setSelectionForeground(UIManager.getColor("List.selectionForeground")); + } + + Long l = (Long)UIManager.get("List.timeFactor"); + timeFactor = (l!=null) ? l.longValue() : 1000L; + + updateIsFileList(); + } + + private void updateIsFileList() { + boolean b = Boolean.TRUE.equals(list.getClientProperty("List.isFileList")); + if (b != isFileList) { + isFileList = b; + Font oldFont = list.getFont(); + if (oldFont == null || oldFont instanceof UIResource) { + Font newFont = UIManager.getFont(b ? "FileChooser.listFont" : "List.font"); + if (newFont != null && newFont != oldFont) { + list.setFont(newFont); + } + } + } + } + + + /** + * Set the JList properties that haven't been explicitly overridden to + * null. A property is considered overridden if its current value + * is not a UIResource. + * + * @see #installDefaults + * @see #uninstallUI + * @see CellRendererPane + */ + protected void uninstallDefaults() + { + LookAndFeel.uninstallBorder(list); + if (list.getFont() instanceof UIResource) { + list.setFont(null); + } + if (list.getForeground() instanceof UIResource) { + list.setForeground(null); + } + if (list.getBackground() instanceof UIResource) { + list.setBackground(null); + } + if (list.getSelectionBackground() instanceof UIResource) { + list.setSelectionBackground(null); + } + if (list.getSelectionForeground() instanceof UIResource) { + list.setSelectionForeground(null); + } + if (list.getCellRenderer() instanceof UIResource) { + list.setCellRenderer(null); + } + if (list.getTransferHandler() instanceof UIResource) { + list.setTransferHandler(null); + } + } + + + /** + * Initializes this.list by calling installDefaults(), + * installListeners(), and installKeyboardActions() + * in order. + * + * @see #installDefaults + * @see #installListeners + * @see #installKeyboardActions + */ + public void installUI(JComponent c) + { + list = (JXList)c; + + layoutOrientation = list.getLayoutOrientation(); + + rendererPane = new CellRendererPane(); + list.add(rendererPane); + + columnCount = 1; + + updateLayoutStateNeeded = modelChanged; + isLeftToRight = list.getComponentOrientation().isLeftToRight(); + + installDefaults(); + installListeners(); + installKeyboardActions(); + installSortUI(); + } + + + /** + * Uninitializes this.list by calling uninstallListeners(), + * uninstallKeyboardActions(), and uninstallDefaults() + * in order. Sets this.list to null. + * + * @see #uninstallListeners + * @see #uninstallKeyboardActions + * @see #uninstallDefaults + */ + public void uninstallUI(JComponent c) + { + uninstallSortUI(); + uninstallListeners(); + uninstallDefaults(); + uninstallKeyboardActions(); + + cellWidth = cellHeight = -1; + cellHeights = null; + + listWidth = listHeight = -1; + + list.remove(rendererPane); + rendererPane = null; + list = null; + } + + + /** + * Returns a new instance of BasicXListUI. BasicXListUI delegates are + * allocated one per JList. + * + * @return A new ListUI implementation for the Windows look and feel. + */ + public static ComponentUI createUI(JComponent list) { + return new BasicXListUI(); + } + + + /** + * {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + */ + public int locationToIndex(JList list, Point location) { + maybeUpdateLayoutState(); + return convertLocationToModel(location.x, location.y); + } + + + /** + * {@inheritDoc} + */ + public Point indexToLocation(JList list, int index) { + maybeUpdateLayoutState(); + Rectangle rect = getCellBounds(list, index, index); + + if (rect != null) { + return new Point(rect.x, rect.y); + } + return null; + } + + + /** + * {@inheritDoc} + */ + public Rectangle getCellBounds(JList list, int index1, int index2) { + maybeUpdateLayoutState(); + + int minIndex = Math.min(index1, index2); + int maxIndex = Math.max(index1, index2); + + if (minIndex >= getElementCount()) { + return null; + } + + Rectangle minBounds = getCellBounds(list, minIndex); + + if (minBounds == null) { + return null; + } + if (minIndex == maxIndex) { + return minBounds; + } + Rectangle maxBounds = getCellBounds(list, maxIndex); + + if (maxBounds != null) { + if (layoutOrientation == JList.HORIZONTAL_WRAP) { + int minRow = convertModelToRow(minIndex); + int maxRow = convertModelToRow(maxIndex); + + if (minRow != maxRow) { + minBounds.x = 0; + minBounds.width = list.getWidth(); + } + } + else if (minBounds.x != maxBounds.x) { + // Different columns + minBounds.y = 0; + minBounds.height = list.getHeight(); + } + minBounds.add(maxBounds); + } + return minBounds; + } + + /** + * Gets the bounds of the specified model index, returning the resulting + * bounds, or null if index is not valid. + */ + private Rectangle getCellBounds(JList list, int index) { + maybeUpdateLayoutState(); + + int row = convertModelToRow(index); + int column = convertModelToColumn(index); + + if (row == -1 || column == -1) { + return null; + } + + Insets insets = list.getInsets(); + int x; + int w = cellWidth; + int y = insets.top; + int h; + switch (layoutOrientation) { + case JList.VERTICAL_WRAP: + case JList.HORIZONTAL_WRAP: + if (isLeftToRight) { + x = insets.left + column * cellWidth; + } else { + x = list.getWidth() - insets.right - (column+1) * cellWidth; + } + y += cellHeight * row; + h = cellHeight; + break; + default: + x = insets.left; + if (cellHeights == null) { + y += (cellHeight * row); + } + else if (row >= cellHeights.length) { + y = 0; + } + else { + for(int i = 0; i < row; i++) { + y += cellHeights[i]; + } + } + w = list.getWidth() - (insets.left + insets.right); + h = getRowHeight(index); + break; + } + return new Rectangle(x, y, w, h); + } + + /** + * Returns the height of the specified row based on the current layout. + * + * @return The specified row height or -1 if row isn't valid. + * @see #convertYToRow + * @see #convertRowToY + * @see #updateLayoutState + */ + protected int getRowHeight(int row) + { + return getHeight(0, row); + } + + + /** + * Convert the JList relative coordinate to the row that contains it, + * based on the current layout. If y0 doesn't fall within any row, + * return -1. + * + * @return The row that contains y0, or -1. + * @see #getRowHeight + * @see #updateLayoutState + */ + protected int convertYToRow(int y0) + { + return convertLocationToRow(0, y0, false); + } + + + /** + * Return the JList relative Y coordinate of the origin of the specified + * row or -1 if row isn't valid. + * + * @return The Y coordinate of the origin of row, or -1. + * @see #getRowHeight + * @see #updateLayoutState + */ + protected int convertRowToY(int row) + { + if (row >= getRowCount(0) || row < 0) { + return -1; + } + Rectangle bounds = getCellBounds(list, row, row); + return bounds.y; + } + + /** + * Returns the height of the cell at the passed in location. + */ + private int getHeight(int column, int row) { + if (column < 0 || column > columnCount || row < 0) { + return -1; + } + if (layoutOrientation != JList.VERTICAL) { + return cellHeight; + } + if (row >= getElementCount()) { + return -1; + } + return (cellHeights == null) ? cellHeight : + ((row < cellHeights.length) ? cellHeights[row] : -1); + } + + /** + * Returns the row at location x/y. + * + * @param closest If true and the location doesn't exactly match a + * particular location, this will return the closest row. + */ + private int convertLocationToRow(int x, int y0, boolean closest) { + int size = getElementCount(); + + if (size <= 0) { + return -1; + } + Insets insets = list.getInsets(); + if (cellHeights == null) { + int row = (cellHeight == 0) ? 0 : + ((y0 - insets.top) / cellHeight); + if (closest) { + if (row < 0) { + row = 0; + } + else if (row >= size) { + row = size - 1; + } + } + return row; + } + else if (size > cellHeights.length) { + return -1; + } + else { + int y = insets.top; + int row = 0; + + if (closest && y0 < y) { + return 0; + } + int i; + for (i = 0; i < size; i++) { + if ((y0 >= y) && (y0 < y + cellHeights[i])) { + return row; + } + y += cellHeights[i]; + row += 1; + } + return i - 1; + } + } + + /** + * Returns the closest row that starts at the specified y-location + * in the passed in column. + */ + private int convertLocationToRowInColumn(int y, int column) { + int x = 0; + + if (layoutOrientation != JList.VERTICAL) { + if (isLeftToRight) { + x = column * cellWidth; + } else { + x = list.getWidth() - (column+1)*cellWidth - list.getInsets().right; + } + } + return convertLocationToRow(x, y, true); + } + + /** + * Returns the closest location to the model index of the passed in + * location. + */ + private int convertLocationToModel(int x, int y) { + int row = convertLocationToRow(x, y, true); + int column = convertLocationToColumn(x, y); + + if (row >= 0 && column >= 0) { + return getModelIndex(column, row); + } + return -1; + } + + /** + * Returns the number of rows in the given column. + */ + private int getRowCount(int column) { + if (column < 0 || column >= columnCount) { + return -1; + } + if (layoutOrientation == JList.VERTICAL || + (column == 0 && columnCount == 1)) { + return getElementCount(); + } + if (column >= columnCount) { + return -1; + } + if (layoutOrientation == JList.VERTICAL_WRAP) { + if (column < (columnCount - 1)) { + return rowsPerColumn; + } + return getElementCount() - (columnCount - 1) * + rowsPerColumn; + } + // JList.HORIZONTAL_WRAP + int diff = columnCount - (columnCount * rowsPerColumn - + getElementCount()); + + if (column >= diff) { + return Math.max(0, rowsPerColumn - 1); + } + return rowsPerColumn; + } + + /** + * Returns the model index for the specified display location. + * If columnxrow is beyond the length of the + * model, this will return the model size - 1. + */ + private int getModelIndex(int column, int row) { + switch (layoutOrientation) { + case JList.VERTICAL_WRAP: + return Math.min(getElementCount() - 1, rowsPerColumn * + column + Math.min(row, rowsPerColumn-1)); + case JList.HORIZONTAL_WRAP: + return Math.min(getElementCount() - 1, row * columnCount + + column); + default: + return row; + } + } + + /** + * Returns the closest column to the passed in location. + */ + private int convertLocationToColumn(int x, int y) { + if (cellWidth > 0) { + if (layoutOrientation == JList.VERTICAL) { + return 0; + } + Insets insets = list.getInsets(); + int col; + if (isLeftToRight) { + col = (x - insets.left) / cellWidth; + } else { + col = (list.getWidth() - x - insets.right - 1) / cellWidth; + } + if (col < 0) { + return 0; + } + else if (col >= columnCount) { + return columnCount - 1; + } + return col; + } + return 0; + } + + /** + * Returns the row that the model index index will be + * displayed in.. + */ + private int convertModelToRow(int index) { + int size = getElementCount(); + + if ((index < 0) || (index >= size)) { + return -1; + } + + if (layoutOrientation != JList.VERTICAL && columnCount > 1 && + rowsPerColumn > 0) { + if (layoutOrientation == JList.VERTICAL_WRAP) { + return index % rowsPerColumn; + } + return index / columnCount; + } + return index; + } + + /** + * Returns the column that the model index index will be + * displayed in. + */ + private int convertModelToColumn(int index) { + int size = getElementCount(); + + if ((index < 0) || (index >= size)) { + return -1; + } + + if (layoutOrientation != JList.VERTICAL && rowsPerColumn > 0 && + columnCount > 1) { + if (layoutOrientation == JList.VERTICAL_WRAP) { + return index / rowsPerColumn; + } + return index % columnCount; + } + return 0; + } + + /** + * If updateLayoutStateNeeded is non zero, call updateLayoutState() and reset + * updateLayoutStateNeeded. This method should be called by methods + * before doing any computation based on the geometry of the list. + * For example it's the first call in paint() and getPreferredSize(). + * + * @see #updateLayoutState + */ + protected void maybeUpdateLayoutState() + { + if (updateLayoutStateNeeded != 0) { + updateLayoutState(); + updateLayoutStateNeeded = 0; + } + } + + + /** + * Recompute the value of cellHeight or cellHeights based + * and cellWidth, based on the current font and the current + * values of fixedCellWidth, fixedCellHeight, and prototypeCellValue. + * + * @see #maybeUpdateLayoutState + */ + protected void updateLayoutState() + { + /* If both JList fixedCellWidth and fixedCellHeight have been + * set, then initialize cellWidth and cellHeight, and set + * cellHeights to null. + */ + + int fixedCellHeight = list.getFixedCellHeight(); + int fixedCellWidth = list.getFixedCellWidth(); + + cellWidth = (fixedCellWidth != -1) ? fixedCellWidth : -1; + + if (fixedCellHeight != -1) { + cellHeight = fixedCellHeight; + cellHeights = null; + } + else { + cellHeight = -1; + cellHeights = new int[getElementCount()]; + } + + /* If either of JList fixedCellWidth and fixedCellHeight haven't + * been set, then initialize cellWidth and cellHeights by + * scanning through the entire model. Note: if the renderer is + * null, we just set cellWidth and cellHeights[*] to zero, + * if they're not set already. + */ + + if ((fixedCellWidth == -1) || (fixedCellHeight == -1)) { + + ListModel dataModel = getViewModel(); + int dataModelSize = dataModel.getSize(); + ListCellRenderer renderer = list.getCellRenderer(); + + if (renderer != null) { + for(int index = 0; index < dataModelSize; index++) { + Object value = dataModel.getElementAt(index); + Component c = renderer.getListCellRendererComponent(list, value, index, false, false); + rendererPane.add(c); + Dimension cellSize = c.getPreferredSize(); + if (fixedCellWidth == -1) { + cellWidth = Math.max(cellSize.width, cellWidth); + } + if (fixedCellHeight == -1) { + cellHeights[index] = cellSize.height; + } + } + } + else { + if (cellWidth == -1) { + cellWidth = 0; + } + if (cellHeights == null) { + cellHeights = new int[dataModelSize]; + } + for(int index = 0; index < dataModelSize; index++) { + cellHeights[index] = 0; + } + } + } + + columnCount = 1; + if (layoutOrientation != JList.VERTICAL) { + updateHorizontalLayoutState(fixedCellWidth, fixedCellHeight); + } + } + + /** + * Invoked when the list is layed out horizontally to determine how + * many columns to create. + *

      + * This updates the rowsPerColumn, columnCount, + * preferredHeight and potentially cellHeight + * instance variables. + */ + private void updateHorizontalLayoutState(int fixedCellWidth, + int fixedCellHeight) { + int visRows = list.getVisibleRowCount(); + int dataModelSize = getElementCount(); + Insets insets = list.getInsets(); + + listHeight = list.getHeight(); + listWidth = list.getWidth(); + + if (dataModelSize == 0) { + rowsPerColumn = columnCount = 0; + preferredHeight = insets.top + insets.bottom; + return; + } + + int height; + + if (fixedCellHeight != -1) { + height = fixedCellHeight; + } + else { + // Determine the max of the renderer heights. + int maxHeight = 0; + if (cellHeights.length > 0) { + maxHeight = cellHeights[cellHeights.length - 1]; + for (int counter = cellHeights.length - 2; + counter >= 0; counter--) { + maxHeight = Math.max(maxHeight, cellHeights[counter]); + } + } + height = cellHeight = maxHeight; + cellHeights = null; + } + // The number of rows is either determined by the visible row + // count, or by the height of the list. + rowsPerColumn = dataModelSize; + if (visRows > 0) { + rowsPerColumn = visRows; + columnCount = Math.max(1, dataModelSize / rowsPerColumn); + if (dataModelSize > 0 && dataModelSize > rowsPerColumn && + dataModelSize % rowsPerColumn != 0) { + columnCount++; + } + if (layoutOrientation == JList.HORIZONTAL_WRAP) { + // Because HORIZONTAL_WRAP flows differently, the + // rowsPerColumn needs to be adjusted. + rowsPerColumn = (dataModelSize / columnCount); + if (dataModelSize % columnCount > 0) { + rowsPerColumn++; + } + } + } + else if (layoutOrientation == JList.VERTICAL_WRAP && height != 0) { + rowsPerColumn = Math.max(1, (listHeight - insets.top - + insets.bottom) / height); + columnCount = Math.max(1, dataModelSize / rowsPerColumn); + if (dataModelSize > 0 && dataModelSize > rowsPerColumn && + dataModelSize % rowsPerColumn != 0) { + columnCount++; + } + } + else if (layoutOrientation == JList.HORIZONTAL_WRAP && cellWidth > 0 && + listWidth > 0) { + columnCount = Math.max(1, (listWidth - insets.left - + insets.right) / cellWidth); + rowsPerColumn = dataModelSize / columnCount; + if (dataModelSize % columnCount > 0) { + rowsPerColumn++; + } + } + preferredHeight = rowsPerColumn * cellHeight + insets.top + + insets.bottom; + } + + private Handler getHandler() { + if (handler == null) { + handler = new Handler(); + } + return handler; + } + + /** + * Mouse input, and focus handling for JList. An instance of this + * class is added to the appropriate java.awt.Component lists + * at installUI() time. Note keyboard input is handled with JComponent + * KeyboardActions, see installKeyboardActions(). + *

      + * Warning: + * Serialized objects of this class will not be compatible with + * future Swing releases. The current serialization support is + * appropriate for short term storage or RMI between applications running + * the same version of Swing. As of 1.4, support for long term storage + * of all JavaBeansTM + * has been added to the java.beans package. + * Please see {@link java.beans.XMLEncoder}. + * + * @see #createMouseInputListener + * @see #installKeyboardActions + * @see #installUI + */ + public class MouseInputHandler implements MouseInputListener + { + public void mouseClicked(MouseEvent e) { + getHandler().mouseClicked(e); + } + + public void mouseEntered(MouseEvent e) { + getHandler().mouseEntered(e); + } + + public void mouseExited(MouseEvent e) { + getHandler().mouseExited(e); + } + + public void mousePressed(MouseEvent e) { + getHandler().mousePressed(e); + } + + public void mouseDragged(MouseEvent e) { + getHandler().mouseDragged(e); + } + + public void mouseMoved(MouseEvent e) { + getHandler().mouseMoved(e); + } + + public void mouseReleased(MouseEvent e) { + getHandler().mouseReleased(e); + } + } + + + /** + * Creates a delegate that implements MouseInputListener. + * The delegate is added to the corresponding java.awt.Component listener + * lists at installUI() time. Subclasses can override this method to return + * a custom MouseInputListener, e.g. + *

      +     * class MyListUI extends BasicXListUI {
      +     *    protected MouseInputListener createMouseInputListener() {
      +     *        return new MyMouseInputHandler();
      +     *    }
      +     *    public class MyMouseInputHandler extends MouseInputHandler {
      +     *        public void mouseMoved(MouseEvent e) {
      +     *            // do some extra work when the mouse moves
      +     *            super.mouseMoved(e);
      +     *        }
      +     *    }
      +     * }
      +     * 
      + * + * @see MouseInputHandler + * @see #installUI + */ + protected MouseInputListener createMouseInputListener() { + return getHandler(); + } + + /** + * This inner class is marked "public" due to a compiler bug. + * This class should be treated as a "protected" inner class. + * Instantiate it only within subclasses of BasicTableUI. + */ + public class FocusHandler implements FocusListener + { + protected void repaintCellFocus() + { + getHandler().repaintCellFocus(); + } + + /* The focusGained() focusLost() methods run when the JList + * focus changes. + */ + + public void focusGained(FocusEvent e) { + getHandler().focusGained(e); + } + + public void focusLost(FocusEvent e) { + getHandler().focusLost(e); + } + } + + protected FocusListener createFocusListener() { + return getHandler(); + } + + /** + * The ListSelectionListener that's added to the JLists selection + * model at installUI time, and whenever the JList.selectionModel property + * changes. When the selection changes we repaint the affected rows. + *

      + * Warning: + * Serialized objects of this class will not be compatible with + * future Swing releases. The current serialization support is + * appropriate for short term storage or RMI between applications running + * the same version of Swing. As of 1.4, support for long term storage + * of all JavaBeansTM + * has been added to the java.beans package. + * Please see {@link java.beans.XMLEncoder}. + * + * @see #createListSelectionListener + * @see #getCellBounds + * @see #installUI + */ + public class ListSelectionHandler implements ListSelectionListener + { + public void valueChanged(ListSelectionEvent e) + { + if (processedBySortUI(e)) return; + getHandler().valueChanged(e); + } + } + + + /** + * Creates an instance of ListSelectionHandler that's added to + * the JLists by selectionModel as needed. Subclasses can override + * this method to return a custom ListSelectionListener, e.g. + *

      +     * class MyListUI extends BasicXListUI {
      +     *    protected ListSelectionListener createListSelectionListener() {
      +     *        return new MySelectionListener();
      +     *    }
      +     *    public class MySelectionListener extends ListSelectionHandler {
      +     *        public void valueChanged(ListSelectionEvent e) {
      +     *            // do some extra work when the selection changes
      +     *            super.valueChange(e);
      +     *        }
      +     *    }
      +     * }
      +     * 
      + * + * @see ListSelectionHandler + * @see #installUI + */ + protected ListSelectionListener createListSelectionListener() { + return new ListSelectionHandler(); + } + + + private void redrawList() { + list.revalidate(); + list.repaint(); + } + + + /** + * The ListDataListener that's added to the JLists model at + * installUI time, and whenever the JList.model property changes. + *

      + * Warning: + * Serialized objects of this class will not be compatible with + * future Swing releases. The current serialization support is + * appropriate for short term storage or RMI between applications running + * the same version of Swing. As of 1.4, support for long term storage + * of all JavaBeansTM + * has been added to the java.beans package. + * Please see {@link java.beans.XMLEncoder}. + * + * @see JList#getModel + * @see #maybeUpdateLayoutState + * @see #createListDataListener + * @see #installUI + */ + public class ListDataHandler implements ListDataListener + { + public void intervalAdded(ListDataEvent e) { + if (processedBySortUI(e)) + return; + getHandler().intervalAdded(e); + } + + + public void intervalRemoved(ListDataEvent e) { + if (processedBySortUI(e)) + return; + getHandler().intervalRemoved(e); + } + + + public void contentsChanged(ListDataEvent e) { + if (processedBySortUI(e)) + return; + getHandler().contentsChanged(e); + } + } + + + /** + * Creates an instance of ListDataListener that's added to + * the JLists by model as needed. Subclasses can override + * this method to return a custom ListDataListener, e.g. + *

      +     * class MyListUI extends BasicXListUI {
      +     *    protected ListDataListener createListDataListener() {
      +     *        return new MyListDataListener();
      +     *    }
      +     *    public class MyListDataListener extends ListDataHandler {
      +     *        public void contentsChanged(ListDataEvent e) {
      +     *            // do some extra work when the models contents change
      +     *            super.contentsChange(e);
      +     *        }
      +     *    }
      +     * }
      +     * 
      + * + * @see ListDataListener + * @see JList#getModel + * @see #installUI + */ + protected ListDataListener createListDataListener() { + return new ListDataHandler(); + } + + + /** + * The PropertyChangeListener that's added to the JList at + * installUI time. When the value of a JList property that + * affects layout changes, we set a bit in updateLayoutStateNeeded. + * If the JLists model changes we additionally remove our listeners + * from the old model. Likewise for the JList selectionModel. + *

      + * Warning: + * Serialized objects of this class will not be compatible with + * future Swing releases. The current serialization support is + * appropriate for short term storage or RMI between applications running + * the same version of Swing. As of 1.4, support for long term storage + * of all JavaBeansTM + * has been added to the java.beans package. + * Please see {@link java.beans.XMLEncoder}. + * + * @see #maybeUpdateLayoutState + * @see #createPropertyChangeListener + * @see #installUI + */ + public class PropertyChangeHandler implements PropertyChangeListener + { + public void propertyChange(PropertyChangeEvent e) { + getHandler().propertyChange(e); + updateSortUI(e.getPropertyName()); + } + } + + /** + * Creates an instance of PropertyChangeHandler that's added to + * the JList by installUI(). Subclasses can override this method + * to return a custom PropertyChangeListener, e.g. + *

      +     * class MyListUI extends BasicXListUI {
      +     *    protected PropertyChangeListener createPropertyChangeListener() {
      +     *        return new MyPropertyChangeListener();
      +     *    }
      +     *    public class MyPropertyChangeListener extends PropertyChangeHandler {
      +     *        public void propertyChange(PropertyChangeEvent e) {
      +     *            if (e.getPropertyName().equals("model")) {
      +     *                // do some extra work when the model changes
      +     *            }
      +     *            super.propertyChange(e);
      +     *        }
      +     *    }
      +     * }
      +     * 
      + * + * @see PropertyChangeListener + * @see #installUI + */ + protected PropertyChangeListener createPropertyChangeListener() { + return new PropertyChangeHandler(); + } + + /** Used by IncrementLeadSelectionAction. Indicates the action should + * change the lead, and not select it. */ + private static final int CHANGE_LEAD = 0; + /** Used by IncrementLeadSelectionAction. Indicates the action should + * change the selection and lead. */ + private static final int CHANGE_SELECTION = 1; + /** Used by IncrementLeadSelectionAction. Indicates the action should + * extend the selection from the anchor to the next index. */ + private static final int EXTEND_SELECTION = 2; + + // PENDING JW: this is not a complete replacement of sun.UIAction ... + private static class Actions extends UIAction { + private static final String SELECT_PREVIOUS_COLUMN = + "selectPreviousColumn"; + private static final String SELECT_PREVIOUS_COLUMN_EXTEND = + "selectPreviousColumnExtendSelection"; + private static final String SELECT_PREVIOUS_COLUMN_CHANGE_LEAD = + "selectPreviousColumnChangeLead"; + private static final String SELECT_NEXT_COLUMN = "selectNextColumn"; + private static final String SELECT_NEXT_COLUMN_EXTEND = + "selectNextColumnExtendSelection"; + private static final String SELECT_NEXT_COLUMN_CHANGE_LEAD = + "selectNextColumnChangeLead"; + private static final String SELECT_PREVIOUS_ROW = "selectPreviousRow"; + private static final String SELECT_PREVIOUS_ROW_EXTEND = + "selectPreviousRowExtendSelection"; + private static final String SELECT_PREVIOUS_ROW_CHANGE_LEAD = + "selectPreviousRowChangeLead"; + private static final String SELECT_NEXT_ROW = "selectNextRow"; + private static final String SELECT_NEXT_ROW_EXTEND = + "selectNextRowExtendSelection"; + private static final String SELECT_NEXT_ROW_CHANGE_LEAD = + "selectNextRowChangeLead"; + private static final String SELECT_FIRST_ROW = "selectFirstRow"; + private static final String SELECT_FIRST_ROW_EXTEND = + "selectFirstRowExtendSelection"; + private static final String SELECT_FIRST_ROW_CHANGE_LEAD = + "selectFirstRowChangeLead"; + private static final String SELECT_LAST_ROW = "selectLastRow"; + private static final String SELECT_LAST_ROW_EXTEND = + "selectLastRowExtendSelection"; + private static final String SELECT_LAST_ROW_CHANGE_LEAD = + "selectLastRowChangeLead"; + private static final String SCROLL_UP = "scrollUp"; + private static final String SCROLL_UP_EXTEND = + "scrollUpExtendSelection"; + private static final String SCROLL_UP_CHANGE_LEAD = + "scrollUpChangeLead"; + private static final String SCROLL_DOWN = "scrollDown"; + private static final String SCROLL_DOWN_EXTEND = + "scrollDownExtendSelection"; + private static final String SCROLL_DOWN_CHANGE_LEAD = + "scrollDownChangeLead"; + private static final String SELECT_ALL = "selectAll"; + private static final String CLEAR_SELECTION = "clearSelection"; + + // add the lead item to the selection without changing lead or anchor + private static final String ADD_TO_SELECTION = "addToSelection"; + + // toggle the selected state of the lead item and move the anchor to it + private static final String TOGGLE_AND_ANCHOR = "toggleAndAnchor"; + + // extend the selection to the lead item + private static final String EXTEND_TO = "extendTo"; + + // move the anchor to the lead and ensure only that item is selected + private static final String MOVE_SELECTION_TO = "moveSelectionTo"; + + Actions(String name) { + super(name); + } + public void actionPerformed(ActionEvent e) { + String name = getName(); + JList list = (JList)e.getSource(); + BasicXListUI ui = (BasicXListUI)LookAndFeelUtils.getUIOfType( + list.getUI(), BasicXListUI.class); + + if (name == SELECT_PREVIOUS_COLUMN) { + changeSelection(list, CHANGE_SELECTION, + getNextColumnIndex(list, ui, -1), -1); + } + else if (name == SELECT_PREVIOUS_COLUMN_EXTEND) { + changeSelection(list, EXTEND_SELECTION, + getNextColumnIndex(list, ui, -1), -1); + } + else if (name == SELECT_PREVIOUS_COLUMN_CHANGE_LEAD) { + changeSelection(list, CHANGE_LEAD, + getNextColumnIndex(list, ui, -1), -1); + } + else if (name == SELECT_NEXT_COLUMN) { + changeSelection(list, CHANGE_SELECTION, + getNextColumnIndex(list, ui, 1), 1); + } + else if (name == SELECT_NEXT_COLUMN_EXTEND) { + changeSelection(list, EXTEND_SELECTION, + getNextColumnIndex(list, ui, 1), 1); + } + else if (name == SELECT_NEXT_COLUMN_CHANGE_LEAD) { + changeSelection(list, CHANGE_LEAD, + getNextColumnIndex(list, ui, 1), 1); + } + else if (name == SELECT_PREVIOUS_ROW) { + changeSelection(list, CHANGE_SELECTION, + getNextIndex(list, ui, -1), -1); + } + else if (name == SELECT_PREVIOUS_ROW_EXTEND) { + changeSelection(list, EXTEND_SELECTION, + getNextIndex(list, ui, -1), -1); + } + else if (name == SELECT_PREVIOUS_ROW_CHANGE_LEAD) { + changeSelection(list, CHANGE_LEAD, + getNextIndex(list, ui, -1), -1); + } + else if (name == SELECT_NEXT_ROW) { + changeSelection(list, CHANGE_SELECTION, + getNextIndex(list, ui, 1), 1); + } + else if (name == SELECT_NEXT_ROW_EXTEND) { + changeSelection(list, EXTEND_SELECTION, + getNextIndex(list, ui, 1), 1); + } + else if (name == SELECT_NEXT_ROW_CHANGE_LEAD) { + changeSelection(list, CHANGE_LEAD, + getNextIndex(list, ui, 1), 1); + } + else if (name == SELECT_FIRST_ROW) { + changeSelection(list, CHANGE_SELECTION, 0, -1); + } + else if (name == SELECT_FIRST_ROW_EXTEND) { + changeSelection(list, EXTEND_SELECTION, 0, -1); + } + else if (name == SELECT_FIRST_ROW_CHANGE_LEAD) { + changeSelection(list, CHANGE_LEAD, 0, -1); + } + else if (name == SELECT_LAST_ROW) { + changeSelection(list, CHANGE_SELECTION, + getElementCount(list) - 1, 1); + } + else if (name == SELECT_LAST_ROW_EXTEND) { + changeSelection(list, EXTEND_SELECTION, + getElementCount(list) - 1, 1); + } + else if (name == SELECT_LAST_ROW_CHANGE_LEAD) { + changeSelection(list, CHANGE_LEAD, + getElementCount(list) - 1, 1); + } + else if (name == SCROLL_UP) { + changeSelection(list, CHANGE_SELECTION, + getNextPageIndex(list, -1), -1); + } + else if (name == SCROLL_UP_EXTEND) { + changeSelection(list, EXTEND_SELECTION, + getNextPageIndex(list, -1), -1); + } + else if (name == SCROLL_UP_CHANGE_LEAD) { + changeSelection(list, CHANGE_LEAD, + getNextPageIndex(list, -1), -1); + } + else if (name == SCROLL_DOWN) { + changeSelection(list, CHANGE_SELECTION, + getNextPageIndex(list, 1), 1); + } + else if (name == SCROLL_DOWN_EXTEND) { + changeSelection(list, EXTEND_SELECTION, + getNextPageIndex(list, 1), 1); + } + else if (name == SCROLL_DOWN_CHANGE_LEAD) { + changeSelection(list, CHANGE_LEAD, + getNextPageIndex(list, 1), 1); + } + else if (name == SELECT_ALL) { + selectAll(list); + } + else if (name == CLEAR_SELECTION) { + clearSelection(list); + } + else if (name == ADD_TO_SELECTION) { + int index = adjustIndex( + list.getSelectionModel().getLeadSelectionIndex(), list); + + if (!list.isSelectedIndex(index)) { + int oldAnchor = list.getSelectionModel().getAnchorSelectionIndex(); + list.setValueIsAdjusting(true); + list.addSelectionInterval(index, index); + list.getSelectionModel().setAnchorSelectionIndex(oldAnchor); + list.setValueIsAdjusting(false); + } + } + else if (name == TOGGLE_AND_ANCHOR) { + int index = adjustIndex( + list.getSelectionModel().getLeadSelectionIndex(), list); + + if (list.isSelectedIndex(index)) { + list.removeSelectionInterval(index, index); + } else { + list.addSelectionInterval(index, index); + } + } + else if (name == EXTEND_TO) { + changeSelection( + list, EXTEND_SELECTION, + adjustIndex(list.getSelectionModel().getLeadSelectionIndex(), list), + 0); + } + else if (name == MOVE_SELECTION_TO) { + changeSelection( + list, CHANGE_SELECTION, + adjustIndex(list.getSelectionModel().getLeadSelectionIndex(), list), + 0); + } + } + /** + * @param list + * @return + */ + private int getElementCount(JList list) { + return ((JXList) list).getElementCount(); + } + + public boolean isEnabled(Object c) { + Object name = getName(); + if (name == SELECT_PREVIOUS_COLUMN_CHANGE_LEAD || + name == SELECT_NEXT_COLUMN_CHANGE_LEAD || + name == SELECT_PREVIOUS_ROW_CHANGE_LEAD || + name == SELECT_NEXT_ROW_CHANGE_LEAD || + name == SELECT_FIRST_ROW_CHANGE_LEAD || + name == SELECT_LAST_ROW_CHANGE_LEAD || + name == SCROLL_UP_CHANGE_LEAD || + name == SCROLL_DOWN_CHANGE_LEAD) { + + // discontinuous selection actions are only enabled for + // DefaultListSelectionModel + return c != null && ((JList)c).getSelectionModel() + instanceof DefaultListSelectionModel; + } + + return true; + } + + private void clearSelection(JList list) { + list.clearSelection(); + } + + private void selectAll(JList list) { + int size = getElementCount(list); + if (size > 0) { + ListSelectionModel lsm = list.getSelectionModel(); + int lead = adjustIndex(lsm.getLeadSelectionIndex(), list); + + if (lsm.getSelectionMode() == ListSelectionModel.SINGLE_SELECTION) { + if (lead == -1) { + int min = adjustIndex(list.getMinSelectionIndex(), list); + lead = (min == -1 ? 0 : min); + } + + list.setSelectionInterval(lead, lead); + list.ensureIndexIsVisible(lead); + } else { + list.setValueIsAdjusting(true); + + int anchor = adjustIndex(lsm.getAnchorSelectionIndex(), list); + + list.setSelectionInterval(0, size - 1); + + // this is done to restore the anchor and lead + SwingXUtilities.setLeadAnchorWithoutSelection(lsm, anchor, lead); + + list.setValueIsAdjusting(false); + } + } + } + + private int getNextPageIndex(JList list, int direction) { + if (getElementCount(list) == 0) { + return -1; + } + + int index = -1; + Rectangle visRect = list.getVisibleRect(); + ListSelectionModel lsm = list.getSelectionModel(); + int lead = adjustIndex(lsm.getLeadSelectionIndex(), list); + Rectangle leadRect = + (lead==-1) ? new Rectangle() : list.getCellBounds(lead, lead); + + if (list.getLayoutOrientation() == JList.VERTICAL_WRAP && + list.getVisibleRowCount() <= 0) { + if (!list.getComponentOrientation().isLeftToRight()) { + direction = -direction; + } + // apply for horizontal scrolling: the step for next + // page index is number of visible columns + if (direction < 0) { + // left + visRect.x = leadRect.x + leadRect.width - visRect.width; + Point p = new Point(visRect.x - 1, leadRect.y); + index = list.locationToIndex(p); + Rectangle cellBounds = list.getCellBounds(index, index); + if (visRect.intersects(cellBounds)) { + p.x = cellBounds.x - 1; + index = list.locationToIndex(p); + cellBounds = list.getCellBounds(index, index); + } + // this is necessary for right-to-left orientation only + if (cellBounds.y != leadRect.y) { + p.x = cellBounds.x + cellBounds.width; + index = list.locationToIndex(p); + } + } + else { + // right + visRect.x = leadRect.x; + Point p = new Point(visRect.x + visRect.width, leadRect.y); + index = list.locationToIndex(p); + Rectangle cellBounds = list.getCellBounds(index, index); + if (visRect.intersects(cellBounds)) { + p.x = cellBounds.x + cellBounds.width; + index = list.locationToIndex(p); + cellBounds = list.getCellBounds(index, index); + } + if (cellBounds.y != leadRect.y) { + p.x = cellBounds.x - 1; + index = list.locationToIndex(p); + } + } + } + else { + if (direction < 0) { + // up + // go to the first visible cell + Point p = new Point(leadRect.x, visRect.y); + index = list.locationToIndex(p); + if (lead <= index) { + // if lead is the first visible cell (or above it) + // adjust the visible rect up + visRect.y = leadRect.y + leadRect.height - visRect.height; + p.y = visRect.y; + index = list.locationToIndex(p); + Rectangle cellBounds = list.getCellBounds(index, index); + // go one cell down if first visible cell doesn't fit + // into adjasted visible rectangle + if (cellBounds.y < visRect.y) { + p.y = cellBounds.y + cellBounds.height; + index = list.locationToIndex(p); + cellBounds = list.getCellBounds(index, index); + } + // if index isn't less then lead + // try to go to cell previous to lead + if (cellBounds.y >= leadRect.y) { + p.y = leadRect.y - 1; + index = list.locationToIndex(p); + } + } + } + else { + // down + // go to the last completely visible cell + Point p = new Point(leadRect.x, + visRect.y + visRect.height - 1); + index = list.locationToIndex(p); + Rectangle cellBounds = list.getCellBounds(index, index); + // go up one cell if last visible cell doesn't fit + // into visible rectangle + if (cellBounds.y + cellBounds.height > + visRect.y + visRect.height) { + p.y = cellBounds.y - 1; + index = list.locationToIndex(p); + cellBounds = list.getCellBounds(index, index); + index = Math.max(index, lead); + } + + if (lead >= index) { + // if lead is the last completely visible index + // (or below it) adjust the visible rect down + visRect.y = leadRect.y; + p.y = visRect.y + visRect.height - 1; + index = list.locationToIndex(p); + cellBounds = list.getCellBounds(index, index); + // go one cell up if last visible cell doesn't fit + // into adjasted visible rectangle + if (cellBounds.y + cellBounds.height > + visRect.y + visRect.height) { + p.y = cellBounds.y - 1; + index = list.locationToIndex(p); + cellBounds = list.getCellBounds(index, index); + } + // if index isn't greater then lead + // try to go to cell next after lead + if (cellBounds.y <= leadRect.y) { + p.y = leadRect.y + leadRect.height; + index = list.locationToIndex(p); + } + } + } + } + return index; + } + + private void changeSelection(JList list, int type, + int index, int direction) { + if (index >= 0 && index < getElementCount(list)) { + ListSelectionModel lsm = list.getSelectionModel(); + + // CHANGE_LEAD is only valid with multiple interval selection + if (type == CHANGE_LEAD && + list.getSelectionMode() + != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION) { + + type = CHANGE_SELECTION; + } + + // IMPORTANT - This needs to happen before the index is changed. + // This is because JFileChooser, which uses JList, also scrolls + // the selected item into view. If that happens first, then + // this method becomes a no-op. + adjustScrollPositionIfNecessary(list, index, direction); + + if (type == EXTEND_SELECTION) { + int anchor = adjustIndex(lsm.getAnchorSelectionIndex(), list); + if (anchor == -1) { + anchor = 0; + } + + list.setSelectionInterval(anchor, index); + } + else if (type == CHANGE_SELECTION) { + list.setSelectedIndex(index); + } + else { + // casting should be safe since the action is only enabled + // for DefaultListSelectionModel + if (lsm instanceof DefaultListSelectionModel) + ((DefaultListSelectionModel)lsm).moveLeadSelectionIndex(index); + } + } + } + + /** + * When scroll down makes selected index the last completely visible + * index. When scroll up makes selected index the first visible index. + * Adjust visible rectangle respect to list's component orientation. + */ + private void adjustScrollPositionIfNecessary(JList list, int index, + int direction) { + if (direction == 0) { + return; + } + Rectangle cellBounds = list.getCellBounds(index, index); + Rectangle visRect = list.getVisibleRect(); + if (cellBounds != null && !visRect.contains(cellBounds)) { + if (list.getLayoutOrientation() == JList.VERTICAL_WRAP && + list.getVisibleRowCount() <= 0) { + // horizontal + if (list.getComponentOrientation().isLeftToRight()) { + if (direction > 0) { + // right for left-to-right + int x =Math.max(0, + cellBounds.x + cellBounds.width - visRect.width); + int startIndex = + list.locationToIndex(new Point(x, cellBounds.y)); + Rectangle startRect = list.getCellBounds(startIndex, + startIndex); + if (startRect.x < x && startRect.x < cellBounds.x) { + startRect.x += startRect.width; + startIndex = + list.locationToIndex(startRect.getLocation()); + startRect = list.getCellBounds(startIndex, + startIndex); + } + cellBounds = startRect; + } + cellBounds.width = visRect.width; + } + else { + if (direction > 0) { + // left for right-to-left + int x = cellBounds.x + visRect.width; + int rightIndex = + list.locationToIndex(new Point(x, cellBounds.y)); + Rectangle rightRect = list.getCellBounds(rightIndex, + rightIndex); + if (rightRect.x + rightRect.width > x && + rightRect.x > cellBounds.x) { + rightRect.width = 0; + } + cellBounds.x = Math.max(0, + rightRect.x + rightRect.width - visRect.width); + cellBounds.width = visRect.width; + } + else { + cellBounds.x += Math.max(0, + cellBounds.width - visRect.width); + // adjust width to fit into visible rectangle + cellBounds.width = Math.min(cellBounds.width, + visRect.width); + } + } + } + else { + // vertical + if (direction > 0 && + (cellBounds.y < visRect.y || + cellBounds.y + cellBounds.height + > visRect.y + visRect.height)) { + //down + int y = Math.max(0, + cellBounds.y + cellBounds.height - visRect.height); + int startIndex = + list.locationToIndex(new Point(cellBounds.x, y)); + Rectangle startRect = list.getCellBounds(startIndex, + startIndex); + if (startRect.y < y && startRect.y < cellBounds.y) { + startRect.y += startRect.height; + startIndex = + list.locationToIndex(startRect.getLocation()); + startRect = + list.getCellBounds(startIndex, startIndex); + } + cellBounds = startRect; + cellBounds.height = visRect.height; + } + else { + // adjust height to fit into visible rectangle + cellBounds.height = Math.min(cellBounds.height, visRect.height); + } + } + list.scrollRectToVisible(cellBounds); + } + } + + private int getNextColumnIndex(JList list, BasicXListUI ui, + int amount) { + if (list.getLayoutOrientation() != JList.VERTICAL) { + int index = adjustIndex(list.getLeadSelectionIndex(), list); + int size = getElementCount(list); + + if (index == -1) { + return 0; + } else if (size == 1) { + // there's only one item so we should select it + return 0; + } else if (ui == null || ui.columnCount <= 1) { + return -1; + } + + int column = ui.convertModelToColumn(index); + int row = ui.convertModelToRow(index); + + column += amount; + if (column >= ui.columnCount || column < 0) { + // No wrapping. + return -1; + } + int maxRowCount = ui.getRowCount(column); + if (row >= maxRowCount) { + return -1; + } + return ui.getModelIndex(column, row); + } + // Won't change the selection. + return -1; + } + + private int getNextIndex(JList list, BasicXListUI ui, int amount) { + int index = adjustIndex(list.getLeadSelectionIndex(), list); + int size = getElementCount(list); + + if (index == -1) { + if (size > 0) { + if (amount > 0) { + index = 0; + } + else { + index = size - 1; + } + } + } else if (size == 1) { + // there's only one item so we should select it + index = 0; + } else if (list.getLayoutOrientation() == JList.HORIZONTAL_WRAP) { + if (ui != null) { + index += ui.columnCount * amount; + } + } else { + index += amount; + } + + return index; + } + } + + + private class Handler implements FocusListener, KeyListener, + ListDataListener, ListSelectionListener, + MouseInputListener, PropertyChangeListener, + BeforeDrag { + // + // KeyListener + // + private String prefix = ""; + private String typedString = ""; + private long lastTime = 0L; + + + /** + * Invoked when a key has been typed. + * + * Moves the keyboard focus to the first element whose prefix matches the + * sequence of alphanumeric keys pressed by the user with delay less + * than value of timeFactor property (or 1000 milliseconds + * if it is not defined). Subsequent same key presses move the keyboard + * focus to the next object that starts with the same letter until another + * key is pressed, then it is treated as the prefix with appropriate number + * of the same letters followed by first typed anothe letter. + */ + public void keyTyped(KeyEvent e) { + JList src = (JList)e.getSource(); + + if (getElementCount() == 0 || e.isAltDown() || e.isControlDown() || e.isMetaDown() || + isNavigationKey(e)) { + // Nothing to select + return; + } + boolean startingFromSelection = true; + + char c = e.getKeyChar(); + + long time = e.getWhen(); + int startIndex = adjustIndex(src.getLeadSelectionIndex(), list); + if (time - lastTime < timeFactor) { + typedString += c; + if((prefix.length() == 1) && (c == prefix.charAt(0))) { + // Subsequent same key presses move the keyboard focus to the next + // object that starts with the same letter. + startIndex++; + } else { + prefix = typedString; + } + } else { + startIndex++; + typedString = "" + c; + prefix = typedString; + } + lastTime = time; + + if (startIndex < 0 || startIndex >= getElementCount()) { + startingFromSelection = false; + startIndex = 0; + } + int index = src.getNextMatch(prefix, startIndex, + Position.Bias.Forward); + if (index >= 0) { + src.setSelectedIndex(index); + src.ensureIndexIsVisible(index); + } else if (startingFromSelection) { // wrap + index = src.getNextMatch(prefix, 0, + Position.Bias.Forward); + if (index >= 0) { + src.setSelectedIndex(index); + src.ensureIndexIsVisible(index); + } + } + } + + /** + * Invoked when a key has been pressed. + * + * Checks to see if the key event is a navigation key to prevent + * dispatching these keys for the first letter navigation. + */ + public void keyPressed(KeyEvent e) { + if ( isNavigationKey(e) ) { + prefix = ""; + typedString = ""; + lastTime = 0L; + } + } + + /** + * Invoked when a key has been released. + * See the class description for {@link KeyEvent} for a definition of + * a key released event. + */ + public void keyReleased(KeyEvent e) { + } + + /** + * Returns whether or not the supplied key event maps to a key that is used for + * navigation. This is used for optimizing key input by only passing non- + * navigation keys to the first letter navigation mechanism. + */ + private boolean isNavigationKey(KeyEvent event) { + InputMap inputMap = list.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); + KeyStroke key = KeyStroke.getKeyStrokeForEvent(event); + + if (inputMap != null && inputMap.get(key) != null) { + return true; + } + return false; + } + + // + // PropertyChangeListener + // + public void propertyChange(PropertyChangeEvent e) { + String propertyName = e.getPropertyName(); + + /* If the JList.model property changes, remove our listener, + * listDataListener from the old model and add it to the new one. + */ + if (propertyName == "model") { + ListModel oldModel = (ListModel)e.getOldValue(); + ListModel newModel = (ListModel)e.getNewValue(); + if (oldModel != null) { + oldModel.removeListDataListener(listDataListener); + } + if (newModel != null) { + newModel.addListDataListener(listDataListener); + } + updateLayoutStateNeeded |= modelChanged; + redrawList(); + } + + /* If the JList.selectionModel property changes, remove our listener, + * listSelectionListener from the old selectionModel and add it to the new one. + */ + else if (propertyName == "selectionModel") { + ListSelectionModel oldModel = (ListSelectionModel)e.getOldValue(); + ListSelectionModel newModel = (ListSelectionModel)e.getNewValue(); + if (oldModel != null) { + oldModel.removeListSelectionListener(listSelectionListener); + } + if (newModel != null) { + newModel.addListSelectionListener(listSelectionListener); + } + updateLayoutStateNeeded |= modelChanged; + redrawList(); + } + else if (propertyName == "cellRenderer") { + updateLayoutStateNeeded |= cellRendererChanged; + redrawList(); + } + else if (propertyName == "font") { + updateLayoutStateNeeded |= fontChanged; + redrawList(); + } + else if (propertyName == "prototypeCellValue") { + updateLayoutStateNeeded |= prototypeCellValueChanged; + redrawList(); + } + else if (propertyName == "fixedCellHeight") { + updateLayoutStateNeeded |= fixedCellHeightChanged; + redrawList(); + } + else if (propertyName == "fixedCellWidth") { + updateLayoutStateNeeded |= fixedCellWidthChanged; + redrawList(); + } + else if (propertyName == "cellRenderer") { + updateLayoutStateNeeded |= cellRendererChanged; + redrawList(); + } + else if (propertyName == "selectionForeground") { + list.repaint(); + } + else if (propertyName == "selectionBackground") { + list.repaint(); + } + else if ("layoutOrientation" == propertyName) { + updateLayoutStateNeeded |= layoutOrientationChanged; + layoutOrientation = list.getLayoutOrientation(); + redrawList(); + } + else if ("visibleRowCount" == propertyName) { + if (layoutOrientation != JList.VERTICAL) { + updateLayoutStateNeeded |= layoutOrientationChanged; + redrawList(); + } + } + else if ("componentOrientation" == propertyName) { + isLeftToRight = list.getComponentOrientation().isLeftToRight(); + updateLayoutStateNeeded |= componentOrientationChanged; + redrawList(); + + InputMap inputMap = getInputMap(JComponent.WHEN_FOCUSED); + SwingUtilities.replaceUIInputMap(list, JComponent.WHEN_FOCUSED, + inputMap); + } else if ("List.isFileList" == propertyName) { + updateIsFileList(); + redrawList(); + } else if ("dropLocation" == propertyName) { + JList.DropLocation oldValue = (JList.DropLocation)e.getOldValue(); + repaintDropLocation(oldValue); + repaintDropLocation(list.getDropLocation()); + } + } + + private void repaintDropLocation(JList.DropLocation loc) { + if (loc == null) { + return; + } + + Rectangle r; + + if (loc.isInsert()) { + r = getDropLineRect(loc); + } else { + r = getCellBounds(list, loc.getIndex()); + } + + if (r != null) { + list.repaint(r); + } + } + + // + // ListDataListener + // + public void intervalAdded(ListDataEvent e) { + updateLayoutStateNeeded = modelChanged; + + int minIndex = Math.min(e.getIndex0(), e.getIndex1()); + int maxIndex = Math.max(e.getIndex0(), e.getIndex1()); + + /* Sync the SelectionModel with the DataModel. + */ + + ListSelectionModel sm = list.getSelectionModel(); + if (sm != null) { + sm.insertIndexInterval(minIndex, maxIndex - minIndex+1, true); + } + + /* Repaint the entire list, from the origin of + * the first added cell, to the bottom of the + * component. + */ + redrawList(); + } + + + public void intervalRemoved(ListDataEvent e) + { + updateLayoutStateNeeded = modelChanged; + + /* Sync the SelectionModel with the DataModel. + */ + + ListSelectionModel sm = list.getSelectionModel(); + if (sm != null) { + sm.removeIndexInterval(e.getIndex0(), e.getIndex1()); + } + + /* Repaint the entire list, from the origin of + * the first removed cell, to the bottom of the + * component. + */ + + redrawList(); + } + + + public void contentsChanged(ListDataEvent e) { + updateLayoutStateNeeded = modelChanged; + redrawList(); + } + + + // + // ListSelectionListener + // + public void valueChanged(ListSelectionEvent e) { + maybeUpdateLayoutState(); + int size = getElementCount(); + int firstIndex = Math.min(size - 1, Math.max(e.getFirstIndex(), 0)); + int lastIndex = Math.min(size - 1, Math.max(e.getLastIndex(), 0)); + + Rectangle bounds = getCellBounds(list, firstIndex, lastIndex); + + if (bounds != null) { + list.repaint(bounds.x, bounds.y, bounds.width, bounds.height); + } + } + + // + // MouseListener + // + public void mouseClicked(MouseEvent e) { + } + + public void mouseEntered(MouseEvent e) { + } + + public void mouseExited(MouseEvent e) { + } + + // Whether or not the mouse press (which is being considered as part + // of a drag sequence) also caused the selection change to be fully + // processed. + private boolean dragPressDidSelection; + + public void mousePressed(MouseEvent e) { + if (SwingXUtilities.shouldIgnore(e, list)) { + return; + } + + boolean dragEnabled = list.getDragEnabled(); + boolean grabFocus = true; + + // different behavior if drag is enabled + if (dragEnabled) { + // PENDING JW: this isn't aware of sorting/filtering - fix! + int row = SwingXUtilities.loc2IndexFileList(list, e.getPoint()); + // if we have a valid row and this is a drag initiating event + if (row != -1 && DragRecognitionSupport.mousePressed(e)) { + dragPressDidSelection = false; + + if (e.isControlDown()) { + // do nothing for control - will be handled on release + // or when drag starts + return; + } else if (!e.isShiftDown() && list.isSelectedIndex(row)) { + // clicking on something that's already selected + // and need to make it the lead now + list.addSelectionInterval(row, row); + return; + } + + // could be a drag initiating event - don't grab focus + grabFocus = false; + + dragPressDidSelection = true; + } + } else { + // When drag is enabled mouse drags won't change the selection + // in the list, so we only set the isAdjusting flag when it's + // not enabled + list.setValueIsAdjusting(true); + } + + if (grabFocus) { + SwingXUtilities.adjustFocus(list); + } + + adjustSelection(e); + } + + private void adjustSelection(MouseEvent e) { + // PENDING JW: this isn't aware of sorting/filtering - fix! + int row = SwingXUtilities.loc2IndexFileList(list, e.getPoint()); + if (row < 0) { + // If shift is down in multi-select, we should do nothing. + // For single select or non-shift-click, clear the selection + if (isFileList && + e.getID() == MouseEvent.MOUSE_PRESSED && + (!e.isShiftDown() || + list.getSelectionMode() == ListSelectionModel.SINGLE_SELECTION)) { + list.clearSelection(); + } + } + else { + int anchorIndex = adjustIndex(list.getAnchorSelectionIndex(), list); + boolean anchorSelected; + if (anchorIndex == -1) { + anchorIndex = 0; + anchorSelected = false; + } else { + anchorSelected = list.isSelectedIndex(anchorIndex); + } + + if (e.isControlDown()) { + if (e.isShiftDown()) { + if (anchorSelected) { + list.addSelectionInterval(anchorIndex, row); + } else { + list.removeSelectionInterval(anchorIndex, row); + if (isFileList) { + list.addSelectionInterval(row, row); + list.getSelectionModel().setAnchorSelectionIndex(anchorIndex); + } + } + } else if (list.isSelectedIndex(row)) { + list.removeSelectionInterval(row, row); + } else { + list.addSelectionInterval(row, row); + } + } else if (e.isShiftDown()) { + list.setSelectionInterval(anchorIndex, row); + } else { + list.setSelectionInterval(row, row); + } + } + } + + public void dragStarting(MouseEvent me) { + if (me.isControlDown()) { + // PENDING JW: this isn't aware of sorting/filtering - fix! + int row = SwingXUtilities.loc2IndexFileList(list, me.getPoint()); + list.addSelectionInterval(row, row); + } + } + + public void mouseDragged(MouseEvent e) { + if (SwingXUtilities.shouldIgnore(e, list)) { + return; + } + + if (list.getDragEnabled()) { + DragRecognitionSupport.mouseDragged(e, this); + return; + } + + if (e.isShiftDown() || e.isControlDown()) { + return; + } + + int row = locationToIndex(list, e.getPoint()); + if (row != -1) { + // 4835633. Dragging onto a File should not select it. + if (isFileList) { + return; + } + Rectangle cellBounds = getCellBounds(list, row, row); + if (cellBounds != null) { + list.scrollRectToVisible(cellBounds); + list.setSelectionInterval(row, row); + } + } + } + + public void mouseMoved(MouseEvent e) { + } + + public void mouseReleased(MouseEvent e) { + if (SwingXUtilities.shouldIgnore(e, list)) { + return; + } + + if (list.getDragEnabled()) { + MouseEvent me = DragRecognitionSupport.mouseReleased(e); + if (me != null) { + SwingXUtilities.adjustFocus(list); + if (!dragPressDidSelection) { + adjustSelection(me); + } + } + } else { + list.setValueIsAdjusting(false); + } + } + + // + // FocusListener + // + protected void repaintCellFocus() + { + int leadIndex = adjustIndex(list.getLeadSelectionIndex(), list); + if (leadIndex != -1) { + Rectangle r = getCellBounds(list, leadIndex, leadIndex); + if (r != null) { + list.repaint(r.x, r.y, r.width, r.height); + } + } + } + + /* The focusGained() focusLost() methods run when the JList + * focus changes. + */ + + public void focusGained(FocusEvent e) { + repaintCellFocus(); + } + + public void focusLost(FocusEvent e) { + repaintCellFocus(); + } + } + + private static int adjustIndex(int index, JList list) { + return index < ((JXList) list).getElementCount() ? index : -1; + } + + private static final TransferHandler defaultTransferHandler = new ListTransferHandler(); + + static class ListTransferHandler extends TransferHandler implements UIResource { + + /** + * Create a Transferable to use as the source for a data transfer. + * + * @param c The component holding the data to be transfered. This + * argument is provided to enable sharing of TransferHandlers by + * multiple components. + * @return The representation of the data to be transfered. + * + */ + protected Transferable createTransferable(JComponent c) { + if (c instanceof JList) { + JList list = (JList) c; + Object[] values = list.getSelectedValues(); + + if (values == null || values.length == 0) { + return null; + } + + StringBuffer plainBuf = new StringBuffer(); + StringBuffer htmlBuf = new StringBuffer(); + + htmlBuf.append("\n\n
        \n"); + + for (int i = 0; i < values.length; i++) { + Object obj = values[i]; + String val = ((obj == null) ? "" : obj.toString()); + plainBuf.append(val + "\n"); + htmlBuf.append("
      • " + val + "\n"); + } + + // remove the last newline + plainBuf.deleteCharAt(plainBuf.length() - 1); + htmlBuf.append("
      \n\n"); + + return new BasicTransferable(plainBuf.toString(), htmlBuf.toString()); + } + + return null; + } + + public int getSourceActions(JComponent c) { + return COPY; + } + + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/core/DragRecognitionSupport.java b/src/main/java/org/jdesktop/swingx/plaf/basic/core/DragRecognitionSupport.java new file mode 100644 index 0000000000..35804fc5c6 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/core/DragRecognitionSupport.java @@ -0,0 +1,205 @@ +/* + * $Id$ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.plaf.basic.core; + +/* + * @(#)DragRecognitionSupport.java 1.2 05/11/17 + * + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + */ + +import org.jdesktop.swingx.SwingXUtilities; + +import javax.swing.*; +import java.awt.dnd.DragSource; +import java.awt.event.MouseEvent; +//import sun.awt.dnd.SunDragSourceContextPeer; +//import sun.awt.AppContext; + +/** + * Drag gesture recognition support for classes that have a + * TransferHandler. The gesture for a drag in this class is a mouse + * press followed by movement by DragSource.getDragThreshold() + * pixels. An instance of this class is maintained per AppContext, and the + * public static methods call into the appropriate instance.

      + * + * This is a c&p of core (package private) needed for BasicXListUI. It differs from + * core in that references to sun packages have been replaced. + *

        + *
      • a static method of SunDragSourceContextPeer has been copied into SwingXUtilities + * and is used here + *
      • the shared instance of this class is maintained in the UIManager instead of + * per appContext. + *
      + * + * @author Shannon Hickey + * @version 1.2 11/17/05 + */ +public class DragRecognitionSupport { + private int motionThreshold; + private MouseEvent dndArmedEvent; + private JComponent component; + + /** + * This interface allows us to pass in a handler to mouseDragged, + * so that we can be notified immediately before a drag begins. + */ + public static interface BeforeDrag { + public void dragStarting(MouseEvent me); + } + + /** + * Returns the DragRecognitionSupport for the caller's AppContext. + */ + private static DragRecognitionSupport getDragRecognitionSupport() { +// DragRecognitionSupport support = +// (DragRecognitionSupport)AppContext.getAppContext(). +// get(DragRecognitionSupport.class); +// +// if (support == null) { +// support = new DragRecognitionSupport(); +// AppContext.getAppContext().put(DragRecognitionSupport.class, support); +// } + + DragRecognitionSupport support = (DragRecognitionSupport) + UIManager.get("sharedInstance.dragRecognitionSupport"); + if (support == null) { + support = new DragRecognitionSupport(); + UIManager.put("sharedInstance.dragRecognitionSupport", support); + } + return support; + } + + /** + * Returns whether or not the event is potentially part of a drag sequence. + */ + public static boolean mousePressed(MouseEvent me) { + return ((DragRecognitionSupport)getDragRecognitionSupport()). + mousePressedImpl(me); + } + + /** + * If a dnd recognition has been going on, return the MouseEvent + * that started the recognition. Otherwise, return null. + */ + public static MouseEvent mouseReleased(MouseEvent me) { + return ((DragRecognitionSupport)getDragRecognitionSupport()). + mouseReleasedImpl(me); + } + + /** + * Returns whether or not a drag gesture recognition is ongoing. + */ + public static boolean mouseDragged(MouseEvent me, BeforeDrag bd) { + return ((DragRecognitionSupport)getDragRecognitionSupport()). + mouseDraggedImpl(me, bd); + } + + private void clearState() { + dndArmedEvent = null; + component = null; + } + + private int mapDragOperationFromModifiers(MouseEvent me, + TransferHandler th) { + + if (th == null || !SwingUtilities.isLeftMouseButton(me)) { + return TransferHandler.NONE; + } + // PENDING JW: c'p from SunDragSourceContextPeer + return SwingXUtilities. + convertModifiersToDropAction(me.getModifiersEx(), + th.getSourceActions(component)); + } + + /** + * Returns whether or not the event is potentially part of a drag sequence. + */ + private boolean mousePressedImpl(MouseEvent me) { + component = (JComponent)me.getSource(); + + if (mapDragOperationFromModifiers(me, component.getTransferHandler()) + != TransferHandler.NONE) { + + motionThreshold = DragSource.getDragThreshold(); + dndArmedEvent = me; + return true; + } + + clearState(); + return false; + } + + /** + * If a dnd recognition has been going on, return the MouseEvent + * that started the recognition. Otherwise, return null. + */ + private MouseEvent mouseReleasedImpl(MouseEvent me) { + /* no recognition has been going on */ + if (dndArmedEvent == null) { + return null; + } + + MouseEvent retEvent = null; + + if (me.getSource() == component) { + retEvent = dndArmedEvent; + } // else component has changed unexpectedly, so return null + + clearState(); + return retEvent; + } + + /** + * Returns whether or not a drag gesture recognition is ongoing. + */ + private boolean mouseDraggedImpl(MouseEvent me, BeforeDrag bd) { + /* no recognition is in progress */ + if (dndArmedEvent == null) { + return false; + } + + /* component has changed unexpectedly, so bail */ + if (me.getSource() != component) { + clearState(); + return false; + } + + int dx = Math.abs(me.getX() - dndArmedEvent.getX()); + int dy = Math.abs(me.getY() - dndArmedEvent.getY()); + if ((dx > motionThreshold) || (dy > motionThreshold)) { + TransferHandler th = component.getTransferHandler(); + int action = mapDragOperationFromModifiers(me, th); + if (action != TransferHandler.NONE) { + /* notify the BeforeDrag instance */ + if (bd != null) { + bd.dragStarting(dndArmedEvent); + } + th.exportAsDrag(component, dndArmedEvent, action); + clearState(); + } + } + + return true; + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/core/LazyActionMap.java b/src/main/java/org/jdesktop/swingx/plaf/basic/core/LazyActionMap.java new file mode 100644 index 0000000000..036d8e2ff5 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/core/LazyActionMap.java @@ -0,0 +1,165 @@ +/* + * $Id$ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.plaf.basic.core; + + +import javax.swing.*; +import javax.swing.plaf.ActionMapUIResource; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * An ActionMap that populates its contents as necessary. The + * contents are populated by invoking the loadActionMap + * method on the passed in Object. + * + * @version 1.6, 11/17/05 + * @author Scott Violet + */ +public class LazyActionMap extends ActionMapUIResource { + /** + * Object to invoke loadActionMap on. This may be + * a Class object. + */ + private transient Object _loader; + + /** + * Installs an ActionMap that will be populated by invoking the + * loadActionMap method on the specified Class + * when necessary. + *

      + * This should be used if the ActionMap can be shared. + * + * @param c JComponent to install the ActionMap on. + * @param loaderClass Class object that gets loadActionMap invoked + * on. + * @param defaultsKey Key to use to defaults table to check for + * existing map and what resulting Map will be registered on. + */ + public static void installLazyActionMap(JComponent c, Class loaderClass, + String defaultsKey) { + ActionMap map = (ActionMap)UIManager.get(defaultsKey); + if (map == null) { + map = new LazyActionMap(loaderClass); + UIManager.getLookAndFeelDefaults().put(defaultsKey, map); + } + SwingUtilities.replaceUIActionMap(c, map); + } + + /** + * Returns an ActionMap that will be populated by invoking the + * loadActionMap method on the specified Class + * when necessary. + *

      + * This should be used if the ActionMap can be shared. + * + * @param c JComponent to install the ActionMap on. + * @param loaderClass Class object that gets loadActionMap invoked + * on. + * @param defaultsKey Key to use to defaults table to check for + * existing map and what resulting Map will be registered on. + */ + static ActionMap getActionMap(Class loaderClass, + String defaultsKey) { + ActionMap map = (ActionMap)UIManager.get(defaultsKey); + if (map == null) { + map = new LazyActionMap(loaderClass); + UIManager.getLookAndFeelDefaults().put(defaultsKey, map); + } + return map; + } + + + private LazyActionMap(Class loader) { + _loader = loader; + } + + public void put(Action action) { + put(action.getValue(Action.NAME), action); + } + + public void put(Object key, Action action) { + loadIfNecessary(); + super.put(key, action); + } + + public Action get(Object key) { + loadIfNecessary(); + return super.get(key); + } + + public void remove(Object key) { + loadIfNecessary(); + super.remove(key); + } + + public void clear() { + loadIfNecessary(); + super.clear(); + } + + public Object[] keys() { + loadIfNecessary(); + return super.keys(); + } + + public int size() { + loadIfNecessary(); + return super.size(); + } + + public Object[] allKeys() { + loadIfNecessary(); + return super.allKeys(); + } + + public void setParent(ActionMap map) { + loadIfNecessary(); + super.setParent(map); + } + + private void loadIfNecessary() { + if (_loader != null) { + Object loader = _loader; + + _loader = null; + Class klass = (Class)loader; + try { + Method method = klass.getDeclaredMethod("loadActionMap", + new Class[] { LazyActionMap.class }); + method.invoke(klass, new Object[] { this }); + } catch (NoSuchMethodException nsme) { + assert false : "LazyActionMap unable to load actions " + + klass; + } catch (IllegalAccessException iae) { + assert false : "LazyActionMap unable to load actions " + + iae; + } catch (InvocationTargetException ite) { + assert false : "LazyActionMap unable to load actions " + + ite; + } catch (IllegalArgumentException iae) { + assert false : "LazyActionMap unable to load actions " + + iae; + } + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/core/ListSortUI.java b/src/main/java/org/jdesktop/swingx/plaf/basic/core/ListSortUI.java new file mode 100644 index 0000000000..0291d5b45b --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/core/ListSortUI.java @@ -0,0 +1,503 @@ +/* + * $Id$ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.plaf.basic.core; + +import org.jdesktop.swingx.JXList; +import org.jdesktop.swingx.SwingXUtilities; +import org.jdesktop.swingx.util.Contract; + +import javax.swing.*; +import javax.swing.event.ListDataEvent; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.RowSorterEvent; +import javax.swing.event.RowSorterListener; + +/** + * ListSortUI provides support for managing the synchronization between + * RowSorter, SelectionModel and ListModel if a JXList is sortable.

      + * + * This implementation is an adaption of JTable.SortManager fit to the + * needs of a ListUI. In contrast to JTable tradition, the ui delegate has + * full control about listening to model/selection changes and updating + * the list accordingly. So the role of this class is that of a helper to the ListUI + * (vs. as a helper of the JTable). + *

      + * It's up to the ListUI to + * listen to model/selection and propagate the notification to this class if + * a sorter is installed, but still do the usual updates (layout, repaint) itself. + * On the other hand, listening to the sorter and updating list state accordingly + * is completely done by this. + * + */ +public final class ListSortUI { + private RowSorter sorter; + private JXList list; + + // Selection, in terms of the model. This is lazily created + // as needed. + private ListSelectionModel modelSelection; + private int modelLeadIndex; + // Set to true while in the process of changing the selection. + // If this is true the selection change is ignored. + private boolean syncingSelection; + // Temporary cache of selection, in terms of model. This is only used + // if we don't need the full weight of modelSelection. + private int[] lastModelSelection; + private boolean sorterChanged; + private boolean ignoreSortChange; + private RowSorterListener sorterListener; + + /** + * Intanstiates a SortUI on the list which has the given RowSorter. + * + * @param list the list to control, must not be null + * @param sorter the rowSorter of the list, must not be null + * @throws NullPointerException if either the list or the sorter is null + * @throws IllegalStateException if the sorter is not the sorter installed + * on the list + */ + public ListSortUI(JXList list, RowSorter sorter) { + this.sorter = Contract.asNotNull(sorter, "RowSorter must not be null"); + this.list = Contract.asNotNull(list, "list must not be null"); + if (sorter != list.getRowSorter()) throw + new IllegalStateException("sorter must be same as the one on list"); + sorterListener = createRowSorterListener(); + sorter.addRowSorterListener(sorterListener); + } + + /** + * Disposes any resources used by this SortManager. + * Note: this instance must not be used after dispose! + */ + public void dispose() { + if (sorter != null) { + sorter.removeRowSorterListener(sorterListener); + } + sorter = null; + list = null; + } + +//----------------------methods called by listeners + + /** + * Called after notification from ListModel. + * @param e the change event from the listModel. + */ + public void modelChanged(ListDataEvent e) { + ModelChange change = new ModelChange(e); + prepareForChange(change); + notifySorter(change); + if (change.type != ListDataEvent.CONTENTS_CHANGED) { + // If the Sorter is unsorted we will not have received + // notification, force treating insert/delete as a change. + sorterChanged = true; + } + processChange(change); + } + + /** + * Called after notification from selectionModel. + * + * Invoked when the selection, on the view, has changed. + */ + public void viewSelectionChanged(ListSelectionEvent e) { + if (!syncingSelection && modelSelection != null) { + modelSelection = null; + } + } + + /** + * Called after notification from RowSorter. + * + * @param e RowSorter event of type SORTED. + */ + protected void sortedChanged(RowSorterEvent e) { + sorterChanged = true; + if (!ignoreSortChange) { + prepareForChange(e); + processChange(null); + // PENDING Jw: this is fix of 1161-swingx - not updated after setting + // rowFilter + // potentially costly? but how to distinguish a mere sort from a + // filterchanged? (only the latter requires a revalidate) + // first fix had only revalidate/repaint but was not + // good enough, see #1261-swingx - no items visible + // after setting rowFilter + // need to invalidate the cell size cache which might be needed + // even after plain sorting as the indi-sizes are now at different + // positions + list.invalidateCellSizeCache(); + } + } + + +//--------------------- prepare change, that is cache selection if needed + /** + * Invoked when the RowSorter has changed. + * Updates the internal cache of the selection based on the change. + * + * @param sortEvent the notification + * @throws NullPointerException if the given event is null. + */ + private void prepareForChange(RowSorterEvent sortEvent) { + Contract.asNotNull(sortEvent, "sorter event not null"); + // sort order changed. If modelSelection is null and filtering + // is enabled we need to cache the selection in terms of the + // underlying model, this will allow us to correctly restore + // the selection even if rows are filtered out. + if (modelSelection == null && + sorter.getViewRowCount() != sorter.getModelRowCount()) { + modelSelection = new DefaultListSelectionModel(); + ListSelectionModel viewSelection = getViewSelectionModel(); + int min = viewSelection.getMinSelectionIndex(); + int max = viewSelection.getMaxSelectionIndex(); + int modelIndex; + for (int viewIndex = min; viewIndex <= max; viewIndex++) { + if (viewSelection.isSelectedIndex(viewIndex)) { + modelIndex = convertRowIndexToModel( + sortEvent, viewIndex); + if (modelIndex != -1) { + modelSelection.addSelectionInterval( + modelIndex, modelIndex); + } + } + } + modelIndex = convertRowIndexToModel(sortEvent, + viewSelection.getLeadSelectionIndex()); + SwingXUtilities.setLeadAnchorWithoutSelection( + modelSelection, modelIndex, modelIndex); + } else if (modelSelection == null) { + // Sorting changed, haven't cached selection in terms + // of model and no filtering. Temporarily cache selection. + cacheModelSelection(sortEvent); + } + } + /** + * Invoked when the list model has changed. This is invoked prior to + * notifying the sorter of the change. + * Updates the internal cache of the selection based on the change. + * + * @param change the notification + * @throws NullPointerException if the given event is null. + */ + private void prepareForChange(ModelChange change) { + Contract.asNotNull(change, "table event not null"); + if (change.allRowsChanged) { + // All the rows have changed, chuck any cached selection. + modelSelection = null; + } else if (modelSelection != null) { + // Table changed, reflect changes in cached selection model. + switch (change.type) { + // JW: core incorrectly uses change.endModelIndex! + // sneaked into here via c&p + // reported as #1536-swingx + case ListDataEvent.INTERVAL_REMOVED: + modelSelection.removeIndexInterval(change.startModelIndex, + // Note: api difference between remove vs. insert + // nothing do do here! + change.endModelIndex); + break; + case ListDataEvent.INTERVAL_ADDED: + modelSelection.insertIndexInterval(change.startModelIndex, + // insert is tested + change.length, true); + break; + default: + break; + } + } else { + // list changed, but haven't cached rows, temporarily + // cache them. + cacheModelSelection(null); + } + } + + private void cacheModelSelection(RowSorterEvent sortEvent) { + lastModelSelection = convertSelectionToModel(sortEvent); + modelLeadIndex = convertRowIndexToModel(sortEvent, + getViewSelectionModel().getLeadSelectionIndex()); + } + +//----------------------- process change, that is restore selection if needed + /** + * Inovked when either the table has changed or the sorter has changed + * and after the sorter has been notified. If necessary this will + * reapply the selection and variable row heights. + */ + private void processChange(ModelChange change) { + if (change != null && change.allRowsChanged) { + allChanged(); + getViewSelectionModel().clearSelection(); + } else if (sorterChanged) { + restoreSelection(change); + } + } + + /** + * Restores the selection from that in terms of the model. + */ + private void restoreSelection(ModelChange change) { + syncingSelection = true; + if (lastModelSelection != null) { + restoreSortingSelection(lastModelSelection, + modelLeadIndex, change); + lastModelSelection = null; + } else if (modelSelection != null) { + ListSelectionModel viewSelection = getViewSelectionModel(); + viewSelection.setValueIsAdjusting(true); + viewSelection.clearSelection(); + int min = modelSelection.getMinSelectionIndex(); + int max = modelSelection.getMaxSelectionIndex(); + int viewIndex; + for (int modelIndex = min; modelIndex <= max; modelIndex++) { + if (modelSelection.isSelectedIndex(modelIndex)) { + viewIndex = sorter.convertRowIndexToView(modelIndex); + if (viewIndex != -1) { + viewSelection.addSelectionInterval(viewIndex, + viewIndex); + } + } + } + // Restore the lead + int viewLeadIndex = modelSelection.getLeadSelectionIndex(); + if (viewLeadIndex != -1) { + viewLeadIndex = sorter.convertRowIndexToView(viewLeadIndex); + } + SwingXUtilities.setLeadAnchorWithoutSelection( + viewSelection, viewLeadIndex, viewLeadIndex); + viewSelection.setValueIsAdjusting(false); + } + syncingSelection = false; + } + + /** + * Restores the selection after a model event/sort order changes. + * All coordinates are in terms of the model. + */ + private void restoreSortingSelection(int[] selection, int lead, + ModelChange change) { + // Convert the selection from model to view + for (int i = selection.length - 1; i >= 0; i--) { + selection[i] = convertRowIndexToView(change, selection[i]); + } + lead = convertRowIndexToView(change, lead); + + // Check for the common case of no change in selection for 1 row + if (selection.length == 0 || + (selection.length == 1 && selection[0] == list.getSelectedIndex())) { + return; + } + ListSelectionModel selectionModel = getViewSelectionModel(); + // And apply the new selection + selectionModel.setValueIsAdjusting(true); + selectionModel.clearSelection(); + for (int i = selection.length - 1; i >= 0; i--) { + if (selection[i] != -1) { + selectionModel.addSelectionInterval(selection[i], + selection[i]); + } + } + SwingXUtilities.setLeadAnchorWithoutSelection( + selectionModel, lead, lead); + selectionModel.setValueIsAdjusting(false); + } + +//------------------- row index conversion methods + /** + * Converts a model index to view index. This is called when the + * sorter or model changes and sorting is enabled. + * + * @param change describes the TableModelEvent that initiated the change; + * will be null if called as the result of a sort + */ + private int convertRowIndexToView(ModelChange change, int modelIndex) { + if (modelIndex < 0) { + return -1; + } +// Contract.asNotNull(change, "change must not be null?"); + if (change != null && modelIndex >= change.startModelIndex) { + if (change.type == ListDataEvent.INTERVAL_ADDED) { + if (modelIndex + change.length >= change.modelRowCount) { + return -1; + } + return sorter.convertRowIndexToView( + modelIndex + change.length); + } + else if (change.type == ListDataEvent.INTERVAL_REMOVED) { + if (modelIndex <= change.endModelIndex) { + // deleted + return -1; + } + else { + if (modelIndex - change.length >= change.modelRowCount) { + return -1; + } + return sorter.convertRowIndexToView( + modelIndex - change.length); + } + } + // else, updated + } + if (modelIndex >= sorter.getModelRowCount()) { + return -1; + } + return sorter.convertRowIndexToView(modelIndex); + } + + + private int convertRowIndexToModel(RowSorterEvent e, int viewIndex) { + // JW: the event is null if the selection is cached in prepareChange + // after model notification. Then the conversion from the + // sorter is still valid as the prepare is called before + // notifying the sorter. + if (e != null) { + if (e.getPreviousRowCount() == 0) { + return viewIndex; + } + // range checking handled by RowSorterEvent + return e.convertPreviousRowIndexToModel(viewIndex); + } + // Make sure the viewIndex is valid + if (viewIndex < 0 || viewIndex >= sorter.getViewRowCount()) { + return -1; + } + return sorter.convertRowIndexToModel(viewIndex); + } + + /** + * Converts the selection to model coordinates. This is used when + * the model changes or the sorter changes. + */ + private int[] convertSelectionToModel(RowSorterEvent e) { + int[] selection = list.getSelectedIndices(); + for (int i = selection.length - 1; i >= 0; i--) { + selection[i] = convertRowIndexToModel(e, selection[i]); + } + return selection; + } + +//------------------ + /** + * Notifies the sorter of a change in the underlying model. + */ + private void notifySorter(ModelChange change) { + try { + ignoreSortChange = true; + sorterChanged = false; + if (change.allRowsChanged) { + sorter.allRowsChanged(); + } else { + switch (change.type) { + case ListDataEvent.CONTENTS_CHANGED: + sorter.rowsUpdated(change.startModelIndex, + change.endModelIndex); + break; + case ListDataEvent.INTERVAL_ADDED: + sorter.rowsInserted(change.startModelIndex, + change.endModelIndex); + break; + case ListDataEvent.INTERVAL_REMOVED: + sorter.rowsDeleted(change.startModelIndex, + change.endModelIndex); + break; + } + } + } finally { + ignoreSortChange = false; + } + } + + + private ListSelectionModel getViewSelectionModel() { + return list.getSelectionModel(); + } + /** + * Invoked when the underlying model has completely changed. + */ + private void allChanged() { + modelLeadIndex = -1; + modelSelection = null; + } + +//------------------- implementing listeners + + /** + * Creates and returns a RowSorterListener. This implementation + * calls sortedChanged if the event is of type SORTED. + * + * @return rowSorterListener to install on sorter. + */ + protected RowSorterListener createRowSorterListener() { + RowSorterListener l = new RowSorterListener() { + + @Override + public void sorterChanged(RowSorterEvent e) { + if (e.getType() == RowSorterEvent.Type.SORTED) { + sortedChanged(e); + } + } + + }; + return l; + } + /** + * ModelChange is used when sorting to restore state, it corresponds + * to data from a TableModelEvent. The values are precalculated as + * they are used extensively.

      + * + * PENDING JW: this is not yet fully adapted to ListDataEvent. + */ + final static class ModelChange { + // JW: if we received a dataChanged, there _is no_ notion + // of end/start/length of change + // Starting index of the change, in terms of the model, -1 if dataChanged + int startModelIndex; + + // Ending index of the change, in terms of the model, -1 if dataChanged + int endModelIndex; + + // Length of the change (end - start + 1), - 1 if dataChanged + int length; + + // Type of change + int type; + + // Number of rows in the model + int modelRowCount; + + + // True if the event indicates all the contents have changed + boolean allRowsChanged; + + public ModelChange(ListDataEvent e) { + type = e.getType(); + modelRowCount = ((ListModel) e.getSource()).getSize(); + startModelIndex = e.getIndex0(); + endModelIndex = e.getIndex1(); + allRowsChanged = startModelIndex < 0; + length = allRowsChanged ? -1 : endModelIndex - startModelIndex + 1; + } + } + + +} + diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/package-info.java b/src/main/java/org/jdesktop/swingx/plaf/basic/package-info.java new file mode 100644 index 0000000000..c6e2ad56f0 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/package-info.java @@ -0,0 +1,26 @@ +/* + * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +/** + * Provides basic implementation of pluggable look-and-feel for SwingX components together with a + * mechanism to support custom component look-and-feels. + */ +package org.jdesktop.swingx.plaf.basic; + diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker.properties new file mode 100644 index 0000000000..d63b0a1f41 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker.properties @@ -0,0 +1,11 @@ +# +# Text, formatting for JXDatePicker +# +JXDatePicker.linkFormat=Today is {0,date} +# Issue #945-swingx: force a default fallback here not the right thing to +# for locales which do not have a properties file. In that case the +# language defaults provide a better fit +#JXDatePicker.longFormat=EEE MM/dd/yyyy +#JXDatePicker.mediumFormat=MM/dd/yy +#JXDatePicker.shortFormat=MM/dd +JXDatePicker.numColumns=10 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_cs.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_cs.properties new file mode 100644 index 0000000000..8f557508d0 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_cs.properties @@ -0,0 +1,9 @@ +# +# Text, formatting for JXDatePicker +# +JXDatePicker.linkFormat=Dnes je {0,date, d. MMMM yyyy} +JXDatePicker.longFormat=EEE d.MM.yyyy +#czech grammar vs application admits more variants to use +JXDatePicker.mediumFormat=d.M.yyyy +JXDatePicker.shortFormat=d.M. +JXDatePicker.numColumns=10 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_da.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_da.properties new file mode 100644 index 0000000000..d3db54bacc --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_da.properties @@ -0,0 +1,12 @@ +# +# Text, formatting for JXDatePicker +# +# Note: Danish official date formating allows for both a traditional +# and the ISO version. Since de-facto is to use the traditional +# that's what JXDatePicker will use default. +# +JXDatePicker.linkFormat=Dags dato {0,date, dd MMMM yyyy} +JXDatePicker.longFormat=EEE dd-MM-yyyy +JXDatePicker.mediumFormat=dd-MM-yyyy +JXDatePicker.shortFormat=dd/MM +JXDatePicker.numColumns=10 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_de.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_de.properties new file mode 100644 index 0000000000..80963be6bf --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_de.properties @@ -0,0 +1,8 @@ +# +# Text, formatting for JXDatePicker +# +JXDatePicker.linkFormat=Heute ist der{0,date, dd. MMMM yyyy} +JXDatePicker.longFormat=dd.MM.yyyy +JXDatePicker.mediumFormat=dd.MM.yyyy +JXDatePicker.shortFormat=dd.MM +JXDatePicker.numColumns=10 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_en.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_en.properties new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_en_GB.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_en_GB.properties new file mode 100644 index 0000000000..f8b9703039 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_en_GB.properties @@ -0,0 +1,11 @@ +# +# Text, formatting for JXDatePicker +# +# + +JXDatePicker.linkFormat=Today is {0,date, dd MMMM yyyy} +JXDatePicker.longFormat=EEE dd/MM/yyyy +JXDatePicker.mediumFormat=dd/MM/yyyy +JXDatePicker.shortFormat=dd/MM +JXDatePicker.numColumns=10 + diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_en_US.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_en_US.properties new file mode 100644 index 0000000000..be1346cbb2 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_en_US.properties @@ -0,0 +1,8 @@ +# +# Text, formatting for JXDatePicker +# +JXDatePicker.linkFormat=Today is {0,date, dd MMMM yyyy} +JXDatePicker.longFormat=EEE MM/dd/yyyy +JXDatePicker.mediumFormat=MM/dd/yy +JXDatePicker.shortFormat=MM/dd +JXDatePicker.numColumns=10 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_es.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_es.properties new file mode 100644 index 0000000000..34a01e3ef5 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_es.properties @@ -0,0 +1,4 @@ +# +# Text, formatting for JXDatePicker +# +JXDatePicker.linkFormat=Hoy es {0,date, dd MMMM yyyy} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_fr.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_fr.properties new file mode 100644 index 0000000000..54087d804b --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_fr.properties @@ -0,0 +1,8 @@ +# +# Text, formatting for JXDatePicker +# +JXDatePicker.linkFormat=Aujourd''hui, nous sommes le {0,date, dd MMMM yyyy} +JXDatePicker.longFormat=EEE dd/MM/yyyy +JXDatePicker.mediumFormat=dd/MM/yyyy +JXDatePicker.shortFormat=dd/MM +JXDatePicker.numColumns=10 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_it.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_it.properties new file mode 100644 index 0000000000..41b633de93 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_it.properties @@ -0,0 +1,8 @@ +# +# Text, formatting for JXDatePicker +# +JXDatePicker.linkFormat=Oggi \u00E8 il {0,date, dd MMMM yyyy} +JXDatePicker.longFormat=EEE dd/MM/yyyy +JXDatePicker.mediumFormat=dd/MM/yyyy +JXDatePicker.shortFormat=dd/MM +JXDatePicker.numColumns=10 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_nl.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_nl.properties new file mode 100644 index 0000000000..148aa7d614 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_nl.properties @@ -0,0 +1,8 @@ +# +# Text, formatting for JXDatePicker +# +JXDatePicker.linkFormat=Vandaag is het {0,date, dd MMMM yyyy} +JXDatePicker.longFormat=EEE yyyy-MM-dd +JXDatePicker.mediumFormat=yyyy-MM-dd +JXDatePicker.shortFormat=yyyy-MM-dd +JXDatePicker.numColumns=10 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_pl_PL.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_pl_PL.properties new file mode 100644 index 0000000000..f8408bbcf3 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_pl_PL.properties @@ -0,0 +1,8 @@ +# +# Text, formatting for JXDatePicker +# +JXDatePicker.linkFormat=Dzisiaj jest {0,date, dd MMMM yyyy} +JXDatePicker.longFormat=EEE dd.MM.yyyy +JXDatePicker.mediumFormat=dd.MM.yy +JXDatePicker.shortFormat=MM/dd +JXDatePicker.numColumns=10 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_pt_BR.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_pt_BR.properties new file mode 100644 index 0000000000..9176d39dc9 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_pt_BR.properties @@ -0,0 +1,8 @@ +# +# Text, formatting for JXDatePicker +# +JXDatePicker.linkFormat=Hoje \u00E9 {0,date, dd MMMM yyyy} +JXDatePicker.longFormat=EEE dd/MM/yyyy +JXDatePicker.mediumFormat=dd/MM/yyyy +JXDatePicker.shortFormat=dd/MM +JXDatePicker.numColumns=10 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_sv.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_sv.properties new file mode 100644 index 0000000000..62c6068ade --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_sv.properties @@ -0,0 +1,8 @@ +# +# Text, formatting for JXDatePicker +# +JXDatePicker.linkFormat=Idag \u00E4r {0,date, yyyy MMMM dd} +JXDatePicker.longFormat=EEE yyyy/MM/dd +JXDatePicker.mediumFormat=yyyy/MM/dd +JXDatePicker.shortFormat=MM/dd +JXDatePicker.numColumns=10 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane.properties new file mode 100644 index 0000000000..7989b7cc10 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane.properties @@ -0,0 +1,9 @@ +# +# JXErrorPanel properties +# +JXErrorPane.details_expand_text=Details >> +JXErrorPane.details_contract_text=Details << +JXErrorPane.ok_button_text=Close +JXErrorPane.fatal_button_text=Exit Application +JXErrorPane.report_button_text=Report Error +JXErrorPane.copy_to_clipboard_button_text=Copy to Clipboard diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_cs.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_cs.properties new file mode 100644 index 0000000000..8ab1c97948 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_cs.properties @@ -0,0 +1,9 @@ +# +# JXErrorPanel properties +# +JXErrorPane.details_expand_text=Detaily >> +JXErrorPane.details_contract_text=Detaily << +JXErrorPane.ok_button_text=Zav\u0159\u00edt +JXErrorPane.fatal_button_text=Ukon\u010dit Aplikaci +JXErrorPane.report_button_text=Ozn\u00e1mit Chybu +JXErrorPane.copy_to_clipboard_button_text=Zkop\u00edrovat do Schr\u00e1nky diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_da.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_da.properties new file mode 100644 index 0000000000..e3c998e619 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_da.properties @@ -0,0 +1,9 @@ +# +# JXErrorPanel properties +# +JXErrorPane.details_expand_text=Detaljer >> +JXErrorPane.details_contract_text=Detaljer << +JXErrorPane.ok_button_text=Luk +JXErrorPane.fatal_button_text=Afslut program +JXErrorPane.report_button_text=Rapporter fejl +JXErrorPane.copy_to_clipboard_button_text=Kopier til udklipsholder diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_de.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_de.properties new file mode 100644 index 0000000000..3c11b1ca72 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_de.properties @@ -0,0 +1,9 @@ +# +# JXErrorPanel properties +# +JXErrorPane.details_expand_text=Details >> +JXErrorPane.details_contract_text=Details << +JXErrorPane.ok_button_text=Schliessen +JXErrorPane.fatal_button_text=Anwendung beenden +JXErrorPane.report_button_text=Fehler berichten +JXErrorPane.copy_to_clipboard_button_text=In Zwischenablage kopieren diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_en.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_en.properties new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_es.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_es.properties new file mode 100644 index 0000000000..8e1de5d76d --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_es.properties @@ -0,0 +1,10 @@ +# spanish properties for swingx +# JXErrorPanel +# @author: Sergio Samayoa + +JXErrorPane.details_expand_text=Detalles >> +JXErrorPane.details_contract_text=Detalles << +JXErrorPane.ok_button_text=Cerrar +JXErrorPane.fatal_button_text=Salir de la aplicaci\u00F3n +JXErrorPane.report_button_text=Reportar error +JXErrorPane.copy_to_clipboard_button_text=Copiar al portapapeles diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_fr.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_fr.properties new file mode 100644 index 0000000000..0fa3b553cc --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_fr.properties @@ -0,0 +1,9 @@ +# +# JXErrorPanel properties +# +JXErrorPane.details_expand_text=Détails >> +JXErrorPane.details_contract_text=Détails << +JXErrorPane.ok_button_text=Fermer +JXErrorPane.fatal_button_text=Quitter l'application +JXErrorPane.report_button_text=Rapport d'erreur +JXErrorPane.copy_to_clipboard_button_text=Copier dans le presse-papiers diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_it.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_it.properties new file mode 100644 index 0000000000..8ed51948b2 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_it.properties @@ -0,0 +1,9 @@ +# +# JXErrorPanel properties +# +JXErrorPane.details_expand_text=Dettagli >> +JXErrorPane.details_contract_text=Dettagli << +JXErrorPane.ok_button_text=Chiudi +JXErrorPane.fatal_button_text=Esci dall'Applicazione +JXErrorPane.report_button_text=Segnala Errore +JXErrorPane.copy_to_clipboard_button_text=Copia negli Appunti diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_nl.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_nl.properties new file mode 100644 index 0000000000..581a742ae7 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_nl.properties @@ -0,0 +1,9 @@ +# +# JXErrorPanel properties +# +JXErrorPane.details_expand_text=Details >> +JXErrorPane.details_contract_text=Details << +JXErrorPane.ok_button_text=Sluiten +JXErrorPane.fatal_button_text=Sluit Applicatie +JXErrorPane.report_button_text=Rapporteer Fout +JXErrorPane.copy_to_clipboard_button_text=Kopieer naar klembord diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_pl.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_pl.properties new file mode 100644 index 0000000000..9158625924 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_pl.properties @@ -0,0 +1,9 @@ +# +# JXErrorPanel properties +# +JXErrorPane.details_expand_text=Szczeg\u00F3\u0142y >> +JXErrorPane.details_contract_text=Szczeg\u00F3\u0142y << +JXErrorPane.ok_button_text=Zamknij +JXErrorPane.fatal_button_text=Zako\u0144cz aplikacje +JXErrorPane.report_button_text=Raportuj b\u0142\u0105d +JXErrorPane.copy_to_clipboard_button_text=Kopiuj do schowka diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_pt_BR.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_pt_BR.properties new file mode 100644 index 0000000000..42c5e348ad --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_pt_BR.properties @@ -0,0 +1,9 @@ +# +# JXErrorPanel properties +# +JXErrorPane.details_expand_text=Detalhes >> +JXErrorPane.details_contract_text=Detalhes << +JXErrorPane.ok_button_text=Fechar +JXErrorPane.fatal_button_text=Sair da Aplica\u00E7\u00E3o +JXErrorPane.report_button_text=Reportar Erro +JXErrorPane.copy_to_clipboard_button_text=Copia para a \u00E1rea de Transfer\u00EAncia diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_sv.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_sv.properties new file mode 100644 index 0000000000..c28a689dd7 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_sv.properties @@ -0,0 +1,9 @@ +# +# JXErrorPanel properties +# +JXErrorPane.details_expand_text=Detaljer >> +JXErrorPane.details_contract_text=Detaljer << +JXErrorPane.ok_button_text=St\u00E4ng +JXErrorPane.fatal_button_text=Avsluta applikation +JXErrorPane.report_button_text=Rapportera fel +JXErrorPane.copy_to_clipboard_button_text=Kopiera till Urklipp diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane.properties new file mode 100644 index 0000000000..811b87e9f1 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane.properties @@ -0,0 +1,26 @@ +# Strings used by JXLoginDialog and JXLoginPane +JXLoginPane.okString=OK +JXLoginPane.okString.mnemonic=O +JXLoginPane.cancelString=Cancel +JXLoginPane.cancelString.mnemonic=C +JXLoginPane.stopString=Stop Login +JXLoginPane.stopString.mnemonic=S +JXLoginPane.promptString=Enter your user name and password +JXLoginPane.serverString=Server +JXLoginPane.nameString=User Name +JXLoginPane.loginString=Login +JXLoginPane.bannerString=Login +JXLoginPane.titleString=Login +JXLoginPane.passwordString=Password +JXLoginPane.rememberUserString=Remember Me +JXLoginPane.rememberPasswordString=Remember Password +JXLoginPane.loggingInString=Please wait, logging in... +JXLoginPane.failedString=Login failed; invalid user name/password combination. +JXLoginPane.enterUserNamePassword=Enter your user name and password +JXLoginPane.pleaseWait=Please wait, logging in... +JXLoginPane.cancelWait=Canceling login, please wait... +JXLoginPane.cancelLogin=Stop login +JXLoginPane.errorMessage=Couldn't log in

      \ + Check your user name and password. Check to see if \ + Caps Lock is turned on. +JXLoginPane.capsOnWarning=CapsLock seems to be ON! diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_cs.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_cs.properties new file mode 100644 index 0000000000..8817f7804f --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_cs.properties @@ -0,0 +1,26 @@ +#Czech resource bundle created by Vity (vitywap@seznam.cz) 08/2007 +# Strings used by JXLoginDialog and JXLoginPanel +JXLoginPane.okString=OK +JXLoginPane.okString.mnemonic=O +JXLoginPane.cancelString=Storno +JXLoginPane.cancelString.mnemonic=S +JXLoginPane.stopString=P\u0159eru\u0161it p\u0159ihla\u0161ov\u00e1n\u00ed +JXLoginPane.stopString.mnemonic=P +JXLoginPane.promptString=Zadejte sv\u00e9 u\u017eivatelsk\u00e9 jm\u00e9no a heslo +JXLoginPane.serverString=Server +JXLoginPane.nameString=U\u017eivatelsk\u00e9 Jm\u00e9no +JXLoginPane.loginString=P\u0159ihl\u00e1sit +JXLoginPane.bannerString=P\u0159ihl\u00e1\u0161en\u00ed +JXLoginPane.titleString=P\u0159ihl\u00e1\u0161en\u00ed +JXLoginPane.passwordString=Heslo +JXLoginPane.rememberUserString=Zapamatovat +JXLoginPane.rememberPasswordString=Zapamatovat Heslo +JXLoginPane.loggingInString=Pros\u00edm \u010dekejte, prob\u00edh\u00e1 p\u0159ihla\u0161ov\u00e1n\u00ed.... +JXLoginPane.failedString=P\u0159ihla\u0161ov\u00e1n\u00ed selhalo; \u0161patn\u00e9 u\u017eivatelsk\u00e9 jm\u00e9no nebo heslo. +JXLoginPane.enterUserNamePassword=Zadejte sv\u00e9 u\u017eivatelsk\u00e9 jm\u00e9no a heslo +JXLoginPane.pleaseWait=Pros\u00edm \u010dekejte, prob\u00edh\u00e1 p\u0159ihla\u0161ov\u00e1n\u00ed.... +JXLoginPane.cancelWait=Ukon\u010den\u00ed p\u0159ihla\u0161ov\u00e1n\u00ed, pros\u00edm \u010dekejte.... +JXLoginPane.cancelLogin=P\u0159eru\u0161it p\u0159ihla\u0161ov\u00e1n\u00ed +JXLoginPane.errorMessage=Nepoda\u0159ilo se p\u0159ihl\u00e1sit.

      \ + Pros\u00edm ov\u011b\u0159te sv\u00e9 u\u017eivatelsk\u00e9 jm\u00e9no a heslo. \ + P\u0159\u00edpadn\u011b zkontrolujte nen\u00ed-li zapnuta kl\u00e1vesa Caps Lock. diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_de.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_de.properties new file mode 100644 index 0000000000..297f6b11a5 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_de.properties @@ -0,0 +1,25 @@ +# Strings used by JXLoginDialog and JXLoginPane +JXLoginPane.okString=Ok +JXLoginPane.okString.mnemonic=O +JXLoginPane.cancelString=Abbrechen +JXLoginPane.cancelString.mnemonic=A +JXLoginPane.stopString=Anmeldung abbrechen +JXLoginPane.stopString.mnemonic=A +JXLoginPane.promptString=Bitte geben Sie Ihren Benutzernamen und Ihr Passwort ein +JXLoginPane.serverString=Server +JXLoginPane.nameString=Benutzername +JXLoginPane.loginString=Anmelden +JXLoginPane.bannerString=Anmeldung +JXLoginPane.titleString=Anmeldung +JXLoginPane.passwordString=Passwort +JXLoginPane.rememberUserString=Benutzernamen merken +JXLoginPane.rememberPasswordString=Passwort merken +JXLoginPane.loggingInString=Bitte warten, Sie werden angemeldet... +JXLoginPane.failedString=Anmeldung fehlgeschlagen; Der Benutzername mit diesem Passwort ist ung\u00FCltig. +JXLoginPane.enterUserNamePassword=Bitte geben Sie Ihren Benutzernamen und Ihr Passwort ein +JXLoginPane.pleaseWait=Bitte warten, Sie werden angemeldet... +JXLoginPane.cancelWait=Anmeldung wird abgebrochen, bitte warten... +JXLoginPane.cancelLogin=Anmeldung abbrechen +JXLoginPane.errorMessage=Anmeldung fehlgeschlagen

      \ + Bitte pr\u00FCfen Sie den Benutzernamen und das Passwort. +JXLoginPane.capsOnWarning=Achtung: Die Hochstelltaste ('CapsLock') ist aktiv ! diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_en.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_en.properties new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_es.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_es.properties new file mode 100644 index 0000000000..b81ca85f24 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_es.properties @@ -0,0 +1,28 @@ +# Spanish properties for SwingX +# JXLoginPane +# @author: Sergio Samayoa +# @author: Lola Traba Martínez + +JXLoginPane.okString=Aceptar +JXLoginPane.okString.mnemonic='A' +JXLoginPane.cancelString=Cancelar +JXLoginPane.cancelString.mnemonic='C' +JXLoginPane.nameString=Usuario +JXLoginPane.loginString=Conectar +JXLoginPane.passwordString=Contrase\u00F1a +JXLoginPane.rememberUserString=Recu\u00E9rdame +JXLoginPane.rememberPasswordString=Recordar contrase\u00F1a +JXLoginPane.loggingInString=Conectando, espere por favor... +JXLoginPane.bannerString=Conectar +JXLoginPane.cancelLogin=Cancelar conexi\u00F3n +JXLoginPane.cancelWait=Cancelando conexi\u00F3n, espere por favor... +JXLoginPane.capsOnWarning=\u00A1Bloqueo de may\u00FAsculas activado! +JXLoginPane.enterUserNamePassword=Ingrese su nombre de usuario y contrase\u00F1a +JXLoginPane.errorMessage=No fue posible autenticar.

      Compruebe su nombre de usuario y contrase\u00F1a. Compruebe si el bloqueo de may\u00FAsculas est\u00E1 activado. +JXLoginPane.failedString=Fall\u00F3 la conexi\u00F3n; la combinaci\u00F3n de nombre de usuario/contrase\u00F1a es inv\u00E1lida. +JXLoginPane.pleaseWait=Espere por favor... +JXLoginPane.promptString=Ingrese su nombre de usuario y contrase\u00F1a +JXLoginPane.serverString=Servidor +JXLoginPane.stopString=Detener conexi\u00F3n +JXLoginPane.stopString.mnemonic=D +JXLoginPane.titleString=Conectar \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_fr.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_fr.properties new file mode 100644 index 0000000000..4ad9344f4a --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_fr.properties @@ -0,0 +1,25 @@ +# Strings used by JXLoginDialog and JXLoginPanel +JXLoginPane.okString=OK +JXLoginPane.okString.mnemonic='O' +JXLoginPane.cancelString=Annuler +JXLoginPane.cancelString.mnemonic='A' +JXLoginPane.stopString=Interrompre +JXLoginPane.stopString.mnemonic='I' +JXLoginPane.promptString=Entrez votre login et mot de passe +JXLoginPane.serverString=Serveur +JXLoginPane.nameString=Nom +JXLoginPane.loginString=Identifiant +JXLoginPane.bannerString=Identifiant +JXLoginPane.titleString=Identifiant +JXLoginPane.passwordString=Mot de passe +JXLoginPane.rememberUserString=Se souvenir de moi +JXLoginPane.rememberPasswordString=Se souvenir du mot de passe +JXLoginPane.loggingInString=Veuillez patienter, authentification en cours.... +JXLoginPane.failedString=\u00C9ched d'authentification. Identifiant ou mot de passe invalide. +JXLoginPane.enterUserNamePassword=Entrez votre identifiant et mot de pass +JXLoginPane.pleaseWait=Veuillez patienter, authentification en cours.... +JXLoginPane.cancelWait=Annulation de la connexion, veuillez patienter.... +JXLoginPane.cancelLogin=Interrompre +JXLoginPane.errorMessage=\u00C9chec d'authentification

      \ + V\u00E9rifiez votre identifiant et votre mot de passe. \ + V\u00E9rifiez que la touche Verrouill. Maj. est d\u00E9sactiv\u00E9e. diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_it.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_it.properties new file mode 100644 index 0000000000..d4225a534a --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_it.properties @@ -0,0 +1,26 @@ +# Strings used by JXLoginDialog and JXLoginPanel +JXLoginPane.okString=OK +JXLoginPane.okString.mnemonic=O +JXLoginPane.cancelString=Annulla +JXLoginPane.cancelString.mnemonic=C +JXLoginPane.stopString=Stop Login +JXLoginPane.stopString.mnemonic=S +JXLoginPane.promptString=Immetti il tuo Nome Utente e Password +JXLoginPane.serverString=Server +JXLoginPane.nameString=Nome Utente +JXLoginPane.loginString=Login +JXLoginPane.bannerString=Identificativo Utente +JXLoginPane.titleString=Identificativo Utente +JXLoginPane.passwordString=Password +JXLoginPane.rememberUserString=Ricordami +JXLoginPane.rememberPasswordString=Ricorda la Password +JXLoginPane.loggingInString=Attendere, autenticazione in corso.... +JXLoginPane.failedString=Login fallito; nome utente/password non validi. +JXLoginPane.enterUserNamePassword=Immetti il tuo Nome Utente e Password +JXLoginPane.pleaseWait=Attendere, autenticazione in corso.... +JXLoginPane.cancelWait=Annullamento login, attendere.... +JXLoginPane.cancelLogin=Stop login +JXLoginPane.errorMessage=Autenticazione fallita

      \ + Controlla il tuo Nome Utente/Password. Controlla che Bloc-Maiusc \ + non sia attivo. + diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_nl.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_nl.properties new file mode 100644 index 0000000000..d7f09b9488 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_nl.properties @@ -0,0 +1,26 @@ +# Strings used by JXLoginDialog and JXLoginPane +JXLoginPane.okString=OK +JXLoginPane.okString.mnemonic=O +JXLoginPane.cancelString=Annuleren +JXLoginPane.cancelString.mnemonic=A +JXLoginPane.stopString=Stop Login +JXLoginPane.stopString.mnemonic=S +JXLoginPane.promptString=Voer uw gebruikersnaam en wachtwoord in +JXLoginPane.serverString=Server +JXLoginPane.nameString=Gebruikersnaam +JXLoginPane.loginString=Login +JXLoginPane.bannerString=Login +JXLoginPane.titleString=Login +JXLoginPane.passwordString=Wachtwoord +JXLoginPane.rememberUserString=Onthouden +JXLoginPane.rememberPasswordString=Onthoud wachtwoord +JXLoginPane.loggingInString=Even geduld, er wordt nu ingelogd... +JXLoginPane.failedString=Inloggen mislukt; ongeldige gebruikersnaam/wachtwoord combinatie. +JXLoginPane.enterUserNamePassword=Voer uw gebruikersnaam en wachtwoord in +JXLoginPane.pleaseWait=Even geduld, er wordt nu ingelogd... +JXLoginPane.cancelWait=Login wordt geannuleerd, even geduld... +JXLoginPane.cancelLogin=Stop login +JXLoginPane.errorMessage=Kan niet inloggen

      \ + Controleer uw gebruikersnaam en wachtwoord. Controleer of Caps Lock aan \ + staat. +JXLoginPane.capsOnWarning=CapsLock staat AAN! diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_pl.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_pl.properties new file mode 100644 index 0000000000..6b4bc963fa --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_pl.properties @@ -0,0 +1,24 @@ +# Strings used by JXLoginDialog and JXLoginPane +JXLoginPane.okString=OK +JXLoginPane.okString.mnemonic=O +JXLoginPane.cancelString=Anuluj +JXLoginPane.cancelString.mnemonic=A +JXLoginPane.stopString=Przerwij Logowanie +JXLoginPane.stopString.mnemonic=P +JXLoginPane.promptString= Podaj nazw\u0119 u\u017Cytkownika oraz has\u0142o +JXLoginPane.serverString=Serwer +JXLoginPane.nameString=Nazwa u\u017Cytkownika +JXLoginPane.loginString=Logowanie +JXLoginPane.bannerString=Logowanie +JXLoginPane.titleString=Logowanie +JXLoginPane.passwordString=Has\u0142o +JXLoginPane.rememberUserString=Zapami\u0119taj mnie +JXLoginPane.rememberPasswordString=Zapami\u0119taj has\u0142o +JXLoginPane.loggingInString=Prosz\u0119 czeka\u0107, trwa logowanie... +JXLoginPane.failedString=Nieudane logowanie; nieprawid\u0142owa nazwa u\u017Cytkownika lub has\u0142o. +JXLoginPane.enterUserNamePassword=Podaj nazw\u0119 u\u017Cytkownika oraz has\u0142o +JXLoginPane.pleaseWait=Prosz\u0119 czeka\u0107, trwa logowanie... +JXLoginPane.cancelWait=Anulowanie logowania, prosz\u0119 czeka\u0107... +JXLoginPane.cancelLogin=Anuluj logowanie +JXLoginPane.errorMessage=Nieudane logowanie

      Sprawd\u017A nazw\u0119 u\u017Cytkownika oraz has\u0142o. Sprawd\u017A czy Caps Lock jest
      wcisni\u0119ty. +JXLoginPane.capsOnWarning=Uwaga: CapsLock jest aktywny! diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_pt_BR.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_pt_BR.properties new file mode 100644 index 0000000000..907007d911 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_pt_BR.properties @@ -0,0 +1,23 @@ +# Strings used by JXLoginDialog and JXLoginPanel +JXLoginPane.okString=OK +JXLoginPane.okString.mnemonic='O' +JXLoginPane.cancelString=Cancelar +JXLoginPane.cancelString.mnemonic='C' +JXLoginPane.stopString=Interromper Autentica\u00E7\u00E3o +JXLoginPane.stopString.mnemonic=P +JXLoginPane.promptString=Informe seu usu\u00E1rio e senha +JXLoginPane.serverString=Servidor +JXLoginPane.nameString=Usu\u00E1rio +JXLoginPane.loginString=Ok +JXLoginPane.bannerString=Autentica\u00E7\u00E3o +JXLoginPane.titleString=Autentica\u00E7\u00E3o +JXLoginPane.passwordString=Senha +JXLoginPane.rememberUserString=Lembrar usu\u00E1rio +JXLoginPane.rememberPasswordString=Lembrar senha +JXLoginPane.loggingInString=Aguarde, autenticando... +JXLoginPane.failedString=Autentica\u00E7\u00E3o falhou; combina\u00E7\u00E3o usu\u00E1rio/senha inv\u00E1lida. +JXLoginPane.enterUserNamePassword=Informe seu usu\u00E1rio e senha +JXLoginPane.pleaseWait=Aguarde, autenticando... +JXLoginPane.cancelWait=Aguarde, cancelando autentica\u00E7\u00E3o... +JXLoginPane.cancelLogin=Parar autentica\u00E7\u00E3o +JXLoginPane.errorMessage=N\u00E3o foi poss\u00edvel autenticar.

      Confira usu\u00E1rio e senha e verifique se Caps Lock est\u00E1 ativado. diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_sv.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_sv.properties new file mode 100644 index 0000000000..725cd5ab3a --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_sv.properties @@ -0,0 +1,26 @@ +# Strings used by JXLoginDialog and JXLoginPanel +JXLoginPane.okString=OK +JXLoginPane.okString.mnemonic=O +JXLoginPane.cancelString=Avbryt +JXLoginPane.cancelString.mnemonic=v +JXLoginPane.stopString=Avbryt inloggning +JXLoginPane.stopString.mnemonic=v +JXLoginPane.promptString=Ange ditt anv\u00E4ndarnamn och l\u00F6senord +JXLoginPane.serverString=Server +JXLoginPane.nameString=Anv\u00E4ndarnamn +JXLoginPane.loginString=Inloggning +JXLoginPane.bannerString=Inloggning +JXLoginPane.titleString=Inloggning +JXLoginPane.passwordString=L\u00F6senord +JXLoginPane.rememberUserString=Kom ih\u00E5g mig +JXLoginPane.rememberPasswordString=Kom ih\u00E5g l\u00F6senord +JXLoginPane.loggingInString=Var god v\u00E4nta, inloggning p\u00E5g\u00E5r.... +JXLoginPane.failedString=Inloggningen misslyckades; felaktig kombination av anv\u00E4ndarnamn/l\u00F6senord. +JXLoginPane.enterUserNamePassword=Ange ditt anv\u00E4ndarnamn och l\u00F6senord +JXLoginPane.pleaseWait=Var god v\u00E4nta, inloggning p\u00E5g\u00E5r.... +JXLoginPane.cancelWait=Avbryter inloggningen, var god v\u00E4nta.... +JXLoginPane.cancelLogin=Avbryt inloggning +JXLoginPane.errorMessage=Inloggningen misslyckades

      \ + Kontrollera ditt anv\u00E4ndarnamn och l\u00F6senord. Kontrollera om Caps Lock \ + \u00E4r aktiverat. +JXLoginPane.capsOnWarning=Caps Lock \u00E4r aktiverat \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/SearchField.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/SearchField.properties new file mode 100644 index 0000000000..dd11693de0 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/SearchField.properties @@ -0,0 +1,4 @@ +SearchField.prompt=Search +SearchField.recentsMenuTitle=Recent Searches +SearchField.clearRecentsText=Clear Recent Searches +SearchField.noRecentsText=No Recent Searches diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/SearchField_de.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/SearchField_de.properties new file mode 100644 index 0000000000..b0db945076 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/SearchField_de.properties @@ -0,0 +1,4 @@ +SearchField.prompt=Suchen +SearchField.recentsMenuTitle=Letzte Sucheinteäge +SearchField.clearRecentsText=Letzte Sucheinträge Löschen +SearchField.noRecentsText=Keine letzten Sucheinträge diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/SearchField_en.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/SearchField_en.properties new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay.properties new file mode 100644 index 0000000000..949d2de8fa --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay.properties @@ -0,0 +1,6 @@ +TipOfTheDay.dialogTitle=Tip of the Day +TipOfTheDay.didYouKnowText=Did you know... +TipOfTheDay.showOnStartupText=Show tips on startup +TipOfTheDay.previousTipText=< Back +TipOfTheDay.nextTipText=Next > +TipOfTheDay.closeText=Close diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay24.gif b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay24.gif new file mode 100644 index 0000000000000000000000000000000000000000..9376ede8a89675708ff2c429dc2af31f88d1c476 GIT binary patch literal 742 zcmZ?wbhEHblwgoxc=n$G2>$>7-`F_4u&|-1sG+vDt)`|qFR$?QnV{=8qCb6NSiKsk zfOp3Z-s{(O4 zJ?!k+unQNGE?!K&dpGC7gQ|xQ8?Ifey>_kc@#FRzH`<;&>A!WW_tmSVuU;*G^=iev zd-Lz#U-b6vrnhf5KYqM1EiLfr(;ZKqY)?-Q{rvfCadBcvNpfap?Dy~Y%gb}$zPfta-~h<0gAXYFWMO1r@MX{eISmvi4D25oe4CnETHD%Nd>FdA7`;2%y2bT%HPr;W z`zJIpNE&GAXlba*Df0Qun&4xgXJpF4s-Yw+!N54%M_kL;(wd1`TSY;PciRL8eH~L9 zMr%_YbvY@46{~c$jP^3IXdMz2KGwmYsj+tb#?2D^S37*vR5cc`u`iJkda(VGpq!Gj zx}B|^$^wH#M(aXFtA-y7n2)nHy$;%1+_3NvuasGmPep)^ydI0NLXM1NUa&BOph!>6 zfddPU_;!mK1O&WUa8k{u&VXsck|(|l4ow1@MJ%1ZlPz0$3>Pg~etfp~3=Y*-pVKC< z=w21I_tlG+*VZSPvobL#{_}UvFQ_caOwTA$FfuSOP)Mp&2rkW2@Xbsv$}g@gE=kQT y)=}`xOV(3x%*jy*0x2w32udwZEhL literal 0 HcmV?d00001 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_cs.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_cs.properties new file mode 100644 index 0000000000..dc596dee4b --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_cs.properties @@ -0,0 +1,7 @@ +#Czech resource bundle created by Vity (vitywap@seznam.cz) 08/2007 +TipOfTheDay.dialogTitle=Tip dne +TipOfTheDay.didYouKnowText=V\u011bd\u011bli jste, \u017ee... +TipOfTheDay.showOnStartupText=Zobrazovat tipy p\u0159i startu +TipOfTheDay.previousTipText=< P\u0159edchoz\u00ed +TipOfTheDay.nextTipText=Dal\u0161\u00ed > +TipOfTheDay.closeText=Zav\u0159\u00edt \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_de.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_de.properties new file mode 100644 index 0000000000..48ae901d24 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_de.properties @@ -0,0 +1,6 @@ +TipOfTheDay.dialogTitle=Tipp des Tages +TipOfTheDay.didYouKnowText=Wussten Sie schon...? +TipOfTheDay.showOnStartupText=Tipps beim Start anzeigen +TipOfTheDay.previousTipText=< Zur\u00fcck +TipOfTheDay.nextTipText=Weiter > +TipOfTheDay.closeText=Schlie\u00dfen diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_en.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_en.properties new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_es.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_es.properties new file mode 100644 index 0000000000..f78a8dd791 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_es.properties @@ -0,0 +1,6 @@ +TipOfTheDay.dialogTitle=Sugerencia del D\u00eda +TipOfTheDay.didYouKnowText=Sab\u00edas que ... +TipOfTheDay.showOnStartupText=Mostrar sugerencias al inicio +TipOfTheDay.previousTipText=< Anterior +TipOfTheDay.nextTipText=Siguiente > +TipOfTheDay.closeText=Cerrar diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_fr.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_fr.properties new file mode 100644 index 0000000000..bb9e32e748 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_fr.properties @@ -0,0 +1,6 @@ +TipOfTheDay.dialogTitle=Astuce du Jour +TipOfTheDay.didYouKnowText=Le saviez-vous... +TipOfTheDay.showOnStartupText=Afficher au d\u00e9marrage +TipOfTheDay.previousTipText=< Pr\u00e9c\u00e9dente +TipOfTheDay.nextTipText=Suivante > +TipOfTheDay.closeText=Fermer diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_it.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_it.properties new file mode 100644 index 0000000000..45ca54a1e6 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_it.properties @@ -0,0 +1,6 @@ +TipOfTheDay.dialogTitle=Consiglio del Giorno +TipOfTheDay.didYouKnowText=Lo sapevi che... +TipOfTheDay.showOnStartupText=Visualizza Consigli all'Avvio +TipOfTheDay.previousTipText=< Precedente +TipOfTheDay.nextTipText=Successivo > +TipOfTheDay.closeText=Chiudi diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_nl.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_nl.properties new file mode 100644 index 0000000000..2ab13942af --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_nl.properties @@ -0,0 +1,6 @@ +TipOfTheDay.dialogTitle=Tip van de dag +TipOfTheDay.didYouKnowText=Wist u dat... +TipOfTheDay.showOnStartupText=Laat tips zien bij opstarten +TipOfTheDay.previousTipText=< Vorige +TipOfTheDay.nextTipText=Volgende > +TipOfTheDay.closeText=Sluiten diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_pl.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_pl.properties new file mode 100644 index 0000000000..8b30532abe --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_pl.properties @@ -0,0 +1,6 @@ +TipOfTheDay.dialogTitle=Porada dnia +TipOfTheDay.didYouKnowText=Czy wiesz \u017Ce... +TipOfTheDay.showOnStartupText=Pokazuj porady przy uruchomieniu +TipOfTheDay.previousTipText=< Wstecz +TipOfTheDay.nextTipText=Dalej > +TipOfTheDay.closeText=Zamknij diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_pt_BR.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_pt_BR.properties new file mode 100644 index 0000000000..71e1c32617 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_pt_BR.properties @@ -0,0 +1,6 @@ +TipOfTheDay.dialogTitle=Dica do Dia +TipOfTheDay.didYouKnowText=Voc\u00EA sabia... +TipOfTheDay.showOnStartupText=Mostrar dicas ao iniciar +TipOfTheDay.previousTipText=< Anterior +TipOfTheDay.nextTipText=Pr\u00F3xima > +TipOfTheDay.closeText=Fechar diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_sv.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_sv.properties new file mode 100644 index 0000000000..47d1beaae6 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_sv.properties @@ -0,0 +1,6 @@ +TipOfTheDay.dialogTitle=Dagens tips +TipOfTheDay.didYouKnowText=Visste du... +TipOfTheDay.showOnStartupText=Visa tips vid start +TipOfTheDay.previousTipText=< Tillbaka +TipOfTheDay.nextTipText=N\u00E4sta > +TipOfTheDay.closeText=St\u00E4ng diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/clear.gif b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/clear.gif new file mode 100644 index 0000000000000000000000000000000000000000..45b8481eeb901977ce6128c597dfc86a37bc16ef GIT binary patch literal 1026 zcmZ?wbhEHb6krfwcwWz->RYYqU$5q0ukK%~9@wB0(xegEsT10&AKGXT)?^soWE|P1 z6Fb#7rpGL%LpOe=UcyYvxOS_!cGLI?#z`|Q6Zz7|`SaGd!<@KgjH=0*nZ(e<~WzDVDwYS^W-DzKcw`0S-&W-oGHa+Ox{IF-squ#BL zXKcRIzy0aNozExjdNF0stA)F+P2c}^)}ap@j@;gM=IMo-k1yVOdg;#dYxkaAzxVw5 z{TJ8ozr6n7)eRte^ybc^S2rKOzx(9vy(h2lJbi!f>AQPC^7+U6FFrng_4&!`&rjce z{rUU%|4}f|A)xq^g^`P)fkB4>2tav)f#W^{<5vfU1q%)~bJlRk2rlE1G)&l6a55>C zOGH_wP2q;nGjDO-@D&b)hgdo%Dwv5d+-O?rC8Fs&Nk%a!#l26;h|4Z>(-R+gRqK)u z7n0jtxdd2y{|FpELiZ2e;&W=qd%3!j)ez#X6${U z*cf?aj-ix+IFF+hOV=eup9O3a9<+2_-;liDZs7uU7IFQe*Z1x|ViaiPyr=Z#p}GQt FH2`9P#s&ZY literal 0 HcmV?d00001 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/clear_pressed.gif b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/clear_pressed.gif new file mode 100644 index 0000000000000000000000000000000000000000..acebf5e2a53fe597b0ee2cafa372ec60e9ce230d GIT binary patch literal 1032 zcmZ?wbhEHb6krfwc;3jsrWeDmAIo77$7vYPWt6~eoXBI6#A}+&XO_Zmo+@CGCTx)+ zXqhf#nIUAAA#9x~Vv{9mlPzkSEpD4BW|t#wpDSUXBVnH>;gBckm@nm2AmfxP?OZ71 zTp;68Bsl=5RwD0KD(_yZ;8CXNS+3|+rQ}ti;#I2RRi@-!t?XT?>{FxSQ>Erp zuHsv(>RYYuTdCn!sp?;^;a{!gU!@+>p%+}Q5!#^<+NmGbY!KFD7~W(Y(PR|aW)j(= z6Fb!?wof;Hre4BK)A)YFgz3gfGYrxe8>TI`OrLF?IoCFSrCH%t`@&VWW!r7bc34#& zvZ*_jT0hsZR?)w*Vfv=V8Jn7BY_3~ytaa(hg&U77 z+H`dB=3`4XA6>fj__D3Xmu@??eEZ3jJ5DX%d2ZFNGi!FAS+VEx>OB|M?m4?=@5Oa{ z&#l>ib?t#`8xCCFc=*QpBX>3*zkBrj?PC}29KQ%ecTZdbqI)N=JUn&n$&a7Ee*XUb ze-sQ{2q^w!VdP@?&!EEq1fV>@!10vfe`q9wLPG;13zvjNgTlc!o&RfOe0FR|WMbu! zv1wS4+T_$GZk#v8Vc{WGab82K4GfA*C;23dG9Elw*yzR)bZkM#OeP*N0Y#S{g~*4@ zGp$ooj&L-m^Y#jA>u&jksR449* z!Qu9PDS6{h=NdL1V(XJ}y!2(}=VkuVN|r*KS(%n_NZXf8QJMHyy-)C1PJhO?q~)E@ zR7>|MTs$+aGgHmq&$w`r)Gc)O H!e9*m+pLa< literal 0 HcmV?d00001 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/clear_rollover.gif b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/clear_rollover.gif new file mode 100644 index 0000000000000000000000000000000000000000..b9b298f8e3cb5ac361c83393ae38707389bc3842 GIT binary patch literal 1025 zcmZ?wbhEHb6krfwcwWz-mpIEHd5%HqLc{dMCg}?-GUi!k&Nt0oZk09PCU3cE;TD_1 zwN3?#?F&~p6t8h9UF}}B%C%ynW91H)nw_3CTfJ(x**2baYB?9!w9B>ad|=Do;I{p) z{X1PJ?DFZp za5Zn?t^7r|3zyt2U3xos!~fRBN7@!2DOvuYeaX?%6%Wf++%H@CsC?yv@>P$cxBQRU z^1ovBld84Ps@FZQU-zhH{fpWSFY7kGs^9dwVe^~DEpMB)yldL}u4U_+=56m=wtr~d z@iF1_x3-<163%>U-}$+H*XNGiUpn`E?b`dTd+*ooecyWz{OmjUYr^3_X*d2)Jp5GX@$7k*Z~|5o+>`}Rv8TmJsPbobTe2XAjYdUNy9o2!pM z-FW=|=93S%o_)IY?907p@9#bTbpQE>yDz@nfARUw%kTGJetGcf``tHxAH4bT`0e*6 zZ+|>`|M%(pUq65U{XYr@Is_DdvM_QnG%)Be00AgZFmT*sU_7b8uwcQ#X0|krBL|*# zPuGiF7cudXOQ)c$V3x>=O|0EhH3O!}C<+~E=abeJU|1m3$~j5JsqV{xiH}{mMb)^( zBtI?okyp@fiK#sMGx2kSLqn6f_mTyl)%j-I7f8skFgP)@ zvI|`K!>C>Mriq!EMa5#XnJ+VA1*=HNfpDf-_GNeeWEwZFZ)y_LiHZ2Eex9Ab^xvM7 z6Lu`KLZ*U+5Lu!Sk^o_Z5E4Meg@_7P6crJiNL9pw)e1;Xm069{HJUZAPk55R%$-RIA z6-eL&AQ0xu!e<4=008gy@A0LT~suv4>S3ILP<0Bm`DLLvaF4FK%)Nj?Pt*r}7;7Xa9z9H|HZjR63e zC`Tj$K)V27Re@400>HumpsYY5E(E}?0f1SyGDiY{y#)Yvj#!WnKwtoXnL;eg03bL5 z07D)V%>y7z1E4U{zu>7~aD})?0RX_umCct+(lZpemCzb@^6=o|A>zVpu|i=NDG+7} zl4`aK{0#b-!z=TL9Wt0BGO&T{GJWpjryhdijfaIQ&2!o}p04JRKYg3k&Tf zVxhe-O!X z{f;To;xw^bEES6JSc$k$B2CA6xl)ltA<32E66t?3@gJ7`36pmX0IY^jz)rRYwaaY4 ze(nJRiw;=Qb^t(r^DT@T3y}a2XEZW-_W%Hszxj_qD**t_m!#tW0KDiJT&R>6OvVTR z07RgHDzHHZ48atvzz&?j9lXF70$~P3Knx_nJP<+#`N z#-MZ2bTkiLfR>_b(HgWKJ%F~Nr_oF3b#wrIijHG|(J>BYjM-sajE6;FiC7vY#};Gd zST$CUHDeuEH+B^pz@B062qXfFfD`NpUW5?BY=V%GM_5c)L#QR}BeW8_2v-S%gfYS= zB9o|3v?Y2H`NVi)In3rTB8+ej^> zQ=~r95NVuDChL%G$=>7$vVg20myx%S50Foi`^m%Pw-h?Xh~i8Mq9jtJloCocWk2Nv zrJpiFnV_ms&8eQ$2&#xWpIS+6pmtC%Q-`S&GF4Q#^mhymh7E(qNMa}%YZ-ePrx>>xFPTiH1=E+A$W$=bG8>s^ zm=Bn5Rah$aDtr}@$`X}2l~$F0mFKEdRdZE8)p@E5RI61Ft6o-prbbn>P~)iy)E2AN zsU20jsWz_8Qg>31P|s0cqrPALg8E|(vWA65poU1JRAaZs8I2(p#xiB`SVGovRs-uS zYnV-9TeA7=Om+qP8+I>yOjAR1s%ETak!GFdam@h^# z)@rS0t$wXH+Irf)+G6c;?H29p+V6F6oj{!|o%K3xI`?%6x;DB|x`n#ibhIR?(H}Q3Gzd138Ei2)WAMz7W9Vy`X}HnwgyEn!VS)>mv$8&{hQn>w4zwy3R}t;BYlZQm5)6pty=DfLrs+A-|>>;~;Q z_F?uV_HFjh9n2gO9o9Q^JA86v({H5aB!kjoO6 zc9$1ZZKsN-Zl8L~mE{`ly3)1N^`o1+o7}D0ZPeY&J;i;i`%NyJ8_8Y6J?}yE@b_5a zam?eLr<8@mESk|3$_SkmS{wQ>%qC18))9_|&j{ZT zes8AvOzF(F2#DZEY>2oYX&IRp`F#{ADl)1r>QS^)ba8a|EY_^#S^HO&t^Rgqwv=MZThqqEWH8 zxJo>d=ABlR_Bh=;eM9Tw|Ih34~oTE|= zX_mAr*D$vzw@+p(E0Yc6dFE}(8oqt`+R{gE3x4zjX+Sb3_cYE^= zgB=w+-tUy`ytONMS8KgRef4hA?t0j zufM;t32jm~jUGrkaOInTZ`zyfns>EuS}G30LFK_G-==(f<51|K&cocp&EJ`SxAh3? zNO>#LI=^+SEu(FqJ)ynt=!~PC9bO$rzPJB=?=j6w@a-(u02P7 zaQ)#(uUl{HW%tYNS3ItC^iAtK(eKlL`f9+{bJzISE?u8_z3;~C8@FyI-5j_jy7l;W z_U#vU3hqqYU3!mrul&B+{ptt$59)uk{;_4iZQ%G|z+lhASr6|H35TBkl>gI*;nGLU zN7W-nBaM%pA0HbH8olyl&XeJ%vZoWz%6?Y=dFykl=imL}`%BMQ{Mhgd`HRoLu6e2R za__6DuR6yg#~-}Tc|Gx_{H@O0eebyMy5GmWADJlpK>kqk(fVV@r_fLLKIeS?{4e)} z^ZO;zpECde00d`2O+f$vv5tKEQIh}w03c&XQcVB=dL;k=fP(-4`Tqa_faw4Lbua(` z>RI+y?e7jKeZ#YO-C0~<+1K~#9!G}BLLlXo1)@%Q(6k~U9pO<0?DuCY3$E7%U* z2AdlQs}v?4I>x}$5IywJV3TM{OxvF| zHh$#n-u=1d`%L!r;iS_%&Sq)6caOmH$k*%K?(fIBe3@#kMs#9=Xei_?dETot#{9^? zO-(nS?cc8^nbb$Al$MN9Hwbj>{CS-f(eDQabiQ0xRn_Hl=X5a?(o8I-!;Vw8;C-fJ zvA-U-wrVkv&~?z6)2FrMdHUSvr zp;p5HemIPE;|2>TUEh4m8@;^%EDQ{gzkZ#NWnqGVsxcr!BkT)Re7~xy7!ewbp$Oz( zYNE@s05HHxBEc5ms8SCCDyo!32q0|ve(D8K9|TOFI>nn;uHg7S?#Kux2Fn7Me7qU(}8ae|KH$5}Fl zjq!0jU`m7ss^3{-sy96-7J!ywkv*!+q|-dSb`1e?!^2FaQpCz-B5P|DfIKjv#s%)m zS%HMeP*VA7OO=+M9x9m(VL(LiRq=pGcQ?7&Swg_CBHUH|61ZR+fjMPrQzYIFM~beC z2h_K#Znv?%jseDjQTPqG095Q{U=^5!(dQzy29dWKAZmau;Kequ1pEU2eHa1KzzTru zyeb0h0t(Q*2hOy?dkyfi2i6+YbMTLWTfooTE&%@x0E%yq_?hqOR{#J207*qoM6N<$ Ef=!;=y8r+H literal 0 HcmV?d00001 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/month-down.png b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/month-down.png new file mode 100644 index 0000000000000000000000000000000000000000..2d21e24454667a2d863104da25695602871f4375 GIT binary patch literal 3138 zcmV-I488M-P)KLZ*U+DPS_9i&56Iack~*0${?rBqwA zbZoLZbZI-x{_&l!W7+oidpv&MzdrMQd_LdL_w#zb-}9IOG}_oAkw5`JT)b2q;N?aS z4GX6aX@nvap#xPIusITuD~sia!7l=McMAjn+^}YgL<0P8m!>#0EF2)L0O-b$V;2Cr zQRFxXfbKG>ND2_K0CYLAY%V~g0I(7V1uy`j7=UIBIj#br8AXnN0-%}1iID=t9e}12 z&*jGhiXDKmGnXgf0BTkMxLgS*4p7Sli0-&JAs5id1z^DuiKT!>6#(nduy8v0%3K@3 z$r_-I?7g=%4JgY7jCFeNjkf>{qJgqMd+)st00Dr|tCvLE+mQ*Xy8%k~WwO`iz>qxP z^JbasW1&p;xd@;&0pANaiQ*(GZ=w$Px6kC6$nYYiJdLf^p9XtXiiWSptl zcnix36RoUmCfQE5v$vn(;52pWwCOWuInQ=+b#r65yL)(gdCl?m@%8na%bdqz`3D3B z1_g)ApC1|)9ucu%!NNt6i`h|894;?9CN`G8WT_x7UMNZsOQeZO%a$jvO!+D`ZB<6b z>dZB3*JfpZy>8w54I6WEHs$7S&fBu(KU=qL-@aq#u3fwL?A^OBzo1}$VbOsD2M--O zTwHSGNa@jI$B&;VJ9)DFn~I84rz@+ft82ckJ#+T#ci*2o|HFlg7k|9;)8(Ikxl&hm zwf@@mh8s5)pHenwsz5|E=Z0gNKhE|Nf-yY5O0~p10{j69tVsDPbya%C>eZQR)@Eg8f4y$~`VAX%Hf_q? zoR>!huu~pD{@?+Wmw!`n>U3pgRdvm`wY6vD=f5C7|1VeSuGU|>euFyyy?af~_kU|? z?R)$eov+@!d;dWOWmR=e?P0@p_4Ex4M;VPCV@x+0JI>V1%-npug{9?$2@|bsCQY)P zY-c~k!I7+lnKP+cV90AhUJT56ES5i24xv;Ui+bvzPaJ*HAPXWrV|8ZcnzcP~H<1!~ZQ4)ibzbeO>gPH@9qgnTRJBi<@Z zRPa*ZD P(Tr%3v~*g#;uOU+#X6EgiwoOV-!dKW*@7Vy59NO=p_($JuXO5?r3UwK3YjGTGcFG_WAYc8y@C(ZPv+Kx#iJT*0$Q6yLYSaN!$A- ze@S6@k^VvdL-obXlB=cu$HPywoaC3ksz^PpR=Km!jXz4or{Zf%SVUtK?wxwcFfR`~dmn(quPe(5D`{=WY+#Y$#yXechi@!ak~$@w@;^Ip|_=Yv`D;+5Me4 zCrD?0g}@??8()|(HsJ?J$zXjxnDkZ_CB64eL2F@nk$zDl)#?4MD|hzQK7Y7>w_m+p zUv%B4VRS=huXyvF{^n2bwpVu~Jqw}SU+p60-L21F9HCDGK2!5nC{1L z$N%?=6C_f5XaP{?OTB{t=mNM9E%pom;0EAMd=zsY03CoZA$3=YI#BOY|LfDR7C;6nl_ z*hs{3&>;XH2}lGVT<{=~uTO*kJS0L4Pp~0I40y1X?=Wzm5Ilky{4dPlg9~9KKSLZ> z9-Xv9m^?is*0$94sqH7w<+FWzcH|+x=Sk%5QPk(F9hDtDD;Pabr}xZ$1TkX%_8!)V z1{-1|fd>he;&+G<7s=1)T?g{N2LLE*1A80*pvI|XK%Gc=%cTIo5Q>t;{FqoN-BlzK z@aPO-oG4Ms6I;=}<2g1~bURyH2LSjB$yWB2$fH0R00004XF*Lt007wQ^&Awc0000W zV@Og>003>600482008hD004+h004H!007aO001)|0010z3vYEj0008zNkl19;`(#kW2SEh;(ooPh zB_b^?XsRNl?L)EgvkwJL&?iNaJ{V~5L5yy8H#2w6y?w}Lk~Il#F8sN3hkJkLoH=v< z31bZX{r#N1c)1hE0&}Q*guuCu{cRlpzVCxEhW6~yV`t9)XomAeqat0gih(j+KmTgl zvq!({>+8c9g9TvQ*5N(7TCl2;1fnaJNhH|4a~qaroj7~(vJKz>7{fd_LiYB)c~&@fCMB&Ym7vg_YjAMa?G8Zj@Nd=0Rs!?*H!_eB?E|Pl+q|AQ|kHi z0XmL+kN-tm*?SRP7SI`ch_qld=yy_l@nVp3=exOdy%#(WXYmq#`lW}xZQB7zR#{U6 zMKqB%F>qjDkgf|?>F&7+o=5uAm1O3)9Ne{q?G0-PRWzDN(wPouP+Ft4;mWl?In{Xq zJdb&+S5Y(D4TruEH zVWbR@N;;Sc|3+C;0u>R25smA8S{pY&Dg&tur@MaR>pjQ0(sPF(jLHQC-|AASE)7#Sf`TZ4$i=O*5U@up}~iaH)K5@DnSKG5_r-+Z~8yH5sLT3<^z z))Z}~&QKeTh?&&2`1^)A9NVUOg~!``k%{Lf0UgKA{r2qf`07*qoM6N<$f`*a!761SM literal 0 HcmV?d00001 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/month-up.png b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/month-up.png new file mode 100644 index 0000000000000000000000000000000000000000..2c270393b95a9605d26329cb15debec2aebaba04 GIT binary patch literal 3139 zcmV-J47~G+P)KLZ*U+DPS_9i&56Iack~*0${?rBqwA zbZoLZbZI-x{_&l!W7+oidpv&MzdrMQd_LdL_w#zb-}9IOG}_oAkw5`JT)b2q;N?aS z4GX6aX@nvap#xPIusITuD~sia!7l=McMAjn+^}YgL<0P8m!>#0EF2)L0O-b$V;2Cr zQRFxXfbKG>ND2_K0CYLAY%V~g0I(7V1uy`j7=UIBIj#br8AXnN0-%}1iID=t9e}12 z&*jGhiXDKmGnXgf0BTkMxLgS*4p7Sli0-&JAs5id1z^DuiKT!>6#(nduy8v0%3K@3 z$r_-I?7g=%4JgY7jCFeNjkf>{qJgqMd+)st00Dr|tCvLE+mQ*Xy8%k~WwO`iz>qxP z^JbasW1&p;xd@;&0pANaiQ*(GZ=w$Px6kC6$nYYiJdLf^p9XtXiiWSptl zcnix36RoUmCfQE5v$vn(;52pWwCOWuInQ=+b#r65yL)(gdCl?m@%8na%bdqz`3D3B z1_g)ApC1|)9ucu%!NNt6i`h|894;?9CN`G8WT_x7UMNZsOQeZO%a$jvO!+D`ZB<6b z>dZB3*JfpZy>8w54I6WEHs$7S&fBu(KU=qL-@aq#u3fwL?A^OBzo1}$VbOsD2M--O zTwHSGNa@jI$B&;VJ9)DFn~I84rz@+ft82ckJ#+T#ci*2o|HFlg7k|9;)8(Ikxl&hm zwf@@mh8s5)pHenwsz5|E=Z0gNKhE|Nf-yY5O0~p10{j69tVsDPbya%C>eZQR)@Eg8f4y$~`VAX%Hf_q? zoR>!huu~pD{@?+Wmw!`n>U3pgRdvm`wY6vD=f5C7|1VeSuGU|>euFyyy?af~_kU|? z?R)$eov+@!d;dWOWmR=e?P0@p_4Ex4M;VPCV@x+0JI>V1%-npug{9?$2@|bsCQY)P zY-c~k!I7+lnKP+cV90AhUJT56ES5i24xv;Ui+bvzPaJ*HAPXWrV|8ZcnzcP~H<1!~ZQ4)ibzbeO>gPH@9qgnTRJBi<@Z zRPa*ZD P(Tr%3v~*g#;uOU+#X6EgiwoOV-!dKW*@7Vy59NO=p_($JuXO5?r3UwK3YjGTGcFG_WAYc8y@C(ZPv+Kx#iJT*0$Q6yLYSaN!$A- ze@S6@k^VvdL-obXlB=cu$HPywoaC3ksz^PpR=Km!jXz4or{Zf%SVUtK?wxwcFfR`~dmn(quPe(5D`{=WY+#Y$#yXechi@!ak~$@w@;^Ip|_=Yv`D;+5Me4 zCrD?0g}@??8()|(HsJ?J$zXjxnDkZ_CB64eL2F@nk$zDl)#?4MD|hzQK7Y7>w_m+p zUv%B4VRS=huXyvF{^n2bwpVu~Jqw}SU+p60-L21F9HCDGK2!5nC{1L z$N%?=6C_f5XaP{?OTB{t=mNM9E%pom;0EAMd=zsY03CoZA$3=YI#BOY|LfDR7C;6nl_ z*hs{3&>;XH2}lGVT<{=~uTO*kJS0L4Pp~0I40y1X?=Wzm5Ilky{4dPlg9~9KKSLZ> z9-Xv9m^?is*0$94sqH7w<+FWzcH|+x=Sk%5QPk(F9hDtDD;Pabr}xZ$1TkX%_8!)V z1{-1|fd>he;&+G<7s=1)T?g{N2LLE*1A80*pvI|XK%Gc=%cTIo5Q>t;{FqoN-BlzK z@aPO-oG4Ms6I;=}<2g1~bURyH2LSjB$yWB2$fH0R00004XF*Lt007wQ^&Awc0000W zV@Og>003>600482008hD004+h004H!007aO001)|0010z3vYEj0008!NklIT8bNkn}9D+lZ=lsv+SF|gLs)!qBe$uqy&w_egJZAw-$t?V!7&fBi; z-XBLsMzGf61MvHOU+wy$6JJ??w3w1*9fuv;H{I8{~U?}hpu7c4{;SC;|V$N(Y+r3^|5N(r%b zd+7Q4C&tFc^Y3Poh$%WC57b6bT9eV5-_BjeYyObm|NNVEZTsmxdYrK*<79H*3#E+| z0o_7}+E|RWZ0Y$mu@jx8>auSf9St_h~3-TIdXU>jg57AfDX7bI~1`f5tJ6n z0s*=@)-!ziTaNDAOjER(s*hJQc<&KyTf6yV@Cr(2*Zx0p9-mfk1#TML+qR45bi%uN6dYAqS*LaFcO+y0!Ez6r|S+k0C zMrR|>=1{CbM9^Zm|L`#<`Y!TJ94hLT5)KB5Evsj5`zNeg8ez&yp>+0V6ecPHT9XBe z7%HnP@am&k&_{KA*}8(2%{9amDPF#wDgszzD4d3O4vj_U97gLu;6TS(K8=Q1(Xfbk zBE|DplQU0?u`@d~)*xaLF(k#XV{MdFM$HN;+&2yAy8gK5B|_!%L%f=Z1Mqq>{@$^x zET2y@kwj_bb#DA@G7qe;4PU=;^LE#!jcqj6R+n53o|oYI@NJevYHk5(04Gl8I%mJJnDZzO!vp>kq)aSS*$Y0;~a60t@EQU`gN+F#2C(7Y^csoSZoT dr`6}b0|3Qh%H}o#G^hXo002ovPDHLkV1k;*{0RU6 literal 0 HcmV?d00001 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/search.gif b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/search.gif new file mode 100644 index 0000000000000000000000000000000000000000..9070fda723c54e89fc9a4fb6d95b3b249f785949 GIT binary patch literal 547 zcmZ?wbhEHb6kyMHVtXR5o?TS@vfMEUl z4VyM?+Pr1U+HL!{ZriqT``&FpuzmZE9Xqz~J-m1C-h&4Z?mB$-@ZrOIj-EeqcYx@{)%&lm-G6oc;hXD^-rRWf_Tj^aHy^*f z_2k{{C+}}P{qW%Ft9vg#-GBM{{>v{9UwwJ@^3B87UtYd^`TX_!CvU$!dHem%n>SD2 zeSiM`$LkMYUw!)d`qPispMSmk^6UNA-(PYfB*ga_us#N z|LF@9f3h%gG1N2YFaQB4P8ir58tR*xnm3yCqSUG^uCFM= zuW9M(XD`^JBcQ9n#-JamDkLl>-y@}_%f-m*wx~%6;$;h#tX#Zo)v{&FmMmYjV#SK3E7z`AwFU^* zuivm~)27W^wyfQ@f9tkw8@KP>1_ax;@7S?p``*KQ_wGG#;K0Fy2X`Gld-(9-Jx9+U zIdbI8nKS3kox6DP;^`~*Zrr$W;r6o&x1V1GqB}tJ;_CfZ*Y3Z%{_xH9M{jOCdi(I< z!<&!a-g@%x_LKLwo_=`n^wqr=pYFf>eE;Q_hp)aod->+!>n|@~zI^`r{gb!fp1l43 z=FOX@@4i2O|Ks(CudhD+eEsRi>(9U5efjnN>+df={(SrS=jYF#-+%r4@%!)Z-@pI< z{rm6VzyI_Cia%KxxftphbU-lyiW3I*&W8G?=4NG14o;bt?rvUHe{UmAo}M0c#Rvxz zb1MV6sV#h-E~d6l?yjN>nz;haES>#=0wh;A@mm<#c?E}sN-gk_uo06tw)YK*bW@(H zB4MZ`E1+%d9^fd{qbI1Z#m;D0uO=)auFxZ`q0i04=CQ3=M}n0__hXA7v&0XEfB$}P O{N>)k(XhaQ!5RQ;H)&u1 literal 0 HcmV?d00001 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx.properties new file mode 100644 index 0000000000..8c49b1961e --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx.properties @@ -0,0 +1,59 @@ +# default properties for swingx +# JXSearchPanel + +Search.matchCase=Case sensitive +Search.contains=contains +Search.equals=equals +Search.endsWith=ends with +Search.startsWith=begins with + +Search.wrapSearch=Wrap Search +Search.backwardsSearch=Backward +Search.match=Find +Search.close=Close +Search.findNext=Find Next +Search.findPrevious=Find Previous +Search.searchFieldLabel=Find +Search.searchFieldLabel.mnemonic=F +Search.searchTitle=Find + +Search.notFound=Value not found +Search.notFoundBackground=FF6666 +Search.notFoundForeground=000000 + +XDialog.close=Close +XDialog.cancel=Cancel +XDialog.execute=OK + +# default properties for swingx +# JXTable column control entries +# +JXTable.column.horizontalScroll=Horizontal Scroll +JXTable.column.packAll=Pack All Columns +JXTable.column.packSelected=Pack Selected Column + +# +# default actions in context menues for TextComponents +# +DefaultEditorKit.cut-to-clipboard=Cut +DefaultEditorKit.copy-to-clipboard=Copy +DefaultEditorKit.paste-from-clipboard=Paste +DefaultEditorKit.delete-next=Delete +DefaultEditorKit.select-all=Select All + +# +# default actions for context menus for ScrollBars +# +JScrollBar.vertical.minScroll=Top +JScrollBar.vertical.maxScroll=Bottom +JScrollBar.vertical.negativeUnitIncrement=Scroll Up +JScrollBar.vertical.negativeBlockIncrement=Page Up +JScrollBar.vertical.positiveUnitIncrement=Scroll Down +JScrollBar.vertical.positiveBlockIncrement=Page Down + +JScrollBar.horizontal.minScroll=Left Edge +JScrollBar.horizontal.maxScroll=Right Edge +JScrollBar.horizontal.negativeUnitIncrement=Scroll Left +JScrollBar.horizontal.negativeBlockIncrement=Page Left +JScrollBar.horizontal.positiveUnitIncrement=Scroll Right +JScrollBar.horizontal.positiveBlockIncrement=Page Right diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_cs.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_cs.properties new file mode 100644 index 0000000000..44eed5389d --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_cs.properties @@ -0,0 +1,61 @@ +#Czech resource bundle created by Vity (vitywap@seznam.cz) 08/2007 +## default properties for swingx +# JXSearchPanel + +Search.matchCase=Nerozli\u0161ovat mal\u00e1/velk\u00e1 +Search.contains=obsahuje +Search.equals=je rovno +Search.endsWith=kon\u010d\u00ed na +Search.startsWith=za\u010d\u00edn\u00e1 na + +Search.wrapSearch=Po dosa\u017een\u00ed konce hledat znovu odshora +Search.backwardsSearch=Hledat Nahoru +Search.match=Naj\u00edt +Search.close=Zav\u0159\u00edt +Search.findNext=Naj\u00edt Dal\u0161\u00ed +Search.findPrevious=Naj\u00edt P\u0159edchoz\u00ed +Search.searchFieldLabel=Naj\u00edt +Search.searchFieldLabel.mnemonic=N +Search.searchTitle=Hledat + +Search.notFound=Hledan\u00fd v\u00fdraz nebyl nalezen +Search.notFoundBackground=FF6666 +Search.notFoundForeground=000000 + +XDialog.close=Zav\u0159\u00edt +XDialog.cancel=Storno +XDialog.execute=OK + + +# default properties for swingx +# JXTable column control entries +# +JXTable.column.horizontalScroll=Horizont\u00e1ln\u00ed Scrollbar +JXTable.column.packAll=P\u0159izp\u016fsobit V\u0161echny Sloupce +JXTable.column.packSelected=P\u0159izp\u016fsobit Aktivn\u00ed Sloupec + +# +# default actions in context menues for TextComponents +# +DefaultEditorKit.cut-to-clipboard=Vyjmout +DefaultEditorKit.copy-to-clipboard=Zkop\u00edrovat +DefaultEditorKit.paste-from-clipboard=Vlo\u017eit +DefaultEditorKit.delete-next=Smazat +DefaultEditorKit.select-all=Vybrat V\u0161e + +# +# default actions for context menus for ScrollBars +# +JScrollBar.vertical.minScroll=Nahoru +JScrollBar.vertical.maxScroll=Dolu +JScrollBar.vertical.negativeUnitIncrement=Posun Nahoru +JScrollBar.vertical.negativeBlockIncrement=Str\u00e1nku Nahoru +JScrollBar.vertical.positiveUnitIncrement=Posun Dolu +JScrollBar.vertical.positiveBlockIncrement=Str\u00e1nku Dolu + +JScrollBar.horizontal.minScroll=Lev\u00fd Kraj +JScrollBar.horizontal.maxScroll=Prav\u00fd Kraj +JScrollBar.horizontal.negativeUnitIncrement=Posun Vlevo +JScrollBar.horizontal.negativeBlockIncrement=Str\u00e1nka Vlevo +JScrollBar.horizontal.positiveUnitIncrement=Posun Vpravo +JScrollBar.horizontal.positiveBlockIncrement=Str\u00e1nka Vpravo diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_da.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_da.properties new file mode 100644 index 0000000000..7a36d0449a --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_da.properties @@ -0,0 +1,59 @@ +# default properties for swingx +# JXSearchPanel + +Search.matchCase=Forskel p\u00e5 sm\u00e5/store bogstaver +Search.contains=indeholder +Search.equals=passer med +Search.endsWith=ender med +Search.startsWith=starter med + +Search.wrapSearch=Ombryd s\u00f8gning +Search.backwardsSearch=Tilbage +Search.match=Find +Search.close=Luk +Search.findNext=Find N\u00e6ste +Search.findPrevious=Find Forrige +Search.searchFieldLabel=Find +Search.searchFieldLabel.mnemonic=F +Search.searchTitle=Find + +Search.notFoundBackground=FF6666 +Search.notFoundForeground=000000 + +XDialog.close=Luk +XDialog.cancel=Annuller +XDialog.execute=OK + + +# default properties for swingx +# JXTable column control entries +# +JXTable.column.horizontalScroll=Vandret Scroll +JXTable.column.packAll=Tilpas alle kolonner +JXTable.column.packSelected=Tilpas markerede kolonner + +# +# default actions in context menues for TextComponents +# +DefaultEditorKit.cut-to-clipboard=Klip +DefaultEditorKit.copy-to-clipboard=Kopier +DefaultEditorKit.paste-from-clipboard=Inds\u00e6t +DefaultEditorKit.delete-next=Slet +DefaultEditorKit.select-all=Marker alt + +# +# default actions for context menus for ScrollBars +# +JScrollBar.vertical.minScroll=Top +JScrollBar.vertical.maxScroll=Bund +JScrollBar.vertical.negativeUnitIncrement=Scroll op +JScrollBar.vertical.negativeBlockIncrement=Side op +JScrollBar.vertical.positiveUnitIncrement=Scroll ned +JScrollBar.vertical.positiveBlockIncrement=Side ned + +JScrollBar.horizontal.minScroll=Venstre hj\u00f8rne +JScrollBar.horizontal.maxScroll=H\u00f8jre hj\u00f8rne +JScrollBar.horizontal.negativeUnitIncrement=Scroll til venstre +JScrollBar.horizontal.negativeBlockIncrement=Side til venstre +JScrollBar.horizontal.positiveUnitIncrement=Scroll til h\u00f8jre +JScrollBar.horizontal.positiveBlockIncrement=Side til h\u00f8jre diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_de.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_de.properties new file mode 100644 index 0000000000..bf344e5dcb --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_de.properties @@ -0,0 +1,60 @@ +# default properties for swingx +# Search + +Search.matchCase=Groß-/Kleinschreibung +Search.contains=enth\u00E4lt +Search.equals=ist gleich +Search.endsWith=endet mit +Search.startsWith=beginnt mit + +Search.wrapSearch=Umlaufend +Search.backwardsSearch=Rückwärts +Search.match=Weitersuchen +Search.close=Schließen +Search.findNext=Nächste +Search.findPrevious=Vorherige +Search.searchFieldLabel=Suchen nach +Search.searchFieldLabel.mnemonic=S +Search.searchTitle=Suchen + +Search.notFound=Ausdruck nicht gefunden +Search.notFoundBackground=FF6666 +Search.notFoundForeground=000000 + +XDialog.close=Schließen +XDialog.cancel=Abbrechen +XDialog.execute=OK + + +# default properties for swingx +# JXTable column control entries +# +JXTable.column.horizontalScroll=Horizontaler Rollbalken +JXTable.column.packAll=Alle Spalten anpassen +JXTable.column.packSelected=Ausgewählte Spalte anpassen + +# +# default actions in context menues for TextComponents +# +DefaultEditorKit.cut-to-clipboard=Ausschneiden +DefaultEditorKit.copy-to-clipboard=Kopieren +DefaultEditorKit.paste-from-clipboard=Einfügen +DefaultEditorKit.delete-next=Löschen +DefaultEditorKit.select-all=Alles markieren + +# +# default actions for context menue for ScrollBars +# +JScrollBar.vertical.minScroll=Oberer Rand +JScrollBar.vertical.maxScroll=Unterer Rand +JScrollBar.vertical.negativeUnitIncrement=Bildlauf oben +JScrollBar.vertical.negativeBlockIncrement=Nach oben +JScrollBar.vertical.positiveUnitIncrement=Bildlauf unten +JScrollBar.vertical.positiveBlockIncrement=Nach unten + +JScrollBar.horizontal.minScroll=Linker Rand +JScrollBar.horizontal.maxScroll=Rechter Rand +JScrollBar.horizontal.negativeUnitIncrement=Bildlauf links +JScrollBar.horizontal.negativeBlockIncrement=Seite links +JScrollBar.horizontal.positiveUnitIncrement=Bildlauf rechts +JScrollBar.horizontal.positiveBlockIncrement=Seite rechts diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_en.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_en.properties new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_es.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_es.properties new file mode 100644 index 0000000000..8bbbd7707b --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_es.properties @@ -0,0 +1,57 @@ +# spanish properties for swingx +# JXSearchPanel +# @author: Diego Gil (dags in jdesktop) + +Search.matchCase=Coincidencia de may\u00fasculas/min\u00fasculas +Search.contains=contiene +Search.equals=igual +Search.endsWith=termina con +Search.startsWith=empieza con + +Search.wrapSearch=B\u00fasqueda Circular +Search.backwardsSearch=Hacia Atr\u00e1s +Search.match=Buscar +Search.close=Cerrar +Search.findNext=Buscar Siguiente +Search.findPrevious=Buscar Anterior +Search.searchFieldLabel=Buscar +Search.searchFieldLabel.mnemonic=B +Search.searchTitle=Buscar + +XDialog.close=Cerrar +XDialog.cancel=Cancelar +XDialog.execute=Aceptar + + +# default properties for swingx +# JXTable column control entries +# +JXTable.column.horizontalScroll=Desplazamiento Horizontal +JXTable.column.packAll=Compactar Todas las Columnas +JXTable.column.packSelected=Compactar la Columna Seleccionada + +# +# default actions in context menues for TextComponents +# +DefaultEditorKit.cut-to-clipboard=Cortar +DefaultEditorKit.copy-to-clipboard=Copiar +DefaultEditorKit.paste-from-clipboard=Pegar +DefaultEditorKit.delete-next=Eliminar +DefaultEditorKit.select-all=Seleccionar Todo + +# +# default actions for context menue for ScrollBars +# +JScrollBar.vertical.minScroll=Arriba +JScrollBar.vertical.maxScroll=Abajo +JScrollBar.vertical.negativeUnitIncrement=Desplazar hacia Arriba +JScrollBar.vertical.negativeBlockIncrement=P\u00e1gina Arriba +JScrollBar.vertical.positiveUnitIncrement=Desplazar hacia Abajo +JScrollBar.vertical.positiveBlockIncrement=P\u00e1gina Abajo + +JScrollBar.horizontal.minScroll=Borde Izquierdo +JScrollBar.horizontal.maxScroll=Borde Derecho +JScrollBar.horizontal.negativeUnitIncrement=Desplazar a la Izquierda +JScrollBar.horizontal.negativeBlockIncrement=P\u00e1gina Izquierda +JScrollBar.horizontal.positiveUnitIncrement=Desplazar a la Derecha +JScrollBar.horizontal.positiveBlockIncrement=P\u00e1gina Derecha diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_fr.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_fr.properties new file mode 100644 index 0000000000..55127405de --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_fr.properties @@ -0,0 +1,60 @@ +# default properties for swingx +# JXSearchPanel +# @author: Florent Garin + +Search.matchCase=Respecter la casse +Search.contains=contenant +Search.equals=égale +Search.endsWith=se terminant par +Search.startsWith=commençant par + +Search.wrapSearch=Recherche circulaire +Search.backwardsSearch=Vers le haut +Search.match=Rechercher +Search.close=Fermer +Search.findNext=Suivant +Search.findPrevious=Précédent +Search.searchFieldLabel=Rechercher +Search.searchFieldLabel.mnemonic=R +Search.searchTitle=Rechercher + +Search.notFoundBackground=FF6666 +Search.notFoundForeground=000000 + +XDialog.close=Fermer +XDialog.cancel=Annuler +XDialog.execute=OK + + +# default properties for swingx +# JXTable column control entries +# +JXTable.column.horizontalScroll=Défilement horizontal +JXTable.column.packAll=Compacter toutes les colonnes +JXTable.column.packSelected=Compacter la colonne sélectionnée + +# +# default actions in context menues for TextComponents +# +DefaultEditorKit.cut-to-clipboard=Couper +DefaultEditorKit.copy-to-clipboard=Copier +DefaultEditorKit.paste-from-clipboard=Coller +DefaultEditorKit.delete-next=Supprimer +DefaultEditorKit.select-all=Tout sélectionner + +# +# default actions for context menue for ScrollBars +# +JScrollBar.vertical.minScroll=Haut +JScrollBar.vertical.maxScroll=Bas +JScrollBar.vertical.negativeUnitIncrement=Défilement vers le haut +JScrollBar.vertical.negativeBlockIncrement=Page précédente +JScrollBar.vertical.positiveUnitIncrement=Défilement vers le bas +JScrollBar.vertical.positiveBlockIncrement=Page suivante + +JScrollBar.horizontal.minScroll=Côté gauche +JScrollBar.horizontal.maxScroll=Côté droit +JScrollBar.horizontal.negativeUnitIncrement=Défilement vers la gauche +JScrollBar.horizontal.negativeBlockIncrement=Page vers la gauche +JScrollBar.horizontal.positiveUnitIncrement=Défilement vers la droite +JScrollBar.horizontal.positiveBlockIncrement=Page vers la droite diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_it.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_it.properties new file mode 100644 index 0000000000..32698fd9a2 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_it.properties @@ -0,0 +1,59 @@ +# default properties for swingx +# JXSearchPanel + +Search.matchCase=Maiuscole/Minuscole +Search.contains=contiene +Search.equals=uguale +Search.endsWith=termina per +Search.startsWith=inizia con + +Search.wrapSearch=Ricomincia dall'Inizio +Search.backwardsSearch=Indietro +Search.match=Trova +Search.close=Chiudi +Search.findNext=Trova Prossimo +Search.findPrevious=Trova Precedente +Search.searchFieldLabel=Trova +Search.searchFieldLabel.mnemonic=F +Search.searchTitle=Trova + +Search.notFoundBackground=FF6666 +Search.notFoundForeground=000000 + +XDialog.close=Chiudi +XDialog.cancel=Annulla +XDialog.execute=OK + + +# default properties for swingx +# JXTable column control entries +# +JXTable.column.horizontalScroll=Muovi in Orizzontale +JXTable.column.packAll=Auto-dimensiona Colonne +JXTable.column.packSelected=Auto-dimensiona Colonna Selezionata + +# +# default actions in context menues for TextComponents +# +DefaultEditorKit.cut-to-clipboard=Taglia +DefaultEditorKit.copy-to-clipboard=Copia +DefaultEditorKit.paste-from-clipboard=Incolla +DefaultEditorKit.delete-next=Cancella +DefaultEditorKit.select-all=Seleziona Tutto + +# +# default actions for context menus for ScrollBars +# +JScrollBar.vertical.minScroll=Inizio +JScrollBar.vertical.maxScroll=Fine +JScrollBar.vertical.negativeUnitIncrement=Sposta Su +JScrollBar.vertical.negativeBlockIncrement=Pagina Su +JScrollBar.vertical.positiveUnitIncrement=Sposta Gi\u00F9 +JScrollBar.vertical.positiveBlockIncrement=Sposta Gi\u00F9 + +JScrollBar.horizontal.minScroll=Bordo Sinistro +JScrollBar.horizontal.maxScroll=Bordo Destro +JScrollBar.horizontal.negativeUnitIncrement=Sposta a Sinistra +JScrollBar.horizontal.negativeBlockIncrement=Pagina a Sinistra +JScrollBar.horizontal.positiveUnitIncrement=Sposta a Destra +JScrollBar.horizontal.positiveBlockIncrement=Pagina a Destra diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_nl.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_nl.properties new file mode 100644 index 0000000000..0027c2bc2b --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_nl.properties @@ -0,0 +1,59 @@ +# default properties for swingx +# JXSearchPanel + +Search.matchCase=Hoofdlettergevoelig +Search.contains=bevat +Search.equals=gelijk aan +Search.endsWith=eindigd met +Search.startsWith=begint met + +Search.wrapSearch=Herhaaldelijk Doorzoeken +Search.backwardsSearch=Achterwaarts Zoeken +Search.match=Zoeken +Search.close=Sluiten +Search.findNext=Vind Volgende +Search.findPrevious=Vind Vorige +Search.searchFieldLabel=Zoek +Search.searchFieldLabel.mnemonic=F +Search.searchTitle=Zoeken + +Search.notFoundBackground=FF6666 +Search.notFoundForeground=000000 + +XDialog.close=Sluiten +XDialog.cancel=Annuleren +XDialog.execute=OK + + +# default properties for swingx +# JXTable column control entries +# +JXTable.column.horizontalScroll=Horizontaal Scrollen +JXTable.column.packAll=Pack Alle Kolommen +JXTable.column.packSelected=Pack Geselecteerde Kolom + +# +# default actions in context menues for TextComponents +# +DefaultEditorKit.cut-to-clipboard=Knippen +DefaultEditorKit.copy-to-clipboard=Kopieeren +DefaultEditorKit.paste-from-clipboard=Plakken +DefaultEditorKit.delete-next=Verwijderen +DefaultEditorKit.select-all=Selecteer alles + +# +# default actions for context menus for ScrollBars +# +JScrollBar.vertical.minScroll=Boven +JScrollBar.vertical.maxScroll=Onder +JScrollBar.vertical.negativeUnitIncrement=Scroll Up +JScrollBar.vertical.negativeBlockIncrement=Page Up +JScrollBar.vertical.positiveUnitIncrement=Scroll Down +JScrollBar.vertical.positiveBlockIncrement=Page Down + +JScrollBar.horizontal.minScroll=Linker kantlijn +JScrollBar.horizontal.maxScroll=Rechter kantlijn +JScrollBar.horizontal.negativeUnitIncrement=Scroll Links +JScrollBar.horizontal.negativeBlockIncrement=Page Links +JScrollBar.horizontal.positiveUnitIncrement=Scroll Rechts +JScrollBar.horizontal.positiveBlockIncrement=Page Rechts diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_pl.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_pl.properties new file mode 100644 index 0000000000..7aaf58a7a5 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_pl.properties @@ -0,0 +1,59 @@ +# default properties for swingx +# JXSearchPanel + +Search.matchCase=Uwzgl\u0119dniaj wielko\u015B\u0107 liter +Search.contains=zawieraj\u0105ce +Search.equals=r\u00F3wne +Search.endsWith=ko\u0144cz\u0105ce si\u0119 na +Search.startsWith=zaczynaj\u0105ce si\u0119 od + +Search.wrapSearch=Zawi\u0144 wyszukiwanie +Search.backwardsSearch=Poszukiwanie w ty\u0142 +Search.match=Szukaj +Search.close=Zamknij +Search.findNext=Szukaj nast\u0119pnego +Search.findPrevious=Szukaj poprzedniego +Search.searchFieldLabel=Szukaj +Search.searchFieldLabel.mnemonic=S +Search.searchTitle=Szukaj + +Search.notFoundBackground=FF6666 +Search.notFoundForeground=000000 + +XDialog.close=Zamknij +XDialog.cancel=Anuluj +XDialog.execute=OK + + +# default properties for swingx +# JXTable column control entries +# +JXTable.column.horizontalScroll=Przewijanie poziome +JXTable.column.packAll=Upakuj wszystkie kolumny +JXTable.column.packSelected=Upakuj wybrane kolumny + +# +# default actions in context menues for TextComponents +# +DefaultEditorKit.cut-to-clipboard=Wytnij +DefaultEditorKit.copy-to-clipboard=Kopiuj +DefaultEditorKit.paste-from-clipboard=Wklej +DefaultEditorKit.delete-next=Usu\u0144 +DefaultEditorKit.select-all=Zaznacz wszystko + +# +# default actions for context menus for ScrollBars +# +JScrollBar.vertical.minScroll=G\u00F3ra +JScrollBar.vertical.maxScroll=D\u00F3\u0142 +JScrollBar.vertical.negativeUnitIncrement=Przewi\u0144 w g\u00F3r\u0119 +JScrollBar.vertical.negativeBlockIncrement=Strona w g\u00F3r\u0119 +JScrollBar.vertical.positiveUnitIncrement=Przewi\u0144 w d\u00F3\u0142 +JScrollBar.vertical.positiveBlockIncrement=Strona w d\u00F3\u0142 + +JScrollBar.horizontal.minScroll=Lewa kraw\u0119d\u017A +JScrollBar.horizontal.maxScroll=Prawa kraw\u0119d\u017A +JScrollBar.horizontal.negativeUnitIncrement=Przewi\u0144 w lewo +JScrollBar.horizontal.negativeBlockIncrement=Strona w lewo +JScrollBar.horizontal.positiveUnitIncrement=Przewi\u0144 w prawo +JScrollBar.horizontal.positiveBlockIncrement=Strona w prawo diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_pt_BR.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_pt_BR.properties new file mode 100644 index 0000000000..2382819aa6 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_pt_BR.properties @@ -0,0 +1,59 @@ +# default properties for swingx +# JXSearchPanel + +Search.matchCase=Diferenciar Mai\u00FAscula/Min\u00FAscula +Search.contains=Cont\u00E9m +Search.equals=Igual +Search.endsWith=Come\u00E7a com +Search.startsWith=Termina com + +Search.wrapSearch=Circular +Search.backwardsSearch=Pesquisa para Tr\u00E1s +Search.match=Localizar +Search.close=Fechar +Search.findNext=Localizar Pr\u00F3xima +Search.findPrevious=Localizar Anterior +Search.searchFieldLabel=Localizar +Search.searchFieldLabel.mnemonic=F +Search.searchTitle=Localizar + +Search.notFoundBackground=FF6666 +Search.notFoundForeground=000000 + +XDialog.close=Fechar +XDialog.cancel=Cancelar +XDialog.execute=OK + + +# default properties for swingx +# JXTable column control entries +# +JXTable.column.horizontalScroll=Rolagem Horizontal +JXTable.column.packAll=Reduzir Todas as Colunas +JXTable.column.packSelected=Reduzir Colunas Selecionadas + +# +# default actions in context menues for TextComponents +# +DefaultEditorKit.cut-to-clipboard=Recortar +DefaultEditorKit.copy-to-clipboard=Copiar +DefaultEditorKit.paste-from-clipboard=Colar +DefaultEditorKit.delete-next=Excluir +DefaultEditorKit.select-all=Selecionar Tudo + +# +# default actions for context menus for ScrollBars +# +JScrollBar.vertical.minScroll=In\u00EDcio +JScrollBar.vertical.maxScroll=Fim +JScrollBar.vertical.negativeUnitIncrement=Rolar para Cima +JScrollBar.vertical.negativeBlockIncrement=P\u00E1gina Anterior +JScrollBar.vertical.positiveUnitIncrement=Rolar para Baixo +JScrollBar.vertical.positiveBlockIncrement=Pr\u00F3xima P\u00E1gina + +JScrollBar.horizontal.minScroll=Limite Esquerdo +JScrollBar.horizontal.maxScroll=Limite Direito +JScrollBar.horizontal.negativeUnitIncrement=Rolar para Esquerda +JScrollBar.horizontal.negativeBlockIncrement=P\u00E1gina Esquerda +JScrollBar.horizontal.positiveUnitIncrement=Rolar para Direita +JScrollBar.horizontal.positiveBlockIncrement=P\u00E1gina Direita diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_sv.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_sv.properties new file mode 100644 index 0000000000..af3c70d2e8 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_sv.properties @@ -0,0 +1,59 @@ +# default properties for swingx +# JXSearchPanel + +Search.matchCase=Matcha gemener/VERSALER +Search.contains=inneh\u00E5ller +Search.equals=lika med +Search.endsWith=slutar med +Search.startsWith=b\u00F6rjar med + +Search.wrapSearch=S\u00F6k runt +Search.backwardsSearch=Bak\u00E5t +Search.match=S\u00F6k +Search.close=St\u00E4ng +Search.findNext=S\u00F6k n\u00E4sta +Search.findPrevious=S\u00F6k tidigare +Search.searchFieldLabel=S\u00F6k +Search.searchFieldLabel.mnemonic=k +Search.searchTitle=S\u00F6k + +Search.notFoundBackground=FF6666 +Search.notFoundForeground=000000 + +XDialog.close=St\u00E4ng +XDialog.cancel=Avbryt +XDialog.execute=OK + + +# default properties for swingx +# JXTable column control entries +# +JXTable.column.horizontalScroll=Horizontell rullning +JXTable.column.packAll=Anpassa kolumnbredd +JXTable.column.packSelected=Anpassa valda kolumners bredd + +# +# default actions in context menues for TextComponents +# +DefaultEditorKit.cut-to-clipboard=Klipp ut +DefaultEditorKit.copy-to-clipboard=Kopiera +DefaultEditorKit.paste-from-clipboard=Klistra in +DefaultEditorKit.delete-next=Radera +DefaultEditorKit.select-all=Markera allt + +# +# default actions for context menus for ScrollBars +# +JScrollBar.vertical.minScroll=\u00D6verkant +JScrollBar.vertical.maxScroll=Nederkant +JScrollBar.vertical.negativeUnitIncrement=Rulla upp\u00E5t +JScrollBar.vertical.negativeBlockIncrement=En sida upp +JScrollBar.vertical.positiveUnitIncrement=Rulla ned\u00E5t +JScrollBar.vertical.positiveBlockIncrement=En sida ned + +JScrollBar.horizontal.minScroll=V\u00E4nsterkant +JScrollBar.horizontal.maxScroll=H\u00F6gerkant +JScrollBar.horizontal.negativeUnitIncrement=Rulla \u00E5t v\u00E4nster +JScrollBar.horizontal.negativeBlockIncrement=En sida \u00E5t v\u00E4nster +JScrollBar.horizontal.positiveUnitIncrement=Rulla \u00E5t h\u00F6ger +JScrollBar.horizontal.positiveBlockIncrement=En sida \u00E5t h\u00F6ger diff --git a/src/main/java/org/jdesktop/swingx/plaf/linux/LinuxLookAndFeelAddons.java b/src/main/java/org/jdesktop/swingx/plaf/linux/LinuxLookAndFeelAddons.java new file mode 100644 index 0000000000..854b4a1bea --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/linux/LinuxLookAndFeelAddons.java @@ -0,0 +1,65 @@ +/* + * $Id: LinuxLookAndFeelAddons.java 4092 2011-11-30 18:04:36Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf.linux; + +import org.jdesktop.swingx.plaf.LookAndFeelAddons; +import org.jdesktop.swingx.plaf.basic.BasicLookAndFeelAddons; +import org.jdesktop.swingx.util.OS; +import org.kohsuke.MetaInfServices; + +import static javax.swing.UIManager.getLookAndFeel; +import static javax.swing.UIManager.getSystemLookAndFeelClassName; + +@MetaInfServices(LookAndFeelAddons.class) +public class LinuxLookAndFeelAddons extends BasicLookAndFeelAddons { + /** + * {@inheritDoc} + */ + @Override + protected boolean matches() { + return isSystemAddon() && getSystemLookAndFeelClassName().equals(getLookAndFeel().getClass().getName()); + } + + /** + * {@inheritDoc} + */ + @Override + protected boolean isSystemAddon() { + return OS.isLinux(); + } + + /** + * {@inheritDoc} + */ + @Override + public void initialize() { + super.initialize(); + + //Issue 1297: added border to ensure non-null insets + // JW: moved into Table/ListAddon + // +// Border b = UIManagerExt.getSafeBorder("Table.focusSelectedCellHighlightBorder", BorderFactory.createEmptyBorder()); +// +// if (b instanceof UIResource) { +// UIManager.put("Table.focusSelectedCellHighlightBorder", new BorderUIResource(b)); +// } + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/linux/package-info.java b/src/main/java/org/jdesktop/swingx/plaf/linux/package-info.java new file mode 100644 index 0000000000..2f63c1e20a --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/linux/package-info.java @@ -0,0 +1,26 @@ +/* + * $Id: package-info.java 4028 2011-06-03 19:32:19Z kschaefe $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +/** + * Provides linux specific implementation of pluggable look-and-feel for SwingX components together with a + * mechanism to support custom component look-and-feels. + */ +package org.jdesktop.swingx.plaf.linux; + diff --git a/src/main/java/org/jdesktop/swingx/plaf/linux/resources/combo-gtk.png b/src/main/java/org/jdesktop/swingx/plaf/linux/resources/combo-gtk.png new file mode 100644 index 0000000000000000000000000000000000000000..c767ea97e21116b3f377dc39fb4a4715347668e7 GIT binary patch literal 2904 zcmV-e3#asnP)KLZ*U+5Lu!Sk^o_Z5E4Meg@_7P6crJiNL9pw)e1;Xm069{HJUZAPk55R%$-RIA z6-eL&AQ0xu!e<4=008gy@A0LT~suv4>S3ILP<0Bm`DLLvaF4FK%)Nj?Pt*r}7;7Xa9z9H|HZjR63e zC`Tj$K)V27Re@400>HumpsYY5E(E}?0f1SyGDiY{y#)Yvj#!WnKwtoXnL;eg03bL5 z07D)V%>y7z1E4U{zu>7~aD})?0RX_umCct+(lZpemCzb@^6=o|A>zVpu|i=NDG+7} zl4`aK{0#b-!z=TL9Wt0BGO&T{GJWpjryhdijfaIQ&2!o}p04JRKYg3k&Tf zVxhe-O!X z{f;To;xw^bEES6JSc$k$B2CA6xl)ltA<32E66t?3@gJ7`36pmX0IY^jz)rRYwaaY4 ze(nJRiw;=Qb^t(r^DT@T3y}a2XEZW-_W%Hszxj_qD**t_m!#tW0KDiJT&R>6OvVTR z07RgHDzHHZ48atvzz&?j9lXF70$~P3Knx_nJP<+#`N z#-MZ2bTkiLfR>_b(HgWKJ%F~Nr_oF3b#wrIijHG|(J>BYjM-sajE6;FiC7vY#};Gd zST$CUHDeuEH+B^pz@B062qXfFfD`NpUW5?BY=V%GM_5c)L#QR}BeW8_2v-S%gfYS= zB9o|3v?Y2H`NVi)In3rTB8+ej^> zQ=~r95NVuDChL%G$=>7$vVg20myx%S50Foi`^m%Pw-h?Xh~i8Mq9jtJloCocWk2Nv zrJpiFnV_ms&8eQ$2&#xWpIS+6pmtC%Q-`S&GF4Q#^mhymh7E(qNMa}%YZ-ePrx>>xFPTiH1=E+A$W$=bG8>s^ zm=Bn5Rah$aDtr}@$`X}2l~$F0mFKEdRdZE8)p@E5RI61Ft6o-prbbn>P~)iy)E2AN zsU20jsWz_8Qg>31P|s0cqrPALg8E|(vWA65poU1JRAaZs8I2(p#xiB`SVGovRs-uS zYnV-9TeA7=Om+qP8+I>yOjAR1s%ETak!GFdam@h^# z)@rS0t$wXH+Irf)+G6c;?H29p+V6F6oj{!|o%K3xI`?%6x;DB|x`n#ibhIR?(H}Q3Gzd138Ei2)WAMz7W9Vy`X}HnwgyEn!VS)>mv$8&{hQn>w4zwy3R}t;BYlZQm5)6pty=DfLrs+A-|>>;~;Q z_F?uV_HFjh9n2gO9o9Q^JA86v({H5aB!kjoO6 zc9$1ZZKsN-Zl8L~mE{`ly3)1N^`o1+o7}D0ZPeY&J;i;i`%NyJ8_8Y6J?}yE@b_5a zam?eLr<8@mESk|3$_SkmS{wQ>%qC18))9_|&j{ZT zes8AvOzF(F2#DZEY>2oYX&IRp`F#{ADl)1r>QS^)ba8a|EY_^#S^HO&t^Rgqwv=MZThqqEWH8 zxJo>d=ABlR_Bh=;eM9Tw|Ih34~oTE|= zX_mAr*D$vzw@+p(E0Yc6dFE}(8oqt`+R{gE3x4zjX+Sb3_cYE^= zgB=w+-tUy`ytONMS8KgRef4hA?t0j zufM;t32jm~jUGrkaOInTZ`zyfns>EuS}G30LFK_G-==(f<51|K&cocp&EJ`SxAh3? zNO>#LI=^+SEu(FqJ)ynt=!~PC9bO$rzPJB=?=j6w@a-(u02P7 zaQ)#(uUl{HW%tYNS3ItC^iAtK(eKlL`f9+{bJzISE?u8_z3;~C8@FyI-5j_jy7l;W z_U#vU3hqqYU3!mrul&B+{ptt$59)uk{;_4iZQ%G|z+lhASr6|H35TBkl>gI*;nGLU zN7W-nBaM%pA0HbH8olyl&XeJ%vZoWz%6?Y=dFykl=imL}`%BMQ{Mhgd`HRoLu6e2R za__6DuR6yg#~-}Tc|Gx_{H@O0eebyMy5GmWADJlpK>kqk(fVV@r_fLLKIeS?{4e)} z^ZO;zpECde00d`2O+f$vv5tKEQIh}w03c&XQcVB=dL;k=fP(-4`Tqa_faw4Lbua(` z>RI+y?e7jKeZ#YO-C0Ch=3K~#9!e9AEjgFqNW;Sq9|SZuXTyocmiTL~^~5j>4K z%7WnEx`OgjC^jJ(c-4IByB|&N%>NW&%hleIf5X0A?oHWU|S$uRFtQ zxKQ(G4s`yvvK2{EnM>6^Tb6ZE2tng}RL;55xz7NoF<&-s&Dy;H0000= fatal, it should say "Exit Application". If there is a report + * action but error < fatal, it should say "Don't Send" for ok, "Send Report" for + * the report button. If there is a report action and the error >= fatal, then + * one button should say "Exit", and the report button should say + * "Send Report and Exit". + * + * Whenever either button is clicked (ok button or report button), the "close dialog" + * procedure should occur. + * + * @author rbair + */ +public class MacOSXErrorPaneUI extends BasicErrorPaneUI { + private JLabel titleLabel; + private JEditorPane disclaimerText; // this is actually part of the details!!! + + //---------------------------------------------------------- constructor + /** Creates a new instance of BasicErrorPanelUI */ + public MacOSXErrorPaneUI() { + super(); + } + + @Override + protected void configureDetailsButton(boolean expanded) { + if (expanded) { + detailButton.setText(UIManagerExt.getString(CLASS_NAME + ".details_contract_text", detailButton.getLocale())); + detailButton.setIcon(UIManager.getIcon("Tree.expandedIcon")); + } else { + detailButton.setText(UIManagerExt.getString(CLASS_NAME + ".details_expand_text", detailButton.getLocale())); + detailButton.setIcon(UIManager.getIcon("Tree.collapsedIcon")); + } + } + + @Override + protected void configureReportAction(AbstractActionExt reportAction) { + reportAction.setName(UIManagerExt.getString(CLASS_NAME + ".report_button_text", pane.getLocale())); +// reportButton.setText("Send Report To Apple"); +// reportButton.setPreferredSize(new Dimension(100, 30)); +// reportButton.setMinimumSize(new Dimension(100, 30)); + } + + public static ComponentUI createUI(JComponent c) { + return new MacOSXErrorPaneUI(); + } + + /** + * {@inheritDoc} + */ + @Override + public JFrame getErrorFrame(Component owner) { + JFrame frame = super.getErrorFrame(owner); + frame.setTitle(" "); + return frame; + } + + /** + * {@inheritDoc} + */ + @Override + public JDialog getErrorDialog(Component owner) { + JDialog dlg = super.getErrorDialog(owner); + dlg.setTitle(" "); + return dlg; + } + + /** + * {@inheritDoc} + */ + @Override + public JInternalFrame getErrorInternalFrame(Component owner) { + JInternalFrame frame = super.getErrorInternalFrame(owner); + frame.setTitle(" "); + return frame; + } + + /** + * {@inheritDoc} + */ + @Override + protected LayoutManager createErrorPaneLayout() { + createExtraComponents(); + GridBagLayout layout = new GridBagLayout(); + try { + layout.addLayoutComponent(iconLabel, new GridBagConstraints(0, 0, 1, 2, 0.0, 0.0, GridBagConstraints.NORTH, GridBagConstraints.NONE, new Insets(0, 0, 0, 17), 0, 0)); + layout.addLayoutComponent(titleLabel, new GridBagConstraints(1, 0, 2, 1, 1.0, 0.0, GridBagConstraints.NORTH, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 12, 0), 0 ,0)); + layout.addLayoutComponent(errorScrollPane,new GridBagConstraints(1, 1, 2, 1, 1.0, 1.0, GridBagConstraints.NORTH, GridBagConstraints.BOTH, new Insets(0, 0, 10, 0), 0, 0)); + layout.addLayoutComponent(detailButton, new GridBagConstraints(0, 2, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 6, 0), 0, 0)); + layout.addLayoutComponent(detailsPanel, new GridBagConstraints(0, 3, 3, 1, 1.0, 1.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 0, 6, 0), 0 ,0)); + layout.addLayoutComponent(disclaimerText, new GridBagConstraints(0, 4, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 6, 0), 0, 0)); + layout.addLayoutComponent(closeButton, new GridBagConstraints(1, 5, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 0, 5), 0, 0)); + layout.addLayoutComponent(reportButton, new GridBagConstraints(2, 5, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + } catch (Exception e) { + e.printStackTrace(); + } + return layout; + } + + /** + * {@inheritDoc} + */ + @Override + protected LayoutManager createDetailPanelLayout() { + GridBagLayout layout = new GridBagLayout(); + layout.addLayoutComponent(detailsScrollPane, new GridBagConstraints(0,0,1,1,1.0,1.0,GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0,0,0,0),0,0)); + copyToClipboardButton.setVisible(false); + return layout; + } + + /** + * {@inheritDoc} + */ + @Override + protected void reinit() { + super.reinit(); + ErrorInfo info = pane == null ? null : pane.getErrorInfo(); + titleLabel.setText(info == null ? "Unknown Error" : info.getTitle()); + + Object finePrint = pane.getClientProperty("fine-print"); + String text = finePrint == null ? null : finePrint.toString(); + disclaimerText.setText(text); + disclaimerText.setVisible(text != null); + + if (info != null && info.getErrorLevel() == ErrorLevel.FATAL) { + closeButton.setText(UIManagerExt.getString(CLASS_NAME + ".fatal_button_text", closeButton.getLocale())); + } else { + closeButton.setText(UIManagerExt.getString(CLASS_NAME + ".ok_button_text", closeButton.getLocale())); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected int getDetailsHeight() { + return 150; + } + + private void createExtraComponents() { + titleLabel = new JLabel("Unknown Error"); + titleLabel.setFont(titleLabel.getFont().deriveFont(Font.BOLD)); + pane.add(titleLabel); + + Font f = errorMessage.getFont(); + if (f != null) { + errorMessage.setFont(f.deriveFont(f.getSize() - 2f)); + } + + disclaimerText = new JEditorPane(); + disclaimerText.setContentType("text/html"); + disclaimerText.setVisible(false); + disclaimerText.setEditable(false); + disclaimerText.setOpaque(false); + disclaimerText.putClientProperty(JXEditorPane.HONOR_DISPLAY_PROPERTIES, Boolean.TRUE); + if (f != null) { + disclaimerText.setFont(f.deriveFont(f.getSize() - 2f)); + } + pane.add(disclaimerText); + + detailButton.setBorderPainted(false); + detailButton.setContentAreaFilled(false); + detailButton.setBorder(BorderFactory.createEmptyBorder()); + detailButton.setMargin(new Insets(0, 0, 0 ,0)); + detailButton.setIcon(UIManager.getIcon("Tree.collapsedIcon")); + detailButton.setText(UIManagerExt.getString(CLASS_NAME + ".details_expand_text", detailButton.getLocale())); + + closeButton.setText(UIManagerExt.getString(CLASS_NAME + ".ok_button_text", closeButton.getLocale())); + } +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/plaf/macosx/MacOSXLookAndFeelAddons.java b/src/main/java/org/jdesktop/swingx/plaf/macosx/MacOSXLookAndFeelAddons.java new file mode 100644 index 0000000000..bf325b6b0a --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/macosx/MacOSXLookAndFeelAddons.java @@ -0,0 +1,48 @@ +/* + * $Id: MacOSXLookAndFeelAddons.java 4092 2011-11-30 18:04:36Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf.macosx; + +import org.jdesktop.swingx.plaf.LookAndFeelAddons; +import org.jdesktop.swingx.plaf.basic.BasicLookAndFeelAddons; +import org.jdesktop.swingx.util.OS; +import org.kohsuke.MetaInfServices; + +import static javax.swing.UIManager.getLookAndFeel; +import static javax.swing.UIManager.getSystemLookAndFeelClassName; + +@MetaInfServices(LookAndFeelAddons.class) +public class MacOSXLookAndFeelAddons extends BasicLookAndFeelAddons { + /** + * {@inheritDoc} + */ + @Override + protected boolean matches() { + return isSystemAddon() && getSystemLookAndFeelClassName().equals(getLookAndFeel().getClass().getName()); + } + + /** + * {@inheritDoc} + */ + @Override + protected boolean isSystemAddon() { + return OS.isMacOSX(); + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/macosx/MacOSXStatusBarUI.java b/src/main/java/org/jdesktop/swingx/plaf/macosx/MacOSXStatusBarUI.java new file mode 100644 index 0000000000..4eb1bbcb26 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/macosx/MacOSXStatusBarUI.java @@ -0,0 +1,58 @@ +/* + * $Id: MacOSXStatusBarUI.java 3475 2009-08-28 08:30:47Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.plaf.macosx; + +import org.jdesktop.swingx.JXStatusBar; +import org.jdesktop.swingx.plaf.basic.BasicStatusBarUI; + +import javax.swing.*; +import javax.swing.plaf.ComponentUI; + +/** + * + * @author rbair + */ +public class MacOSXStatusBarUI extends BasicStatusBarUI { + /** + * Returns an instance of the UI delegate for the specified component. + * Each subclass must provide its own static createUI + * method that returns an instance of that UI delegate subclass. + * If the UI delegate subclass is stateless, it may return an instance + * that is shared by multiple components. If the UI delegate is + * stateful, then it should return a new instance per component. + * The default implementation of this method throws an error, as it + * should never be invoked. + */ + public static ComponentUI createUI(JComponent c) { + return new MacOSXStatusBarUI(); + } + + /** + * {@inheritDoc} + */ + @Override + protected void installDefaults(JXStatusBar sb) { + super.installDefaults(sb); + + LookAndFeel.installProperty(statusBar, "opaque", Boolean.FALSE); + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/macosx/package-info.java b/src/main/java/org/jdesktop/swingx/plaf/macosx/package-info.java new file mode 100644 index 0000000000..8552e38246 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/macosx/package-info.java @@ -0,0 +1,26 @@ +/* + * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +/** + * Provides macos specific implementation of pluggable look-and-feel for SwingX components together with a + * mechanism to support custom component look-and-feels. + */ +package org.jdesktop.swingx.plaf.macosx; + diff --git a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane.properties b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane.properties new file mode 100644 index 0000000000..8f817cf7b2 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane.properties @@ -0,0 +1,7 @@ +# +# JXErrorPanel properties +# +JXErrorPane.report_button_text=Send Report +JXErrorPane.ok_button_text=Don't Send +JXErrorPane.details_expand_text=Details +JXErrorPane.details_contract_text=Details diff --git a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_cs.properties b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_cs.properties new file mode 100644 index 0000000000..7c9fdefba3 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_cs.properties @@ -0,0 +1,7 @@ +# +# JXErrorPanel properties +# +JXErrorPane.report_button_text=Ozn\u00e1mit Chybu +JXErrorPane.ok_button_text=Neodes\u00edlat +JXErrorPane.details_expand_text=Detaily +JXErrorPane.details_contract_text=Detaily diff --git a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_da.properties b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_da.properties new file mode 100644 index 0000000000..5c8c06f36c --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_da.properties @@ -0,0 +1,7 @@ +# +# JXErrorPanel properties +# +JXErrorPane.report_button_text=Afsend rapport +JXErrorPane.ok_button_text=Send ikke +JXErrorPane.details_expand_text=Detaljer +JXErrorPane.details_contract_text=Detaljer diff --git a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_de.properties b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_de.properties new file mode 100644 index 0000000000..e856bcd3f9 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_de.properties @@ -0,0 +1,7 @@ +# +# JXErrorPanel properties +# +JXErrorPane.report_button_text=Bericht senden +JXErrorPane.ok_button_text=Nicht senden +JXErrorPane.details_expand_text=Details +JXErrorPane.details_contract_text=Details diff --git a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_es.properties b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_es.properties new file mode 100644 index 0000000000..6d77238db3 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_es.properties @@ -0,0 +1,3 @@ +# +# JXErrorPanel properties +# diff --git a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_fr.properties b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_fr.properties new file mode 100644 index 0000000000..ad0a63b8c2 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_fr.properties @@ -0,0 +1,7 @@ +# +# JXErrorPanel properties +# +JXErrorPane.report_button_text=Envoyer le rapport +JXErrorPane.ok_button_text=Ne pas envoyer +JXErrorPane.details_expand_text=Détails +JXErrorPane.details_contract_text=Détails diff --git a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_it.properties b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_it.properties new file mode 100644 index 0000000000..4a9856d2d9 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_it.properties @@ -0,0 +1,7 @@ +# +# JXErrorPanel properties +# +JXErrorPane.report_button_text=Invia Resoconto +JXErrorPane.ok_button_text=Non Inviare +JXErrorPane.details_expand_text=Dettagli +JXErrorPane.details_contract_text=Dettagli diff --git a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_nl.properties b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_nl.properties new file mode 100644 index 0000000000..c8780e5300 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_nl.properties @@ -0,0 +1,7 @@ +# +# JXErrorPanel properties +# +JXErrorPane.report_button_text=Verstuur Rapport +JXErrorPane.ok_button_text=Niet versturen +JXErrorPane.details_expand_text=Details +JXErrorPane.details_contract_text=Details diff --git a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_pt_BR.properties b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_pt_BR.properties new file mode 100644 index 0000000000..42643d3b30 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_pt_BR.properties @@ -0,0 +1,7 @@ +# +# JXErrorPanel properties +# +JXErrorPane.report_button_text=Envia Relat\u00F3rio +JXErrorPane.ok_button_text=N\u00E3o Envia +JXErrorPane.details_expand_text=Detalhes +JXErrorPane.details_contract_text=Detalhes diff --git a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_sv.properties b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_sv.properties new file mode 100644 index 0000000000..5a74625180 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_sv.properties @@ -0,0 +1,7 @@ +# +# JXErrorPanel properties +# +JXErrorPane.report_button_text=S\u00E4nd rapport +JXErrorPane.ok_button_text=S\u00E4nd inte +JXErrorPane.details_expand_text=Detaljer +JXErrorPane.details_contract_text=Detaljer diff --git a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/clear.png b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/clear.png new file mode 100644 index 0000000000000000000000000000000000000000..7bf98a52ab5bc76d592ad3b489cf3568b773670f GIT binary patch literal 322 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh1|;P@bT0xa&H|6fVg?3oVGw3ym^DWNDEP(G z#W6%<;?heSy_g*(SRTB8q{(Q>b0brE+jOPYl{udTD!Fg(>Dusqg3Y?350PUM z*I!y+O$d7+xBO+6;j#8z>!x2YIh$rR*Dme${{Fs}LXoA;XC~J~T{e+k@w!Sfd5UlD zPj?H^XDcnam7YIa(e{joRioZAw_iP{{n?wUqBj>Nyua`$GJdDh??1&S?HLM;%OXTP ROF^N<;OXk;vd$@?2>@%mg^U0I literal 0 HcmV?d00001 diff --git a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/clear_pressed.png b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/clear_pressed.png new file mode 100644 index 0000000000000000000000000000000000000000..5fc7f8e3c8a19d0c5da280f6f8c060310db80ef9 GIT binary patch literal 412 zcmV;N0b~A&P)`xJeMkTFk@hX{EDxr;a$C_%V` zICSYHty2Xl2rl=+ZLkPEbhRXaoh+Y63Yo8lB%kjx~|twh-u(l4}hX5-mCzis;b6v4n_k` zL}30ju|$?-GaJu!?zwIP^W2F&jup9sLWrbo+fQfDl@IKJD`2RU+G(x3vMfKWNJ`mh ztvg4w1zx;f0m6S)+XGL)(l$q%Mx2{J9jn~4t(JRF&Jw0E&4)L z*zyKjg~VV;C76n)wM8tVyUFFI6cR>l&d$l6o%t)@tAk5i;5=(9EbxJ4X{>6dEA((? zcdhY;IULkX*EspX1jjka2Fq5C*FzgZ2;HWmrnPf#2l^Oc6hauMlv0dwTC^DBG^Lb6 z2;-umU*R@K05Qgg!C-JVo6Vk}oQK2VV=cs@!aa`a(TW*%m|)Y|GmO1;XuGGqQDcl6 zude0P@m$C$0JMMk+9zHxFXSw3i8!?-WLK^6DkE1lF8>Vq Y7u|zw!w`0Re*gdg07*qoM6N<$f~%W}3;+NC literal 0 HcmV?d00001 diff --git a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/combo-osx.png b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/combo-osx.png new file mode 100644 index 0000000000000000000000000000000000000000..658e48ac21bbef723b977026c77948e8c5a5f9e7 GIT binary patch literal 2850 zcmV+-3*GdIP)KLZ*U+5Lu!Sk^o_Z5E4Meg@_7P6crJiNL9pw)e1;Xm069{HJUZAPk55R%$-RIA z6-eL&AQ0xu!e<4=008gy@A0LT~suv4>S3ILP<0Bm`DLLvaF4FK%)Nj?Pt*r}7;7Xa9z9H|HZjR63e zC`Tj$K)V27Re@400>HumpsYY5E(E}?0f1SyGDiY{y#)Yvj#!WnKwtoXnL;eg03bL5 z07D)V%>y7z1E4U{zu>7~aD})?0RX_umCct+(lZpemCzb@^6=o|A>zVpu|i=NDG+7} zl4`aK{0#b-!z=TL9Wt0BGO&T{GJWpjryhdijfaIQ&2!o}p04JRKYg3k&Tf zVxhe-O!X z{f;To;xw^bEES6JSc$k$B2CA6xl)ltA<32E66t?3@gJ7`36pmX0IY^jz)rRYwaaY4 ze(nJRiw;=Qb^t(r^DT@T3y}a2XEZW-_W%Hszxj_qD**t_m!#tW0KDiJT&R>6OvVTR z07RgHDzHHZ48atvzz&?j9lXF70$~P3Knx_nJP<+#`N z#-MZ2bTkiLfR>_b(HgWKJ%F~Nr_oF3b#wrIijHG|(J>BYjM-sajE6;FiC7vY#};Gd zST$CUHDeuEH+B^pz@B062qXfFfD`NpUW5?BY=V%GM_5c)L#QR}BeW8_2v-S%gfYS= zB9o|3v?Y2H`NVi)In3rTB8+ej^> zQ=~r95NVuDChL%G$=>7$vVg20myx%S50Foi`^m%Pw-h?Xh~i8Mq9jtJloCocWk2Nv zrJpiFnV_ms&8eQ$2&#xWpIS+6pmtC%Q-`S&GF4Q#^mhymh7E(qNMa}%YZ-ePrx>>xFPTiH1=E+A$W$=bG8>s^ zm=Bn5Rah$aDtr}@$`X}2l~$F0mFKEdRdZE8)p@E5RI61Ft6o-prbbn>P~)iy)E2AN zsU20jsWz_8Qg>31P|s0cqrPALg8E|(vWA65poU1JRAaZs8I2(p#xiB`SVGovRs-uS zYnV-9TeA7=Om+qP8+I>yOjAR1s%ETak!GFdam@h^# z)@rS0t$wXH+Irf)+G6c;?H29p+V6F6oj{!|o%K3xI`?%6x;DB|x`n#ibhIR?(H}Q3Gzd138Ei2)WAMz7W9Vy`X}HnwgyEn!VS)>mv$8&{hQn>w4zwy3R}t;BYlZQm5)6pty=DfLrs+A-|>>;~;Q z_F?uV_HFjh9n2gO9o9Q^JA86v({H5aB!kjoO6 zc9$1ZZKsN-Zl8L~mE{`ly3)1N^`o1+o7}D0ZPeY&J;i;i`%NyJ8_8Y6J?}yE@b_5a zam?eLr<8@mESk|3$_SkmS{wQ>%qC18))9_|&j{ZT zes8AvOzF(F2#DZEY>2oYX&IRp`F#{ADl)1r>QS^)ba8a|EY_^#S^HO&t^Rgqwv=MZThqqEWH8 zxJo>d=ABlR_Bh=;eM9Tw|Ih34~oTE|= zX_mAr*D$vzw@+p(E0Yc6dFE}(8oqt`+R{gE3x4zjX+Sb3_cYE^= zgB=w+-tUy`ytONMS8KgRef4hA?t0j zufM;t32jm~jUGrkaOInTZ`zyfns>EuS}G30LFK_G-==(f<51|K&cocp&EJ`SxAh3? zNO>#LI=^+SEu(FqJ)ynt=!~PC9bO$rzPJB=?=j6w@a-(u02P7 zaQ)#(uUl{HW%tYNS3ItC^iAtK(eKlL`f9+{bJzISE?u8_z3;~C8@FyI-5j_jy7l;W z_U#vU3hqqYU3!mrul&B+{ptt$59)uk{;_4iZQ%G|z+lhASr6|H35TBkl>gI*;nGLU zN7W-nBaM%pA0HbH8olyl&XeJ%vZoWz%6?Y=dFykl=imL}`%BMQ{Mhgd`HRoLu6e2R za__6DuR6yg#~-}Tc|Gx_{H@O0eebyMy5GmWADJlpK>kqk(fVV@r_fLLKIeS?{4e)} z^ZO;zpECde00d`2O+f$vv5tKEQIh}w03c&XQcVB=dL;k=fP(-4`Tqa_faw4Lbua(` z>RI+y?e7jKeZ#YO-C06$4YK~#9!Y|GmTfG`vT(1YE=HSUJ}b~TrAf%s#!NCSa9 zH*{pd-f*F?`*7lr#8b|%NqW0R!>LpE%f6e6}rVUpiQQI?O4IW=nX2cx!lrK)Enr!Lf3 z;d1a$yh@HqWcOTNYlAa;pP!p!T&@;)L&j`Fo9ms2Lg^Ku8>>{_C##=(JmaX*vcm59 zn-X}UZyXWz``D?O!~U;j@umfBW{cste|rE72nJ7AKbLh*2~7Z8x_>|b literal 0 HcmV?d00001 diff --git a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/search_popup.png b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/search_popup.png new file mode 100644 index 0000000000000000000000000000000000000000..0afca49c03ff9d2a876803d8bab55fcdcaba101d GIT binary patch literal 954 zcmaLVKS-2e9KiAC?e+9LlL$-Fp~ajWqCg^TNIN+X{}d%bQxY^6=-@0Z%??pGC=JF9 zLevyQK|@1}lMYf~qM3uK5n_2>-|z7XNnO9=bHDfbJ%8Wl?%}n;Tz#fF6GEuZ_hzq? zkK<^%n(qg#H{Xz|@AT!eq2y=k^~?m7+WWn?Mnb4-P!yg_o}ySYnjh$?Sx9BlZH;TG z&8H#6xqP;3Xe^2@#qahah8T^wjCQo(BR=3Q*74tn>2-l&Tuq)+A0*~-lIB5^N@IB?PVz#!8PdJY@Ea5W_g^L*OUQGNV%SEiY;Bu0E ZE&C3|YGL5n + * + */ +@MetaInfServices(LookAndFeelAddons.class) +public class MetalLookAndFeelAddons extends BasicLookAndFeelAddons { + /** + * {@inheritDoc} + */ + @Override + protected boolean matches() { + LookAndFeel laf = getLookAndFeel(); + + if (getCrossPlatformLookAndFeelClassName().equals(laf.getClass().getName())) { + //just in case someone sets Nimbus as the cross platform Look and Feel + return !laf.getID().equals("Nimbus"); + } + + return getLookAndFeel() instanceof MetalLookAndFeel; + } + + @Override + public void initialize() { + super.initialize(); + loadDefaults(getDefaults()); + } + + @Override + public void uninitialize() { + super.uninitialize(); + unloadDefaults(getDefaults()); + } + + private Object[] getDefaults() { + return new Object[] { + // "DirectoryChooserUI", + // "org.jdesktop.jdnc.swing.plaf.windows.WindowsDirectoryChooserUI", + }; + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/metal/MetalStatusBarUI.java b/src/main/java/org/jdesktop/swingx/plaf/metal/MetalStatusBarUI.java new file mode 100644 index 0000000000..ba906b8935 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/metal/MetalStatusBarUI.java @@ -0,0 +1,94 @@ +/* + * $Id: MetalStatusBarUI.java 3472 2009-08-27 13:12:42Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.plaf.metal; + +import org.jdesktop.swingx.JXStatusBar; +import org.jdesktop.swingx.plaf.basic.BasicStatusBarUI; + +import javax.swing.*; +import javax.swing.plaf.ComponentUI; +import java.awt.*; +import java.util.List; + +/** + * + * @author rbair + */ +public class MetalStatusBarUI extends BasicStatusBarUI { + + /** Creates a new instance of BasicStatusBarUI */ + public MetalStatusBarUI() { + } + + /** + * Returns an instance of the UI delegate for the specified component. + * Each subclass must provide its own static createUI + * method that returns an instance of that UI delegate subclass. + * If the UI delegate subclass is stateless, it may return an instance + * that is shared by multiple components. If the UI delegate is + * stateful, then it should return a new instance per component. + * The default implementation of this method throws an error, as it + * should never be invoked. + */ + public static ComponentUI createUI(JComponent c) { + return new MetalStatusBarUI(); + } + + @Override + protected void paintBackground(Graphics2D g, JXStatusBar bar) { + int w = bar.getWidth(); //TODO deal with insets + int h = bar.getHeight(); //TODO deal with insets + + //This list is comprised of floats and Colors, which together + //constitute the gradient. + List gradient = (List) UIManager.get("MenuBar.gradient"); + + if (gradient != null && w > 0 && 0 < h) { + float ratio1 = ((Number)gradient.get(0)).floatValue(); + float ratio2 = ((Number)gradient.get(1)).floatValue(); + Color c1 = (Color)gradient.get(2); + Color c2 = (Color)gradient.get(3); + Color c3 = (Color)gradient.get(4); + int mid = (int)(ratio1 * h); + int mid2 = (int)(ratio2 * h); + if (mid > 0) { + g.setPaint(new GradientPaint((float)0, (float)0, c1, (float)0, + (float)mid, c2)); + g.fillRect(0, 0, w, mid); + } + if (mid2 > 0) { + g.setColor(c2); + g.fillRect(0, mid, w, mid2); + } + if (mid > 0) { + g.setPaint(new GradientPaint((float)0, (float)mid + mid2, c2, + (float)0, (float)mid * 2 + mid2, c1)); + g.fillRect(0, mid + mid2, w, mid); + } + if (h - mid * 2 - mid2 > 0) { + g.setPaint(new GradientPaint((float)0, (float)mid * 2 + mid2, c1, + (float)0, (float)h, c3)); + g.fillRect(0, mid * 2 + mid2, w, h - mid * 2 - mid2); + } + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/metal/MetalTaskPaneUI.java b/src/main/java/org/jdesktop/swingx/plaf/metal/MetalTaskPaneUI.java new file mode 100644 index 0000000000..761cefef07 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/metal/MetalTaskPaneUI.java @@ -0,0 +1,84 @@ +/* + * $Id: MetalTaskPaneUI.java 3448 2009-08-19 08:12:23Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf.metal; + +import org.jdesktop.swingx.JXTaskPane; +import org.jdesktop.swingx.plaf.basic.BasicTaskPaneUI; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.plaf.ComponentUI; +import java.awt.*; + +/** + * Metal implementation of the JXTaskPane UI.
      + * + * @author Frederic Lavigne + */ +public class MetalTaskPaneUI extends BasicTaskPaneUI { + + public static ComponentUI createUI(JComponent c) { + return new MetalTaskPaneUI(); + } + + @Override + protected void installDefaults() { + super.installDefaults(); + + LookAndFeel.installProperty(group, "opaque", false); + } + + @Override + protected Border createPaneBorder() { + return new MetalPaneBorder(); + } + + /** + * The border of the task pane group paints the "text", the "icon", + * the "expanded" status and the "special" type. + * + */ + class MetalPaneBorder extends PaneBorder { + + @Override + protected void paintExpandedControls(JXTaskPane group, Graphics g, int x, + int y, int width, int height) { + ((Graphics2D)g).setRenderingHint( + RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + + g.setColor(getPaintColor(group)); + paintRectAroundControls(group, g, x, y, width, height, g.getColor(), g + .getColor()); + paintChevronControls(group, g, x, y, width, height); + + ((Graphics2D)g).setRenderingHint( + RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_OFF); + } + + @Override + protected boolean isMouseOverBorder() { + return true; + } + } + +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/plaf/metal/package-info.java b/src/main/java/org/jdesktop/swingx/plaf/metal/package-info.java new file mode 100644 index 0000000000..59897edc1f --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/metal/package-info.java @@ -0,0 +1,26 @@ +/* + * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +/** + * Provides metal laf specific implementation of pluggable look-and-feel for SwingX components together with a + * mechanism to support custom component look-and-feels. + */ +package org.jdesktop.swingx.plaf.metal; + diff --git a/src/main/java/org/jdesktop/swingx/plaf/misc/GlossyTaskPaneUI.java b/src/main/java/org/jdesktop/swingx/plaf/misc/GlossyTaskPaneUI.java new file mode 100644 index 0000000000..7c142e643e --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/misc/GlossyTaskPaneUI.java @@ -0,0 +1,156 @@ +/* + * $Id: GlossyTaskPaneUI.java 3448 2009-08-19 08:12:23Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf.misc; + +import org.jdesktop.swingx.JXTaskPane; +import org.jdesktop.swingx.plaf.basic.BasicTaskPaneUI; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.plaf.ComponentUI; +import java.awt.*; + +/** + * Paints the JXTaskPane with a gradient in the title bar. + * + * @author Frederic Lavigne + */ +public class GlossyTaskPaneUI extends BasicTaskPaneUI { + + public static ComponentUI createUI(JComponent c) { + return new GlossyTaskPaneUI(); + } + + @Override + protected Border createPaneBorder() { + return new GlossyPaneBorder(); + } + + /** + * Overriden to paint the background of the component but keeping the rounded + * corners. + */ + @Override + public void update(Graphics g, JComponent c) { + if (c.isOpaque()) { + g.setColor(c.getParent().getBackground()); + g.fillRect(0, 0, c.getWidth(), c.getHeight()); + g.setColor(c.getBackground()); + g.fillRect(0, getRoundHeight(), c.getWidth(), c.getHeight() - getRoundHeight()); + } + paint(g, c); + } + + /** + * The border of the taskpane group paints the "text", the "icon", the + * "expanded" status and the "special" type. + * + */ + class GlossyPaneBorder extends PaneBorder { + + @Override + protected void paintTitleBackground(JXTaskPane group, Graphics g) { + if (group.isSpecial()) { + g.setColor(specialTitleBackground); + g.fillRoundRect( + 0, + 0, + group.getWidth(), + getRoundHeight() * 2, + getRoundHeight(), + getRoundHeight()); + g.fillRect( + 0, + getRoundHeight(), + group.getWidth(), + getTitleHeight(group) - getRoundHeight()); + } else { + Paint oldPaint = ((Graphics2D)g).getPaint(); + GradientPaint gradient = + new GradientPaint( + 0f, + 0f, //group.getWidth() / 2, + titleBackgroundGradientStart, + 0f, //group.getWidth(), + getTitleHeight(group), + titleBackgroundGradientEnd); + + ((Graphics2D)g).setRenderingHint( + RenderingHints.KEY_COLOR_RENDERING, + RenderingHints.VALUE_COLOR_RENDER_QUALITY); + ((Graphics2D)g).setRenderingHint( + RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BILINEAR); + ((Graphics2D)g).setRenderingHint( + RenderingHints.KEY_RENDERING, + RenderingHints.VALUE_RENDER_QUALITY); + ((Graphics2D)g).setPaint(gradient); + + g.fillRoundRect( + 0, + 0, + group.getWidth(), + getRoundHeight() * 2, + getRoundHeight(), + getRoundHeight()); + g.fillRect( + 0, + getRoundHeight(), + group.getWidth(), + getTitleHeight(group) - getRoundHeight()); + ((Graphics2D)g).setPaint(oldPaint); + } + + g.setColor(borderColor); + g.drawRoundRect( + 0, + 0, + group.getWidth() - 1, + getTitleHeight(group) + getRoundHeight(), + getRoundHeight(), + getRoundHeight()); + g.drawLine(0, getTitleHeight(group) - 1, group.getWidth(), getTitleHeight(group) - 1); + } + + @Override + protected void paintExpandedControls(JXTaskPane group, Graphics g, int x, + int y, int width, int height) { + ((Graphics2D)g).setRenderingHint( + RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + + paintOvalAroundControls(group, g, x, y, width, height); + g.setColor(getPaintColor(group)); + paintChevronControls(group, g, x, y, width, height); + + ((Graphics2D)g).setRenderingHint( + RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_OFF); + } + + @Override + protected boolean isMouseOverBorder() { + return true; + } + + } + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/misc/package-info.java b/src/main/java/org/jdesktop/swingx/plaf/misc/package-info.java new file mode 100644 index 0000000000..379a1784ae --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/misc/package-info.java @@ -0,0 +1,25 @@ +/* + * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +/** + * A package to collect miscellaneous UI delegates. + */ +package org.jdesktop.swingx.plaf.misc; + diff --git a/src/main/java/org/jdesktop/swingx/plaf/motif/MotifLookAndFeelAddons.java b/src/main/java/org/jdesktop/swingx/plaf/motif/MotifLookAndFeelAddons.java new file mode 100644 index 0000000000..7f504c89c8 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/motif/MotifLookAndFeelAddons.java @@ -0,0 +1,38 @@ +/* + * $Id: MotifLookAndFeelAddons.java 4092 2011-11-30 18:04:36Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf.motif; + +import org.jdesktop.swingx.plaf.LookAndFeelAddons; +import org.jdesktop.swingx.plaf.basic.BasicLookAndFeelAddons; +import org.kohsuke.MetaInfServices; + +import javax.swing.*; + +@MetaInfServices(LookAndFeelAddons.class) +public class MotifLookAndFeelAddons extends BasicLookAndFeelAddons { + /** + * {@inheritDoc} + */ + @Override + protected boolean matches() { + return UIManager.getLookAndFeel().getID().equals("Motif"); + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/motif/package-info.java b/src/main/java/org/jdesktop/swingx/plaf/motif/package-info.java new file mode 100644 index 0000000000..c310a5f549 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/motif/package-info.java @@ -0,0 +1,26 @@ +/* + * $Id: package-info.java 4028 2011-06-03 19:32:19Z kschaefe $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +/** + * Provides motif laf specific implementation of pluggable look-and-feel for SwingX components together with a + * mechanism to support custom component look-and-feels. + */ +package org.jdesktop.swingx.plaf.motif; + diff --git a/src/main/java/org/jdesktop/swingx/plaf/nimbus/NimbusLookAndFeelAddons.java b/src/main/java/org/jdesktop/swingx/plaf/nimbus/NimbusLookAndFeelAddons.java new file mode 100644 index 0000000000..8393b089e9 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/nimbus/NimbusLookAndFeelAddons.java @@ -0,0 +1,38 @@ +/* + * $Id: NimbusLookAndFeelAddons.java 4092 2011-11-30 18:04:36Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf.nimbus; + +import org.jdesktop.swingx.plaf.LookAndFeelAddons; +import org.jdesktop.swingx.plaf.basic.BasicLookAndFeelAddons; +import org.kohsuke.MetaInfServices; + +import javax.swing.*; + +@MetaInfServices(LookAndFeelAddons.class) +public class NimbusLookAndFeelAddons extends BasicLookAndFeelAddons { + /** + * {@inheritDoc} + */ + @Override + protected boolean matches() { + return UIManager.getLookAndFeel().getID().equals("Nimbus"); + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/nimbus/NimbusTaskPaneUI.java b/src/main/java/org/jdesktop/swingx/plaf/nimbus/NimbusTaskPaneUI.java new file mode 100644 index 0000000000..bc0d0b73a4 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/nimbus/NimbusTaskPaneUI.java @@ -0,0 +1,167 @@ +/* + * $Id$ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.plaf.nimbus; + +import org.jdesktop.swingx.JXTaskPane; +import org.jdesktop.swingx.plaf.basic.BasicTaskPaneUI; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.FontUIResource; +import java.awt.*; + +/** + * Nimbus implementation of the JXTaskPane UI.
      + * + * @author Radu Dumitrescu + */ +public class NimbusTaskPaneUI extends BasicTaskPaneUI { + + public static ComponentUI createUI(JComponent c) { + return new NimbusTaskPaneUI(); + } + + @Override +protected Border createPaneBorder() { + return new NimbusPaneBorder(); + } + + /** + * Overriden to paint the background of the component but keeping the rounded + * corners. + */ + @Override +public void update(Graphics g, JComponent c) { + if (c.isOpaque()) { + g.setColor(c.getParent().getBackground()); + g.fillRect(0, 0, c.getWidth(), c.getHeight()); + g.setColor(c.getBackground()); + g.fillRect(0, getRoundHeight(), c.getWidth(), c.getHeight() - +getRoundHeight()); + } + paint(g, c); + } + + /** + * The border of the task pane group paints the "text", the "icon", the + * "expanded" status and the "special" type. + * + */ + class NimbusPaneBorder extends PaneBorder { + + @Override + protected void paintTitleBackground(JXTaskPane group, Graphics g) { + + Paint oldPaint = ((Graphics2D) g).getPaint(); + + roundHeight = 7; + + if (group.isSpecial()) { + g.setColor(specialTitleBackground); + + g.fillRoundRect(0, 0, group.getWidth(), getRoundHeight() * 2, + getRoundHeight(), getRoundHeight()); + g.fillRect(0, getRoundHeight(), group.getWidth(), + getTitleHeight(group) - getRoundHeight()); + + } else { + Color[] colors = { titleBackgroundGradientStart, + titleBackgroundGradientEnd }; + + float[] fractions = { 0.0f, 1.0f }; + + LinearGradientPaint gradient = new LinearGradientPaint(group + .getWidth() / 2, 0.0f, group.getWidth() / 2, + getTitleHeight(group), fractions, colors); + + ((Graphics2D) g).setPaint(gradient); + + ((Graphics2D) g).setRenderingHint( + RenderingHints.KEY_COLOR_RENDERING, + RenderingHints.VALUE_COLOR_RENDER_QUALITY); + ((Graphics2D) g).setRenderingHint( + RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BILINEAR); + ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_RENDERING, + RenderingHints.VALUE_RENDER_QUALITY); + ((Graphics2D) g).setRenderingHint( + RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + + g.fillRoundRect(0, 0, group.getWidth(), + getTitleHeight(group) / 2, getRoundHeight(), + getRoundHeight()); + + g.fillRect(0, getRoundHeight(), group.getWidth(), + getTitleHeight(group) - getRoundHeight()); + + } + + // draw the border around the title area + g.setColor(borderColor); + + g.drawRoundRect(0, 0, group.getWidth() - 1, getTitleHeight(group) + + getRoundHeight(), getRoundHeight(), getRoundHeight()); + g.drawLine(0, getTitleHeight(group) - 1, group.getWidth(), + getTitleHeight(group) - 1); + + ((Graphics2D) g).setPaint(oldPaint); + } + + @Override + protected void paintExpandedControls(JXTaskPane group, Graphics g, + int x, int y, int width, int height) { + ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + + g.setColor(getPaintColor(group)); + paintChevronControls(group, g, x, y, width, height); + + ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_OFF); + } + + @Override + protected void paintTitle(JXTaskPane group, Graphics g, + Color textColor, int x, int y, int width, int height) { + configureLabel(group); + // Nimbus has some issues with ColorUIResource + label.setForeground(new Color(textColor.getRGB())); + if (group.getFont() != null + && !(group.getFont() instanceof FontUIResource)) { + label.setFont(group.getFont()); + } + g.translate(x, y); + label.setBounds(0, 0, width, height); + label.paint(g); + g.translate(-x, -y); + } + + @Override + protected boolean isMouseOverBorder() { + return true; + } + } + +} + diff --git a/src/main/java/org/jdesktop/swingx/plaf/nimbus/package-info.java b/src/main/java/org/jdesktop/swingx/plaf/nimbus/package-info.java new file mode 100644 index 0000000000..b0d6b64888 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/nimbus/package-info.java @@ -0,0 +1,26 @@ +/* + * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +/** + * Provides nimbus laf specific implementation of pluggable look-and-feel for SwingX components together with a + * mechanism to support custom component look-and-feels. + */ +package org.jdesktop.swingx.plaf.nimbus; + diff --git a/src/main/java/org/jdesktop/swingx/plaf/package-info.java b/src/main/java/org/jdesktop/swingx/plaf/package-info.java new file mode 100644 index 0000000000..4baeaf9891 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/package-info.java @@ -0,0 +1,32 @@ +/* + * $Id: package-info.java 4146 2012-01-25 19:40:05Z kschaefe $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +/** + * Provides pluggable look-and-feel for SwingX components together with a + * mechanism to support custom component look-and-feels. + *

      + * The addons are loaded with {@link java.util.ServiceLoader}. As such we + * maintain, a services file for our implementations. SwingX uses the + * MetaInf/services + * generator API. This add a compile time dependency to the plaf module. + * The services generator, however, is not required at runtime. + */ +package org.jdesktop.swingx.plaf; + diff --git a/src/main/java/org/jdesktop/swingx/plaf/synth/SynthBorder.java b/src/main/java/org/jdesktop/swingx/plaf/synth/SynthBorder.java new file mode 100644 index 0000000000..c26dd38855 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/synth/SynthBorder.java @@ -0,0 +1,125 @@ +/* + * $Id$ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.plaf.synth; + +/* + * @(#)SynthBorder.java 1.15 06/11/30 + * + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + */ + +import javax.swing.*; +import javax.swing.border.AbstractBorder; +import javax.swing.plaf.UIResource; +import javax.swing.plaf.synth.SynthContext; +import javax.swing.plaf.synth.SynthStyle; +import java.awt.*; + +/** + * SynthBorder is a border that delegates to a Painter. The Insets + * are determined at construction time.

      + * + * Copied from core + * + * @version 1.15, 11/30/06 + * @author Scott Violet + */ +class SynthBorder extends AbstractBorder implements UIResource { + private SynthUI ui; + private Insets insets; + + SynthBorder(SynthUI ui, Insets insets) { + this.ui = ui; + this.insets = insets; + } + + SynthBorder(SynthUI ui) { + this(ui, null); + } + + @Override + public void paintBorder(Component c, Graphics g, int x, int y, + int width, int height) { + JComponent jc = (JComponent)c; + SynthContext context = ui.getContext(jc); + SynthStyle style = context.getStyle(); + if (style == null) { + assert false: "SynthBorder is being used outside after the UI " + + "has been uninstalled"; + return; + } + ui.paintBorder(context, g, x, y, width, height); + } + + /** + * This default implementation returns a new Insets + * instance where the top, left, + * bottom, and + * right fields are set to 0. + * @param c the component for which this border insets value applies + * @return the new Insets object initialized to 0 + */ + @Override + public Insets getBorderInsets(Component c) { + return getBorderInsets(c, null); + } + + /** + * Reinitializes the insets parameter with this Border's current Insets. + * @param c the component for which this border insets value applies + * @param insets the object to be reinitialized + * @return the insets object + */ + @Override + public Insets getBorderInsets(Component c, Insets insets) { + if (this.insets != null) { + if (insets == null) { + insets = new Insets(this.insets.top, this.insets.left, + this.insets.bottom, this.insets.right); + } + else { + insets.top = this.insets.top; + insets.bottom = this.insets.bottom; + insets.left = this.insets.left; + insets.right = this.insets.right; + } + } + else if (insets == null) { + insets = new Insets(0, 0, 0, 0); + } + else { + insets.top = insets.bottom = insets.left = insets.right = 0; + } + return insets; + } + + /** + * This default implementation returns false. + * @return false + */ + @Override + public boolean isBorderOpaque() { + return false; + } + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/synth/SynthUI.java b/src/main/java/org/jdesktop/swingx/plaf/synth/SynthUI.java new file mode 100644 index 0000000000..c4f6cb54ae --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/synth/SynthUI.java @@ -0,0 +1,45 @@ +/* + * $Id$ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.plaf.synth; + +import javax.swing.*; +import javax.swing.plaf.synth.SynthContext; +import java.awt.*; + +/** + * Replacement of sun.swing.plaf.SynthUI.

      + * + * Note: this is a temporary emergency measure to make SwingX web-deployable. It is + * used internally only. Expect problems in future, as custom styles might not be + * found: SynthStyleFactory checks against type of sun SynthUI. + * + * @author Jeanette Winzenburg + */ +public interface SynthUI { + + public SynthContext getContext(JComponent arg0); + + public void paintBorder(SynthContext context, Graphics g, int x, + int y, int w, int h); + + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/synth/SynthUtils.java b/src/main/java/org/jdesktop/swingx/plaf/synth/SynthUtils.java new file mode 100644 index 0000000000..194620d019 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/synth/SynthUtils.java @@ -0,0 +1,200 @@ +/* + * $Id$ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.plaf.synth; + +import javax.swing.*; +import javax.swing.plaf.synth.*; +import java.awt.*; +import java.beans.PropertyChangeEvent; + +/** + * Utility class as stand-in for package private synth utility methods. + * + * @author Jeanette Winzenburg + */ +public class SynthUtils { + +//----------------------- context-related + + /** + * Used to avoid null painter checks everywhere. + */ + private static SynthPainter NULL_PAINTER = new SynthPainter() {}; + + /** + * Returns a SynthContext with the specified values. + * + * @param component JComponent + * @param region Identifies the portion of the JComponent + * @param style Style associated with the component + * @param state State of the component as defined in SynthConstants. + * @return a SynthContext with the specified values. + * + * @throws NullPointerException if component, region of style is null. + * + */ + public static SynthContext getContext(JComponent c, Region region, SynthStyle style, int state) { + return new SynthContext(c, region, style, state); + } + + /** + * @param context + * @param style + * @return + */ + public static SynthContext getContext(SynthContext context, SynthStyle style) { + if (context.getStyle().equals(style)) return context; + return getContext(context.getComponent(), context.getRegion(), style, context.getComponentState()); + } + /** + * Returns a context with the given component state and all other fields same as input context. + * + * @param context the context, must not be null + * @param state the component state. + * @return a context with the given component state and other fields as inpu context. + */ + public static SynthContext getContext(SynthContext context, int state) { + if (context.getComponentState() == state) return context; + return getContext(context.getComponent(), context.getRegion(), context.getStyle(), state); + } + + /** + * Returns a SynthPainter from the context's style. Fall-back to default if + * none available. + * + * @param context SynthContext containing the style, must not be null. + * @return a SynthPainter from the context's style, or a default if null. + */ + public static SynthPainter getPainter(SynthContext context) { + SynthPainter painter = context.getStyle().getPainter(context); + return painter != null ? painter : NULL_PAINTER; + } + +//------------------- style-related + + /** + * Returns true if the Style should be updated in response to the + * specified PropertyChangeEvent. This forwards to + * shouldUpdateStyleOnAncestorChanged as necessary. + */ + public static boolean shouldUpdateStyle(PropertyChangeEvent event) { + String eName = event.getPropertyName(); + if ("name" == eName) { + // Always update on a name change + return true; + } + else if ("componentOrientation" == eName) { + // Always update on a component orientation change + return true; + } + else if ("ancestor" == eName && event.getNewValue() != null) { + // Only update on an ancestor change when getting a valid + // parent and the LookAndFeel wants this. + LookAndFeel laf = UIManager.getLookAndFeel(); + return (laf instanceof SynthLookAndFeel && + ((SynthLookAndFeel)laf). + shouldUpdateStyleOnAncestorChanged()); + } + // Note: The following two nimbus based overrides should be refactored + // to be in the Nimbus LAF. Due to constraints in an update release, + // we couldn't actually provide the public API necessary to allow + // NimbusLookAndFeel (a subclass of SynthLookAndFeel) to provide its + // own rules for shouldUpdateStyle. + else if ("Nimbus.Overrides" == eName) { + // Always update when the Nimbus.Overrides client property has + // been changed + return true; + } + else if ("Nimbus.Overrides.InheritDefaults" == eName) { + // Always update when the Nimbus.Overrides.InheritDefaults + // client property has changed + return true; + } + else if ("JComponent.sizeVariant" == eName) { + // Always update when the JComponent.sizeVariant + // client property has changed + return true; + } + return false; + } + +//--------------- component related + + public static int getComponentState(JComponent c) { + if (c.isEnabled()) { + if (c.isFocusOwner()) { + return SynthConstants.ENABLED | SynthConstants.FOCUSED; + } + return SynthConstants.ENABLED; + } + return SynthConstants.DISABLED; + } + + // ---------------- divers ... + + /** + * A convenience method that handles painting of the background. All SynthUI + * implementations should override update and invoke this method. + * + * @param context must not be null + * @param g must not be null + */ + public static void update(SynthContext context, Graphics g) { + update(context, g, null); + } + + /** + * A convenience method that handles painting of the background. All SynthUI + * implementations should override update and invoke this method. + * + * @param context must not be null + * @param g must not be null + * @param the bounds to fill, may be null to indicate the complete size + */ + public static void update(SynthContext context, Graphics g, Rectangle bounds) { + JComponent c = context.getComponent(); + SynthStyle style = context.getStyle(); + int x, y, width, height; + + if (bounds == null) { + x = 0; + y = 0; + width = c.getWidth(); + height = c.getHeight(); + } else { + x = bounds.x; + y = bounds.y; + width = bounds.width; + height = bounds.height; + } + + // Fill in the background, if necessary. + boolean subregion = context.getRegion().isSubregion(); + if ((subregion && style.isOpaque(context)) + || (!subregion && c.isOpaque())) { + g.setColor(style.getColor(context, ColorType.BACKGROUND)); + g.fillRect(x, y, width, height); + } + } + +} + diff --git a/src/main/java/org/jdesktop/swingx/plaf/synth/SynthXListUI.java b/src/main/java/org/jdesktop/swingx/plaf/synth/SynthXListUI.java new file mode 100644 index 0000000000..833ce6c9c0 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/synth/SynthXListUI.java @@ -0,0 +1,280 @@ +/* + * $Id$ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.plaf.synth; + +import org.jdesktop.swingx.SwingXUtilities; +import org.jdesktop.swingx.plaf.basic.core.BasicXListUI; + +import javax.swing.*; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.UIResource; +import javax.swing.plaf.synth.*; +import java.awt.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +/** + * TODO add type doc + * + * @author Jeanette Winzenburg + */ +public class SynthXListUI extends BasicXListUI + // PENDING JW: SynthUI is sun package (here: used by c&p'ed SynthBorder) - replace? + // maybe not: SynthLookUp looks up styles from delegates of type SynthUI only + implements SynthConstants, SynthUI /*, PropertyChangeListener */{ + + private SynthStyle style; + @SuppressWarnings("unused") + private boolean useListColors; + @SuppressWarnings("unused") + private boolean useUIBorder; + + /** + * Returns a new instance of SynthXListUI. SynthXListUI delegates are + * allocated one per JList. + * + * @return A new ListUI implementation for the Synth look and feel. + */ + public static ComponentUI createUI(JComponent list) { + return new SynthXListUI(); + } + + /** + * {@inheritDoc}

      + * Overridden to fill background, Synth-style. + */ + @Override + public void update(Graphics g, JComponent c) { + SynthContext context = getContext(c); + SynthUtils.update(context, g); + paintBorder(context, g, 0, 0, c.getWidth(), c.getHeight()); + paint(g, c); + } + + + /** + * {@inheritDoc}

      + * Overridden to update style if appropriate. + */ + @Override + protected PropertyChangeListener createPropertyChangeListener() { + PropertyChangeListener l = new PropertyChangeHandler() { + + @Override + public void propertyChange(PropertyChangeEvent e) { + if (SynthUtils.shouldUpdateStyle(e)) { + updateStyle(); + } + super.propertyChange(e); + } + + }; + return l; + } + + /** + * {@inheritDoc}

      + * Overridden to install properties, Synth-style. + */ + @Override + protected void installDefaults() { + // never happens - the delegate renderer is always not-null and + // not a ui-resource +// if (list.getCellRenderer() == null || +// (list.getCellRenderer() instanceof UIResource)) { +// list.setCellRenderer(new SynthListCellRenderer()); +// } + updateStyle(); + } + + private void updateStyle() { + // compare local reference to style from factory + // nothing to do if same + if (style == getStyle()) return; + // check if this is called from init or from later update + // if from later updates, need to cleanup old + boolean refresh = style != null; + if (refresh) { + style.uninstallDefaults(getContext(ENABLED)); + } + // update local reference + style = getStyle(); + // special case border + installSynthBorder(); + // install defaults + style.installDefaults(getContext(ENABLED)); + // install selected properties + SynthContext selectedContext = getContext(SELECTED); + Color sbg = list.getSelectionBackground(); + if (sbg == null || sbg instanceof UIResource) { + list.setSelectionBackground(style.getColor( + selectedContext, ColorType.TEXT_BACKGROUND)); + } + + Color sfg = list.getSelectionForeground(); + if (sfg == null || sfg instanceof UIResource) { + list.setSelectionForeground(style.getColor( + selectedContext, ColorType.TEXT_FOREGROUND)); + } + // install cell height + int height = style.getInt(selectedContext, "List.cellHeight", -1); + if (height != -1) { + list.setFixedCellHeight(height); + } + // we do this because ... ?? + if (refresh) { + uninstallKeyboardActions(); + installKeyboardActions(); + } + // install currently unused properties of this delegate + useListColors = style.getBoolean(selectedContext, + "List.rendererUseListColors", true); + useUIBorder = style.getBoolean(selectedContext, + "List.rendererUseUIBorder", true); + + } + + /** + * Installs a SynthBorder from the current style, if ui-installable. + * + * @param context the context + */ + protected void installSynthBorder() { + if (SwingXUtilities.isUIInstallable(list.getBorder())) { + list.setBorder(new SynthBorder(this, style.getInsets(getContext(ENABLED), null))); + } + } + + /** + * {@inheritDoc}

      + * Overridden to uninstall properties, Synth-style, after calling super. + */ + @Override + protected void uninstallDefaults() { + super.uninstallDefaults(); + style.uninstallDefaults(getContext(ENABLED)); + style = null; + } + + + /** + * Paints border with the context's style's painter. + * Implemented for SynthUI interface. + */ + @Override + public void paintBorder(SynthContext context, Graphics g, int x, int y, + int w, int h) { + SynthUtils.getPainter(context).paintListBorder(context, g, x, y, w, h); + } + + /** + * {@inheritDoc}

      + * + * Returns a context for the component's current state. + * Implemented for SynthUI interface.

      + * + * PENDING JW: not entirely sure if allowed ... but need to replace SynthUI anyway?. + * + * @throws IllegalArgumentException if the component is not controlled by this + * delegate + */ + @Override + public SynthContext getContext(JComponent c) { + if (c != list) throw new IllegalArgumentException("must be ui-delegate for component"); + return getContext(); + } + + /** + * Returns the context based on current state. + * @return + */ + private SynthContext getContext() { + return getContext(getComponentState()); + } + + /** + * Returns the current component state for the controlled list. + * @return + */ + private int getComponentState() { + return SynthUtils.getComponentState(list); + } + + /** + * Returns a Context with the given component state. + * + * @param state + * @return + */ + private SynthContext getContext(int state) { + return SynthUtils.getContext(list, getRegion(), style, state); + } + + private Region getRegion() { + return XRegion.getXRegion(list, true); + } + + + /** + * Returns the style for this component from the style factory. + * @return + */ + private SynthStyle getStyle() { + return SynthLookAndFeel.getStyleFactory().getStyle(list, getRegion()); + } + + +// private class SynthListCellRenderer extends DefaultListCellRenderer.UIResource { +// public String getName() { +// return "List.cellRenderer"; +// } +// +// public void setBorder(Border b) { +// if (useUIBorder || b instanceof SynthBorder) { +// super.setBorder(b); +// } +// } +// +// public Component getListCellRendererComponent(JList list, Object value, +// int index, boolean isSelected, boolean cellHasFocus) { +// if (!useListColors && (isSelected || cellHasFocus)) { +// SynthLookAndFeel.setSelectedUI((SynthLabelUI)SynthLookAndFeel. +// getUIOfType(getUI(), SynthLabelUI.class), +// isSelected, cellHasFocus, list.isEnabled(), false); +// } +// else { +// SynthLookAndFeel.resetSelectedUI(); +// } +// +// super.getListCellRendererComponent(list, value, index, +// isSelected, cellHasFocus); +// return this; +// } +// +// public void paint(Graphics g) { +// super.paint(g); +// SynthLookAndFeel.resetSelectedUI(); +// } +// } + + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/synth/XRegion.java b/src/main/java/org/jdesktop/swingx/plaf/synth/XRegion.java new file mode 100644 index 0000000000..0bc72b3ee4 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/synth/XRegion.java @@ -0,0 +1,73 @@ +/* + * $Id$ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.plaf.synth; + +import javax.swing.*; +import javax.swing.plaf.synth.Region; +import java.util.HashMap; +import java.util.Map; + +/** + * Extended Region to register custom component delegates. + * + * @author Jeanette Winzenburg + */ +public class XRegion extends Region { + + static Map uiToXRegionMap = new HashMap(); + public static final Region XLIST = new XRegion("XList", null, false, "XListUI", LIST); + + /** the Region which identifies the base styles */ + private Region parent; + + /** + * Creates a XRegion with the specified name. + * + * @param name Name of the region + * @param subregion Whether or not this is a subregion. + * @param realUI String that will be returned from + * component.getUIClassID. + * @param parent the parent region which this is extending. + */ + public XRegion(String name, String dummyUI, boolean subregion, String realUI, Region parent) { + super(name, dummyUI, subregion); + this.parent = parent; + if (realUI != null) { + uiToXRegionMap.put(realUI, this); + } + } + + /** + * Returns a region appropriate for the component. + * + * @param component the component to get the region for + * @param useParent a boolean indicating whether or not to return a fallback + * of the XRegion, if available + * @return a region for the component or null if not available. + */ + public static Region getXRegion(JComponent component, boolean useParent) { + XRegion region = uiToXRegionMap.get(component.getUIClassID()); + if (region != null) + return useParent && region.parent != null ? region.parent : region; + return region; + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsClassicLookAndFeelAddons.java b/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsClassicLookAndFeelAddons.java new file mode 100644 index 0000000000..a8601f640a --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsClassicLookAndFeelAddons.java @@ -0,0 +1,61 @@ +/* + * $Id: WindowsClassicLookAndFeelAddons.java 4092 2011-11-30 18:04:36Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf.windows; + +import org.jdesktop.swingx.plaf.LookAndFeelAddons; +import org.jdesktop.swingx.util.OS; +import org.kohsuke.MetaInfServices; + +import static javax.swing.UIManager.getLookAndFeel; +import static javax.swing.UIManager.getSystemLookAndFeelClassName; + +/** + * Adds new pluggable UI following the Windows Classic look and feel. Currently + * it extends the XP look and feel and overrides the JTaskPane + * and JTaskPaneGroup UIs. + */ +@MetaInfServices(LookAndFeelAddons.class) +public class WindowsClassicLookAndFeelAddons extends WindowsLookAndFeelAddons { + + /** + * {@inheritDoc} + */ + @Override + protected boolean matches() { + String laf = getLookAndFeel().getClass().getName(); + + if (isSystemAddon()) { + //special-case the jgoodies to ensure that we can match it + return getSystemLookAndFeelClassName().equals(laf) + || "com.jgoodies.looks.windows.WindowsLookAndFeel".equals(laf); + } + + return "com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel".equals(laf); + } + + /** + * {@inheritDoc} + */ + @Override + protected boolean isSystemAddon() { + return OS.isWindows() && !OS.isUsingWindowsVisualStyles(); + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsClassicStatusBarUI.java b/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsClassicStatusBarUI.java new file mode 100644 index 0000000000..30b8bf89e7 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsClassicStatusBarUI.java @@ -0,0 +1,87 @@ +/* + * $Id: WindowsClassicStatusBarUI.java 3472 2009-08-27 13:12:42Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.plaf.windows; + +import org.jdesktop.swingx.JXStatusBar; +import org.jdesktop.swingx.plaf.basic.BasicStatusBarUI; + +import javax.swing.*; +import javax.swing.border.BevelBorder; +import javax.swing.border.Border; +import javax.swing.plaf.BorderUIResource; +import javax.swing.plaf.ComponentUI; +import java.awt.*; + +/** + * + * @author rbair + */ +public class WindowsClassicStatusBarUI extends BasicStatusBarUI { + /** Creates a new instance of BasicStatusBarUI */ + public WindowsClassicStatusBarUI() { + } + + /** + * Returns an instance of the UI delegate for the specified component. + * Each subclass must provide its own static createUI + * method that returns an instance of that UI delegate subclass. + * If the UI delegate subclass is stateless, it may return an instance + * that is shared by multiple components. If the UI delegate is + * stateful, then it should return a new instance per component. + * The default implementation of this method throws an error, as it + * should never be invoked. + */ + public static ComponentUI createUI(JComponent c) { + return new WindowsClassicStatusBarUI(); + } + + @Override protected void paintBackground(Graphics2D g, JXStatusBar bar) { + g.setColor(bar.getBackground()); + g.fillRect(0, 0, bar.getWidth(), bar.getHeight()); + + //paint an inset border around each component. This suggests that + //there is an extra border around the status bar...! + Border b = BorderFactory.createBevelBorder(BevelBorder.LOWERED, + Color.WHITE, bar.getBackground(), bar.getBackground(), Color.GRAY); + Insets insets = new Insets(0, 0, 0, 0); + for (Component c : bar.getComponents()) { + getSeparatorInsets(insets); + int x = c.getX() - insets.right; + int y = c.getY() - 2; + int w = c.getWidth() + insets.left + insets.right; + int h = c.getHeight() + 4; + b.paintBorder(c, g, x, y, w, h); + } + } + + @Override protected void paintSeparator(Graphics2D g, JXStatusBar bar, int x, int y, int w, int h) { + //paint nothing, since paintBackground handles this + } + + @Override protected int getSeparatorWidth() { + return 11; + } + + @Override protected BorderUIResource createBorder() { + return new BorderUIResource(BorderFactory.createEmptyBorder(4, 5, 3, 22)); + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsClassicTaskPaneUI.java b/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsClassicTaskPaneUI.java new file mode 100644 index 0000000000..b316480b29 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsClassicTaskPaneUI.java @@ -0,0 +1,80 @@ +/* + * $Id: WindowsClassicTaskPaneUI.java 3448 2009-08-19 08:12:23Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf.windows; + +import org.jdesktop.swingx.JXTaskPane; +import org.jdesktop.swingx.plaf.basic.BasicTaskPaneUI; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.plaf.ComponentUI; +import java.awt.*; + +/** + * Windows Classic (NT/2000) implementation of the + * JXTaskPane UI. + * + * @author Frederic Lavigne + */ +public class WindowsClassicTaskPaneUI extends BasicTaskPaneUI { + + public static ComponentUI createUI(JComponent c) { + return new WindowsClassicTaskPaneUI(); + } + + @Override + protected void installDefaults() { + super.installDefaults(); + + LookAndFeel.installProperty(group, "opaque", false); + } + + @Override + protected Border createPaneBorder() { + return new ClassicPaneBorder(); + } + + /** + * The border of the taskpane group paints the "text", the "icon", the + * "expanded" status and the "special" type. + * + */ + class ClassicPaneBorder extends PaneBorder { + + @Override + protected void paintExpandedControls(JXTaskPane group, Graphics g, int x, + int y, int width, int height) { + ((Graphics2D)g).setRenderingHint( + RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + + paintRectAroundControls(group, g, x, y, width, height, Color.white, + Color.gray); + g.setColor(getPaintColor(group)); + paintChevronControls(group, g, x, y, width, height); + + ((Graphics2D)g).setRenderingHint( + RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_OFF); + } + } + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsLookAndFeelAddons.java b/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsLookAndFeelAddons.java new file mode 100644 index 0000000000..629994fa45 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsLookAndFeelAddons.java @@ -0,0 +1,93 @@ +/* + * $Id: WindowsLookAndFeelAddons.java 4092 2011-11-30 18:04:36Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf.windows; + +import org.jdesktop.swingx.plaf.LookAndFeelAddons; +import org.jdesktop.swingx.plaf.basic.BasicLookAndFeelAddons; +import org.jdesktop.swingx.util.OS; +import org.kohsuke.MetaInfServices; + +import static javax.swing.UIManager.getLookAndFeel; +import static javax.swing.UIManager.getSystemLookAndFeelClassName; + +/** + * Adds new pluggable UI following the Windows XP look and feel. + */ +@MetaInfServices(LookAndFeelAddons.class) +public class WindowsLookAndFeelAddons extends BasicLookAndFeelAddons { + + public static final String HOMESTEAD_VISUAL_STYLE = "HomeStead"; + + public static final String SILVER_VISUAL_STYLE = "Metallic"; + + public static final String VISTA_VISUAL_STYLE = "NormalColor"; + + /** + * {@inheritDoc} + */ + @Override + protected boolean matches() { + if (isSystemAddon()) { + String laf = getLookAndFeel().getClass().getName(); + + //special-case the jgoodies to ensure that we can match it + return getSystemLookAndFeelClassName().equals(laf) + || "com.jgoodies.looks.windows.WindowsLookAndFeel".equals(laf); + } + + return false; + } + + /** + * {@inheritDoc} + */ + @Override + protected boolean isSystemAddon() { + return OS.isWindows() && OS.isUsingWindowsVisualStyles(); + } + +// JW: reverting ... +// /** +// * {@inheritDoc}

      +// * +// */ +// @Override +// public void initialize() { +// super.initialize(); +// // fix Issue #1305-swingx: wrapper for core issue #6753637 +// // set ui property to prevent eating mousePressed when closing popup +// UIManager.put("PopupMenu.consumeEventOnClose", Boolean.FALSE); +// } +// +// /** +// * {@inheritDoc}

      +// */ +// @Override +// public void uninitialize() { +// // fix Issue #1305-swingx: wrapper for core issue #6753637 +// // remove the ui property again +// UIManager.put("PopupMenu.consumeEventOnClose", null); +// super.uninitialize(); +// } +// + + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsStatusBarUI.java b/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsStatusBarUI.java new file mode 100644 index 0000000000..a4b501a6ca --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsStatusBarUI.java @@ -0,0 +1,101 @@ +/* + * $Id: WindowsStatusBarUI.java 3472 2009-08-27 13:12:42Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.plaf.windows; + +import org.jdesktop.swingx.JXStatusBar; +import org.jdesktop.swingx.plaf.UIManagerExt; +import org.jdesktop.swingx.plaf.basic.BasicStatusBarUI; + +import javax.imageio.ImageIO; +import javax.swing.*; +import javax.swing.plaf.ComponentUI; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * @author rbair + */ +public class WindowsStatusBarUI extends BasicStatusBarUI { + private static final Logger log = Logger.getLogger(WindowsStatusBarUI.class.getName()); + private BufferedImage leftImage; + private BufferedImage middleImage; + private BufferedImage rightImage; + + + /** Creates a new instance of WindowsStatusBarUI */ + public WindowsStatusBarUI() { + //SwingX #827: must create these here or size is incorrect + //TODO need to determine a better way to handle these images + try { + leftImage = ImageIO.read(WindowsStatusBarUI.class.getResource(UIManagerExt.getString("StatusBar.leftImage"))); + middleImage = ImageIO.read(WindowsStatusBarUI.class.getResource(UIManagerExt.getString("StatusBar.middleImage"))); + rightImage = ImageIO.read(WindowsStatusBarUI.class.getResource(UIManagerExt.getString("StatusBar.rightImage"))); + } catch (Exception e) { + // log the message in case of init failure + log.log(Level.FINE, e.getLocalizedMessage(), e); + } + } + + /** + * Returns an instance of the UI delegate for the specified component. + * Each subclass must provide its own static createUI + * method that returns an instance of that UI delegate subclass. + * If the UI delegate subclass is stateless, it may return an instance + * that is shared by multiple components. If the UI delegate is + * stateful, then it should return a new instance per component. + * The default implementation of this method throws an error, as it + * should never be invoked. + */ + public static ComponentUI createUI(JComponent c) { + return new WindowsStatusBarUI(); + } + + @Override protected void paintBackground(Graphics2D g, JXStatusBar statusBar) { + if (leftImage == null || middleImage == null || rightImage == null) { + log.severe("Failed to initialize necessary assets. Set logging to FINE to see more details."); + return; + } + //if bidi, reverse the image painting order + //TODO need to handle vertical stretching better + g.drawImage(leftImage, 0, 0, leftImage.getWidth(), statusBar.getHeight(), null); + + if (statusBar.isResizeHandleEnabled()) { + g.drawImage(middleImage, leftImage.getWidth(), 0, statusBar.getWidth() - leftImage.getWidth() - rightImage.getWidth(), statusBar.getHeight(), null); + g.drawImage(rightImage, statusBar.getWidth() - rightImage.getWidth(), 0, rightImage.getWidth(), statusBar.getHeight(), null); + } else { + g.drawImage(middleImage, leftImage.getWidth(), 0, statusBar.getWidth() - leftImage.getWidth(), statusBar.getHeight(), null); + } + } + + @Override protected Insets getSeparatorInsets(Insets insets) { + if (insets == null) { + insets = new Insets(0, 0, 0, 0); + } + insets.top = 1; + insets.left = 4; + insets.bottom = 0; + insets.right = 4; + return insets; + } +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsTaskPaneUI.java b/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsTaskPaneUI.java new file mode 100644 index 0000000000..f359baed9d --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsTaskPaneUI.java @@ -0,0 +1,148 @@ +/* + * $Id: WindowsTaskPaneUI.java 3448 2009-08-19 08:12:23Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf.windows; + +import org.jdesktop.swingx.JXTaskPane; +import org.jdesktop.swingx.plaf.basic.BasicTaskPaneUI; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.plaf.ComponentUI; +import java.awt.*; + +/** + * Windows implementation of the TaskPaneUI. + * + * @author Frederic Lavigne + */ +public class WindowsTaskPaneUI extends BasicTaskPaneUI { + + public static ComponentUI createUI(JComponent c) { + return new WindowsTaskPaneUI(); + } + + @Override + protected Border createPaneBorder() { + return new XPPaneBorder(); + } + + /** + * Overriden to paint the background of the component but keeping the rounded + * corners. + */ + @Override + public void update(Graphics g, JComponent c) { + if (c.isOpaque()) { + g.setColor(c.getParent().getBackground()); + g.fillRect(0, 0, c.getWidth(), c.getHeight()); + g.setColor(c.getBackground()); + g.fillRect(0, getRoundHeight(), c.getWidth(), c.getHeight() - getRoundHeight()); + } + paint(g, c); + } + + /** + * The border of the taskpane group paints the "text", the "icon", the + * "expanded" status and the "special" type. + * + */ + class XPPaneBorder extends PaneBorder { + + @Override + protected void paintTitleBackground(JXTaskPane group, Graphics g) { + if (group.isSpecial()) { + g.setColor(specialTitleBackground); + g.fillRoundRect( + 0, + 0, + group.getWidth(), + getRoundHeight() * 2, + getRoundHeight(), + getRoundHeight()); + g.fillRect( + 0, + getRoundHeight(), + group.getWidth(), + getTitleHeight(group) - getRoundHeight()); + } else { + Paint oldPaint = ((Graphics2D)g).getPaint(); + GradientPaint gradient = new GradientPaint( + 0f, + group.getWidth() / 2, + group.getComponentOrientation().isLeftToRight()? + titleBackgroundGradientStart + :titleBackgroundGradientEnd, + group.getWidth(), + getTitleHeight(group), + group.getComponentOrientation().isLeftToRight()? + titleBackgroundGradientEnd + :titleBackgroundGradientStart); + + ((Graphics2D)g).setRenderingHint( + RenderingHints.KEY_COLOR_RENDERING, + RenderingHints.VALUE_COLOR_RENDER_QUALITY); + ((Graphics2D)g).setRenderingHint( + RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BILINEAR); + ((Graphics2D)g).setRenderingHint( + RenderingHints.KEY_RENDERING, + RenderingHints.VALUE_RENDER_QUALITY); + ((Graphics2D)g).setPaint(gradient); + g.fillRoundRect( + 0, + 0, + group.getWidth(), + getRoundHeight() * 2, + getRoundHeight(), + getRoundHeight()); + g.fillRect( + 0, + getRoundHeight(), + group.getWidth(), + getTitleHeight(group) - getRoundHeight()); + ((Graphics2D)g).setPaint(oldPaint); + } + } + + @Override + protected void paintExpandedControls(JXTaskPane group, Graphics g, int x, + int y, int width, int height) { + ((Graphics2D)g).setRenderingHint( + RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + + paintOvalAroundControls(group, g, x, y, width, height); + g.setColor(getPaintColor(group)); + paintChevronControls(group, g, x, y, width, height); + + ((Graphics2D)g).setRenderingHint( + RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_OFF); + } + + @Override + protected boolean isMouseOverBorder() { + return true; + } + + } + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsTipOfTheDayUI.java b/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsTipOfTheDayUI.java new file mode 100644 index 0000000000..d029980c64 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsTipOfTheDayUI.java @@ -0,0 +1,116 @@ +/* + * $Id: WindowsTipOfTheDayUI.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.plaf.windows; + +import org.jdesktop.swingx.JXTipOfTheDay; +import org.jdesktop.swingx.JXTipOfTheDay.ShowOnStartupChoice; +import org.jdesktop.swingx.plaf.UIManagerExt; +import org.jdesktop.swingx.plaf.basic.BasicTipOfTheDayUI; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.plaf.ComponentUI; +import java.awt.*; + +/** + * Windows implementation of the TipOfTheDayUI. + * + * @author Frederic Lavigne + */ +public class WindowsTipOfTheDayUI extends BasicTipOfTheDayUI { + + public static ComponentUI createUI(JComponent c) { + return new WindowsTipOfTheDayUI((JXTipOfTheDay)c); + } + + public WindowsTipOfTheDayUI(JXTipOfTheDay tipPane) { + super(tipPane); + } + + @Override + public JDialog createDialog(Component parentComponent, + final ShowOnStartupChoice choice) { + return createDialog(parentComponent, choice, false); + } + + @Override +protected void installComponents() { + tipPane.setLayout(new BorderLayout()); + + // tip icon + JLabel tipIcon = new JLabel(); + tipIcon.setPreferredSize(new Dimension(60, 100)); + tipIcon.setIcon(UIManager.getIcon("TipOfTheDay.icon")); + tipIcon.setHorizontalAlignment(JLabel.CENTER); + tipIcon.setVerticalAlignment(JLabel.TOP); + tipIcon.setBorder(BorderFactory.createEmptyBorder(24, 0, 0, 0)); + tipPane.add("West", tipIcon); + + // tip area + JPanel rightPane = new JPanel(new BorderLayout()); + JLabel didYouKnow = new JLabel(UIManagerExt + .getString("TipOfTheDay.didYouKnowText", tipPane.getLocale())); + didYouKnow.setPreferredSize(new Dimension(50, 32)); + didYouKnow.setOpaque(true); + didYouKnow.setBackground(UIManager.getColor("TextArea.background")); + didYouKnow.setBorder(new CompoundBorder(BorderFactory.createMatteBorder(0, + 0, 2, 0, tipPane.getBackground()), BorderFactory.createEmptyBorder(4, 4, + 4, 4))); + didYouKnow.setFont(tipPane.getFont().deriveFont(Font.BOLD, 15)); + rightPane.add("North", didYouKnow); + + tipArea = new JPanel(new BorderLayout()); + tipArea.setOpaque(true); + tipArea.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + tipArea.setBackground(UIManager.getColor("TextArea.background")); + rightPane.add("Center", tipArea); + + tipPane.add("Center", rightPane); + } + + public static class TipAreaBorder implements Border { + @Override + public Insets getBorderInsets(Component c) { + return new Insets(2, 2, 2, 2); + } + @Override + public boolean isBorderOpaque() { + return false; + } + @Override + public void paintBorder(Component c, Graphics g, int x, int y, int width, + int height) { + g.setColor(UIManager.getColor("TipOfTheDay.background")); + g.drawLine(x, y, x + width - 1, y); + g.drawLine(x, y, x, y + height - 1); + + g.setColor(Color.black); + g.drawLine(x + 1, y + 1, x + width - 3, y + 1); + g.drawLine(x + 1, y + 1, x + 1, y + height - 3); + + g.setColor(Color.white); + g.drawLine(x, y + height - 1, x + width, y + height - 1); + g.drawLine(x + width - 1, y, x + width - 1, y + height - 1); + } + } + +} diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/package-info.java b/src/main/java/org/jdesktop/swingx/plaf/windows/package-info.java new file mode 100644 index 0000000000..25a1494e7f --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/windows/package-info.java @@ -0,0 +1,26 @@ +/* + * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +/** + * Provides window laf specific implementation of pluggable look-and-feel for SwingX components together with a + * mechanism to support custom component look-and-feels. + */ +package org.jdesktop.swingx.plaf.windows; + diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay.properties b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay.properties new file mode 100644 index 0000000000..0f04eb3e52 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay.properties @@ -0,0 +1,2 @@ +TipOfTheDay.previousTipText=Previous Tip +TipOfTheDay.nextTipText=Next Tip diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_cs.properties b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_cs.properties new file mode 100644 index 0000000000..8ed8af4d0a --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_cs.properties @@ -0,0 +1,3 @@ +#Czech resource bundle created by Vity (vitywap@seznam.cz) 08/2007 +TipOfTheDay.previousTipText=P\u0159edchoz\u00ed Tip +TipOfTheDay.nextTipText=N\u00e1sleduj\u00edc\u00ed Tip diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_de.properties b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_de.properties new file mode 100644 index 0000000000..af1384a0c8 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_de.properties @@ -0,0 +1,2 @@ +TipOfTheDay.previousTipText=Vorheriger Tipp +TipOfTheDay.nextTipText=N\u00e4chster Tipp diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_es.properties b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_es.properties new file mode 100644 index 0000000000..e895cb6af2 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_es.properties @@ -0,0 +1,2 @@ +TipOfTheDay.previousTipText=Sugerencia Anterior +TipOfTheDay.nextTipText=Siguiente Sugerencia diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_fr.properties b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_fr.properties new file mode 100644 index 0000000000..78eb2a5d3c --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_fr.properties @@ -0,0 +1,2 @@ +TipOfTheDay.previousTipText=Astuce Pr\u00e9c\u00e9dente +TipOfTheDay.nextTipText=Astuce Suivante diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_it.properties b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_it.properties new file mode 100644 index 0000000000..1dbe765fec --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_it.properties @@ -0,0 +1,2 @@ +TipOfTheDay.previousTipText=Consiglio Precedente +TipOfTheDay.nextTipText=Consiglio Successivo diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_nl.properties b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_nl.properties new file mode 100644 index 0000000000..f35001c76a --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_nl.properties @@ -0,0 +1,2 @@ +TipOfTheDay.previousTipText=Vorige Tip +TipOfTheDay.nextTipText=Volgende Tip diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_pl.properties b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_pl.properties new file mode 100644 index 0000000000..452a9ebe89 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_pl.properties @@ -0,0 +1,2 @@ +TipOfTheDay.previousTipText=Poprzednia porada +TipOfTheDay.nextTipText=Nast\u0119pna porada diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_pt_BR.properties b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_pt_BR.properties new file mode 100644 index 0000000000..dadd7e7db8 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_pt_BR.properties @@ -0,0 +1,2 @@ +TipOfTheDay.previousTipText=Dica Anterior +TipOfTheDay.nextTipText=Pr\u00F3xima Dica diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_sv.properties b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_sv.properties new file mode 100644 index 0000000000..b5105eb93f --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_sv.properties @@ -0,0 +1,2 @@ +TipOfTheDay.previousTipText=Föreg\u00E5ende tips +TipOfTheDay.nextTipText=N\u00E4sta tips diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/clear.gif b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/clear.gif new file mode 100644 index 0000000000000000000000000000000000000000..a28caff081ec5d329bfb072c909b8787550f7f30 GIT binary patch literal 222 zcmZ?wbhEHblwuHJXpv%2_Nh{j>ekMfqhGovIJe)pac9lMRdv%hr7XG7IDdD`@}q6* z&v$IS*0t;QvuDpGS8$KYsl9_3PK~-@pI-`SbVh-+%x9{l@@` zKUo;L7~~mr7=Qp|Cj)Er1NFX?%y}8B*5$n3SD7IE03{=HuK)l5 literal 0 HcmV?d00001 diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/clear_pressed.gif b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/clear_pressed.gif new file mode 100644 index 0000000000000000000000000000000000000000..250e6b50023b3abd7d18c0fa9b43ca0352a28e64 GIT binary patch literal 660 zcmZ?wbh9u|lwuHJI2OU6>{F#4)vc4%t(`GPzjRG-ZohHk&Zvs6;QE=NjkCg=W=A&7 zj%uD8+cGz{bzXeiyo~mlnH{rodl%&O&d={#P&08=-SkZ@7%Yujc$BdA;oT&8g?! zOgr~x#)Y>tFT9;|>D`=5@8?~5H~;dx1(!c8y8L$jl@E(Azg>Lw!{VzSw_bd>?E1%L z*FUYi`DxY7kK1m%+;!*u&U+tsKKQcl(U-#yKc2n!{NUrShn{>p`|!>Ahp#U_dVS-; zlgm%voqhS^*6YvL-+aIM_WP}OKkvQ!{^-N^#~*(@`SkPY=ijfs{(k-K&;J1eIt)Mn ziVFtzpAAgx0>Z6r?H!%MOiY4uS`#Ktnmk3LM?}kQ*6cZR=ebUwIdAde1xz!RtemrK z#i~_n7Oh*dVM#!EsE?aZXn4Ttg-gQ3l|y_&l*PmLGYM^+8zj#rAAC_hXpU14zpBmM zdv_hYrCAx^?INJzINENoGk;d)t?<-`;cdmtg!SF^}b-`T@pHSq@>Pk|f3k zRwZ6ti@=WNKxRP;qpUL$OC4BMET@EM2&6vdapQ5i5^=KV=>#?RnmZo~mz{F#4)vcW|N56DUaBjbG<4&9UjWrWj)lJ`&vgAVJ{M{|f zkG8Ep-?8;t*RI>kZhl#L>+9+}-`Cyyap30jjrV_Uy8mPI{U2NJf8Tch`<92lwmGz}0f1ZB+{rJn@CtvFMq3pJ3zL%@-_Ow0McRcbAvRs?}@Ot~2pkVYqqA)@|DjH?n!^@7=f8-rdzy-_+IJe!sqF zmxu283+LT9`J9ZM_&D7z>Ux~jdhqapH7_f#-BVucM_TvT+%-Oa`lzYR!~C5`Tl2Gq zdzXt1hm6Jt<_=+Q799)5N6e1=E(~@u8BA?W4C0~(3_cnxYE+bU>f(9H#K^#+=%Z9% zxaqKftE1V8Ihw(2jm#=)9TpA_Y-f2LMHo*yY|*$Vq~lg_;Xooww~(7LL*SL>rmL&N zbQ5+2G&;3%OXwslz2Gd!&zW&~RqW|7CVp3COQjQYy|=Hs%V8<KLZ*U+5Lu!Sk^o_Z5E4Meg@_7P6crJiNL9pw)e1;Xm069{HJUZAPk55R%$-RIA z6-eL&AQ0xu!e<4=008gy@A0LT~suv4>S3ILP<0Bm`DLLvaF4FK%)Nj?Pt*r}7;7Xa9z9H|HZjR63e zC`Tj$K)V27Re@400>HumpsYY5E(E}?0f1SyGDiY{y#)Yvj#!WnKwtoXnL;eg03bL5 z07D)V%>y7z1E4U{zu>7~aD})?0RX_umCct+(lZpemCzb@^6=o|A>zVpu|i=NDG+7} zl4`aK{0#b-!z=TL9Wt0BGO&T{GJWpjryhdijfaIQ&2!o}p04JRKYg3k&Tf zVxhe-O!X z{f;To;xw^bEES6JSc$k$B2CA6xl)ltA<32E66t?3@gJ7`36pmX0IY^jz)rRYwaaY4 ze(nJRiw;=Qb^t(r^DT@T3y}a2XEZW-_W%Hszxj_qD**t_m!#tW0KDiJT&R>6OvVTR z07RgHDzHHZ48atvzz&?j9lXF70$~P3Knx_nJP<+#`N z#-MZ2bTkiLfR>_b(HgWKJ%F~Nr_oF3b#wrIijHG|(J>BYjM-sajE6;FiC7vY#};Gd zST$CUHDeuEH+B^pz@B062qXfFfD`NpUW5?BY=V%GM_5c)L#QR}BeW8_2v-S%gfYS= zB9o|3v?Y2H`NVi)In3rTB8+ej^> zQ=~r95NVuDChL%G$=>7$vVg20myx%S50Foi`^m%Pw-h?Xh~i8Mq9jtJloCocWk2Nv zrJpiFnV_ms&8eQ$2&#xWpIS+6pmtC%Q-`S&GF4Q#^mhymh7E(qNMa}%YZ-ePrx>>xFPTiH1=E+A$W$=bG8>s^ zm=Bn5Rah$aDtr}@$`X}2l~$F0mFKEdRdZE8)p@E5RI61Ft6o-prbbn>P~)iy)E2AN zsU20jsWz_8Qg>31P|s0cqrPALg8E|(vWA65poU1JRAaZs8I2(p#xiB`SVGovRs-uS zYnV-9TeA7=Om+qP8+I>yOjAR1s%ETak!GFdam@h^# z)@rS0t$wXH+Irf)+G6c;?H29p+V6F6oj{!|o%K3xI`?%6x;DB|x`n#ibhIR?(H}Q3Gzd138Ei2)WAMz7W9Vy`X}HnwgyEn!VS)>mv$8&{hQn>w4zwy3R}t;BYlZQm5)6pty=DfLrs+A-|>>;~;Q z_F?uV_HFjh9n2gO9o9Q^JA86v({H5aB!kjoO6 zc9$1ZZKsN-Zl8L~mE{`ly3)1N^`o1+o7}D0ZPeY&J;i;i`%NyJ8_8Y6J?}yE@b_5a zam?eLr<8@mESk|3$_SkmS{wQ>%qC18))9_|&j{ZT zes8AvOzF(F2#DZEY>2oYX&IRp`F#{ADl)1r>QS^)ba8a|EY_^#S^HO&t^Rgqwv=MZThqqEWH8 zxJo>d=ABlR_Bh=;eM9Tw|Ih34~oTE|= zX_mAr*D$vzw@+p(E0Yc6dFE}(8oqt`+R{gE3x4zjX+Sb3_cYE^= zgB=w+-tUy`ytONMS8KgRef4hA?t0j zufM;t32jm~jUGrkaOInTZ`zyfns>EuS}G30LFK_G-==(f<51|K&cocp&EJ`SxAh3? zNO>#LI=^+SEu(FqJ)ynt=!~PC9bO$rzPJB=?=j6w@a-(u02P7 zaQ)#(uUl{HW%tYNS3ItC^iAtK(eKlL`f9+{bJzISE?u8_z3;~C8@FyI-5j_jy7l;W z_U#vU3hqqYU3!mrul&B+{ptt$59)uk{;_4iZQ%G|z+lhASr6|H35TBkl>gI*;nGLU zN7W-nBaM%pA0HbH8olyl&XeJ%vZoWz%6?Y=dFykl=imL}`%BMQ{Mhgd`HRoLu6e2R za__6DuR6yg#~-}Tc|Gx_{H@O0eebyMy5GmWADJlpK>kqk(fVV@r_fLLKIeS?{4e)} z^ZO;zpECde00d`2O+f$vv5tKEQIh}w03c&XQcVB=dL;k=fP(-4`Tqa_faw4Lbua(` z>RI+y?e7jKeZ#YO-C04_;HK~#9!bc#U=05A-LO#c68cN&b+$hvK}|2r j0MIGk;{gBw|NjF3hvgFc{mQf)00000NkvXXu0mjf*KLZ*U+5Lu!Sk^o_Z5E4Meg@_7P6crJiNL9pw)e1;Xm069{HJUZAPk55R%$-RIA z6-eL&AQ0xu!e<4=008gy@A0LT~suv4>S3ILP<0Bm`DLLvaF4FK%)Nj?Pt*r}7;7Xa9z9H|HZjR63e zC`Tj$K)V27Re@400>HumpsYY5E(E}?0f1SyGDiY{y#)Yvj#!WnKwtoXnL;eg03bL5 z07D)V%>y7z1E4U{zu>7~aD})?0RX_umCct+(lZpemCzb@^6=o|A>zVpu|i=NDG+7} zl4`aK{0#b-!z=TL9Wt0BGO&T{GJWpjryhdijfaIQ&2!o}p04JRKYg3k&Tf zVxhe-O!X z{f;To;xw^bEES6JSc$k$B2CA6xl)ltA<32E66t?3@gJ7`36pmX0IY^jz)rRYwaaY4 ze(nJRiw;=Qb^t(r^DT@T3y}a2XEZW-_W%Hszxj_qD**t_m!#tW0KDiJT&R>6OvVTR z07RgHDzHHZ48atvzz&?j9lXF70$~P3Knx_nJP<+#`N z#-MZ2bTkiLfR>_b(HgWKJ%F~Nr_oF3b#wrIijHG|(J>BYjM-sajE6;FiC7vY#};Gd zST$CUHDeuEH+B^pz@B062qXfFfD`NpUW5?BY=V%GM_5c)L#QR}BeW8_2v-S%gfYS= zB9o|3v?Y2H`NVi)In3rTB8+ej^> zQ=~r95NVuDChL%G$=>7$vVg20myx%S50Foi`^m%Pw-h?Xh~i8Mq9jtJloCocWk2Nv zrJpiFnV_ms&8eQ$2&#xWpIS+6pmtC%Q-`S&GF4Q#^mhymh7E(qNMa}%YZ-ePrx>>xFPTiH1=E+A$W$=bG8>s^ zm=Bn5Rah$aDtr}@$`X}2l~$F0mFKEdRdZE8)p@E5RI61Ft6o-prbbn>P~)iy)E2AN zsU20jsWz_8Qg>31P|s0cqrPALg8E|(vWA65poU1JRAaZs8I2(p#xiB`SVGovRs-uS zYnV-9TeA7=Om+qP8+I>yOjAR1s%ETak!GFdam@h^# z)@rS0t$wXH+Irf)+G6c;?H29p+V6F6oj{!|o%K3xI`?%6x;DB|x`n#ibhIR?(H}Q3Gzd138Ei2)WAMz7W9Vy`X}HnwgyEn!VS)>mv$8&{hQn>w4zwy3R}t;BYlZQm5)6pty=DfLrs+A-|>>;~;Q z_F?uV_HFjh9n2gO9o9Q^JA86v({H5aB!kjoO6 zc9$1ZZKsN-Zl8L~mE{`ly3)1N^`o1+o7}D0ZPeY&J;i;i`%NyJ8_8Y6J?}yE@b_5a zam?eLr<8@mESk|3$_SkmS{wQ>%qC18))9_|&j{ZT zes8AvOzF(F2#DZEY>2oYX&IRp`F#{ADl)1r>QS^)ba8a|EY_^#S^HO&t^Rgqwv=MZThqqEWH8 zxJo>d=ABlR_Bh=;eM9Tw|Ih34~oTE|= zX_mAr*D$vzw@+p(E0Yc6dFE}(8oqt`+R{gE3x4zjX+Sb3_cYE^= zgB=w+-tUy`ytONMS8KgRef4hA?t0j zufM;t32jm~jUGrkaOInTZ`zyfns>EuS}G30LFK_G-==(f<51|K&cocp&EJ`SxAh3? zNO>#LI=^+SEu(FqJ)ynt=!~PC9bO$rzPJB=?=j6w@a-(u02P7 zaQ)#(uUl{HW%tYNS3ItC^iAtK(eKlL`f9+{bJzISE?u8_z3;~C8@FyI-5j_jy7l;W z_U#vU3hqqYU3!mrul&B+{ptt$59)uk{;_4iZQ%G|z+lhASr6|H35TBkl>gI*;nGLU zN7W-nBaM%pA0HbH8olyl&XeJ%vZoWz%6?Y=dFykl=imL}`%BMQ{Mhgd`HRoLu6e2R za__6DuR6yg#~-}Tc|Gx_{H@O0eebyMy5GmWADJlpK>kqk(fVV@r_fLLKIeS?{4e)} z^ZO;zpECde00d`2O+f$vv5tKEQIh}w03c&XQcVB=dL;k=fP(-4`Tqa_faw4Lbua(` z>RI+y?e7jKeZ#YO-C0FOyTK~#9!RE;qX!Y~v={UZve%ONr{AXODgI>1JTBE;0Y zpo$BSp&e1q!GMI4^s}^(db9m3!}$oQEIS|Je8hc7==y-J50J|8t&bq7EMwBa7<#=4 zBadj?^Dh@iQ`2ltO~dPA32b+o=C&+a|W(Javsl`g)7(jn0`%?6WsI=4>!8-0GOO z*|}hgTj3V-iXEOso6Q>c23PGg>pYm!xi7r`a9PLVnn_!d=A6!*d#Zlcf!sys@)n=Z zT7IE&!qgVHyyuJVQ{r#sO9=`hY z(_7JzJ2-r^ZWPjKYsl92?D>s;5QKb z`Sa)R-@pI<{rf-2fZ|UUMlObW1|0?<0L2Ld`@V+yrskH`w)T$BuI_q&KR^GTPJeG> zEp1aDze%nBHZnpBg=C#(&uQ|K5|Gx{6&H~6THb5GC*kGh;wr*t<-6QZnM=pT%gaUe z@TNJwDr}lAK0ae}4AuZ48i8a0 literal 0 HcmV?d00001 diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/search_popup.gif b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/search_popup.gif new file mode 100644 index 0000000000000000000000000000000000000000..0d648bc0d8603d704bdc5e2d544fb484560ed924 GIT binary patch literal 273 zcmZ?wbhEHb~L|^qDhf&Ye4V{`~n%mo8noa^>2!Yfqm(eg6FU zix)3mzI^%W)vMR9U%!3(_T9U8@87@w`0?YXPoIAL`0?x4uiw9a|M~Oh@87@w{{8!p z0TlmCBD!6<41~BL_00GDm46L;Y6M9oT*%mEcw`TIf2<_w- zs@ui0?yhMK>s?TAW5pkd6H6u~@&t%nSP|mr7$|gOjaeYCkHCWs5{8bRd>IW=b} zg3Wg}%#&8q@JQUx{^7fT20u$nYa0u{1|M@zZyz(C1~1c;sneKvHF&1a;nCopGo5?> Q!fA^cgI5MHDl%9D0Nogn>;M1& literal 0 HcmV?d00001 diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/search_popup_pressed.gif b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/search_popup_pressed.gif new file mode 100644 index 0000000000000000000000000000000000000000..7a4dbc95205a8532e46b4d524b485176ba5c10b1 GIT binary patch literal 228 zcmZ?wbh9u|Nd;nj<4=XYnkphaa&gBtgOyC#S<2kPg+*Jq0AOGC_ z^ylTbzi)s1d;jC#r(gfR{QCFp&%Ymk{{8y<@6W&gfB*fbI?!PN0+3%A*d{m}>~PVM z@?6~Duq4xJzSKk`mbGnD7HEkq`IEBmLBX@dUlPu}FgdpPO~kzqJC-hf67cSaP3z)2 z9{(B|g`~a2IomrrImNw1Ir9Q~vqa%Yg09QKj#sB~S literal 0 HcmV?d00001 diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/search_pressed.gif b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/search_pressed.gif new file mode 100644 index 0000000000000000000000000000000000000000..f79cb16a17e488bc206333133e1706d7481e14e6 GIT binary patch literal 1161 zcmdVZ`%}^f003~TTyeJMtFrT2YBjUm-E^I@rcTAEyj|z&rfKQS&CO}C&8s`TJwh`T z@QH#7_&_bmvs5g_MExRfm8ZM~gfH?CP(i`9{jk@cvCseTxpEBwOS~Hn35D3WkcgDh z^Vgpw-?&s^Gj4$0-BjqMa-=x<~=6oJbIH~LoRsIU0BTvI#O*vd0^ zaMpTQs=;~vAfT_3*l_%HB3IwT(c<}L3R~UJR<#MN?Tc0l$Joo$wJ+J~rS>6?oFo8o z%TB_QwR6e(jHe+8H}E1CLF6DW+d4$fQI4)lw9z4UQ3c=#-$W8SIwhMViK|=UAW1hT z_y)3Ut7m1KDzWs+-BZH#0fpzO((_EU!w{ROD))rkLREXFf(it*Hb85iF}PW}4M6XjHEaWhE#9hAptEufPJ!M9n0EPV zPL|0HfL)vX!D8eKBdJiw)-S@pTgnO zICfRey;aA~s>7>udi74P&gENkc@3LBt9#Gt`Tg(zl~~BX{<{D<^cMt*q6Q81F&`fa zId&#BB7>Y0csi4OH7E*Rcl6_L8E><`zHo=}KvAl|L|ti|#$5iBX1y_%`#ckolpA#X zMaZ%8yh2j|AqIUhdN?XOy&9Tag1CBXJRnq3doo(9-CuQJJd;>ciOO=U+-wNBy;02s z0zOE%hY+6o+423sv(4b*eYV4YUt|*@!zACAb{tWDD(;tX*jE3w+9Mf#7IZuH9q2bt zp-Ev#(`9PxhcIKRKSp>dKA-YA>PI8uWo+`fJ_@SLAC`~|g%jbe8T63Iyt4uT62vOf zLmyy+gJbTOM;FnVJc_L@A|^P{xPR%yD*L$Uh&qi{?Xz5RQRdTy~u-MHGv*h xP*%xZ?uk-+t&Fa>eyz&o3(ECD=13OWSIP#MeUG^{v|Q9Oc+8)l0GQH~9w zY|L;AMOq4!wjAMDaU6w0Df9qZdeHl%^n&l}`L&My7khcXyg%SwNlA)}&5nSD!gOrd zk$CLsv}dvCnzJ{XVsm@rb8&I`-{=nt?)Y#&Y)Q7_t zf2GEhj%Pn=h^d;)EcyFd)kJ<-&)u4_uV2n#>KK?N=I!Q%thU9U8ks-0vhH=VD_e#N zySNnuP6NKbinRQkwDN+=uOHyo53aS2iW(>QZBwgFba4w^)HNk-WyoGJ*2{VNmN|Jl zLxN+7I~d~Dc?D@&+CDF@UN*d1P~qnkIF`DDr6ep!U$YcFOwj;CN?26l*t*UoO($DN zWhzN56=_l0%~q3_WO%NDvLq$3mBS12E}ntN(-W4B_!Se0r=##RgjF+*EhMg*`c{oJ zu7oJC(m1*vft9#s!wZ34fu&oxMHGRPJk>udGP2OxBL;fKV4oQ1kvK>aJ4FJJ)*Vy{ zKws63h)jJlGiBY;Cv{S!PO8i`vt}HSyM`2&L4|vEePdYV8C7}aWt(Gah$S`C)E7J+V?v*8lyw|GXk*sv`$f_$?_V1a}dkH`us%%IZjShuXdCkN}F44bLy;i<(5Zhvun1T%56xs4e7SsYQU=jAU)tQ0B$Yl-2|Kl&|?50 z6X4X_z2=>?Lz|#`!|pXYycWA_)8R8YedZlqKG5T{dwdSa=Y)K&9lbuc*Z21S$H%Z8 z{(Auy;DbfQ1twA2Cd&eLpZ`6m9ZjwXin`kDq=a9}8;QnBGE?^>F~itZQGIRrbnak! zcSb>B*un7gX7v-zRX#KK#CHY>@rQl*_d=hf2i?HdoobDXI{D?yAo28#H!;cblBZYv zcOS{R`=GlzJ30e1jr-=Iy3{}G8Dgm43JX3)ou@r+OcjLmydRha8+*{nt)l&uV4D2Sk2l_(flz$8Ua{ z+}$yOD%*PqOOSUw9Wn+qq4zQ~7`sZ}h5vB^m7ss;0~w+QY>?)&kHz}=W0L(+=0d)B zbf*O|pE&;fZ21h(B?$Sxx&*dA%`cmqld|Uk3Rj0F6T&XtE{0b!B0qv56Hcb|$`cXi zoY&!@nBMG)5<2oI;=?c=w>;`-iSlwF62QPi(lK1W14XRbcH};$0(USk(>A;3vrQB| z^k#8zd3b);q<_Sx{b32ng3tnWZSrw+4ie#woc;59(kV^dHQyBH%(pp2&C6RqHfYWS J!{M+C{{p{fFv|b{ literal 0 HcmV?d00001 diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/silver-statusbar-left.png b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/silver-statusbar-left.png new file mode 100644 index 0000000000000000000000000000000000000000..000a9b157adb933ef7fd67dd631d7bc4e91514fe GIT binary patch literal 223 zcmeAS@N?(olHy`uVBq!ia0vp^sz5Bx!2~3k{RBS&DbW(wh!W?b)Wnj^{5*w_%-mE4 z_G{dXf*cZ8`mOtc3fN0LeOrMq>1fP%4}E{-7; zx87da$lGAR;}YnwF<{q)3eJ2k*57S67N#}EE8J)Dzy9azo_DM`g=PXYh{ literal 0 HcmV?d00001 diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/silver-statusbar-middle.png b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/silver-statusbar-middle.png new file mode 100644 index 0000000000000000000000000000000000000000..118762e2166bfceb8932d0783e8620cb47a3367d GIT binary patch literal 196 zcmeAS@N?(olHy`uVBq!ia0vp^j6f{T!2~2-e{6~XQlcfU5hcz=sfi_-`FRQ6|k3h`nrOJc(@dUl*x|L&e@ k>i^KzGcGPQaXp1PTHTV%y|(TC0yK@m)78&qol`;+00C=1oB#j- literal 0 HcmV?d00001 diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/silver-statusbar-right.png b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/silver-statusbar-right.png new file mode 100644 index 0000000000000000000000000000000000000000..6df09e6bc67046b09d8a8fc1746278a48ecdd0b1 GIT binary patch literal 370 zcmV-&0ge8NP)uE_c2SE_1Fbvav zKCE@=dLmN!e0~!Fz;^(k#4kgv?Z8`zO#CBs)PEfHAO7r0h-zg3YC9I4c4|lEWyyH* zF3LFvkcVV7vTxBU%}HuJ=mNHOasyy8>;ksh=m22nq?EAQ^=iKTuzLq>0Iapq+K53j zj^>~9r9P``twpz*b*;5Zk_14w)4IGk2aqJmT5XJ>=a>G*K>;Ex7FNFa1#FUyNZ=}* Q(f|Me07*qoM6N<$f{x>(y#N3J literal 0 HcmV?d00001 diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/statusbar-left.png b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/statusbar-left.png new file mode 100644 index 0000000000000000000000000000000000000000..d344719ae513ee443b45f204d744b7db2e29c495 GIT binary patch literal 3200 zcmV-`41e>9P)KLZ*U+5Lu!Sk^o_Z5E4Meg@_7P6crJiNL9pw)e1;Xm069{HJUZAPk55R%$-RIA z6-eL&AQ0xu!e<4=008gy@A0LT~suv4>S3ILP<0Bm`DLLvaF4FK%)Nj?Pt*r}7;7Xa9z9H|HZjR63e zC`Tj$K)V27Re@400>HumpsYY5E(E}?0f1SyGDiY{y#)Yvj#!WnKwtoXnL;eg03bL5 z07D)V%>y7z1E4U{zu>7~aD})?0RX_umCct+(lZpemCzb@^6=o|A>zVpu|i=NDG+7} zl4`aK{0#b-!z=TL9Wt0BGO&T{GJWpjryhdijfaIQ&2!o}p04JRKYg3k&Tf zVxhe-O!X z{f;To;xw^bEES6JSc$k$B2CA6xl)ltA<32E66t?3@gJ7`36pmX0IY^jz)rRYwaaY4 ze(nJRiw;=Qb^t(r^DT@T3y}a2XEZW-_W%Hszxj_qD**t_m!#tW0KDiJT&R>6OvVTR z07RgHDzHHZ48atvzz&?j9lXF70$~P3Knx_nJP<+#`N z#-MZ2bTkiLfR>_b(HgWKJ%F~Nr_oF3b#wrIijHG|(J>BYjM-sajE6;FiC7vY#};Gd zST$CUHDeuEH+B^pz@B062qXfFfD`NpUW5?BY=V%GM_5c)L#QR}BeW8_2v-S%gfYS= zB9o|3v?Y2H`NVi)In3rTB8+ej^> zQ=~r95NVuDChL%G$=>7$vVg20myx%S50Foi`^m%Pw-h?Xh~i8Mq9jtJloCocWk2Nv zrJpiFnV_ms&8eQ$2&#xWpIS+6pmtC%Q-`S&GF4Q#^mhymh7E(qNMa}%YZ-ePrx>>xFPTiH1=E+A$W$=bG8>s^ zm=Bn5Rah$aDtr}@$`X}2l~$F0mFKEdRdZE8)p@E5RI61Ft6o-prbbn>P~)iy)E2AN zsU20jsWz_8Qg>31P|s0cqrPALg8E|(vWA65poU1JRAaZs8I2(p#xiB`SVGovRs-uS zYnV-9TeA7=Om+qP8+I>yOjAR1s%ETak!GFdam@h^# z)@rS0t$wXH+Irf)+G6c;?H29p+V6F6oj{!|o%K3xI`?%6x;DB|x`n#ibhIR?(H}Q3Gzd138Ei2)WAMz7W9Vy`X}HnwgyEn!VS)>mv$8&{hQn>w4zwy3R}t;BYlZQm5)6pty=DfLrs+A-|>>;~;Q z_F?uV_HFjh9n2gO9o9Q^JA86v({H5aB!kjoO6 zc9$1ZZKsN-Zl8L~mE{`ly3)1N^`o1+o7}D0ZPeY&J;i;i`%NyJ8_8Y6J?}yE@b_5a zam?eLr<8@mESk|3$_SkmS{wQ>%qC18))9_|&j{ZT zes8AvOzF(F2#DZEY>2oYX&IRp`F#{ADl)1r>QS^)ba8a|EY_^#S^HO&t^Rgqwv=MZThqqEWH8 zxJo>d=ABlR_Bh=;eM9Tw|Ih34~oTE|= zX_mAr*D$vzw@+p(E0Yc6dFE}(8oqt`+R{gE3x4zjX+Sb3_cYE^= zgB=w+-tUy`ytONMS8KgRef4hA?t0j zufM;t32jm~jUGrkaOInTZ`zyfns>EuS}G30LFK_G-==(f<51|K&cocp&EJ`SxAh3? zNO>#LI=^+SEu(FqJ)ynt=!~PC9bO$rzPJB=?=j6w@a-(u02P7 zaQ)#(uUl{HW%tYNS3ItC^iAtK(eKlL`f9+{bJzISE?u8_z3;~C8@FyI-5j_jy7l;W z_U#vU3hqqYU3!mrul&B+{ptt$59)uk{;_4iZQ%G|z+lhASr6|H35TBkl>gI*;nGLU zN7W-nBaM%pA0HbH8olyl&XeJ%vZoWz%6?Y=dFykl=imL}`%BMQ{Mhgd`HRoLu6e2R za__6DuR6yg#~-}Tc|Gx_{H@O0eebyMy5GmWADJlpK>kqk(fVV@r_fLLKIeS?{4e)} z^ZO;zpECde00d`2O+f$vv5tKEQIh}w03c&XQcVB=dL;k=fP(-4`Tqa_faw4Lbua(` z>RI+y?e7jKeZ#YO-C0i8)iK~#9!>{d%s!!QheDq@;4bl@T^H~`1w1`JnBrjGSk z)J`17j>C%1P_G=>k)A$&l6-%Ca_9$)qeooc3^W5l{Ke9@_YVwx59iz@H1A4Hb?K=QS11UHJxpv(N z$72UG!@_mRb+8zvl9Bnl7P&mc%l}pNq2Nd>+^mMa3?{1Ou5$(b8V>-GV4Uq*AlU$Y z)nSxSd))m0fE!$QPw_Yz@1SLKq^Wn#?K?VNE@`t{D&{&_`N*-SVb_MmSW{e< z6x6w4$|oh|3QjqmBb3+1z9sa(qXg&1;(;%Z1C6xoHO@`Fs`>~(!gH1O%oGatup>Qa mz1}JGOK0@_e7ZSC{}}+lI|%JQe90pK0000 literal 0 HcmV?d00001 diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/statusbar-middle.png b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/statusbar-middle.png new file mode 100644 index 0000000000000000000000000000000000000000..37bd99e6a3ec16241c1fb5c6d28cd341fc22ae48 GIT binary patch literal 2864 zcmV-03(xe4P)KLZ*U+5Lu!Sk^o_Z5E4Meg@_7P6crJiNL9pw)e1;Xm069{HJUZAPk55R%$-RIA z6-eL&AQ0xu!e<4=008gy@A0LT~suv4>S3ILP<0Bm`DLLvaF4FK%)Nj?Pt*r}7;7Xa9z9H|HZjR63e zC`Tj$K)V27Re@400>HumpsYY5E(E}?0f1SyGDiY{y#)Yvj#!WnKwtoXnL;eg03bL5 z07D)V%>y7z1E4U{zu>7~aD})?0RX_umCct+(lZpemCzb@^6=o|A>zVpu|i=NDG+7} zl4`aK{0#b-!z=TL9Wt0BGO&T{GJWpjryhdijfaIQ&2!o}p04JRKYg3k&Tf zVxhe-O!X z{f;To;xw^bEES6JSc$k$B2CA6xl)ltA<32E66t?3@gJ7`36pmX0IY^jz)rRYwaaY4 ze(nJRiw;=Qb^t(r^DT@T3y}a2XEZW-_W%Hszxj_qD**t_m!#tW0KDiJT&R>6OvVTR z07RgHDzHHZ48atvzz&?j9lXF70$~P3Knx_nJP<+#`N z#-MZ2bTkiLfR>_b(HgWKJ%F~Nr_oF3b#wrIijHG|(J>BYjM-sajE6;FiC7vY#};Gd zST$CUHDeuEH+B^pz@B062qXfFfD`NpUW5?BY=V%GM_5c)L#QR}BeW8_2v-S%gfYS= zB9o|3v?Y2H`NVi)In3rTB8+ej^> zQ=~r95NVuDChL%G$=>7$vVg20myx%S50Foi`^m%Pw-h?Xh~i8Mq9jtJloCocWk2Nv zrJpiFnV_ms&8eQ$2&#xWpIS+6pmtC%Q-`S&GF4Q#^mhymh7E(qNMa}%YZ-ePrx>>xFPTiH1=E+A$W$=bG8>s^ zm=Bn5Rah$aDtr}@$`X}2l~$F0mFKEdRdZE8)p@E5RI61Ft6o-prbbn>P~)iy)E2AN zsU20jsWz_8Qg>31P|s0cqrPALg8E|(vWA65poU1JRAaZs8I2(p#xiB`SVGovRs-uS zYnV-9TeA7=Om+qP8+I>yOjAR1s%ETak!GFdam@h^# z)@rS0t$wXH+Irf)+G6c;?H29p+V6F6oj{!|o%K3xI`?%6x;DB|x`n#ibhIR?(H}Q3Gzd138Ei2)WAMz7W9Vy`X}HnwgyEn!VS)>mv$8&{hQn>w4zwy3R}t;BYlZQm5)6pty=DfLrs+A-|>>;~;Q z_F?uV_HFjh9n2gO9o9Q^JA86v({H5aB!kjoO6 zc9$1ZZKsN-Zl8L~mE{`ly3)1N^`o1+o7}D0ZPeY&J;i;i`%NyJ8_8Y6J?}yE@b_5a zam?eLr<8@mESk|3$_SkmS{wQ>%qC18))9_|&j{ZT zes8AvOzF(F2#DZEY>2oYX&IRp`F#{ADl)1r>QS^)ba8a|EY_^#S^HO&t^Rgqwv=MZThqqEWH8 zxJo>d=ABlR_Bh=;eM9Tw|Ih34~oTE|= zX_mAr*D$vzw@+p(E0Yc6dFE}(8oqt`+R{gE3x4zjX+Sb3_cYE^= zgB=w+-tUy`ytONMS8KgRef4hA?t0j zufM;t32jm~jUGrkaOInTZ`zyfns>EuS}G30LFK_G-==(f<51|K&cocp&EJ`SxAh3? zNO>#LI=^+SEu(FqJ)ynt=!~PC9bO$rzPJB=?=j6w@a-(u02P7 zaQ)#(uUl{HW%tYNS3ItC^iAtK(eKlL`f9+{bJzISE?u8_z3;~C8@FyI-5j_jy7l;W z_U#vU3hqqYU3!mrul&B+{ptt$59)uk{;_4iZQ%G|z+lhASr6|H35TBkl>gI*;nGLU zN7W-nBaM%pA0HbH8olyl&XeJ%vZoWz%6?Y=dFykl=imL}`%BMQ{Mhgd`HRoLu6e2R za__6DuR6yg#~-}Tc|Gx_{H@O0eebyMy5GmWADJlpK>kqk(fVV@r_fLLKIeS?{4e)} z^ZO;zpECde00d`2O+f$vv5tKEQIh}w03c&XQcVB=dL;k=fP(-4`Tqa_faw4Lbua(` z>RI+y?e7jKeZ#YO-C08L3mK~#9!e8jO0fG`jQ!PNyX0!t=B?!f_7QURh^u=%3{ zCK#^suzKH7Ya6O-MG=8)Bj6|dMUV)CoQX0M%{kC`ZZzgV5eq7%@C^XFz#7+S7$;%? O0000KLZ*U+5Lu!Sk^o_Z5E4Meg@_7P6crJiNL9pw)e1;Xm069{HJUZAPk55R%$-RIA z6-eL&AQ0xu!e<4=008gy@A0LT~suv4>S3ILP<0Bm`DLLvaF4FK%)Nj?Pt*r}7;7Xa9z9H|HZjR63e zC`Tj$K)V27Re@400>HumpsYY5E(E}?0f1SyGDiY{y#)Yvj#!WnKwtoXnL;eg03bL5 z07D)V%>y7z1E4U{zu>7~aD})?0RX_umCct+(lZpemCzb@^6=o|A>zVpu|i=NDG+7} zl4`aK{0#b-!z=TL9Wt0BGO&T{GJWpjryhdijfaIQ&2!o}p04JRKYg3k&Tf zVxhe-O!X z{f;To;xw^bEES6JSc$k$B2CA6xl)ltA<32E66t?3@gJ7`36pmX0IY^jz)rRYwaaY4 ze(nJRiw;=Qb^t(r^DT@T3y}a2XEZW-_W%Hszxj_qD**t_m!#tW0KDiJT&R>6OvVTR z07RgHDzHHZ48atvzz&?j9lXF70$~P3Knx_nJP<+#`N z#-MZ2bTkiLfR>_b(HgWKJ%F~Nr_oF3b#wrIijHG|(J>BYjM-sajE6;FiC7vY#};Gd zST$CUHDeuEH+B^pz@B062qXfFfD`NpUW5?BY=V%GM_5c)L#QR}BeW8_2v-S%gfYS= zB9o|3v?Y2H`NVi)In3rTB8+ej^> zQ=~r95NVuDChL%G$=>7$vVg20myx%S50Foi`^m%Pw-h?Xh~i8Mq9jtJloCocWk2Nv zrJpiFnV_ms&8eQ$2&#xWpIS+6pmtC%Q-`S&GF4Q#^mhymh7E(qNMa}%YZ-ePrx>>xFPTiH1=E+A$W$=bG8>s^ zm=Bn5Rah$aDtr}@$`X}2l~$F0mFKEdRdZE8)p@E5RI61Ft6o-prbbn>P~)iy)E2AN zsU20jsWz_8Qg>31P|s0cqrPALg8E|(vWA65poU1JRAaZs8I2(p#xiB`SVGovRs-uS zYnV-9TeA7=Om+qP8+I>yOjAR1s%ETak!GFdam@h^# z)@rS0t$wXH+Irf)+G6c;?H29p+V6F6oj{!|o%K3xI`?%6x;DB|x`n#ibhIR?(H}Q3Gzd138Ei2)WAMz7W9Vy`X}HnwgyEn!VS)>mv$8&{hQn>w4zwy3R}t;BYlZQm5)6pty=DfLrs+A-|>>;~;Q z_F?uV_HFjh9n2gO9o9Q^JA86v({H5aB!kjoO6 zc9$1ZZKsN-Zl8L~mE{`ly3)1N^`o1+o7}D0ZPeY&J;i;i`%NyJ8_8Y6J?}yE@b_5a zam?eLr<8@mESk|3$_SkmS{wQ>%qC18))9_|&j{ZT zes8AvOzF(F2#DZEY>2oYX&IRp`F#{ADl)1r>QS^)ba8a|EY_^#S^HO&t^Rgqwv=MZThqqEWH8 zxJo>d=ABlR_Bh=;eM9Tw|Ih34~oTE|= zX_mAr*D$vzw@+p(E0Yc6dFE}(8oqt`+R{gE3x4zjX+Sb3_cYE^= zgB=w+-tUy`ytONMS8KgRef4hA?t0j zufM;t32jm~jUGrkaOInTZ`zyfns>EuS}G30LFK_G-==(f<51|K&cocp&EJ`SxAh3? zNO>#LI=^+SEu(FqJ)ynt=!~PC9bO$rzPJB=?=j6w@a-(u02P7 zaQ)#(uUl{HW%tYNS3ItC^iAtK(eKlL`f9+{bJzISE?u8_z3;~C8@FyI-5j_jy7l;W z_U#vU3hqqYU3!mrul&B+{ptt$59)uk{;_4iZQ%G|z+lhASr6|H35TBkl>gI*;nGLU zN7W-nBaM%pA0HbH8olyl&XeJ%vZoWz%6?Y=dFykl=imL}`%BMQ{Mhgd`HRoLu6e2R za__6DuR6yg#~-}Tc|Gx_{H@O0eebyMy5GmWADJlpK>kqk(fVV@r_fLLKIeS?{4e)} z^ZO;zpECde00d`2O+f$vv5tKEQIh}w03c&XQcVB=dL;k=fP(-4`Tqa_faw4Lbua(` z>RI+y?e7jKeZ#YO-C0hdWcK~#9!)RWIjTTu|ke{=51jR>{@lSZsiS4va05~K+h znu6$?^$AMf$1H3U7gBXAbW^OTFW|j5XI$KK|HTwUSI%m_GiSbU=A0S%@%@|f9}@&I zh9EHcS*=#1UJp6g-@|q~ol1gg!6gF5NHy77>T`7f>{7e=P*DU`m-}KQj|E1sL69al z8jVbIs)Ed$q$QMF@z{o8IHTK1$+w=`dDeo+7CpKtg4vabWcLCL&#)JeJUl#NczEQCiZxxG3^eZZKKa*>z@hr^#zZXMDT5!(DQh1NX;O+!}mvoqP9nd|-8otZm3J1FtM-MROk zd(L<7Io~~Z6+#F?Bve@uB}^Gvg=7EPK%hv7fg{%3uONlbknJDjV{$u;p z21N0NNE(>wgD{u-py*dX)~rX}ka+=I=PSR%7}*D{yhxxg^4)7l2Jg+n%L${=<$3roF~Qx=#9W772g@7x?PnX04> zv49+~_${3S3ctZJFVdKe!$^<98199&SmJlUbq>e*O^5Cj#%b57w(jl#fR2M8lX;kn z6lCcv-*Lm{%a|M<0#Uz#M&f8tyJ*{q=<;ojbMy4+&>Tb{ZXDssyuJu?Y!KRx6lLdK zFk?1aQa@piGBWX2w_`N%;wzGq4bf(dP6=u@-?7W9Qlt#_eB-fnK>-^BGvnVJ>=Y~n ztW2|0SW3ZV$CqXNmhd4v4nytSjxsW^^WqNV2J`$|tvjCG*F2d)Cy)}(PGaYVMGmYB zHO_szuaO8sL}}Q~^U yQ@7yJka?fex_^Mf03?twHis$I)foQ^J^ld`!qce0#nc-B0000 + * + */ +public class BuddyButton extends JButton { + public BuddyButton() { + this(null); + } + + public BuddyButton(String text) { + super(text); + setFocusable(false); + setMargin(SearchFieldUI.NO_INSETS); + + // Windows UI will add 1 pixel for width and height, if this is true + setFocusPainted(false); + + setBorderPainted(false); + setContentAreaFilled(false); + setIconTextGap(0); + + setBorder(null); + + setOpaque(false); + + setCursor(Cursor.getDefaultCursor()); + } + + // Windows UI overrides Insets. + // Who knows what other UIs are doing... + @Override + public Insets getInsets() { + return SearchFieldUI.NO_INSETS; + } + + @Override + public Insets getInsets(Insets insets) { + return getInsets(); + } + + @Override + public Insets getMargin() { + return getInsets(); + } + + @Override + public void setBorder(Border border) { + // Don't let Motif overwrite my Border + super.setBorder(BorderFactory.createEmptyBorder()); + } +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/prompt/BuddySupport.java b/src/main/java/org/jdesktop/swingx/prompt/BuddySupport.java new file mode 100644 index 0000000000..a92c300044 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/prompt/BuddySupport.java @@ -0,0 +1,151 @@ +package org.jdesktop.swingx.prompt; + +import org.jdesktop.swingx.plaf.TextUIWrapper; + +import javax.swing.*; +import javax.swing.plaf.basic.BasicTextUI; +import java.awt.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class BuddySupport { + public enum Position { + LEFT, RIGHT + }; + + public static final String OUTER_MARGIN = "outerMargin"; + + public static void addLeft(Component c, JTextField textField) { + add(c, Position.LEFT, textField); + } + + public static void addRight(Component c, JTextField textField) { + add(c, Position.RIGHT, textField); + } + + public static void add(Component c, Position pos, JTextField textField) { + TextUIWrapper.getDefaultWrapper().install(textField, true); + + List leftBuddies = buddies(Position.LEFT, textField); + List rightBuddies = buddies(Position.RIGHT, textField); + + // ensure buddies are added + setLeft(textField, leftBuddies); + setRight(textField, rightBuddies); + + // check if component is already here + if (isBuddy(c, textField)) { + throw new IllegalStateException("Component already added."); + } + + if (Position.LEFT == pos) { + leftBuddies.add(c); + } else { + rightBuddies.add(0, c); + } + + addToComponentHierarchy(c, pos, textField); + } + + public static void addGap(int width, Position pos, JTextField textField) { + add(createGap(width), pos, textField); + } + + public static void setRight(JTextField textField, List rightBuddies) { + set(rightBuddies, Position.RIGHT, textField); + } + + public static void setLeft(JTextField textField, List leftBuddies) { + set(leftBuddies, Position.LEFT, textField); + } + + public static void set(List buddies, Position pos, JTextField textField) { + textField.putClientProperty(pos, buddies); + } + + private static void addToComponentHierarchy(Component c, Position pos, JTextField textField) { + textField.add(c, pos.toString()); + } + + public static List getLeft(JTextField textField) { + return getBuddies(Position.LEFT, textField); + } + + public static List getRight(JTextField textField) { + return getBuddies(Position.RIGHT, textField); + } + + public static List getBuddies(Position pos, JTextField textField) { + return Collections.unmodifiableList(buddies(pos, textField)); + } + + @SuppressWarnings("unchecked") + private static List buddies(Position pos, JTextField textField) { + List buddies = (List) textField.getClientProperty(pos); + + if (buddies != null) { + return buddies; + } + return new ArrayList(); + } + + public static boolean isBuddy(Component c, JTextField textField) { + return buddies(Position.LEFT, textField).contains(c) || buddies(Position.RIGHT, textField).contains(c); + } + + /** + * Because {@link BasicTextUI} removes all components when uninstalled and + * therefore all buddies are removed when the LnF changes. + * + * @param c + * @param textField + */ + public static void remove(JComponent c, JTextField textField) { + buddies(Position.LEFT, textField).remove(c); + buddies(Position.RIGHT, textField).remove(c); + + textField.remove(c); + } + + public static void removeAll(JTextField textField) { + List left = buddies(Position.LEFT, textField); + for (Component c : left) { + textField.remove(c); + } + left.clear(); + List right = buddies(Position.RIGHT, textField); + for (Component c : right) { + textField.remove(c); + } + right.clear(); + + } + + public static void setOuterMargin(JTextField buddyField, Insets margin) { + buddyField.putClientProperty(OUTER_MARGIN, margin); + } + + public static Insets getOuterMargin(JTextField buddyField) { + return (Insets) buddyField.getClientProperty(OUTER_MARGIN); + } + + public static void ensureBuddiesAreInComponentHierarchy(JTextField textField) { + for (Component c : BuddySupport.getLeft(textField)) { + addToComponentHierarchy(c, Position.LEFT, textField); + } + for (Component c : BuddySupport.getRight(textField)) { + addToComponentHierarchy(c, Position.RIGHT, textField); + } + } + + /** + * Create a gap to insert between to buddies. + * + * @param width + * @return + */ + public static Component createGap(int width) { + return Box.createHorizontalStrut(width); + } +} diff --git a/src/main/java/org/jdesktop/swingx/prompt/PromptSupport.java b/src/main/java/org/jdesktop/swingx/prompt/PromptSupport.java new file mode 100644 index 0000000000..f672a8bb45 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/prompt/PromptSupport.java @@ -0,0 +1,327 @@ +package org.jdesktop.swingx.prompt; + +import org.jdesktop.swingx.JXFormattedTextField; +import org.jdesktop.swingx.JXTextArea; +import org.jdesktop.swingx.JXTextField; +import org.jdesktop.swingx.painter.Painter; +import org.jdesktop.swingx.painter.Painters; +import org.jdesktop.swingx.plaf.PromptTextUI; +import org.jdesktop.swingx.plaf.TextUIWrapper; + +import javax.swing.text.JTextComponent; +import java.awt.*; + +/** + *

      + * Sets prompt text, foreground, background and {@link FocusBehavior} properties + * on a JTextComponent by calling + * {@link JTextComponent#putClientProperty(Object, Object)}. These properties + * are used by {@link PromptTextUI} instances to render the prompt of a text + * component. + *

      + * + *

      + * This class is used by {@link JXTextField}, {@link JXFormattedTextField} and + * {@link JXTextArea} to get and set prompt properties. {@link PromptTextUI} + * retrieves these properties using PromptSupport. + *

      + * + * @see JXTextField + * @see JXFormattedTextField + * @see JXTextArea + * @see PromptTextUI + * + * @author Peter Weishapl + * @author Karl Schaefer + */ +public final class PromptSupport { + /** + * The prompt text property. + */ + public static final String PROMPT = "promptText"; + + /** + * The color of the prompt text property. + */ + public static final String FOREGROUND = "promptForeground"; + + /** + * The prompt background property. + */ + public static final String BACKGROUND = "promptBackground"; + + /** + * The prompt background property. + */ + public static final String BACKGROUND_PAINTER = "promptBackgroundPainter"; + + /** + * The focus behavior property. + */ + public static final String FOCUS_BEHAVIOR = "focusBehavior"; + + /** + * The font style property, if different from the components font. + */ + public static final String FONT_STYLE = "promptFontStyle"; + + /** + *

      + * Determines how the {@link JTextComponent} is rendered when focused and no + * text is present. + *

      + */ + public static enum FocusBehavior { + /** + * Keep the prompt text visible. + */ + SHOW_PROMPT, + /** + * Highlight the prompt text as it would be selected. + */ + HIGHLIGHT_PROMPT, + /** + * Hide the prompt text. + */ + HIDE_PROMPT + }; + + private PromptSupport() { + //prevent instantiation + } + + /** + *

      + * Convenience method to set the promptText and + * promptTextColor on a {@link JTextComponent}. + *

      + *

      + * If stayOnUIChange is true, The prompt support will stay + * installed, even when the text components UI changes. See + * {@link #install(JTextComponent, boolean)}. + *

      + * + * @param promptText + * @param promptForeground + * @param promptBackground + * @param textComponent + */ + public static void init(String promptText, Color promptForeground, Color promptBackground, + final JTextComponent textComponent) { + if (promptText != null && promptText.length() > 0) { + setPrompt(promptText, textComponent); + } + if (promptForeground != null) { + setForeground(promptForeground, textComponent); + } + if (promptBackground != null) { + setBackground(promptBackground, textComponent); + } + } + + /** + * Get the {@link FocusBehavior} of textComponent. + * + * @param textComponent + * @return the {@link FocusBehavior} or {@link FocusBehavior#HIDE_PROMPT} if + * none is set + */ + public static FocusBehavior getFocusBehavior(JTextComponent textComponent) { + FocusBehavior fb = (FocusBehavior) textComponent.getClientProperty(FOCUS_BEHAVIOR); + if (fb == null) { + fb = FocusBehavior.HIDE_PROMPT; + } + return fb; + } + + /** + * Sets the {@link FocusBehavior} on textComponent and + * repaints the component to reflect the changes, if it is the focus owner. + * + * @param focusBehavior + * @param textComponent + */ + public static void setFocusBehavior(FocusBehavior focusBehavior, JTextComponent textComponent) { + textComponent.putClientProperty(FOCUS_BEHAVIOR, focusBehavior); + if (textComponent.isFocusOwner()) { + textComponent.repaint(); + } + } + + /** + * Get the prompt text of textComponent. + * + * @param textComponent + * @return the prompt text + */ + public static String getPrompt(JTextComponent textComponent) { + return (String) textComponent.getClientProperty(PROMPT); + } + + /** + *

      + * Sets the prompt text on textComponent. Also sets the + * tooltip text to the prompt text if textComponent has no + * tooltip text or the current tooltip text is the same as the current + * prompt text. + *

      + *

      + * Calls {@link #install(JTextComponent)} to ensure that the + * textComponents UI is wrapped by the appropriate + * {@link PromptTextUI}. + *

      + * + * @param promptText + * @param textComponent + */ + public static void setPrompt(String promptText, JTextComponent textComponent) { + TextUIWrapper.getDefaultWrapper().install(textComponent, true); + + // display prompt as tooltip by default + if (textComponent.getToolTipText() == null || textComponent.getToolTipText().equals(getPrompt(textComponent))) { + textComponent.setToolTipText(promptText); + } + + textComponent.putClientProperty(PROMPT, promptText); + textComponent.repaint(); + } + + /** + * Get the foreground color of the prompt text. If no color has been set, + * the textComponents disabled text color will be returned. + * + * @param textComponent + * @return the color of the prompt text or + * {@link JTextComponent#getDisabledTextColor()} if none is set + */ + public static Color getForeground(JTextComponent textComponent) { + if (textComponent.getClientProperty(FOREGROUND) == null) { + return textComponent.getDisabledTextColor(); + } + return (Color) textComponent.getClientProperty(FOREGROUND); + } + + /** + * Sets the foreground color of the prompt on textComponent + * and repaints the component to reflect the changes. This color will be + * used when no text is present. + * + * @param promptTextColor + * @param textComponent + */ + public static void setForeground(Color promptTextColor, JTextComponent textComponent) { + textComponent.putClientProperty(FOREGROUND, promptTextColor); + textComponent.repaint(); + } + + /** + * Get the background color of the textComponent, when no + * text is present. If no color has been set, the textComponents + * background color color will be returned. + * + * @param textComponent + * @return the the background color of the text component, when no text is + * present + */ + public static Color getBackground(JTextComponent textComponent) { + if (textComponent.getClientProperty(BACKGROUND) == null) { + return textComponent.getBackground(); + } + return (Color) textComponent.getClientProperty(BACKGROUND); + } + + /** + *

      + * Sets the prompts background color on textComponent and + * repaints the component to reflect the changes. This background color will + * only be used when no text is present. + *

      + *

      + * Calls {@link #install(JTextComponent)} to ensure that the + * textComponents UI is wrapped by the appropriate + * {@link PromptTextUI}. + *

      + * + * @param background + * @param textComponent + */ + public static void setBackground(Color background, JTextComponent textComponent) { + TextUIWrapper.getDefaultWrapper().install(textComponent, true); + + textComponent.putClientProperty(BACKGROUND, background); + textComponent.repaint(); + } + + /** + * Get the background painter of the textComponent, when no + * text is present. If no painter has been set, then {@code null} will be returned. + * + * @param textComponent + * @return the background painter of the text component + */ + @SuppressWarnings("unchecked") + public static Painter getBackgroundPainter(T textComponent) { + Painter painter = (Painter) textComponent.getClientProperty(BACKGROUND_PAINTER); + + if (painter == null) { + painter = Painters.EMPTY_PAINTER; + } + + return painter; + } + + /** + *

      + * Sets the prompts background painter on textComponent and + * repaints the component to reflect the changes. This background painter will + * only be used when no text is present. + *

      + *

      + * Calls {@link #install(JTextComponent)} to ensure that the + * textComponents UI is wrapped by the appropriate + * {@link PromptTextUI}. + *

      + * + * @param background + * @param textComponent + */ + public static void setBackgroundPainter(Painter background, T textComponent) { + TextUIWrapper.getDefaultWrapper().install(textComponent, true); + + textComponent.putClientProperty(BACKGROUND_PAINTER, background); + textComponent.repaint(); + } + + /** + *

      + * Set the style of the prompt font, if different from the + * textComponents font. + *

      + *

      + * Allowed values are {@link Font#PLAIN}, {@link Font#ITALIC}, + * {@link Font#BOLD}, a combination of {@link Font#BOLD} and + * {@link Font#ITALIC} or null if the prompt font should be + * the same as the textComponents font. + *

      + * + * @param fontStyle + * @param textComponent + */ + public static void setFontStyle(Integer fontStyle, JTextComponent textComponent) { + textComponent.putClientProperty(FONT_STYLE, fontStyle); + textComponent.revalidate(); + textComponent.repaint(); + } + + /** + * Returns the font style of the prompt text, or null if the + * prompt's font style should not differ from the textComponents + * font. + * + * @param textComponent + * @return font style of the prompt text + */ + public static Integer getFontStyle(JTextComponent textComponent) { + return (Integer) textComponent.getClientProperty(FONT_STYLE); + } +} diff --git a/src/main/java/org/jdesktop/swingx/renderer/AbstractRenderer.java b/src/main/java/org/jdesktop/swingx/renderer/AbstractRenderer.java new file mode 100644 index 0000000000..8ec8d78fb1 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/renderer/AbstractRenderer.java @@ -0,0 +1,126 @@ +/* + * $Id: AbstractRenderer.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.renderer; + +import org.jdesktop.swingx.plaf.UIDependent; +import org.jdesktop.swingx.rollover.RolloverRenderer; + +import java.awt.*; +import java.io.Serializable; + +/** + * Convenience common ancestor for SwingX renderers. Concrete subclasses + * should + * + *
        + *
      • provide a bunch of convenience constructors as appropriate for the type of + * collection component + *
      • create a reasonable default ComponentProvider if none is given + *
      • implement the getXXCellRenderer by delegating to the ComponentProvider + *
      + * + * @author Jeanette Winzenburg + */ +public abstract class AbstractRenderer + implements RolloverRenderer, StringValue, Serializable, UIDependent { + + protected ComponentProvider componentController; + + public AbstractRenderer(ComponentProvider provider) { + if (provider == null) { + provider = createDefaultComponentProvider(); + } + this.componentController = provider; + } + + /** + * Returns the ComponentProvider used by this renderer. + * + * @return the ComponentProvider used by this renderer + */ + public ComponentProvider getComponentProvider() { + return componentController; + } + + /** + * The default ComponentProvider to use if no special. + * + * @return the default ComponentProvider + */ + protected abstract ComponentProvider createDefaultComponentProvider(); + +// --------------- implement StringValue + + /** + * {@inheritDoc} + */ + @Override + public String getString(Object value) { + return componentController.getString(value); + } + + // ------------ implement RolloverRenderer + + /** + * {@inheritDoc} + */ + @Override + public void doClick() { + if (isEnabled()) { + ((RolloverRenderer) componentController).doClick(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isEnabled() { + return (componentController instanceof RolloverRenderer) + && ((RolloverRenderer) componentController).isEnabled(); + } + + /** + * {@inheritDoc} + */ + @Override + public void updateUI() { + componentController.updateUI(); + } + +//-------------------- legacy: configure arbitrary visuals + /** + * @param background + */ + public void setBackground(Color background) { + componentController.getDefaultVisuals().setBackground(background); + + } + + /** + * @param foreground + */ + public void setForeground(Color foreground) { + componentController.getDefaultVisuals().setForeground(foreground); + } + + +} diff --git a/src/main/java/org/jdesktop/swingx/renderer/BooleanValue.java b/src/main/java/org/jdesktop/swingx/renderer/BooleanValue.java new file mode 100644 index 0000000000..c3ee11c462 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/renderer/BooleanValue.java @@ -0,0 +1,32 @@ +/* + * $Id: BooleanValue.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.renderer; + +/** + * A simple converter to return a Boolean value from an Object. + * + * @author Jeanette Winzenburg + */ +public interface BooleanValue { + + boolean getBoolean(Object value); + +} diff --git a/src/main/java/org/jdesktop/swingx/renderer/CellContext.java b/src/main/java/org/jdesktop/swingx/renderer/CellContext.java new file mode 100644 index 0000000000..42eb2b2ee4 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/renderer/CellContext.java @@ -0,0 +1,436 @@ +/* + * $Id: CellContext.java 3778 2010-09-07 10:10:49Z kleopatra $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.renderer; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; +import java.awt.*; +import java.io.Serializable; + +/** + * Encapsulates a snapshop of cell content and default display context + * for usage by a ComponentProvider. + *

      + * + * One part is the super-set of properties that's traditionally passed into the + * core renderers' (Table-, List-, Tree-) getXXCellRendererComponent. Raw + * properties which define the context are + * + *

        + *
      • selected + *
      • focused + *
      • expanded + *
      • leaf + *
      + * + * Similarl to a ComponentAdapter, the properties are a super-set of those for + * a concrete component type. It's up to sub-classes (once the generics will be removed, until + * then the DefaultXXRenderers - PENDING JW: undecided - even after the generics removal, the + * param list in the subclasses are the same) fill any reasonable + * defaults for those not applicable to the specific component context. + * + * With those raw properties given, a CellContext looks up and returns dependent visual + * properties as appropriate for the concrete component. Typically, they are taken + * from the component if supported, or requested from the UIManager. + * Dependent properties are + * + *
        + *
      • foreground and background color + *
      • border + *
      • icon (relevant for trees only) + *
      • editable + *
      + * + * For a backdoor, the cell location (in horizontal and vertical view coordinates) + * and the originating component is accessible as well. Note that they are not necessarily + * valid for the "life" component. It's not recommended to actually use them. If needed, + * that's probably a sign the api is lacking :-) + *

      + * + * + *

        + * + *
      • PENDING: still incomplete? how about Font? + *
      • PENDING: protected methods? Probably need to open up - derived + * properties should be accessible in client code. + *
      + * + * @author Jeanette Winzenburg + */ +public class CellContext implements Serializable { + + /** the default border for unfocused cells. */ + protected static Border noFocusBorder = new EmptyBorder(1, 1, 1, 1); + + /** ?? the default border for unfocused cells. ?? */ + private static final Border SAFE_NO_FOCUS_BORDER = new EmptyBorder(1, 1, 1, + 1); + + /** + * Returns the shared border for unfocused cells. + *

      + * PENDING: ?? copied from default renderers - why is it done like this? + * + * @return the border for unfocused cells. + */ + private static Border getNoFocusBorder() { + if (System.getSecurityManager() != null) { + return SAFE_NO_FOCUS_BORDER; + } else { + return noFocusBorder; + } + } + + /** PENDING JW: maybe make this a WeakReference? Would be a more robust fix for Issue #1040-swingx. */ + protected transient JComponent component; + + /** PENDING JW: maybe make this a WeakReference? Would be a more robust fix for Issue #1040-swingx. */ + protected transient Object value; + + protected transient int row; + + protected transient int column; + + protected transient boolean selected; + + protected transient boolean focused; + + protected transient boolean expanded; + + protected transient boolean leaf; + + protected transient boolean dropOn; + + // --------------------------- install context + + + /** + * Sets the state of the cell's context. Convenience method for subclasses. + * + * @param value the content value of the cell + * @param row the cell's row index in view coordinates + * @param column the cell's column index in view coordinates + * @param selected the cell's selected state + * @param focused the cell's focused state + * @param expanded the cell's expanded state + * @param leaf the cell's leaf state + */ + protected void installState(Object value, int row, int column, + boolean selected, boolean focused, boolean expanded, boolean leaf) { + this.value = value; + this.row = row; + this.column = column; + this.selected = selected; + this.focused = focused; + this.expanded = expanded; + this.leaf = leaf; + } + + /** + * Replaces the value of this cell context with the given parameter and returns + * the replaced value. + * + * @param value the new value of the cell context + * @return the replaced value of the cell context + */ + public Object replaceValue(Object value) { + Object old = getValue(); + this.value = value; + return old; + } + + // -------------------- accessors of installed state + + /** + * Returns the component the cell resides on, may be null. Subclasses are + * expected to override and return the component type they are handling. + * + * @return the component the cell resides on, may be null. + */ + public JComponent getComponent() { + return component; + } + + /** + * Returns the value of the cell as set in the install. + * + * @return the content value of the cell. + */ + public Object getValue() { + return value; + } + + /** + * Returns the cell's row index in view coordinates as set in the install. + * + * @return the cell's row index. + */ + public int getRow() { + return row; + } + + /** + * Returns the cell's column index in view coordinates as set in the + * install. + * + * @return the cell's column index. + */ + public int getColumn() { + return column; + } + + /** + * Returns the selected state as set in the install. + * + * @return the cell's selected state. + */ + public boolean isSelected() { + return selected; + } + + /** + * Returns the focused state as set in the install. + * + * @return the cell's focused state. + */ + public boolean isFocused() { + return focused; + } + + /** + * Returns the expanded state as set in the install. + * + * @return the cell's expanded state. + */ + public boolean isExpanded() { + return expanded; + } + + /** + * Returns the leaf state as set in the install. + * + * @return the cell's leaf state. + */ + public boolean isLeaf() { + return leaf; + } + + // -------------------- accessors for derived state + /** + * Returns the cell's editability. Subclasses should override to return a + * reasonable cell-related state. + *

      + * + * Here: false. + * + * @return the cell's editable property. + */ + public boolean isEditable() { + return false; + } + + /** + * Returns the icon. Subclasses should override to return a reasonable + * cell-related state. + *

      + * + * Here: null. + * + * @return the cell's icon. + */ + public Icon getIcon() { + return null; + } + + /** + * Returns a boolean indicating if the cell is a drop location with any of the dropOn + * modes. It's up to subclasses to implement. + *

      + * + * Here: false. + * + * @return true if the current cell is a drop location with any of the dropOn modes, + * false otherwise + */ + protected boolean isDropOn() { + return dropOn; + } + + /** + * Returns the foreground color of the renderered component or null if the + * component is null + *

      + * + * PENDING: fallback to UI properties if comp == null? + * + * @return the foreground color of the rendered component. + */ + protected Color getForeground() { + if (isDropOn()) { + return getSelectionForeground(); + } + return getComponent() != null ? getComponent().getForeground() : null; + } + + /** + * Returns the background color of the renderered component or null if the + * component is null + *

      + * + * PENDING: fallback to UI properties if comp == null? + * + * @return the background color of the rendered component. + */ + protected Color getBackground() { + if (isDropOn()) { + return getSelectionBackground(); + } + return getComponent() != null ? getComponent().getBackground() : null; + } + + /** + * Returns the default selection background color of the renderered + * component. Typically, the color is LF specific. It's up to subclasses to + * look it up. Here: returns null. + *

      + * + * PENDING: return UI properties here? + * + * @return the selection background color of the rendered component. + */ + protected Color getSelectionBackground() { + return null; + } + + /** + * Returns the default selection foreground color of the renderered + * component. Typically, the color is LF specific. It's up to subclasses to + * look it up. Here: returns null. + *

      + * + * PENDING: return UI properties here? + * + * @return the selection foreground color of the rendered component. + */ + protected Color getSelectionForeground() { + return null; + } + + /** + * Returns the default focus border of the renderered component. Typically, + * the border is LF specific. + * + * @return the focus border of the rendered component. + */ + protected Border getFocusBorder() { + Border border = null; + if (isSelected()) { + border = UIManager + .getBorder(getUIKey("focusSelectedCellHighlightBorder")); + } + if (border == null) { + border = UIManager.getBorder(getUIKey("focusCellHighlightBorder")); + } + return border; + } + + /** + * Returns the default border of the renderered component depending on cell + * state. Typically, the border is LF specific. + *

      + * + * Here: returns the focus border if the cell is focused, the context + * defined no focus border otherwise. + * + * @return the default border of the rendered component. + */ + protected Border getBorder() { + if (isFocused()) { + return getFocusBorder(); + } + Border border = UIManager.getBorder(getUIKey("cellNoFocusBorder")); + return border != null ? border : getNoFocusBorder(); + } + + /** + * Returns the default focused foreground color of the renderered component. + * Typically, the color is LF specific. + * + * @return the focused foreground color of the rendered component. + */ + protected Color getFocusForeground() { + return UIManager.getColor(getUIKey("focusCellForeground")); + } + + /** + * Returns the default focused background color of the renderered component. + * Typically, the color is LF specific. + * + * @return the focused background color of the rendered component. + */ + protected Color getFocusBackground() { + return UIManager.getColor(getUIKey("focusCellBackground")); + } + + protected Color getDropCellForeground() { + return UIManager.getColor(getUIKey("dropCellForeground")); + } + + protected Color getDropCellBackground() { + return UIManager.getColor(getUIKey("dropCellBackground")); + } + // ----------------------- convenience + + /** + * Convenience method to build a component type specific lookup key for the + * UIManager. + * + * @param key the general part of the key + * @return a composed key build of a component type prefix and the input. + */ + protected String getUIKey(String key) { + return getUIPrefix() + key; + } + + /** + * Returns the component type specific prefix of keys for lookup in the + * UIManager. Subclasses must override, here: returns the empty String. + * + * @return the component type specific prefix. + */ + protected String getUIPrefix() { + return ""; + } + + /** + * Returns the Font of the target component or null if no component installed. + * @return + */ + protected Font getFont() { + return getComponent() != null ? getComponent().getFont() : null; + } + + public String getCellRendererName() { + return getUIPrefix() + "cellRenderer"; + } + +} diff --git a/src/main/java/org/jdesktop/swingx/renderer/CheckBoxProvider.java b/src/main/java/org/jdesktop/swingx/renderer/CheckBoxProvider.java new file mode 100644 index 0000000000..93a22bc981 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/renderer/CheckBoxProvider.java @@ -0,0 +1,184 @@ +/* + * $Id: CheckBoxProvider.java 3152 2008-12-23 18:12:39Z kschaefe $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.renderer; + +import javax.swing.*; + +/** + * A component provider which uses a JCheckBox. + *

      + * + * This implementation respects a BooleanValue and a StringValue to configure + * the button's selected and text property. By default, the selected is mapped + * to a Boolean-type value and the text is empty. + *

      + * + * To allow mapping to different types, client code can supply a custom + * StringValue which also implements BooleanValue. F.i. to render a cell value + * of type TableColumnExt with the column's visibility mapped to the selected + * and the column's title to the text: + * + *

      
      + *            
      + *     BooleanValue bv = new BooleanValue(){
      + *        public boolean getBoolean(Object value) {
      + *           if (value instanceof TableColumnExt) 
      + *               return ((TableColumnExt) value).isVisible();
      + *           return false;
      + *        }
      + *     };
      + *     StringValue sv = new StringValue() {
      + *         public String getString(Object value) {
      + *           if (value instanceof TableColumnExt) 
      + *               return ((TableColumnExt) value).getTitle();
      + *           return "";
      + *         }
      + *     };
      + *     list.setCellRenderer(new DefaultListRenderer(
      + *           new CheckBoxProvider(new MappedValue(sv, null, bv), JLabel.LEADING))); 
      + * 
      + * + * + * @see BooleanValue + * @see StringValue + * @see MappedValue + * + * @author Jeanette Winzenburg + */ +public class CheckBoxProvider extends ComponentProvider { + + private boolean borderPainted; + + /** + * Instantiates a CheckBoxProvider with default properties.

      + * + */ + public CheckBoxProvider() { + this(null); + } + + /** + * Instantiates a CheckBoxProvider with the given StringValue and default + * alignment. + * + * @param stringValue the StringValue to use for formatting. + */ + public CheckBoxProvider(StringValue stringValue) { + this(stringValue, JLabel.CENTER); + } + + /** + * Instantiates a CheckBoxProvider with the given StringValue and + * alignment. + * + * @param stringValue the StringValue to use for formatting. + * @param alignment the horizontalAlignment. + */ + public CheckBoxProvider(StringValue stringValue, int alignment) { + super(stringValue == null ? StringValues.EMPTY : stringValue, alignment); + setBorderPainted(true); + } + + + /** + * Returns the border painted flag. + * @return the borderpainted flag to use on the checkbox. + * @see #setBorderPainted(boolean) + */ + public boolean isBorderPainted() { + return borderPainted; + } + + /** + * Sets the border painted flag. The underlying checkbox + * is configured with this value on every request.

      + * + * The default value is true. + * + * @param borderPainted the borderPainted property to configure + * the underlying checkbox with. + * + * @see #isBorderPainted() + */ + public void setBorderPainted(boolean borderPainted) { + this.borderPainted = borderPainted; + } + + /** + * {@inheritDoc}

      + * Overridden to set the button's selected state and text.

      + * + * PENDING: set icon? + * + * @see #getValueAsBoolean(CellContext) + * @see #getValueAsString(CellContext) + */ + @Override + protected void format(CellContext context) { + rendererComponent.setSelected(getValueAsBoolean(context)); + rendererComponent.setText(getValueAsString(context)); + } + + /** + * Returns a boolean representation of the content.

      + * + * This method messages the + * BooleanValue to get the boolean rep. If none available, + * checks for Boolean type directly and returns its value. Returns + * false otherwise.

      + * + * PENDING: fallback to check for boolean is convenient .. could cleanup + * to use a default BooleanValue instead. + * + * @param context the cell context, must not be null. + * @return the boolean representation of the cell's content, + * or false if none if available. + */ + protected boolean getValueAsBoolean(CellContext context) { + if (formatter instanceof BooleanValue) { + return ((BooleanValue) formatter).getBoolean(context.getValue()); + } + return Boolean.TRUE.equals(context.getValue()); + } + + /** + * {@inheritDoc}

      + * + * Here: set's the buttons horizontal alignment and borderpainted properties + * to this provider's properties. + */ + @Override + protected void configureState(CellContext context) { + rendererComponent.setBorderPainted(isBorderPainted()); + rendererComponent.setHorizontalAlignment(getHorizontalAlignment()); + } + + /** + * {@inheritDoc}

      + * Here: returns a JCheckBox as rendering component.

      + * + */ + @Override + protected AbstractButton createRendererComponent() { + return new JRendererCheckBox(); + } + +} diff --git a/src/main/java/org/jdesktop/swingx/renderer/ComponentProvider.java b/src/main/java/org/jdesktop/swingx/renderer/ComponentProvider.java new file mode 100644 index 0000000000..0a7dbd739e --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/renderer/ComponentProvider.java @@ -0,0 +1,383 @@ +/* + * $Id: ComponentProvider.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.renderer; + +import org.jdesktop.swingx.plaf.UIDependent; + +import javax.swing.*; +import java.io.Serializable; + +/** + * Abstract base class of a provider for a cell rendering component. Configures + * the component's content and default visuals depending on the renderee's state + * as captured in a CellContext. It's basically re-usable across + * all types of renderees (JTable, JList, JTree). + *

      + * + *

      Content

      + * + * A provider guarantees to configure the "content" properties completely + * for any given object. The most frequent mappings are to text and/or icon + * properties of the rendering components. The former is controlled by a + * StringValue (see below), the latter by an IconValue. Subclasses which + * hand out component of type IconAware guarantee to reset its icon property + * always.

      + * + * To ease content configuration, it supports a pluggable + * StringValue which purpose is to create and return a string + * representation of a given object. Implemenations of a ComponentProvider can + * use it to configure their rendering component as appropriate.

      + * + * F.i. to show a Contributor cell object as "Busywoman, Herta" implement a + * custom StringValue and use it in a text rendering provider. (Note that SwingX + * default implementations of Table/List/TreeCellRenderer have convenience + * constructors to take the converter and create a default LabelProvider which + * uses it). + * + *

      
      + * StringValue stringValue = new StringValue() {
      + * 
      + *     public String getString(Object value) {
      + *         if (!(value instanceof Contributor))
      + *             return TO_STRING.getString(value);
      + *         Contributor contributor = (Contributor) value;
      + *         return contributor.lastName + ", " + contributor.firstName;
      + *     }
      + * 
      + * };
      + * table.setDefaultRenderer(Contributor.class, new DefaultTableRenderer(
      + *         stringValue));
      + * list.setCellRenderer(new DefaultListRenderer(stringValue));
      + * tree.setCellRenderer(new DefaultTreeRenderer(stringValue));
      + * 
      + * 
      + * + * To ease handling of formatted localizable content, there's a + * FormatStringValue which is pluggable with a + * Format.

      + * + * F.i. to show a Date's time in the default Locale's SHORT + * version and right align the cell + * + *

      
      + *   StringValue stringValue = new FormatStringValue(
      + *       DateFormat.getTimeInstance(DateFormat.SHORT));
      + *   table.getColumnExt("timeID").setCellRenderer(
      + *       new DefaultTableRenderer(stringValue, JLabel.RIGHT);  
      + * 
      + * + * + *

      + * + * + *

      Default Visuals

      + * + * Guarantees to completely configure the visual properties listed below. As a + * consequence, client code (f.i. in Highlighters) can safely + * change them without long-lasting visual artefacts. + * + *
        + *
      • foreground and background, depending on selected and focused state + *
      • border + *
      • font + *
      • Painter (if applicable) + *
      • enabled + *
      • componentOrientation + *
      • tooltipText + *
      • minimum-, maximum-, preferredSize + *
      • horizontal alignment (if applicable) + *
      + * + * As this internally delegates default visual configuration to a + * DefaultVisuals (which handles the first eight items) + * subclasses have to guarantee the alignment only. + *

      + * + * + * @see StringValue + * @see FormatStringValue + * @see IconValue + * @see BooleanValue + * @see CellContext + * @see DefaultTableRenderer + * @see DefaultListRenderer + * @see DefaultTreeRenderer + * @see DefaultVisuals + */ +public abstract class ComponentProvider + implements Serializable, UIDependent { + /** component to render with. */ + protected T rendererComponent; + /** configurator of default visuals. */ + protected DefaultVisuals defaultVisuals; + /** horizontal (text) alignment of component. + * PENDING: useful only for labels, buttons? */ + protected int alignment; + /** the converter to use for string representation. + * PENDING: IconValue? */ + protected StringValue formatter; + + /** + * Instantiates a component provider with LEADING + * horizontal alignment and default to-String converter.

      + * + */ + public ComponentProvider() { + this(null, JLabel.LEADING); + } + + /** + * Instantiates a component provider with LEADING + * horizontal alignment and the given converter.

      + * + * @param converter the converter to use for mapping the cell value to a + * String representation. + */ + public ComponentProvider(StringValue converter) { + this(converter, JLabel.LEADING); + } + + /** + * Instantiates a LabelProvider with given to-String converter and given + * horizontal alignment. If the converter is null, the default TO_STRING is + * used. + * + * @param converter the converter to use for mapping the cell value to a + * String representation. + * @param alignment the horizontal alignment. + */ + public ComponentProvider(StringValue converter, int alignment) { + setHorizontalAlignment(alignment); + setStringValue(converter); + rendererComponent = createRendererComponent(); + defaultVisuals = createDefaultVisuals(); + } + + /** + * Configures and returns an appropriate component to render a cell + * in the given context. If the context is null, returns the + * component in its current state. + * + * @param context the cell context to configure from + * @return a component to render a cell in the given context. + */ + public T getRendererComponent(CellContext context) { + if (context != null) { + configureVisuals(context); + configureContent(context); + } + return rendererComponent; + } + + /** + * Sets the horizontal alignment property to configure the component with. + * Allowed values are those accepted by corresponding JLabel setter. The + * default value is JLabel.LEADING. This controller guarantees to apply the + * alignment on each request for a configured rendering component, if + * possible. Note that not all components have a horizontal alignment + * property. + * + * @param alignment the horizontal alignment to use when configuring the + * rendering component. + */ + public void setHorizontalAlignment(int alignment) { + this.alignment = alignment; + } + + /** + * Returns the horizontal alignment. + * + * @return the horizontal alignment of the rendering component. + * + * @see #setHorizontalAlignment(int) + * + */ + public int getHorizontalAlignment() { + return alignment; + } + + /** + * Sets the StringValue to use. If the given StringValue is null, + * defaults to StringValue.TO_STRING.

      + * + * @param formatter the format to use. + */ + public void setStringValue(StringValue formatter) { + if (formatter == null) { + formatter = StringValues.TO_STRING; + } + this.formatter = formatter; + } + + /** + * Returns the StringValue to use for obtaining + * the String representation.

      + * + * @return the StringValue used by this provider, guaranteed to + * be not null. + */ + public StringValue getStringValue() { + return formatter; + } + + /** + * Returns a string representation of the content. + *

      + * + * This method guarantees to return the same string representation as would + * appear in the renderer, given that the corresponding cellContext has the + * same value as the parameter passed-in here. That is (assuming that the + * rendering component has a getText()) + * + *

      
      +     * if (equals(value, context.getValue()) {
      +     *     assertEquals(provider.getString(value), 
      +     *     provider.getRenderingComponent(context).getText());
      +     * }
      +     * 
      + * + * This implementation simply delegates to its StringValue. Subclasses might + * need to override to comply. + *

      + * + * This is a second attempt - the driving force is the need for a consistent + * string representation across all (new and old) themes: rendering, + * (pattern) filtering/highlighting, searching, auto-complete ... + *

      + * + * @param value the object to represent as string. + * @return a appropriate string representation of the cell's content. + */ + public String getString(Object value) { + return formatter.getString(value); + } + + /** + * Returns a String representation of the content.

      + * + * This method messages the + * StringValue to get the String rep. Meant as + * a convenience for subclasses. + * + * @param context the cell context, must not be null. + * @return a appropriate string representation of the cell's content. + */ + protected String getValueAsString(CellContext context) { + Object value = context.getValue(); + return formatter.getString(value); + } + + /** + * Returns a Icon representation of the content.

      + * + * This method messages the + * IconValue to get the Icon rep. Meant as + * a convenience for subclasses. + * + * @param context the cell context, must not be null. + * @return a appropriate icon representation of the cell's content, + * or null if non if available. + */ + protected Icon getValueAsIcon(CellContext context) { + Object value = context.getValue(); + if (formatter instanceof IconValue) { + return ((IconValue) formatter).getIcon(value); + } + return null; + } + + /** + * Configures the rendering component's default visuals frome + * the given cell context. Here: delegates to the renderer + * controller. + * + * @param context the cell context to configure from, must not be null. + * @see DefaultVisuals + */ + protected void configureVisuals(CellContext context) { + defaultVisuals.configureVisuals(rendererComponent, context); + } + + /** + * Configures the renderering component's content and state from the + * given cell context. + * + * @param context the cell context to configure from, must not be null. + * + * @see #configureState(CellContext) + * @see #format(CellContext) + */ + protected void configureContent(CellContext context) { + configureState(context); + format(context); + } + + /** + * Formats the renderering component's content from the + * given cell context. + * + * @param context the cell context to configure from, must not be null. + */ + protected abstract void format(CellContext context); + + /** + * Configures the rendering component's state from the + * given cell context. + * @param context the cell context to configure from, must not be null. + */ + protected abstract void configureState(CellContext context); + + /** + * Factory method to create and return the component to use for rendering.

      + * + * @return the component to use for rendering. + */ + protected abstract T createRendererComponent(); + + /** + * Factory method to create and return the DefaultVisuals used by this + * to configure the default visuals. Here: creates the default controller + * parameterized to the same type as this. + * + * @return the controller used to configure the default visuals of + * the rendering component. + */ + protected DefaultVisuals createDefaultVisuals() { + return new DefaultVisuals(); + } + + /** + * Intermediate exposure during refactoring... + * + * @return the default visual configurator used by this. + */ + protected DefaultVisuals getDefaultVisuals() { + return defaultVisuals; + } + + /** + * {@inheritDoc} + */ + @Override + public void updateUI() { + SwingUtilities.updateComponentTreeUI(rendererComponent); + } +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/renderer/DefaultListRenderer.java b/src/main/java/org/jdesktop/swingx/renderer/DefaultListRenderer.java new file mode 100644 index 0000000000..7853fa2578 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/renderer/DefaultListRenderer.java @@ -0,0 +1,205 @@ +/* + * $Id: DefaultListRenderer.java 3779 2010-09-07 18:01:55Z kschaefe $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.renderer; + +import javax.swing.*; +import java.awt.*; + +/** + * Adapter to glue SwingX renderer support to core API. It has convenience + * constructors to create a LabelProvider, optionally configured with a + * StringValue and horizontal alignment. Typically, client code does not + * interact with this class except at instantiation time. + *

      + * + * Note: core DefaultListCellRenderer shows either an icon or the element's + * toString representation, depending on whether or not the given value + * is of type icon or implementors. This renderer's empty/null provider + * constructor takes care of configuring the default provider with a converter + * which mimics that behaviour. When instantiating this renderer with + * any of the constructors which have converters as parameters, + * it's up to the client code to supply the appropriate converter, if needed: + * + * + *

      
      + * StringValue sv = new StringValue() {
      + * 
      + *     public String getString(Object value) {
      + *         if (value instanceof Icon) {
      + *             return "";
      + *         }
      + *         return StringValue.TO_STRING.getString(value);
      + *     }
      + * 
      + * };
      + * StringValue lv = new MappedValue(sv, IconValue.ICON);
      + * listRenderer = new DefaultListRenderer(lv, alignment);
      + * 
      + * 
      + * + *

      + * + * + * @author Jeanette Winzenburg + * + * @see ComponentProvider + * @see StringValue + * @see IconValue + * @see MappedValue + * + * + */ +public class DefaultListRenderer extends AbstractRenderer + implements ListCellRenderer { + + protected ListCellContext cellContext; + + /** + * Instantiates a default list renderer with the default component + * provider. + * + */ + public DefaultListRenderer() { + this((ComponentProvider) null); + } + + /** + * Instantiates a ListCellRenderer with the given ComponentProvider. + * If the provider is null, creates and uses a default. The default + * provider is of type LabelProvider

      + * + * Note: the default provider is configured with a custom StringValue + * which behaves exactly as core DefaultListCellRenderer: depending on + * whether or not given value is of type icon or implementors, it shows + * either the icon or the element's toString. + * + * @param componentProvider the provider of the configured component to + * use for cell rendering + */ + public DefaultListRenderer(ComponentProvider componentProvider) { + super(componentProvider); + this.cellContext = new ListCellContext(); + } + + /** + * Instantiates a default table renderer with a default component controller + * using the given converter.

      + * + * PENDING JW: how to guarantee core consistent icon handling? Leave to + * client code? + * + * @param converter the converter to use for mapping the content value to a + * String representation. + * + */ + public DefaultListRenderer(StringValue converter) { + this(new LabelProvider(converter)); + } + + /** + * Instantiates a default list renderer with a default component + * controller using the given converter and horizontal + * alignment. + * + * PENDING JW: how to guarantee core consistent icon handling? Leave to + * client code? + * + * + * @param converter the converter to use for mapping the + * content value to a String representation. + * @param alignment the horizontal alignment. + */ + public DefaultListRenderer(StringValue converter, int alignment) { + this(new LabelProvider(converter, alignment)); + } + + + /** + * Instantiates a default list renderer with default component provider + * using both converters. + * + * @param stringValue the converter to use for the string representation + * @param iconValue the converter to use for the icon representation + */ + public DefaultListRenderer(StringValue stringValue, IconValue iconValue) { + this(new MappedValue(stringValue, iconValue)); + } + + /** + * Instantiates a default list renderer with default component provider + * using both converters and the given alignment. + * + * @param stringValue the converter to use for the string representation + * @param iconValue the converter to use for the icon representation + * @param alignment the rendering component's horizontal alignment + */ + public DefaultListRenderer(StringValue stringValue, IconValue iconValue, + int alignment) { + this(new MappedValue(stringValue, iconValue), alignment); + } + + // -------------- implements javax.swing.table.ListCellRenderer + /** + * + * Returns a configured component, appropriate to render the given + * list cell.

      + * + * Note: The component's name is set to "List.cellRenderer" for the sake + * of Synth-based LAFs. + * + * @param list the JList to render on + * @param value the value to assign to the cell + * @param isSelected true if cell is selected + * @param cellHasFocus true if cell has focus + * @param index the row index (in view coordinates) of the cell to render + * @return a component to render the given list cell. + */ + @Override + public Component getListCellRendererComponent(JList list, Object value, + int index, boolean isSelected, boolean cellHasFocus) { + cellContext.installContext(list, value, index, 0, isSelected, + cellHasFocus, true, true); + Component comp = componentController.getRendererComponent(cellContext); + // fix issue #1040-swingx: memory leak if value not released + cellContext.replaceValue(null); + return comp; + } + + /** + * {@inheritDoc} + */ + @Override + protected ComponentProvider createDefaultComponentProvider() { + return new LabelProvider(createDefaultStringValue()); + } + + /** + * Creates and returns the default StringValue for a JList.

      + * This is added to keep consistent with core list rendering which + * shows either the Icon (for Icon value types) or the default + * to-string for non-icon types. + * + * @return the StringValue to use by default. + */ + private StringValue createDefaultStringValue() { + return MappedValues.STRING_OR_ICON_ONLY; + } +} diff --git a/src/main/java/org/jdesktop/swingx/renderer/DefaultTableRenderer.java b/src/main/java/org/jdesktop/swingx/renderer/DefaultTableRenderer.java new file mode 100644 index 0000000000..57cd3d7823 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/renderer/DefaultTableRenderer.java @@ -0,0 +1,186 @@ +/* + * $Id: DefaultTableRenderer.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.renderer; + + +import javax.swing.*; +import javax.swing.table.TableCellRenderer; +import java.awt.*; + + +/** + * Adapter to glue SwingX renderer support to core api. It has convenience + * constructors to create a LabelProvider, optionally configured with a + * StringValue and horizontal alignment. Typically, client code does not + * interact with this class except at instantiation time. + *

      + * + * JXTable uses instances of this as per-class default renderers. + * + *

      
      + * setDefaultRenderer(Object.class, new DefaultTableRenderer());
      + * setDefaultRenderer(Number.class, new DefaultTableRenderer(
      + *         FormatStringValues.NUMBER_TO_STRING, JLabel.RIGHT));
      + * setDefaultRenderer(Date.class, new DefaultTableRenderer(
      + *         FormatStringValues.DATE_TO_STRING));
      + * // use the same center aligned default for Image/Icon
      + * TableCellRenderer renderer = new DefaultTableRenderer(new MappedValue(
      + *         StringValues.EMPTY, IconValues.ICON), JLabel.CENTER);
      + * setDefaultRenderer(Icon.class, renderer);
      + * setDefaultRenderer(ImageIcon.class, renderer);
      + * // use a CheckBoxProvider for booleans
      + * setDefaultRenderer(Boolean.class,
      + *         new DefaultTableRenderer(new CheckBoxProvider()));
      + * 
      + * + * + * + * @author Jeanette Winzenburg + * + * @see ComponentProvider + * @see LabelProvider + * @see StringValue + * @see IconValue + * @see MappedValue + * @see CellContext + * + */ +public class DefaultTableRenderer extends AbstractRenderer + implements TableCellRenderer { + + private TableCellContext cellContext; + + + /** + * Instantiates a default table renderer with the default component + * provider. + * + * @see #DefaultTableRenderer(ComponentProvider) + */ + public DefaultTableRenderer() { + this((ComponentProvider) null); + } + + /** + * Instantiates a default table renderer with the given component provider. + * If the controller is null, creates and uses a default. The default + * provider is of type LabelProvider. + * + * @param componentProvider the provider of the configured component to + * use for cell rendering + */ + public DefaultTableRenderer(ComponentProvider componentProvider) { + super(componentProvider); + this.cellContext = new TableCellContext(); + } + + /** + * Instantiates a default table renderer with a default component + * provider using the given converter. + * + * @param converter the converter to use for mapping the + * content value to a String representation. + * + * @see #DefaultTableRenderer(ComponentProvider) + */ + public DefaultTableRenderer(StringValue converter) { + this(new LabelProvider(converter)); + } + + /** + * Instantiates a default table renderer with a default component + * provider using the given converter and horizontal + * alignment. + * + * @param converter the converter to use for mapping the + * content value to a String representation. + * + * @see #DefaultTableRenderer(ComponentProvider) + */ + public DefaultTableRenderer(StringValue converter, int alignment) { + this(new LabelProvider(converter, alignment)); + } + + /** + * Intantiates a default table renderer with default component provider + * using both converters. + * + * @param stringValue the converter to use for the string representation + * @param iconValue the converter to use for the icon representation + */ + public DefaultTableRenderer(StringValue stringValue, IconValue iconValue) { + this(new MappedValue(stringValue, iconValue)); + } + + /** + * Intantiates a default table renderer with default component provider + * using both converters and the given alignment. + * + * @param stringValue the converter to use for the string representation + * @param iconValue the converter to use for the icon representation + * @param alignment the rendering component's horizontal alignment + */ + public DefaultTableRenderer(StringValue stringValue, IconValue iconValue, + int alignment) { + this(new MappedValue(stringValue, iconValue), alignment); + } + + // -------------- implements javax.swing.table.TableCellRenderer + /** + * + * Returns a configured component, appropriate to render the given + * list cell.

      + * + * Note: The component's name is set to "Table.cellRenderer" for the sake + * of Synth-based LAFs. + * + * @param table the JTable + * @param value the value to assign to the cell at + * [row, column] + * @param isSelected true if cell is selected + * @param hasFocus true if cell has focus + * @param row the row of the cell to render + * @param column the column of the cell to render + * @return the default table cell renderer + */ + @Override + public Component getTableCellRendererComponent(JTable table, Object value, + boolean isSelected, boolean hasFocus, int row, int column) { + cellContext.installContext(table, value, row, column, isSelected, hasFocus, + true, true); + Component comp = componentController.getRendererComponent(cellContext); + // fix issue #1040-swingx: memory leak if value not released + cellContext.replaceValue(null); + return comp; + } + + /** + * {@inheritDoc} + */ + @Override + protected ComponentProvider createDefaultComponentProvider() { + return new LabelProvider(); + } + + +} + + diff --git a/src/main/java/org/jdesktop/swingx/renderer/DefaultTreeRenderer.java b/src/main/java/org/jdesktop/swingx/renderer/DefaultTreeRenderer.java new file mode 100644 index 0000000000..50a6af4cf7 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/renderer/DefaultTreeRenderer.java @@ -0,0 +1,165 @@ +/* + * $Id: DefaultTreeRenderer.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.renderer; + + +import javax.swing.*; +import javax.swing.tree.TreeCellRenderer; +import java.awt.*; + + +/** + * Adapter to glue SwingX renderer support to core api. + *

      + * + * + * @author Jeanette Winzenburg + * + * + */ +public class DefaultTreeRenderer extends AbstractRenderer + implements TreeCellRenderer { + + private TreeCellContext cellContext; + + /** + * Instantiates a default tree renderer with the default component + * provider. + * + */ + public DefaultTreeRenderer() { + this((ComponentProvider)null); + } + + + /** + * Instantiates a default tree renderer with the given component provider. + * If the controller is null, creates and uses a default. The default + * controller is of type WrappingProvider. + * + * @param componentProvider the provider of the configured component to + * use for cell rendering + */ + public DefaultTreeRenderer(ComponentProvider componentProvider) { + super(componentProvider); + this.cellContext = new TreeCellContext(); + } + + /** + * Instantiates a default tree renderer with the default + * wrapping provider, using the given IconValue for + * customizing the icons. + * + * @param iv the IconValue to use for mapping a custom icon + * for a given value + * + */ + public DefaultTreeRenderer(IconValue iv) { + this(new WrappingProvider(iv)); + } + + /** + * Instantiates a default tree renderer with a default component + * provider using the given converter. + * + * @param sv the converter to use for mapping the + * content value to a String representation. + * + */ + public DefaultTreeRenderer(StringValue sv) { + this(new WrappingProvider(sv)); + } + + + /** + * Instantiates a default tree renderer with the default + * wrapping provider, using the given IconValue for + * customizing the icons and the given StringValue for + * node content. + * + * @param iv the IconValue to use for mapping a custom icon + * for a given value + * @param sv the converter to use for mapping the + * content value to a String representation. + * + */ + public DefaultTreeRenderer(IconValue iv, StringValue sv) { + this(new WrappingProvider(iv, sv)); + } + + /** + * Instantiates a default tree renderer with the default + * wrapping provider, using the given IconValue for + * customizing the icons and the given StringValue for + * node content. + * + * @param iv the IconValue to use for mapping a custom icon + * for a given value + * @param sv the converter to use for mapping the + * content value to a String representation. + * @param unwrapUserObject a flag indicating whether this provider + * should auto-unwrap the userObject from the context value. + * + */ + public DefaultTreeRenderer(IconValue iv, StringValue sv, boolean unwrapUserObject) { + this(new WrappingProvider(iv, sv, unwrapUserObject)); + } + + // -------------- implements javax.swing.table.TableCellRenderer + /** + * + * Returns a configured component, appropriate to render the given tree + * cell.

      + * + * @param tree the JTree + * @param value the value to assign to the cell + * @param selected true if cell is selected + * @param expanded true if the cell is expanded + * @param leaf true if the cell is a leaf + * @param hasFocus true if cell has focus + * @param row the row of the cell to render + * @return a component to render the given list cell. + */ + @Override + public Component getTreeCellRendererComponent(JTree tree, Object value, + boolean selected, boolean expanded, boolean leaf, int row, + boolean hasFocus) { + cellContext.installContext(tree, value, row, 0, selected, hasFocus, + expanded, leaf); + Component comp = componentController.getRendererComponent(cellContext); + // fix issue #1040-swingx: memory leak if value not released + cellContext.replaceValue(null); + return comp; + } + + + /** + * {@inheritDoc} + */ + @Override + protected ComponentProvider createDefaultComponentProvider() { + return new WrappingProvider(); + } + + +} + + diff --git a/src/main/java/org/jdesktop/swingx/renderer/DefaultVisuals.java b/src/main/java/org/jdesktop/swingx/renderer/DefaultVisuals.java new file mode 100644 index 0000000000..1c96e5e940 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/renderer/DefaultVisuals.java @@ -0,0 +1,264 @@ +/* + * $Id: DefaultVisuals.java 3778 2010-09-07 10:10:49Z kleopatra $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.renderer; + +import javax.swing.*; +import java.awt.*; +import java.io.Serializable; + +/** + * Encapsulates the default visual configuration of renderering components, + * respecting the state of the passed-in CellContext. It's + * basically re-usable across all types of renderees (JTable, JList, JTree). + *

      + * + * Guarantees to completely configure the default visual properties (listed + * below) of a given component. As a consequence, client code (f.i. in + * Highlighters) can safely change them without long-lasting + * visual artefacts. + * + *

        + *
      • foreground and background, depending on selected and focused state + *
      • border + *
      • font + *
      • Painter (if applicable) + *
      • enabled + *
      • componentOrientation + *
      • toolTipText + *
      • minimum-, maximum-, preferredSize + *
      • name + *
      + * + * Client code will rarely need to be aware of this class. It's the single + * place to change on introduction of new properties considered as belonging + * to the "default visuals" of rendering components.

      + * + * PENDING: allow mutators for overruling the CellContexts + * defaults? Would prefer not to, as in the context of SwingX visual config on + * the renderer level is discouraged (the way to go are Highlighters.

      + * + * PENDING: not yet quite decided whether the toolTipText property belongs + * into the visual default config. Doing so gives client code the choice to + * set it either in a Highlighter or a custom ComponentProvider. + * + * @author Jeanette Winzenburg + * + * @see CellContext + */ +public class DefaultVisuals implements Serializable { + + + private Color unselectedForeground; + + private Color unselectedBackground; + + /** + * Sets the renderer's unselected-foreground color to the specified color. + * If not null this color will overrule the default color of + * the CellContext. + * + * @param c set the foreground color to this value + */ + public void setForeground(Color c) { + unselectedForeground = c; + } + + /** + * Sets the renderer's unselected-background color to the specified color. + * If not null this color will overrule the default color of + * the CellContext. + * + * @param c set the background color to this value + */ + public void setBackground(Color c) { + unselectedBackground = c; + } + + + //---------------- subclass configuration + /** + * Configures all default visual state of the rendering component from the + * given cell context. + * + * @param renderingComponent the component to configure, must not be null + * @param context the cell context to configure from, must not be null + * @throws NullPointerException if either renderingComponent or cellContext + * is null + */ + public void configureVisuals(T renderingComponent, CellContext context) { + configureState(renderingComponent, context); + configureColors(renderingComponent, context); + configureBorder(renderingComponent, context); + configurePainter(renderingComponent, context); + } + + /** + * Configures the default Painter if applicable. Here: set's to null. + * + * @param renderingComponent the component to configure, must not be null + * @param context the cell context to configure from, must not be null + */ + protected void configurePainter(T renderingComponent, CellContext context) { + if (renderingComponent instanceof PainterAware) { + ((PainterAware) renderingComponent).setPainter(null); + } + + } + + /** + * Configure "divers" visual state of the rendering component from the given + * cell context. + *

      + * + * Here: synch Font, ComponentOrientation and + * enabled to context's component. Resets toolTipText to null. + * Calls configureSizes to reset xxSize if appropriate. Resets the component's + * name property. + *

      + * + * PENDING: not fully defined - "divers" means everything that's not + * Colors + * nor Border nor Painter. + * + * @param renderingComponent the component to configure, must not be null + * @param context the cell context to configure from, must not be null + */ + protected void configureState(T renderingComponent, CellContext context) { + renderingComponent.setName(context.getCellRendererName()); + renderingComponent.setToolTipText(null); + configureSizes(renderingComponent, context); + // PENDING JW: as of Issue #1269 this was changed to query the + // CellContext for the font - should move out off the else? + // context takes care of null component + renderingComponent.setFont(context.getFont()); + if (context.getComponent() == null) { + // what to do? + // we guarantee to cleanup completely - what are the defaults? + // leave the decistion to the context? + } else { + renderingComponent.setEnabled(context.getComponent().isEnabled()); + renderingComponent.applyComponentOrientation(context.getComponent() + .getComponentOrientation()); + } + } + + /** + * Configures min-, max, preferredSize properties of the renderingComponent. + * + * Here: set all to null. + * + * @param renderingComponent the component to configure, must not be null + * @param context the cell context to configure from, must not be null + */ + protected void configureSizes(T renderingComponent, CellContext context) { + renderingComponent.setPreferredSize(null); + renderingComponent.setMinimumSize(null); + renderingComponent.setMaximumSize(null); + } + + /** + * Configures colors of rendering component from the given cell context. + * + * @param renderingComponent the component to configure, must not be null + * @param context the cell context to configure from, must not be null + */ + protected void configureColors(T renderingComponent, CellContext context) { + if (context.isSelected()) { + renderingComponent.setForeground(context.getSelectionForeground()); + renderingComponent.setBackground(context.getSelectionBackground()); + } else { + renderingComponent.setForeground(getForeground(context)); + renderingComponent.setBackground(getBackground(context)); + } + if (context.isFocused()) { + configureFocusColors(renderingComponent, context); + } + } + /** + * Configures focus-related colors form given cell context.

      + * + * PENDING: move to context as well? - it's the only comp + * with focus specifics? Problem is the parameter type... + * + * @param renderingComponent the component to configure, must not be null + * @param context the cell context to configure from, must not be null + */ + protected void configureFocusColors(T renderingComponent, CellContext context) { + if (!context.isSelected() && context.isEditable()) { + Color col = context.getFocusForeground(); + if (col != null) { + renderingComponent.setForeground(col); + } + col = context.getFocusBackground(); + if (col != null) { + renderingComponent.setBackground(col); + } + } + } + + + /** + * Configures the rendering component's border from the given cell context.

      + * + * @param renderingComponent the component to configure, must not be null + * @param context the cell context to configure from, must not be null + */ + protected void configureBorder(T renderingComponent, CellContext context) { + renderingComponent.setBorder(context.getBorder()); + } + + /** + * Returns the unselected foreground to use for the rendering + * component.

      + * + * Here: returns this renderer's unselected foreground is not null, + * returns the foreground from the given context. In other words: + * the renderer's foreground takes precedence if set. + * + * @param context the cell context. + * @return the unselected foreground. + */ + protected Color getForeground(CellContext context) { + if (unselectedForeground != null) + return unselectedForeground; + return context.getForeground(); + } + + /** + * Returns the unselected background to use for the rendering + * component.

      + * + * Here: returns this renderer's unselected background is not null, + * returns the background from the given context. In other words: + * the renderer's background takes precedence if set. + * + * @param context the cell context. + * @return the unselected background. + */ + protected Color getBackground(CellContext context) { + if (unselectedBackground != null) + return unselectedBackground; + return context.getBackground(); + } + + + +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/renderer/FormatStringValue.java b/src/main/java/org/jdesktop/swingx/renderer/FormatStringValue.java new file mode 100644 index 0000000000..72ddc9bfde --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/renderer/FormatStringValue.java @@ -0,0 +1,93 @@ +/* + * $Id: FormatStringValue.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.renderer; + +import java.text.Format; + +/** + * Base type for Format-backed StringValue. Has + * static defaults for Date and Number which use the locale-dependent default + * Formats as returned from xxFormat.getInstance(). + *

      + * + * This class is intended to ease the handling of formatted cell content. + * F.i. to show a list of Dates in the default + * Locale's FULL version and right align the text: + * + *

      
      + *    StringValue stringValue = new FormatStringValue(
      + *        DateFormat.getInstance(DateFormat.FULL));
      + *    list.setCellRenderer(
      + *        new DefaultListRenderer(stringValue, JLabel.RIGHT);  
      + * 
      + * + * + * PENDING: need to update on Locale change? How to detect? When? + * + * @author Jeanette Winzenburg + */ +public class FormatStringValue implements StringValue { + + /** the format used in creating the String representation. */ + protected Format format; + + /** + * Instantiates a formatted converter with null format. + * + */ + public FormatStringValue() { + this(null); + } + + /** + * Instantiates a formatted converter with the given Format. + * + * @param format the format to use in creating the String representation. + */ + public FormatStringValue(Format format) { + this.format = format; + } + + /** + * + * @return the format used in creating the String representation. + */ + public Format getFormat() { + return format; + } + + /** + * {@inheritDoc} + */ + @Override + public String getString(Object value) { + if (value == null) return ""; + if (format != null) { + try { + return format.format(value); + } catch (IllegalArgumentException e) { + // didn't work, nothing we can do + } + } + return value.toString(); + } + +} diff --git a/src/main/java/org/jdesktop/swingx/renderer/HyperlinkProvider.java b/src/main/java/org/jdesktop/swingx/renderer/HyperlinkProvider.java new file mode 100644 index 0000000000..8742034f85 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/renderer/HyperlinkProvider.java @@ -0,0 +1,285 @@ +/* + * $Id: HyperlinkProvider.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.renderer; + +import org.jdesktop.swingx.JXHyperlink; +import org.jdesktop.swingx.hyperlink.AbstractHyperlinkAction; +import org.jdesktop.swingx.rollover.RolloverProducer; +import org.jdesktop.swingx.rollover.RolloverRenderer; + +import java.awt.*; +import java.awt.event.ActionEvent; + +/** + * Renderer for hyperlinks".

      + * + * The renderer is configured with a LinkAction. + * It's mostly up to the developer to guarantee that the all + * values which are passed into the getXXRendererComponent(...) are + * compatible with T: she can provide a runtime class to check against. + * If it isn't the renderer will configure the + * action with a null target.

      + * + * It's recommended to not use the given Action anywhere else in code, + * as it is updated on each getXXRendererComponent() call which might + * lead to undesirable side-effects.

      + * + * Internally uses JXHyperlink as rendering component.

      + * + * PENDING: can go from ButtonProvider?

      + * + * PENDING: make renderer respect selected cell state.

      + * + * PENDING: TreeCellRenderer has several issues

      + *

        + *
      1. no icons + *
      2. usual background highlighter issues + *
      + * + * @author Jeanette Winzenburg + */ +public class HyperlinkProvider + extends ComponentProvider implements + RolloverRenderer { + + + private AbstractHyperlinkAction linkAction; + protected Class targetClass; + + /** + * Instantiate a LinkRenderer with null LinkAction and null + * targetClass. + * + */ + public HyperlinkProvider() { + this(null, null); + } + + /** + * Instantiate a LinkRenderer with the LinkAction to use with + * target values. + * + * @param linkAction the action that acts on values. + */ + public HyperlinkProvider(AbstractHyperlinkAction linkAction) { + this(linkAction, null); + } + + /** + * Instantiate a LinkRenderer with a LinkAction to use with + * target values and the type of values the action can cope with.

      + * + * It's up to developers to take care of matching types. + * + * @param linkAction the action that acts on values. + * @param targetClass the type of values the action can handle. + */ + public HyperlinkProvider(AbstractHyperlinkAction linkAction, Class targetClass) { + super(); +// rendererComponent.addActionListener(createEditorActionListener()); + setLinkAction(linkAction, targetClass); + } + + /** + * Sets the class the action is supposed to handle.

      + * + * PENDING: make sense to set independently of LinkAction? + * + * @param targetClass the type of values the action can handle. + */ + public void setTargetClass(Class targetClass) { + this.targetClass = targetClass; + } + + /** + * Sets the LinkAction for handling the values.

      + * + * The action is assumed to be able to cope with any type, that is + * this method is equivalent to setLinkAction(linkAction, null). + * + * @param linkAction + */ + public void setLinkAction(AbstractHyperlinkAction linkAction) { + setLinkAction(linkAction, null); + } + + /** + * Sets the LinkAction for handling the values and the + * class the action can handle.

      + * + * PENDING: in the general case this is not independent of the + * targetClass. Need api to set them combined? + * + * @param linkAction + */ + public void setLinkAction(AbstractHyperlinkAction linkAction, Class targetClass) { + if (linkAction == null) { + linkAction = createDefaultLinkAction(); + } + setTargetClass(targetClass); + this.linkAction = linkAction; + rendererComponent.setAction(linkAction); + + } + /** + * decides if the given target is acceptable for setTarget. + *

      + * + * target == null is acceptable for all types. + * targetClass == null is the same as Object.class + * + * @param target the target to set. + * @return true if setTarget can cope with the object, + * false otherwise. + * + */ + public boolean isTargetable(Object target) { + // we accept everything + if (targetClass == null) return true; + if (target == null) return true; + return targetClass.isAssignableFrom(target.getClass()); + } + + + + /** + * default action - does nothing... except showing the target. + * + * @return a default LinkAction for showing the target. + */ + protected AbstractHyperlinkAction createDefaultLinkAction() { + return new AbstractHyperlinkAction(null) { + + @Override + public void actionPerformed(ActionEvent e) { + // TODO Auto-generated method stub + + } + + }; + } + +//----------------------- Implement RolloverRenderer + + @Override + public boolean isEnabled() { + return true; + } + + @Override + public void doClick() { + rendererComponent.doClick(); + } + +//------------------------ ComponentProvider + + /** + * {@inheritDoc}

      + * + * PENDING JW: Needs to be overridden - doesn't comply to contract!. Not sure + * how to do it without disturbing the hyperlinks current setting? + * All hyperlink properties are defined by the LinkAction configured + * with the target ... + */ + @Override + public String getString(Object value) { + if (isTargetable(value)) { + Object oldTarget = linkAction.getTarget(); + linkAction.setTarget(value); + String text = linkAction.getName(); + linkAction.setTarget(oldTarget); + return text; + } + return super.getString(value); + } + + + + /** + * {@inheritDoc}

      + * + * Overridden to set the hyperlink's rollover state. + */ + @Override + protected void configureState(CellContext context) { +// rendererComponent.setHorizontalAlignment(getHorizontalAlignment()); + if (context.getComponent() != null) { + Point p = (Point) context.getComponent() + .getClientProperty(RolloverProducer.ROLLOVER_KEY); + if (/*hasFocus || */(p != null && (p.x >= 0) && + (p.x == context.getColumn()) && (p.y == context.getRow()))) { + if (!rendererComponent.getModel().isRollover()) + rendererComponent.getModel().setRollover(true); + } else { + if (rendererComponent.getModel().isRollover()) + rendererComponent.getModel().setRollover(false); + } + } + } + + /** + * {@inheritDoc} + * + * Overridden to set the LinkAction's target to the context's value, if + * targetable.

      + * + * Forces foreground color to the one defined by hyperlink for unselected + * cells, doesn't change the foreground for selected (as darkish text on dark selection + * background might be unreadable, Issue #840-swingx). Not entirely safe because + * the unselected background might be dark as well. Need to find a better way in + * the long run. Until then, client code can use Highlighters to repair + * (which is nasty!).

      + * + * PENDING JW: by-passes XXValues - state currently is completely defined by + * the action. Hmm ... + * + */ + @Override + protected void format(CellContext context) { + Object value = context.getValue(); + if (isTargetable(value)) { + linkAction.setTarget(value); + } else { + linkAction.setTarget(null); + } + // hmm... the hyperlink should do this automatically.. + // Issue #840-swingx: hyperlink unreadable if selected (for dark selection colors) + // so we only force clicked/unclicked if unselected + if (!context.isSelected()) { + rendererComponent.setForeground(linkAction.isVisited() ? + rendererComponent.getClickedColor() : rendererComponent.getUnclickedColor()); + } else { + // JW: workaround #845-swingx which was introduced by fixing #840 + // if we interfere with the colors, need to do always. Not quite understood + rendererComponent.setForeground(context.getSelectionForeground()); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected JXHyperlink createRendererComponent() { + return new JXRendererHyperlink(); + } + + +} diff --git a/src/main/java/org/jdesktop/swingx/renderer/IconAware.java b/src/main/java/org/jdesktop/swingx/renderer/IconAware.java new file mode 100644 index 0000000000..a4be33152d --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/renderer/IconAware.java @@ -0,0 +1,33 @@ +/* + * Created on 05.11.2010 + * + */ +package org.jdesktop.swingx.renderer; + +import javax.swing.*; + +/** + * Interface for tagging rendering components to allow Highlighters to treat + * the Icon (Issue #1311-swingx) as a visual decoration. ComponentProviders + * which hand out IconAware rendering components must guarantee to reset its + * Icon property in each configuration round. + * + * @author Jeanette Winzenburg, Berlin + */ +public interface IconAware { + + /** + * Sets the icon property. + * + * @param icon + */ + public void setIcon(Icon icon); + + /** + * Returns the icon property. + * + * @return + */ + public Icon getIcon(); + +} diff --git a/src/main/java/org/jdesktop/swingx/renderer/IconValue.java b/src/main/java/org/jdesktop/swingx/renderer/IconValue.java new file mode 100644 index 0000000000..80cf05abe6 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/renderer/IconValue.java @@ -0,0 +1,80 @@ +/* + * $Id: IconValue.java 3298 2009-03-11 13:51:25Z kleopatra $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.renderer; + +import org.jdesktop.swingx.icon.EmptyIcon; + +import javax.swing.*; +import java.io.Serializable; + +/** + * A simple converter to return a Icon representation of an Object. + *

      + * + * This class is intended to be the "small coin" to configure/format icon cell + * content of concrete subclasses of ComponentProvider. + *

      + * + * + * NOTE: this is experimental, most probably will change. A (near) future + * version with change the signature of the getIcon method to + * + *

      
      + * Icon getIcon(Object value, IconType type);
      + * 
      + * + * That will allow a more fine-grained control of custom icons in tree rendering. + * + * @author Jeanette Winzenburg + */ +public interface IconValue extends Serializable { + + /** + * The cell type the icon is used for. + */ + public enum IconType { + + LEAF, + + OPEN_FOLDER, + + CLOSED_FOLDER + + } + + /** + * A marker icon used to indicate a null. + * + */ + public final static Icon NULL_ICON = new EmptyIcon(); + + + /** + * Returns a icon representation of the given value. + * + * @param value the object to present as Icon + * @return a Icon representation of the given value, + * may be null if none available. + * + */ + Icon getIcon(Object value); + +} diff --git a/src/main/java/org/jdesktop/swingx/renderer/IconValues.java b/src/main/java/org/jdesktop/swingx/renderer/IconValues.java new file mode 100644 index 0000000000..359c5fa423 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/renderer/IconValues.java @@ -0,0 +1,85 @@ +/* + * $Id: IconValues.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.renderer; + +import javax.swing.*; +import javax.swing.filechooser.FileSystemView; +import java.io.File; + +/** + * A collection of common {@code IconValue} implementations. + * + * @author Karl George Schaefer + * @author Jeanette Winzenburg + */ +public final class IconValues { + /** + * Always NULL_ICON. This is useful to indicate that we really want + * no icon instead of f.i. a default provided by the CellContext. + */ + @SuppressWarnings("serial") + public static final IconValue NONE = new IconValue() { + + @Override + public Icon getIcon(Object value) { + return IconValue.NULL_ICON; + } + + }; + + /** + * Returns the value as Icon if possible or null. + */ + @SuppressWarnings("serial") + public static final IconValue ICON = new IconValue() { + + @Override + public Icon getIcon(Object value) { + if (value instanceof Icon) { + return (Icon) value; + } + return null; + } + }; + + /** + * An {@code IconValue} that presents the current L&F icon for a given file. + * If the value passed to {@code FILE_ICON} is not a {@link File}, this has + * the same effect as {@link IconValues#NONE}. + */ + @SuppressWarnings("serial") + public static final IconValue FILE_ICON = new IconValue() { + @Override + public Icon getIcon(Object value) { + if (value instanceof File) { + FileSystemView fsv = FileSystemView.getFileSystemView(); + + return fsv.getSystemIcon((File) value); + } + + return IconValues.NONE.getIcon(value); + } + }; + + private IconValues() { + // does nothing + } +} diff --git a/src/main/java/org/jdesktop/swingx/renderer/JRendererCheckBox.java b/src/main/java/org/jdesktop/swingx/renderer/JRendererCheckBox.java new file mode 100644 index 0000000000..1f89360fb1 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/renderer/JRendererCheckBox.java @@ -0,0 +1,311 @@ +/* + * $Id: JRendererCheckBox.java 4282 2013-02-22 11:55:40Z Kleopatra $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.renderer; + +import org.jdesktop.swingx.painter.Painter; + +import javax.swing.*; +import java.awt.*; + +/** + * A JCheckBox optimized for usage in renderers and + * with a minimal background painter support.

      + * + * Note: As of revision #4223, there's a complete overhaul (aka: changed the tricksery) to + * fix Issue swingx-1513 (allow client code to set renderer transparent) while keeping + * fix Issue swingx-897 (striping/background lost when painter installed) + *

      + * + * Note: The change of logic _did_ introduce a regression (swingx-1546) + * which was fixed by forcing the box's opacity to true (for regression release + * 1.6.5-1). Further improvements (like f.i. the option to delegate to the ui's + * update - to allow LAF installed painters - instead of paint) are deferred + * to a later normal release, more discussions needed. + *

      + * + * @author Jeanette Winzenburg + * + * @see #paintComponent(Graphics) + */ +@SuppressWarnings({ "rawtypes", "unchecked" }) +public class JRendererCheckBox extends JCheckBox implements PainterAware { + /** the swingx painter */ + protected Painter painter; + /** a flag to prevent ui painting from filling the background. */ + private boolean fakeTransparency; + + /** + * Instantiates a JRendererCheckBox with opacity true. + */ + public JRendererCheckBox() { + super(); + // fix # 1546-swingx: striping lost in synth-based lafs + // forcing opaque to enable painting the background + setOpaque(true); + } + /** + * {@inheritDoc} + */ + @Override + public Painter getPainter() { + return painter; + } + + /** + * {@inheritDoc} + */ + @Override + public void setPainter(Painter painter) { + Painter old = getPainter(); + this.painter = painter; + firePropertyChange("painter", old, getPainter()); + } + + /** + * {@inheritDoc}

      + * + * Overridden to return false if painting flag is true.

      + * + */ + @Override + public boolean isOpaque() { + if (fakeTransparency) { + return false; + } + return super.isOpaque(); + } + + /** + * {@inheritDoc}

      + * + * Overridden to return false if painting flag is true.

      + * + */ + @Override + public boolean isContentAreaFilled() { + if (fakeTransparency) { + return false; + } + return super.isContentAreaFilled(); + } + + /** + * {@inheritDoc}

      + * + * Overridden to not automatically de/register itself from/to the ToolTipManager. + * As rendering component it is not considered to be active in any way, so the + * manager must not listen. + */ + @Override + public void setToolTipText(String text) { + putClientProperty(TOOL_TIP_TEXT_KEY, text); + } + + /** + * Overridden to snatch painting from super if a painter installed or Nimbus + * detected.

      + * + * The overall logic currently (since 1.6.5) is to simply call super without SwingX + * painter. Otherwise, that is with SwingX painter: + *

        + *
      1. if opaque + *
          + *
        1. set a flag which fakes transparency, that is both + * contentAreaFilled and + * opaque return false + *
        2. fill background with the component's background color + *
        3. apply swingx painter + *
        4. hook into ui.paint(...) + *
        5. reset the flag + *
        + *
      2. else + *
          apply swingx painter + *
            call super + *
          1. + *
              + *
            + * + * Note that Nimbus is special cased (mainly due to its bug of + * even row striping instead of odd) + * and handled as if a SwingX painter were set. + * + */ + @Override + protected void paintComponent(Graphics g) { + // JW: hack around for #1178-swingx (core issue) + // grab painting if Nimbus detected + if ((painter != null) || isNimbus()) { + // we have a custom (background) painter + // try to inject if possible + // there's no guarantee - some LFs have their own background + // handling elsewhere + if (isOpaque()) { + // replace the paintComponent completely + fakeTransparency = true; + paintComponentWithPainter((Graphics2D) g); + fakeTransparency = false; + } else { + // transparent apply the background painter before calling super + paintPainter(g); + super.paintComponent(g); + } + } else { + // nothing to worry about - delegate to super + super.paintComponent(g); + } + } + + /** + * Hack around Nimbus not respecting background colors if UIResource. + * So by-pass ... + * + * @return + */ + private boolean isNimbus() { + return UIManager.getLookAndFeel().getName().contains("Nimbus"); + } + + + /** + * + * Hack around AbstractPainter.paint bug which disposes the Graphics. + * So here we give it a scratch to paint on.

            + * TODO - remove again, the issue is fixed? + * + * @param g the graphics to paint on + */ + private void paintPainter(Graphics g) { + if (painter == null) return; + // fail fast: we assume that g must not be null + // which throws an NPE here instead deeper down the bowels + // this differs from corresponding core implementation! + Graphics2D scratch = (Graphics2D) g.create(); + try { + painter.paint(scratch, this, getWidth(), getHeight()); + } + finally { + scratch.dispose(); + } + } + + /** + * + * @param g + */ + protected void paintComponentWithPainter(Graphics2D g) { + // 1. be sure to fill the background + // 2. paint the painter + // by-pass ui.update and hook into ui.paint directly + if (ui != null) { + // fail fast: we assume that g must not be null + // which throws an NPE here instead deeper down the bowels + // this differs from corresponding core implementation! + Graphics scratchGraphics = g.create(); + try { + scratchGraphics.setColor(getBackground()); + scratchGraphics.fillRect(0, 0, getWidth(), getHeight()); + paintPainter(g); + ui.paint(scratchGraphics, this); +// super.paintComponent(g); + } finally { + scratchGraphics.dispose(); + } + } + + } + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + * + * @since 1.5 + */ + @Override + public void invalidate() {} + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + */ + @Override + public void validate() {} + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + */ + @Override + public void revalidate() {} + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + */ + @Override + public void repaint(long tm, int x, int y, int width, int height) {} + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + */ + @Override + public void repaint(Rectangle r) { } + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + * + * @since 1.5 + */ + @Override + public void repaint() { + } + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + */ + @Override + protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { + // Strings get interned... + if ("text".equals(propertyName)) { + super.firePropertyChange(propertyName, oldValue, newValue); + } + } + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + */ + @Override + public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { } + + + +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/renderer/JRendererLabel.java b/src/main/java/org/jdesktop/swingx/renderer/JRendererLabel.java new file mode 100644 index 0000000000..ea409602c8 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/renderer/JRendererLabel.java @@ -0,0 +1,299 @@ +/* + * $Id: JRendererLabel.java 4222 2012-08-07 10:23:08Z kleopatra $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.renderer; + +import org.jdesktop.swingx.painter.Painter; + +import javax.swing.*; +import java.awt.*; + +/** + * A JLabel optimized for usage in renderers and + * with a minimal background painter support.

            + * + * Note: the painter support will be switched to painter_work as + * soon it enters main. + * + * The reasoning for the performance-overrides is copied from core:

            + * + * The standard JLabel component was not + * designed to be used this way and we want to avoid + * triggering a revalidate each time the + * cell is drawn. This would greatly decrease performance because the + * revalidate message would be + * passed up the hierarchy of the container to determine whether any other + * components would be affected. + * As the renderer is only parented for the lifetime of a painting operation + * we similarly want to avoid the overhead associated with walking the + * hierarchy for painting operations. + * So this class + * overrides the validate, invalidate, + * revalidate, repaint, and + * firePropertyChange methods to be + * no-ops and override the isOpaque method solely to improve + * performance. If you write your own renderer component, + * please keep this performance consideration in mind. + *

            + * + * @author Jeanette Winzenburg + */ +public class JRendererLabel extends JLabel implements PainterAware, IconAware { + + protected Painter painter; + + /** + * + */ + public JRendererLabel() { + super(); + setOpaque(true); + } + +// /** +// * Overridden for performance reasons.

            +// * PENDING: Think about Painters and opaqueness? +// * +// */ +// @Override +// public boolean isOpaque() { +// Color back = getBackground(); +// Component p = getParent(); +// if (p != null) { +// p = p.getParent(); +// } +// // p should now be the JTable. +// boolean colorMatch = (back != null) && (p != null) && +// back.equals(p.getBackground()) && +// p.isOpaque(); +// return !colorMatch && super.isOpaque(); +// // PENDING JW: Issue #1188-swingx: problems with background in Synth +// // basically a core issue - nevertheless, evaluate implications of +// // a simple straight-forward implemenation - return the property +// // no tricks +//// return super.isOpaque(); +// } + + /** + * {@inheritDoc} + */ + public void setPainter(Painter painter) { + Painter old = getPainter(); + this.painter = painter; + firePropertyChange("painter", old, getPainter()); + } + + /** + * {@inheritDoc} + */ + public Painter getPainter() { + return painter; + } + /** + * {@inheritDoc}

            + * + * Overridden to inject Painter's painting.

            + * TODO: cleanup logic - see JRendererCheckBox. + * + */ + @Override + protected void paintComponent(Graphics g) { + // JW: hack around for #1178-swingx (core issue) + // grab painting if Nimbus detected + if ((painter != null) || isNimbus()) { + // we have a custom (background) painter + // try to inject if possible + // there's no guarantee - some LFs have their own background + // handling elsewhere + if (isOpaque()) { + // replace the paintComponent completely + paintComponentWithPainter((Graphics2D) g); + } else { + // transparent apply the background painter before calling super + paintPainter(g); + super.paintComponent(g); + } + } else { + // nothing to worry about - delegate to super + super.paintComponent(g); + } + } + + /** + * Hack around Nimbus not respecting background colors if UIResource. + * So by-pass ... + * + * @return + */ + private boolean isNimbus() { + return UIManager.getLookAndFeel().getName().contains("Nimbus"); + } + + + /** + * + * Hack around AbstractPainter.paint bug which disposes the Graphics. + * So here we give it a scratch to paint on.

            + * TODO - remove again, the issue is fixed? + * + * @param g the graphics to paint on + */ + private void paintPainter(Graphics g) { + if (painter == null) return; + // fail fast: we assume that g must not be null + // which throws an NPE here instead deeper down the bowels + // this differs from corresponding core implementation! + Graphics2D scratch = (Graphics2D) g.create(); + try { + painter.paint(scratch, this, getWidth(), getHeight()); + } + finally { + scratch.dispose(); + } + } + +// public void setStrictWidth(boolean strict) { +// this.strict = strict; +// } +// +// @Override +// public Dimension getMaximumSize() { +// if (strict) { +// return super.getMaximumSize(); +// } +// Dimension max = super.getMaximumSize(); +// max.width = Integer.MAX_VALUE - 1; +// return max; +// } + + /** + * PRE: painter != null, isOpaque() + * @param g + */ + protected void paintComponentWithPainter(Graphics2D g) { + // 1. be sure to fill the background + // 2. paint the painter + // by-pass ui.update and hook into ui.paint directly + if (ui != null) { + // fail fast: we assume that g must not be null + // which throws an NPE here instead deeper down the bowels + // this differs from corresponding core implementation! + Graphics2D scratchGraphics = (Graphics2D) g.create(); + try { + scratchGraphics.setColor(getBackground()); + scratchGraphics.fillRect(0, 0, getWidth(), getHeight()); + paintPainter(g); + ui.paint(scratchGraphics, this); + } + finally { + scratchGraphics.dispose(); + } + } + } + + + /** + * {@inheritDoc}

            + * + * Overridden to not automatically de/register itself from/to the ToolTipManager. + * As rendering component it is not considered to be active in any way, so the + * manager must not listen. + */ + @Override + public void setToolTipText(String text) { + putClientProperty(TOOL_TIP_TEXT_KEY, text); + } + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + * + * @since 1.5 + */ + @Override + public void invalidate() {} + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + */ + @Override + public void validate() {} + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + */ + @Override + public void revalidate() {} + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + */ + @Override + public void repaint(long tm, int x, int y, int width, int height) {} + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + */ + @Override + public void repaint(Rectangle r) { } + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + * + * @since 1.5 + */ + @Override + public void repaint() { + } + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + */ + @Override + protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { + // Strings get interned... + if ("text".equals(propertyName)) { + super.firePropertyChange(propertyName, oldValue, newValue); + } + } + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + */ + @Override + public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { } + + +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/renderer/JRendererPanel.java b/src/main/java/org/jdesktop/swingx/renderer/JRendererPanel.java new file mode 100644 index 0000000000..0107ea8f11 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/renderer/JRendererPanel.java @@ -0,0 +1,85 @@ +package org.jdesktop.swingx.renderer; + +import javax.swing.*; +import java.awt.*; + +/** + * An implementation of JPanel used for rendering. It overrides methods for performance reasons. + * + * @author kschaefer + */ +public class JRendererPanel extends JPanel { + public JRendererPanel() { + super(); + } + + /** + * @param layout + */ + public JRendererPanel(LayoutManager layout) { + super(layout); + } + + /** + * {@inheritDoc}

            + * + * Overridden to not automatically de/register itself from/to the ToolTipManager. + * As rendering component it is not considered to be active in any way, so the + * manager must not listen. + */ + @Override + public void setToolTipText(String text) { + putClientProperty(TOOL_TIP_TEXT_KEY, text); + } + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + */ + @Override + public void revalidate() {} + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + */ + @Override + public void repaint(long tm, int x, int y, int width, int height) {} + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + */ + @Override + public void repaint(Rectangle r) { } + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + * + * @since 1.5 + */ + @Override + public void repaint() { + } + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + */ + @Override + protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { } + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + */ + @Override + public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { } +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/renderer/JXRendererHyperlink.java b/src/main/java/org/jdesktop/swingx/renderer/JXRendererHyperlink.java new file mode 100644 index 0000000000..b018f07819 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/renderer/JXRendererHyperlink.java @@ -0,0 +1,214 @@ +/* + * $Id: JXRendererHyperlink.java 3235 2009-02-01 15:01:07Z rah003 $ + * + * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.renderer; + +import org.jdesktop.swingx.JXHyperlink; +import org.jdesktop.swingx.painter.Painter; + +import java.awt.*; + +/** + * A JXHyperlink optimized for usage in renderers and + * with a minimal background painter support.

            + * + * Note: the painter support will be switched to painter_work as + * soon it enters main. + * + * @author Jeanette Winzenburg + */ +public class JXRendererHyperlink extends JXHyperlink implements PainterAware { + protected Painter painter; + + /** + * {@inheritDoc} + */ + public void setPainter(Painter painter) { + Painter old = getPainter(); + this.painter = painter; + if (painter != null) { + // ui maps to !opaque + // Note: this is incomplete - need to keep track of the + // "real" contentfilled property + setContentAreaFilled(false); + } + firePropertyChange("painter", old, getPainter()); + } + + /** + * {@inheritDoc} + */ + public Painter getPainter() { + return painter; + } + + @Override + protected void paintComponent(Graphics g) { + if (painter != null) { + // we have a custom (background) painter + // try to inject if possible + // there's no guarantee - some LFs have their own background + // handling elsewhere + paintComponentWithPainter((Graphics2D) g); + } else { + // no painter - delegate to super + super.paintComponent(g); + } + } + + /** + * + * Hack around AbstractPainter.paint bug which disposes the Graphics. + * So here we give it a scratch to paint on.

            + * TODO - remove again, the issue is fixed? + * + * @param g the graphics to paint on + */ + private void paintPainter(Graphics g) { + // fail fast: we assume that g must not be null + // which throws an NPE here instead deeper down the bowels + // this differs from corresponding core implementation! + Graphics2D scratch = (Graphics2D) g.create(); + try { + painter.paint(scratch, this, getWidth(), getHeight()); + } + finally { + scratch.dispose(); + } + } + + /** + * PRE: painter != null + * @param g + */ + protected void paintComponentWithPainter(Graphics2D g) { + // 1. be sure to fill the background + // 2. paint the painter + // by-pass ui.update and hook into ui.paint directly + if (ui != null) { + // fail fast: we assume that g must not be null + // which throws an NPE here instead deeper down the bowels + // this differs from corresponding core implementation! + Graphics scratchGraphics = g.create(); + try { + scratchGraphics.setColor(getBackground()); + scratchGraphics.fillRect(0, 0, getWidth(), getHeight()); + paintPainter(g); + ui.paint(scratchGraphics, this); + } finally { + scratchGraphics.dispose(); + } + } + + } + + @Override + public void updateUI() { + super.updateUI(); + setBorderPainted(true); + setOpaque(true); + } + /** + * {@inheritDoc}

            + * + * Overridden to not automatically de/register itself from/to the ToolTipManager. + * As rendering component it is not considered to be active in any way, so the + * manager must not listen. + */ + @Override + public void setToolTipText(String text) { + putClientProperty(TOOL_TIP_TEXT_KEY, text); + } + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + * + * @since 1.5 + */ + @Override + public void invalidate() {} + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + */ + @Override + public void validate() {} + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + */ + @Override + public void revalidate() {} + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + */ + @Override + public void repaint(long tm, int x, int y, int width, int height) {} + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + */ + @Override + public void repaint(Rectangle r) { } + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + * + * @since 1.5 + */ + @Override + public void repaint() { + } + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + */ + @Override + protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { + // Strings get interned... + if ("text".equals(propertyName)) { + super.firePropertyChange(propertyName, oldValue, newValue); + } + } + + /** + * Overridden for performance reasons. + * See the Implementation Note + * for more information. + */ + @Override + public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { } + +} diff --git a/src/main/java/org/jdesktop/swingx/renderer/LabelProvider.java b/src/main/java/org/jdesktop/swingx/renderer/LabelProvider.java new file mode 100644 index 0000000000..f84c019f64 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/renderer/LabelProvider.java @@ -0,0 +1,121 @@ +/* + * $Id: LabelProvider.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.renderer; + +import javax.swing.*; + +/** + * A component provider which uses a JLabel as rendering + * component.

            + * + * It configures the Label's text and icon property from the + * StringValue. + * + * @author Jeanette Winzenburg + * + * @see StringValue + * @see FormatStringValue + * @see IconValue + */ +public class LabelProvider extends ComponentProvider { + + /** + * Instantiates a LabelProvider with default to-String converter and LEADING + * horizontal alignment . + *

            + * + */ + public LabelProvider() { + this(null); + } + + /** + * Instantiates a LabelProvider with the given to-String converter and LEADING + * horizontal alignment. If the converter is null, the default TO_STRING is + * used. + *

            + * + * @param converter the converter to use for mapping the cell value to a + * String representation. + */ + public LabelProvider(StringValue converter) { + this(converter, JLabel.LEADING); + } + + /** + * Instantiates a LabelProvider with default to-String converter and given + * horizontal alignment. + * + * @param alignment the horizontal alignment. + */ + public LabelProvider(int alignment) { + this(null, alignment); + } + + /** + * Instantiates a LabelProvider with given to-String converter and given + * horizontal alignment. If the converter is null, the default TO_STRING is + * used. + * + * @param converter the converter to use for mapping the cell value to a + * String representation. + * @param alignment the horizontal alignment. + */ + public LabelProvider(StringValue converter, int alignment) { + super(converter, alignment); + } + + /** + * {@inheritDoc} + */ + @Override + protected JLabel createRendererComponent() { + return new JRendererLabel(); + } + + /** + * {@inheritDoc} + * Here: sets the Label's horizontal alignment to the alignment as configured + * in the controller. + */ + @Override + protected void configureState(CellContext context) { + rendererComponent.setHorizontalAlignment(getHorizontalAlignment()); + } + + /** + * {@inheritDoc} + * Here: sets the labels's text and icon property to the value as + * returned by getValueAsString/Icon, respectively. + * + * @param context the cellContext to use + * + * @see #getValueAsString(CellContext) + * @see #getValueAsIcon(CellContext) + */ + @Override + protected void format(CellContext context) { + rendererComponent.setIcon(getValueAsIcon(context)); + rendererComponent.setText(getValueAsString(context)); + } + + +} diff --git a/src/main/java/org/jdesktop/swingx/renderer/ListCellContext.java b/src/main/java/org/jdesktop/swingx/renderer/ListCellContext.java new file mode 100644 index 0000000000..30442cdfa3 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/renderer/ListCellContext.java @@ -0,0 +1,110 @@ +/* + * $Id: ListCellContext.java 3424 2009-07-30 10:53:39Z kleopatra $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.renderer; + +import javax.swing.*; +import java.awt.*; + +/** + * List specific CellContext. + */ +public class ListCellContext extends CellContext { + + /** + * Sets state of the cell's context. Note that the component might be null + * to indicate a cell without a concrete context. All accessors must cope + * with. + * + * @param component the component the cell resides on, might be null + * @param value the content value of the cell + * @param row the cell's row index in view coordinates + * @param column the cell's column index in view coordinates + * @param selected the cell's selected state + * @param focused the cell's focused state + * @param expanded the cell's expanded state + * @param leaf the cell's leaf state + */ + public void installContext(JList component, Object value, int row, int column, + boolean selected, boolean focused, boolean expanded, boolean leaf) { + this.component = component; + installState(value, row, column, selected, focused, expanded, leaf); + this.dropOn = checkDropOnState(); + } + + /** + * + */ + private boolean checkDropOnState() { + if ((getComponent() == null)) { + return false; + } + JList.DropLocation dropLocation = getComponent().getDropLocation(); + if (dropLocation != null + && !dropLocation.isInsert() + && dropLocation.getIndex() == row) { + return true; + } + return false; + } + + + @Override + public JList getComponent() { + return (JList) super.getComponent(); + } + + /** + * {@inheritDoc} + */ + @Override + protected Color getSelectionBackground() { + Color selection = null; + if (isDropOn()) { + selection = getDropCellBackground(); + if (selection != null) return selection; + } + return getComponent() != null ? getComponent().getSelectionBackground() : null; + } + + /** + * {@inheritDoc} + */ + @Override + protected Color getSelectionForeground() { + Color selection = null; + if (isDropOn()) { + selection = getDropCellForeground(); + if (selection != null) return selection; + } + return getComponent() != null ? getComponent().getSelectionForeground() : null; + } + + /** + * {@inheritDoc} + */ + @Override + protected String getUIPrefix() { + return "List."; + } + + + +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/renderer/LocalizableStringValue.java b/src/main/java/org/jdesktop/swingx/renderer/LocalizableStringValue.java new file mode 100644 index 0000000000..ec849b84a6 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/renderer/LocalizableStringValue.java @@ -0,0 +1,119 @@ +package org.jdesktop.swingx.renderer; + +import org.jdesktop.swingx.plaf.UIManagerExt; +import org.jdesktop.swingx.util.Contract; + +import java.util.Locale; +import java.util.Map; + +/** + * A StringValue which looks up localized String representations for objects. + */ +public class LocalizableStringValue implements StringValue { + + private Map lookup; + + private Locale locale; + + private String prefix; + + /** + * Instantiates a LocaleStringValue which looks up localized String + * representations for items in the map using the JComponent defaultLocale. + * + * @param lookup a map containing Entries of objects and a string key to + * look up its string representation in the UIManager + */ + public LocalizableStringValue(Map lookup) { + this(lookup, null, null); + } + + /** + * Instantiates a LocaleStringValue which looks up localized String + * representations for items in the map using the given Locale. + * + * @param lookup a map containing Entries of objects and a string key to + * look up its string representation in the UIManager + * @param locale the locale to lookup the localized strings, may be null to + * denote using JComponent.defaultLocale + */ + public LocalizableStringValue(Map lookup, Locale locale) { + this(lookup, null, locale); + } + + /** + * Instantiates a LocaleStringValue which looks up localized String + * representations for items in the map using the JComponent defaultLocale. + * + * @param lookup a map containing Entries of objects and a string key to + * look up its string representation in the UIManager + * @param prefix a common prefix for all string keys in the map, may be null + * to denote that the keys should be use as are + */ + public LocalizableStringValue(Map lookup, String prefix) { + this(lookup, prefix, null); + } + + /** + * Instantiates a LocaleStringValue which looks up localized String + * representations for items in the map using the given Locale. + * + * @param lookup a map containing Entries of objects and a string key to + * look up its string representation in the UIManager + * @param prefix a common prefix for all string keys in the map, may be null + * to denote that the keys should be use as are + * @param locale the locale to lookup the localized strings, may be null to + * denote using JComponent.defaultLocale + */ + public LocalizableStringValue(Map lookup, String prefix, + Locale locale) { + this.lookup = Contract.asNotNull(lookup, "map must not be null"); + this.prefix = prefix; + setLocale(locale); + } + + /** + * + * @inherited

            + * + * Implemented to lookup the value's localized string + * representation, if contained in the lookup map. Returns + * toString if not. + * + */ + @Override + public String getString(Object value) { + String key = lookup.get(value); + if (key != null) { + if (prefix != null) { + key = prefix + key; + } + String text = UIManagerExt.getString(key, getLocale()); + if (text != null) + return text; + } + return StringValues.TO_STRING_UI.getString(value); + } + + // -------------------- implement Localizable + + /** + * Sets the Locale to use for lookup of localized string representation. + * + * @param locale the locale to lookup the localized strings, may be null to + * denote using Locale's default. + */ + public final void setLocale(Locale locale) { + this.locale = locale; + } + + /** + * Returns the Locale to use for lookup, guaranteed to be not null. If + * the initial setting had been null, returns current Locale's default. + * + * @return the Locale used for lookup. + */ + public Locale getLocale() { + return locale != null ? locale : Locale.getDefault(); + } +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/renderer/MappedValue.java b/src/main/java/org/jdesktop/swingx/renderer/MappedValue.java new file mode 100644 index 0000000000..8287decb71 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/renderer/MappedValue.java @@ -0,0 +1,98 @@ +/* + * $Id: MappedValue.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.renderer; + +import javax.swing.*; + +/** + * Compound implementation of XXValue. Currently, XX stands for String, + * Icon, Boolean.

            + * + * Quick hack around #590-swingx: LabelProvider should respect StringValue + * when formatting (instead of going clever with icons). + * + * Note: this will change! + * + * @see CheckBoxProvider + */ +public class MappedValue implements StringValue, IconValue, BooleanValue { + + private StringValue stringDelegate; + private IconValue iconDelegate; + private BooleanValue booleanDelegate; + + public MappedValue(StringValue stringDelegate, IconValue iconDelegate) { + this(stringDelegate, iconDelegate, null); + } + + public MappedValue(StringValue stringDelegate, IconValue iconDelegate, + BooleanValue booleanDelegate) { + this.stringDelegate = stringDelegate; + this.iconDelegate = iconDelegate; + this.booleanDelegate = booleanDelegate; + } + + /** + * {@inheritDoc}

            + * + * This implementation delegates to the contained StringValue if available or + * returns an empty String, if not. + * + */ + @Override + public String getString(Object value) { + if (stringDelegate != null) { + return stringDelegate.getString(value); + } + return ""; + } + + /** + * {@inheritDoc}

            + * + * This implementation delegates to the contained IconValue if available or + * returns null, if not. + * + */ + @Override + public Icon getIcon(Object value) { + if (iconDelegate != null) { + return iconDelegate.getIcon(value); + } + return null; + } + + /** + * {@inheritDoc}

            + * + * This implementation delegates to the contained BooleanValue if available or + * returns false, if not. + * + */ + @Override + public boolean getBoolean(Object value) { + if (booleanDelegate != null) { + return booleanDelegate.getBoolean(value); + } + return false; + } + +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/renderer/MappedValues.java b/src/main/java/org/jdesktop/swingx/renderer/MappedValues.java new file mode 100644 index 0000000000..659edd608c --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/renderer/MappedValues.java @@ -0,0 +1,82 @@ +/* + * $Id: MappedValues.java 3882 2010-11-05 09:24:37Z kleopatra $ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.renderer; + +import javax.swing.*; +import javax.swing.plaf.UIResource; + +/** + * A collection of common {@code MappedValue} implementations. + * + * @author kschaefer + */ +public final class MappedValues { + /** + * A {@code MappedValue} that returns either a {@code String} or {@code Icon}, but not both. + */ + @SuppressWarnings("serial") + public static final MappedValue STRING_OR_ICON_ONLY = new MappedValue(new StringValue() { + @Override + public String getString(Object value) { + if (value instanceof Icon) { + return StringValues.EMPTY.getString(value); + } + + return StringValues.TO_STRING.getString(value); + } + }, IconValues.ICON); + + /** + * MappedValue wrapper of type UIResource to tag LAF installed converters. + * + * @author (Jeanette Winzenburg, Berlin + */ + public static class MappedValueUIResource extends MappedValue implements UIResource { + + public MappedValueUIResource(MappedValue delegate) { + this(delegate, delegate, delegate); + } + + /** + * @param stringDelegate + * @param iconDelegate + * @param booleanDelegate + */ + public MappedValueUIResource(StringValue stringDelegate, + IconValue iconDelegate, BooleanValue booleanDelegate) { + super(stringDelegate, iconDelegate, booleanDelegate); + } + + /** + * @param stringDelegate + * @param iconDelegate + */ + public MappedValueUIResource(StringValue stringDelegate, + IconValue iconDelegate) { + super(stringDelegate, iconDelegate); + } + + } + + private MappedValues() { + //prevent instantiation + } +} diff --git a/src/main/java/org/jdesktop/swingx/renderer/PainterAware.java b/src/main/java/org/jdesktop/swingx/renderer/PainterAware.java new file mode 100644 index 0000000000..d36f7cff37 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/renderer/PainterAware.java @@ -0,0 +1,36 @@ +/* + * $Id: PainterAware.java 3472 2009-08-27 13:12:42Z kleopatra $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.renderer; + +import org.jdesktop.swingx.painter.Painter; + +/** + * Temporary hook to allow painters in rendering.

            + * + * NOTE: this will be removed as soon as the painter_work enters + * main. + * + * @author Jeanette Winzenburg + */ +public interface PainterAware { + void setPainter(Painter painter); + Painter getPainter(); +} diff --git a/src/main/java/org/jdesktop/swingx/renderer/StringValue.java b/src/main/java/org/jdesktop/swingx/renderer/StringValue.java new file mode 100644 index 0000000000..3656bc27bb --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/renderer/StringValue.java @@ -0,0 +1,79 @@ +/* + * $Id: StringValue.java 3297 2009-03-11 13:45:10Z kleopatra $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.renderer; + +import java.io.Serializable; + + +/** + * A simple converter to return a String representation of an object. + * + * This class is intended to be the "small coin" to configure/format textual + * cell content of concrete subclasses of ComponentProvider. + *

            + * + * F.i. to show a Contributor cell object as "Busywoman, Herta" implement a + * custom StringValue and use it in a text rendering provider. + * + *

            
            + * StringValue stringValue = new StringValue() {
            + * 
            + *     public String getString(Object value) {
            + *         if (!(value instanceof Contributor))
            + *             return TO_STRING.getString(value);
            + *         Contributor contributor = (Contributor) value;
            + *         return contributor.lastName + ", " + contributor.firstName;
            + *     }
            + * 
            + * };
            + * 
            + * ComponentProvider provider = new LabelProvider(stringValue);
            + * table.setDefaultRenderer(Contributor.class, 
            + *   new DefaultTableRenderer(provider));
            + * 
            + * + *

            + * + * PENDING: use a full-fledged Format instead? + * Would impose a higher burden onto implementors but could be re-used in + * editors. + * + * @author Jeanette Winzenburg + * + * @see ComponentProvider + * @see LabelProvider + * @see DefaultTableRenderer + * @see DefaultListRenderer + * @see DefaultTreeRenderer + */ +public interface StringValue extends Serializable { + + /** + * Returns a string representation of the given value.

            + * + * PENDING JW: forgot - why not null return guaranteed? + * + * @param value the object to present as a string + * @return a string representation of the given value, + * guaranteed to be not null + */ + String getString(Object value); +} diff --git a/src/main/java/org/jdesktop/swingx/renderer/StringValues.java b/src/main/java/org/jdesktop/swingx/renderer/StringValues.java new file mode 100644 index 0000000000..4806f9d8cb --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/renderer/StringValues.java @@ -0,0 +1,189 @@ +/* + * $Id: StringValues.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.renderer; + +import org.jdesktop.swingx.util.Contract; + +import javax.swing.filechooser.FileSystemView; +import javax.swing.plaf.UIResource; +import java.io.File; +import java.text.DateFormat; +import java.text.NumberFormat; +import java.util.Locale; + + +/** + * A collection of common {@code StringValue} implementations. + * + * @author Karl George Schaefer + * @author Jeanette Winzenburg + */ +public final class StringValues { + /** + * A {@code StringValue} that always presents an empty string. + */ + @SuppressWarnings("serial") + public final static StringValue EMPTY = new StringValue() { + @Override + public String getString(Object value) { + return ""; + } + }; + + /** + * A {@code StringValue} that presents a {@link Object#toString() toString} + * value for the given object. If the value passed is {@code null}, this has + * the same effect as {@link StringValues#EMPTY}. + */ + @SuppressWarnings("serial") + public final static StringValue TO_STRING = new StringValue() { + @Override + public String getString(Object value) { + return (value != null) ? value.toString() : StringValues.EMPTY.getString(value); + } + }; + + /** + * A {@code StringValue} that presents the current L&F display name for a + * given file. If the value passed to {@code FILE_NAME} is not a + * {@link File}, this has the same effect as {@link StringValues#TO_STRING}. + */ + @SuppressWarnings("serial") + public static final StringValue FILE_NAME = new StringValue() { + @Override + public String getString(Object value) { + if (value instanceof File) { + FileSystemView fsv = FileSystemView.getFileSystemView(); + + return fsv.getSystemDisplayName((File) value); + } + + return StringValues.TO_STRING.getString(value); + } + }; + + /** + * A {@code StringValue} that presents the current L&F type name for a + * given file. If the value passed to {@code FILE_TYPE} is not a + * {@link File}, this has the same effect as {@link StringValues#TO_STRING}. + */ + @SuppressWarnings("serial") + public static final StringValue FILE_TYPE = new StringValue() { + @Override + public String getString(Object value) { + if (value instanceof File) { + FileSystemView fsv = FileSystemView.getFileSystemView(); + + return fsv.getSystemTypeDescription((File) value); + } + + return StringValues.TO_STRING.getString(value); + } + }; + + + /** keep track of default locale. */ + private static Locale defaultLocale; + + /** + * Returns a boolean to indicate if the default Locale has changed. + * Updates internal state to keep track of the default Locale. + * + * @return true if the default Locale has changed. + */ + private static boolean localeChanged() { + boolean changed = !Locale.getDefault().equals(defaultLocale); + if (changed) { + defaultLocale = Locale.getDefault(); + } + return changed; + } + + /** + * Default converter for Date types. Uses the default format + * as returned from DateFormat. + */ + @SuppressWarnings("serial") + public final static FormatStringValue DATE_TO_STRING = new FormatStringValue() { + + /** + * {@inheritDoc} + */ + @Override + public String getString(Object value) { + if (format == null || localeChanged()) { + format = DateFormat.getDateInstance(); + } + return super.getString(value); + } + + }; + + /** + * Default converter for Number types. Uses the default format + * as returned from NumberFormat. + */ + @SuppressWarnings("serial") + public final static FormatStringValue NUMBER_TO_STRING = new FormatStringValue() { + + /** + * {@inheritDoc} + */ + @Override + public String getString(Object value) { + if (format == null || localeChanged()) { + format = NumberFormat.getNumberInstance(); + } + return super.getString(value); + } + + }; + + + + public static final StringValue TO_STRING_UI = new StringValueUIResource(StringValues.TO_STRING); + public static final StringValue EMPTY_UI = new StringValueUIResource(StringValues.EMPTY); + + /** + * StringValue wrapper of type UIResource to tag LAF installed converters. + * + * @author Jeanette Winzenburg, Berlin + */ + public static class StringValueUIResource implements StringValue, UIResource { + + private StringValue delegate; + + public StringValueUIResource(StringValue toString) { + Contract.asNotNull(toString, "delegate StringValue must not be null"); + this.delegate = toString; + } + + @Override + public String getString(Object value) { + return delegate.getString(value); + } + + } + + private StringValues() { + // does nothing + } +} diff --git a/src/main/java/org/jdesktop/swingx/renderer/TableCellContext.java b/src/main/java/org/jdesktop/swingx/renderer/TableCellContext.java new file mode 100644 index 0000000000..7bd71ef7aa --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/renderer/TableCellContext.java @@ -0,0 +1,187 @@ +/* + * $Id: TableCellContext.java 3783 2010-09-15 13:13:23Z kleopatra $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.renderer; + +import org.jdesktop.swingx.plaf.UIManagerExt; + +import javax.swing.*; +import java.awt.*; + +/** + * Table specific CellContext. + * + * This implementation optionally can handle LAF provide alternateRowColor. The default + * is not doing it. To enable, client code must set a UI-Property with key + * HANDLE_ALTERNATE_ROW_BACKGROUND to Boolean.TRUE. + */ +public class TableCellContext extends CellContext { + + public static final String HANDLE_ALTERNATE_ROW_BACKGROUND = "TableCellContext.handleAlternateRowBackground"; + /** + * Sets state of the cell's context. Note that the component might be null + * to indicate a cell without a concrete context. All accessors must cope + * with. + * + * @param component the component the cell resides on, might be null + * @param value the content value of the cell + * @param row the cell's row index in view coordinates + * @param column the cell's column index in view coordinates + * @param selected the cell's selected state + * @param focused the cell's focused state + * @param expanded the cell's expanded state + * @param leaf the cell's leaf state + */ + public void installContext(JTable component, Object value, int row, int column, + boolean selected, boolean focused, boolean expanded, boolean leaf) { + this.component = component; + installState(value, row, column, selected, focused, expanded, leaf); + this.dropOn = checkDropOnState(); + } + + + /** + * + */ + private boolean checkDropOnState() { + if ((getComponent() == null) || !isValidRow() || !isValidColumn()) { + return false; + } + JTable.DropLocation dropLocation = getComponent().getDropLocation(); + if (dropLocation != null + && !dropLocation.isInsertRow() + && !dropLocation.isInsertColumn() + && dropLocation.getRow() == row + && dropLocation.getColumn() == column) { + return true; + } + return false; + } + + + @Override + public JTable getComponent() { + return (JTable) super.getComponent(); + } + + /** + * Returns the cell's editable property as returned by table.isCellEditable + * or false if the table is null. + * + * @return the cell's editable property. + */ + @Override + public boolean isEditable() { + if ((getComponent() == null) || !isValidRow() || !isValidColumn()) { + return false; + } + return getComponent().isCellEditable(getRow(), getColumn()); + } + + /** + * @inherited

            + * Overridden to respect UI alternating row colors. + * + */ + @Override + protected Color getBackground() { + if (isDropOn()) { + return getSelectionBackground(); + } + if (getComponent() == null) return null; + Color color = getAlternateRowColor(); + // JW: this is fixing a core bug - alternate color (aka: different + // from default table background) should be the odd row + if ((color != null) && getRow() >= 0 && getRow() % 2 == 1) { + return color; + } + return getComponent().getBackground(); + } + + /** + * Returns a Color to for odd row background if this context should handle the + * alternating row color AND the UIManager has the alternateRowColor property set. + * Returns null otherwise. + * + * @return the color to use for odd row background, or null if either this context + * does not handle or no alternate row color is set. + */ + protected Color getAlternateRowColor() { + if (!Boolean.TRUE.equals(UIManager.get(HANDLE_ALTERNATE_ROW_BACKGROUND))) return null; + return UIManagerExt.getColor(getUIPrefix() + "alternateRowColor"); + + } + + /** + * {@inheritDoc} + */ + @Override + protected Color getSelectionBackground() { + Color selection = null; + if (isDropOn()) { + selection = getDropCellBackground(); + if (selection != null) return selection; + } + return getComponent() != null ? getComponent() + .getSelectionBackground() : null; + } + + /** + * {@inheritDoc} + */ + @Override + protected Color getSelectionForeground() { + Color selection = null; + if (isDropOn()) { + selection = getDropCellForeground(); + if (selection != null) return selection; + } + return getComponent() != null ? getComponent() + .getSelectionForeground() : null; + } + + /** + * {@inheritDoc} + */ + @Override + protected String getUIPrefix() { + return "Table."; + } + + /** + * PRE getComponent != null + * + * @return whether the column coordinate is valid in this context + */ + protected boolean isValidColumn() { + return getColumn() >= 0 && getColumn() < getComponent().getColumnCount() ; + } + + /** + * PRE getComponent != null + * + * @return whether the row coordinate is valid in this context + */ + protected boolean isValidRow() { + return getRow() >= 0 && getRow() < getComponent().getRowCount() ; + } + + +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/renderer/TreeCellContext.java b/src/main/java/org/jdesktop/swingx/renderer/TreeCellContext.java new file mode 100644 index 0000000000..ec2eb32bf3 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/renderer/TreeCellContext.java @@ -0,0 +1,278 @@ +/* + * $Id: TreeCellContext.java 3424 2009-07-30 10:53:39Z kleopatra $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.renderer; + +import org.jdesktop.swingx.JXTree; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.border.LineBorder; +import javax.swing.plaf.basic.BasicGraphicsUtils; +import javax.swing.tree.TreePath; +import java.awt.*; + +/** + * Tree specific CellContext. + * + *

              + *
            • PENDING: use focus border as returned from list or table instead of + * rolling its own? The missing ui-border probably is a consequence of the + * border hacking as implemented in core default renderer. SwingX has a + * composite default which should use the "normal" border. + *
            • PENDING: selection colors couple explicitly to SwingX - should we go JXTree as + * generic type? + *
            • PENDING: for a JXTree use the icons as returned by the xtree api? + *
            + */ +public class TreeCellContext extends CellContext { + /** the icon to use for a leaf node. */ + protected Icon leafIcon; + + /** the default icon to use for a closed folder. */ + protected Icon closedIcon; + + /** the default icon to use for a open folder. */ + protected Icon openIcon; + + /** the border around a focused node. */ + private Border treeFocusBorder; + + /** + * Sets state of the cell's context. Note that the component might be null + * to indicate a cell without a concrete context. All accessors must cope + * with. + * + * @param component the component the cell resides on, might be null + * @param value the content value of the cell + * @param row the cell's row index in view coordinates + * @param column the cell's column index in view coordinates + * @param selected the cell's selected state + * @param focused the cell's focused state + * @param expanded the cell's expanded state + * @param leaf the cell's leaf state + */ + public void installContext(JTree component, Object value, int row, int column, + boolean selected, boolean focused, boolean expanded, boolean leaf) { + this.component = component; + installState(value, row, column, selected, focused, expanded, leaf); + this.dropOn = checkDropOnState(); + } + + private boolean checkDropOnState() { + if ((getComponent() == null)) { + return false; + } + JTree.DropLocation dropLocation = getComponent().getDropLocation(); + if (dropLocation != null + && dropLocation.getChildIndex() == -1 + && getComponent().getRowForPath(dropLocation.getPath()) == row) { + return true; + } + return false; + } + + @Override + public JTree getComponent() { + return (JTree) super.getComponent(); + } + +//------------------- accessors for derived state + + /** + * Returns the treePath for the row or null if invalid. + * + */ + public TreePath getTreePath() { + if (getComponent() == null) return null; + if ((row < 0) || (row >= getComponent().getRowCount())) return null; + return getComponent().getPathForRow(row); + } + /** + * {@inheritDoc} + *

            + * PENDING: implement to return the tree cell editability! + */ + @Override + public boolean isEditable() { + return false; + // return getComponent() != null ? getComponent().isCellEditable( + // getRow(), getColumn()) : false; + } + + /** + * {@inheritDoc} + */ + @Override + protected Color getSelectionBackground() { + Color selection = null; + if (isDropOn()) { + selection = getDropCellBackground(); + if (selection != null) return selection; + } + if (getComponent() instanceof JXTree) { + return ((JXTree) getComponent()).getSelectionBackground(); + } + return UIManager.getColor("Tree.selectionBackground"); + } + + /** + * {@inheritDoc} + */ + @Override + protected Color getSelectionForeground() { + Color selection = null; + if (isDropOn()) { + selection = getDropCellForeground(); + if (selection != null) return selection; + } + if (getComponent() instanceof JXTree) { + return ((JXTree) getComponent()).getSelectionForeground(); + } + return UIManager.getColor("Tree.selectionForeground"); + } + + /** + * {@inheritDoc} + */ + @Override + protected String getUIPrefix() { + return "Tree."; + } + + /** + * Returns the default icon to use for leaf cell. + * + * @return the icon to use for leaf cell. + */ + protected Icon getLeafIcon() { + return leafIcon != null ? leafIcon : UIManager + .getIcon(getUIKey("leafIcon")); + } + + /** + * Returns the default icon to use for open cell. + * + * @return the icon to use for open cell. + */ + protected Icon getOpenIcon() { + return openIcon != null ? openIcon : UIManager + .getIcon(getUIKey("openIcon")); + } + + /** + * Returns the default icon to use for closed cell. + * + * @return the icon to use for closed cell. + */ + protected Icon getClosedIcon() { + return closedIcon != null ? closedIcon : UIManager + .getIcon(getUIKey("closedIcon")); + } + + /** + * {@inheritDoc} + *

            + * + * Overridden to return a default depending for the leaf/open cell state. + */ + @Override + public Icon getIcon() { + if (isLeaf()) { + return getLeafIcon(); + } + if (isExpanded()) { + return getOpenIcon(); + } + return getClosedIcon(); + } + + @Override + protected Border getFocusBorder() { + if (treeFocusBorder == null) { + treeFocusBorder = new TreeFocusBorder(); + } + return treeFocusBorder; + } + + /** + * Border used to draw around the content of the node.

            + * PENDING: isn't that the same as around a list or table cell, but + * without a tree-specific key/value pair in UIManager? + */ + public class TreeFocusBorder extends LineBorder { + + private Color treeBackground; + + private Color focusColor; + + public TreeFocusBorder() { + super(Color.BLACK); + treeBackground = getBackground(); + if (treeBackground != null) { + focusColor = new Color(~treeBackground.getRGB()); + } + } + + @Override + public void paintBorder(Component c, Graphics g, int x, int y, + int width, int height) { + Color color = UIManager.getColor("Tree.selectionBorderColor"); + if (color != null) { + lineColor = color; + } + if (isDashed()) { + if (treeBackground != c.getBackground()) { + treeBackground = c.getBackground(); + focusColor = new Color(~treeBackground.getRGB()); + } + + Color old = g.getColor(); + g.setColor(focusColor); + BasicGraphicsUtils.drawDashedRect(g, x, y, width, height); + g.setColor(old); + + } else { + super.paintBorder(c, g, x, y, width, height); + } + + } + + /** + * @return a boolean indicating whether the focus border + * should be painted dashed style. + */ + private boolean isDashed() { + return Boolean.TRUE.equals(UIManager + .get("Tree.drawDashedFocusIndicator")); + + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isBorderOpaque() { + return false; + } + + } + +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/renderer/WrappingIconPanel.java b/src/main/java/org/jdesktop/swingx/renderer/WrappingIconPanel.java new file mode 100644 index 0000000000..f4e1487a90 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/renderer/WrappingIconPanel.java @@ -0,0 +1,304 @@ +/* + * $Id: WrappingIconPanel.java 4252 2012-11-13 18:37:17Z kschaefe $ + * + * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.renderer; + +import org.jdesktop.swingx.JXPanel; +import org.jdesktop.swingx.painter.Painter; + +import javax.swing.*; +import javax.swing.border.Border; +import java.awt.*; + +/** + * Compound component for usage in tree renderer.

            + * + * Supports setting an icon for the node and a delegate component + * which is used to show the text/content of the node. The delegate + * component can be shared across renderers.

            + * + * This implements the PainterAware by delegating to the delegate component if that + * is of type PainterAware. Does nothing if not. + */ +public class WrappingIconPanel extends JXPanel implements PainterAware, IconAware { + protected JComponent delegate; + JLabel iconLabel; + String labelPosition = BorderLayout.CENTER; //2; + int iconLabelGap; + private Border ltorBorder; + private Border rtolBorder; + private boolean dropHackEnabled; + private boolean extendsComponentOpacity; + + + /** + * Instantiates and configures a WrappingIconPanel with the dropHack + * enabled. + * + */ + public WrappingIconPanel() { + this(true); + } + /** + * Instantiates and configures a WrappingIconPanel with the dropHack + * property set as indicated by the boolean. + * + * @param dropHackEnabled a boolean indicating whether the drop hack should + * be enabled. + * + * @see #isVisible() + */ + public WrappingIconPanel(boolean dropHackEnabled) { + setOpaque(false); + iconLabel = new JRendererLabel(); + iconLabelGap = iconLabel.getIconTextGap(); + iconLabel.setOpaque(false); + updateIconBorder(); + setBorder(null); + setLayout(new BorderLayout()); + add(iconLabel, BorderLayout.LINE_START); + setDropHackEnabled(dropHackEnabled); + } + + /** + * {@inheritDoc}

            + * + * Overridden to update the icon position. + */ + @Override + public void setComponentOrientation(ComponentOrientation o) { + super.setComponentOrientation(o); + updateIconBorder(); + } + + /** + * Updates the icon position according to ComponentOrientation. + */ + private void updateIconBorder() { + if (ltorBorder == null) { + ltorBorder = BorderFactory.createEmptyBorder(0, 0, 0, iconLabelGap); + rtolBorder = BorderFactory.createEmptyBorder(0, iconLabelGap, 0, 0); + } + if (getComponentOrientation().isLeftToRight()) { + iconLabel.setBorder(ltorBorder); + } else { + iconLabel.setBorder(rtolBorder); + } + } + + /** + * Sets the icon. + * + * @param icon the icon to use. + */ + @Override + public void setIcon(Icon icon) { + iconLabel.setIcon(icon); + iconLabel.setText(null); + validate(); + } + + /** + * Returns the icon used in this panel, may be null. + * + * @return the icon used in this panel, may be null. + */ + @Override + public Icon getIcon() { + return iconLabel.getIcon(); + } + + + /** + * Sets the delegate component. + * + * @param comp the component to add as delegate. + */ + public void setComponent(JComponent comp) { + JComponent old = getComponent(); + if (delegate != null) { + remove(delegate); + } + delegate = comp; + if (extendsComponentOpacity) { + iconLabel.setOpaque(comp.isOpaque()); + } else { + iconLabel.setOpaque(false); + } + add(delegate, labelPosition); + validate(); + firePropertyChange("component", old, getComponent()); + } + + /** + * Returns the delegate component. + * + * @return the delegate component. + */ + public JComponent getComponent() { + return delegate; + } + + /** + * {@inheritDoc}

            + * + * Overridden to set the background of the delegate and icon label as well. + */ + @Override + public void setBackground(Color bg) { + super.setBackground(bg); + if (iconLabel != null) { + iconLabel.setBackground(bg); + } + if (delegate != null) { + delegate.setBackground(bg); + } + } + + /** + * {@inheritDoc}

            + * + * Overridden to set the foreground of the delegate and icon label as well. + */ + @Override + public void setForeground(Color bg) { + super.setForeground(bg); + if (iconLabel != null) { + iconLabel.setForeground(bg); + } + if (delegate != null) { + delegate.setForeground(bg); + } + } + + + + + /** + * {@inheritDoc}

            + * + * Overridden to set the Font of the delegate as well. + */ + @Override + public void setFont(Font font) { + if (delegate != null) { + delegate.setFont(font); + } + super.setFont(font); + } + + + /** + * {@inheritDoc} + *

            + * + * Overridden to hack around #766-swingx: cursor flickering in DnD when + * dragging over tree column. This is a core bug (#6700748) related to + * painting the rendering component on a CellRendererPane. A trick around is + * to let this return false. + *

            + * + * Some LayoutManagers don't layout an invisible component, so need to make + * the hack-enabled configurable. This implementation will return false + * if isDropHackEnabled, super.isVisible otherwise. + */ + @Override + public boolean isVisible() { + return dropHackEnabled ? false : super.isVisible(); + } + + + /** + * {@inheritDoc} + *

            + * + * Returns the delegate's Painter if it is of type PainterAware or null + * otherwise. + * + * @return the delegate's Painter or null. + */ + @Override + public Painter getPainter() { + if (delegate instanceof PainterAware) { + return ((PainterAware) delegate).getPainter(); + } + return null; + } + + + /** + * Sets the delegate's Painter if it is of type PainterAware. Does nothing otherwise. + * + * @param painter the Painter to apply to the delegate. + */ + @Override + public void setPainter(Painter painter) { + if (delegate instanceof PainterAware) { + ((PainterAware) delegate).setPainter(painter); + } + + } + + /** + * + * Returns the bounds of the delegate component or null if the delegate is null. + * + * PENDING JW: where do we use it? Maybe it was for testing only? + * + * @return the bounds of the delegate, or null if the delegate is null. + */ + public Rectangle getDelegateBounds() { + if (delegate == null) return null; + return delegate.getBounds(); + } + + + /** + * Sets the dropHackEnabled property.

            + * + * The default value is true. + * + * @param dropHackEnabled + * + * @see #isVisible() + */ + public void setDropHackEnabled(boolean dropHackEnabled) { + this.dropHackEnabled = dropHackEnabled; + } + + /** + * Sets a boolean indicating whether or not the main component's opacity + * should be applied to the Icon region.

            + * + * The default value is false. This covers the main use case in a JTree. + * + * @param extendsComponentOpacity + */ + public void setExtendsComponentOpacity(boolean extendsComponentOpacity) { + this.extendsComponentOpacity = extendsComponentOpacity; + + } + /** + * @return the extendsComponentOpacity + */ + public boolean getExtendsComponentOpacity() { + return extendsComponentOpacity; + } +} diff --git a/src/main/java/org/jdesktop/swingx/renderer/WrappingProvider.java b/src/main/java/org/jdesktop/swingx/renderer/WrappingProvider.java new file mode 100644 index 0000000000..815429a725 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/renderer/WrappingProvider.java @@ -0,0 +1,398 @@ +/* + * $Id: WrappingProvider.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.renderer; + +import org.jdesktop.swingx.rollover.RolloverRenderer; +import org.jdesktop.swingx.treetable.TreeTableNode; + +import javax.swing.*; +import javax.swing.tree.DefaultMutableTreeNode; + + +/** + * Wrapping ComponentProvider for usage in tree rendering. Handles the icon + * itself, delegates the node content to the wrappee. Value-based icon and + * content mapping can be configured by custom IconValues and + * StringValue, respectively. + *

            + * + * An example of how to configure a file tree by using the system icons and + * display names + * + *

            
            + * TreeCellRenderer r = new DefaultTreeRenderer(
            + *         IconValues.FILE_ICON, StringValues.FILE_NAME);
            + * tree.setCellRenderer(r);
            + * treeTable.setTreeCellRenderer(r);
            + * 
            + * + * PENDING: ui specific focus rect variation (draw rect around icon) missing + *

            + */ +public class WrappingProvider extends + ComponentProvider implements RolloverRenderer { + + protected ComponentProvider wrappee; + private boolean unwrapUserObject; + + /** + * Instantiates a WrappingProvider with default delegate provider. + * + */ + public WrappingProvider() { + this((ComponentProvider) null); + } + + /** + * Instantiates a WrappingProvider with default wrappee, configured + * to use the wrappeeStringValue. Uses the + * given IconValue to configure the icon. + * + * @param iconValue the IconValue to use for configuring the icon. + * @param wrappeeStringValue the StringValue to use in the wrappee. + */ + public WrappingProvider(IconValue iconValue, StringValue wrappeeStringValue) { + this(iconValue, wrappeeStringValue, true); + } + + /** + * Instantiates a WrappingProvider with default wrappee. Uses the + * given IconValue to configure the icon. + * + * @param iconValue the IconValue to use for configuring the icon. + */ + public WrappingProvider(IconValue iconValue) { + this(iconValue, null); + } + + /** + * Instantiates a WrappingProvider with default wrappee configured + * with the given StringValue. + * + * PENDING: we have a slight semantic glitch compared to super because + * the given StringValue is not for use in this provider but for use + * in the wrappee! + * + * @param wrappeeStringValue the StringValue to use in the wrappee. + */ + public WrappingProvider(StringValue wrappeeStringValue) { + this(null, wrappeeStringValue); + } + + /** + * Instantiates a WrappingProvider with the given delegate + * provider for the node content. If null, a default + * LabelProvider will be used. + * + * @param delegate the provider to use as delegate + */ + public WrappingProvider(ComponentProvider delegate) { + this(delegate, true); + } + + /** + * Instantiates a WrappingProvider with the given delegate + * provider for the node content and unwrapUserObject property. + * If the delegate is null, a default LabelProvider will be used. + * + * @param delegate the provider to use as delegate + * @param unwrapUserObject a flag indicating whether this provider + * should auto-unwrap the userObject from the context value. + */ + public WrappingProvider(ComponentProvider delegate, boolean unwrapUserObject) { + this(null, delegate, unwrapUserObject); + } + + /** + * Instantiates a WrappingProvider with the given delegate + * provider for the node content and unwrapUserObject property. + * If the delegate is null, a default LabelProvider will be used. + * + * @param iv the icon converter to use for this provider + * @param delegate the provider to use as delegate + * @param unwrapUserObject a flag indicating whether this provider + * should auto-unwrap the userObject from the context value. + */ + public WrappingProvider(IconValue iv, ComponentProvider delegate, boolean unwrapUserObject) { + super(iv != null ? (new MappedValue(null, iv)) : StringValues.EMPTY); + setWrappee(delegate); + setUnwrapUserObject(unwrapUserObject); + } + + /** + * Instantiates a WrappingProvider with the given delegate + * provider for the node content and unwrapUserObject property. + * If the delegate is null, a default LabelProvider will be used. + * + * @param iv the icon converter to use for this provider + * @param delegateStringValue the StringValue to use in the wrappee. + * @param unwrapUserObject a flag indicating whether this provider + * should auto-unwrap the userObject from the context value. + */ + public WrappingProvider(IconValue iv, StringValue delegateStringValue, boolean unwrapUserObject) { + this(iv, (ComponentProvider) null, unwrapUserObject); + getWrappee().setStringValue(delegateStringValue); + } + + /** + * Sets the given provider as delegate for the node content. + * If the delegate is null, a default LabelProvider is set.

            + * + * PENDING: rename to setDelegate? + * + * @param delegate the provider to use as delegate. + */ + public void setWrappee(ComponentProvider delegate) { + if (delegate == null) { + delegate = new LabelProvider(); + } + this.wrappee = delegate; + } + + /** + * Returns the delegate provider used to render the node content. + * + * @return the provider used for rendering the node content. + */ + public ComponentProvider getWrappee() { + return wrappee; + } + + /** + * Sets the unwrapUserObject property. If true, this provider + * replaces a context value of type XXNode with its user object before + * delegating to the wrappee. Otherwise the value is passed as-is always.

            + * + * The default value is true. + * + * @param unwrap + * @see #getUnwrapUserObject() + */ + public void setUnwrapUserObject(boolean unwrap) { + this.unwrapUserObject = unwrap; + } + + /** + * Returns a boolean indicating whether this provider tries to unwrap + * a userObject from a tree/table/node type value before delegating the + * context. + * + * @return a flag indicating the auto-unwrap property. + * + * @see #setUnwrapUserObject(boolean) + */ + public boolean getUnwrapUserObject() { + return unwrapUserObject; + } + + /** + * {@inheritDoc}

            + * + * Overridden to comply to contract: returns the string representation as + * provided by the wrappee (as this level has no string rep). Must do the + * same unwrapping magic as in configuring the rendering component if the + * unwrapUserObject property is true.

            + * + * + * @param value the Object to get a String representation for. + * + * @see #setUnwrapUserObject(boolean) + * @see #getUnwrappedValue(Object) + */ + @Override + public String getString(Object value) { + value = getUnwrappedValue(value); + return wrappee.getString(value); + } + + /** + * Sets a boolean indicating whether or not the main component's opacity + * should be applied to the Icon region.

            + * + * The default value is false. This covers the main use case in a JTree. + * + * @param extendsComponentOpacity + */ + public void setExtendsComponentOpacity(boolean extendsComponentOpacity) { + rendererComponent.setExtendsComponentOpacity(extendsComponentOpacity); + + } + /** + * @return the extendsComponentOpacity + */ + public boolean getExtendsComponentOpacity() { + return rendererComponent.getExtendsComponentOpacity(); + } + + + /** + * Returns the value as it should be passed to the delegate. If the unwrapUserObject + * property is true, tries return a userObject as appropriate for the value type. + * Returns the given value itself, ff the property is false or the type does + * not support the notion of userObject

            + * + * Here: unwraps userObject of DefaultMutableTreeNode and TreeTableNode.

            + * + * @param value the value to possibly unwrap + * @return the userObject if the value has an appropriate type and the + * unwrapUserObject property is true, otherwise returns the value unchanged. + * + * @see #setUnwrapUserObject(boolean) + * @see #getString(Object) + * @see #getRendererComponent(CellContext) + */ + protected Object getUnwrappedValue(Object value) { + if (!getUnwrapUserObject()) return value; + if (value instanceof DefaultMutableTreeNode) { + value = ((DefaultMutableTreeNode) value).getUserObject(); + } else if (value instanceof TreeTableNode) { + TreeTableNode node = (TreeTableNode) value; + value = node.getUserObject(); + } + return value; + } + + /** + * {@inheritDoc} + */ + @Override + public WrappingIconPanel getRendererComponent(CellContext context) { + if (context != null) { + rendererComponent.setComponent(wrappee.rendererComponent); + Object oldValue = adjustContextValue(context); + // PENDING JW: sequence of config? + // A - first wrappee, then this allows to override configure/format methods + // of this class and overrule the wrappee + // B - first this, then wrappee allows overrule by overriding getRendererComp + // would take control from wrappee (f.i. Hyperlink foreground) + super.getRendererComponent(context); + wrappee.getRendererComponent(context); + restoreContextValue(context, oldValue); + return rendererComponent; + } + // PENDING JW: Findbugs barking [NP] Load of known null value + // probably can move the return rendererComponent from the if + // to here (the contract is to return the comp as-is if the + // context is null) - so we can do it here instead of delegating + // to super? + return super.getRendererComponent(context); + } + + /** + * Restores the context value to the old value. + * + * @param context the CellContext to restore. + * @param oldValue the value to restore the context to. + */ + protected void restoreContextValue(CellContext context, Object oldValue) { + context.replaceValue(oldValue); + } + + /** + * Replace the context's value with the userobject if the value is a type + * supporting the notion of userObject and this provider's unwrapUserObject + * property is true. Otherwise does nothing.

            + * + * Subclasses may override but must guarantee to return the original + * value for restoring. + * + * @param context the context to adjust + * @return the old context value + * + * @see #setUnwrapUserObject(boolean) + * @see #getString(Object) + */ + protected Object adjustContextValue(CellContext context) { + Object oldValue = context.getValue(); + if (getUnwrapUserObject()) { + context.replaceValue(getUnwrappedValue(oldValue)); + } + return oldValue; + } + + @Override + protected void configureState(CellContext context) { + rendererComponent.setBorder(BorderFactory.createEmptyBorder()); + } + +// /** +// * @return +// */ +// private boolean isBorderAroundIcon() { +// return Boolean.TRUE.equals(UIManager.get("Tree.drawsFocusBorderAroundIcon")); +// } + + @Override + protected WrappingIconPanel createRendererComponent() { + return new WrappingIconPanel(); + } + + /** + * {@inheritDoc}

            + * + * Here: implemented to set the icon. + */ + @Override + protected void format(CellContext context) { + rendererComponent.setIcon(getValueAsIcon(context)); + } + + /** + * {@inheritDoc}

            + * + * Overridden to fallback to the default icons supplied by the + * context if super returns null. + * + */ + @Override + protected Icon getValueAsIcon(CellContext context) { + Icon icon = super.getValueAsIcon(context); + if (icon == null) { + return context.getIcon(); + } + return IconValue.NULL_ICON == icon ? null : icon; + } + + //----------------- implement RolloverController + + + /** + * {@inheritDoc} + */ + @Override + public void doClick() { + if (isEnabled()) { + ((RolloverRenderer) wrappee).doClick(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isEnabled() { + return (wrappee instanceof RolloverRenderer) && + ((RolloverRenderer) wrappee).isEnabled(); + } + + + +} diff --git a/src/main/java/org/jdesktop/swingx/renderer/package-info.java b/src/main/java/org/jdesktop/swingx/renderer/package-info.java new file mode 100644 index 0000000000..17548705ea --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/renderer/package-info.java @@ -0,0 +1,25 @@ +/* + * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +/** + * Contains implementation of renderers used by JXTable, JXTreeTable and related classes. + */ +package org.jdesktop.swingx.renderer; + diff --git a/src/main/java/org/jdesktop/swingx/rollover/ListRolloverController.java b/src/main/java/org/jdesktop/swingx/rollover/ListRolloverController.java new file mode 100644 index 0000000000..544d93b496 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/rollover/ListRolloverController.java @@ -0,0 +1,112 @@ +/* + * $Id: ListRolloverController.java 3707 2010-07-08 19:19:25Z kschaefe $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.rollover; + +import javax.swing.*; +import java.awt.*; + + +/** + * listens to rollover properties. Repaints effected component regions. + * Updates link cursor. + * + * @author Jeanette Winzenburg + */ +public class ListRolloverController extends RolloverController { + + private Cursor oldCursor; + + // --------------------------------- JList rollover + + @Override + protected void rollover(Point oldLocation, Point newLocation) { + // PENDING JW - track down the -1 in location.y + if (oldLocation != null) { + Rectangle r = component.getCellBounds(oldLocation.y, oldLocation.y); + // LOG.info("old index/cellbounds: " + index + "/" + r); + if (r != null) { + component.repaint(r); + } + } + if (newLocation != null) { + Rectangle r = component.getCellBounds(newLocation.y, newLocation.y); + // LOG.info("new index/cellbounds: " + index + "/" + r); + if (r != null) { + component.repaint(r); + } + } + setRolloverCursor(newLocation); + } + + /** + * something weird: cursor in JList behaves different from JTable? + * Hmm .. no: using the table code snippets seems to fix #503-swingx + * @param location + */ + private void setRolloverCursor(Point location) { + if (hasRollover(location)) { + if (oldCursor == null) { + oldCursor = component.getCursor(); + component.setCursor(Cursor + .getPredefinedCursor(Cursor.HAND_CURSOR)); + } + } else { + if (oldCursor != null) { + component.setCursor(oldCursor); + oldCursor = null; + } + } +// if (hasRollover(location)) { +// oldCursor = component.getCursor(); +// component.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); +// } else { +// component.setCursor(oldCursor); +// oldCursor = null; +// } + + } + + @Override + protected RolloverRenderer getRolloverRenderer(Point location, + boolean prepare) { + ListCellRenderer renderer = component.getCellRenderer(); + RolloverRenderer rollover = renderer instanceof RolloverRenderer + ? (RolloverRenderer) renderer : null; + if ((rollover != null) && !rollover.isEnabled()) { + rollover = null; + } + if ((rollover != null) && prepare) { + Object element = component.getModel().getElementAt(location.y); + renderer.getListCellRendererComponent(component, element, + location.y, false, true); + } + return rollover; + } + + @Override + protected Point getFocusedCell() { + int leadRow = component.getLeadSelectionIndex(); + if (leadRow < 0) + return null; + return new Point(0, leadRow); + } + + } \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/rollover/ListRolloverProducer.java b/src/main/java/org/jdesktop/swingx/rollover/ListRolloverProducer.java new file mode 100644 index 0000000000..a7353388a8 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/rollover/ListRolloverProducer.java @@ -0,0 +1,49 @@ +/* + * $Id: ListRolloverProducer.java 3296 2009-03-11 12:06:01Z kleopatra $ + * + * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.rollover; + +import javax.swing.*; +import java.awt.*; + +/** + * List-specific implementation of RolloverProducer. + * + * @author Jeanette Winzenburg + */ +public class ListRolloverProducer extends RolloverProducer { + + @Override + protected void updateRolloverPoint(JComponent component, Point mousePoint) { + JList list = (JList) component; + int row = list.locationToIndex(mousePoint); + if (row >= 0) { + Rectangle cellBounds = list.getCellBounds(row, row); + if (!cellBounds.contains(mousePoint)) { + row = -1; + } + } + int col = row < 0 ? -1 : 0; + rollover.x = col; + rollover.y = row; + } + +} diff --git a/src/main/java/org/jdesktop/swingx/rollover/RolloverController.java b/src/main/java/org/jdesktop/swingx/rollover/RolloverController.java new file mode 100644 index 0000000000..4f8cbfd739 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/rollover/RolloverController.java @@ -0,0 +1,235 @@ +/* + * $Id: RolloverController.java 3967 2011-03-17 19:18:47Z kschaefe $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.rollover; + +import org.jdesktop.swingx.plaf.UIAction; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.logging.Logger; + +/** + * Controller for "live" behaviour of XXRenderers. + * + * Once installed on a component, it updates renderer's rollover + * state based on the component's rollover properties. Rollover + * client properties are Points with cell coordinates + * in the view coordinate + * system as appropriate for the concrete component + * (Point.x == column, Point.y == row). + * + * Repaints effected component regions. Updates + * link cursor. Installs a click-action bound to space-released in the target's + * actionMap/inputMap. + * + * + * @author Jeanette Winzenburg, Berlin + */ +public abstract class RolloverController implements + PropertyChangeListener { + @SuppressWarnings("unused") + private static final Logger LOG = Logger.getLogger(RolloverController.class + .getName()); + /** + * the key of the rollover click action which is installed in the + * component's actionMap. + */ + public static final String EXECUTE_BUTTON_ACTIONCOMMAND = "executeButtonAction"; + + protected T component; + + @Override + public void propertyChange(PropertyChangeEvent evt) { + // JW: should not happen ... being paranoid. + if ((component == null) || (component != evt.getSource())) + return; + if (RolloverProducer.ROLLOVER_KEY.equals(evt.getPropertyName())) { + rollover((Point) evt.getOldValue(), (Point) evt.getNewValue()); + } else if (RolloverProducer.CLICKED_KEY.equals(evt.getPropertyName())) { + click((Point) evt.getNewValue()); + } + } + + /** + * Install this as controller for the given component. + * + * @param table the component which has renderers to control. + */ + public void install(T table) { + release(); + this.component = table; + table.addPropertyChangeListener(RolloverProducer.CLICKED_KEY, this); + table.addPropertyChangeListener(RolloverProducer.ROLLOVER_KEY, this); + registerExecuteButtonAction(); + } + + /** + * Uninstall this as controller from the component, if any. + * + */ + public void release() { + if (component == null) + return; + component.removePropertyChangeListener(RolloverProducer.CLICKED_KEY, this); + component.removePropertyChangeListener(RolloverProducer.ROLLOVER_KEY, this); + unregisterExecuteButtonAction(); + component = null; + } + + /** + * called on change of client property Rollover_Key. + * + * @param oldLocation the old value of the rollover location. + * @param newLocation the new value of the rollover location. + */ + protected abstract void rollover(Point oldLocation, Point newLocation); + + /** + * called on change of client property Clicked_key. + * @param location the new value of the clicked location. + */ + protected void click(Point location) { + if (!isClickable(location)) + return; + RolloverRenderer rollover = getRolloverRenderer(location, true); + if (rollover != null) { + rollover.doClick(); + component.repaint(); + } + } + + /** + * Returns the rolloverRenderer at the given location.

            + * + * The result + * may be null if there is none or if rollover is not enabled. + * + * If the prepare flag is true, the renderer will be prepared + * with value and state as appropriate for the given location. + * + * Note: PRE - the location must be valid in cell coordinate space. + * + * @param location a valid location in cell coordinates, p.x == column, p.y == row. + * @param prepare + * @return RolloverRenderer at the given location + */ + protected abstract RolloverRenderer getRolloverRenderer(Point location, + boolean prepare); + + /** + * Returns a boolean indicating whether or not the cell at the given + * location is clickable.

            + * + * This implementation returns true if the target is enabled and the + * cell has a rollover renderer. + * + * @param location in cell coordinates, p.x == column, p.y == row. + * @return true if the cell at the given location is clickable + * + * @see #hasRollover(Point) + */ + protected boolean isClickable(Point location) { + return component.isEnabled() && hasRollover(location); + } + + /** + * Returns a boolean indicating whether the or not the cell at the + * given has a rollover renderer. Always returns false if the location + * is not valid. + * + * @param location in cell coordinates, p.x == column, p.y == row. + * @return true if the location is valid and has rollover effects, false + * otherwise. + * + */ + protected boolean hasRollover(Point location) { + if (location == null || location.x < 0 || location.y < 0) + return false; + return getRolloverRenderer(location, false) != null; + } + + /** + * The coordinates of the focused cell in view coordinates. + * + * This method is called if the click action is invoked by a keyStroke. + * The returned cell coordinates should be related to + * what is typically interpreted as "focused" in the context of the + * component. + * + * p.x == focused column, p.y == focused row. + * A null return value or any coordinate value of < 0 + * is interpreted as "outside". + * + * @return the location of the focused cell. + */ + protected abstract Point getFocusedCell(); + + /** + * uninstalls and deregisters the click action from the component's + * actionMap/inputMap. + * + */ + protected void unregisterExecuteButtonAction() { + component.getActionMap().put(EXECUTE_BUTTON_ACTIONCOMMAND, null); + KeyStroke space = KeyStroke.getKeyStroke("released SPACE"); + component.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put( + space, null); + } + + /** + * installs and registers the click action in the component's + * actionMap/inputMap. + * + */ + protected void registerExecuteButtonAction() { + component.getActionMap().put(EXECUTE_BUTTON_ACTIONCOMMAND, + createExecuteButtonAction()); + KeyStroke space = KeyStroke.getKeyStroke("released SPACE"); + component.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put( + space, EXECUTE_BUTTON_ACTIONCOMMAND); + + } + + /** + * creates and returns the click action to install in the + * component's actionMap. + * + */ + protected Action createExecuteButtonAction() { + return new UIAction(null) { + @Override + public void actionPerformed(ActionEvent e) { + click(getFocusedCell()); + } + + @Override + public boolean isEnabled(Object sender) { + if (component == null || !component.isEnabled() || !component.hasFocus()) + return false; + return isClickable(getFocusedCell()); + } + }; + } + +} diff --git a/src/main/java/org/jdesktop/swingx/rollover/RolloverProducer.java b/src/main/java/org/jdesktop/swingx/rollover/RolloverProducer.java new file mode 100644 index 0000000000..c4c1a04e64 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/rollover/RolloverProducer.java @@ -0,0 +1,281 @@ +/* + * $Id: RolloverProducer.java 3982 2011-03-30 12:27:31Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.rollover; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.util.logging.Logger; + +/** + * Mouse/Motion/Listener which maps mouse coordinates to client coordinates + * and stores these as client properties in the target JComponent. The exact + * mapping process is left to subclasses. Typically, they will map to "cell" + * coordinates.

            + * + * Note: this class assumes that the target component is of type JComponent.

            + * Note: this implementation is stateful, it can't be shared across different + * instances of a target component.

            + * + * + * @author Jeanette Winzenburg + */ +public abstract class RolloverProducer implements MouseListener, MouseMotionListener, + ComponentListener { + + @SuppressWarnings("unused") + private static final Logger LOG = Logger.getLogger(RolloverProducer.class + .getName()); + + /** + * Key for client property mapped from mouse-triggered action. + * Note that the actual mouse-event which results in setting the property + * depends on the implementation of the concrete RolloverProducer. + */ + public static final String CLICKED_KEY = "swingx.clicked"; + + /** Key for client property mapped from rollover events */ + public static final String ROLLOVER_KEY = "swingx.rollover"; + + // public static final String PRESSED_KEY = "swingx.pressed"; + + private boolean isDragging; + + /** + * Installs all listeners, as required. + * + * @param component target to install required listeners on, must + * not be null. + */ + public void install(JComponent component) { + component.addMouseListener(this); + component.addMouseMotionListener(this); + component.addComponentListener(this); + } + + /** + * Removes all listeners. + * + * @param component target component to uninstall required listeners from, + * must not be null + */ + public void release(JComponent component) { + component.removeMouseListener(this); + component.removeMouseMotionListener(this); + component.removeComponentListener(this); + } + + //----------------- mouseListener + + /** + * Implemented to map to Rollover properties as needed. This implemenation calls + * updateRollover with both ROLLOVER_KEY and CLICKED_KEY properties. + */ + @Override + public void mouseReleased(MouseEvent e) { + Point oldCell = new Point(rollover); + // JW: fix for #456-swingx - rollover not updated after end of dragging + updateRollover(e, ROLLOVER_KEY, false); + // Fix Issue 1387-swingx - no click on release-after-drag + if (isClick(e, oldCell, isDragging)) { + updateRollover(e, CLICKED_KEY, true); + } + isDragging = false; + } + + /** + * Returns a boolean indicating whether or not the given mouse event should + * be interpreted as a click. This method is called from mouseReleased + * after the cell coordiates were updated. While the ID of mouse event + * is not formally enforced, it is assumed to be a MOUSE_RELEASED. Calling + * for other types might or might not work as expected.

            + * + * This implementation returns true if the current rollover point is the same + * cell as the given oldRollover, that is ending a drag inside the same cell + * triggers the action while ending a drag somewhere does not.

            + * + * PENDING JW: open to more complex logic in case it clashes with existing code, + * see Issue #1387. + * + * @param e the mouseEvent which triggered calling this, assumed to be + * a mouseReleased, must not be null + * @param oldRollover the cell before the mouseEvent was mapped, must not be null + * @param wasDragging true if the release happened + * @return a boolean indicating whether or not the given mouseEvent should + * be interpreted as a click. + */ + protected boolean isClick(MouseEvent e, Point oldRollover, boolean wasDragging) { + return oldRollover.equals(rollover); + } + + /** + * Implemented to map to client property rollover and fire only if client + * coordinate changed. + */ + @Override + public void mouseEntered(MouseEvent e) { +// LOG.info("" + e); + isDragging = false; + updateRollover(e, ROLLOVER_KEY, false); + } + + /** + * Implemented to remove client properties rollover and clicked. if the + * source is a JComponent. Does nothing otherwise. + */ + @Override + public void mouseExited(MouseEvent e) { + isDragging = false; +// screenLocation = null; +// LOG.info("" + e); +// if (((JComponent) e.getComponent()).getMousePosition(true) != null) { +// updateRollover(e, ROLLOVER_KEY, false); +// } else { +// } + ((JComponent) e.getSource()).putClientProperty(ROLLOVER_KEY, null); + ((JComponent) e.getSource()).putClientProperty(CLICKED_KEY, null); + + } + + /** + * Implemented to do nothing. + */ + @Override + public void mouseClicked(MouseEvent e) { + } + + /** + * Implemented to do nothing. + */ + @Override + public void mousePressed(MouseEvent e) { + } + + // ---------------- MouseMotionListener + /** + * Implemented to set a dragging flag to true. + */ + @Override + public void mouseDragged(MouseEvent e) { + isDragging = true; + } + + /** + * Implemented to map to client property rollover and fire only if client + * coordinate changed. + */ + @Override + public void mouseMoved(MouseEvent e) { + updateRollover(e, ROLLOVER_KEY, false); + } + + //---------------- ComponentListener + + + @Override + public void componentShown(ComponentEvent e) { + } + + @Override + public void componentResized(ComponentEvent e) { + updateRollover(e); + } + + @Override + public void componentMoved(ComponentEvent e) { + updateRollover(e); + } + + /** + * @param e + */ + private void updateRollover(ComponentEvent e) { + Point componentLocation = e.getComponent().getMousePosition(); + if (componentLocation == null) { + componentLocation = new Point(-1, -1); + } +// LOG.info("" + componentLocation + " / " + e); + updateRolloverPoint((JComponent) e.getComponent(), componentLocation); + updateClientProperty((JComponent) e.getComponent(), ROLLOVER_KEY, true); + } + + @Override + public void componentHidden(ComponentEvent e) { + } + + //---------------- mapping methods + + /** + * Controls the mapping of the given mouse event to a client property. This + * implementation first calls updateRolloverPoint to convert the mouse coordinates. + * Then calls updateClientProperty to actually set the client property in the + * + * @param e the MouseEvent to map to client coordinates + * @param property the client property to map to + * @param fireAlways a flag indicating whether a client event should be fired if unchanged. + * + * @see #updateRolloverPoint(JComponent, Point) + * @see #updateClientProperty(JComponent, String, boolean) + */ + protected void updateRollover(MouseEvent e, String property, + boolean fireAlways) { + updateRolloverPoint((JComponent) e.getComponent(), e.getPoint()); + updateClientProperty((JComponent) e.getComponent(), property, fireAlways); + } + + /** Current mouse location in client coordinates. */ + protected Point rollover = new Point(-1, -1); + + /** + * Sets the given client property to the value of current mouse location in + * client coordinates. If fireAlways, the property is force to fire a change. + * + * @param component the target component + * @param property the client property to set + * @param fireAlways a flag indicating whether a client property + * should be forced to fire an event. + */ + protected void updateClientProperty(JComponent component, String property, + boolean fireAlways) { + if (fireAlways) { + // fix Issue #864-swingx: force propertyChangeEvent + component.putClientProperty(property, null); + component.putClientProperty(property, new Point(rollover)); + } else { + Point p = (Point) component.getClientProperty(property); + if (p == null || (rollover.x != p.x) || (rollover.y != p.y)) { + component.putClientProperty(property, new Point(rollover)); + } + } + } + + /** + * Subclasses must implement to map the given mouse coordinates into + * appropriate client coordinates. The result must be stored in the + * rollover field. + * + * @param component the target component which received a mouse event + * @param mousePoint the mouse position of the event, coordinates are + * component pixels + */ + protected abstract void updateRolloverPoint(JComponent component, Point mousePoint); + +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/rollover/RolloverRenderer.java b/src/main/java/org/jdesktop/swingx/rollover/RolloverRenderer.java new file mode 100644 index 0000000000..efbbbd09b4 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/rollover/RolloverRenderer.java @@ -0,0 +1,47 @@ +/* + * $Id: RolloverRenderer.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.rollover; + +/** + * Interface to mark renderers as "live".

            + * + * PENDING: probably need methods to enabled/click taking a similar + * set of parameters as getXXComponent because the actual + * outcome might depend on the given value. If so, we'll need + * to extend the XXRenderer interfaces. + * + * @author Jeanette Winzenburg + */ +public interface RolloverRenderer { + /** + * + * @return true if rollover effects are on and clickable. + */ + boolean isEnabled(); + + /** + * Same as AbstractButton.doClick(). It's up to client + * code to prepare the renderer's component before calling + * this method. + * + */ + void doClick(); +} diff --git a/src/main/java/org/jdesktop/swingx/rollover/TableRolloverController.java b/src/main/java/org/jdesktop/swingx/rollover/TableRolloverController.java new file mode 100644 index 0000000000..e8cff8042a --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/rollover/TableRolloverController.java @@ -0,0 +1,168 @@ +/* + * $Id: TableRolloverController.java 3641 2010-04-01 10:48:43Z kleopatra $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.rollover; + +import javax.swing.*; +import javax.swing.table.TableCellRenderer; +import java.awt.*; +import java.util.ArrayList; +import java.util.List; + +/** + * listens to rollover properties. Repaints effected component regions. Updates + * link cursor. + * + * @author Jeanette Winzenburg + */ +public class TableRolloverController extends + RolloverController { + + private Cursor oldCursor; + + // --------------------------- JTable rollover + + @Override + protected void rollover(Point oldLocation, Point newLocation) { + // check which rows are effected and need repaint + boolean paintOldRow = hasRow(oldLocation); + boolean paintNewRow = hasRow(newLocation); + if (paintOldRow && paintNewRow) { + if (oldLocation.y == newLocation.y) { + // row unchanged, no need for repaint + paintOldRow = false; + paintNewRow = false; + } + } + // check which columns are effected and need repaint + boolean paintOldColumn = hasColumn(oldLocation); + boolean paintNewColumn = hasColumn(newLocation); + if (paintOldColumn && paintNewColumn) { + if (oldLocation.x == newLocation.x) { + // column unchanged, no need for repaint + paintOldColumn = false; + paintNewColumn = false; + } + } + + List rectangles = getPaintRectangles(null, oldLocation, paintOldRow, paintOldColumn); + rectangles = getPaintRectangles(rectangles, newLocation, paintNewRow, paintNewColumn); + if (rectangles != null) { + for (Rectangle rectangle : rectangles) { + component.repaint(rectangle); + } + } + setRolloverCursor(newLocation); + } + + /** + * @param rectangles List of rectangles to paint, maybe null + * @param cellLocation the location of the cell, guaranteed to be not null + * @param paintRow boolean indicating whether the row should be painted + * @param paintColumn boolean indicating whether the column should be painted + * @return list of rectangles to paint, maybe null + */ + private List getPaintRectangles(List rectangles, Point cellLocation, + boolean paintRow, boolean paintColumn) { + if (!paintRow && !paintColumn) return rectangles; + if (rectangles == null) { + rectangles = new ArrayList(); + } + Rectangle r = component.getCellRect(cellLocation.y, cellLocation.x, + false); + if (paintRow) { + rectangles.add(new Rectangle(0, r.y, component.getWidth(), + r.height)); + } + if (paintColumn) { + rectangles.add(new Rectangle(r.x, 0, r.width, component + .getHeight())); + } + return rectangles; + } + + /** + * @param cellLocation the cell location to check, may be null + * @return a boolean indicating whether the given cellLocation has a column + * to paint + */ + private boolean hasColumn(Point cellLocation) { + return cellLocation != null && cellLocation.x >= 0; + } + + /** + * @param cellLocation the cell location to check, may be null + * @return a boolean indicating whether the given cellLocation has a row + * to paint + */ + private boolean hasRow(Point cellLocation) { + return cellLocation != null && cellLocation.y >= 0; + } + + /** + * overridden to return false if cell editable. + */ + @Override + protected boolean isClickable(Point location) { + return super.isClickable(location) + && !component.isCellEditable(location.y, location.x); + } + + @Override + protected RolloverRenderer getRolloverRenderer(Point location, + boolean prepare) { + TableCellRenderer renderer = component.getCellRenderer(location.y, + location.x); + RolloverRenderer rollover = renderer instanceof RolloverRenderer ? (RolloverRenderer) renderer + : null; + if ((rollover != null) && !rollover.isEnabled()) { + rollover = null; + } + if ((rollover != null) && prepare) { + component.prepareRenderer(renderer, location.y, location.x); + } + return rollover; + } + + private void setRolloverCursor(Point location) { + if (hasRollover(location)) { + if (oldCursor == null) { + oldCursor = component.getCursor(); + component.setCursor(Cursor + .getPredefinedCursor(Cursor.HAND_CURSOR)); + } + } else { + if (oldCursor != null) { + component.setCursor(oldCursor); + oldCursor = null; + } + } + + } + + @Override + protected Point getFocusedCell() { + int leadRow = component.getSelectionModel().getLeadSelectionIndex(); + int leadColumn = component.getColumnModel().getSelectionModel() + .getLeadSelectionIndex(); + return new Point(leadColumn, leadRow); + } + +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/rollover/TableRolloverProducer.java b/src/main/java/org/jdesktop/swingx/rollover/TableRolloverProducer.java new file mode 100644 index 0000000000..a412767f1d --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/rollover/TableRolloverProducer.java @@ -0,0 +1,47 @@ +/* + * $Id: TableRolloverProducer.java 3296 2009-03-11 12:06:01Z kleopatra $ + * + * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.rollover; + +import javax.swing.*; +import java.awt.*; + +/** + * Table-specific implementation of RolloverProducer. + * + * @author Jeanette Winzenburg + */ +public class TableRolloverProducer extends RolloverProducer { + + @Override + protected void updateRolloverPoint(JComponent component, Point mousePoint) { + JTable table = (JTable) component; + int col = table.columnAtPoint(mousePoint); + int row = table.rowAtPoint(mousePoint); + if ((col < 0) || (row < 0)) { + row = -1; + col = -1; + } + rollover.x = col; + rollover.y = row; + } + +} diff --git a/src/main/java/org/jdesktop/swingx/rollover/TreeRolloverController.java b/src/main/java/org/jdesktop/swingx/rollover/TreeRolloverController.java new file mode 100644 index 0000000000..2340e5b311 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/rollover/TreeRolloverController.java @@ -0,0 +1,108 @@ +/* + * $Id: TreeRolloverController.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.rollover; + +import javax.swing.*; +import javax.swing.tree.TreeCellRenderer; +import javax.swing.tree.TreePath; +import java.awt.*; + + +/** + * listens to rollover properties. + * Repaints effected component regions. + * Updates link cursor. + * + * @author Jeanette Winzenburg + */ + public class TreeRolloverController extends RolloverController { + + private Cursor oldCursor; + +// -------------------------------------JTree rollover + + @Override + protected void rollover(Point oldLocation, Point newLocation) { + // JW: conditional repaint not working? +// component.repaint(); + if (oldLocation != null) { + Rectangle r = component.getRowBounds(oldLocation.y); + if (r != null) { + r.x = 0; + r.width = component.getWidth(); + component.repaint(r); + } + } + if (newLocation != null) { + Rectangle r = component.getRowBounds(newLocation.y); + if (r != null) { + r.x = 0; + r.width = component.getWidth(); + component.repaint(r); + } + } + setRolloverCursor(newLocation); + } + + + private void setRolloverCursor(Point location) { + if (hasRollover(location)) { + if (oldCursor == null) { + oldCursor = component.getCursor(); + component.setCursor(Cursor + .getPredefinedCursor(Cursor.HAND_CURSOR)); + } + } else { + if (oldCursor != null) { + component.setCursor(oldCursor); + oldCursor = null; + } + } + + } + + + @Override + protected RolloverRenderer getRolloverRenderer(Point location, boolean prepare) { + TreeCellRenderer renderer = component.getCellRenderer(); + RolloverRenderer rollover = renderer instanceof RolloverRenderer + ? (RolloverRenderer) renderer : null; + if ((rollover != null) && !rollover.isEnabled()) { + rollover = null; + } + if ((rollover != null) && prepare) { + TreePath path = component.getPathForRow(location.y); + Object element = path != null ? path.getLastPathComponent() : null; + renderer.getTreeCellRendererComponent(component, element, false, + false, false, + location.y, false); + } + return rollover; + } + + + @Override + protected Point getFocusedCell() { + // TODO Auto-generated method stub + return null; + } + + } \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/rollover/TreeRolloverProducer.java b/src/main/java/org/jdesktop/swingx/rollover/TreeRolloverProducer.java new file mode 100644 index 0000000000..973bb0a80b --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/rollover/TreeRolloverProducer.java @@ -0,0 +1,87 @@ +/* + * $Id: TreeRolloverProducer.java 4190 2012-06-27 19:01:06Z kschaefe $ + * + * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.rollover; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.MouseEvent; + +/** + * Tree-specific implementation of RolloverProducer. + *

            + * This implementation assumes a "hit" for rollover if the mouse is anywhere in + * the total width of the tree. Additionally, a pressed to the right (but + * outside of the label bounds) is re-dispatched as a pressed just inside the + * label bounds. This is a first go for #166-swingx. + *

            + * + * PENDING JW: bidi-compliance of pressed? + * + * @author Jeanette Winzenburg + */ +public class TreeRolloverProducer extends RolloverProducer { + + @Override + public void mousePressed(MouseEvent e) { + JTree tree = (JTree) e.getComponent(); + Point mousePoint = e.getPoint(); + int labelRow = tree.getRowForLocation(mousePoint.x, mousePoint.y); + // default selection + if (labelRow >= 0) + return; + int row = tree.getClosestRowForLocation(mousePoint.x, mousePoint.y); + Rectangle bounds = tree.getRowBounds(row); + if (bounds == null) { + row = -1; + } else { + if ((bounds.y + bounds.height < mousePoint.y) + || bounds.x > mousePoint.x) { + row = -1; + } + } + // no hit + if (row < 0) + return; + tree.dispatchEvent(new MouseEvent(tree, e.getID(), e.getWhen(), e + .getModifiers(), bounds.x + bounds.width - 2, mousePoint.y, e + .getClickCount(), e.isPopupTrigger(), e.getButton())); + } + + @Override + protected void updateRolloverPoint(JComponent component, Point mousePoint) { + JTree tree = (JTree) component; + int row = tree.getClosestRowForLocation(mousePoint.x, mousePoint.y); + Rectangle bounds = tree.getRowBounds(row); + if (bounds == null) { + row = -1; + } else { + if ((bounds.y + bounds.height < mousePoint.y) + || bounds.x > mousePoint.x) { + row = -1; + } + } + int col = row < 0 ? -1 : 0; + rollover.x = col; + rollover.y = row; + } + +} diff --git a/src/main/java/org/jdesktop/swingx/search/AbstractSearchable.java b/src/main/java/org/jdesktop/swingx/search/AbstractSearchable.java new file mode 100644 index 0000000000..f5dc73c7a0 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/search/AbstractSearchable.java @@ -0,0 +1,661 @@ +/* + * $Id: AbstractSearchable.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.search; + +import org.jdesktop.swingx.decorator.*; + +import javax.swing.*; +import java.awt.*; +import java.util.regex.MatchResult; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * An abstract implementation of Searchable supporting + * incremental search. + * + * Keeps internal state to represent the previous search result. + * For all methods taking a String as parameter: compiles the String + * to a Pattern as-is and routes to the central method taking a Pattern. + * + * + * @author Jeanette Winzenburg + */ +public abstract class AbstractSearchable implements Searchable { + + /** + * stores the result of the previous search. + */ + protected final SearchResult lastSearchResult = new SearchResult(); + + private AbstractHighlighter matchHighlighter; + + + /** key for client property to use SearchHighlighter as match marker. */ + public static final String MATCH_HIGHLIGHTER = "match.highlighter"; + + /** + * Performs a forward search starting at the beginning + * across the Searchable using String that represents a + * regex pattern; {@link Pattern}. + * + * @param searchString String that we will try to locate + * @return the position of the match in appropriate coordinates or -1 if + * no match found. + */ + @Override + public int search(String searchString) { + return search(searchString, -1); + } + + /** + * Performs a forward search starting at the given startIndex + * using String that represents a regex + * pattern; {@link Pattern}. + * + * @param searchString String that we will try to locate + * @param startIndex position in the document in the appropriate coordinates + * from which we will start search or -1 to start from the beginning + * @return the position of the match in appropriate coordinates or -1 if + * no match found. + */ + @Override + public int search(String searchString, int startIndex) { + return search(searchString, startIndex, false); + } + + /** + * Performs a search starting at the given startIndex + * using String that represents a regex + * pattern; {@link Pattern}. The search direction + * depends on the boolean parameter: forward/backward if false/true, respectively. + * + * @param searchString String that we will try to locate + * @param startIndex position in the document in the appropriate coordinates + * from which we will start search or -1 to start from the beginning + * @param backward true if we should perform search towards the beginning + * @return the position of the match in appropriate coordinates or -1 if + * no match found. + */ + @Override + public int search(String searchString, int startIndex, boolean backward) { + Pattern pattern = null; + if (!isEmpty(searchString)) { + pattern = Pattern.compile(searchString, 0); + } + return search(pattern, startIndex, backward); + } + + /** + * Performs a forward search starting at the beginning + * across the Searchable using the pattern; {@link Pattern}. + * + * @param pattern Pattern that we will try to locate + * @return the position of the match in appropriate coordinates or -1 if + * no match found. + */ + @Override + public int search(Pattern pattern) { + return search(pattern, -1); + } + + /** + * Performs a forward search starting at the given startIndex + * using the Pattern; {@link Pattern}. + * + * @param pattern Pattern that we will try to locate + * @param startIndex position in the document in the appropriate coordinates + * from which we will start search or -1 to start from the beginning + * @return the position of the match in appropriate coordinates or -1 if + * no match found. + */ + @Override + public int search(Pattern pattern, int startIndex) { + return search(pattern, startIndex, false); + } + + /** + * Performs a search starting at the given startIndex + * using the pattern; {@link Pattern}. + * The search direction depends on the boolean parameter: + * forward/backward if false/true, respectively.

            + * + * Updates visible and internal search state. + * + * @param pattern Pattern that we will try to locate + * @param startIndex position in the document in the appropriate coordinates + * from which we will start search or -1 to start from the beginning + * @param backwards true if we should perform search towards the beginning + * @return the position of the match in appropriate coordinates or -1 if + * no match found. + */ + @Override + public int search(Pattern pattern, int startIndex, boolean backwards) { + int matchingRow = doSearch(pattern, startIndex, backwards); + moveMatchMarker(); + return matchingRow; + } + + /** + * Performs a search starting at the given startIndex + * using the pattern; {@link Pattern}. + * The search direction depends on the boolean parameter: + * forward/backward if false/true, respectively.

            + * + * Updates internal search state. + * + * @param pattern Pattern that we will try to locate + * @param startIndex position in the document in the appropriate coordinates + * from which we will start search or -1 to start from the beginning + * @param backwards true if we should perform search towards the beginning + * @return the position of the match in appropriate coordinates or -1 if + * no match found. + */ + protected int doSearch(Pattern pattern, final int startIndex, boolean backwards) { + if (isTrivialNoMatch(pattern, startIndex)) { + updateState(null); + return lastSearchResult.foundRow; + } + + int startRow; + if (isEqualStartIndex(startIndex)) { // implies: the last found coordinates are valid + if (!isEqualPattern(pattern)) { + SearchResult searchResult = findExtendedMatch(pattern, startIndex); + if (searchResult != null) { + updateState(searchResult); + return lastSearchResult.foundRow; + } + + } + // didn't find a match, make sure to move the startPosition + // for looking for the next/previous match + startRow = moveStartPosition(startIndex, backwards); + + } else { + // startIndex is different from last search, reset the column to -1 + // and make sure a -1 startIndex is mapped to first/last row, respectively. + startRow = adjustStartPosition(startIndex, backwards); + } + findMatchAndUpdateState(pattern, startRow, backwards); + return lastSearchResult.foundRow; + } + + /** + * Loops through the searchable until a match is found or the + * end is reached. Updates internal search state. + * + * @param pattern Pattern that we will try to locate + * @param startRow position in the document in the appropriate coordinates + * from which we will start search or -1 to start from the beginning + * @param backwards true if we should perform search towards the beginning + */ + protected abstract void findMatchAndUpdateState(Pattern pattern, int startRow, boolean backwards); + + /** + * Returns a boolean indicating if it can be trivially decided to not match. + *

            + * + * This implementation returns true if pattern is null or startIndex + * exceeds the upper size limit.

            + * + * @param pattern Pattern that we will try to locate + * @param startIndex position in the document in the appropriate coordinates + * from which we will start search or -1 to start from the beginning + * @return true if we can say ahead that no match will be found with given search criteria + */ + protected boolean isTrivialNoMatch(Pattern pattern, final int startIndex) { + return (pattern == null) || (startIndex >= getSize()); + } + + /** + * Called if startIndex is different from last search + * and make sure a backwards/forwards search starts at last/first row, + * respectively.

            + * + * @param startIndex position in the document in the appropriate coordinates + * from which we will start search or -1 to start from the beginning + * @param backwards true if we should perform search from towards the beginning + * @return adjusted startIndex + */ + protected int adjustStartPosition(int startIndex, boolean backwards) { + if (startIndex < 0) { + if (backwards) { + return getSize() - 1; + } else { + return 0; + } + } + return startIndex; + } + + /** + * Moves the internal start position for matching as appropriate and returns + * the new startIndex to use. Called if search was messaged with the same + * startIndex as previously. + *

            + * + * This implementation returns a by 1 decremented/incremented startIndex + * depending on backwards true/false, respectively. + * + * @param startIndex position in the document in the appropriate coordinates + * from which we will start search or -1 to start from the beginning + * @param backwards true if we should perform search towards the beginning + * @return adjusted startIndex + */ + protected int moveStartPosition(int startIndex, boolean backwards) { + if (backwards) { + startIndex--; + } else { + startIndex++; + } + return startIndex; + } + + + /** + * Checks if the given Pattern should be considered as the same as + * in a previous search. + *

            + * This implementation compares the patterns' regex. + * + * @param pattern Pattern that we will compare with last request + * @return if provided Pattern is the same as the stored from + * the previous search attempt + */ + protected boolean isEqualPattern(Pattern pattern) { + return pattern.pattern().equals(lastSearchResult.getRegEx()); + } + + /** + * Checks if the startIndex should be considered as the same as in + * the previous search. + * + * @param startIndex startIndex that we will compare with the index + * stored by the previous search request + * @return true if the startIndex should be re-matched, false if not. + */ + protected boolean isEqualStartIndex(final int startIndex) { + return isValidIndex(startIndex) && (startIndex == lastSearchResult.foundRow); + } + + /** + * Checks if the searchString should be interpreted as empty. + *

            + * This implementation returns true if string is null or has zero length. + * + * @param searchString String that we should evaluate + * @return true if the provided String should be interpreted as empty + */ + protected boolean isEmpty(String searchString) { + return (searchString == null) || searchString.length() == 0; + } + + + /** + * Matches the cell at row/lastFoundColumn against the pattern. + * Called if sameRowIndex && !hasEqualRegEx. + * PRE: lastFoundColumn valid. + * + * @param pattern Pattern that we will try to match + * @param row position at which we will get the value to match with the provided Pattern + * @return result of the match; {@link SearchResult} + */ + protected abstract SearchResult findExtendedMatch(Pattern pattern, int row); + + /** + * Factory method to create a SearchResult from the given parameters. + * + * @param matcher the matcher after a successful find. Must not be null. + * @param row the found index + * @param column the found column + * @return newly created SearchResult + */ + protected SearchResult createSearchResult(Matcher matcher, int row, int column) { + return new SearchResult(matcher.pattern(), + matcher.toMatchResult(), row, column); + } + + /** + * Checks if index is in range: 0 <= index < getSize(). + * + * @param index possible start position that we will check for validity + * @return true if given parameter is valid index + */ + protected boolean isValidIndex(int index) { + return index >= 0 && index < getSize(); + } + + /** + * Returns the size of this searchable. + * + * @return size of this searchable + */ + protected abstract int getSize(); + + /** + * Updates inner searchable state based on provided search result + * + * @param searchResult SearchResult that represents the new state + * of this AbstractSearchable + */ + protected void updateState(SearchResult searchResult) { + lastSearchResult.updateFrom(searchResult); + } + + /** + * Moves the match marker according to current found state. + */ + protected abstract void moveMatchMarker(); + + /** + * It's the responsibility of subclasses to covariant override. + * + * @return the target component + */ + public abstract JComponent getTarget(); + + /** + * Removes the highlighter. + * + * @param searchHighlighter the Highlighter to remove. + */ + protected abstract void removeHighlighter(Highlighter searchHighlighter); + + /** + * Returns the highlighters registered on the search target. + * + * @return all registered highlighters + */ + protected abstract Highlighter[] getHighlighters(); + + /** + * Adds the highlighter to the target. + * + * @param highlighter the Highlighter to add. + */ + protected abstract void addHighlighter(Highlighter highlighter); + + /** + * Ensure that the given Highlighter is the last in the list of + * the highlighters registered on the target. + * + * @param highlighter the Highlighter to be inserted as last. + */ + protected void ensureInsertedSearchHighlighters(Highlighter highlighter) { + if (!isInPipeline(highlighter)) { + addHighlighter(highlighter); + } + } + + /** + * Returns a flag indicating if the given highlighter is last in the + * list of highlighters registered on the target. If so returns true. + * If not, it has the side-effect of removing the highlighter and returns false. + * + * @param searchHighlighter the highlighter to check for being last + * @return a boolean indicating whether the highlighter is last. + */ + private boolean isInPipeline(Highlighter searchHighlighter) { + Highlighter[] inPipeline = getHighlighters(); + if ((inPipeline.length > 0) && + (searchHighlighter.equals(inPipeline[inPipeline.length -1]))) { + return true; + } + removeHighlighter(searchHighlighter); + return false; + } + + /** + * Converts and returns the given column index from view coordinates to model + * coordinates. + *

            + * This implementation returns the view coordinate, that is assumes + * that both coordinate systems are the same. + * + * @param viewColumn the column index in view coordinates, must be a valid index + * in that system. + * @return the column index in model coordinates. + */ + protected int convertColumnIndexToModel(int viewColumn) { + return viewColumn; + } + + /** + * + * @param result + * @return {@code true} if the {@code result} contains a match; + * {@code false} otherwise + */ + private boolean hasMatch(SearchResult result) { + boolean noMatch = (result.getFoundRow() < 0) || (result.getFoundColumn() < 0); + return !noMatch; + } + + /** + * Returns a boolean indicating whether the current search result is a match. + *

            + * PENDING JW: move to SearchResult? + * @return a boolean indicating whether the current search result is a match. + */ + protected boolean hasMatch() { + return hasMatch(lastSearchResult); + } + + /** + * Returns a boolean indicating whether a match should be marked with a + * Highlighter. Typically, if true, the match highlighter is used, otherwise + * a match is indicated by selection. + *

            + * + * This implementation returns true if the target component has a client + * property for key MATCH_HIGHLIGHTER with value Boolean.TRUE, false + * otherwise. The SearchFactory sets that client property in incremental + * search mode, that is when triggering a search via the JXFindBar as + * installed by the factory. + * + * @return a boolean indicating whether a match should be marked by a using + * a Highlighter. + * + * @see SearchFactory + */ + protected boolean markByHighlighter() { + return Boolean.TRUE.equals(getTarget().getClientProperty( + MATCH_HIGHLIGHTER)); + } + + /** + * Sets the AbstractHighlighter to use as match marker, if enabled. A null value + * will re-install the default. + * + * @param hl the Highlighter to use as match marker. + */ + public void setMatchHighlighter(AbstractHighlighter hl) { + removeHighlighter(matchHighlighter); + matchHighlighter = hl; + if (markByHighlighter()) { + moveMatchMarker(); + } + } + + /** + * Returns the Hihglighter to use as match marker, lazyly created if null. + * + * @return a highlighter used for matching, guaranteed to be not null. + */ + protected AbstractHighlighter getMatchHighlighter() { + if (matchHighlighter == null) { + matchHighlighter = createMatchHighlighter(); + } + return matchHighlighter; + } + + /** + * Creates and returns the Highlighter used as match marker. + * + * @return a highlighter used for matching + */ + protected AbstractHighlighter createMatchHighlighter() { + return new ColorHighlighter(HighlightPredicate.NEVER, Color.YELLOW.brighter(), + null, Color.YELLOW.brighter(), + null); + } + + + /** + * Configures and returns the match highlighter for the current match. + * + * @return a highlighter configured for matching + */ + protected AbstractHighlighter getConfiguredMatchHighlighter() { + AbstractHighlighter searchHL = getMatchHighlighter(); + searchHL.setHighlightPredicate(createMatchPredicate()); + return searchHL; + } + + /** + * Creates and returns a HighlightPredicate appropriate for the current + * search result. + * + * @return a HighlightPredicate appropriate for the current search result. + */ + protected HighlightPredicate createMatchPredicate() { + return hasMatch() ? + new SearchPredicate(lastSearchResult.pattern, lastSearchResult.foundRow, + convertColumnIndexToModel(lastSearchResult.foundColumn)) + : HighlightPredicate.NEVER; + } + + /** + * A convenience class to hold search state.

            + * + * NOTE: this is still in-flow, probably will take more responsibility/ + * or even change altogether on further factoring + */ + public static class SearchResult { + int foundRow; + int foundColumn; + MatchResult matchResult; + Pattern pattern; + + /** + * Instantiates an empty SearchResult. + */ + public SearchResult() { + reset(); + } + + /** + * Instantiates a SearchResult with the given state. + * + * @param ex the Pattern used for matching + * @param result the current MatchResult + * @param row the row index of the current match + * @param column the column index of the current match + */ + public SearchResult(Pattern ex, MatchResult result, int row, int column) { + pattern = ex; + matchResult = result; + foundRow = row; + foundColumn = column; + } + + /** + * Sets internal state to the same as the given SearchResult. Resets internals + * if the param is null. + * + * @param searchResult the SearchResult to copy internal state from. + */ + public void updateFrom(SearchResult searchResult) { + if (searchResult == null) { + reset(); + return; + } + foundRow = searchResult.foundRow; + foundColumn = searchResult.foundColumn; + matchResult = searchResult.matchResult; + pattern = searchResult.pattern; + } + + /** + * Returns the regex of the Pattern used for matching. + * + * @return the regex of the Pattern used for matching. + */ + public String getRegEx() { + return pattern != null ? pattern.pattern() : null; + } + + /** + * Resets all internal state to no-match. + */ + public void reset() { + foundRow= -1; + foundColumn = -1; + matchResult = null; + pattern = null; + } + + /** + * Resets the column to OFF. + */ + public void resetFoundColumn() { + foundColumn = -1; + } + + /** + * Returns the column index of the match position. + * + * @return the column index of the match position. + */ + public int getFoundColumn() { + return foundColumn; + } + + /** + * Returns the row index of the match position. + * + * @return the row index of the match position. + */ + public int getFoundRow() { + return foundRow; + } + + /** + * Returns the MatchResult representing the current match. + * + * @return the MatchResult representing the current match. + */ + public MatchResult getMatchResult() { + return matchResult; + } + + /** + * Returns the Pattern used for matching. + * + * @return the Pattern used for the matching. + */ + public Pattern getPattern() { + return pattern; + } + + } + +} diff --git a/src/main/java/org/jdesktop/swingx/search/ListSearchable.java b/src/main/java/org/jdesktop/swingx/search/ListSearchable.java new file mode 100644 index 0000000000..44c6debf2c --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/search/ListSearchable.java @@ -0,0 +1,162 @@ +/* + * $Id: ListSearchable.java 3166 2009-01-02 13:27:18Z rah003 $ + * + * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.search; + +import org.jdesktop.swingx.JXList; +import org.jdesktop.swingx.decorator.AbstractHighlighter; +import org.jdesktop.swingx.decorator.Highlighter; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ListSearchable extends AbstractSearchable { + + protected JXList list; + + public ListSearchable(JXList list) { + this.list = list; + } + + @Override + protected void findMatchAndUpdateState(Pattern pattern, int startRow, boolean backwards) { + SearchResult searchResult = null; + if (backwards) { + for (int index = startRow; index >= 0 && searchResult == null; index--) { + searchResult = findMatchAt(pattern, index); + } + } else { + for (int index = startRow; index < getSize() && searchResult == null; index++) { + searchResult = findMatchAt(pattern, index); + } + } + updateState(searchResult); + } + + @Override + protected SearchResult findExtendedMatch(Pattern pattern, int row) { + + return findMatchAt(pattern, row); + } + /** + * Matches the cell content at row/col against the given Pattern. + * Returns an appropriate SearchResult if matching or null if no + * matching + * + * @param pattern + * @param row a valid row index in view coordinates + * @return SearchResult if matched otherwise null + */ + protected SearchResult findMatchAt(Pattern pattern, int row) { + String text = list.getStringAt(row); + if ((text != null) && (text.length() > 0 )) { + Matcher matcher = pattern.matcher(text); + if (matcher.find()) { + return createSearchResult(matcher, row, 0); + } + } + return null; + } + + /** + * {@inheritDoc} + */ + @Override + protected int getSize() { + return list.getElementCount(); + } + + + /** + * {@inheritDoc} + */ + @Override + public JXList getTarget() { + return list; + } + + /** + * {@inheritDoc} + */ + @Override + protected void moveMatchMarker() { + if (markByHighlighter()) { + moveMatchByHighlighter(); + } else { // use selection + moveMatchBySelection(); + } + } + + protected void moveMatchBySelection() { + // PENDING JW: #718-swingx - don't move selection on not found + // complying here is accidental, defaultListSelectionModel doesn't + // clear on -1 but silently does nothing + // isn't doc'ed anywhere - so we back out + if (!hasMatch()) { + return; + } + list.setSelectedIndex(lastSearchResult.foundRow); + list.ensureIndexIsVisible(lastSearchResult.foundRow); + } + + + /** + * use and move the match highlighter. + * PRE: markByHighlighter + * + */ + protected void moveMatchByHighlighter() { + AbstractHighlighter searchHL = getConfiguredMatchHighlighter(); + // no match + if (!hasMatch()) { + return; + } else { + ensureInsertedSearchHighlighters(searchHL); + list.ensureIndexIsVisible(lastSearchResult.foundRow); + } + } + + + + /** + * @param searchHighlighter + */ + @Override + protected void removeHighlighter(Highlighter searchHighlighter) { + list.removeHighlighter(searchHighlighter); + } + + /** + * @return all registered highlighters + */ + @Override + protected Highlighter[] getHighlighters() { + return list.getHighlighters(); + } + + /** + * @param highlighter + */ + @Override + protected void addHighlighter(Highlighter highlighter) { + list.addHighlighter(highlighter); + } + + } \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/search/NativeSearchFieldSupport.java b/src/main/java/org/jdesktop/swingx/search/NativeSearchFieldSupport.java new file mode 100644 index 0000000000..1ba8e921f9 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/search/NativeSearchFieldSupport.java @@ -0,0 +1,111 @@ +package org.jdesktop.swingx.search; + +import org.jdesktop.swingx.plaf.AbstractUIChangeHandler; +import org.jdesktop.swingx.util.OS; + +import javax.swing.*; +import java.awt.event.ActionListener; +import java.beans.PropertyChangeEvent; + +/** + * TODO: comment + * + * @author Peter Weishapl + */ +public class NativeSearchFieldSupport { + public static final String FIND_POPUP_PROPERTY = "JTextField.Search.FindPopup"; + public static final String FIND_ACTION_PROPERTY = "JTextField.Search.FindAction"; + public static final String MAC_SEARCH_VARIANT = "search"; + public static final String MAC_TEXT_FIELD_VARIANT_PROPERTY = "JTextField.variant"; + public static final String CANCEL_ACTION_PROPERTY = "JTextField.Search.CancelAction"; + + /** + * @return true if we run Leopard and the Mac Look And Feel. + */ + public static boolean isNativeSearchFieldSupported() { + try { + String versionString = System.getProperty("os.version"); + // Mac versions have the format 10.x or 10.x.x + if (versionString.length() < 4) { + return false; + } + // only the part 10.x is important + versionString = versionString.substring(0, 4); + + return OS.isMacOSX() && Float.parseFloat(versionString) >= 10.5 + && UIManager.getLookAndFeel().getName().equals("Mac OS X"); + } catch (Exception e) { + // in case the os.version cannot be parsed, we are surely not + // running mac os x. + return false; + } + } + + public static void setSearchField(JTextField txt, boolean isSearchField) { + // Leopard Hack: ensure property change event is triggered, if nothing + // changes. + if (isSearchField == isSearchField(txt)) { + txt.putClientProperty(MAC_TEXT_FIELD_VARIANT_PROPERTY, "_triggerevent_"); + } else if (isSearchField) { + // if we have a search field here, register listener for ui changes + // (leopard hack) + uiChangeHandler.install(txt); + } else { + // if we don't have a search field, we don't need to listen anymore. + uiChangeHandler.uninstall(txt); + } + + if (isSearchField) { + txt.putClientProperty(MAC_TEXT_FIELD_VARIANT_PROPERTY, MAC_SEARCH_VARIANT); + txt.putClientProperty("Quaqua.TextField.style", MAC_SEARCH_VARIANT); + + } else { + txt.putClientProperty(MAC_TEXT_FIELD_VARIANT_PROPERTY, "default"); + txt.putClientProperty("Quaqua.TextField.style", "default"); + } + } + + public static boolean isSearchField(JTextField txt) { + return MAC_SEARCH_VARIANT.equals(txt.getClientProperty(MAC_TEXT_FIELD_VARIANT_PROPERTY)); + } + + public static boolean isNativeSearchField(JTextField txt) { + return isSearchField(txt) && isNativeSearchFieldSupported(); + } + + public static void setFindPopupMenu(JTextField txt, JPopupMenu popupMenu) { + txt.putClientProperty(FIND_POPUP_PROPERTY, popupMenu); + } + + public static JPopupMenu getFindPopupMenu(JTextField txt) { + return (JPopupMenu) txt.getClientProperty(FIND_POPUP_PROPERTY); + } + + public static void setFindAction(JTextField txt, ActionListener findAction) { + txt.putClientProperty(FIND_ACTION_PROPERTY, findAction); + } + + public static ActionListener getFindAction(JTextField txt) { + return (ActionListener) txt.getClientProperty(FIND_ACTION_PROPERTY); + } + + public static void setCancelAction(JTextField txt, ActionListener cancelAction) { + txt.putClientProperty(CANCEL_ACTION_PROPERTY, cancelAction); + } + + public static ActionListener getCancelAction(JTextField txt) { + return (ActionListener) txt.getClientProperty(CANCEL_ACTION_PROPERTY); + } + + private static final SearchFieldUIChangeHandler uiChangeHandler = new SearchFieldUIChangeHandler(); + + private static final class SearchFieldUIChangeHandler extends AbstractUIChangeHandler { + @Override + public void propertyChange(PropertyChangeEvent evt) { + JTextField txt = (JTextField) evt.getSource(); + // Leopard hack to make appear correctly in search variant when + // changing LnF. + setSearchField(txt, isSearchField(txt)); + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/search/PatternMatcher.java b/src/main/java/org/jdesktop/swingx/search/PatternMatcher.java new file mode 100644 index 0000000000..11ed7c6f1e --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/search/PatternMatcher.java @@ -0,0 +1,34 @@ +/* + * $Id$ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.search; + +import java.util.regex.Pattern; + +/** + * Implemented by classes that work with {@link Pattern} objects. + + * @author Ramesh Gupta + */ +public interface PatternMatcher { + public Pattern getPattern(); + public void setPattern(Pattern pattern); +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/search/PatternModel.java b/src/main/java/org/jdesktop/swingx/search/PatternModel.java new file mode 100644 index 0000000000..dca374874a --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/search/PatternModel.java @@ -0,0 +1,620 @@ +/* + * $Id: PatternModel.java 3472 2009-08-27 13:12:42Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.search; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.regex.Pattern; + +/** + * Presentation Model for Find/Filter Widgets. + *

            + * + * Compiles and holds a Pattern from rawText. There are different + * predefined strategies to control the compilation: + * + *

              + *
            • TODO: list and explain + *
            + * + * Holds state for controlling the match process + * for both find and filter (TODO - explain). + * Relevant in all + * + *
              + *
            • caseSensitive - + *
            • empty - true if there's no searchString + *
            • incremental - a hint to clients to react immediately + * to pattern changes. + * + *
            + * + * Relevant in find contexts: + *
              + *
            • backwards - search direction if used in a find context + *
            • wrapping - wrap over the end/start if not found + *
            • foundIndex - storage for last found index + *
            • autoAdjustFoundIndex - flag to indicate auto-incr/decr of foundIndex on setting. + * Here the property correlates to !isIncremental() - to simplify batch vs. + * incremental search ui. + *
            + * + * + * JW: Work-in-progress - Anchors will be factored into AnchoredSearchMode + * Anchors By default, the scope of the pattern relative to strings + * being tested are unanchored, ie, the pattern will match any part of the + * tested string. Traditionally, special characters ('^' and '$') are used to + * describe patterns that match the beginning (or end) of a string. If those + * characters are included in the pattern, the regular expression will honor + * them. However, for ease of use, two properties are included in this model + * that will determine how the pattern will be evaluated when these characters + * are omitted. + *

            + * The StartAnchored property determines if the pattern must match from + * the beginning of tested strings, or if the pattern can appear anywhere in the + * tested string. Likewise, the EndAnchored property determines if the + * pattern must match to the end of the tested string, or if the end of the + * pattern can appear anywhere in the tested string. The default values (false + * in both cases) correspond to the common database 'LIKE' operation, where the + * pattern is considered to be a match if any part of the tested string matches + * the pattern. + * + * @author Jeanette Winzenburg + * @author David Hall + */ +public class PatternModel { + + /** + * The prefix marker to find component related properties in the + * resourcebundle. + */ + public static final String SEARCH_PREFIX = "Search."; + + /* + * TODO: use Enum for strategy. + */ + public static final String REGEX_UNCHANGED = "regex"; + + public static final String REGEX_ANCHORED = "anchored"; + + public static final String REGEX_WILDCARD = "wildcard"; + + public static final String REGEX_MATCH_RULES = "explicit"; + + /* + * TODO: use Enum for rules. + */ + public static final String MATCH_RULE_CONTAINS = "contains"; + + public static final String MATCH_RULE_EQUALS = "equals"; + + public static final String MATCH_RULE_ENDSWITH = "endsWith"; + + public static final String MATCH_RULE_STARTSWITH = "startsWith"; + + public static final String MATCH_BACKWARDS_ACTION_COMMAND = "backwardsSearch"; + + public static final String MATCH_WRAP_ACTION_COMMAND = "wrapSearch"; + + public static final String MATCH_CASE_ACTION_COMMAND = "matchCase"; + + public static final String MATCH_INCREMENTAL_ACTION_COMMAND = "matchIncremental"; + + + private String rawText; + + private boolean backwards; + + private Pattern pattern; + + private int foundIndex = -1; + + private boolean caseSensitive; + + private PropertyChangeSupport propertySupport; + + private String regexCreatorKey; + + private RegexCreator regexCreator; + + private boolean wrapping; + + private boolean incremental; + + +//---------------------- misc. properties not directly related to Pattern. + + public int getFoundIndex() { + return foundIndex; + } + + public void setFoundIndex(int foundIndex) { + int old = getFoundIndex(); + updateFoundIndex(foundIndex); + firePropertyChange("foundIndex", old, getFoundIndex()); + } + + /** + * + * @param newFoundIndex + */ + protected void updateFoundIndex(int newFoundIndex) { + if (newFoundIndex < 0) { + this.foundIndex = newFoundIndex; + return; + } + if (isAutoAdjustFoundIndex()) { + foundIndex = backwards ? newFoundIndex -1 : newFoundIndex + 1; + } else { + foundIndex = newFoundIndex; + } + + } + + public boolean isAutoAdjustFoundIndex() { + return !isIncremental(); + } + + public boolean isBackwards() { + return backwards; + } + + public void setBackwards(boolean backwards) { + boolean old = isBackwards(); + this.backwards = backwards; + firePropertyChange("backwards", old, isBackwards()); + setFoundIndex(getFoundIndex()); + } + + public boolean isWrapping() { + return wrapping; + } + + public void setWrapping(boolean wrapping) { + boolean old = isWrapping(); + this.wrapping = wrapping; + firePropertyChange("wrapping", old, isWrapping()); + } + + public void setIncremental(boolean incremental) { + boolean old = isIncremental(); + this.incremental = incremental; + firePropertyChange("incremental", old, isIncremental()); + } + + public boolean isIncremental() { + return incremental; + } + + + public boolean isCaseSensitive() { + return caseSensitive; + } + + public void setCaseSensitive(boolean caseSensitive) { + boolean old = isCaseSensitive(); + this.caseSensitive = caseSensitive; + updatePattern(caseSensitive); + firePropertyChange("caseSensitive", old, isCaseSensitive()); + } + + public Pattern getPattern() { + return pattern; + } + + public String getRawText() { + return rawText; + } + + public void setRawText(String findText) { + String old = getRawText(); + boolean oldEmpty = isEmpty(); + this.rawText = findText; + updatePattern(createRegEx(findText)); + firePropertyChange("rawText", old, getRawText()); + firePropertyChange("empty", oldEmpty, isEmpty()); + } + + public boolean isEmpty() { + return isEmpty(getRawText()); + } + + /** + * returns a regEx for compilation into a pattern. Here: either a "contains" + * (== partial find) or null if the input was empty. + * + * @param searchString + * @return null if the input was empty, or a regex according to the internal + * rules + */ + private String createRegEx(String searchString) { + if (isEmpty(searchString)) + return null; //".*"; + return getRegexCreator().createRegEx(searchString); + } + + /** + * + * @param s + * @return + */ + + private boolean isEmpty(String text) { + return (text == null) || (text.length() == 0); + } + + private void updatePattern(String regEx) { + Pattern old = getPattern(); + if (isEmpty(regEx)) { + pattern = null; + } else if ((old == null) || (!old.pattern().equals(regEx))) { + pattern = Pattern.compile(regEx, getFlags()); + } + firePropertyChange("pattern", old, getPattern()); + } + + private int getFlags() { + return isCaseSensitive() ? 0 : getCaseInsensitiveFlag(); + } + + private int getCaseInsensitiveFlag() { + return Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE; + } + + private void updatePattern(boolean caseSensitive) { + if (pattern == null) + return; + Pattern old = getPattern(); + int flags = old.flags(); + int flag = getCaseInsensitiveFlag(); + if ((caseSensitive) && ((flags & flag) != 0)) { + pattern = Pattern.compile(pattern.pattern(), 0); + } else if (!caseSensitive && ((flags & flag) == 0)) { + pattern = Pattern.compile(pattern.pattern(), flag); + } + firePropertyChange("pattern", old, getPattern()); + } + + public void addPropertyChangeListener(PropertyChangeListener l) { + if (propertySupport == null) { + propertySupport = new PropertyChangeSupport(this); + } + propertySupport.addPropertyChangeListener(l); + } + + public void removePropertyChangeListener(PropertyChangeListener l) { + if (propertySupport == null) + return; + propertySupport.removePropertyChangeListener(l); + } + + protected void firePropertyChange(String name, Object oldValue, + Object newValue) { + if (propertySupport == null) + return; + propertySupport.firePropertyChange(name, oldValue, newValue); + } + + /** + * Responsible for converting a "raw text" into a valid + * regular expression in the context of a set of rules. + * + */ + public static class RegexCreator { + protected String matchRule; + private List rules; + + public String getMatchRule() { + if (matchRule == null) { + matchRule = getDefaultMatchRule(); + } + return matchRule; + } + + public boolean isAutoDetect() { + return false; + } + + public String createRegEx(String searchString) { + if (MATCH_RULE_CONTAINS.equals(getMatchRule())) { + return createContainedRegEx(searchString); + } + if (MATCH_RULE_EQUALS.equals(getMatchRule())) { + return createEqualsRegEx(searchString); + } + if (MATCH_RULE_STARTSWITH.equals(getMatchRule())){ + return createStartsAnchoredRegEx(searchString); + } + if (MATCH_RULE_ENDSWITH.equals(getMatchRule())) { + return createEndAnchoredRegEx(searchString); + } + return searchString; + } + + protected String createEndAnchoredRegEx(String searchString) { + return Pattern.quote(searchString) + "$"; + } + + protected String createStartsAnchoredRegEx(String searchString) { + return "^" + Pattern.quote(searchString); + } + + protected String createEqualsRegEx(String searchString) { + return "^" + Pattern.quote(searchString) + "$"; + } + + protected String createContainedRegEx(String searchString) { + return Pattern.quote(searchString); + } + + public void setMatchRule(String category) { + this.matchRule = category; + } + + protected String getDefaultMatchRule() { + return MATCH_RULE_CONTAINS; + } + + public List getMatchRules() { + if (rules == null) { + rules = createAndInitRules(); + } + return rules; + } + + private List createAndInitRules() { + if (!supportsRules()) return Collections.emptyList(); + List list = new ArrayList(); + list.add(MATCH_RULE_CONTAINS); + list.add(MATCH_RULE_EQUALS); + list.add(MATCH_RULE_STARTSWITH); + list.add(MATCH_RULE_ENDSWITH); + return list; + } + + private boolean supportsRules() { + return true; + } + } + + + /** + * Support for anchored input. + * + * PENDING: NOT TESTED - simply moved! + * Need to define requirements... + * + */ + public static class AnchoredSearchMode extends RegexCreator { + + @Override + public boolean isAutoDetect() { + return true; + } + + @Override + public String createRegEx(String searchExp) { + if (isAutoDetect()) { + StringBuffer buf = new StringBuffer(searchExp.length() + 4); + if (!hasStartAnchor(searchExp)) { + if (isStartAnchored()) { + buf.append("^"); + } + } + + //PENDING: doesn't escape contained regex metacharacters... + buf.append(searchExp); + + if (!hasEndAnchor(searchExp)) { + if (isEndAnchored()) { + buf.append("$"); + } + } + + return buf.toString(); + } + return super.createRegEx(searchExp); + } + + private boolean hasStartAnchor(String str) { + return str.startsWith("^"); + } + + private boolean hasEndAnchor(String str) { + int len = str.length(); + if ((str.charAt(len - 1)) != '$') + return false; + + // the string "$" is anchored + if (len == 1) + return true; + + // scan backwards along the string: if there's an odd number + // of backslashes, then the last escapes the dollar and the + // pattern is not anchored. if there's an even number, then + // the dollar is unescaped and the pattern is anchored. + for (int n = len - 2; n >= 0; --n) + if (str.charAt(n) != '\\') + return (len - n) % 2 == 0; + + // The string is of the form "\+$". If the length is an odd + // number (ie, an even number of '\' and a '$') the pattern is + // anchored + return len % 2 != 0; + } + + + /** + * returns true if the pattern must match from the beginning of the string, + * or false if the pattern can match anywhere in a string. + */ + public boolean isStartAnchored() { + return MATCH_RULE_EQUALS.equals(getMatchRule()) || + MATCH_RULE_STARTSWITH.equals(getMatchRule()); + } + // +// /** +// * sets the default interpretation of the pattern for strings it will later +// * be given. Setting this value to true will force the pattern to match from +// * the beginning of tested strings. Setting this value to false will allow +// * the pattern to match any part of a tested string. +// */ +// public void setStartAnchored(boolean startAnchored) { +// boolean old = isStartAnchored(); +// this.startAnchored = startAnchored; +// updatePattern(createRegEx(getRawText())); +// firePropertyChange("startAnchored", old, isStartAnchored()); +// } + // + /** + * returns true if the pattern must match from the beginning of the string, + * or false if the pattern can match anywhere in a string. + */ + public boolean isEndAnchored() { + return MATCH_RULE_EQUALS.equals(getMatchRule()) || + MATCH_RULE_ENDSWITH.equals(getMatchRule()); + } + // +// /** +// * sets the default interpretation of the pattern for strings it will later +// * be given. Setting this value to true will force the pattern to match the +// * end of tested strings. Setting this value to false will allow the pattern +// * to match any part of a tested string. +// */ +// public void setEndAnchored(boolean endAnchored) { +// boolean old = isEndAnchored(); +// this.endAnchored = endAnchored; +// updatePattern(createRegEx(getRawText())); +// firePropertyChange("endAnchored", old, isEndAnchored()); +// } + // +// public boolean isStartEndAnchored() { +// return isEndAnchored() && isStartAnchored(); +// } +// +// /** +// * sets the default interpretation of the pattern for strings it will later +// * be given. Setting this value to true will force the pattern to match the +// * end of tested strings. Setting this value to false will allow the pattern +// * to match any part of a tested string. +// */ +// public void setStartEndAnchored(boolean endAnchored) { +// boolean old = isStartEndAnchored(); +// this.endAnchored = endAnchored; +// this.startAnchored = endAnchored; +// updatePattern(createRegEx(getRawText())); +// firePropertyChange("StartEndAnchored", old, isStartEndAnchored()); +// } + } + /** + * Set the strategy to use for compiling a pattern from + * rawtext. + * + * NOTE: This is imcomplete (in fact it wasn't implemented at + * all) - only recognizes REGEX_ANCHORED, every other value + * results in REGEX_MATCH_RULES. + * + * @param mode the String key of the match strategy to use. + */ + public void setRegexCreatorKey(String mode) { + if (getRegexCreatorKey().equals(mode)) return; + String old = getRegexCreatorKey(); + regexCreatorKey = mode; + createRegexCreator(getRegexCreatorKey()); + firePropertyChange("regexCreatorKey", old, getRegexCreatorKey()); + + } + + /** + * Creates and sets the strategy to use for compiling a pattern from + * rawtext. + * + * NOTE: This is imcomplete (in fact it wasn't implemented at + * all) - only recognizes REGEX_ANCHORED, every other value + * results in REGEX_MATCH_RULES. + * + * @param mode the String key of the match strategy to use. + */ + protected void createRegexCreator(String mode) { + if (REGEX_ANCHORED.equals(mode)) { + setRegexCreator(new AnchoredSearchMode()); + } else { + setRegexCreator(new RegexCreator()); + } + + } + + public String getRegexCreatorKey() { + if (regexCreatorKey == null) { + regexCreatorKey = getDefaultRegexCreatorKey(); + } + return regexCreatorKey; + } + + private String getDefaultRegexCreatorKey() { + return REGEX_MATCH_RULES; + } + + private RegexCreator getRegexCreator() { + if (regexCreator == null) { + regexCreator = new RegexCreator(); + } + return regexCreator; + } + + /** + * This is a quick-fix to allow custom strategies for compiling + * rawtext to patterns. + * + * @param regexCreator the strategy to use for compiling text + * into pattern. + */ + public void setRegexCreator(RegexCreator regexCreator) { + Object old = this.regexCreator; + this.regexCreator = regexCreator; + firePropertyChange("regexCreator", old, regexCreator); + } + + public void setMatchRule(String category) { + if (getMatchRule().equals(category)) { + return; + } + String old = getMatchRule(); + getRegexCreator().setMatchRule(category); + updatePattern(createRegEx(getRawText())); + firePropertyChange("matchRule", old, getMatchRule()); + } + + public String getMatchRule() { + return getRegexCreator().getMatchRule(); + } + + public List getMatchRules() { + return getRegexCreator().getMatchRules(); + } + + + + +} diff --git a/src/main/java/org/jdesktop/swingx/search/RecentSearches.java b/src/main/java/org/jdesktop/swingx/search/RecentSearches.java new file mode 100644 index 0000000000..ad6b06785e --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/search/RecentSearches.java @@ -0,0 +1,364 @@ +package org.jdesktop.swingx.search; + +import org.jdesktop.swingx.JXSearchField; +import org.jdesktop.swingx.plaf.UIManagerExt; + +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.security.AccessControlException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.logging.Logger; +import java.util.prefs.BackingStoreException; +import java.util.prefs.Preferences; + +/** + * Maintains a list of recent searches and persists this list automatically + * using {@link Preferences}. A recent searches popup menu can be installed on + * a {@link JXSearchField} using {@link #install(JXSearchField)}. + * + * @author Peter Weishapl + * + */ +public class RecentSearches implements ActionListener { + private Preferences prefsNode; + + private int maxRecents = 5; + + private List recentSearches = new ArrayList(); + + private List listeners = new ArrayList(); + + /** + * Creates a list of recent searches and uses saveName to + * persist this list under the {@link Preferences} user root node. Existing + * entries will be loaded automatically. + * + * @param saveName + * a unique name for saving this list of recent searches + */ + public RecentSearches(String saveName) { + this(null, saveName); + } + + /** + * Creates a list of recent searches and uses saveName to + * persist this list under the prefs node. Existing entries + * will be loaded automatically. + * + * @param prefsNode + * the preferences node under which this list will be persisted. + * If prefsNode is null the preferences node will + * be set to the user root node + * @param saveName + * a unique name for saving this list of recent searches. If + * saveName is null, the list will not be + * persisted + */ + public RecentSearches(Preferences prefs, String saveName) { + if (prefs == null) { + try { + prefs = Preferences.userRoot(); + } catch (AccessControlException ace) { + // disable persistency, if we aren't allowed to access + // preferences. + Logger.getLogger(getClass().getName()).warning("cannot acces preferences. persistency disabled."); + } + } + + if (prefs != null && saveName != null) { + this.prefsNode = prefs.node(saveName); + load(); + } + } + + private void load() { + // load persisted entries + try { + String[] recent = new String[prefsNode.keys().length]; + for (String key : prefsNode.keys()) { + recent[prefsNode.getInt(key, -1)] = key; + } + recentSearches.addAll(Arrays.asList(recent)); + } catch (Exception ex) { + // ignore + } + } + + private void save() { + if (prefsNode == null) { + return; + } + + try { + prefsNode.clear(); + } catch (BackingStoreException e) { + // ignore + } + + int i = 0; + for (String search : recentSearches) { + prefsNode.putInt(search, i++); + } + } + + /** + * Add a search string as the first element. If the search string is + * null or empty nothing will be added. If the search string + * already exists, the old element will be removed. The modified list will + * automatically be persisted. + * + * If the number of elements exceeds the maximum number of entries, the last + * entry will be removed. + * + * @see #getMaxRecents() + * @param searchString + * the search string to add + */ + public void put(String searchString) { + if (searchString == null || searchString.trim().length() == 0) { + return; + } + + int lastIndex = recentSearches.indexOf(searchString); + if (lastIndex != -1) { + recentSearches.remove(lastIndex); + } + recentSearches.add(0, searchString); + if (getLength() > getMaxRecents()) { + recentSearches.remove(recentSearches.size() - 1); + } + save(); + fireChangeEvent(); + } + + /** + * Returns all recent searches in this list. + * + * @return the recent searches + */ + public String[] getRecentSearches() { + return recentSearches.toArray(new String[] {}); + } + + /** + * The number of recent searches. + * + * @return number of recent searches + */ + public int getLength() { + return recentSearches.size(); + } + + /** + * Remove all recent searches. + */ + public void removeAll() { + recentSearches.clear(); + save(); + fireChangeEvent(); + } + + /** + * Returns the maximum number of recent searches. + * + * @see #put(String) + * @return the maximum number of recent searches + */ + public int getMaxRecents() { + return maxRecents; + } + + /** + * Set the maximum number of recent searches. + * + * @see #put(String) + * @param maxRecents + * maximum number of recent searches + */ + public void setMaxRecents(int maxRecents) { + this.maxRecents = maxRecents; + } + + /** + * Add a change listener. A {@link ChangeEvent} will be fired whenever a + * search is added or removed. + * + * @param l + * the {@link ChangeListener} + */ + public void addChangeListener(ChangeListener l) { + listeners.add(l); + } + + /** + * Remove a change listener. + * + * @param l + * a registered {@link ChangeListener} + */ + public void removeChangeListener(ChangeListener l) { + listeners.remove(l); + } + + /** + * Returns all registered {@link ChangeListener}s. + * + * @return all registered {@link ChangeListener}s + */ + public ChangeListener[] getChangeListeners() { + return listeners.toArray(new ChangeListener[] {}); + } + + private void fireChangeEvent() { + ChangeEvent e = new ChangeEvent(this); + + for (ChangeListener l : listeners) { + l.stateChanged(e); + } + } + + /** + * Creates the recent searches popup menu which will be used by + * {@link #install(JXSearchField)} to set a search popup menu on + * searchField. + * + * Override to return a custom popup menu. + * + * @param searchField + * the search field the returned popup menu will be installed on + * @return the recent searches popup menu + */ + protected JPopupMenu createPopupMenu(JTextField searchField) { + return new RecentSearchesPopup(this, searchField); + } + + /** + * Install a recent the searches popup menu returned by + * {@link #createPopupMenu(JXSearchField)} on searchField. + * Also registers an {@link ActionListener} on searchField + * and adds the search string to the list of recent searches whenever a + * {@link ActionEvent} is received. + * + * Uses {@link NativeSearchFieldSupport} to achieve compatibility with the native + * search field support provided by the Mac Look And Feel since Mac OS 10.5. + * + * @param searchField + * the search field to install a recent searches popup menu on + */ + public void install(JTextField searchField) { + searchField.addActionListener(this); + NativeSearchFieldSupport.setFindPopupMenu(searchField, createPopupMenu(searchField)); + } + + /** + * Remove the recent searches popup from searchField when + * installed and stop listening for {@link ActionEvent}s fired by the + * search field. + * + * @param searchField + * uninstall recent searches popup menu + */ + public void uninstall(JXSearchField searchField) { + searchField.removeActionListener(this); + if (searchField.getFindPopupMenu() instanceof RecentSearchesPopup) { + removeChangeListener((ChangeListener) searchField.getFindPopupMenu()); + searchField.setFindPopupMenu(null); + } + } + + /** + * Calls {@link #put(String)} with the {@link ActionEvent}s action command + * as the search string. + */ + @Override + public void actionPerformed(ActionEvent e) { + put(e.getActionCommand()); + } + + /** + * The popup menu returned by + * {@link RecentSearches#createPopupMenu(JXSearchField)}. + */ + public static class RecentSearchesPopup extends JPopupMenu implements ActionListener, ChangeListener { + private RecentSearches recentSearches; + + private JTextField searchField; + + private JMenuItem clear; + + /** + * Creates a new popup menu based on the given {@link RecentSearches} + * and {@link JXSearchField}. + * + * @param recentSearches + * @param searchField + */ + public RecentSearchesPopup(RecentSearches recentSearches, JTextField searchField) { + this.searchField = searchField; + this.recentSearches = recentSearches; + + recentSearches.addChangeListener(this); + buildMenu(); + } + + /** + * Rebuilds the menu according to the recent searches. + */ + private void buildMenu() { + setVisible(false); + removeAll(); + + if (recentSearches.getLength() == 0) { + JMenuItem noRecent = new JMenuItem(UIManagerExt.getString("SearchField.noRecentsText")); + noRecent.setEnabled(false); + add(noRecent); + } else { + JMenuItem recent = new JMenuItem(UIManagerExt.getString("SearchField.recentsMenuTitle")); + recent.setEnabled(false); + add(recent); + + for (String searchString : recentSearches.getRecentSearches()) { + JMenuItem mi = new JMenuItem(searchString); + mi.addActionListener(this); + add(mi); + } + + addSeparator(); + clear = new JMenuItem(UIManagerExt.getString("SearchField.clearRecentsText")); + clear.addActionListener(this); + add(clear); + } + } + + /** + * Sets {@link #searchField}s text to the {@link ActionEvent}s action + * command and call {@link JXSearchField#postActionEvent()} to fire an + * {@link ActionEvent}, if es source is not the clear + * menu item. If the source is the clear menu item, all recent searches + * will be removed. + */ + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource() == clear) { + recentSearches.removeAll(); + } else { + searchField.setText(e.getActionCommand()); + searchField.postActionEvent(); + } + } + + /** + * Every time the recent searches fires a {@link ChangeEvent} call + * {@link #buildMenu()} to rebuild the whole menu. + */ + @Override + public void stateChanged(ChangeEvent e) { + buildMenu(); + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/search/SearchFactory.java b/src/main/java/org/jdesktop/swingx/search/SearchFactory.java new file mode 100644 index 0000000000..e02c7dc104 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/search/SearchFactory.java @@ -0,0 +1,549 @@ +/* + * $Id: SearchFactory.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.search; + +import org.jdesktop.swingx.*; +import org.jdesktop.swingx.plaf.LookAndFeelAddons; +import org.jdesktop.swingx.plaf.UIDependent; +import org.jdesktop.swingx.util.Utilities; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.lang.ref.WeakReference; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +/** + * Factory to create, configure and show application consistent + * search and find widgets. + * + * Typically a shared JXFindBar is used for incremental search, while + * a shared JXFindPanel is used for batch search. This implementation + * + *

              + *
            • JXFindBar - adds and shows it in the target's toplevel container's + * toolbar (assuming a JXRootPane) + *
            • JXFindPanel - creates a JXDialog, adds and shows the findPanel in the + * Dialog + *
            + * + * + * PENDING: JW - update (?) views/wiring on focus change. Started brute force - + * stop searching. This looks extreme confusing for findBars added to ToolBars + * which are empty except for the findbar. Weird problem if triggered from + * menu - find widget disappears after having been shown for an instance. + * Where's the focus? + * + * + * PENDING: add methods to return JXSearchPanels (for use by PatternMatchers). + * + * @author Jeanette Winzenburg + */ +public class SearchFactory implements UIDependent { + private static class LaFListener implements PropertyChangeListener { + private final WeakReference ref; + + public LaFListener(SearchFactory sf) { + this.ref = new WeakReference(sf); + } + + /** + * {@inheritDoc} + */ + @Override + public void propertyChange(PropertyChangeEvent evt) { + SearchFactory sf = ref.get(); + + if (sf == null) { + UIManager.removePropertyChangeListener(this); + } else if ("lookAndFeel".equals(evt.getPropertyName())) { + sf.updateUI(); + } + } + } + + // PENDING: rename methods to batch/incremental instead of dialog/toolbar + + static { + // Hack to enforce loading of SwingX framework ResourceBundle + LookAndFeelAddons.getAddon(); + } + + private static SearchFactory searchFactory; + + + /** the shared find widget for batch-find. */ + protected JXFindPanel findPanel; + + /** the shared find widget for incremental-find. */ + protected JXFindBar findBar; + /** this is a temporary hack: need to remove the useSearchHighlighter property. */ + protected JComponent lastFindBarTarget; + + private boolean useFindBar; + + private Point lastFindDialogLocation; + + private FindRemover findRemover; + + /** + * Returns the shared SearchFactory. + * + * @return the shared SearchFactory + */ + public static SearchFactory getInstance() { + if (searchFactory == null) { + searchFactory = new SearchFactory(); + } + return searchFactory; + } + + /** + * Sets the shared SearchFactory. + * + * @param factory + */ + public static void setInstance(SearchFactory factory) { + searchFactory = factory; + } + + public SearchFactory() { + UIManager.addPropertyChangeListener(new LaFListener(this)); + } + + /** + * Returns a common Keystroke for triggering + * a search. Tries to be OS-specific.

            + * + * PENDING: this should be done in the LF and the + * keyStroke looked up in the UIManager. + * + * @return the keyStroke to register with a findAction. + */ + public KeyStroke getSearchAccelerator() { + // JW: this should be handled by the LF! + // get the accelerator mnemonic from the UIManager + String findMnemonic = "F"; + KeyStroke findStroke = Utilities.stringToKey("D-" + findMnemonic); + // fallback for sandbox (this should be handled in Utilities instead!) + if (findStroke == null) { + findStroke = KeyStroke.getKeyStroke("control F"); + } + return findStroke; + + } + /** + * Returns decision about using a batch- vs. incremental-find for the + * searchable. This implementation returns the useFindBar property directly. + * + * @param target - the component associated with the searchable + * @param searchable - the object to search. + * @return true if a incremental-find should be used, false otherwise. + */ + public boolean isUseFindBar(JComponent target, Searchable searchable) { + return useFindBar; + } + + /** + * Sets the default search type to incremental or batch, for a + * true/false boolean. The default value is false (== batch). + * + * @param incremental a boolean to indicate the default search + * type, true for incremental and false for batch. + */ + public void setUseFindBar(boolean incremental) { + if (incremental == useFindBar) return; + this.useFindBar = incremental; + getFindRemover().endSearching(); + } + + + /** + * Shows an appropriate find widget targeted at the searchable. + * Opens a batch-find or incremental-find + * widget based on the return value of isUseFindBar. + * + * @param target - the component associated with the searchable + * @param searchable - the object to search. + * + * @see #isUseFindBar(JComponent, Searchable) + * @see #setUseFindBar(boolean) + */ + public void showFindInput(JComponent target, Searchable searchable) { + if (isUseFindBar(target, searchable)) { + showFindBar(target, searchable); + } else { + showFindDialog(target, searchable); + } + } + +//------------------------- incremental search + + /** + * Show a incremental-find widget targeted at the searchable. + * + * This implementation uses a JXFindBar and inserts it into the + * target's toplevel container toolbar. + * + * PENDING: Nothing shown if there is no toolbar found. + * + * @param target - the component associated with the searchable + * @param searchable - the object to search. + */ + public void showFindBar(JComponent target, Searchable searchable) { + if (target == null) return; + if (findBar == null) { + findBar = getSharedFindBar(); + } else { + releaseFindBar(); + } + Window topLevel = SwingUtilities.getWindowAncestor(target); + if (topLevel instanceof JXFrame) { + JXRootPane rootPane = ((JXFrame) topLevel).getRootPaneExt(); + JToolBar toolBar = rootPane.getToolBar(); + if (toolBar == null) { + toolBar = new JToolBar(); + rootPane.setToolBar(toolBar); + } + toolBar.add(findBar, 0); + rootPane.revalidate(); + KeyboardFocusManager.getCurrentKeyboardFocusManager().focusNextComponent(findBar); + + } + lastFindBarTarget = target; + findBar.setLocale(target.getLocale()); + target.putClientProperty(AbstractSearchable.MATCH_HIGHLIGHTER, Boolean.TRUE); + getSharedFindBar().setSearchable(searchable); + installFindRemover(target, findBar); + } + + /** + * Returns the shared JXFindBar. Creates and configures on + * first call. + * + * @return the shared JXFindBar + */ + public JXFindBar getSharedFindBar() { + if (findBar == null) { + findBar = createFindBar(); + configureSharedFindBar(); + } + return findBar; + } + + /** + * Factory method to create a JXFindBar. + * + * @return the JXFindBar + */ + public JXFindBar createFindBar() { + return new JXFindBar(); + } + + + protected void installFindRemover(Container target, Container findWidget) { + if (target != null) { + getFindRemover().addTarget(target); + } + getFindRemover().addTarget(findWidget); + } + + private FindRemover getFindRemover() { + if (findRemover == null) { + findRemover = new FindRemover(); + } + return findRemover; + } + + /** + * convenience method to remove a component from its parent + * and revalidate the parent + */ + protected void removeFromParent(JComponent component) { + Container oldParent = component.getParent(); + if (oldParent != null) { + oldParent.remove(component); + if (oldParent instanceof JComponent) { + ((JComponent) oldParent).revalidate(); + } else { + // not sure... never have non-j comps + oldParent.invalidate(); + oldParent.validate(); + } + } + } + + protected void stopSearching() { + if (findPanel != null) { + lastFindDialogLocation = hideSharedFindPanel(false); + findPanel.setSearchable(null); + } + if (findBar != null) { + releaseFindBar(); + } + } + + /** + * Pre: findbar != null. + */ + protected void releaseFindBar() { + findBar.setSearchable(null); + if (lastFindBarTarget != null) { + lastFindBarTarget.putClientProperty(AbstractSearchable.MATCH_HIGHLIGHTER, Boolean.FALSE); + lastFindBarTarget = null; + } + removeFromParent(findBar); + } + + + /** + * Configures the shared FindBar. This method is + * called once after creation of the shared FindBar. + * Subclasses can override to add configuration code.

            + * + * Here: registers a custom action to remove the + * findbar from its ancestor container. + * + * PRE: findBar != null. + * + */ + protected void configureSharedFindBar() { + Action removeAction = new AbstractAction() { + + @Override + public void actionPerformed(ActionEvent e) { + removeFromParent(findBar); +// stopSearching(); +// releaseFindBar(); + + } + + }; + findBar.getActionMap().put(JXDialog.CLOSE_ACTION_COMMAND, removeAction); + } + +//------------------------ batch search + + /** + * Show a batch-find widget targeted at the given Searchable. + * + * This implementation uses a shared JXFindPanel contained + * JXDialog. + * + * @param target - + * the component associated with the searchable + * @param searchable - + * the object to search. + */ + public void showFindDialog(JComponent target, Searchable searchable) { + Window frame = null; //JOptionPane.getRootFrame(); + if (target != null) { + target.putClientProperty(AbstractSearchable.MATCH_HIGHLIGHTER, Boolean.FALSE); + frame = SwingUtilities.getWindowAncestor(target); +// if (window instanceof Frame) { +// frame = (Frame) window; +// } + } + JXDialog topLevel = getDialogForSharedFindPanel(); + JXDialog findDialog; + if ((topLevel != null) && (topLevel.getOwner().equals(frame))) { + findDialog = topLevel; + // JW: #635-swingx - quick hack to update title to current locale ... +// findDialog.setTitle(getSharedFindPanel().getName()); + KeyboardFocusManager.getCurrentKeyboardFocusManager().focusNextComponent(findDialog); + } else { + Point location = hideSharedFindPanel(true); + if (frame instanceof Frame) { + findDialog = new JXDialog((Frame) frame, getSharedFindPanel()); + } else if (frame instanceof Dialog) { + // fix #215-swingx: had problems with secondary modal dialogs. + findDialog = new JXDialog((Dialog) frame, getSharedFindPanel()); + } else { + findDialog = new JXDialog(JOptionPane.getRootFrame(), getSharedFindPanel()); + } + // RJO: shouldn't we avoid overloaded useage like this in a JSR296 world? swap getName() for getTitle() here? +// findDialog.setTitle(getSharedFindPanel().getName()); + // JW: don't - this will stay on top of all applications! + // findDialog.setAlwaysOnTop(true); + findDialog.pack(); + if (location == null) { + findDialog.setLocationRelativeTo(frame); + } else { + findDialog.setLocation(location); + } + } + if (target != null) { + findDialog.setLocale(target.getLocale()); + } + getSharedFindPanel().setSearchable(searchable); + installFindRemover(target, findDialog); + findDialog.setVisible(true); + } + + + /** + * Returns the shared JXFindPanel. Lazyly creates and configures on + * first call. + * + * @return the shared JXFindPanel + */ + public JXFindPanel getSharedFindPanel() { + if (findPanel == null) { + findPanel = createFindPanel(); + configureSharedFindPanel(); + } else { + // JW: temporary hack around #718-swingx + // no longer needed with cleanup of hideSharedFindPanel +// if (findPanel.getParent() == null) { +// SwingUtilities.updateComponentTreeUI(findPanel); +// } + } + return findPanel; + } + + /** + * Factory method to create a JXFindPanel. + * + * @return JXFindPanel + */ + public JXFindPanel createFindPanel() { + return new JXFindPanel(); + } + + + /** + * Configures the shared FindPanel. This method is + * called once after creation of the shared FindPanel. + * Subclasses can override to add configuration code.

            + * + * Here: no-op + * PRE: findPanel != null. + * + */ + protected void configureSharedFindPanel() { + } + + + + private JXDialog getDialogForSharedFindPanel() { + if (findPanel == null) return null; + Window window = SwingUtilities.getWindowAncestor(findPanel); + return (window instanceof JXDialog) ? (JXDialog) window : null; + } + + + /** + * Hides the findPanel's toplevel window and returns its location. + * If the dispose is true, the findPanel is removed from its parent + * and the toplevel window is disposed. + * + * @param dispose boolean to indicate whether the findPanels toplevel + * window should be disposed. + * @return the location of the window if visible, or the last known + * location. + */ + protected Point hideSharedFindPanel(boolean dispose) { + if (findPanel == null) return null; + Window window = SwingUtilities.getWindowAncestor(findPanel); + Point location = lastFindDialogLocation; + if (window != null) { + // PENDING JW: can't remember why it it removed always? + if (window.isVisible()) { + location = window.getLocationOnScreen(); + window.setVisible(false); + } + if (dispose) { + findPanel.getParent().remove(findPanel); + window.dispose(); + } + } + return location; + } + + public class FindRemover implements PropertyChangeListener { + KeyboardFocusManager focusManager; + Set targets; + + public FindRemover() { + updateManager(); + } + + public void addTarget(Container target) { + getTargets().add(target); + } + + public void removeTarget(Container target) { + getTargets().remove(target); + } + + private Set getTargets() { + if (targets == null) { + targets = new HashSet(); + } + return targets; + } + + private void updateManager() { + if (focusManager != null) { + focusManager.removePropertyChangeListener("permanentFocusOwner", this); + } + this.focusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); + focusManager.addPropertyChangeListener("permanentFocusOwner", this); + } + + @Override + public void propertyChange(PropertyChangeEvent ev) { + + Component c = focusManager.getPermanentFocusOwner(); + if (c == null) return; + for (Iterator iter = getTargets().iterator(); iter.hasNext();) { + Container element = iter.next(); + if ((element == c) || (SwingUtilities.isDescendingFrom(c, element))) { + return; + } + } + endSearching(); + } + + public void endSearching() { + getTargets().clear(); + stopSearching(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void updateUI() { + if (findBar != null) { + SwingUtilities.updateComponentTreeUI(findBar); + } + + if (findPanel != null) { + SwingUtilities.updateComponentTreeUI(findPanel); + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/search/Searchable.java b/src/main/java/org/jdesktop/swingx/search/Searchable.java new file mode 100644 index 0000000000..bc624f3a62 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/search/Searchable.java @@ -0,0 +1,90 @@ +/* + * $Id: Searchable.java 2948 2008-06-16 15:02:14Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.search; + +import java.util.regex.Pattern; + +/** + * Interface that used to implement search logic in all the search capable + * components. + * + * @author Ramesh Gupta + */ +public interface Searchable { + + /** + * Search searchString from the beginning of a document. + * + * @param searchString String we should find in a document. + * + * @return index of matched String or -1 if a match cannot be found. + */ + public int search(String searchString); + + /** + * Search searchString from the given position in a document. + * + * @param searchString String we should find in a document. + * @param startIndex Start position in a document or -1 if we want to search from the beginning. + * + * @return index of matched String or -1 if a match cannot be found. + */ + public int search(String searchString, int startIndex); + + /** + * Search searchString in the given direction from the some position in a document. + * + * @param searchString String we should find in a document. + * @param startIndex Start position in a document or -1 if we want to search from the beginning. + * @param backward Indicates search direction, will search from the given position towards the + * beginning of a document if this parameter is true. + * + * @return index of matched String or -1 if a match cannot be found. + */ + public int search(String searchString, int startIndex, boolean backward); + + /** + * Search for the pattern from the beginning of the document. + * + * @param pattern Pattern for search + * + * @return index of matched Pattern or -1 if a match cannot be found. + */ + public int search(Pattern pattern); + + /** + * Search for the pattern from the start index. + * @param pattern Pattern for search + * @param startIndex starting index of search. If -1 then start from the beginning + * @return index of matched pattern or -1 if a match cannot be found. + */ + public int search(Pattern pattern, int startIndex); + + /** + * Search for the pattern from the start index. + * @param pattern Pattern for search + * @param startIndex starting index of search. If -1 then start from the beginning + * @param backward indicates the direction if true then search is backwards + * @return index of matched pattern or -1 if a match cannot be found. + */ + public int search(Pattern pattern, int startIndex, boolean backward); +} diff --git a/src/main/java/org/jdesktop/swingx/search/TableSearchable.java b/src/main/java/org/jdesktop/swingx/search/TableSearchable.java new file mode 100644 index 0000000000..25cba6c544 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/search/TableSearchable.java @@ -0,0 +1,335 @@ +/* + * $Id: TableSearchable.java 3194 2009-01-21 11:39:19Z kleopatra $ + * + * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.search; + +import org.jdesktop.swingx.JXTable; +import org.jdesktop.swingx.decorator.AbstractHighlighter; +import org.jdesktop.swingx.decorator.Highlighter; + +import java.awt.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * An Searchable implementation for use in JXTable. + * + * @author Jeanette Winzenburg + */ +public class TableSearchable extends AbstractSearchable { + + /** The target JXTable. */ + protected JXTable table; + + /** + * Instantiates a TableSearchable with the given table as target. + * + * @param table the JXTable to search. + */ + public TableSearchable(JXTable table) { + this.table = table; + } + + /** + * {@inheritDoc} + *

            + * + * This implementation loops through the cells in a row to find a match. + */ + @Override + protected void findMatchAndUpdateState(Pattern pattern, int startRow, + boolean backwards) { + SearchResult matchRow = null; + if (backwards) { + // CHECK: off-one end still needed? + // Probably not - the findXX don't have side-effects any longer + // hmmm... still needed: even without side-effects we need to + // guarantee calling the notfound update at the very end of the + // loop. + for (int r = startRow; r >= -1 && matchRow == null; r--) { + matchRow = findMatchBackwardsInRow(pattern, r); + updateState(matchRow); + } + } else { + for (int r = startRow; r <= getSize() && matchRow == null; r++) { + matchRow = findMatchForwardInRow(pattern, r); + updateState(matchRow); + } + } + // KEEP - JW: Needed to update if loop wasn't entered! + // the alternative is to go one off in the loop. Hmm - which is + // preferable? + // updateState(matchRow); + + } + + /** + * {@inheritDoc} + *

            + * + * Implemented to search for an extension in the cell given by row and + * foundColumn. + */ + @Override + protected SearchResult findExtendedMatch(Pattern pattern, int row) { + return findMatchAt(pattern, row, lastSearchResult.foundColumn); + } + + /** + * Searches forward through columns of the given row. Starts at + * lastFoundColumn or first column if lastFoundColumn < 0. returns an + * appropriate SearchResult if a matching cell is found in this row or null + * if no match is found. A row index out off range results in a no-match. + * + * @param pattern Pattern that we will try to locate + * @param row the row to search + * @return an appropriate SearchResult if a matching cell is + * found in this row or null if no match is found + */ + private SearchResult findMatchForwardInRow(Pattern pattern, int row) { + int startColumn = (lastSearchResult.foundColumn < 0) ? 0 + : lastSearchResult.foundColumn; + if (isValidIndex(row)) { + for (int column = startColumn; column < table.getColumnCount(); column++) { + SearchResult result = findMatchAt(pattern, row, column); + if (result != null) + return result; + } + } + return null; + } + + /** + * Searches forward through columns of the given row. Starts at + * lastFoundColumn or first column if lastFoundColumn < 0. returns an + * appropriate SearchResult if a matching cell is found in this row or null + * if no match is found. A row index out off range results in a no-match. + * + * @param pattern Pattern that we will try to locate + * @param row the row to search + * @return an appropriate SearchResult if a matching cell is + * found in this row or null if no match is found + */ + private SearchResult findMatchBackwardsInRow(Pattern pattern, int row) { + int startColumn = (lastSearchResult.foundColumn < 0) ? table + .getColumnCount() - 1 : lastSearchResult.foundColumn; + if (isValidIndex(row)) { + for (int column = startColumn; column >= 0; column--) { + SearchResult result = findMatchAt(pattern, row, column); + if (result != null) + return result; + } + } + return null; + } + + /** + * Matches the cell content at row/col against the given Pattern. Returns an + * appropriate SearchResult if matching or null if no matching + * + * @param pattern Pattern that we will try to locate + * @param row a valid row index in view coordinates + * @param column a valid column index in view coordinates + * @return an appropriate SearchResult if matching or null + */ + protected SearchResult findMatchAt(Pattern pattern, int row, int column) { + String text = table.getStringAt(row, column); + if ((text != null) && (text.length() > 0)) { + Matcher matcher = pattern.matcher(text); + if (matcher.find()) { + return createSearchResult(matcher, row, column); + } + } + return null; + } + + /** + * + * {@inheritDoc} + *

            + * + * Overridden to adjust the column index to -1. + */ + @Override + protected int adjustStartPosition(int startIndex, boolean backwards) { + lastSearchResult.foundColumn = -1; + return super.adjustStartPosition(startIndex, backwards); + } + + /** + * {@inheritDoc} + *

            + * + * Overridden to loop through all columns in a row. + */ + @Override + protected int moveStartPosition(int startRow, boolean backwards) { + if (backwards) { + lastSearchResult.foundColumn--; + if (lastSearchResult.foundColumn < 0) { + startRow--; + } + } else { + lastSearchResult.foundColumn++; + if (lastSearchResult.foundColumn >= table.getColumnCount()) { + lastSearchResult.foundColumn = -1; + startRow++; + } + } + return startRow; + } + + /** + * {@inheritDoc} + *

            + * + * Overridden to check the column index of last find. + */ + @Override + protected boolean isEqualStartIndex(final int startIndex) { + return super.isEqualStartIndex(startIndex) + && isValidColumn(lastSearchResult.foundColumn); + } + + /** + * Checks if row is in range: 0 <= row < getRowCount(). + * + * @param column the column index to check in view coordinates. + * @return true if the column is in range, false otherwise + */ + private boolean isValidColumn(int column) { + return column >= 0 && column < table.getColumnCount(); + } + + /** + * {@inheritDoc} + */ + @Override + protected int getSize() { + return table.getRowCount(); + } + + /** + * {@inheritDoc} + */ + @Override + public JXTable getTarget() { + return table; + } + + /** + * Configures the match highlighter to the current match. Ensures that the + * matched cell is visible, if there is a match. + * + * PRE: markByHighlighter + * + */ + protected void moveMatchByHighlighter() { + AbstractHighlighter searchHL = getConfiguredMatchHighlighter(); + // no match + if (!hasMatch()) { + return; + } else { + ensureInsertedSearchHighlighters(searchHL); + table.scrollCellToVisible(lastSearchResult.foundRow, + lastSearchResult.foundColumn); + } + } + + /** + * {@inheritDoc} + *

            + * + * Overridden to convert the column index in the table's view coordinate + * system to model coordinate. + *

            + * + * PENDING JW: this is only necessary because the SearchPredicate wants its + * highlight column in model coordinates. But code comments in the + * SearchPredicate seem to indicate that we probably want to revise that + * (legacy?). + */ + @Override + protected int convertColumnIndexToModel(int viewColumn) { + return getTarget().convertColumnIndexToModel(viewColumn); + } + + /** + * Moves the row selection to the matching cell and ensures its visibility, + * if any. Does nothing if there is no match. + * + */ + protected void moveMatchBySelection() { + if (!hasMatch()) { + return; + } + int row = lastSearchResult.foundRow; + int column = lastSearchResult.foundColumn; + table.changeSelection(row, column, false, false); + if (!table.getAutoscrolls()) { + // scrolling not handled by moving selection + Rectangle cellRect = table.getCellRect(row, column, true); + if (cellRect != null) { + table.scrollRectToVisible(cellRect); + } + } + } + + /** + * {@inheritDoc} + *

            + */ + @Override + protected void moveMatchMarker() { + if (markByHighlighter()) { + moveMatchByHighlighter(); + } else { // use selection + moveMatchBySelection(); + } + } + + /** + * {@inheritDoc} + *

            + */ + @Override + protected void removeHighlighter(Highlighter searchHighlighter) { + table.removeHighlighter(searchHighlighter); + } + + /** + * {@inheritDoc} + *

            + */ + @Override + protected Highlighter[] getHighlighters() { + return table.getHighlighters(); + } + + /** + * {@inheritDoc} + *

            + */ + @Override + protected void addHighlighter(Highlighter highlighter) { + table.addHighlighter(highlighter); + } + +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/search/TreeSearchable.java b/src/main/java/org/jdesktop/swingx/search/TreeSearchable.java new file mode 100644 index 0000000000..fa4bd0c5a1 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/search/TreeSearchable.java @@ -0,0 +1,167 @@ +/* + * $Id: TreeSearchable.java 4178 2012-06-20 08:52:11Z kleopatra $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.search; + +import org.jdesktop.swingx.JXTree; +import org.jdesktop.swingx.decorator.AbstractHighlighter; +import org.jdesktop.swingx.decorator.Highlighter; +import org.jdesktop.swingx.util.Contract; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A searchable targetting the visible rows of a JXTree. + * + * + */ +public class TreeSearchable extends AbstractSearchable { + + protected JXTree tree; + + /** + * Instantiates a Searchable for the given JTree. + * + * @param tree the JTree to search, must not be null. + */ + public TreeSearchable(JXTree tree) { + this.tree = Contract.asNotNull(tree, "tree must not be null"); + } + + @Override + protected void findMatchAndUpdateState(Pattern pattern, int startRow, + boolean backwards) { + SearchResult searchResult = null; + if (backwards) { + for (int index = startRow; index >= 0 && searchResult == null; index--) { + searchResult = findMatchAt(pattern, index); + } + } else { + for (int index = startRow; index < getSize() + && searchResult == null; index++) { + searchResult = findMatchAt(pattern, index); + } + } + updateState(searchResult); + + } + + @Override + protected SearchResult findExtendedMatch(Pattern pattern, int row) { + return findMatchAt(pattern, row); + } + + /** + * Matches the cell content at row/col against the given Pattern. Returns an + * appropriate SearchResult if matching or null if no matching + * + * @param pattern + * @param row a valid row index in view coordinates a valid column index in + * view coordinates + * @return an appropriate SearchResult if matching or null if + * no matching + */ + protected SearchResult findMatchAt(Pattern pattern, int row) { + String text = tree.getStringAt(row); + if ((text != null) && (text.length() > 0)) { + Matcher matcher = pattern.matcher(text); + if (matcher.find()) { + return createSearchResult(matcher, row, 0); + } + } + return null; + } + + @Override + protected int getSize() { + return tree.getRowCount(); + } + + /** + * {@inheritDoc} + */ + @Override + public JXTree getTarget() { + return tree; + } + + /** + * {@inheritDoc} + */ + @Override + protected void moveMatchMarker() { + if (markByHighlighter()) { + moveMatchByHighlighter(); + } else { // use selection + moveMatchBySelection(); + } + } + + protected void moveMatchBySelection() { + // // the common behaviour (JXList, JXTable) is to not + // // move the selection if not found + if (!hasMatch()) { + return; + } + tree.setSelectionRow(lastSearchResult.foundRow); + tree.scrollRowToVisible(lastSearchResult.foundRow); + } + + /** + * use and move the match highlighter. PRE: markByHighlighter + * + */ + protected void moveMatchByHighlighter() { + AbstractHighlighter searchHL = getConfiguredMatchHighlighter(); + // no match + if (!hasMatch()) { + return; + } else { + ensureInsertedSearchHighlighters(searchHL); + tree.scrollRowToVisible(lastSearchResult.foundRow); + } + } + + /** + * @param searchHighlighter + */ + @Override + protected void removeHighlighter(Highlighter searchHighlighter) { + tree.removeHighlighter(searchHighlighter); + } + + /** + * @return all registered highlighters + */ + @Override + protected Highlighter[] getHighlighters() { + return tree.getHighlighters(); + } + + /** + * @param highlighter + */ + @Override + protected void addHighlighter(Highlighter highlighter) { + tree.addHighlighter(highlighter); + } + +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/sort/DefaultSortController.java b/src/main/java/org/jdesktop/swingx/sort/DefaultSortController.java new file mode 100644 index 0000000000..888c4b2751 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/sort/DefaultSortController.java @@ -0,0 +1,405 @@ +/* + * $Id$ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.sort; + +import org.jdesktop.swingx.renderer.StringValue; +import org.jdesktop.swingx.renderer.StringValues; +import org.jdesktop.swingx.util.Contract; + +import javax.swing.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +/** + * A default SortController implementation used as parent class for concrete + * SortControllers in SwingX.

            + * + * Additionally, this implementation contains a fix for core + * Issue 6894632. + * It guarantees to only touch the underlying model during sort/filter and during + * processing the notification methods. This implies that the conversion and size query + * methods are valid at all times outside the internal updates, including the critical + * period (in core with undefined behaviour) after the underlying model has changed and + * before this sorter has been notified. + * + * @author Jeanette Winzenburg + */ +public abstract class DefaultSortController extends DefaultRowSorter implements + SortController { + + /** + * Comparator that uses compareTo on the contents. + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public static final Comparator COMPARABLE_COMPARATOR = + new ComparableComparator(); + + private final static SortOrder[] DEFAULT_CYCLE = new SortOrder[] {SortOrder.ASCENDING, SortOrder.DESCENDING}; + + private List sortCycle; + + private boolean sortable; + + private StringValueProvider stringValueProvider; + + protected int cachedModelRowCount; + + public DefaultSortController() { + super(); + setSortable(true); + setSortOrderCycle(DEFAULT_CYCLE); + setSortsOnUpdates(true); + } + /** + * {@inheritDoc}

            + * + */ + @Override + public void setSortable(boolean sortable) { + this.sortable = sortable; + } + + /** + * {@inheritDoc}

            + * + */ + @Override + public boolean isSortable() { + return sortable; + } + + /** + * {@inheritDoc}

            + * + */ + @Override + public void setSortable(int column, boolean sortable) { + super.setSortable(column, sortable); + } + + /** + * {@inheritDoc}

            + * + */ + @Override + public boolean isSortable(int column) { + if (!isSortable()) return false; + return super.isSortable(column); + } + + /** + * {@inheritDoc} + *

            + * + * Overridden - that is completely new implementation - to get first/next SortOrder + * from sort order cycle. Does nothing if the cycle is empty. + */ + @Override + public void toggleSortOrder(int column) { + checkColumn(column); + if (!isSortable(column)) + return; + SortOrder firstInCycle = getFirstInCycle(); + // nothing to toggle through + if (firstInCycle == null) + return; + List keys = new ArrayList(getSortKeys()); + SortKey sortKey = SortUtils.getFirstSortKeyForColumn(keys, column); + if (keys.indexOf(sortKey) == 0) { + // primary key: in this case we'll use next sortorder in cylce + keys.set(0, new SortKey(column, getNextInCycle(sortKey.getSortOrder()))); + } else { + // all others: make primary with first sortOrder in cycle + keys.remove(sortKey); + keys.add(0, new SortKey(column, getFirstInCycle())); + } + if (keys.size() > getMaxSortKeys()) { + keys = keys.subList(0, getMaxSortKeys()); + } + setSortKeys(keys); + } + + + /** + * Returns the next SortOrder relative to the current, or null + * if the sort order cycle is empty. + * + * @param current the current SortOrder + * @return the next SortOrder to use, may be null if the cycle is empty. + */ + private SortOrder getNextInCycle(SortOrder current) { + int pos = sortCycle.indexOf(current); + if (pos < 0) { + // not in cycle ... what to do? + return getFirstInCycle(); + } + pos++; + if (pos >= sortCycle.size()) { + pos = 0; + } + return sortCycle.get(pos); + } + + /** + * Returns the first SortOrder in the sort order cycle, or null if empty. + * + * @return the first SortOrder in the sort order cycle or null if empty. + */ + private SortOrder getFirstInCycle() { + return sortCycle.size() > 0 ? sortCycle.get(0) : null; + } + + private void checkColumn(int column) { + if (column < 0 || column >= getModelWrapper().getColumnCount()) { + throw new IndexOutOfBoundsException( + "column beyond range of TableModel"); + } + } + + /** + * {@inheritDoc}

            + * + * PENDING JW: toggle has two effects: makes the column the primary sort column, + * and cycle through. So here we something similar. Should we? + * + */ + @Override + public void setSortOrder(int column, SortOrder sortOrder) { + if (!isSortable(column)) return; + SortKey replace = new SortKey(column, sortOrder); + List keys = new ArrayList(getSortKeys()); + SortUtils.removeFirstSortKeyForColumn(keys, column); + keys.add(0, replace); + // PENDING max sort keys, respect here? + setSortKeys(keys); + } + + /** + * {@inheritDoc}

            + * + */ + @Override + public SortOrder getSortOrder(int column) { + SortKey key = SortUtils.getFirstSortKeyForColumn(getSortKeys(), column); + return key != null ? key.getSortOrder() : SortOrder.UNSORTED; + } + + /** + * {@inheritDoc}

            + * + */ + @Override + public void resetSortOrders() { + if (!isSortable()) return; + List keys = new ArrayList(getSortKeys()); + for (int i = keys.size() -1; i >= 0; i--) { + SortKey sortKey = keys.get(i); + if (isSortable(sortKey.getColumn())) { + keys.remove(sortKey); + } + + } + setSortKeys(keys); + + } + + + /** + * {@inheritDoc}

            + */ + @Override + public SortOrder[] getSortOrderCycle() { + return sortCycle.toArray(new SortOrder[0]); + } + + /** + * {@inheritDoc}

            + */ + @Override + public void setSortOrderCycle(SortOrder... cycle) { + Contract.asNotNull(cycle, "Elements of SortOrderCycle must not be null"); + // JW: not safe enough? + sortCycle = Arrays.asList(cycle); + } + + /** + * Sets the registry of string values. If null, the default provider is used. + * + * @param registry the registry to get StringValues for conversion. + */ + @Override + public void setStringValueProvider(StringValueProvider registry) { + this.stringValueProvider = registry; +// updateStringConverter(); + } + + /** + * Returns the registry of string values. + * + * @return the registry of string converters, guaranteed to never be null. + */ + @Override + public StringValueProvider getStringValueProvider() { + if (stringValueProvider == null) { + stringValueProvider = DEFAULT_PROVIDER; + } + return stringValueProvider; + } + + /** + * Returns the default cycle. + * + * @return default sort order cycle. + */ + public static SortOrder[] getDefaultSortOrderCycle() { + return Arrays.copyOf(DEFAULT_CYCLE, DEFAULT_CYCLE.length); + } + + private static final StringValueProvider DEFAULT_PROVIDER = new StringValueProvider() { + + @Override + public StringValue getStringValue(int row, int column) { + return StringValues.TO_STRING; + } + + }; + + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private static class ComparableComparator implements Comparator { + @Override + public int compare(Object o1, Object o2) { + return ((Comparable)o1).compareTo(o2); + } + } + +//-------------------------- replacing super for more consistent conversion/rowCount behaviour + + /** + * {@inheritDoc}

            + * + * Overridden to use check against getViewRowCount for validity. + * + * @see #getViewRowCount() + */ + @Override + public int convertRowIndexToModel(int viewIndex) { + if ((viewIndex < 0) || viewIndex >= getViewRowCount()) + throw new IndexOutOfBoundsException("valid viewIndex: 0 <= index < " + + getViewRowCount() + + " but was: " + viewIndex); + try { + return super.convertRowIndexToModel(viewIndex); + } catch (Exception e) { + // this will happen only if unsorted/-filtered and super + // incorrectly access the model while it had been changed + // under its feet + } + return viewIndex; + } + + + /** + * {@inheritDoc}

            + * + * Overridden to use check against getModelRowCount for validity. + * + * @see #getModelRowCount() + */ + @Override + public int convertRowIndexToView(int modelIndex) { + if ((modelIndex < 0) || modelIndex >= getModelRowCount()) + throw new IndexOutOfBoundsException("valid modelIndex: 0 <= index < " + + getModelRowCount() + + " but was: " + modelIndex); + try { + return super.convertRowIndexToView(modelIndex); + } catch (Exception e) { + // this will happen only if unsorted/-filtered and super + // incorrectly access the model while it had been changed + // under its feet + } + return modelIndex; + } + + /** + * {@inheritDoc}

            + * + * Overridden to return the model row count which corresponds to the currently + * mapped model instead of accessing the model directly (as super does). + * This may differ from the "real" current model row count if the model has changed + * but this sorter not yet notified. + * + */ + @Override + public int getModelRowCount() { + return cachedModelRowCount; + } + + /** + * {@inheritDoc}

            + * + * Overridden to return the model row count if no filters installed, otherwise + * return super. + * + * @see #getModelRowCount() + * + */ + @Override + public int getViewRowCount() { + if (hasRowFilter()) + return super.getViewRowCount(); + return getModelRowCount(); + } + + /** + * @return + */ + private boolean hasRowFilter() { + return getRowFilter() != null; + } + +//------------------ overridden notification methods: cache model row count + @Override + public void allRowsChanged() { + cachedModelRowCount = getModelWrapper().getRowCount(); + super.allRowsChanged(); + } + @Override + public void modelStructureChanged() { + super.modelStructureChanged(); + cachedModelRowCount = getModelWrapper().getRowCount(); + } + @Override + public void rowsDeleted(int firstRow, int endRow) { + cachedModelRowCount = getModelWrapper().getRowCount(); + super.rowsDeleted(firstRow, endRow); + } + @Override + public void rowsInserted(int firstRow, int endRow) { + cachedModelRowCount = getModelWrapper().getRowCount(); + super.rowsInserted(firstRow, endRow); + } + +} diff --git a/src/main/java/org/jdesktop/swingx/sort/ListSortController.java b/src/main/java/org/jdesktop/swingx/sort/ListSortController.java new file mode 100644 index 0000000000..d2f1b916ed --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/sort/ListSortController.java @@ -0,0 +1,93 @@ +/* + * $Id$ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.sort; + +import javax.swing.*; + +/** + * A SortController to use with JXList. + * + * @author Jeanette Winzenburg + */ +public class ListSortController extends DefaultSortController { + + /** underlying model */ + private M listModel; + /** + * @param model + */ + public ListSortController(M model) { + setModel(model); + } + + /** + * Sets the TableModel to use as the underlying model + * for this TableRowSorter. A value of null + * can be used to set an empty model. + * + * @param model the underlying model to use, or null + */ + public void setModel(M model) { + listModel = model; + if (model != null) + cachedModelRowCount = model.getSize(); + setModelWrapper(new ListRowSorterModelWrapper()); + } + + /** + * Implementation of DefaultRowSorter.ModelWrapper that delegates to a + * TableModel. + */ + private class ListRowSorterModelWrapper extends ModelWrapper { + @Override + public M getModel() { + return listModel; + } + + @Override + public int getColumnCount() { + return (listModel == null) ? 0 : 1; + } + + @Override + public int getRowCount() { + return (listModel == null) ? 0 : listModel.getSize(); + } + + @Override + public Object getValueAt(int row, int column) { + return listModel.getElementAt(row); + } + + @Override + public String getStringValueAt(int row, int column) { + return getStringValueProvider().getStringValue(row, column) + .getString(getValueAt(row, column)); + } + + @Override + public Integer getIdentifier(int index) { + return index; + } + } + +} diff --git a/src/main/java/org/jdesktop/swingx/sort/RowFilters.java b/src/main/java/org/jdesktop/swingx/sort/RowFilters.java new file mode 100644 index 0000000000..1503be582f --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/sort/RowFilters.java @@ -0,0 +1,216 @@ +/* + * $Id$ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.sort; + +import org.jdesktop.swingx.util.Contract; + +import javax.swing.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +/** + * Factory of additional RowFilters.

            + * + * Trigger is the missing of Pattern/Regex+matchflags factory method in core. + * Can't do much other than c&p core as both abstract base class GeneralFilter and + * concrete RowFilter are private. Expose the base as public for custom subclasses + * + * @author Jeanette Winzenburg + */ +@SuppressWarnings("unchecked") +public class RowFilters { + + /** + * Returns a RowFilter that uses a regular + * expression to determine which entries to include. Only entries + * with at least one matching value are included. For + * example, the following creates a RowFilter that + * includes entries with at least one value starting with + * "a": + *

            +     *   RowFilter.regexFilter("^a");
            +     * 
            + *

            + * The returned filter uses {@link Matcher#find} + * to test for inclusion. To test for exact matches use the + * characters '^' and '$' to match the beginning and end of the + * string respectively. For example, "^foo$" includes only rows whose + * string is exactly "foo" and not, for example, "food". See + * {@link Pattern} for a complete description of + * the supported regular-expression constructs. + * + * @param regex the regular expression to filter on + * @param indices the indices of the values to check. If not supplied all + * values are evaluated + * @return a RowFilter implementing the specified criteria + * @throws NullPointerException if regex is + * null + * @throws IllegalArgumentException if any of the indices + * are < 0 + * @throws PatternSyntaxException if regex is + * not a valid regular expression. + * @see Pattern + */ + public static RowFilter regexFilter(String regex, + int... indices) { + return regexFilter(0, regex, indices); + } + + /** + * Returns a RowFilter that uses a regular + * expression to determine which entries to include. Only entries + * with at least one matching value are included. For + * example, the following creates a RowFilter that + * includes entries with at least one value starting with + * "a" ignoring case: + *

            +     *   RowFilter.regexFilter(Pattern.CASE_INSENSITIVE, "^a");
            +     * 
            + *

            + * The returned filter uses {@link Matcher#find} + * to test for inclusion. To test for exact matches use the + * characters '^' and '$' to match the beginning and end of the + * string respectively. For example, "^foo$" includes only rows whose + * string is exactly "foo" and not, for example, "food". See + * {@link Pattern} for a complete description of + * the supported regular-expression constructs. + * + * @param matchFlags + * Match flags, a bit mask that may include + * {@link Pattern#CASE_INSENSITIVE}, {@link Pattern#MULTILINE}, {@link Pattern#DOTALL}, + * {@link Pattern#UNICODE_CASE}, {@link Pattern#CANON_EQ}, {@link Pattern#UNIX_LINES}, + * {@link Pattern#LITERAL} and {@link Pattern#COMMENTS} + * + * @param regex the regular expression to filter on + * @param indices the indices of the values to check. If not supplied all + * values are evaluated + * @return a RowFilter implementing the specified criteria + * @throws NullPointerException if regex is + * null + * @throws IllegalArgumentException if any of the indices + * are < 0 + * @throws IllegalArgumentException + * If bit values other than those corresponding to the defined + * match flags are set in flags + * @throws PatternSyntaxException if regex is + * not a valid regular expression. + * @see Pattern + */ + public static RowFilter regexFilter(int matchFlags, String regex, + int... indices) { + return regexFilter(Pattern.compile(regex, matchFlags), indices); + } + + /** + * Returns a RowFilter that uses a regular + * expression to determine which entries to include. + * + * @param pattern the Pattern to use for matching + * @param indices the indices of the values to check. If not supplied all + * values are evaluated + * @return a RowFilter implementing the specified criteria + * @throws NullPointerException if pattern is + * null + * @see Pattern + */ + public static RowFilter regexFilter(Pattern pattern, + int... indices) { + return (RowFilter)new RegexFilter(pattern, indices); + } + + /** + * C&P from core Swing to allow subclassing. + */ + public static abstract class GeneralFilter extends RowFilter { + private int[] columns; + + protected GeneralFilter(int... columns) { + checkIndices(columns); + this.columns = columns; + } + + @Override + public boolean include(Entry value){ + int count = value.getValueCount(); + if (columns.length > 0) { + for (int i = columns.length - 1; i >= 0; i--) { + int index = columns[i]; + if (index < count) { + if (include(value, index)) { + return true; + } + } + } + } + else { + while (--count >= 0) { + if (include(value, count)) { + return true; + } + } + } + return false; + } + + protected abstract boolean include( + Entry value, int index); + /** + * Throws an IllegalArgumentException if any of the values in + * columns are < 0. + */ + protected void checkIndices(int[] columns) { + for (int i = columns.length - 1; i >= 0; i--) { + if (columns[i] < 0) { + throw new IllegalArgumentException("Index must be >= 0"); + } + } + } + } + + /** + * C&P from core to allow richer factory methods. + */ + private static class RegexFilter extends GeneralFilter { + private Matcher matcher; + + RegexFilter(Pattern regex, int[] columns) { + super(columns); + if (regex == null) { + // JW: Exception type changed to comply with swingx convention + Contract.asNotNull(regex, "Pattern must be non-null"); +// throw new IllegalArgumentException("Pattern must be non-null"); + } + matcher = regex.matcher(""); + } + + @Override + protected boolean include( + Entry value, int index) { + matcher.reset(value.getStringValue(index)); + return matcher.find(); + } + } + + private RowFilters() {}; + +} diff --git a/src/main/java/org/jdesktop/swingx/sort/SortController.java b/src/main/java/org/jdesktop/swingx/sort/SortController.java new file mode 100644 index 0000000000..61d2faa1a9 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/sort/SortController.java @@ -0,0 +1,279 @@ +/* + * $Id: SortController.java 3498 2009-09-10 09:35:40Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.sort; + +import javax.swing.*; +import java.util.Comparator; + +/** + * Defines the interactive sort control for sortable collection components (like + * JXList, JXTable). All sort gesture requests from their sort api + * are routed through the SortController. + *

            + * + * This is very-much work-in-progress: while moving from ol' SwingX sorting to + * core jdk6 sorting we need a hook for sorting api on the view. So in terms of + * jdk6 classes, this is something like:

            + * + *

            + * SortController == DefaultRowSorter - RowSorter + XX
            + * 
            + * All methods which change sort state must respect per-controller and per-column + * sortable property, as follows + *
              + *
            1. if per-controller sortable is false, do nothing + *
            2. if per-controller sortable is true, if per-column sortable is false, do nothing + *
            3. if both are true toggle the SortOrder of the given column + *
            + * + * + * @author Jesse Wilson + * @author Jeanette Winzenburg + * + */ +public interface SortController { + +//----------------- configuration + + /** + * Sets whether or not this controller is sortable.

            + * + * The default is true.

            + * + * PENDING JW: define behaviour if sortable is disabled while has sortOrders. + * In this case JXTable resets all sorts. + * + * @param sortable whether or not this controller is sortable + * @see #isSortable() + */ + void setSortable(boolean sortable); + + /** + * Returns true if this controller is sortable; otherwise, false. + * + * @return true if this controller is sortable + * + * @see #isSortable() + */ + boolean isSortable(); + + /** + * Sets whether or not the specified column is sortable.

            + * + * The default is true.

            + * + * PENDING JW: define behaviour if sortable is disabled while has sortOrders. + * In this case JXTable removes the sort of the column.

            + * + * PENDING JW: decide whether or not this method should trigger a resort + * DefaultRowSorter explicitly doesn't, JXTable does. + * + * @param column the column to enable or disable sorting on, in terms + * of the underlying model + * @param sortable whether or not the specified column is sortable + * @throws IndexOutOfBoundsException if column is outside + * the range of the model + * @see #isSortable(int) + * @see #toggleSortOrder(int) + * @see #setSortOrder(int, SortOrder) + */ + void setSortable(int column, boolean sortable); + + /** + * Returns true if the specified column is sortable.

            + * This returns true if both the controller's sortable property and + * the column's sortable property is true. Returns false if any of + * them is false. + * + * @param column the column to check sorting for, in terms of the + * underlying model + * @return true if the column is sortable + * @throws IndexOutOfBoundsException if column is outside + * the range of the underlying model + * + * @see #isSortable(int) + */ + boolean isSortable(int column); + + /** + * Sets the Comparator to use when sorting the specified + * column. This does not trigger a sort. If you want to sort after + * setting the comparator you need to explicitly invoke sort. + * + * @param column the index of the column the Comparator is + * to be used for, in terms of the underlying model + * @param comparator the Comparator to use + * @throws IndexOutOfBoundsException if column is outside + * the range of the underlying model + */ + public void setComparator(int column, Comparator comparator); + + /** + * Returns the Comparator for the specified + * column. This will return null if a Comparator + * has not been specified for the column. + * + * @param column the column to fetch the Comparator for, in + * terms of the underlying model + * @return the Comparator for the specified column + * @throws IndexOutOfBoundsException if column is outside + * the range of the underlying model + */ + public Comparator getComparator(int column); + + /** + * Sets the cycle of sort ordes to toggle through. Zero or more SortOrders which + * must not be null. + * + * @param cycle the SortOrders to cycle through, may be empty + * @throws NullPointerException if the array or any of its elements is null + */ + void setSortOrderCycle(SortOrder... cycle); + + /** + * Returns the cycle of sort orders to cycle through. + * + * @return + */ + SortOrder[] getSortOrderCycle(); + + /** + * If true, specifies that a sort should happen when the underlying + * model is updated (rowsUpdated is invoked). For + * example, if this is true and the user edits an entry the + * location of that item in the view may change. The default is + * true. + * + * @param sortsOnUpdates whether or not to sort on update events + */ + void setSortsOnUpdates(boolean sortsOnUpdates); + + /** + * Returns true if a sort should happen when the underlying + * model is updated; otherwise, returns false. + * + * @return whether or not to sort when the model is updated + */ + boolean getSortsOnUpdates(); + + /** + * Sets the StringValueProvider to look up the StringValues. If the value + * is not-null, it guarantees to use it exclusively for string conversion.

            + * + * PENDING JW: this is more or less parallel to TableStringConverter. Need to think + * about merging somehow. + * + * @param provider the look up for StringValues, may be null. + */ + void setStringValueProvider(StringValueProvider provider); + + /** + * Returns the StringValueProvider used to look up StringValues. + * + * @return StringValueProvider used to look up StringValues, guaranteed to + * be not null. + */ + StringValueProvider getStringValueProvider(); + +//------------------------ sort + + /** + * Reverses the sort order of the specified column. The exact behaviour is + * up to implementations.

            + * + * Implementations must respect the per-controller and per-column-sortable + * property. + * + * @param column the model index of the column to toggle + * @see #isSortable(int) + * @see #isSortable() + */ + void toggleSortOrder(int column); + + /** + * Sets the sort order of the specified column.

            + * + * Implementations must respect the per-controller and per-column-sortable + * property. + * + * @param column the model index of the column to set + * @param sortOrder the SortOrder to set for the column + * + * @see #isSortable(int) + * @see #isSortable() + */ + void setSortOrder(int column, SortOrder sortOrder); + + /** + * Returns the sort order of the specified column. + * + * + * @return one of {@link SortOrder#ASCENDING}, + * {@link SortOrder#DESCENDING} or {@link SortOrder#UNSORTED}. + */ + SortOrder getSortOrder(int column); + + + /** + * Resets all interactive sorting.

            + * + * Implementations must respect the per-controller and per-column-sortable + * property. + * + */ + void resetSortOrders(); + +//-------------------- filter + + /** + * Sets the filter that determines which rows, if any, should be + * hidden from the view. The filter is applied before sorting. A value + * of null indicates all values from the model should be + * included. + *

            + * RowFilter's include method is passed an + * Entry that wraps the underlying model. The number + * of columns in the Entry corresponds to the + * number of columns in the underlying model. The identifier + * comes from the underlying model as well. + *

            + * This method triggers a sort. + * + * PENDING JW: the "underlying model" is the ModelWrapper ... want to + * expose here as well? Otherwise, the second paramter doesn't make much sense. + * + * @param filter the filter used to determine what entries should be + * included + */ + void setRowFilter(RowFilter filter); + + /** + * Returns the filter that determines which rows, if any, should + * be hidden from view. + * + * @return the filter + */ + RowFilter getRowFilter(); + + + +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/sort/SortUtils.java b/src/main/java/org/jdesktop/swingx/sort/SortUtils.java new file mode 100644 index 0000000000..c9f18c9544 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/sort/SortUtils.java @@ -0,0 +1,90 @@ +/* + * Created on 15.03.2006 + * + */ +package org.jdesktop.swingx.sort; + +import javax.swing.*; +import java.util.List; + +/** + * Collection of convenience methods. + */ +public class SortUtils { + +//---------------------- static utility methods + + /** + * Returns the first SortKey in the list which is sorted. + * If none is sorted, null is returned. + * + * @param keys a list of SortKeys to search + * @return the first SortKey which is sorted or null, if no + * is found. + */ + public static RowSorter.SortKey getFirstSortingKey(List keys) { + for (RowSorter.SortKey key : keys) { + if (isSorted(key.getSortOrder())) { + return key; + } + } + return null; + } + + /** + * Returns the first SortKey in the list for the given column, + * or null if the column has no SortKey. + * + * @param keys a list of SortKeys to search + * @param modelColumn the column index in model coordinates + * @return the first SortKey for the given column or null if none is + * found. + */ + public static RowSorter.SortKey getFirstSortKeyForColumn(List keys, int modelColumn) { + for (RowSorter.SortKey key : keys) { + if (key.getColumn() == modelColumn) { + return key; + } + } + return null; + } + + /** + * Removes and returns the first SortKey in the list for the given column, + * or null if the column has no SortKey. + * + * @param keys a list of SortKeys to search + * @param modelColumn the column index in model coordinates + * @return the first SortKey for the given column or null if none is + * found. + */ + public static RowSorter.SortKey removeFirstSortKeyForColumn(List keys, int modelColumn) { + for (RowSorter.SortKey key : keys) { + if (key.getColumn() == modelColumn) { + keys.remove(key); + return key; + } + } + return null; + } + public static boolean isSorted(SortOrder sortOrder) { + return sortOrder != null && (SortOrder.UNSORTED != sortOrder); + } + + /** + * Convenience to check for ascending sort order. + * PENDING: is this helpful at all? + * + * @return true if ascendingly sorted, false for unsorted/descending. + */ + public static boolean isAscending(SortOrder sortOrder) { + return sortOrder == SortOrder.ASCENDING; + } + + public static boolean isSorted(SortOrder sortOrder, boolean ascending) { + return isSorted(sortOrder) && (ascending == isAscending(sortOrder)); + } + + + private SortUtils() {}; +} diff --git a/src/main/java/org/jdesktop/swingx/sort/StringValueProvider.java b/src/main/java/org/jdesktop/swingx/sort/StringValueProvider.java new file mode 100644 index 0000000000..0df7542edb --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/sort/StringValueProvider.java @@ -0,0 +1,50 @@ +/* + * $Id$ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.sort; + +import org.jdesktop.swingx.renderer.StringValue; + +/** + * Read-only repository for StringValues. This is meant to be shared by collection views + * (in rendering a cell) and RowSorters/SortControllers/ComponentAdapters.

            + * + * Note: this is work-in-progress, related to re-enable WYSIWYM in sorting/filtering. + * It's location and api is expected to change. + * + * @author Jeanette Winzenburg + */ +public interface StringValueProvider { + + /** + * Returns a StringValue to use for conversion of the cell content at row and column. + * The converter is guaranteed to be not null, so implemenations are responsible for + * a reasonable fall-back value always, f.i. if they have no converters registered of + * if any or both of the row/column coordinate is "invalid" (f.i. -1)

            + * + * @param row the row of the cell in model coordinates + * @param column the column of the cell in model coordinates + * + * @return a StringValue to use for conversion, guaranteed to not null. + */ + StringValue getStringValue(int row, int column); + +} diff --git a/src/main/java/org/jdesktop/swingx/sort/StringValueRegistry.java b/src/main/java/org/jdesktop/swingx/sort/StringValueRegistry.java new file mode 100644 index 0000000000..0a2b79cace --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/sort/StringValueRegistry.java @@ -0,0 +1,193 @@ +/* + * $Id$ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.sort; + +import org.jdesktop.swingx.renderer.StringValue; +import org.jdesktop.swingx.renderer.StringValues; + +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Logger; + +/** + * A writable implemenation of StringValueProvider. Typically, this is created and + * maintained by a collection view and then passed over to interested parties. It is + * modeled/implemented after the default renderer maintenance in a JTable.

            + * + * PENDING JW: for safety - better not implement but return a provider. We probably don't want + * readers to frickle around here?. + * + * @author Jeanette Winzenburg + */ +public final class StringValueRegistry implements StringValueProvider { + + @SuppressWarnings("unused") + private static final Logger LOG = Logger + .getLogger(StringValueRegistry.class.getName()); + + private Map, StringValue> perClass; + private HashMap perColumn; + private HashMap> classPerColumn; + + /** + * {@inheritDoc}

            + */ + @Override + public StringValue getStringValue(int row, int column) { + StringValue sv = getPerColumnMap().get(column); + if (sv == null) { + sv = getStringValueByClass(getClass(row, column)); + } + if (sv == null) { + sv = getStringValueByClass(Object.class); + } + return sv != null ? sv : StringValues.TO_STRING; + } + +//-------------------- manage + /** + * Sets a StringValue to use for the given column. If the converter is null, + * the mapping is removed. + * + * @param sv the StringValue to use for the given column. + * @param column the column index in model coordinates. + * + */ + public void setStringValue(StringValue sv, int column) { + // PENDING really remove mapping if sv null + getPerColumnMap().put(column, sv); + } + + /** + * Removes all per-column mappings of StringValues. + * + */ + public void clearColumnStringValues() { + getPerColumnMap().clear(); + } + + /** + * Sets the StringValue to use for the given class. If the converter is null, + * the mapping is removed. + * + * @param sv the StringValue to use for the given column. + * @param clazz the class + */ + public void setStringValue(StringValue sv, Class clazz) { + // PENDING really remove mapping if sv null + getPerClassMap().put(clazz, sv); + } + + /** + * Returns the StringValue registered for the given class.

            + * + * This is temporarily exposed for testing only - do not use, it will + * be removed very soon! + * + * @param clazz the class to find the registered StringValue for + * @return the StringValue registered for the class, or null if not directly + * registered. + */ + public StringValue getStringValue(Class clazz) { + return getPerClassMap().get(clazz); + } + /** + * Sets the column class. + * + * @param clazz + * @param column index in model coordinates + */ + public void setColumnClass(Class clazz, int column) { + getColumnClassMap().put(column, clazz); + } + + /** + * @param classPerColumn + */ + public void setColumnClasses(Map> classPerColumn) { + this.classPerColumn = classPerColumn != null ? + new HashMap>(classPerColumn) : null; + } + + /** + * + * @param clazz + * @return + */ + private StringValue getStringValueByClass(Class clazz) { + if (clazz == null) return null; + StringValue sv = getPerClassMap().get(clazz); + if (sv != null) return sv; + return getStringValueByClass(clazz.getSuperclass()); + } + + /** + * Returns the Class of the column. + * + * @param row + * @param column + * @return + */ + private Class getClass(int row, int column) { + Class clazz = getColumnClassMap().get(column); + return clazz != null ? clazz : Object.class; + } + + /** + * Returns the Map which stores the per-column Class, lazily + * creates one if null. + * + * @return the per-column storage map of Class + */ + private Map> getColumnClassMap() { + if (classPerColumn == null) { + classPerColumn = new HashMap>(); + } + return classPerColumn; + } + + /** + * Returns the Map which stores the per-class StringValues, lazily + * creates one if null. + * + * @return the per-class storage map of StringValues + */ + private Map, StringValue> getPerClassMap() { + if (perClass == null) { + perClass = new HashMap, StringValue>(); + } + return perClass; + } + + /** + * Returns the Map which stores the per-column StringValues, lazily + * creates one if null. + * + * @return the per-column storage map of StringValues + */ + private Map getPerColumnMap() { + if (perColumn == null) { + perColumn = new HashMap(); + } + return perColumn; + } +} diff --git a/src/main/java/org/jdesktop/swingx/sort/TableSortController.java b/src/main/java/org/jdesktop/swingx/sort/TableSortController.java new file mode 100644 index 0000000000..b84569bf4c --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/sort/TableSortController.java @@ -0,0 +1,164 @@ +/* + * $Id$ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.sort; + +import javax.swing.table.TableModel; +import java.text.Collator; +import java.util.Comparator; + +/** + * A SortController to use for a JXTable.

            + * + * @author Jeanette Winzenburg + */ +public class TableSortController extends DefaultSortController { + /** + * Underlying model. + */ + private M tableModel; + + public TableSortController() { + this(null); + } + + /** + * @param model + */ + public TableSortController(M model) { + super(); + setModel(model); + } + + /** + * Sets the TableModel to use as the underlying model + * for this TableRowSorter. A value of null + * can be used to set an empty model. + * + * @param model the underlying model to use, or null + */ + public void setModel(M model) { + tableModel = model; + if (model != null) + cachedModelRowCount = model.getRowCount(); + setModelWrapper(new TableRowSorterModelWrapper()); + } + + + /** + * Returns the Comparator for the specified + * column. If a Comparator has not been specified using + * the setComparator method a Comparator + * will be returned based on the column class + * (TableModel.getColumnClass) of the specified column. + * If the column class is String, + * Collator.getInstance is returned. If the + * column class implements Comparable a private + * Comparator is returned that invokes the + * compareTo method. Otherwise + * Collator.getInstance is returned.

            + * + * PENDING JW: think about implications to string value lookup! + * + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + @Override + public Comparator getComparator(int column) { + Comparator comparator = super.getComparator(column); + if (comparator != null) { + return comparator; + } + Class columnClass = getModel().getColumnClass(column); + if (columnClass == String.class) { + return Collator.getInstance(); + } + if (Comparable.class.isAssignableFrom(columnClass)) { + return COMPARABLE_COMPARATOR; + } + return Collator.getInstance(); + } + + /** + * {@inheritDoc}

            + * Note: must implement same logic as the overridden comparator + * lookup, otherwise will throw ClassCastException because + * here the comparator is never null.

            + * + * PENDING JW: think about implications to string value lookup! + * + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + @Override + protected boolean useToString(int column) { + Comparator comparator = super.getComparator(column); + if (comparator != null) { + return false; + } + Class columnClass = getModel().getColumnClass(column); + if (columnClass == String.class) { + return false; + } + if (Comparable.class.isAssignableFrom(columnClass)) { + return false; + } + return true; + } + + + /** + * Implementation of DefaultRowSorter.ModelWrapper that delegates to a + * TableModel. + */ + private class TableRowSorterModelWrapper extends ModelWrapper { + @Override + public M getModel() { + return tableModel; + } + + @Override + public int getColumnCount() { + return (tableModel == null) ? 0 : tableModel.getColumnCount(); + } + + @Override + public int getRowCount() { + return (tableModel == null) ? 0 : tableModel.getRowCount(); + } + + @Override + public Object getValueAt(int row, int column) { + return tableModel.getValueAt(row, column); + } + + @Override + public String getStringValueAt(int row, int column) { + return getStringValueProvider().getStringValue(row, column) + .getString(getValueAt(row, column)); + } + + @Override + public Integer getIdentifier(int index) { + return index; + } + } + + +} diff --git a/src/main/java/org/jdesktop/swingx/table/ColumnControlButton.java b/src/main/java/org/jdesktop/swingx/table/ColumnControlButton.java new file mode 100644 index 0000000000..80fdd1245d --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/table/ColumnControlButton.java @@ -0,0 +1,1040 @@ +/* + * $Id: ColumnControlButton.java 4065 2011-08-19 13:28:26Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.table; + +import org.jdesktop.swingx.JXTable; +import org.jdesktop.swingx.action.AbstractActionExt; +import org.jdesktop.swingx.action.ActionContainerFactory; +import org.jdesktop.swingx.plaf.ColumnControlButtonAddon; +import org.jdesktop.swingx.plaf.LookAndFeelAddons; +import org.jdesktop.swingx.table.ColumnControlPopup.ActionGroupable; +import org.jdesktop.swingx.table.ColumnControlPopup.ActionGrouper; + +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.TableColumnModelEvent; +import javax.swing.event.TableColumnModelListener; +import javax.swing.plaf.UIResource; +import javax.swing.table.TableColumn; +import javax.swing.table.TableColumnModel; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ItemEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * A component to allow interactive customization of JXTable's + * columns. + * It's main purpose is to allow toggling of table columns' visibility. + * Additionally, arbitrary configuration actions can be exposed. + *

            + * + * This component is installed in the JXTable's + * trailing corner, if enabled: + * + *

            
            + * table.setColumnControlVisible(true);
            + * 
            + * + * From the perspective of a JXTable, the component's behaviour is + * opaque. Typically, the button's action is to popup a component for user + * interaction.

            + * + * This class is responsible for handling/providing/updating the lists of + * actions and to keep each Action's state in synch with Table-/Column state. + * The visible behaviour of the popup is delegated to a + * ColumnControlPopup.

            + * + * Default support for adding table (configuration or other) Actions is + * informal, driven by convention: + *

              + *
            • the JXTable's actionMap is scanned for candidate actions, the default marker + * is a key of type String which starts with {@link ColumnControlButton.COLUMN_CONTROL_MARKER} + *
            • the actions are sorted by that key and then handed over to the ColumnControlPopup + * for binding and addition of appropriate menu items + *
            • the addition as such is control by additionalActionsVisible property, its + * default value is true + *
            + * + * + * + * @see TableColumnExt + * @see TableColumnModelExt + * @see JXTable#setColumnControl + * + */ +public class ColumnControlButton extends JButton { + + // JW: really want to extend? for builders? + /** Marker to auto-recognize actions which should be added to the popup. */ + public static final String COLUMN_CONTROL_MARKER = "column."; + + /** the key for looking up the control's icon in the UIManager. Typically, it's LAF dependent. */ + public static final String COLUMN_CONTROL_BUTTON_ICON_KEY = "ColumnControlButton.actionIcon"; + + /** the key for looking up the control's margin in the UIManager. Typically, it's LAF dependent. */ + public static final String COLUMN_CONTROL_BUTTON_MARGIN_KEY = "ColumnControlButton.margin"; + + static { + LookAndFeelAddons.contribute(new ColumnControlButtonAddon()); + } + + /** exposed for testing. */ + protected ColumnControlPopup popup; + // TODO: the table reference is a potential leak? + /** The table which is controlled by this. */ + private JXTable table; + /** Listener for table property changes. */ + private PropertyChangeListener tablePropertyChangeListener; + /** Listener for table's columnModel. */ + TableColumnModelListener columnModelListener; + /** the list of actions for column menuitems.*/ + private List columnVisibilityActions; + + private boolean additionalActionsVisible; + + + /** + * Creates a column control button for the table. Uses the default + * icon as provided by the addon. + * + * @param table the JXTable controlled by this component + */ + public ColumnControlButton(JXTable table) { + this(table, null); + } + + /** + * Creates a column control button for the table. The button + * uses the given icon and has no text. + * @param table the JXTable controlled by this component + * @param icon the Icon to show + */ + public ColumnControlButton(JXTable table, Icon icon) { + super(); + init(); + // JW: icon LF dependent? + setAction(createControlAction(icon)); + updateActionUI(); + updateButtonUI(); + installTable(table); + } + + + @Override + public void updateUI() { + super.updateUI(); + // JW: icon may be LF dependent + updateActionUI(); + updateButtonUI(); + getColumnControlPopup().updateUI(); + } + + /** + * Updates this button's properties provided by the LAF. + * Here: overwrites the action's small_icon with the icon from the ui if the current + * icon is null or a UIResource. + */ + protected void updateButtonUI() { + if ((getMargin() == null) || (getMargin() instanceof UIResource)) { + Insets insets = UIManager.getInsets(COLUMN_CONTROL_BUTTON_MARGIN_KEY); + setMargin(insets); + } + } + + /** + * Updates the action properties provided by the LAF. + * Here: overwrites the action's small_icon with the icon from the ui if the current + * icon is null or a UIResource. + */ + protected void updateActionUI() { + if (getAction() == null) return; + Icon icon = (Icon) getAction().getValue(Action.SMALL_ICON); + if ((icon == null) || (icon instanceof UIResource)) { + icon = UIManager.getIcon(COLUMN_CONTROL_BUTTON_ICON_KEY); + getAction().putValue(Action.SMALL_ICON, icon); + } + } + + /** + * Toggles the popup component's visibility. This method is + * called by this control's default action.

            + * + * Here: delegates to getControlPopup(). + */ + public void togglePopup() { + getColumnControlPopup().toggleVisibility(this); + } + + /** + * Returns the actionsVisible property which controls whether or not + * additional table Actions should be included into the popup. + * + * @return a boolean indicating whether or not additional table Actions + * are visible + */ + public boolean getAdditionalActionsVisible() { + return additionalActionsVisible; + } + + + /** + * Sets the additonalActionsVisible property. It controls whether or + * not additional table actions should be included into the popup.

            + * + * The default value is true. + * + * @param additionalActionsVisible the additionalActionsVisible to set + */ + public void setAdditionalActionsVisible(boolean additionalActionsVisible) { + if (additionalActionsVisible == getAdditionalActionsVisible()) return; + boolean old = getAdditionalActionsVisible(); + this.additionalActionsVisible = additionalActionsVisible; + populatePopup(); + firePropertyChange("additionalActionsVisible", old, getAdditionalActionsVisible()); + } + + /** + * Sets the grouper to use for grouping the additional actions. Maybe null to + * have no additional grouping. Has no effect + * if the ColumnControlPopup doesn't implement Groupable. The default + * ColumnControlPopup supports Groupable, but is instantiated without a Grouper. + * + * @param grouper + */ + public void setActionGrouper(ActionGrouper grouper) { + if (!(getColumnControlPopup() instanceof ActionGroupable)) return; + ((ActionGroupable) getColumnControlPopup()).setActionGrouper(grouper); + populatePopup(); + } + + @Override + public void applyComponentOrientation(ComponentOrientation o) { + super.applyComponentOrientation(o); + getColumnControlPopup().applyComponentOrientation(o); + } + + +//-------------------------- Action in synch with column properties + /** + * A specialized Action which takes care of keeping in synch with + * TableColumn state. + * + * NOTE: client must call releaseColumn if this action is no longer needed! + * + */ + public class ColumnVisibilityAction extends AbstractActionExt { + + private TableColumn column; + + private PropertyChangeListener columnListener; + + /** flag to distinguish selection changes triggered by + * column's property change from those triggered by + * user interaction. Hack around #212-swingx. + */ + private boolean fromColumn; + + /** + * Creates a action synched to the table column. + * + * @param column the TableColumn to keep synched to. + */ + public ColumnVisibilityAction(TableColumn column) { + super((String) null); + setStateAction(); + installColumn(column); + } + + /** + * Releases all references to the synched TableColumn. + * Client code must call this method if the + * action is no longer needed. After calling this action must not be + * used any longer. + */ + public void releaseColumn() { + column.removePropertyChangeListener(columnListener); + column = null; + } + + /** + * Returns true if the action is enabled. Returns + * true only if the action is enabled and the table + * column can be controlled. + * + * @return true if the action is enabled, false otherwise + * @see #canControlColumn() + */ + @Override + public boolean isEnabled() { + return super.isEnabled() && canControlColumn(); + } + + /** + * Returns flag to indicate if column's visibility can + * be controlled. Minimal requirement is that column is of type + * TableColumnExt. + * + * @return boolean to indicate if columns's visibility can be controlled. + */ + protected boolean canControlColumn() { + // JW: should have direction? control is from action to column, the + // other way round should be guaranteed always + return (column instanceof TableColumnExt); + } + + @Override + public void itemStateChanged(final ItemEvent e) { + if (canControlColumn()) { + if ((e.getStateChange() == ItemEvent.DESELECTED) + //JW: guarding against 1 leads to #212-swingx: setting + // column visibility programatically fails if + // the current column is the second last visible + // guarding against 0 leads to hiding all columns + // by deselecting the menu item. + && (table.getColumnCount() <= 1) + // JW Fixed #212: basically implemented Rob's idea to distinguish + // event sources instead of unconditionally reselect + // not entirely sure if the state transitions are completely + // defined but all related tests are passing now. + && !fromColumn) { + reselect(); + } else { + setSelected(e.getStateChange() == ItemEvent.SELECTED); + } + } + } + + + @Override + public synchronized void setSelected(boolean newValue) { + super.setSelected(newValue); + if (canControlColumn()) { + if (!fromColumn) + ((TableColumnExt) column).setVisible(newValue); + } + } + + /** + * Does nothing. Synch from action state to TableColumn state + * is done in itemStateChanged. + */ + @Override + public void actionPerformed(ActionEvent e) { + + } + + /** + * Synchs selected property to visible. This + * is called on change of tablecolumn's visible property. + * + * @param visible column visible state to synch to. + */ + private void updateFromColumnVisible(boolean visible) { +// /*boolean*/ visible = true; +// if (canControlColumn()) { +// visible = ((TableColumnExt) column).isVisible(); +// } + fromColumn = true; + setSelected(visible); + fromColumn = false; + } + + + protected void updateFromColumnHideable(boolean hideable) { + setEnabled(hideable); + } + + /** + * Synchs name property to value. This is called on change of + * tableColumn's headerValue property. + * + * @param value + */ + private void updateFromColumnHeader(Object value) { + setName(String.valueOf(value)); + } + + /** + * Enforces selected to true. Called if user interaction + * tried to de-select the last single visible column. + * + */ + private void reselect() { + firePropertyChange("selected", null, Boolean.TRUE); + } + + // -------------- init + private void installColumn(TableColumn column) { + this.column = column; + column.addPropertyChangeListener(getColumnListener()); + updateFromColumnHeader(column.getHeaderValue()); + // #429-swing: actionCommand must be string + if (column.getIdentifier() != null) { + setActionCommand(column.getIdentifier().toString()); + } + boolean visible = (column instanceof TableColumnExt) ? + ((TableColumnExt) column).isVisible() : true; + updateFromColumnVisible(visible); + } + + /** + * Returns the listener to column's property changes. The listener + * is created lazily if necessary. + * + * @return the PropertyChangeListener listening to + * TableColumn's property changes, guaranteed to be + * not null. + */ + protected PropertyChangeListener getColumnListener() { + if (columnListener == null) { + columnListener = createPropertyChangeListener(); + } + return columnListener; + } + + /** + * Creates and returns the listener to column's property changes. + * Subclasses are free to roll their own. + *

            + * Implementation note: this listener reacts to column's + * visible and headerValue properties and + * calls the respective updateFromXX methodes. + * + * @return the PropertyChangeListener to use with the + * column + */ + protected PropertyChangeListener createPropertyChangeListener() { + return new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + if ("visible".equals(evt.getPropertyName())) { + updateFromColumnVisible((Boolean) evt.getNewValue()); + } else if ("headerValue".equals(evt.getPropertyName())) { + updateFromColumnHeader(evt.getNewValue()); + } else if ("hideable".equals(evt.getPropertyName())) { + updateFromColumnHideable((Boolean) evt.getNewValue()); + } + } + }; + } + } + + // ---------------------- the popup + + /** + * A default implementation of ColumnControlPopup. + * It uses a JPopupMenu with + * MenuItems corresponding to the Actions as + * provided by the ColumnControlButton. + * + * + */ + public class DefaultColumnControlPopup implements ColumnControlPopup, ActionGroupable { + private JPopupMenu popupMenu; + private ActionGrouper grouper; + + public DefaultColumnControlPopup() { + this(null); + } + + //------------------ public methods to control visibility status + + public DefaultColumnControlPopup(ActionGrouper grouper) { + this.grouper = grouper; + } + + /** + * @inheritDoc + * + */ + @Override + public void updateUI() { + SwingUtilities.updateComponentTreeUI(getPopupMenu()); + } + + /** + * @inheritDoc + * + */ + @Override + public void toggleVisibility(JComponent owner) { + JPopupMenu popupMenu = getPopupMenu(); + if (popupMenu.isVisible()) { + popupMenu.setVisible(false); + } else if (popupMenu.getComponentCount() > 0) { + Dimension buttonSize = owner.getSize(); + int xPos = owner.getComponentOrientation().isLeftToRight() ? buttonSize.width + - popupMenu.getPreferredSize().width + : 0; + popupMenu.show(owner, + // JW: trying to allow popup without CCB showing + // weird behaviour +// owner.isShowing()? owner : null, + xPos, buttonSize.height); + } + + } + + /** + * @inheritDoc + * + */ + @Override + public void applyComponentOrientation(ComponentOrientation o) { + getPopupMenu().applyComponentOrientation(o); + + } + + //-------------------- public methods to manipulate popup contents. + + /** + * @inheritDoc + * + */ + @Override + public void removeAll() { + getPopupMenu().removeAll(); + } + + + /** + * @inheritDoc + * + */ + @Override + public void addVisibilityActionItems( + List actions) { + addItems(new ArrayList(actions)); + + } + + + /** + * @inheritDoc + * + */ + @Override + public void addAdditionalActionItems(List actions) { + if (actions.size() == 0) + return; + // JW: this is a reference to the enclosing class + // prevents to make this implementation static + // Hmmm...any way around? + if (canControl()) { + addSeparator(); + } + + if (getGrouper() == null) { + addItems(actions); + return; + } + List> groups = grouper.group(actions); + for (List group : groups) { + addItems(group); + if (group != groups.get(groups.size()- 1)) + addSeparator(); + + } + + } + + //--------------------------- internal helpers to manipulate popups content + + /** + * Here: creates and adds a menuItem to the popup for every + * Action in the list. Does nothing if + * if the list is empty. + * + * PRE: actions != null. + * + * @param actions a list containing the actions to add to the popup. + * Must not be null. + * + */ + protected void addItems(List actions) { + ActionContainerFactory factory = new ActionContainerFactory(null); + for (Action action : actions) { + addItem(factory.createMenuItem(action)); + } + } + + /** + * adds a separator to the popup. + * + */ + protected void addSeparator() { + getPopupMenu().addSeparator(); + } + + /** + * + * @param item the menuItem to add to the popup. + */ + protected void addItem(JMenuItem item) { + getPopupMenu().add(item); + } + + /** + * + * @return the popup to add menuitems. Guaranteed to be != null. + */ + protected JPopupMenu getPopupMenu() { + if (popupMenu == null) { + popupMenu = new JPopupMenu(); + } + return popupMenu; + } + + // --------------- implement Groupable + + @Override + public void setActionGrouper(ActionGrouper grouper) { + this.grouper = grouper; + } + + protected ActionGrouper getGrouper() { + return grouper; + } + + } + /** + * Returns to popup component for user interaction. Lazily + * creates the component if necessary. + * + * @return the ColumnControlPopup for showing the items, guaranteed + * to be not null. + * @see #createColumnControlPopup() + */ + protected ColumnControlPopup getColumnControlPopup() { + if (popup == null) { + popup = createColumnControlPopup(); + } + return popup; + } + + /** + * Factory method to return a ColumnControlPopup. + * Subclasses can override to hook custom implementations. + * + * @return the ColumnControlPopup used. + */ + protected ColumnControlPopup createColumnControlPopup() { + return new DefaultColumnControlPopup(); + } + + +//-------------------------- updates from table propertyChangelistnere + + /** + * Adjusts internal state after table's column model property has changed. + * Handles cleanup of listeners to the old/new columnModel (Note, that + * it listens to the column model only if it can control column visibility). + * Updates content of popup. + * + * @param oldModel the old TableColumnModel we had been listening to. + */ + protected void updateFromColumnModelChange(TableColumnModel oldModel) { + if (oldModel != null) { + oldModel.removeColumnModelListener(columnModelListener); + } + populatePopup(); + if (canControl()) { + table.getColumnModel().addColumnModelListener(getColumnModelListener()); + } + } + + /** + * Synchs this button's enabled with table's enabled. + * + */ + protected void updateFromTableEnabledChanged() { + getAction().setEnabled(table.isEnabled()); + + } + /** + * Method to check if we can control column visibility POST: if true we can + * be sure to have an extended TableColumnModel + * + * @return boolean to indicate if controlling the visibility state is + * possible. + */ + protected boolean canControl() { + return table.getColumnModel() instanceof TableColumnModelExt; + } + +// ------------------------ updating the popup + /** + * Populates the popup from scratch. + * + * If applicable, creates and adds column visibility actions. Always adds + * additional actions. + */ + protected void populatePopup() { + clearAll(); + if (canControl()) { + createVisibilityActions(); + addVisibilityActionItems(); + } + addAdditionalActionItems(); + } + + /** + * + * removes all components from the popup, making sure to release all + * columnVisibility actions. + * + */ + protected void clearAll() { + clearColumnVisibilityActions(); + getColumnControlPopup().removeAll(); + } + + + /** + * Releases actions and clears list of actions. + * + */ + protected void clearColumnVisibilityActions() { + if (columnVisibilityActions == null) + return; + for (ColumnVisibilityAction action : columnVisibilityActions) { + action.releaseColumn(); + } + columnVisibilityActions.clear(); + } + + + /** + * Adds visibility actions into the popup view. + * + * Here: delegates the list of actions to the DefaultColumnControlPopup. + *

            + * PRE: columnVisibilityActions populated before calling this. + * + */ + protected void addVisibilityActionItems() { + getColumnControlPopup().addVisibilityActionItems( + Collections.unmodifiableList(getColumnVisibilityActions())); + } + + /** + * Adds additional actions to the popup, if additionalActionsVisible is true, + * does nothing otherwise.

            + * + * Here: delegates the list of actions as returned by #getAdditionalActions() + * to the DefaultColumnControlPopup. + * Does nothing if #getColumnActions() is empty. + * + */ + protected void addAdditionalActionItems() { + if (!getAdditionalActionsVisible()) return; + getColumnControlPopup().addAdditionalActionItems( + Collections.unmodifiableList(getAdditionalActions())); + } + + + /** + * Creates and adds a ColumnVisiblityAction for every column that should be + * togglable via the column control.

            + * + * Here: all table columns contained in the TableColumnModel - + * visible and invisible columns - to createColumnVisibilityAction and + * adds all not null return values. + * + *

            + * PRE: canControl() + * + * @see #createColumnVisibilityAction + */ + protected void createVisibilityActions() { + List columns = table.getColumns(true); + for (TableColumn column : columns) { + ColumnVisibilityAction action = createColumnVisibilityAction(column); + if (action != null) { + getColumnVisibilityActions().add(action); + } + } + + } + + /** + * Creates and returns a ColumnVisibilityAction for the given + * TableColumn. The return value might be null, f.i. if the + * column should not be allowed to be toggled. + * + * @param column the TableColumn to use for the action + * @return a ColumnVisibilityAction to use for the given column, + * may be null. + */ + protected ColumnVisibilityAction createColumnVisibilityAction(TableColumn column) { + return new ColumnVisibilityAction(column); + } + + /** + * Lazyly creates and returns the List of visibility actions. + * + * @return the list of visibility actions, guaranteed to be != null. + */ + protected List getColumnVisibilityActions() { + if (columnVisibilityActions == null) { + columnVisibilityActions = new ArrayList(); + } + return columnVisibilityActions; + } + + + /** + * creates and returns a list of additional Actions to add to the popup. + * Here: the actions are looked up in the table's actionMap according + * to the keys as returned from #getColumnControlActionKeys(); + * + * @return a list containing all additional actions to include into the popup. + */ + protected List getAdditionalActions() { + List actionKeys = getColumnControlActionKeys(); + List actions = new ArrayList(); + for (Object key : actionKeys) { + actions.add(table.getActionMap().get(key)); + } + return actions; + } + + /** + * Looks up and returns action keys to access actions in the + * table's actionMap which should be included into the popup. + * + * Here: all keys with isColumnControlActionKey(key). The list + * is sorted by those keys. + * + * @return the action keys of table's actionMap entries whose + * action should be included into the popup. + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + protected List getColumnControlActionKeys() { + Object[] allKeys = table.getActionMap().allKeys(); + List columnKeys = new ArrayList(); + for (int i = 0; i < allKeys.length; i++) { + if (isColumnControlActionKey(allKeys[i])) { + columnKeys.add(allKeys[i]); + } + } + // JW: this will blow for non-String keys! + // so this method is less decoupled from the + // decision method isControl than expected. + Collections.sort(columnKeys); + return columnKeys; + } + + /** + * Here: true if a String key starts with #COLUMN_CONTROL_MARKER. + * + * @param actionKey a key in the table's actionMap. + * @return a boolean to indicate whether the given actionKey maps to + * an action which should be included into the popup. + * + */ + protected boolean isColumnControlActionKey(Object actionKey) { + return (actionKey instanceof String) && + ((String) actionKey).startsWith(COLUMN_CONTROL_MARKER); + } + + + //--------------------------- init + + private void installTable(JXTable table) { + this.table = table; + table.addPropertyChangeListener(getTablePropertyChangeListener()); + updateFromColumnModelChange(null); + updateFromTableEnabledChanged(); + } + + + /** + * Initialize the column control button's gui + */ + private void init() { + setFocusPainted(false); + setFocusable(false); + // this is a trick to get hold of the client prop which + // prevents closing of the popup + JComboBox box = new JComboBox(); + Object preventHide = box.getClientProperty("doNotCancelPopup"); + putClientProperty("doNotCancelPopup", preventHide); + additionalActionsVisible = true; + } + + + /** + * Creates and returns the default action for this button. + * @param icon + * + * @param icon the Icon to use in the action. + * @return the default action. + */ + private Action createControlAction(Icon icon) { + + Action control = new AbstractAction() { + + @Override + public void actionPerformed(ActionEvent e) { + togglePopup(); + } + + }; + control.putValue(Action.SMALL_ICON, icon); + return control; + } + + // -------------------------------- listeners + + /** + * Returns the listener to table's property changes. The listener is + * lazily created if necessary. + * @return the PropertyChangeListener for use with the + * table, guaranteed to be not null. + */ + protected PropertyChangeListener getTablePropertyChangeListener() { + if (tablePropertyChangeListener == null) { + tablePropertyChangeListener = createTablePropertyChangeListener(); + } + return tablePropertyChangeListener; + } + + /** + * Creates the listener to table's property changes. Subclasses are free + * to roll their own.

            + * Implementation note: this listener reacts to table's enabled and + * columnModel properties and calls the respective + * updateFromXX methodes. + * + * @return the PropertyChangeListener for use with the table. + */ + protected PropertyChangeListener createTablePropertyChangeListener() { + return new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + if ("columnModel".equals(evt.getPropertyName())) { + updateFromColumnModelChange((TableColumnModel) evt + .getOldValue()); + } else if ("enabled".equals(evt.getPropertyName())) { + updateFromTableEnabledChanged(); + } + } + }; + } + + /** + * Returns the listener to table's column model. The listener is + * lazily created if necessary. + * @return the TableColumnModelListener for use with the + * table's column model, guaranteed to be not null. + */ + protected TableColumnModelListener getColumnModelListener() { + if (columnModelListener == null) { + columnModelListener = createColumnModelListener(); + } + return columnModelListener; + } + + /** + * Creates the listener to columnModel. Subclasses are free to roll their + * own. + *

            + * Implementation note: this listener reacts to "real" columnRemoved/-Added by + * populating the popups content from scratch. + * + * @return the TableColumnModelListener for use with the + * table's columnModel. + */ + protected TableColumnModelListener createColumnModelListener() { + return new TableColumnModelListener() { + /** Tells listeners that a column was added to the model. */ + @Override + public void columnAdded(TableColumnModelEvent e) { + // quickfix for #192 + if (!isVisibilityChange(e, true)) { + populatePopup(); + } + } + + /** Tells listeners that a column was removed from the model. */ + @Override + public void columnRemoved(TableColumnModelEvent e) { + if (!isVisibilityChange(e, false)) { + populatePopup(); + } + } + + /** + * Check if the add/remove event is triggered by a move to/from the + * invisible columns. + * + * PRE: the event must be received in columnAdded/Removed. + * + * @param e the received event + * @param added if true the event is assumed to be received via + * columnAdded, otherwise via columnRemoved. + * @return boolean indicating whether the removed/added is a side-effect + * of hiding/showing the column. + */ + private boolean isVisibilityChange(TableColumnModelEvent e, + boolean added) { + // can't tell + if (!(e.getSource() instanceof DefaultTableColumnModelExt)) + return false; + DefaultTableColumnModelExt model = (DefaultTableColumnModelExt) e + .getSource(); + if (added) { + return model.isAddedFromInvisibleEvent(e.getToIndex()); + } else { + return model.isRemovedToInvisibleEvent(e.getFromIndex()); + } + } + + /** Tells listeners that a column was repositioned. */ + @Override + public void columnMoved(TableColumnModelEvent e) { + } + + /** Tells listeners that a column was moved due to a margin change. */ + @Override + public void columnMarginChanged(ChangeEvent e) { + } + + /** + * Tells listeners that the selection model of the TableColumnModel + * changed. + */ + @Override + public void columnSelectionChanged(ListSelectionEvent e) { + } + }; + } + + +} // end class ColumnControlButton diff --git a/src/main/java/org/jdesktop/swingx/table/ColumnControlPopup.java b/src/main/java/org/jdesktop/swingx/table/ColumnControlPopup.java new file mode 100644 index 0000000000..976b4c494b --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/table/ColumnControlPopup.java @@ -0,0 +1,105 @@ +/* + * $Id: ColumnControlPopup.java 4065 2011-08-19 13:28:26Z kleopatra $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.table; + +import org.jdesktop.swingx.action.AbstractActionExt; +import org.jdesktop.swingx.plaf.UIDependent; + +import javax.swing.*; +import java.awt.*; +import java.util.List; + +/** + * Encapsulates the popup component which is the delegate for + * all popup visuals, used by a ColumnControlButton. + *

            + * For now, this class a simple extraction of what a ColumnControl needs. + * Usage will drive further evolution. + * + */ +public interface ColumnControlPopup extends UIDependent { + /** + * Toggles the popup's visibility. This method is responsible for + * placing itself relative to the given owner if toggled to visible. + * + * @param owner the JComponent which triggered the visibility change, typically + * a ColumnControlButton. + */ + void toggleVisibility(JComponent owner); + + /** + * Applies the specified component orientation to all internal widgets. + * This method must be called by the owner if its component orientation + * changes. + * + * @param o the componentOrientation to apply to all internal widgets. + * @see JComponent#applyComponentOrientation(ComponentOrientation). + */ + void applyComponentOrientation(ComponentOrientation o); + + /** + * Removes all items from the popup. + */ + void removeAll(); + + /** + * Adds items corresponding to the column's visibility actions. + *

            + * Each Action in the list is a stateAction, + * its selected property bound to a column's + * visible property, that is toggling the selected will + * toggle the column's visibility (if the action is enabled). + * + * The Actions name property is bound to + * the column's title. + * + * @param actions List of AbstractActionExt to add. + */ + void addVisibilityActionItems(List actions); + // JW: dooohhh ... what a winding description ... + // sure need to have a better abstraction! + // + + /** + * Adds additional actions to the popup. + * + * @param actions List of Actions to add to the popup. + */ + void addAdditionalActionItems(List actions); + + /** + * Splits and returns a List of actions into sub-lists. + */ + public interface ActionGrouper { + List> group(List actions); + } + + /** + * Interface indicating support for grouping of menu actions. + * Implementations of ColumnControlPopup may implement this + * if they support grouping of additional action. + */ + public interface ActionGroupable { + public void setActionGrouper(ActionGrouper grouper); + } + + +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/table/ColumnFactory.java b/src/main/java/org/jdesktop/swingx/table/ColumnFactory.java new file mode 100644 index 0000000000..c08d405f50 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/table/ColumnFactory.java @@ -0,0 +1,465 @@ +/* + * $Id: ColumnFactory.java 3554 2009-11-06 09:07:55Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.table; + +import org.jdesktop.swingx.JXTable; + +import javax.swing.table.JTableHeader; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableModel; +import java.awt.*; + +/** + * Creates and configures TableColumnExts. + *

            + * TODO JW: explain types of configuration - initial from tableModel, initial + * from table context, user triggered at runtime. + *

            + * + * JXTable delegates all TableColumn creation and + * configuration to a ColumnFactory. Enhanced column + * configuration should be implemented in a custom factory subclass. The example + * beautifies the column titles to always start with a capital letter: + * + *

            + * 
            + *    MyColumnFactory extends ColumnFactory {
            + *        //@Override
            + *        public void configureTableColumn(TableModel model, 
            + *            TableColumnExt columnExt) {
            + *            super.configureTableColumn(model, columnExt);
            + *            String title = columnExt.getTitle();
            + *            title = title.substring(0,1).toUpperCase() + title.substring(1).toLowerCase();
            + *            columnExt.setTitle(title);
            + *        }
            + *    };
            + * 
            + * 
            + * + * By default a single instance is shared across all tables of an application. + * This instance can be replaced by a custom implementation, preferably "early" + * in the application's lifetime. + * + *
            
            + * ColumnFactory.setInstance(new MyColumnFactory());
            + * 
            + * + * Alternatively, any instance of JXTable can be configured + * individually with its own ColumnFactory. + * + *
            + *  
            + * JXTable table = new JXTable();
            + * table.setColumnFactory(new MyColumnFactory());
            + * table.setModel(myTableModel);
            + * 
            + *  
            + * + *

            + * + * @see JXTable#setColumnFactory(ColumnFactory) + * + * @author Jeanette Winzenburg + * @author M.Hillary (the pack code) + */ +public class ColumnFactory { + + /** the shared instance. */ + private static ColumnFactory columnFactory; + /** the default margin to use in pack. */ + private int packMargin = 4; + + /** + * Returns the shared default factory. + * + * @return the shared instance of ColumnFactory + * @see #setInstance(ColumnFactory) + */ + public static synchronized ColumnFactory getInstance() { + if (columnFactory == null) { + columnFactory = new ColumnFactory(); + } + return columnFactory; + } + + /** + * Sets the shared default factory. The shared instance is used + * by JXTable if none has been set individually. + * + * @param factory the default column factory. + * @see #getInstance() + * @see JXTable#getColumnFactory() + */ + public static synchronized void setInstance(ColumnFactory factory) { + columnFactory = factory; + } + + /** + * Creates and configures a TableColumnExt. JXTable calls + * this method for each column in the TableModel. + * + * @param model the TableModel to read configuration properties from + * @param modelIndex column index in model coordinates + * @return a TableColumnExt to use for the modelIndex + * @throws NPE if model == null + * @throws IllegalStateException if the modelIndex is invalid + * (in coordinate space of the tablemodel) + * + * @see #createTableColumn(int) + * @see #configureTableColumn(TableModel, TableColumnExt) + * @see JXTable#createDefaultColumnsFromModel() + */ + public TableColumnExt createAndConfigureTableColumn(TableModel model, int modelIndex) { + TableColumnExt column = createTableColumn(modelIndex); + if (column != null) { + configureTableColumn(model, column); + } + return column; + } + + /** + * Creates a table column with modelIndex. + *

            + * The factory's column creation is passed through this method, so + * subclasses can override to return custom column types. + * + * @param modelIndex column index in model coordinates + * @return a TableColumnExt with modelIndex + * + * @see #createAndConfigureTableColumn(TableModel, int) + * + */ + public TableColumnExt createTableColumn(int modelIndex) { + return new TableColumnExt(modelIndex); + } + + /** + * Configure column properties from TableModel. This implementation + * sets the column's headerValue property from the + * model's columnName. + *

            + * + * The factory's initial column configuration is passed through this method, so + * subclasses can override to customize. + *

            + * + * @param model the TableModel to read configuration properties from + * @param columnExt the TableColumnExt to configure. + * @throws NullPointerException if model or column == null + * @throws IllegalStateException if column does not have valid modelIndex + * (in coordinate space of the tablemodel) + * + * @see #createAndConfigureTableColumn(TableModel, int) + */ + public void configureTableColumn(TableModel model, TableColumnExt columnExt) { + if ((columnExt.getModelIndex() < 0) + || (columnExt.getModelIndex() >= model.getColumnCount())) + throw new IllegalStateException("column must have valid modelIndex"); + columnExt.setHeaderValue(model.getColumnName(columnExt.getModelIndex())); + } + + + /** + * Configures column initial widths properties from JXTable. + * This implementation sets the column's + * preferredWidth with the strategy: + *

              if the column has a prototype, measure the rendering + * component with the prototype as value and use that as + * pref width + *
                if the column has no prototype, use the standard magic + * pref width (= 75) + *
                  try to measure the column's header and use it's preferred + * width if it exceeds the former. + *
                + * + * TODO JW - rename method to better convey what's happening, maybe + * initializeColumnWidths like the old method in JXTable.

                + * + * TODO JW - how to handle default settings which are different from + * standard 75? + * + * @param table the context the column will live in. + * @param columnExt the Tablecolumn to configure. + * + * @see JXTable#getPreferredScrollableViewportSize() + */ + public void configureColumnWidths(JXTable table, TableColumnExt columnExt) { + /* + * PENDING JW: really only called once in a table's lifetime? + * unfortunately: yes - should be called always after structureChanged. + * + */ + // magic value: default in TableColumn + int prefWidth = 75 - table.getColumnMargin(); + int prototypeWidth = calcPrototypeWidth(table, columnExt); + if (prototypeWidth > 0) { + prefWidth = prototypeWidth; + } + int headerWidth = calcHeaderWidth(table, columnExt); + prefWidth = Math.max(prefWidth, headerWidth); + prefWidth += table.getColumnModel().getColumnMargin(); + columnExt.setPreferredWidth(prefWidth); + } + + /** + * Calculates and returns the preferred scrollable viewport + * width of the given table. Subclasses are free to override + * and implement a custom strategy.

                + * + * This implementation sums the pref widths of the first + * visibleColumnCount contained visible tableColumns. If + * the table contains less columns, the standard preferred + * width per column is added to the result. + * + * @param table the table containing the columns + */ + public int getPreferredScrollableViewportWidth(JXTable table) { + int w = 0; + int count; + if (table.getVisibleColumnCount() < 0) { + count = table.getColumnCount(); + } else { + count = Math.min(table.getColumnCount(), table.getVisibleColumnCount()); + } + for (int i = 0; i < count; i++) { + // sum up column's pref size, until maximal the + // visibleColumnCount + w += table.getColumn(i).getPreferredWidth(); + } + if (count < table.getVisibleColumnCount()) { + w += (table.getVisibleColumnCount() - count) * 75; + } + return w; + + } + /** + * Measures and returns the preferred width of the header. Returns -1 if not + * applicable. + * + * @param table the component the renderer lives in + * @param columnExt the TableColumn to configure + * @return the preferred width of the header or -1 if none. + */ + protected int calcHeaderWidth(JXTable table, TableColumnExt columnExt) { + int prototypeWidth = -1; + // now calculate how much room the column header wants + TableCellRenderer renderer = getHeaderRenderer(table, columnExt); + if (renderer != null) { + Component comp = renderer.getTableCellRendererComponent(table, + columnExt.getHeaderValue(), false, false, -1, -1); + + prototypeWidth = comp.getPreferredSize().width; + } + return prototypeWidth; + } + + /** + * Measures and returns the preferred width of the rendering component + * configured with the prototype value, if any. Returns -1 if not + * applicable. + * + * @param table the component the renderer lives in + * @param columnExt the TableColumn to configure + * @return the preferred width of the prototype or -1 if none. + */ + protected int calcPrototypeWidth(JXTable table, TableColumnExt columnExt) { + int prototypeWidth = -1; + Object prototypeValue = columnExt.getPrototypeValue(); + if (prototypeValue != null) { + // calculate how much room the prototypeValue requires + TableCellRenderer cellRenderer = getCellRenderer(table, columnExt); + Component comp = cellRenderer.getTableCellRendererComponent(table, + prototypeValue, false, false, 0, -1); + prototypeWidth = comp.getPreferredSize().width; + } + return prototypeWidth; + } + + /** + * Returns the cell renderer to use for measuring. Delegates to + * JXTable for visible columns, duplicates table logic for hidden + * columns.

                + * + * @param table the table which provides the renderer + * @param columnExt the TableColumn to configure + * + * @return returns a cell renderer for measuring. + */ + protected TableCellRenderer getCellRenderer(JXTable table, TableColumnExt columnExt) { + int viewIndex = table.convertColumnIndexToView(columnExt + .getModelIndex()); + if (viewIndex >= 0) { + // JW: ok to not guard against rowCount < 0? + // technically, the index should be a valid coordinate + return table.getCellRenderer(0, viewIndex); + } + // hidden column - need api on JXTable to access renderer for hidden? + // here we duplicate JXTable api ... maybe by-passing the strategy + // implemented there + TableCellRenderer renderer = columnExt.getCellRenderer(); + if (renderer == null) { + renderer = table.getDefaultRenderer(table.getModel().getColumnClass(columnExt.getModelIndex())); + } + return renderer; + } + + /** + * Looks up and returns the renderer used for the column's header.

                + * + * @param table the table which contains the column + * @param columnExt the column to lookup the header renderer for + * @return the renderer for the columns header, may be null. + */ + protected TableCellRenderer getHeaderRenderer(JXTable table, TableColumnExt columnExt) { + TableCellRenderer renderer = columnExt.getHeaderRenderer(); + if (renderer == null) { + JTableHeader header = table.getTableHeader(); + if (header != null) { + renderer = header.getDefaultRenderer(); + } + } + // JW: default to something if null? + // if so, could be table's default object/string header? + return renderer; + } + + + /** + * Configures the column's preferredWidth to fit the content. + * It respects the table context, a margin to add and a maximum width. This + * is typically called in response to a user gesture to adjust the column's + * width to the "widest" cell content of a column. + *

                + * + * This implementation loops through all rows of the given column and + * measures the renderers pref width (it's a potential performance sink). + * Subclasses can override to implement a different strategy. + *

                + * + * Note: though 2 * margin is added as spacing, this does not imply + * a left/right symmetry - it's up to the table to place the renderer and/or + * the renderer/highlighter to configure a border.

                + * + * PENDING: support pack for hidden column? + * This implementation can't handle it! For now, added doc and + * fail-fast. + * + * @param table the context the column will live in. + * @param columnExt the column to configure. + * @param margin the extra spacing to add twice, if -1 uses this factories + * default + * @param max an upper limit to preferredWidth, -1 is interpreted as no + * limit + * @throws IllegalStateException if column is not visible + * + * @see #setDefaultPackMargin(int) + * @see JXTable#packTable(int) + * @see JXTable#packColumn(int, int) + * + */ + public void packColumn(JXTable table, TableColumnExt columnExt, int margin, + int max) { + if (!columnExt.isVisible()) + throw new IllegalStateException("column must be visible to pack"); + + int column = table.convertColumnIndexToView(columnExt.getModelIndex()); + int width = 0; + TableCellRenderer headerRenderer = getHeaderRenderer(table, columnExt); + if (headerRenderer != null) { + Component comp = headerRenderer.getTableCellRendererComponent(table, + columnExt.getHeaderValue(), false, false, 0, column); + width = comp.getPreferredSize().width; + } + // PENDING JW: slightly inconsistent - the getCellRenderer here + // returns a (guessed) renderer for invisible columns which must not + // be used in the loop. For now that's okay, as we back out early anyway + TableCellRenderer renderer = getCellRenderer(table, columnExt); + for (int r = 0; r < getRowCount(table); r++) { + // JW: fix for #1215-swing as suggested by the reporter adrienclerc + Component comp = table.prepareRenderer(renderer, r, column); +// Component comp = renderer.getTableCellRendererComponent(table, table +// .getValueAt(r, column), false, false, r, column); + width = Math.max(width, comp.getPreferredSize().width); + } + if (margin < 0) { + margin = getDefaultPackMargin(); + } + width += 2 * margin; + + /* Check if the width exceeds the max */ + if (max != -1 && width > max) + width = max; + + columnExt.setPreferredWidth(width); + + } + + /** + * Returns the number of table view rows accessible during row-related + * config. All row-related access is bounded by the value returned from this + * method. + * + * Here: delegates to table.getRowCount(). + *

                + * + * Subclasses can override to reduce the number (for performance) or support + * restrictions due to lazy loading, f.i. Implementors must guarantee that + * view row access with 0 <= row < getRowCount(JXTable) + * succeeds. + * + * @param table the table to access + * @return valid rowCount + */ + protected int getRowCount(JXTable table) { + return table.getRowCount(); + } + +// ------------------------ default state + + /** + * Returns the default pack margin. + * + * @return the default pack margin to use in packColumn. + * + * @see #setDefaultPackMargin(int) + */ + public int getDefaultPackMargin() { + return packMargin; + } + + /** + * Sets the default pack margin.

                + * + * Note: this is not really a margin in the sense of symmetrically + * adding white space to the left/right of a cell's content. It's simply an + * amount of space which is added twice to the measured widths in packColumn. + * + * @param margin the default marging to use in packColumn. + * + * @see #getDefaultPackMargin() + * @see #packColumn(JXTable, TableColumnExt, int, int) + */ + public void setDefaultPackMargin(int margin) { + this.packMargin = margin; + } + + +} diff --git a/src/main/java/org/jdesktop/swingx/table/DatePickerCellEditor.java b/src/main/java/org/jdesktop/swingx/table/DatePickerCellEditor.java new file mode 100644 index 0000000000..3bb251a775 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/table/DatePickerCellEditor.java @@ -0,0 +1,334 @@ +/* + * $Id: DatePickerCellEditor.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.table; + +import org.jdesktop.swingx.JXDatePicker; +import org.jdesktop.swingx.treetable.AbstractMutableTreeTableNode; + +import javax.swing.*; +import javax.swing.table.TableCellEditor; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreeCellEditor; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseEvent; +import java.text.DateFormat; +import java.text.ParseException; +import java.util.Date; +import java.util.EventObject; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * A CellEditor using a JXDatePicker as editor component.

                + * + * NOTE: this class will be moved! + * + * @author Richard Osbald + * @author Jeanette Winzenburg + */ +public class DatePickerCellEditor extends AbstractCellEditor implements + TableCellEditor, TreeCellEditor { + + protected JXDatePicker datePicker; + + protected DateFormat dateFormat; + + protected int clickCountToStart = 2; + + private ActionListener pickerActionListener; + + protected boolean ignoreAction; + + private static Logger logger = Logger.getLogger(DatePickerCellEditor.class + .getName()); + + private static final long serialVersionUID = -1L; + + /** + * Instantiates a editor with the default dateFormat. + * + * PENDING: always override default from DatePicker? + * + */ + public DatePickerCellEditor() { + this(null); + } + + /** + * Instantiates an editor with the given dateFormat. If + * null, the datePickers default is used. + * + * @param dateFormat + */ + public DatePickerCellEditor(DateFormat dateFormat) { + // JW: the copy is used to synchronize .. can + // we use something else? + this.dateFormat = dateFormat != null ? dateFormat : + DateFormat.getDateInstance(); + datePicker = new JXDatePicker(); + // default border crushes the editor/combo + datePicker.getEditor().setBorder( + BorderFactory.createEmptyBorder(0, 1, 0, 1)); + // should be fixed by j2se 6.0 + datePicker.setFont(UIManager.getDefaults().getFont("TextField.font")); + if (dateFormat != null) { + datePicker.setFormats(dateFormat); + } + datePicker.addActionListener(getPickerActionListener()); + } + +//-------------------- CellEditor + + /** + * Returns the pickers date. + * + * Note: the date is only meaningful after a stopEditing and + * before the next call to getTableCellEditorComponent. + */ + @Override + public Date getCellEditorValue() { + return datePicker.getDate(); + } + + @Override + public boolean isCellEditable(EventObject anEvent) { + if (anEvent instanceof MouseEvent) { + return ((MouseEvent) anEvent).getClickCount() >= getClickCountToStart(); + } + return super.isCellEditable(anEvent); + } + + /** + * {@inheritDoc} + *

                + * + * Overridden to commit pending edits. If commit successful, returns super, + * else returns false. + * + * + */ + @Override + public boolean stopCellEditing() { + ignoreAction = true; + boolean canCommit = commitChange(); + ignoreAction = false; + if (canCommit) { + return super.stopCellEditing(); + } + return false; + } + + /** + * Specifies the number of clicks needed to start editing. + * + * @param count an int specifying the number of clicks needed to start + * editing + * @see #getClickCountToStart + */ + public void setClickCountToStart(int count) { + clickCountToStart = count; + } + + /** + * Returns the number of clicks needed to start editing. + * + * @return the number of clicks needed to start editing + */ + public int getClickCountToStart() { + return clickCountToStart; + } + + +//------------------------ TableCellEditor + + @Override + public Component getTableCellEditorComponent(JTable table, Object value, + boolean isSelected, int row, int column) { + // PENDING JW: can remove the ignore flags here? + // the picker learnde to behave ... + ignoreAction = true; + datePicker.setDate(getValueAsDate(value)); + // todo how to.. + // SwingUtilities.invokeLater(new Runnable() { + // public void run() { + // datePicker.getEditor().selectAll(); + // } + // }); + ignoreAction = false; + return datePicker; + } + + //------------------------- TreeCellEditor + + @Override + public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row) { + // PENDING JW: can remove the ignore flags here? + // the picker learnde to behave ... + ignoreAction = true; + datePicker.setDate(getValueAsDate(value)); + // todo how to.. + // SwingUtilities.invokeLater(new Runnable() { + // public void run() { + // datePicker.getEditor().selectAll(); + // } + // }); + ignoreAction = false; + return datePicker; + } + +//-------------------- helpers + + /** + * Returns the given value as Date. + * + * PENDING: abstract into something pluggable (like StringValue + * in ComponentProvider?) + * + * @param value the value to map as Date + * @return the value as Date or null, if not successful. + * + */ + protected Date getValueAsDate(Object value) { + if (isEmpty(value)) return null; + if (value instanceof Date) { + return (Date) value; + } + if (value instanceof Long) { + return new Date((Long) value); + } + if (value instanceof String) { + try { + // JW: why was the parsing synchronized? +// synchronized (dateFormat) { +// datePicker.setDate(dateFormat.parse((String) value)); +// } + return dateFormat.parse((String) value); + } catch (ParseException e) { + handleParseException(e); + } + } + if (value instanceof DefaultMutableTreeNode) { + return getValueAsDate(((DefaultMutableTreeNode) value).getUserObject()); + } + if (value instanceof AbstractMutableTreeTableNode) { + return getValueAsDate(((AbstractMutableTreeTableNode) value).getUserObject()); + } + return null; + } + + /** + * @param e + */ + protected void handleParseException(ParseException e) { + logger.log(Level.SEVERE, e.getMessage(), e.getMessage()); + } + + protected boolean isEmpty(Object value) { + return value == null || value instanceof String + && ((String) value).length() == 0; + } + +//--------------- picker specifics + /** + * Commits any pending edits and returns a boolean indicating whether the + * commit was successful. + * + * @return true if the edit was valid, false otherwise. + */ + protected boolean commitChange() { + try { + datePicker.commitEdit(); + return true; + } catch (ParseException e) { + } + return false; + } + + /** + * + * @return the DatePicker's formats. + * + * @see JXDatePicker#getFormats(). + */ + public DateFormat[] getFormats() { + return datePicker.getFormats(); + } + + /** + * + * @param formats the formats to use in the datepicker. + * + * @see JXDatePicker#setFormats(DateFormat...) + * + */ + public void setFormats(DateFormat... formats) { + datePicker.setFormats(formats); + } + /** + * Returns the ActionListener to add to the datePicker. + * + * @return the action listener to listen for datePicker's + * action events. + */ + protected ActionListener getPickerActionListener() { + if (pickerActionListener == null) { + pickerActionListener = createPickerActionListener(); + } + return pickerActionListener; + } + + /** + * Creates and returns the ActionListener for the Picker. + * @return the ActionListener to listen for Picker's action events. + */ + protected ActionListener createPickerActionListener() { + ActionListener l = new ActionListener() { + @Override + public void actionPerformed(final ActionEvent e) { + // avoid duplicate trigger from + // commit in stopCellEditing + if (ignoreAction) + return; + // still need to invoke .. hmm + // no ... with the table cooperating the + // invoke is contra-productive! + terminateEdit(e); + } + + /** + * @param e + */ + private void terminateEdit(final ActionEvent e) { + if ((e != null) + && (JXDatePicker.COMMIT_KEY.equals(e.getActionCommand()))) { + stopCellEditing(); + } else { + cancelCellEditing(); + } + } + }; + return l; + } + + +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/table/DefaultTableColumnModelExt.java b/src/main/java/org/jdesktop/swingx/table/DefaultTableColumnModelExt.java new file mode 100644 index 0000000000..d64182a010 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/table/DefaultTableColumnModelExt.java @@ -0,0 +1,388 @@ +/* + * $Id: DefaultTableColumnModelExt.java 3975 2011-03-28 14:05:29Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.table; + +import org.jdesktop.swingx.event.TableColumnModelExtListener; + +import javax.swing.event.EventListenerList; +import javax.swing.event.TableColumnModelListener; +import javax.swing.table.DefaultTableColumnModel; +import javax.swing.table.TableColumn; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + + +/** + * A default implementation of TableColumnModelExt. + *

                + * + * TODO: explain sub-optimal notification on showing/hiding columns. + * (hot fixed issues #156, #157. To really do it + * need enhanced TableColumnModelEvent and -Listeners that are + * aware of the event.) + * + * + * @author Richard Bair + * @author Jeanette Winzenburg + */ +public class DefaultTableColumnModelExt extends DefaultTableColumnModel + implements TableColumnModelExt { + /** flag to distinguish a shown/hidden column from really added/removed + * columns during notification. This is brittle! + */ +// private static final String IGNORE_EVENT = "TableColumnModelExt.ignoreEvent"; + private boolean isVisibilityChange; + /** + * contains a list of all columns, in the order in which were + * added to the model. + */ + private List initialColumns = new ArrayList(); + + /** + * contains a list of all column, in the order they would appear if + * all were visible. + */ + private List currentColumns = new ArrayList(); + + /** + * Listener attached to TableColumnExt instances to listen for changes + * to their visibility status, and to hide/show the column as oppropriate + */ + private VisibilityListener visibilityListener = new VisibilityListener(); + + /** + * Creates a an empty DefaultTableColumnModelExt. + */ + public DefaultTableColumnModelExt() { + super(); + } + +//----------------------- implement TableColumnModelExt + + /** + * {@inheritDoc} + */ + @Override + public List getColumns(boolean includeHidden) { + if (includeHidden) { + return new ArrayList(initialColumns); + } + return Collections.list(getColumns()); + } + + /** + * {@inheritDoc} + */ + @Override + public int getColumnCount(boolean includeHidden) { + if (includeHidden) { + return initialColumns.size(); + } + return getColumnCount(); + } + + /** + * {@inheritDoc} + */ + @Override + public TableColumnExt getColumnExt(Object identifier) { + for (Iterator iter = initialColumns.iterator(); iter.hasNext();) { + TableColumn column = iter.next(); + if ((column instanceof TableColumnExt) && (identifier.equals(column.getIdentifier()))) { + return (TableColumnExt) column; + } + } + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public TableColumnExt getColumnExt(int columnIndex) { + TableColumn column = getColumn(columnIndex); + if (column instanceof TableColumnExt) { + return (TableColumnExt) column; + } + return null; + } + + /** + * hot fix for #157: listeners that are aware of + * the possible existence of invisible columns + * should check if the received columnRemoved originated + * from moving a column from visible to invisible. + * + * @param oldIndex the fromIndex of the columnEvent + * @return true if the column was moved to invisible + */ + public boolean isRemovedToInvisibleEvent(int oldIndex) { + return isVisibilityChange; + } + + /** + * hot fix for #157: listeners that are aware of + * the possible existence of invisible columns + * should check if the received columnAdded originated + * from moving a column from invisible to visible. + * + * @param newIndex the toIndex of the columnEvent + * @return true if the column was moved to visible + */ + public boolean isAddedFromInvisibleEvent(int newIndex) { + return isVisibilityChange; + } + +//------------------------ TableColumnModel + + /** + * {@inheritDoc}

                + * + * Overridden to update internals related to column visibility. + */ + @Override + public void removeColumn(TableColumn column) { + boolean oldVisible = true; + //remove the visibility listener if appropriate + if (column instanceof TableColumnExt) { + oldVisible = ((TableColumnExt) column).isVisible(); + ((TableColumnExt) column).setVisible(true); + ((TableColumnExt)column).removePropertyChangeListener(visibilityListener); + } + currentColumns.remove(column); + initialColumns.remove(column); + //let the superclass handle notification etc + super.removeColumn(column); + if (column instanceof TableColumnExt) { + ((TableColumnExt) column).setVisible(oldVisible); + } + } + + /** + * {@inheritDoc}

                + * + * Overridden to update internals related to column visibility. + */ + @Override + public void addColumn(TableColumn aColumn) { + // hacking to guarantee correct events + // two step: add as visible, setVisible + boolean oldVisible = true; + //add the visibility listener if appropriate + if (aColumn instanceof TableColumnExt) { + TableColumnExt xColumn = (TableColumnExt) aColumn; + oldVisible = xColumn.isVisible(); + xColumn.setVisible(true); + xColumn.addPropertyChangeListener(visibilityListener); + } + // append the column to the end of both initial- and currentColumns. + currentColumns.add(aColumn); + initialColumns.add(aColumn); + // let super handle the event notification, super.book-keeping + super.addColumn(aColumn); + if (aColumn instanceof TableColumnExt) { + // reset original visibility + ((TableColumnExt) aColumn).setVisible(oldVisible); + } + + } + + /** + * {@inheritDoc}

                + * + * Overridden to update internals related to column visibility. + */ + @Override + public void moveColumn(int columnIndex, int newIndex) { + if (columnIndex != newIndex) { + updateCurrentColumns(columnIndex, newIndex); + } + super.moveColumn(columnIndex, newIndex); + } + + /** + * Adjusts the current column sequence when a visible column is moved. + * + * @param oldIndex the old visible position. + * @param newIndex the new visible position. + */ + private void updateCurrentColumns(int oldIndex, int newIndex) { + TableColumn movedColumn = tableColumns.elementAt(oldIndex); + int oldPosition = currentColumns.indexOf(movedColumn); + TableColumn targetColumn = tableColumns.elementAt(newIndex); + int newPosition = currentColumns.indexOf(targetColumn); + currentColumns.remove(oldPosition); + currentColumns.add(newPosition, movedColumn); + + } + + /** + * Update internal state after the visibility of the column + * was changed to invisible. The given column is assumed to + * be contained in this model. + * + * @param col the column which was hidden. + */ + protected void moveToInvisible(TableColumnExt col) { + isVisibilityChange = true; + super.removeColumn(col); + isVisibilityChange = false; + } + + + /** + * Update internal state after the visibility of the column + * was changed to visible. The given column is assumed to + * be contained in this model. + * + * @param col the column which was made visible. + */ + protected void moveToVisible(TableColumnExt col) { + isVisibilityChange = true; + // two step process: first add at end of columns + // then move to "best" position relative to where it + // was before hiding. + super.addColumn(col); + // this is analogous to the proposed fix in #253-swingx + // but uses the currentColumns as reference. + Integer addIndex = currentColumns.indexOf(col); + for (int i = 0; i < (getColumnCount() - 1); i++) { + TableColumn tableCol = getColumn(i); + int actualPosition = currentColumns.indexOf(tableCol); + if (actualPosition > addIndex) { + super.moveColumn(getColumnCount() - 1, i); + break; + } + } + isVisibilityChange = false; + } + + + /** + * TODO JW: move into propertyChanged! No need for a dedicated listener. + * Changed evaluation JW: may still be required as super removes itself as + * propertyChangeListener if column is hidden + */ + private class VisibilityListener implements PropertyChangeListener, Serializable { + @Override + public void propertyChange(PropertyChangeEvent evt) { + if ("visible".equals(evt.getPropertyName())) { + TableColumnExt columnExt = (TableColumnExt)evt.getSource(); + + if (columnExt.isVisible()) { + moveToVisible(columnExt); + fireColumnPropertyChange(evt); + } else { + moveToInvisible(columnExt); + } + } else if (!((TableColumnExt) evt.getSource()).isVisible()) { + fireColumnPropertyChange(evt); + } + } + } + + // enhanced listener notification + + + /** + * Exposed for testing only - don't use! Will be removed again! + * @return super's listener list + */ + protected EventListenerList getEventListenerList() { + return listenerList; + } + + + + /** + * {@inheritDoc} + */ + @Override + public void propertyChange(PropertyChangeEvent evt) { + super.propertyChange(evt); + fireColumnPropertyChange(evt); + } + + /** + * Notifies TableColumnModelExtListeners about property + * changes of contained columns. The event instance + * is the original as fired by the TableColumn. + * @param evt the event received + * @see EventListenerList + */ + protected void fireColumnPropertyChange(PropertyChangeEvent evt) { +// if (IGNORE_EVENT.equals(evt.getPropertyName())) return; + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==TableColumnModelExtListener.class) { + ((TableColumnModelExtListener)listeners[i+1]). + columnPropertyChange(evt); + } + } + } + + + /** + * {@inheritDoc}

                + * + * + * Overridden to install enhanced notification of listeners of type. + * TableColumnModelListenerExt about property changes of contained columns. + * + */ + @Override + public void addColumnModelListener(TableColumnModelListener x) { + super.addColumnModelListener(x); + if (x instanceof TableColumnModelExtListener) { + listenerList.add(TableColumnModelExtListener.class, (TableColumnModelExtListener) x); + } + } + + /** + * {@inheritDoc}

                + * + * Overridden to uninstall enhanced notification of listeners of type. + * TableColumnModelListenerExt about property changes of contained columns. + */ + @Override + public void removeColumnModelListener(TableColumnModelListener x) { + super.removeColumnModelListener(x); + if (x instanceof TableColumnModelExtListener) { + listenerList.remove(TableColumnModelExtListener.class, (TableColumnModelExtListener) x); + } + } + + /** + * @return array of all registered listeners + */ + public TableColumnModelExtListener[] getTableColumnModelExtListeners() { + return listenerList.getListeners(TableColumnModelExtListener.class); + } +} diff --git a/src/main/java/org/jdesktop/swingx/table/NumberEditorExt.java b/src/main/java/org/jdesktop/swingx/table/NumberEditorExt.java new file mode 100644 index 0000000000..4402a40c1c --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/table/NumberEditorExt.java @@ -0,0 +1,335 @@ +/* + * $Id: NumberEditorExt.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.table; + +import org.jdesktop.swingx.text.NumberFormatExt; +import org.jdesktop.swingx.text.StrictNumberFormatter; + +import javax.swing.*; +import javax.swing.border.LineBorder; +import javax.swing.text.NumberFormatter; +import java.awt.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.text.NumberFormat; +import java.text.ParseException; + + +/** + * + * Issue #393-swingx: localized NumberEditor. Added feature to use StrictNumberFormatter. + * + * @author Noel Grandin + * @author Jeanette Winzenburg + */ +public class NumberEditorExt extends DefaultCellEditor { + + private static Class[] argTypes = new Class[]{String.class}; + java.lang.reflect.Constructor constructor; + private boolean useStrictFormatter; + + /** + * Instantiates an editor with default NumberFormat and default NumberFormatter. + */ + public NumberEditorExt() { + this(null); + } + + /** + * Instantiates an editor with the given NumberFormat and default NumberFormatter. + * + * @param format the NumberFormat to use for conversion, may be null to indicate + * usage of default NumberFormat. + */ + public NumberEditorExt(NumberFormat format) { + this(format, false); + } + + /** + * Instantiates an editor with default NumberFormat and NumberFormatter depending + * on useStrictFormatter. + * + * @param useStrictFormatter if true, uses a StrictNumberFormatter, else uses + * default NumberFormatter + */ + public NumberEditorExt(boolean useStrictFormatter) { + this(null, useStrictFormatter); + } + + /** + * Instantiates an editor with the given NumberFormat and NumberFormatter depending on + * useStrictFormatter. + * + * @param format the NumberFormat to use for conversion, may be null to indicate + * usage of default NumberFormat + * @param useStrictFormatter if true, uses a StrictNumberFormatter, else uses + * default NumberFormatter + */ + public NumberEditorExt(NumberFormat format, boolean useStrictFormatter) { + + super(useStrictFormatter ? createFormattedTextFieldX(format) : createFormattedTextField(format)); + this.useStrictFormatter = useStrictFormatter; + final JFormattedTextField textField = getComponent(); + + textField.setName("Table.editor"); + textField.setHorizontalAlignment(JTextField.RIGHT); + + // remove action listener added in DefaultCellEditor + textField.removeActionListener(delegate); + // replace the delegate created in DefaultCellEditor + delegate = new EditorDelegate() { + @Override + public void setValue(Object value) { + getComponent().setValue(value); + } + + @Override + public Object getCellEditorValue() { + try { + getComponent().commitEdit(); + return getComponent().getValue(); + } catch (ParseException ex) { + return null; + } + } + }; + textField.addActionListener(delegate); + } + + @Override + public boolean stopCellEditing() { + if (!isValid()) return false; + return super.stopCellEditing(); + } + + /** + * Returns a boolean indicating whether the current text is valid for + * instantiating the expected Number type. + * + * @return true if text is valid, false otherwise. + */ + protected boolean isValid() { + if (!getComponent().isEditValid()) return false; + try { + if (!hasStrictFormatter()) + getNumber(); + return true; + } catch (Exception ex) { + + } + return false; + } + + /** + * Returns the editor value as number. May fail for a variety of reasons, + * as it forces parsing of the current text as well as reflective construction + * of the target type. + * + * @return the editor value or null + * @throws Exception if creation of the expected type fails in some way. + */ + protected Number getNumber() throws Exception { + Number number = (Number) super.getCellEditorValue(); + if (number==null) return null; + return hasStrictFormatter() ? number : + (Number) constructor.newInstance(new Object[]{number.toString()}); + } + + /** + * @return + */ + protected boolean hasStrictFormatter() { + return useStrictFormatter; + } + + /** Override and set the border back to normal in case there was an error previously */ + @Override + public Component getTableCellEditorComponent(JTable table, Object value, + boolean isSelected, + int row, int column) { + ((JComponent)getComponent()).setBorder(new LineBorder(Color.black)); + try { + final Class type = table.getColumnClass(column); + if (hasStrictFormatter()) { + // delegate to formatter which decides at parsing time + // then either handles or throws + ((NumberFormatter) getComponent().getFormatter()).setValueClass(type); + } else { + // Assume that the Number object we are dealing with has a constructor which takes + // a single string parameter. + if (!Number.class.isAssignableFrom(type)) { + throw new IllegalStateException("NumberEditor can only handle subclasses of java.lang.Number"); + } + constructor = type.getConstructor(argTypes); + } + // JW: in strict mode this may fail in setting the value in the formatter + return super.getTableCellEditorComponent(table, value, isSelected, row, column); + } catch (Exception ex) { + // PENDING JW: super generic editor swallows all failures and returns null + // should we do so as well? + throw new IllegalStateException("value/type not compatible with Number", ex); + } + } + + /** + * {@inheritDoc}

                + * + * Overridden to instantiate a Number of the expected type. Note that this + * may throw a IllegalStateException if invoked without querying + * for a valid value with stopCellEditing. This should not happen during + * normal usage. + * + * @throws IllegalStateException if current value invalid + * + */ + @Override + public Number getCellEditorValue() throws IllegalStateException { + try { + return getNumber(); + } catch (Exception ex) { + throw new IllegalStateException("Number conversion not possible from " + + "current string " + getComponent().getText()); + } + } + + + /** + * {@inheritDoc}

                + * + * Convenience override with type cast. + */ + @Override + public JFormattedTextField getComponent() { + return (JFormattedTextField) super.getComponent(); + } + + /** + * Creates and returns a JFormattedTextField configured with SwingX extended + * NumberFormat and StrictNumberFormatter. This method is called if + * the constructor parameter useStrictFormatter is true. + * + * Use a static method so that we can do some stuff before calling the + * superclass. + */ + private static JFormattedTextField createFormattedTextFieldX( + NumberFormat format) { + StrictNumberFormatter formatter = new StrictNumberFormatter( + new NumberFormatExt(format)); + final JFormattedTextField textField = new JFormattedTextField( + formatter); + /* + * FIXME: I am sure there is a better way to do this, but I don't know + * what it is. JTable sets up a binding for the ESCAPE key, but + * JFormattedTextField overrides that binding with it's own. Remove the + * JFormattedTextField binding. + */ + InputMap map = textField.getInputMap(); + map.put(KeyStroke.getKeyStroke("ESCAPE"), "none"); +// while (map != null) { +// map.remove(KeyStroke.getKeyStroke("pressed ESCAPE")); +// map = map.getParent(); +// } + /* + * Set an input verifier to prevent the cell losing focus when the value + * is invalid + */ + textField.setInputVerifier(new InputVerifier() { + @Override + public boolean verify(JComponent input) { + JFormattedTextField ftf = (JFormattedTextField) input; + return ftf.isEditValid(); + } + }); + /* + * The formatted text field will not call stopCellEditing() until the + * value is valid. So do the red border thing here. + */ + textField.addPropertyChangeListener("editValid", + new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getNewValue() == Boolean.TRUE) { + ((JFormattedTextField) evt.getSource()) + .setBorder(new LineBorder(Color.black)); + } else { + ((JFormattedTextField) evt.getSource()) + .setBorder(new LineBorder(Color.red)); + } + } + }); + return textField; + } + + + /** + * Creates and returns a JFormattedTextField configured with defaults. This + * method is called if the contructor useStrictFormatter is false.

                + * + * Use a static method so that we can do some stuff before calling the + * superclass. + */ + private static JFormattedTextField createFormattedTextField( + NumberFormat formatter) { + final JFormattedTextField textField = new JFormattedTextField( + new NumberFormatExt(formatter)); + /* + * FIXME: I am sure there is a better way to do this, but I don't know + * what it is. JTable sets up a binding for the ESCAPE key, but + * JFormattedTextField overrides that binding with it's own. Remove the + * JFormattedTextField binding. + */ + InputMap map = textField.getInputMap(); + map.put(KeyStroke.getKeyStroke("ESCAPE"), "none"); +// while (map != null) { +// map.remove(KeyStroke.getKeyStroke("pressed ESCAPE")); +// map = map.getParent(); +// } + /* + * Set an input verifier to prevent the cell losing focus when the value + * is invalid + */ + textField.setInputVerifier(new InputVerifier() { + @Override + public boolean verify(JComponent input) { + JFormattedTextField ftf = (JFormattedTextField) input; + return ftf.isEditValid(); + } + }); + /* + * The formatted text field will not call stopCellEditing() until the + * value is valid. So do the red border thing here. + */ + textField.addPropertyChangeListener("editValid", + new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getNewValue() == Boolean.TRUE) { + ((JFormattedTextField) evt.getSource()) + .setBorder(new LineBorder(Color.black)); + } else { + ((JFormattedTextField) evt.getSource()) + .setBorder(new LineBorder(Color.red)); + } + } + }); + return textField; + } +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/table/NumberEditorNumberFormat.java b/src/main/java/org/jdesktop/swingx/table/NumberEditorNumberFormat.java new file mode 100644 index 0000000000..166068b882 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/table/NumberEditorNumberFormat.java @@ -0,0 +1,82 @@ +/* + * $Id: NumberEditorNumberFormat.java 3781 2010-09-15 08:33:52Z kleopatra $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.table; + +import java.text.*; + +/** + * A specialised Format for the NumberEditor that returns a null for empty + * strings. + * + * @author Noel Grandin + * + * @deprecated (pre-1.6.2) replaced by NumberEditorExt, no longer used internally + */ +@Deprecated +class NumberEditorNumberFormat extends Format { + private final NumberFormat childFormat; + + public NumberEditorNumberFormat(NumberFormat childFormat) { + if (childFormat == null) { + childFormat = NumberFormat.getInstance(); + } + this.childFormat = childFormat; + } + + @Override + public AttributedCharacterIterator formatToCharacterIterator(Object obj) { + if (obj == null) + return new AttributedString("").getIterator(); + return childFormat.formatToCharacterIterator(obj); + } + + @Override + public StringBuffer format(Object obj, StringBuffer toAppendTo, + FieldPosition pos) { + if (obj == null) + return new StringBuffer(""); + return childFormat.format(obj, toAppendTo, pos); + } + + @Override + public Object parseObject(String source, ParsePosition pos) { + if (source == null) { + pos.setIndex(1); // otherwise Format thinks parse failed + return null; + } + if (source.trim().equals("")) { + pos.setIndex(1); // otherwise Format thinks parse failed + return null; + } + Object val = childFormat.parseObject(source, pos); + /* + * The default behaviour of Format objects is to keep parsing as long as + * they encounter valid data. By for table editing we don't want + * trailing bad data to be considered a "valid value". So set the index + * to 0 so that the parse(Object) method knows that we had an error. + */ + if (pos.getIndex() != source.length()) { + pos.setErrorIndex(pos.getIndex()); + pos.setIndex(0); + } + return val; + } +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/table/NumberFormatExt.java b/src/main/java/org/jdesktop/swingx/table/NumberFormatExt.java new file mode 100644 index 0000000000..ff7b0cd87e --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/table/NumberFormatExt.java @@ -0,0 +1,102 @@ +/* + * $Id$ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.table; + +import java.text.*; + +/** + * A specialised NumberFormat which handles null values and empty Strings. + * This is useful in cell editors and used in StrictNumberFormatter. + * + * @author Noel Grandin + * @author Jeanette Winzenburg + * + * @deprecated (pre-1.6.2) moved to org.jdesktop.swingx.text + */ +@Deprecated +class NumberFormatExt extends NumberFormat { + + private NumberFormat childFormat; + + public NumberFormatExt() { + this(null); + } + + public NumberFormatExt(NumberFormat childFormat) { + if (childFormat == null) { + childFormat = NumberFormat.getInstance(); + } + this.childFormat = childFormat; + } + + @Override + public AttributedCharacterIterator formatToCharacterIterator(Object obj) { + if (obj == null) + return new AttributedString("").getIterator(); + return childFormat.formatToCharacterIterator(obj); + } + + @Override + public StringBuffer format(Object obj, StringBuffer toAppendTo, + FieldPosition pos) { + if (obj == null) + return new StringBuffer(""); + return childFormat.format(obj, toAppendTo, pos); + } + + @Override + public Number parse(String source, ParsePosition pos) { + if (source == null) { + pos.setIndex(1); // otherwise Format thinks parse failed + return null; + } + if (source.trim().equals("")) { + pos.setIndex(1); // otherwise Format thinks parse failed + return null; + } + Number val = childFormat.parse(source, pos); + /* + * The default behaviour of Format objects is to keep parsing as long as + * they encounter valid data. By for table editing we don't want + * trailing bad data to be considered a "valid value". So set the index + * to 0 so that the parse(Object) method knows that we had an error. + */ + if (pos.getIndex() != source.length()) { + pos.setErrorIndex(pos.getIndex()); + pos.setIndex(0); + } + return val; + } + + @Override + public StringBuffer format(double number, StringBuffer toAppendTo, + FieldPosition pos) { + return childFormat.format(number, toAppendTo, pos); + } + + @Override + public StringBuffer format(long number, StringBuffer toAppendTo, + FieldPosition pos) { + return childFormat.format(number, toAppendTo, pos); + } + +} diff --git a/src/main/java/org/jdesktop/swingx/table/StrictNumberFormatter.java b/src/main/java/org/jdesktop/swingx/table/StrictNumberFormatter.java new file mode 100644 index 0000000000..3e922133a5 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/table/StrictNumberFormatter.java @@ -0,0 +1,227 @@ +/* + * $Id$ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.table; + +import javax.swing.text.NumberFormatter; +import java.math.BigDecimal; +import java.text.Format; +import java.text.NumberFormat; +import java.text.ParseException; + +/** + * Experiment to work around Issue #1183-swingx: NumberEditorExt throws exception + * on getCellValue. Remaining issue: no visual error feedback if the expected + * number type exceeds its range. + * + * @author Jeanette Winzenburg + * + * @deprecated (pre-1.6.2) moved to text package + */ +@Deprecated +class StrictNumberFormatter extends NumberFormatter { + + + private BigDecimal maxAsBig; + private BigDecimal minAsBig; + + /** + * @param format + */ + public StrictNumberFormatter(NumberFormat format) { + super(format); + } + + /** + * {@inheritDoc}

                + * + * Overridden to automatically set the minimum/maximum to the boundaries of + * the Number type if it corresponds to a raw type, or null if not. + */ + @Override + public void setValueClass(Class valueClass) { + super.setValueClass(valueClass); + updateMinMax(); + } + + + /** + * + */ + @SuppressWarnings("unchecked") + private void updateMinMax() { + Comparable min = null; + Comparable max = null; + if (getValueClass() == Integer.class) { + max = Integer.MAX_VALUE; + min = Integer.MIN_VALUE; + } else if (getValueClass() == Long.class) { + max = Long.MAX_VALUE; + min = Long.MIN_VALUE; + } else if (getValueClass() == Short.class) { + max = Short.MAX_VALUE; + min = Short.MIN_VALUE; + } else if (getValueClass() == Byte.class) { + max = Byte.MAX_VALUE; + min = Byte.MIN_VALUE; + } else if (getValueClass() == Float.class) { + max = Float.MAX_VALUE; + min = Float.MIN_VALUE; + } else if (getValueClass() == Double.class) { + // don*t understand what's happening here, naive compare with bigDecimal + // fails - so don't do anything for now + // JW: revisit! + } + setMaximum(max); + setMinimum(min); + } + + + @SuppressWarnings("unchecked") + @Override + public void setMaximum(Comparable max) { + super.setMaximum(max); + this.maxAsBig = max != null ? new BigDecimal(max.toString()) : null; + } + + @SuppressWarnings("unchecked") + @Override + public void setMinimum(Comparable minimum) { + super.setMinimum(minimum); + this.minAsBig = minimum != null ? new BigDecimal(minimum.toString()) : null; + } + + + /** + * Returns the Object representation of the + * String text, may be null. + * + * @param text String to convert + * @return Object representation of text + * @throws ParseException if there is an error in the conversion + */ + @Override + public Object stringToValue(String text) throws ParseException { + Object value = getParsedValue(text, getFormat()); + try { + if (!isValueInRange(value, true)) { + throw new ParseException("Value not within min/max range", 0); + } + } catch (ClassCastException cce) { + throw new ParseException("Class cast exception comparing values: " + + cce, 0); + } + return convertValueToValueClass(value, getValueClass()); + } + + /** + * Converts the passed in value to the passed in class. This only + * works if valueClass is one of Integer, + * Long, Float, Double, + * Byte or Short and value + * is an instanceof Number. + */ + private Object convertValueToValueClass(Object value, Class valueClass) { + if (valueClass != null && (value instanceof Number)) { + if (valueClass == Integer.class) { + return ((Number) value).intValue(); + } + else if (valueClass == Long.class) { + return ((Number) value).longValue(); + } + else if (valueClass == Float.class) { + return ((Number) value).floatValue(); + } + else if (valueClass == Double.class) { + return ((Number) value).doubleValue(); + } + else if (valueClass == Byte.class) { + return ((Number) value).byteValue(); + } + else if (valueClass == Short.class) { + return ((Number) value).shortValue(); + } + } + return value; + } + + /** + * Invokes parseObject on f, returning + * its value. + */ + private Object getParsedValue(String text, Format f) throws ParseException { + if (f == null) { + return text; + } + return f.parseObject(text); + } + + + /** + * Returns true if value is between the min/max. + * + * @param wantsCCE If false, and a ClassCastException is thrown in + * comparing the values, the exception is consumed and + * false is returned. + */ + private boolean isValueInRange(Object orgValue, boolean wantsCCE) { + if (orgValue == null) return true; + if ((getMinimum() == null) && getMaximum() == null) return true; + + BigDecimal value = new BigDecimal(orgValue.toString()); + Comparable min = getMinimumAsBig(); + + try { + if (min != null && min.compareTo(value) > 0) { + return false; + } + } catch (ClassCastException cce) { + if (wantsCCE) { + throw cce; + } + return false; + } + + Comparable max = getMaximumAsBig(); + try { + if (max != null && max.compareTo(value) < 0) { + return false; + } + } catch (ClassCastException cce) { + if (wantsCCE) { + throw cce; + } + return false; + } + return true; + } + + + private Comparable getMinimumAsBig() { + return minAsBig; + } + + private Comparable getMaximumAsBig() { + return maxAsBig; + } + + +} diff --git a/src/main/java/org/jdesktop/swingx/table/TableColumnExt.java b/src/main/java/org/jdesktop/swingx/table/TableColumnExt.java new file mode 100644 index 0000000000..498e3511f4 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/table/TableColumnExt.java @@ -0,0 +1,761 @@ +/* + * $Id: TableColumnExt.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.table; + +import org.jdesktop.swingx.decorator.CompoundHighlighter; +import org.jdesktop.swingx.decorator.Highlighter; +import org.jdesktop.swingx.plaf.UIDependent; +import org.jdesktop.swingx.renderer.AbstractRenderer; + +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.table.TableCellEditor; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableColumn; +import java.awt.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.Comparator; +import java.util.Hashtable; + +/** + * TableColumn extension for enhanced view column configuration. + * The general drift is to strengthen the TableColumn abstraction as the + * place to configure and dynamically update view column properties, covering a + * broad range of customization requirements. Using collaborators are expected + * to listen to property changes and update themselves accordingly. + *

                + * + * A functionality enhancement is the notion of column visibility: + * TableColumnModelExt manages sets of visible/hidden + * TableColumnExts controlled by the columns' + * visible property. Typically, users can toggle column + * visibility at runtime, f.i. through a dedicated control in the upper trailing + * corner of a JScrollPane. + *

                + * + * A prominent group of properties allows fine-grained, per-column control of + * corresponding Table/-Header features. + * + *

                  + *
                • Sorting: sortable controls whether this column + * should be sortable by user's sort gestures; Comparator can + * hold a column specific type. + * + *
                • Editing: editable controls whether cells of this + * column should be accessible to in-table editing. + * + *
                • Tooltip: toolTipText holds the column tooltip + * which is shown when hovering over the column's header. + * + *
                • Highlighter: highlighters holds the column + * highlighters; these are applied to the renderer after the table highlighters. + * Any modification of the list of contained Highlighters + * (setting them, adding one or removing one) will result in a + * {@code PropertyChangeEvent} being fired for "highlighters". State changes on + * contained Highlighters will result in a PropertyChangeEvent + * for "highlighterStateChanged". + *
                + * + * + * Analogous to JComponent, this class supports per-instance + * "client" properties. They are meant as a small-scale extension mechanism. + * They are similar to regular bean properties in that registered + * PropertyChangeListeners are notified about changes. TODO: + * example? + *

                + * + * A TableColumnExt implements UIDependent, that is it takes over + * responsibility to update LAF dependent properties of contained elements when + * messaged with updateUI. This implementation updates its Highlighters, + * Cell-/HeaderRenderer and CellEditor.

                + * + * TODO: explain prototype (sizing, collaborator-used-by ColumnFactory (?)) + *

                + * + * @author Ramesh Gupta + * @author Amy Fowler + * @author Jeanette Winzenburg + * @author Karl Schaefer + * + * @see TableColumnModelExt + * @see ColumnFactory + * @see UIDependent + * @see JComponent#putClientProperty + */ +public class TableColumnExt extends TableColumn implements UIDependent { + + /** visible property. Initialized to true.*/ + protected boolean visible = true; + /** hideable property. Initialized to true.*/ + protected boolean hideable = true; + + /** prototype property. */ + protected Object prototypeValue; + + + /** per-column comparator */ + protected Comparator comparator; + /** per-column sortable property. Initialized to true. */ + protected boolean sortable = true; + /** per-column editable property. Initialized to true.*/ + protected boolean editable = true; + /** per-column tool tip text. */ + private String toolTipText; + + /** storage for client properties. */ + protected Hashtable clientProperties; + + /** + * The compound highlighter for the column. + */ + protected CompoundHighlighter compoundHighlighter; + + private ChangeListener highlighterChangeListener; + + private boolean ignoreHighlighterStateChange; + + + /** + * Creates new table view column with a model index = 0. + */ + public TableColumnExt() { + this(0); + } + + /** + * Creates new table view column with the specified model index. + * @param modelIndex index of table model column to which this view column + * is bound. + */ + public TableColumnExt(int modelIndex) { + this(modelIndex, 75); // default width taken from javax.swing.table.TableColumn + } + + /** + * Creates new table view column with the specified model index and column width. + * @param modelIndex index of table model column to which this view column + * is bound. + * @param width pixel width of view column + */ + public TableColumnExt(int modelIndex, int width) { + this(modelIndex, width, null, null); + } + + /** + * Creates new table view column with the specified model index, column + * width, cell renderer and cell editor. + * @param modelIndex index of table model column to which this view column + * is bound. + * @param width pixel width of view column + * @param cellRenderer the cell renderer which will render all cells in this + * view column + * @param cellEditor the cell editor which will edit cells in this view column + */ + public TableColumnExt(int modelIndex, int width, + TableCellRenderer cellRenderer, TableCellEditor cellEditor) { + super(modelIndex, width, cellRenderer, cellEditor); + } + + /** + * Instantiates a new table view column with all properties copied from the + * given original. + * + * @param columnExt the column to copy properties from + * @see #copyFrom(TableColumnExt) + */ + public TableColumnExt(TableColumnExt columnExt) { + this(columnExt.getModelIndex(), columnExt.getWidth(), columnExt + .getCellRenderer(), columnExt.getCellEditor()); + copyFrom(columnExt); + } + + + /** + * Sets the Highlighters to the table, replacing any old settings. + * None of the given Highlighters must be null.

                + * + * This is a bound property.

                + * + * Note: as of version #1.257 the null constraint is enforced strictly. To remove + * all highlighters use this method without param. + * + * @param highlighters zero or more not null highlighters to use for renderer decoration. + * @throws NullPointerException if array is null or array contains null values. + * + * @see #getHighlighters() + * @see #addHighlighter(Highlighter) + * @see #removeHighlighter(Highlighter) + * + */ + public void setHighlighters(Highlighter... highlighters) { + ignoreHighlighterStateChange = true; + Highlighter[] old = getHighlighters(); + getCompoundHighlighter().setHighlighters(highlighters); + firePropertyChange("highlighters", old, getHighlighters()); + ignoreHighlighterStateChange = false; + } + + /** + * Returns the Highlighters used by this table. + * Maybe empty, but guarantees to be never null. + * + * @return the Highlighters used by this table, guaranteed to never null. + * @see #setHighlighters(Highlighter[]) + */ + public Highlighter[] getHighlighters() { + return getCompoundHighlighter().getHighlighters(); + } + /** + * Appends a Highlighter to the end of the list of used + * Highlighters. The argument must not be null. + *

                + * + * @param highlighter the Highlighter to add, must not be null. + * @throws NullPointerException if Highlighter is null. + * + * @see #removeHighlighter(Highlighter) + * @see #setHighlighters(Highlighter[]) + */ + public void addHighlighter(Highlighter highlighter) { + ignoreHighlighterStateChange = true; + Highlighter[] old = getHighlighters(); + getCompoundHighlighter().addHighlighter(highlighter); + firePropertyChange("highlighters", old, getHighlighters()); + ignoreHighlighterStateChange = false; + } + + /** + * Removes the given Highlighter.

                + * + * Does nothing if the Highlighter is not contained. + * + * @param highlighter the Highlighter to remove. + * @see #addHighlighter(Highlighter) + * @see #setHighlighters(Highlighter...) + */ + public void removeHighlighter(Highlighter highlighter) { + ignoreHighlighterStateChange = true; + Highlighter[] old = getHighlighters(); + getCompoundHighlighter().removeHighlighter(highlighter); + firePropertyChange("highlighters", old, getHighlighters()); + ignoreHighlighterStateChange = false; + } + + /** + * Returns the CompoundHighlighter assigned to the table, null if none. + * PENDING: open up for subclasses again?. + * + * @return the CompoundHighlighter assigned to the table. + */ + protected CompoundHighlighter getCompoundHighlighter() { + if (compoundHighlighter == null) { + compoundHighlighter = new CompoundHighlighter(); + compoundHighlighter.addChangeListener(getHighlighterChangeListener()); + } + return compoundHighlighter; + } + + /** + * Returns the ChangeListener to use with highlighters. Lazily + * creates the listener. + * + * @return the ChangeListener for observing changes of highlighters, + * guaranteed to be not-null + */ + protected ChangeListener getHighlighterChangeListener() { + if (highlighterChangeListener == null) { + highlighterChangeListener = createHighlighterChangeListener(); + } + return highlighterChangeListener; + } + + /** + * Creates and returns the ChangeListener observing Highlighters. + *

                + * Here: repaints the table on receiving a stateChanged. + * + * @return the ChangeListener defining the reaction to changes of + * highlighters. + */ + protected ChangeListener createHighlighterChangeListener() { + return new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + if (ignoreHighlighterStateChange) return; + firePropertyChange("highlighterStateChanged", false, true); + } + }; + } + + /** + * Returns true if the user can resize the TableColumn's width, + * false otherwise. This is a usability override: it takes into account + * the case where it's principally allowed to resize the column + * but not possible because the column has fixed size. + * + * @return a boolean indicating whether the user can resize this column. + */ + @Override + public boolean getResizable() { + // TODO JW: resizable is a bound property, so to be strict + // we'll need to override setMin/MaxWidth to fire resizable + // property change. + return super.getResizable() && (getMinWidth() < getMaxWidth()); + } + + /** + * Sets the editable property. This property allows to mark all cells in a + * column as read-only, independent of the per-cell editability as returned + * by the TableModel.isCellEditable. If the cell is + * read-only in the model layer, this property will have no effect. + * + * @param editable boolean indicating whether or not the user may edit cell + * values in this view column + * @see #isEditable + * @see org.jdesktop.swingx.JXTable#isCellEditable(int, int) + * @see javax.swing.table.TableModel#isCellEditable + */ + public void setEditable(boolean editable) { + boolean oldEditable = this.editable; + this.editable = editable; + firePropertyChange("editable", + Boolean.valueOf(oldEditable), + Boolean.valueOf(editable)); + } + + /** + * Returns the per-column editable property. + * The default is true. + * + * @return boolean indicating whether or not the user may edit cell + * values in this view column + * @see #setEditable + */ + public boolean isEditable() { + return editable; + } + + /** + * Sets the prototypeValue property. The value should be of a type + * which corresponds to the column's class as defined by the table model. + * If non-null, the JXTable instance will use this property to calculate + * and set the initial preferredWidth of the column. Note that this + * initial preferredWidth will be overridden if the user resizes columns + * directly. + * + * @param value Object containing the value of the prototype to be used + * to calculate the initial preferred width of the column + * @see #getPrototypeValue + * @see org.jdesktop.swingx.JXTable#getPreferredScrollableViewportSize + */ + public void setPrototypeValue(Object value) { + Object oldPrototypeValue = this.prototypeValue; + this.prototypeValue = value; + firePropertyChange("prototypeValue", + oldPrototypeValue, + value); + + } + + /** + * Returns the prototypeValue property. + * The default is null. + * + * @return Object containing the value of the prototype to be used + * to calculate the initial preferred width of the column + * @see #setPrototypeValue + */ + public Object getPrototypeValue() { + return prototypeValue; + } + + + /** + * Sets the comparator to use for this column. + * JXTable sorting api respects this property by passing it on + * to the SortController. + * + * @param comparator a custom comparator to use in interactive + * sorting. + * @see #getComparator + * @see org.jdesktop.swingx.sort.SortController + * @see org.jdesktop.swingx.decorator.SortKey + */ + public void setComparator(Comparator comparator) { + Comparator old = getComparator(); + this.comparator = comparator; + firePropertyChange("comparator", old, getComparator()); + } + + /** + * Returns the comparator to use for the column. + * The default is null. + * + * @return Comparator to use for this column + * @see #setComparator + */ + public Comparator getComparator() { + return comparator; + } + + /** + * Sets the sortable property. JXTable sorting api respects this + * property by disabling interactive sorting on this column if false. + * + * @param sortable boolean indicating whether or not this column can + * be sorted in the table + * @see #isSortable + */ + public void setSortable(boolean sortable) { + boolean old = isSortable(); + this.sortable = sortable; + firePropertyChange("sortable", old, isSortable()); + } + + /** + * Returns the sortable property. + * The default value is true. + * + * @return boolean indicating whether this view column is sortable + * @see #setSortable + */ + public boolean isSortable() { + return sortable; + } + + /** + * Registers the text to display in the column's tool tip. + * Typically, this is used by JXTableHeader to + * display when the mouse cursor lingers over the column's + * header cell. + * + * @param toolTipText text to show. + * @see #setToolTipText(String) + */ + public void setToolTipText(String toolTipText) { + String old = getToolTipText(); + this.toolTipText = toolTipText; + firePropertyChange("toolTipText", old, getToolTipText()); + } + + /** + * Returns the text of to display in the column's tool tip. + * The default is null. + * + * @return the text of the column ToolTip. + * @see #setToolTipText(String) + */ + public String getToolTipText() { + return toolTipText; + } + + + /** + * Sets the title of this view column. This is a convenience + * wrapper for setHeaderValue. + * @param title String containing the title of this view column + */ + public void setTitle(String title) { + setHeaderValue(title); // simple wrapper + } + + /** + * Convenience method which returns the headerValue property after + * converting it to a string. + * @return String containing the title of this view column or null if + * no headerValue is set. + */ + public String getTitle() { + Object header = getHeaderValue(); + return header != null ? header.toString() : null; // simple wrapper + } + + /** + * Sets the visible property. This property controls whether or not + * this view column is currently visible in the table. + * + * @param visible boolean indicating whether or not this view column is + * visible in the table + * @see #setVisible + */ + public void setVisible(boolean visible) { + boolean oldVisible = isVisible(); + this.visible = visible; + firePropertyChange("visible", oldVisible, isVisible()); + } + + /** + * Returns a boolean indicating whether or not this column is visible. + * The bare property value is constrained by this column's hideable setting, + * that is a not hideable column is always visible, irrespective of the + * property setting. + *

                + * The default is true. + * + * @return boolean indicating whether or not this view column is + * visible in the table + * @see #setVisible + */ + public boolean isVisible() { + if (!isHideable()) return true; + return visible; + } + + /** + * Sets the hideable property. This property controls whether the column can + * be hidden. This is a bound property. If the column's visibilty is affected, + * listeners are notified about that change as well.. + *

                + * + * The default value is true. + * + * @param hideable + */ + public void setHideable(boolean hideable) { + boolean old = isHideable(); + boolean oldVisible = isVisible(); + this.hideable = hideable; + firePropertyChange("visible", oldVisible, isVisible()); + firePropertyChange("hideable", old, isHideable()); + } + + /** + * Returns the hideable property. + * + * @return the hideable property. + * + * @see #setHideable(boolean) + */ + public boolean isHideable() { + return hideable; + } + + /** + * Sets the client property "key" to value. + * If value is null this method will remove the property. + * Changes to + * client properties are reported with PropertyChange events. + * The name of the property (for the sake of PropertyChange events) is + * key.toString(). + *

                + * The get/putClientProperty methods provide access to a + * per-instance hashtable, which is intended for small scale extensions of + * TableColumn. + *

                + * + * @param key Object which is used as key to retrieve value + * @param value Object containing value of client property + * @throws IllegalArgumentException if key is null + * @see #getClientProperty + * @see JComponent#putClientProperty + */ + public void putClientProperty(Object key, Object value) { + if (key == null) + throw new IllegalArgumentException("null key"); + + if ((value == null) && (getClientProperty(key) == null)) { + return; + } + + Object old = getClientProperty(key); + if (value == null) { + getClientProperties().remove(key); + } + else { + getClientProperties().put(key, value); + } + + firePropertyChange(key.toString(), old, value); + /* Make all fireXXX methods in TableColumn protected instead of private */ + } + + /** + * Returns the value of the property with the specified key. Only properties + * added with putClientProperty will return a non-null + * value. + * + * @param key Object which is used as key to retrieve value + * @return Object containing value of client property or null + * + * @see #putClientProperty + */ + public Object getClientProperty(Object key) { + return ((key == null) || (clientProperties == null)) ? + null : clientProperties.get(key); + } + + private Hashtable getClientProperties() { + if (clientProperties == null) { + clientProperties = new Hashtable(); + } + return clientProperties; + } + + + /** + * Copies properties from original. Handles all properties except + * modelIndex, width, cellRenderer, cellEditor. Called from copy + * constructor. + * + * @param original the tableColumn to copy from + * + * @see #TableColumnExt(TableColumnExt) + */ + protected void copyFrom(TableColumnExt original) { + setEditable(original.isEditable()); + setHeaderValue(original.getHeaderValue()); // no need to copy setTitle(); + setToolTipText(original.getToolTipText()); + setIdentifier(original.getIdentifier()); + setMaxWidth(original.getMaxWidth()); + setMinWidth(original.getMinWidth()); + setPreferredWidth(original.getPreferredWidth()); + setPrototypeValue(original.getPrototypeValue()); + // JW: isResizable is overridden to return a calculated property! + setResizable(original.isResizable); + setVisible(original.isVisible()); + setSortable(original.isSortable()); + setComparator(original.getComparator()); + copyClientPropertiesFrom(original); + + if (original.compoundHighlighter != null) { + setHighlighters(original.getHighlighters()); + } + + } + + /** + * Copies all clientProperties of this TableColumnExt + * to the target column. + * + * @param original the target column. + */ + protected void copyClientPropertiesFrom(TableColumnExt original) { + if (original.clientProperties == null) return; + for(Object key: original.clientProperties.keySet()) { + putClientProperty(key, original.getClientProperty(key)); + } + } + + + /** + * Notifies registered PropertyChangeListeners + * about property changes. This method must be invoked internally + * whe any of the enhanced properties changed. + *

                + * Implementation note: needed to replicate super + * functionality because super's field propertyChangeSupport + * and method fireXX are both private. + * + * @param propertyName name of changed property + * @param oldValue old value of changed property + * @param newValue new value of changed property + */ + protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { + if ((oldValue != null && !oldValue.equals(newValue)) || + oldValue == null && newValue != null) { + PropertyChangeListener pcl[] = getPropertyChangeListeners(); + if (pcl != null && pcl.length != 0) { + PropertyChangeEvent pce = new PropertyChangeEvent(this, + propertyName, + oldValue, newValue); + + for (int i = 0; i < pcl.length; i++) { + pcl[i].propertyChange(pce); + } + } + } + } + +//---------------- implement UIDependent + + /** + * Update ui of owned ui-dependent parts. This implementation + * updates the contained highlighters. + * + */ + @Override + public void updateUI() { + updateHighlighterUI(); + updateRendererUI(getCellRenderer()); + updateRendererUI(getHeaderRenderer()); + updateEditorUI(getCellEditor()); + } + + /** + * @param editor + * + */ + private void updateEditorUI(TableCellEditor editor) { + if (editor == null) return; + // internal knowledge of core table - already updated + if ((editor instanceof JComponent) + || (editor instanceof DefaultCellEditor)) + return; + try { + Component comp = editor + .getTableCellEditorComponent(null, null, false, -1, -1); + if (comp != null) { + SwingUtilities.updateComponentTreeUI(comp); + } + } catch (Exception e) { + // can't do anything - renderer can't cope with off-range cells + } + } + + /** + * @param tableCellRenderer + * + */ + private void updateRendererUI(TableCellRenderer renderer) { + if (renderer == null) return; + // internal knowledge of core table - already updated + if (renderer instanceof JComponent) { + return; + } + Component comp = null; + if (renderer instanceof AbstractRenderer) { + comp = ((AbstractRenderer) renderer).getComponentProvider().getRendererComponent(null); + } else { + try { + comp = renderer + .getTableCellRendererComponent(null, null, false, false, + -1, -1); + + } catch (Exception e) { + // can't do anything - renderer can't cope with off-range cells + } + } + if (comp != null) { + SwingUtilities.updateComponentTreeUI(comp); + } + } + + /** + * + */ + private void updateHighlighterUI() { + if (compoundHighlighter == null) return; + compoundHighlighter.updateUI(); + } +} diff --git a/src/main/java/org/jdesktop/swingx/table/TableColumnModelExt.java b/src/main/java/org/jdesktop/swingx/table/TableColumnModelExt.java new file mode 100644 index 0000000000..06a16edf47 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/table/TableColumnModelExt.java @@ -0,0 +1,232 @@ +/* + * $Id: TableColumnModelExt.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.table; + +import org.jdesktop.swingx.JXTable; +import org.jdesktop.swingx.event.TableColumnModelExtListener; + +import javax.swing.event.TableColumnModelListener; +import javax.swing.table.TableColumn; +import javax.swing.table.TableColumnModel; +import java.util.List; + +/** + * An extension of TableColumnModel suitable for use with + * JXTable. It extends the notion of columns considered as part + * of the view realm to include invisible columns. Conceptually, there are + * several sets of "columns": + * + *

                  + *
                1. model columns: all columns of a TableModel. They are but + * a virtual concept, characterizable f.i. by (model) column index, (model) + * column name. + *
                2. view columns: all TableColumnExt objects added to the + * TableColumnModelExt, each typically created and configured in + * relation to a model column. These can be regarded as a kind of subset of the + * model columns (not literally, obviously). Each view column belongs to exactly + * one of the following (real) subsets: + *
                    + *
                  • visible columns: all view columns with the visibility property enabled + *
                  • hidden columns: all view columns with the visibility property disabled + *
                  + *
                + * + * This class manages the view columns and automatically takes care of keeping + * track of their location in the visible/hidden subset, triggering the + * corresponding changes in interested views. Typically, a view column's + * visibility can be toggled by user interaction, f.i. via a ColumnControlButton. + *

                + * An example to programmatically hide + * the first visible column in the column model: + * + *

                
                + * TableColumnExt columnExt = columnModel.getColumnExt(0);
                + * if (columnExt != null) {
                + *     columnExt.setVisible(false);
                + * }
                + * 
                + * + * Note that it is principally allowed to add standard TableColumns. + * Practically, it doesn't make much sense to do so - they will always be + * visible. + *

                + * + * While individual visible columns can be requested by both column identifier + * and column index, the latter is not available for hidden columns. An example + * to programmatically guarantee that the view column which corresponds to the + * first column in the associated TableModel. + * + *

                
                + * List<TableColumn> columns = colModel.getColumns(true);
                + * for (TableColumn column : columns) {
                + *     if (column.getModelIndex() == 0) {
                + *         if (column instanceof TableColumnExt) {
                + *             ((TableColumnExt) column).setVisible(false);
                + *         }
                + *         return;
                + *     }
                + * }
                + * 
                + * + * Alternatively, the column could be requested directly by identifier. By + * default the column's headerValue is returned as identifier, if none is set. + * + *
                
                + * Object identifier = tableModel.getColumnName(0);
                + * TableColumnExt columnExt = columnModel.getColumnExt(identifier);
                + * if (columnExt != null) {
                + *     columnExt.setVisible(false);
                + * }
                + * 
                + * + * Relying on default identifiers is inherently brittle (headerValues might + * change f.i. with Locales), so explicit configuration of columns with + * identifiers is strongly recommended. A custom ColumnFactory + * helps to automate column configuration. + *

                + * + * + * This class guarantees to notify registered + * TableColumnModelListeners of type + * TableColumnModelExtListener about propertyChanges fired by + * contained TableColumns. + * An example of a client which adjusts itself based on + * headerValue property of visible columns: + *

                
                + * TableColumnModelExtListener l = new TableColumnModelExtListener() {
                + * 
                + *     public void columnPropertyChange(PropertyChangeEvent event) {
                + *         if ("headerValue".equals(event.getPropertyName())) {
                + *             TableColumn column = (TableColumn) event.getSource();
                + *             if ((column instanceof TableColumnExt)
                + *                     && !((TableColumnExt) column).isVisible()) {
                + *                 return;
                + *             }
                + *             resizeAndRepaint();
                + *         }
                + *     }
                + * 
                + *     public void columnAdded(TableColumnModelEvent e) {
                + *     }
                + * 
                + *     public void columnMarginChanged(ChangeEvent e) {
                + *     }
                + * 
                + *     public void columnMoved(TableColumnModelEvent e) {
                + *     }
                + * 
                + *     public void columnRemoved(TableColumnModelEvent e) {
                + *     }
                + * 
                + *     public void columnSelectionChanged(ListSelectionEvent e) {
                + *     }
                + * 
                + * };
                + * columnModel.addColumnModelListener(l);
                + * 
                + * + * + * @author Richard Bair + * @author Jeanette Winzenburg + * + * @see DefaultTableColumnModelExt + * @see TableColumnExt + * @see TableColumnModelExtListener + * @see ColumnControlButton + * @see JXTable#setColumnControlVisible + * @see ColumnFactory + * + */ +public interface TableColumnModelExt extends TableColumnModel { + + /** + * Returns the number of contained columns. The count includes or excludes invisible + * columns, depending on whether the includeHidden is true or + * false, respectively. If false, this method returns the same count as + * getColumnCount(). + * + * @param includeHidden a boolean to indicate whether invisible columns + * should be included + * @return the number of contained columns, including or excluding the + * invisible as specified. + */ + public int getColumnCount(boolean includeHidden); + + /** + * Returns a List of contained TableColumns. + * Includes or excludes invisible columns, depending on whether the + * includeHidden is true or false, respectively. If false, an + * Iterator over the List is equivalent to the + * Enumeration returned by getColumns(). + *

                + * + * NOTE: the order of columns in the List depends on whether or not the + * invisible columns are included, in the former case it's the insertion + * order in the latter it's the current order of the visible columns. + * + * @param includeHidden a boolean to indicate whether invisible columns + * should be included + * @return a List of contained columns. + */ + public List getColumns(boolean includeHidden); + + /** + * Returns the first TableColumnExt with the given + * identifier. The return value is null if there is no contained + * column with identifier or if the column with identifier is not + * of type TableColumnExt. The returned column + * may be visible or hidden. + * + * @param identifier the object used as column identifier + * @return first TableColumnExt with the given identifier or + * null if none is found + */ + public TableColumnExt getColumnExt(Object identifier); + + /** + * Returns the TableColumnExt at view position + * columnIndex. The return value is null, if the + * column at position columnIndex is not of type + * TableColumnExt. + * The returned column is visible. + * + * @param columnIndex the index of the column desired + * @return the TableColumnExt object that matches the column + * index + * @throws ArrayIndexOutOfBoundsException if columnIndex out of allowed + * range, that is if + * (columnIndex < 0) || (columnIndex >= getColumnCount()). + */ + public TableColumnExt getColumnExt(int columnIndex); + + /** + * Adds a listener for table column model events. This enhances super's + * behaviour in that it guarantees to notify listeners of type + * TableColumnModelListenerExt about property changes of contained columns. + * + * @param x a TableColumnModelListener object + */ + @Override + public void addColumnModelListener(TableColumnModelListener x); + + +} diff --git a/src/main/java/org/jdesktop/swingx/table/TableRowHeightController.java b/src/main/java/org/jdesktop/swingx/table/TableRowHeightController.java new file mode 100644 index 0000000000..a41e8417f1 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/table/TableRowHeightController.java @@ -0,0 +1,215 @@ +/* + * $Id$ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.table; + +import javax.swing.*; +import javax.swing.event.TableModelEvent; +import javax.swing.event.TableModelListener; +import javax.swing.table.TableModel; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.logging.Logger; + +import static org.jdesktop.swingx.table.TableUtilities.*; + +/** + * A controller to adjust JTable rowHeight based on sizing requirements of its renderers. + * + * @author Jeanette Winzenburg, Berlin + */ +public class TableRowHeightController { + + private JTable table; + private TableModelListener tableModelListener; + private PropertyChangeListener tablePropertyListener; + + /** + * Instantiates an unbound TableRowHeightController. + */ + public TableRowHeightController() { + this(null); + } + + /** + * Instantiates a TableRowHeightController and installs itself to the given table. + * The row heights of all visible rows are automatically adjusted on model changes. + * + * @param table the table to control. + */ + public TableRowHeightController(JTable table) { + install(table); + } + + /** + * Installs this controller on the given table. Releases control from previously + * installed table, if any. + * @param table the table to install upon. + */ + public void install(JTable table) { + release(); + if (table != null) { + this.table = table; + installListeners(); + updatePreferredRowHeights(); + } + } + + /** + * Release this controller from its table. Does nothing if no table installed. + * + */ + public void release() { + if (table == null) + return; + uninstallListeners(); + table = null; + } + + /** + * Sets the row heights of the rows in the range of first- to lastRow, inclusive. + * The coordinates are model indices. + * + * @param firstRow the first row in model coordinates + * @param lastRow the last row in model coordinates + */ + protected void updatePreferredRowHeights(int firstRow, int lastRow) { + for (int row = firstRow; row <= lastRow; row++) { + int viewRow = table.convertRowIndexToView(row); + if (viewRow >= 0) { +// int oldHeight = table.getRowHeight(viewRow); +// LOG.info("in viewRow/old/new: " + viewRow + " / " + oldHeight + " / " + table.getRowHeight(viewRow)); + setPreferredRowHeight(table, viewRow); + } + } + } + + /** + * Sets the row heights of all rows. + */ + protected void updatePreferredRowHeights() { + if (table.getRowCount() == 0) return; + updatePreferredRowHeights(0, table.getModel().getRowCount() - 1); + } + + /** + * @param oldValue + */ + protected void updateModel(TableModel oldValue) { + if (oldValue != null) { + oldValue.removeTableModelListener(getTableModelListener()); + } + table.getModel().addTableModelListener(getTableModelListener()); + updatePreferredRowHeights(); + } + + + /** + * @return + */ + protected PropertyChangeListener createTablePropertyListener() { + PropertyChangeListener l = new PropertyChangeListener() { + + @Override + public void propertyChange(PropertyChangeEvent evt) { + invokedPropertyChanged(evt); + } + + /** + * @param evt + */ + private void invokedPropertyChanged(final PropertyChangeEvent evt) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + if ("model".equals(evt.getPropertyName())) { + updateModel((TableModel) evt.getOldValue()); + } + + } + }); + } + }; + return l; + } + + protected TableModelListener createTableModelListener() { + TableModelListener l = new TableModelListener() { + @Override + public void tableChanged(final TableModelEvent e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + invokedTableChanged(e); + } + }); + } + + private void invokedTableChanged(TableModelEvent e) { + if (isStructureChanged(e) || isDataChanged(e)) { + updatePreferredRowHeights(); + } else if (isUpdate(e) || isInsert(e)) { + updatePreferredRowHeights(e.getFirstRow(), e.getLastRow()); + } + // do nothing on delete + } + }; + return l; + } + /** + * + */ + private void uninstallListeners() { + table.removePropertyChangeListener(getPropertyChangeListener()); + table.getModel().removeTableModelListener(getTableModelListener()); + // whatever else turns out to be needed + } + + private void installListeners() { + table.addPropertyChangeListener(getPropertyChangeListener()); + table.getModel().addTableModelListener(getTableModelListener()); + // whatever else turns out to be needed + } + + /** + * @return + */ + protected TableModelListener getTableModelListener() { + if (tableModelListener == null) { + tableModelListener = createTableModelListener(); + } + return tableModelListener; + } + + /** + * @return + */ + protected PropertyChangeListener getPropertyChangeListener() { + if (tablePropertyListener == null) { + tablePropertyListener = createTablePropertyListener(); + } + return tablePropertyListener; + } + + @SuppressWarnings("unused") + private static final Logger LOG = Logger + .getLogger(TableRowHeightController.class.getName()); +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/table/TableUtilities.java b/src/main/java/org/jdesktop/swingx/table/TableUtilities.java new file mode 100644 index 0000000000..4063789950 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/table/TableUtilities.java @@ -0,0 +1,172 @@ +/* + * Created on 25.02.2011 + * + */ +package org.jdesktop.swingx.table; + +import javax.swing.*; +import javax.swing.event.TableModelEvent; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableColumn; +import javax.swing.table.TableColumnModel; +import java.awt.*; +import java.util.Collections; +import java.util.List; + +/** + * Collection of utility methods for J/X/Table. + * + * @author Jeanette Winzenburg, Berlin + */ +public class TableUtilities { + + + /** + * Returns a boolean indication whether the event represents a + * dataChanged type. + * + * @param e the event to examine. + * @return true if the event is of type dataChanged, false else. + */ + public static boolean isDataChanged(TableModelEvent e) { + if (e == null) + return false; + return e.getType() == TableModelEvent.UPDATE && e.getFirstRow() == 0 + && e.getLastRow() == Integer.MAX_VALUE; + } + + /** + * Returns a boolean indication whether the event represents a + * update type. + * + * @param e the event to examine. + * @return true if the event is a true update, false + * otherwise. + */ + public static boolean isUpdate(TableModelEvent e) { + if (isStructureChanged(e)) + return false; + return e.getType() == TableModelEvent.UPDATE + && e.getLastRow() < Integer.MAX_VALUE; + } + + /** + * Returns a boolean indication whether the event represents a + * insert type. + * + * @param e the event to examine + * @return true if the event is of type insert, false otherwise. + */ + public static boolean isInsert(TableModelEvent e) { + if (e == null) return false; + return TableModelEvent.INSERT == e.getType(); + } + + + /** + * Returns a boolean indication whether the event represents a + * structureChanged type. + * + * @param e the event to examine. + * @return true if the event is of type structureChanged or null, false + * else. + */ + public static boolean isStructureChanged(TableModelEvent e) { + return e == null || e.getFirstRow() == TableModelEvent.HEADER_ROW; + } + + + + /** + * Returns the preferred height for the given row. It loops + * across all visible columns and returns the maximal pref height of + * the rendering component. Falls back to the table's base rowheight, i + * f there are no columns or the renderers + * max is zeor.

                + * + * @param table the table which provides the renderers, must not be null + * @param row the index of the row in view coordinates + * @return the preferred row height of + * @throws NullPointerException if table is null. + * @throws IndexOutOfBoundsException if the row is not a valid row index + */ + public static int getPreferredRowHeight(JTable table, int row) { + int pref = 0; + for (int column = 0; column < table.getColumnCount(); column++) { + TableCellRenderer renderer = table.getCellRenderer(row, column); + Component comp = table.prepareRenderer(renderer, row, column); + pref = Math.max(pref, comp.getPreferredSize().height); + } + return pref > 0 ? pref : table.getRowHeight(); + } + + /** + * + * @param table the table which provides the renderers, must not be null + * @param row the index of the row in view coordinates + * @throws NullPointerException if table is null. + * @throws IndexOutOfBoundsException if the row is not a valid row index + */ + public static void setPreferredRowHeight(JTable table, int row) { + int prefHeight = getPreferredRowHeight(table, row); + table.setRowHeight(row, prefHeight); + } + + /** + * Sets preferred row heights for all visible rows. + * + * @param table the table to set row heights to + * @throws NullPointerException if no table installed. + */ + public static void setPreferredRowHeights(JTable table) { + // care about visible rows only + for (int row = 0; row < table.getRowCount(); row++) { + setPreferredRowHeight(table, row); + } + } + + /** + * Returns an array containing the ordinals of the given values of an Enum.

                + * + * Convience for clients which define TableColumns as Enums (Issue #1304-swingx). + * + * @param values the enums to map to its ordinals + * @return an array of ordinals, guaranteed to be not null + */ + public static int[] ordinalsOf(Enum... values) { + int[] cols = new int[values.length]; + for (int i = 0; i < values.length; i++) { + cols[i] = values[i].ordinal(); + } + return cols; + } + + + /** + * Removes all columns of the given column model. Includes hidden + * columns as indicated by the includesHidden flag, the flag has no + * effect if the model is not of type TableColumnModelExt.

                + * + * @param model the column model to remove all columns from. + * @param includeHidden indicates whether hidden columns should be + * removed as well, has no effect if model is not of type TableColumnModelExt. + */ + /* + * Stand-in instead of fixing of issue http://java.net/jira/browse/SWINGX-1407 + */ + public static void clear(TableColumnModel model, boolean includeHidden) { + if (model instanceof TableColumnModelExt) { + clear(model, ((TableColumnModelExt) model).getColumns(includeHidden)); + } else { + clear(model, Collections.list(model.getColumns())); + } + } + + private static void clear(TableColumnModel model, List columns) { + for (TableColumn tableColumn : columns) { + model.removeColumn(tableColumn); + } + } + + private TableUtilities() {} +} diff --git a/src/main/java/org/jdesktop/swingx/table/package-info.java b/src/main/java/org/jdesktop/swingx/table/package-info.java new file mode 100644 index 0000000000..7f48e8d2d1 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/table/package-info.java @@ -0,0 +1,36 @@ +/* + * $Id: package-info.java 3167 2009-01-02 13:28:04Z rah003 $ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +/**Contains API required by the extended JTable component, JXTable. +

                Package Specification

                + +
                + +

                Related Documentation

                + + + +*/ +package org.jdesktop.swingx.table; \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/text/NumberFormatExt.java b/src/main/java/org/jdesktop/swingx/text/NumberFormatExt.java new file mode 100644 index 0000000000..0c685e17cb --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/text/NumberFormatExt.java @@ -0,0 +1,99 @@ +/* + * $Id$ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.text; + +import java.text.*; + +/** + * A specialised NumberFormat which handles null values and empty Strings. + * This is useful in cell editors and used in StrictNumberFormatter. + * + * @author Noel Grandin + * @author Jeanette Winzenburg + */ +public class NumberFormatExt extends NumberFormat { + + private NumberFormat childFormat; + + public NumberFormatExt() { + this(null); + } + + public NumberFormatExt(NumberFormat childFormat) { + if (childFormat == null) { + childFormat = NumberFormat.getInstance(); + } + this.childFormat = childFormat; + } + + @Override + public AttributedCharacterIterator formatToCharacterIterator(Object obj) { + if (obj == null) + return new AttributedString("").getIterator(); + return childFormat.formatToCharacterIterator(obj); + } + + @Override + public StringBuffer format(Object obj, StringBuffer toAppendTo, + FieldPosition pos) { + if (obj == null) + return new StringBuffer(""); + return childFormat.format(obj, toAppendTo, pos); + } + + @Override + public Number parse(String source, ParsePosition pos) { + if (source == null) { + pos.setIndex(1); // otherwise Format thinks parse failed + return null; + } + if (source.trim().equals("")) { + pos.setIndex(1); // otherwise Format thinks parse failed + return null; + } + Number val = childFormat.parse(source, pos); + /* + * The default behaviour of Format objects is to keep parsing as long as + * they encounter valid data. By for table editing we don't want + * trailing bad data to be considered a "valid value". So set the index + * to 0 so that the parse(Object) method knows that we had an error. + */ + if (pos.getIndex() != source.length()) { + pos.setErrorIndex(pos.getIndex()); + pos.setIndex(0); + } + return val; + } + + @Override + public StringBuffer format(double number, StringBuffer toAppendTo, + FieldPosition pos) { + return childFormat.format(number, toAppendTo, pos); + } + + @Override + public StringBuffer format(long number, StringBuffer toAppendTo, + FieldPosition pos) { + return childFormat.format(number, toAppendTo, pos); + } + +} diff --git a/src/main/java/org/jdesktop/swingx/text/StrictNumberFormatter.java b/src/main/java/org/jdesktop/swingx/text/StrictNumberFormatter.java new file mode 100644 index 0000000000..9e576ba1eb --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/text/StrictNumberFormatter.java @@ -0,0 +1,226 @@ +/* + * $Id$ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.text; + +import javax.swing.text.NumberFormatter; +import java.math.BigDecimal; +import java.text.Format; +import java.text.NumberFormat; +import java.text.ParseException; + +/** + * Experiment to work around Issue #1183-swingx: NumberEditorExt throws exception + * on getCellValue. Remaining issue: no visual error feedback if the expected + * number type exceeds its range. + * + * @author Jeanette Winzenburg + */ +public class StrictNumberFormatter extends NumberFormatter { + + + private BigDecimal maxAsBig; + private BigDecimal minAsBig; + + /** + * @param format + */ + public StrictNumberFormatter(NumberFormat format) { + super(format); + } + + /** + * {@inheritDoc}

                + * + * Overridden to automatically set the minimum/maximum to the boundaries of + * the Number type if it corresponds to a raw type, or null if not. + */ + @Override + public void setValueClass(Class valueClass) { + super.setValueClass(valueClass); + updateMinMax(); + } + + + /** + * + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + private void updateMinMax() { + Comparable min = null; + Comparable max = null; + if (getValueClass() == Integer.class) { + max = Integer.MAX_VALUE; + min = Integer.MIN_VALUE; + } else if (getValueClass() == Long.class) { + max = Long.MAX_VALUE; + min = Long.MIN_VALUE; + } else if (getValueClass() == Short.class) { + max = Short.MAX_VALUE; + min = Short.MIN_VALUE; + } else if (getValueClass() == Byte.class) { + max = Byte.MAX_VALUE; + min = Byte.MIN_VALUE; + } else if (getValueClass() == Float.class) { + // Issue #1528-swingx: float doesn't take negative/zero numbers + // was: crude error - Float.MIN_VALUE is the smallest _positive_ + max = Float.MAX_VALUE; + min = - Float.MAX_VALUE; + } else if (getValueClass() == Double.class) { + // don*t understand what's happening here, naive compare with bigDecimal + // fails - so don't do anything for now + // JW: revisit! + } + setMaximum(max); + setMinimum(min); + } + + + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + public void setMaximum(Comparable max) { + super.setMaximum(max); + this.maxAsBig = max != null ? new BigDecimal(max.toString()) : null; + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + public void setMinimum(Comparable minimum) { + super.setMinimum(minimum); + this.minAsBig = minimum != null ? new BigDecimal(minimum.toString()) : null; + } + + + /** + * Returns the Object representation of the + * String text, may be null. + * + * @param text String to convert + * @return Object representation of text + * @throws ParseException if there is an error in the conversion + */ + @Override + public Object stringToValue(String text) throws ParseException { + Object value = getParsedValue(text, getFormat()); + try { + if (!isValueInRange(value, true)) { + throw new ParseException("Value not within min/max range", 0); + } + } catch (ClassCastException cce) { + throw new ParseException("Class cast exception comparing values: " + + cce, 0); + } + return convertValueToValueClass(value, getValueClass()); + } + + /** + * Converts the passed in value to the passed in class. This only + * works if valueClass is one of Integer, + * Long, Float, Double, + * Byte or Short and value + * is an instanceof Number. + */ + private Object convertValueToValueClass(Object value, Class valueClass) { + if (valueClass != null && (value instanceof Number)) { + if (valueClass == Integer.class) { + return ((Number) value).intValue(); + } + else if (valueClass == Long.class) { + return ((Number) value).longValue(); + } + else if (valueClass == Float.class) { + return ((Number) value).floatValue(); + } + else if (valueClass == Double.class) { + return ((Number) value).doubleValue(); + } + else if (valueClass == Byte.class) { + return ((Number) value).byteValue(); + } + else if (valueClass == Short.class) { + return ((Number) value).shortValue(); + } + } + return value; + } + + /** + * Invokes parseObject on f, returning + * its value. + */ + private Object getParsedValue(String text, Format f) throws ParseException { + if (f == null) { + return text; + } + return f.parseObject(text); + } + + + /** + * Returns true if value is between the min/max. + * + * @param wantsCCE If false, and a ClassCastException is thrown in + * comparing the values, the exception is consumed and + * false is returned. + */ + private boolean isValueInRange(Object orgValue, boolean wantsCCE) { + if (orgValue == null) return true; + if ((getMinimum() == null) && getMaximum() == null) return true; + + BigDecimal value = new BigDecimal(orgValue.toString()); + Comparable min = getMinimumAsBig(); + + try { + if (min != null && min.compareTo(value) > 0) { + return false; + } + } catch (ClassCastException cce) { + if (wantsCCE) { + throw cce; + } + return false; + } + + Comparable max = getMaximumAsBig(); + try { + if (max != null && max.compareTo(value) < 0) { + return false; + } + } catch (ClassCastException cce) { + if (wantsCCE) { + throw cce; + } + return false; + } + return true; + } + + + private Comparable getMinimumAsBig() { + return minAsBig; + } + + private Comparable getMaximumAsBig() { + return maxAsBig; + } + + +} diff --git a/src/main/java/org/jdesktop/swingx/tips/DefaultTip.java b/src/main/java/org/jdesktop/swingx/tips/DefaultTip.java new file mode 100644 index 0000000000..22d1c53850 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/tips/DefaultTip.java @@ -0,0 +1,67 @@ +/* + * $Id: DefaultTip.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.tips; + +import org.jdesktop.swingx.tips.TipOfTheDayModel.Tip; + +/** + * Default {@link Tip} implementation.
                + * + * @author Frederic Lavigne + */ +public class DefaultTip implements Tip { + + private String name; + + private Object tip; + + public DefaultTip() { + } + + public DefaultTip(String name, Object tip) { + this.name = name; + this.tip = tip; + } + + @Override + public Object getTip() { + return tip; + } + + public void setTip(Object tip) { + this.tip = tip; + } + + @Override + public String getTipName() { + return name; + } + + public void setTipName(String name) { + this.name = name; + } + + @Override + public String toString() { + return getTipName(); + } + +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/tips/DefaultTipOfTheDayModel.java b/src/main/java/org/jdesktop/swingx/tips/DefaultTipOfTheDayModel.java new file mode 100644 index 0000000000..c5662f3b77 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/tips/DefaultTipOfTheDayModel.java @@ -0,0 +1,75 @@ +/* + * $Id: DefaultTipOfTheDayModel.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.tips; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +/** + * Default {@link TipOfTheDayModel} implementation.
                + * + * @author Frederic Lavigne + */ +public class DefaultTipOfTheDayModel implements TipOfTheDayModel { + + private List tips = new ArrayList(); + + public DefaultTipOfTheDayModel() { + } + + public DefaultTipOfTheDayModel(Tip[] tips) { + this(Arrays.asList(tips)); + } + + public DefaultTipOfTheDayModel(Collection tips) { + this.tips.addAll(tips); + } + + @Override + public Tip getTipAt(int index) { + return tips.get(index); + } + + @Override + public int getTipCount() { + return tips.size(); + } + + public void add(Tip tip) { + tips.add(tip); + } + + public void remove(Tip tip) { + tips.remove(tip); + } + + public Tip[] getTips() { + return tips.toArray(new Tip[tips.size()]); + } + + public void setTips(Tip[] tips) { + this.tips.clear(); + this.tips.addAll(Arrays.asList(tips)); + } + +} diff --git a/src/main/java/org/jdesktop/swingx/tips/TipLoader.java b/src/main/java/org/jdesktop/swingx/tips/TipLoader.java new file mode 100644 index 0000000000..4d2f010400 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/tips/TipLoader.java @@ -0,0 +1,86 @@ +/* + * $Id: TipLoader.java 542 2005-10-10 18:03:15Z rbair $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.tips; + +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + +/** + * Loads tips from Properties.
                + * + * @author Frederic Lavigne + */ +public class TipLoader { + + private TipLoader() { } + + /** + * Initializes a TipOfTheDayModel from properties. Each tip is defined by two + * properties, its name and its description: + * + *

                +   * 
                +   * tip.1.name=First Tip
                +   * tip.1.description=This is the description
                +   *  
                +   * tip.2.name=Second Tip
                +   * tip.2.description=<html>This is an html description
                +   * 
                +   * ...
                +   * 
                +   * tip.10.description=No name for this tip, name is optional
                +   * 
                +   * 
                + * + * @param props + * @return a TipOfTheDayModel + * @throws IllegalArgumentException + * if a name is found without description + */ + public static TipOfTheDayModel load(Properties props) { + List tips = new ArrayList(); + + int count = 1; + while (true) { + String nameKey = "tip." + count + ".name"; + String nameValue = props.getProperty(nameKey); + + String descriptionKey = "tip." + count + ".description"; + String descriptionValue = props.getProperty(descriptionKey); + + if (nameValue != null && descriptionValue == null) { throw new IllegalArgumentException( + "No description for name " + nameValue); } + + if (descriptionValue == null) { + break; + } + + DefaultTip tip = new DefaultTip(nameValue, descriptionValue); + tips.add(tip); + + count++; + } + + return new DefaultTipOfTheDayModel(tips); + } + +} diff --git a/src/main/java/org/jdesktop/swingx/tips/TipOfTheDayModel.java b/src/main/java/org/jdesktop/swingx/tips/TipOfTheDayModel.java new file mode 100644 index 0000000000..1e608e125e --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/tips/TipOfTheDayModel.java @@ -0,0 +1,65 @@ +/* + * $Id: TipOfTheDayModel.java 542 2005-10-10 18:03:15Z rbair $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.tips; + +import org.jdesktop.swingx.JXTipOfTheDay; + +/** + * A model for {@link JXTipOfTheDay}.
                + * + * @author Frederic Lavigne + */ +public interface TipOfTheDayModel { + + /** + * @return the number of tips in this model + */ + int getTipCount(); + + /** + * @param index + * @return the tip at index + * @throws IndexOutOfBoundsException + * if the index is out of range (index < 0 || index >= + * getTipCount()). + */ + Tip getTipAt(int index); + + /** + * A tip.
                + */ + interface Tip { + + /** + * @return very short (optional) text describing the tip + */ + String getTipName(); + + /** + * The tip object to show. See {@link JXTipOfTheDay} for supported object + * types. + * + * @return the tip to display + */ + Object getTip(); + } + +} diff --git a/src/main/java/org/jdesktop/swingx/tips/package-info.java b/src/main/java/org/jdesktop/swingx/tips/package-info.java new file mode 100644 index 0000000000..04408783ed --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/tips/package-info.java @@ -0,0 +1,27 @@ +/* + * $Id: package-info.java 3167 2009-01-02 13:28:04Z rah003 $ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +/** Provides classes and interfaces for dealing with + org.jdesktop.swingx.JXTipOfTheDay. + +

                Related Documentation

                +*/ +package org.jdesktop.swingx.tips; \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/tree/DefaultXTreeCellEditor.java b/src/main/java/org/jdesktop/swingx/tree/DefaultXTreeCellEditor.java new file mode 100644 index 0000000000..04063e1a47 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/tree/DefaultXTreeCellEditor.java @@ -0,0 +1,178 @@ +/* + * $Id: DefaultXTreeCellEditor.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.tree; + +import org.jdesktop.swingx.plaf.UIDependent; + +import javax.swing.*; +import javax.swing.tree.DefaultTreeCellEditor; +import javax.swing.tree.DefaultTreeCellRenderer; +import javax.swing.tree.TreeCellEditor; +import java.awt.*; + +/** + * Subclassed to hack around core bug with RtoL editing (#4980473). + * + * The price to pay is currently is to guarantee a minimum size of the + * editing field (is only one char wide if the node value is null). + * + * PENDING: any possibility to position the editorContainer? + * BasicTreeUI adds it to the tree and positions at the node location. + * That's not a problem in LToR, only + * in RToL + * + * + * @author Jeanette Winzenburg + */ +public class DefaultXTreeCellEditor extends DefaultTreeCellEditor implements UIDependent { + + public DefaultXTreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer) { + super(tree, renderer); + } + + public DefaultXTreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer, + TreeCellEditor editor) { + super(tree, renderer, editor); + } + + public void setRenderer(DefaultTreeCellRenderer renderer) { + this.renderer = renderer; + } + + public DefaultTreeCellRenderer getRenderer() { + return renderer; + } + + public class XEditorContainer extends EditorContainer { + + @Override + public Dimension getPreferredSize() { + if (isRightToLeft()) { + if(editingComponent != null) { + Dimension pSize = editingComponent.getPreferredSize(); + + pSize.width += offset + 5; + + Dimension rSize = (renderer != null) ? + renderer.getPreferredSize() : null; + + if(rSize != null) + pSize.height = Math.max(pSize.height, rSize.height); + if(editingIcon != null) + pSize.height = Math.max(pSize.height, + editingIcon.getIconHeight()); + + // trying to enforce a minimum size leads to field being painted over the icon + // Make sure width is at least 100. + // pSize.width = Math.max(pSize.width, 100); + return pSize; + } + return new Dimension(0, 0); + } + return super.getPreferredSize(); + + } + + @Override + public void doLayout() { + if (isRightToLeft()) { + Dimension cSize = getSize(); + + editingComponent.getPreferredSize(); + editingComponent.setLocation(0, 0); + editingComponent.setBounds(0, 0, + cSize.width - offset, + cSize.height); + } else { + + super.doLayout(); + } + } + + + @Override + public void paint(Graphics g) { + if (isRightToLeft()) { + Dimension size = getSize(); + + // Then the icon. + if (editingIcon != null) { + int yLoc = Math.max(0, (size.height - editingIcon + .getIconHeight()) / 2); + int xLoc = Math.max(0, size.width - offset); + editingIcon.paintIcon(this, g, xLoc, yLoc); + } + // need to prevent super from painting the icon + Icon rememberIcon = editingIcon; + editingIcon = null; + super.paint(g); + editingIcon = rememberIcon; + + } else { + super.paint(g); + } + } + + } + + + @Override + protected Container createContainer() { + return new XEditorContainer(); + } + + @Override + protected void prepareForEditing() { + super.prepareForEditing(); + applyComponentOrientation(); + } + + protected void applyComponentOrientation() { + if (tree != null) { + editingContainer.applyComponentOrientation(tree.getComponentOrientation()); + } + + } + + /** + * @return + */ + private boolean isRightToLeft() { + return (tree != null) && (!tree.getComponentOrientation().isLeftToRight()); + } + + /** + * Implement UIDependent. Quick hack for #1060-swingx: icons lost on laf toggle. + */ + @Override + public void updateUI() { + if (getRenderer() != null) { + SwingUtilities.updateComponentTreeUI(getRenderer()); + } + if (realEditor instanceof JComponent) { + SwingUtilities.updateComponentTreeUI((JComponent) realEditor); + } else if (realEditor instanceof UIDependent) { + ((UIDependent) realEditor).updateUI(); + } + + } + +} diff --git a/src/main/java/org/jdesktop/swingx/tree/DefaultXTreeCellRenderer.java b/src/main/java/org/jdesktop/swingx/tree/DefaultXTreeCellRenderer.java new file mode 100644 index 0000000000..e8a2eeb157 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/tree/DefaultXTreeCellRenderer.java @@ -0,0 +1,98 @@ +/* + * $Id: DefaultXTreeCellRenderer.java 3304 2009-03-20 15:11:25Z kleopatra $ + * + * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.jdesktop.swingx.tree; + +import org.jdesktop.swingx.SwingXUtilities; + +import javax.swing.*; +import javax.swing.tree.DefaultTreeCellRenderer; + +/** + * Quick fix for #1061-swingx (which actually is a core issue): + * tree icons lost on toggle laf. Updates colors as well - + * but beware: this is incomplete as some of super's properties are private! + * + * Will not do more because in the longer run (as soon as we've fixed the editor issues) + * the JXTree's default renderer will be changed to SwingX DefaultTreeRenderer. + * + * @author Jeanette Winzenburg + */ +public class DefaultXTreeCellRenderer extends DefaultTreeCellRenderer { + + /** + * {@inheritDoc}

                + * + * Overridden to update icons and colors. + */ + @Override + public void updateUI() { + super.updateUI(); + updateIcons(); + updateColors(); + } + + /** + * + */ + protected void updateColors() { + if (SwingXUtilities.isUIInstallable(getTextSelectionColor())) { + setTextSelectionColor(UIManager.getColor("Tree.selectionForeground")); + } + if (SwingXUtilities.isUIInstallable(getTextNonSelectionColor())) { + setTextNonSelectionColor(UIManager.getColor("Tree.textForeground")); + } + if (SwingXUtilities.isUIInstallable(getBackgroundSelectionColor())) { + setBackgroundSelectionColor(UIManager.getColor("Tree.selectionBackground")); + } + if (SwingXUtilities.isUIInstallable(getBackgroundNonSelectionColor())) { + setBackgroundNonSelectionColor(UIManager.getColor("Tree.textBackground")); + } + if (SwingXUtilities.isUIInstallable(getBorderSelectionColor())) { + setBorderSelectionColor(UIManager.getColor("Tree.selectionBorderColor")); + } +// Object value = UIManager.get("Tree.drawsFocusBorderAroundIcon"); +// drawsFocusBorderAroundIcon = (value != null && ((Boolean)value). +// booleanValue()); +// value = UIManager.get("Tree.drawDashedFocusIndicator"); +// drawDashedFocusIndicator = (value != null && ((Boolean)value). +// booleanValue()); + } + + /** + * + */ + protected void updateIcons() { + if (SwingXUtilities.isUIInstallable(getLeafIcon())) { + setLeafIcon(UIManager.getIcon("Tree.leafIcon")); + } + if (SwingXUtilities.isUIInstallable(getClosedIcon())) { + setClosedIcon(UIManager.getIcon("Tree.closedIcon")); + } + if (SwingXUtilities.isUIInstallable(getOpenIcon())) { + setOpenIcon(UIManager.getIcon("Tree.openIcon")); + } + + } + + + +} diff --git a/src/main/java/org/jdesktop/swingx/tree/TreeModelSupport.java b/src/main/java/org/jdesktop/swingx/tree/TreeModelSupport.java new file mode 100644 index 0000000000..f6e8742a1b --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/tree/TreeModelSupport.java @@ -0,0 +1,338 @@ +/* + * $Id: TreeModelSupport.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.tree; + +import org.jdesktop.swingx.util.Contract; + +import javax.swing.event.EventListenerList; +import javax.swing.event.TreeModelEvent; +import javax.swing.event.TreeModelListener; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreePath; + +/** + * Support for change notification, usable by {@code TreeModel}s. + * + * The changed/inserted/removed is expressed in terms of a {@code TreePath}, + * it's up to the client model to build it as appropriate. + * + * This is inspired by {@code AbstractTreeModel} from Christian Kaufhold, + * www.chka.de. + * + * TODO - implement and test precondition failure of added/removed notification + * + * @author JW + */ +public final class TreeModelSupport { + protected EventListenerList listeners; + + private TreeModel treeModel; + + /** + * Creates the support class for the given {@code TreeModel}. + * + * @param model the model to support + * @throws NullPointerException if {@code model} is {@code null} + */ + public TreeModelSupport(TreeModel model) { + if (model == null) + throw new NullPointerException("model must not be null"); + listeners = new EventListenerList(); + this.treeModel = model; + } + +//---------------------- structural changes on subtree + + /** + * Notifies registered TreeModelListeners that the tree's root has + * been replaced. Can cope with a null root. + */ + public void fireNewRoot() { + + Object root = treeModel.getRoot(); + + /* + * Undocumented. I think it is the only reasonable/possible solution to + * use use null as path if there is no root. TreeModels without root + * aren't important anyway, since JTree doesn't support them (yet). + */ + TreePath path = (root != null) ? new TreePath(root) : null; + fireTreeStructureChanged(path); + } + + /** + * Call when a node has changed its leaf state.

                + * + * PENDING: rename? Do we need it? + * @param path the path to the node with changed leaf state. + */ + public void firePathLeafStateChanged(TreePath path) { + fireTreeStructureChanged(path); + } + + /** + * Notifies registered TreeModelListeners that the structure + * below the node identified by the given path has been + * completely changed. + *

                + * NOTE: the subtree path maybe null if the root is null. + * If not null, it must contain at least one element (the root). + * + * @param subTreePath the path to the root of the subtree + * whose structure was changed. + * @throws NullPointerException if the path is not null but empty + * or contains null elements. + */ + public void fireTreeStructureChanged(TreePath subTreePath) { + if (subTreePath != null) { + Contract.asNotNull(subTreePath.getPath(), + "path must not contain null elements"); + } + Object[] pairs = listeners.getListenerList(); + + TreeModelEvent e = null; + + for (int i = pairs.length - 2; i >= 0; i -= 2) { + if (pairs[i] == TreeModelListener.class) { + if (e == null) + e = createStructureChangedEvent(subTreePath); + + ((TreeModelListener) pairs[i + 1]).treeStructureChanged(e); + } + } + } + +//----------------------- node modifications, no mutations + + /** + * Notifies registered TreeModelListeners that the + * the node identified by the given path has been modified. + * + * @param path the path to the node that has been modified, + * must not be null and must not contain null path elements. + * + */ + public void firePathChanged(TreePath path) { + Object node = path.getLastPathComponent(); + TreePath parentPath = path.getParentPath(); + + if (parentPath == null) + fireChildrenChanged(path, null, null); + else { + Object parent = parentPath.getLastPathComponent(); + + fireChildChanged(parentPath, treeModel + .getIndexOfChild(parent, node), node); + } + } + + /** + * Notifies registered TreeModelListeners that the given child of + * the node identified by the given parent path has been modified. + * The parent path must not be null, nor empty nor contain null + * elements. + * + * @param parentPath the path to the parent of the modified children. + * @param index the position of the child + * @param child child node that has been modified, must not be null + */ + public void fireChildChanged(TreePath parentPath, int index, Object child) { + fireChildrenChanged(parentPath, new int[] { index }, + new Object[] { child }); + } + + /** + * Notifies registered TreeModelListeners that the given children of + * the node identified by the given parent path have been modified. + * The parent path must not be null, nor empty nor contain null + * elements. Note that the index array must contain the position of the + * corresponding child in the the children array. The indices must be in + * ascending order.

                + * + * The exception to these rules is if the root itself has been + * modified (which has no parent by definition). In this case + * the path must be the path to the root and both indices and children + * arrays must be null. + * + * @param parentPath the path to the parent of the modified children. + * @param indices the positions of the modified children + * @param children the modified children + */ + public void fireChildrenChanged(TreePath parentPath, int[] indices, + Object[] children) { + Contract.asNotNull(parentPath.getPath(), + "path must not be null and must not contain null elements"); + Object[] pairs = listeners.getListenerList(); + + TreeModelEvent e = null; + + for (int i = pairs.length - 2; i >= 0; i -= 2) { + if (pairs[i] == TreeModelListener.class) { + if (e == null) + e = createTreeModelEvent(parentPath, indices, children); + + ((TreeModelListener) pairs[i + 1]).treeNodesChanged(e); + } + } + } + + +//------------------------ mutations (insert/remove nodes) + + + /** + * Notifies registered TreeModelListeners that the child has been added to + * the the node identified by the given parent path at the given position. + * The parent path must not be null, nor empty nor contain null elements. + * + * @param parentPath the path to the parent of added child. + * @param index the position of the added children + * @param child the added child + */ + public void fireChildAdded(TreePath parentPath, int index, Object child) { + fireChildrenAdded(parentPath, new int[] { index }, + new Object[] { child }); + } + + /** + * Notifies registered TreeModelListeners that the child has been removed + * from the node identified by the given parent path from the given position. + * The parent path must not be null, nor empty nor contain null elements. + * + * @param parentPath the path to the parent of removed child. + * @param index the position of the removed children before the removal + * @param child the removed child + */ + public void fireChildRemoved(TreePath parentPath, int index, Object child) { + fireChildrenRemoved(parentPath, new int[] { index }, + new Object[] { child }); + } + + /** + * Notifies registered TreeModelListeners that the given children have been + * added to the the node identified by the given parent path at the given + * locations. The parent path and the child array must not be null, nor + * empty nor contain null elements. Note that the index array must contain + * the position of the corresponding child in the the children array. The + * indices must be in ascending order. + *

                + * + * @param parentPath the path to the parent of the added children. + * @param indices the positions of the added children. + * @param children the added children. + */ + public void fireChildrenAdded(TreePath parentPath, int[] indices, + Object[] children) { + Object[] pairs = listeners.getListenerList(); + + TreeModelEvent e = null; + + for (int i = pairs.length - 2; i >= 0; i -= 2) { + if (pairs[i] == TreeModelListener.class) { + if (e == null) + e = createTreeModelEvent(parentPath, indices, children); + + ((TreeModelListener) pairs[i + 1]).treeNodesInserted(e); + } + } + } + + /** + * Notifies registered TreeModelListeners that the given children have been + * removed to the the node identified by the given parent path from the + * given locations. The parent path and the child array must not be null, + * nor empty nor contain null elements. Note that the index array must + * contain the position of the corresponding child in the the children + * array. The indices must be in ascending order. + *

                + * + * @param parentPath the path to the parent of the removed children. + * @param indices the positions of the removed children before the removal + * @param children the removed children + */ + public void fireChildrenRemoved(TreePath parentPath, int[] indices, + Object[] children) { + Object[] pairs = listeners.getListenerList(); + + TreeModelEvent e = null; + + for (int i = pairs.length - 2; i >= 0; i -= 2) { + if (pairs[i] == TreeModelListener.class) { + if (e == null) + e = createTreeModelEvent(parentPath, indices, children); + ((TreeModelListener) pairs[i + 1]).treeNodesRemoved(e); + } + } + } + +//------------------- factory methods of TreeModelEvents + + /** + * Creates and returns a TreeModelEvent for structureChanged + * event notification. The given path may be null to indicate + * setting a null root. In all other cases, the first path element + * must contain the root and the last path element the rootNode of the + * structural change. Specifically, a TreePath with a single element + * (which is the root) denotes a structural change of the complete tree. + * + * @param parentPath the path to the root of the changed structure, + * may be null to indicate setting a null root. + * @return a TreeModelEvent for structureChanged notification. + * + * @see TreeModelEvent + * @see TreeModelListener + */ + private TreeModelEvent createStructureChangedEvent(TreePath parentPath) { + return createTreeModelEvent(parentPath, null, null); + } + + /** + * Creates and returns a TreeModelEvent for changed/inserted/removed + * event notification. + * + * @param parentPath path to parent of modified node + * @param indices the indices of the modified children (before the change) + * @param children the array of modified children + * @return a TreeModelEvent for changed/inserted/removed notification + * + * @see TreeModelEvent + * @see TreeModelListener + */ + private TreeModelEvent createTreeModelEvent(TreePath parentPath, + int[] indices, Object[] children) { + return new TreeModelEvent(treeModel, parentPath, indices, children); + } + + +//------------------------ handling listeners + + public void addTreeModelListener(TreeModelListener l) { + listeners.add(TreeModelListener.class, l); + } + + public TreeModelListener[] getTreeModelListeners() { + return listeners.getListeners(TreeModelListener.class); + } + + public void removeTreeModelListener(TreeModelListener l) { + listeners.remove(TreeModelListener.class, l); + } +} diff --git a/src/main/java/org/jdesktop/swingx/tree/TreeUtilities.java b/src/main/java/org/jdesktop/swingx/tree/TreeUtilities.java new file mode 100644 index 0000000000..88dfa6b2be --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/tree/TreeUtilities.java @@ -0,0 +1,423 @@ +package org.jdesktop.swingx.tree; + +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; +import java.util.*; +import java.util.logging.Logger; + +/** + * Contains convenience classes/methods for handling hierarchical Swing structures. + * + * @author Jeanette Winzenburg, Berlin + */ +@SuppressWarnings({ "unchecked", "rawtypes" }) +public class TreeUtilities { + + /** + * An enumeration that is always empty. + */ + public static final Enumeration EMPTY_ENUMERATION + = new Enumeration() { + @Override + public boolean hasMoreElements() { return false; } + @Override + public Object nextElement() { + throw new NoSuchElementException("No more elements"); + } + }; + + /** + * Implementation of a preorder traversal of a TreeModel. + */ + public static class PreorderModelEnumeration implements Enumeration { + protected Deque stack; + protected TreeModel model; + // the last component is the current subtree to travers + private TreePath path; + + /** + * Instantiates a preorder traversal starting from the root of the + * TreeModel. + * + * @param model the TreeModel to travers. + */ + public PreorderModelEnumeration(TreeModel model) { + this(model, model.getRoot()); + } + + /** + * Instantiates a preorder traversal of the TreeModel which + * starts at the given node. It iterates over all nodes of the + * subtree, only. + * + * @param model the TreeModel to travers. + * @param node the node to start + */ + public PreorderModelEnumeration(TreeModel model, Object node) { + this.model = model; + stack = new ArrayDeque(); + pushNodeAsEnumeration(node); + } + + /** + * Instantiates a preorder traversal of the TreeModel which starts at the + * last path component of the given TreePath. It iterates over all nodes + * of the subtree and all of its siblings, with the same end as a traversal + * starting at the model's roolt would have. + * + * @param model the TreeModel to travers. + * @param path the TreePath to start from + */ + public PreorderModelEnumeration(TreeModel model, TreePath path) { + this(model, path.getLastPathComponent()); + this.path = path; + } + + @Override + public boolean hasMoreElements() { + return (!stack.isEmpty() && stack.peek().hasMoreElements()); + } + + @Override + public Object nextElement() { + Enumeration enumer = stack.peek(); + Object node = enumer.nextElement(); + Enumeration children = children(model, node); + + if (!enumer.hasMoreElements()) { + stack.pop(); + } + if (children.hasMoreElements()) { + stack.push(children); + } + if (!hasMoreElements()) { + // check if there are more subtrees to travers + // and update internal state accordingly + updateSubtree(); + } + return node; + } + + /** + * + */ + private void updateSubtree() { + if (path == null) return; + TreePath parentPath = path.getParentPath(); + if (parentPath == null) { + // root + path = null; + return; + } + Object parent = parentPath.getLastPathComponent(); + Object currentNode = path.getLastPathComponent(); + int currentIndex = model.getIndexOfChild(parent, currentNode); + if (currentIndex +1 < model.getChildCount(parent)) { + // use sibling + Object child = model.getChild(parent, currentIndex + 1); + path = parentPath.pathByAddingChild(child); + pushNodeAsEnumeration(child); + } else { + path = parentPath; + // up one level + updateSubtree(); + } + } + + private void pushNodeAsEnumeration(Object node) { + // single element enum + Vector v = new Vector(1); + v.add(node); + stack.push(v.elements()); //children(model)); + } + + + } // End of class PreorderEnumeration + + + /** + * Implementation of a breadthFirst traversal of a subtree in a TreeModel. + */ + public static class BreadthFirstModelEnumeration implements Enumeration { + protected Queue queue; + private TreeModel model; + + public BreadthFirstModelEnumeration(TreeModel model) { + this(model, model.getRoot()); + } + + public BreadthFirstModelEnumeration(TreeModel model, Object node) { + this.model = model; + // Vector is just used for getting an Enumeration easily + Vector v = new Vector(1); + v.addElement(node); + queue = new ArrayDeque(); + queue.offer(v.elements()); + } + + @Override + public boolean hasMoreElements() { + return !queue.isEmpty() && + queue.peek().hasMoreElements(); + } + + @Override + public Object nextElement() { + // look at head + Enumeration enumer = queue.peek(); + Object node = enumer.nextElement(); + Enumeration children = children(model, node); + + if (!enumer.hasMoreElements()) { + // remove head + queue.poll(); + } + if (children.hasMoreElements()) { + // add at tail + queue.offer(children); + } + return node; + } + + } // End of class BreadthFirstEnumeration + + + /** + * Implementation of a postorder traversal of a subtree in a TreeModel. + */ + public static class PostorderModelEnumeration implements Enumeration { + protected TreeModel model; + protected Object root; + protected Enumeration children; + protected Enumeration subtree; + + public PostorderModelEnumeration(TreeModel model) { + this(model, model.getRoot()); + } + + public PostorderModelEnumeration(TreeModel model, Object node) { + this.model = model; + root = node; + children = children(model, root); + subtree = EMPTY_ENUMERATION; + } + + @Override + public boolean hasMoreElements() { + return root != null; + } + + @Override + public Object nextElement() { + Object retval; + + if (subtree.hasMoreElements()) { + retval = subtree.nextElement(); + } else if (children.hasMoreElements()) { + subtree = new PostorderModelEnumeration(model, + children.nextElement()); + retval = subtree.nextElement(); + } else { + retval = root; + root = null; + } + + return retval; + } + + } // End of class PostorderEnumeration + + /** + * Implementation of a preorder traversal of a subtree with nodes of type TreeNode. + */ + public static class PreorderNodeEnumeration implements Enumeration { + protected Deque> stack; + + public PreorderNodeEnumeration(M rootNode) { + // Vector is just used for getting an Enumeration easily + Vector v = new Vector(1); + v.addElement(rootNode); + stack = new ArrayDeque>(); + stack.push(v.elements()); + } + + @Override + public boolean hasMoreElements() { + return (!stack.isEmpty() && + stack.peek().hasMoreElements()); + } + + @Override + public M nextElement() { + Enumeration enumer = stack.peek(); + M node = enumer.nextElement(); + Enumeration children = getChildren(node); + + if (!enumer.hasMoreElements()) { + stack.pop(); + } + if (children.hasMoreElements()) { + stack.push(children); + } + return node; + } + + protected Enumeration getChildren(M node) { + return (Enumeration) node.children(); + } + + } // End of class PreorderEnumeration + + /** + * Implementation of a postorder traversal of a subtree with nodes of type TreeNode. + */ + public static class PostorderNodeEnumeration implements Enumeration { + protected M root; + protected Enumeration children; + protected Enumeration subtree; + + public PostorderNodeEnumeration(M rootNode) { + super(); + root = rootNode; + children = getChildren(rootNode); + subtree = EMPTY_ENUMERATION; + } + @Override + public boolean hasMoreElements() { + return root != null; + } + + @Override + public M nextElement() { + M retval; + + if (subtree.hasMoreElements()) { + retval = subtree.nextElement(); + } else if (children.hasMoreElements()) { + subtree = createSubTree(children.nextElement()); + retval = subtree.nextElement(); + } else { + retval = root; + root = null; + } + + return retval; + } + + /** + * Creates and returns a PostorderEnumeration on the given node. + * + * @param node the node to create the PostorderEnumeration for + * @return the PostorderEnumeration on the given node + */ + protected PostorderNodeEnumeration createSubTree(M node) { + return new PostorderNodeEnumeration(node); + } + + /** + * Returns an enumeration on the children of the root node. + * @param node + * @return + */ + protected Enumeration getChildren(M node) { + return (Enumeration) node.children(); + } + + + } // End of class PostorderEnumeration + + + /** + * Implementation of a breadthFirst traversal of a subtree with nodes of type TreeNode. + */ + public static class BreadthFirstNodeEnumeration implements Enumeration { + protected Queue> queue; + + public BreadthFirstNodeEnumeration(M rootNode) { + // Vector is just used for getting an Enumeration easily + Vector v = new Vector(1); + v.addElement(rootNode); + queue = new ArrayDeque>(); + queue.offer(v.elements()); + } + + @Override + public boolean hasMoreElements() { + return !queue.isEmpty() && + queue.peek().hasMoreElements(); + } + + @Override + public M nextElement() { + // look at head + Enumeration enumer = queue.peek(); + M node = enumer.nextElement(); + Enumeration children = getChildren(node); + + if (!enumer.hasMoreElements()) { + // remove head + queue.poll(); + } + if (children.hasMoreElements()) { + // add at tail + queue.offer(children); + } + return node; + } + + protected Enumeration getChildren(M node) { + return (Enumeration) node.children(); + } + + + } // End of class BreadthFirstEnumeration + + /** + * Creates and returns an Enumeration across the direct children of the + * rootNode in the given TreeModel. + * + * @param model the TreeModel which contains parent, must not be null + * @return an Enumeration across the direct children of the model's root, the enumeration + * is empty if the root is null or contains no children + */ + public static Enumeration children(TreeModel model) { + return children(model, model.getRoot()); + } + + /** + * Creates and returns an Enumeration across the direct children of parentNode + * in the given TreeModel. + * + * @param model the TreeModel which contains parent, must not be null + * @param parent the parent of the enumerated children + * @return an Enumeration across the direct children of parent, the enumeration + * is empty if the parent is null or contains no children + */ + public static Enumeration children(final TreeModel model, final Object parent) { + if (parent == null || model.isLeaf(parent)) { + return EMPTY_ENUMERATION; + } + Enumeration e = new Enumeration() { + + int currentIndex = 0; + @Override + public boolean hasMoreElements() { + return model.getChildCount(parent) > currentIndex; + } + + @Override + public Object nextElement() { + return model.getChild(parent, currentIndex++); + } + + }; + return e; + } + + private TreeUtilities() {} + + @SuppressWarnings("unused") + private static final Logger LOG = Logger.getLogger(TreeUtilities.class + .getName()); +} diff --git a/src/main/java/org/jdesktop/swingx/tree/package-info.java b/src/main/java/org/jdesktop/swingx/tree/package-info.java new file mode 100644 index 0000000000..95fef0416d --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/tree/package-info.java @@ -0,0 +1,25 @@ +/* + * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ + * + * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +/** + * Contains Tree specific classes and interfaces. + */ +package org.jdesktop.swingx.tree; + diff --git a/src/main/java/org/jdesktop/swingx/treetable/AbstractMutableTreeTableNode.java b/src/main/java/org/jdesktop/swingx/treetable/AbstractMutableTreeTableNode.java new file mode 100644 index 0000000000..72e35b58f7 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/treetable/AbstractMutableTreeTableNode.java @@ -0,0 +1,282 @@ +/* + * $Id: AbstractMutableTreeTableNode.java 3780 2010-09-09 16:17:41Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.treetable; + +import javax.swing.tree.TreeNode; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; + +/** + * {@code AbstractMutableTreeTableNode} provides an implementation of most of + * the {@code MutableTreeTableNode} features. + * + * @author Karl Schaefer + */ +public abstract class AbstractMutableTreeTableNode implements + MutableTreeTableNode { + /** this node's parent, or null if this node has no parent */ + protected MutableTreeTableNode parent; + + /** + * List of children, if this node has no children the list will be empty. + * This list will never be null. + */ + protected final List children; + + /** optional user object */ + protected transient Object userObject; + + protected boolean allowsChildren; + + public AbstractMutableTreeTableNode() { + this(null); + } + + public AbstractMutableTreeTableNode(Object userObject) { + this(userObject, true); + } + + public AbstractMutableTreeTableNode(Object userObject, + boolean allowsChildren) { + this.userObject = userObject; + this.allowsChildren = allowsChildren; + children = createChildrenList(); + } + + /** + * Creates the list used to manage the children of this node. + *

                + * This method is called by the constructor. + * + * @return a list; this list is guaranteed to be non-{@code null} + */ + protected List createChildrenList() { + return new ArrayList(); + } + + public void add(MutableTreeTableNode child) { + insert(child, getChildCount()); + } + + /** + * {@inheritDoc} + */ + @Override + public void insert(MutableTreeTableNode child, int index) { + if (!allowsChildren) { + throw new IllegalStateException("this node cannot accept children"); + } + + if (children.contains(child)) { + children.remove(child); + index--; + } + + children.add(index, child); + + if (child.getParent() != this) { + child.setParent(this); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void remove(int index) { + children.remove(index).setParent(null); + } + + /** + * {@inheritDoc} + */ + @Override + public void remove(MutableTreeTableNode node) { + children.remove(node); + node.setParent(null); + } + + /** + * {@inheritDoc} + */ + @Override + public void removeFromParent() { + parent.remove(this); + } + + /** + * {@inheritDoc} + */ + @Override + public void setParent(MutableTreeTableNode newParent) { + if (newParent == null || newParent.getAllowsChildren()) { + if (parent != null && parent.getIndex(this) != -1) { + parent.remove(this); + } + } else { + throw new IllegalArgumentException( + "newParent does not allow children"); + } + + parent = newParent; + + if (parent != null && parent.getIndex(this) == -1) { + parent.insert(this, parent.getChildCount()); + } + } + + /** + * Returns this node's user object. + * + * @return the Object stored at this node by the user + * @see #setUserObject + * @see #toString + */ + @Override + public Object getUserObject() { + return userObject; + } + + /** + * {@inheritDoc} + */ + @Override + public void setUserObject(Object object) { + userObject = object; + } + + /** + * {@inheritDoc} + */ + @Override + public TreeTableNode getChildAt(int childIndex) { + return children.get(childIndex); + } + + /** + * {@inheritDoc} + */ + @Override + public int getIndex(TreeNode node) { + return children.indexOf(node); + } + + /** + * {@inheritDoc} + */ + @Override + public TreeTableNode getParent() { + return parent; + } + + /** + * {@inheritDoc} + */ + @Override + public Enumeration children() { + return Collections.enumeration(children); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean getAllowsChildren() { + return allowsChildren; + } + + /** + * Determines whether or not this node is allowed to have children. If + * {@code allowsChildren} is {@code false}, all of this node's children are + * removed. + *

                + * Note: By default, a node allows children. + * + * @param allowsChildren + * {@code true} if this node is allowed to have children + */ + public void setAllowsChildren(boolean allowsChildren) { + this.allowsChildren = allowsChildren; + + if (!this.allowsChildren) { + children.clear(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public int getChildCount() { + return children.size(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isLeaf() { + return getChildCount() == 0; + } + + /** + * Determines whether the specified column is editable. + * + * @param column + * the column to query + * @return always returns {@code false} + */ + @Override + public boolean isEditable(int column) { + return false; + } + + /** + * Sets the value for the given {@code column}. + * + * @impl does nothing. It is provided for convenience. + * @param aValue + * the value to set + * @param column + * the column to set the value on + */ + @Override + public void setValueAt(Object aValue, int column) { + // does nothing + } + + /** + * Returns the result of sending toString() to this node's + * user object, or null if this node has no user object. + * + * @see #getUserObject + */ + @Override + public String toString() { + if (userObject == null) { + return ""; + } else { + return userObject.toString(); + } + } +} diff --git a/src/main/java/org/jdesktop/swingx/treetable/AbstractTreeTableModel.java b/src/main/java/org/jdesktop/swingx/treetable/AbstractTreeTableModel.java new file mode 100644 index 0000000000..aa5a0cc821 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/treetable/AbstractTreeTableModel.java @@ -0,0 +1,214 @@ +/* + * $Id: AbstractTreeTableModel.java 3780 2010-09-09 16:17:41Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.treetable; + +import org.jdesktop.swingx.tree.TreeModelSupport; + +import javax.swing.event.TreeModelListener; +import javax.swing.tree.TreePath; + +// There is no javax.swing.tree.AbstractTreeModel; There ought to be one. + +/** + * AbstractTreeTableModel provides an implementation of + * {@link TreeTableModel} as a convenient starting + * point in defining custom data models for + * {@link org.jdesktop.swingx.JXTreeTable}. It takes care of listener + * management and contains convenience methods for creating and dispatching + * {@code TreeModelEvent}s. To create a concreate instance of + * {@code TreeTableModel} you need only to provide implementations for the + * following methods: + * + *

                + * public int getColumnCount();
                + * public Object getValueAt(Object node, int column);
                + * public Object getChild(Object parent, int index);
                + * public int getChildCount(Object parent);
                + * public int getIndexOfChild(Object parent, Object child);
                + * public boolean isLeaf(Object node);
                + * 
                + * + * @author Ramesh Gupta + * @author Karl Schaefer + */ +public abstract class AbstractTreeTableModel implements TreeTableModel { + + /** + * Root node of the model + */ + protected Object root; + + /** + * Provides support for event dispatching. + */ + protected TreeModelSupport modelSupport; + + /** + * Constructs an {@code AbstractTreeTableModel} with a {@code null} root + * node. + */ + public AbstractTreeTableModel() { + this(null); + } + + /** + * Constructs an {@code AbstractTreeTableModel} with the specified root + * node. + * + * @param root + * root node + */ + public AbstractTreeTableModel(Object root) { + this.root = root; + this.modelSupport = new TreeModelSupport(this); + } + + /** + * {@inheritDoc} + */ + @Override + public Class getColumnClass(int column) { + return Object.class; + } + + /** + * {@inheritDoc} + */ + @Override + public String getColumnName(int column) { + //Copied from AbstractTableModel. + //Should use same defaults when possible. + String result = ""; + + for (; column >= 0; column = column / 26 - 1) { + result = (char) ((char) (column % 26) + 'A') + result; + } + + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public int getHierarchicalColumn() { + if (getColumnCount() == 0) { + return -1; + } + + return 0; + } + + /** + * {@inheritDoc} + */ + @Override + public Object getRoot() { + return root; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isCellEditable(Object node, int column) { + // RG: Fix Issue 49 -- Cell not editable, by default. + // Subclasses might override this to return true. + return false; + } + + /** + * Returns true if node is a leaf. + * + * @impl {@code true} if {@code getChildCount(node) == 0} + * @param node a node in the tree, obtained from this data source + * @return true if node is a leaf + */ + @Override + public boolean isLeaf(Object node) { + return getChildCount(node) == 0; + } + + /** + * Sets the value for the {@code node} at {@code columnIndex} to + * {@code value}. + * + * @impl is no-op; provided for convenience for uneditable models + * @param value + * the new value + * @param node + * the node whose value is to be changed + * @param column + * the column whose value is to be changed + * @see #getValueAt + * @see #isCellEditable + * @see javax.swing.table.TableModel#setValueAt(Object, int, int) + */ + @Override + public void setValueAt(Object value, Object node, int column) { + //does nothing + } + + /** + * Called when value for the item identified by path has been changed. If + * newValue signifies a truly new value the model should post a + * {@code treeNodesChanged} event. + *

                + * + * @impl is no-op. A {@code JXTreeTable} does not usually edit the node directly. + * @param path + * path to the node that has changed + * @param newValue + * the new value from the TreeCellEditor + */ + @Override + public void valueForPathChanged(TreePath path, Object newValue) { + //does nothing + } + + /** + * {@inheritDoc} + */ + @Override + public void addTreeModelListener(TreeModelListener l) { + modelSupport.addTreeModelListener(l); + } + + /** + * {@inheritDoc} + */ + @Override + public void removeTreeModelListener(TreeModelListener l) { + modelSupport.removeTreeModelListener(l); + } + + /** + * Returns an array of all the TreeModelListeners added + * to this JXTreeTable with addTreeModelListener(). + * + * @return all of the TreeModelListeners added or an empty + * array if no listeners have been added + */ + public TreeModelListener[] getTreeModelListeners() { + return modelSupport.getTreeModelListeners(); + } +} diff --git a/src/main/java/org/jdesktop/swingx/treetable/DefaultMutableTreeTableNode.java b/src/main/java/org/jdesktop/swingx/treetable/DefaultMutableTreeTableNode.java new file mode 100644 index 0000000000..2eefb6c910 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/treetable/DefaultMutableTreeTableNode.java @@ -0,0 +1,88 @@ +/* + * $Id: DefaultMutableTreeTableNode.java 3780 2010-09-09 16:17:41Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.treetable; + +/** + * A default implementation of an {@code AbstractMutableTreeTableNode} that + * returns {@code getUserObject().toString()} for all value queries. This + * implementation is designed mainly for testing. It is NOT recommended to use + * this implementation. Any user that needs to create {@code TreeTableNode}s + * should consider directly extending {@code AbstractMutableTreeTableNode} or + * directly implementing the interface. + * + * @author Karl Schaefer + */ +public class DefaultMutableTreeTableNode extends AbstractMutableTreeTableNode { + + /** + * + */ + public DefaultMutableTreeTableNode() { + super(); + } + + /** + * @param userObject + */ + public DefaultMutableTreeTableNode(Object userObject) { + super(userObject); + } + + /** + * @param userObject + * @param allowsChildren + */ + public DefaultMutableTreeTableNode(Object userObject, boolean allowsChildren) { + super(userObject, allowsChildren); + } + + /** + * {@inheritDoc} + */ + @Override + public Object getValueAt(int column) { + return getUserObject(); + } + + /** + * {@inheritDoc} + */ + @Override + public int getColumnCount() { + return 1; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isEditable(int column) { + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public void setValueAt(Object aValue, int column) { + setUserObject(aValue); + } +} diff --git a/src/main/java/org/jdesktop/swingx/treetable/DefaultTreeTableModel.java b/src/main/java/org/jdesktop/swingx/treetable/DefaultTreeTableModel.java new file mode 100644 index 0000000000..fe75a7c611 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/treetable/DefaultTreeTableModel.java @@ -0,0 +1,453 @@ +/* + * $Id: DefaultTreeTableModel.java 3780 2010-09-09 16:17:41Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.treetable; + +import javax.swing.tree.TreePath; +import java.util.ArrayList; +import java.util.List; + +/** + * {@code DefaultTreeTableModel} is a concrete implementation of + * {@code AbstractTreeTableModel} and is provided purely as a convenience for + * use with {@code TreeTableNode}s. Applications that use {@code JXTreeTable} + * without {@code TreeTableNode}s are expected to provide their own + * implementation of a {@code TreeTableModel}. + *

                + * The {@code DefaultTreeTableModel} is designed to be used with + * {@code TreeTableNode}s. Specifically, users should extend + * {@code AbstractMutableTreeTableNode} to provide custom implementations for + * data display. + *

                + * Users who do not provide a list of column identifiers must provide a root + * that contains at least one column. Without specified identifiers the model + * will attempt to calculate the columns required for display by querying the + * root node. Normally, the root node can be little more than a shell (in + * displays that hide it), but without identifiers, the model relies on the root + * node metadata for display. + * + * @author Ramesh Gupta + * @author Karl Schaefer + */ +public class DefaultTreeTableModel extends AbstractTreeTableModel { + /** The List of column identifiers. */ + protected List columnIdentifiers; + + private boolean useAutoCalculatedIdentifiers; + + /** + * Creates a new {@code DefaultTreeTableModel} with a {@code null} root. + */ + public DefaultTreeTableModel() { + this(null); + } + + /** + * Creates a new {@code DefaultTreeTableModel} with the specified + * {@code root}. + * + * @param root + * the root node of the tree + */ + public DefaultTreeTableModel(TreeTableNode root) { + this(root, null); + } + + /** + * Creates a new {@code DefaultTreeTableModel} with the specified {@code + * root} and column names. + * + * @param root + * the root node of the tree + * @param columnNames + * the names of the columns used by this model + * @see #setColumnIdentifiers(List) + */ + public DefaultTreeTableModel(TreeTableNode root, List columnNames) { + super(root); + + setColumnIdentifiers(columnNames); + } + + private boolean isValidTreeTableNode(Object node) { + boolean result = false; + + if (node instanceof TreeTableNode) { + TreeTableNode ttn = (TreeTableNode) node; + + while (!result && ttn != null) { + result = ttn == root; + + ttn = ttn.getParent(); + } + } + + return result; + } + + /** + * Replaces the column identifiers in the model. If the number of + * newIdentifiers is greater than the current number of + * columns, new columns are added to the end of each row in the model. If + * the number of newIdentifiers is less than the current + * number of columns, all the extra columns at the end of a row are + * discarded. + *

                + * + * @param columnIdentifiers + * vector of column identifiers. If null, set the + * model to zero columns + */ + // from DefaultTableModel + public void setColumnIdentifiers(List columnIdentifiers) { + useAutoCalculatedIdentifiers = columnIdentifiers == null; + + this.columnIdentifiers = useAutoCalculatedIdentifiers + ? getAutoCalculatedIdentifiers(getRoot()) + : columnIdentifiers; + + modelSupport.fireNewRoot(); + } + + private static List getAutoCalculatedIdentifiers( + TreeTableNode exemplar) { + List autoCalculatedIndentifiers = new ArrayList(); + + if (exemplar != null) { + for (int i = 0, len = exemplar.getColumnCount(); i < len; i++) { + // forces getColumnName to use super.getColumnName + autoCalculatedIndentifiers.add(null); + } + } + + return autoCalculatedIndentifiers; + } + + /** + * Returns the root of the tree. Returns {@code null} only if the tree has + * no nodes. + * + * @return the root of the tree + * + * @throws ClassCastException + * if {@code root} is not a {@code TreeTableNode}. Even though + * subclasses have direct access to {@code root}, they should + * avoid accessing it directly. + * @see AbstractTreeTableModel#root + * @see #setRoot(TreeTableNode) + */ + @Override + public TreeTableNode getRoot() { + return (TreeTableNode) root; + } + + /** + * Gets the value for the {@code node} at {@code column}. + * + * @impl delegates to {@code TreeTableNode.getValueAt(int)} + * @param node + * the node whose value is to be queried + * @param column + * the column whose value is to be queried + * @return the value Object at the specified cell + * @throws IllegalArgumentException + * if {@code node} is not an instance of {@code TreeTableNode} + * or is not managed by this model, or {@code column} is not a + * valid column index + */ + @Override + public Object getValueAt(Object node, int column) { + if (!isValidTreeTableNode(node)) { + throw new IllegalArgumentException( + "node must be a valid node managed by this model"); + } + + if (column < 0 || column >= getColumnCount()) { + throw new IllegalArgumentException("column must be a valid index"); + } + + TreeTableNode ttn = (TreeTableNode) node; + + if (column >= ttn.getColumnCount()) { + return null; + } + + return ttn.getValueAt(column); + } + + /** + * {@inheritDoc} + */ + @Override + public void setValueAt(Object value, Object node, int column) { + if (!isValidTreeTableNode(node)) { + throw new IllegalArgumentException( + "node must be a valid node managed by this model"); + } + + if (column < 0 || column >= getColumnCount()) { + throw new IllegalArgumentException("column must be a valid index"); + } + + TreeTableNode ttn = (TreeTableNode) node; + + if (column < ttn.getColumnCount()) { + ttn.setValueAt(value, column); + + modelSupport.firePathChanged(new TreePath(getPathToRoot(ttn))); + } + } + + /** + * {@inheritDoc} + */ + @Override + public int getColumnCount() { + return columnIdentifiers.size(); + } + + /** + * {@inheritDoc} + */ + // Can we make getColumnClass final and avoid the complex DTM copy? -- kgs + @Override + public String getColumnName(int column) { + // Copied from DefaultTableModel. + Object id = null; + + // This test is to cover the case when + // getColumnCount has been subclassed by mistake ... + if (column < columnIdentifiers.size() && (column >= 0)) { + id = columnIdentifiers.get(column); + } + + return (id == null) ? super.getColumnName(column) : id.toString(); + } + + /** + * {@inheritDoc} + */ + @Override + public Object getChild(Object parent, int index) { + if (!isValidTreeTableNode(parent)) { + throw new IllegalArgumentException( + "parent must be a TreeTableNode managed by this model"); + } + + return ((TreeTableNode) parent).getChildAt(index); + } + + /** + * {@inheritDoc} + */ + @Override + public int getChildCount(Object parent) { + if (!isValidTreeTableNode(parent)) { + throw new IllegalArgumentException( + "parent must be a TreeTableNode managed by this model"); + } + + return ((TreeTableNode) parent).getChildCount(); + } + + /** + * {@inheritDoc} + */ + @Override + public int getIndexOfChild(Object parent, Object child) { + if (!isValidTreeTableNode(parent) || !isValidTreeTableNode(child)) { + return -1; + } + + return ((TreeTableNode) parent).getIndex((TreeTableNode) child); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isCellEditable(Object node, int column) { + if (!isValidTreeTableNode(node)) { + throw new IllegalArgumentException( + "node must be a valid node managed by this model"); + } + + if (column < 0 || column >= getColumnCount()) { + throw new IllegalArgumentException("column must be a valid index"); + } + + TreeTableNode ttn = (TreeTableNode) node; + + if (column >= ttn.getColumnCount()) { + return false; + } + + return ttn.isEditable(column); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isLeaf(Object node) { + if (!isValidTreeTableNode(node)) { + throw new IllegalArgumentException( + "node must be a TreeTableNode managed by this model"); + } + + return ((TreeTableNode) node).isLeaf(); + } + + /** + * Gets the path from the root to the specified node. + * + * @param aNode + * the node to query + * @return an array of {@code TreeTableNode}s, where + * {@code arr[0].equals(getRoot())} and + * {@code arr[arr.length - 1].equals(aNode)}, or an empty array if + * the node is not found. + * @throws NullPointerException + * if {@code aNode} is {@code null} + */ + public TreeTableNode[] getPathToRoot(TreeTableNode aNode) { + List path = new ArrayList(); + TreeTableNode node = aNode; + + while (node != root) { + path.add(0, node); + + node = node.getParent(); + } + + if (node == root) { + path.add(0, node); + } + + return path.toArray(new TreeTableNode[0]); + } + + /** + * Sets the root for this table model. If no column identifiers have been + * specified, this will rebuild the identifier list, using {@code root} as + * an examplar of the table. + * + * @param root + * the node to set as root + */ + public void setRoot(TreeTableNode root) { + this.root = root; + + if (useAutoCalculatedIdentifiers) { + // rebuild the list + //this already fires an event don't duplicate + setColumnIdentifiers(null); + } else { + modelSupport.fireNewRoot(); + } + } + + /** + * Invoked this to insert newChild at location index in parents children. + * This will then message nodesWereInserted to create the appropriate event. + * This is the preferred way to add children as it will create the + * appropriate event. + */ + public void insertNodeInto(MutableTreeTableNode newChild, + MutableTreeTableNode parent, int index) { + parent.insert(newChild, index); + + modelSupport.fireChildAdded(new TreePath(getPathToRoot(parent)), index, + newChild); + } + + /** + * Message this to remove node from its parent. This will message + * nodesWereRemoved to create the appropriate event. This is the preferred + * way to remove a node as it handles the event creation for you. + */ + public void removeNodeFromParent(MutableTreeTableNode node) { + MutableTreeTableNode parent = (MutableTreeTableNode) node.getParent(); + + if (parent == null) { + throw new IllegalArgumentException("node does not have a parent."); + } + + int index = parent.getIndex(node); + node.removeFromParent(); + + modelSupport.fireChildRemoved(new TreePath(getPathToRoot(parent)), + index, node); + } + + /** + * Called when value for the item identified by path has been changed. If + * newValue signifies a truly new value the model should post a {@code + * treeNodesChanged} event. + *

                + * This changes the object backing the {@code TreeTableNode} described by + * the path. This change does not alter a nodes children in any way. If you + * need to change structure of the node, use one of the provided mutator + * methods. + * + * @param path + * path to the node that has changed + * @param newValue + * the new value + * @throws NullPointerException + * if {@code path} is {@code null} + * @throws IllegalArgumentException + * if {@code path} is not a path managed by this model + * @throws ClassCastException + * if {@code path.getLastPathComponent()} is not a {@code + * TreeTableNode} + */ + @Override + public void valueForPathChanged(TreePath path, Object newValue) { + if (path.getPathComponent(0) != root) { + throw new IllegalArgumentException("invalid path"); + } + + TreeTableNode node = (TreeTableNode) path.getLastPathComponent(); + node.setUserObject(newValue); + + modelSupport.firePathChanged(path); + } + + /** + * Sets the user object for a node. Client code must use this method, so + * that the model can notify listeners that a change has occurred. + *

                + * This method is a convenient cover for + * {@link #valueForPathChanged(TreePath, Object)}. + * + * @param node + * the node to modify + * @param userObject + * the new user object to set + * @throws NullPointerException + * if {@code node} is {@code null} + * @throws IllegalArgumentException + * if {@code node} is not a node managed by this model + */ + public void setUserObject(TreeTableNode node, Object userObject) { + valueForPathChanged(new TreePath(getPathToRoot(node)), userObject); + } +} diff --git a/src/main/java/org/jdesktop/swingx/treetable/FileSystemModel.java b/src/main/java/org/jdesktop/swingx/treetable/FileSystemModel.java new file mode 100644 index 0000000000..ab191d601d --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/treetable/FileSystemModel.java @@ -0,0 +1,234 @@ +/* + * $Id: FileSystemModel.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.treetable; + +import java.io.File; +import java.util.Arrays; +import java.util.Date; + +/** + * A tree table model to simulate a file system. + *

                + * This tree table model implementation extends {@code AbstractTreeTableModel}. + * The file system metaphor demonstrates that it is often easier to directly + * implement tree structures directly instead of using intermediaries, such as + * {@code TreeTableNode}. + *

                + * A comparison of this class with {@code SimpleFileSystemModel}, shows that + * extending {@code AbstractTreeTableModel} is often easier than creating a model + * from scratch. + *

                + * A "full" version of this model might allow editing of file names, the + * deletion of files, and the movement of files. This simple implementation does + * not intend to tackle such problems, but this implementation may be extended + * to handle such details. + * + * @author Ramesh Gupta + * @author Karl Schaefer + */ +public class FileSystemModel extends AbstractTreeTableModel { + // The the returned file length for directories. + private static final Long DIRECTORY = 0L; + + /** + * Creates a file system model using the root directory as the model root. + */ + public FileSystemModel() { + this(new File(File.separator)); + } + + /** + * Creates a file system model using the specified {@code root}. + * + * @param root + * the root for this model; this may be different than the root + * directory for a file system. + */ + public FileSystemModel(File root) { + super(root); + } + + private boolean isValidFileNode(Object file) { + boolean result = false; + + if (file instanceof File) { + File f = (File) file; + + while (!result && f != null) { + result = f.equals(root); + + f = f.getParentFile(); + } + } + + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public File getChild(Object parent, int index) { + if (!isValidFileNode(parent)) { + throw new IllegalArgumentException("parent is not a file governed by this model"); + } + + File parentFile = (File) parent; + String[] children = parentFile.list(); + + if (children != null) { + return new File(parentFile, children[index]); + } + + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public int getChildCount(Object parent) { + if (parent instanceof File) { + String[] children = ((File) parent).list(); + + if (children != null) { + return children.length; + } + } + + return 0; + } + + /** + * {@inheritDoc} + */ + @Override + public Class getColumnClass(int column) { + switch (column) { + case 0: + return String.class; + case 1: + return Long.class; + case 2: + return Boolean.class; + case 3: + return Date.class; + default: + return super.getColumnClass(column); + } + } + + @Override + public int getColumnCount() { + return 4; + } + + @Override + public String getColumnName(int column) { + switch (column) { + case 0: + return "Name"; + case 1: + return "Size"; + case 2: + return "Directory"; + case 3: + return "Modification Date"; + default: + return super.getColumnName(column); + } + } + + @Override + public Object getValueAt(Object node, int column) { + if (node instanceof File) { + File file = (File) node; + switch (column) { + case 0: + return file.getName(); + case 1: + return isLeaf(node) ? file.length() : DIRECTORY; + case 2: + return file.isDirectory(); + case 3: + return new Date(file.lastModified()); + } + } + + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public int getIndexOfChild(Object parent, Object child) { + if (parent instanceof File && child instanceof File) { + File parentFile = (File) parent; + File[] files = parentFile.listFiles(); + + Arrays.sort(files); + + for (int i = 0, len = files.length; i < len; i++) { + if (files[i].equals(child)) { + return i; + } + } + } + + return -1; + } + + /** + * {@inheritDoc} + */ + @Override + public File getRoot() { + return (File) root; + } + + /** + * Sets the root for this tree table model. This method will notify + * listeners that a change has taken place. + * + * @param root + * the new root node to set + */ + public void setRoot(File root) { + this.root = root; + + modelSupport.fireNewRoot(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isLeaf(Object node) { + if (node instanceof File) { + //do not use isFile(); some system files return false + return ((File) node).list() == null; + } + + return true; + } +} diff --git a/src/main/java/org/jdesktop/swingx/treetable/MutableTreeTableNode.java b/src/main/java/org/jdesktop/swingx/treetable/MutableTreeTableNode.java new file mode 100644 index 0000000000..6fa36d13c1 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/treetable/MutableTreeTableNode.java @@ -0,0 +1,99 @@ +/* + * $Id: MutableTreeTableNode.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.treetable; + +import java.util.Enumeration; + +/** + * Defines the requirements for a tree table node object that can change -- by + * adding or removing child nodes, or by changing the contents of a user object + * stored in the node. + *

                + * Note this does not extend {@code MutableTreeNode} to minimize the contract + * breakage, cf. {@link TreeTableNode#getIndex(javax.swing.tree.TreeNode)}. + * + * @see javax.swing.tree.MutableTreeNode + * + * @author Karl Schaefer + */ +public interface MutableTreeTableNode extends TreeTableNode { + /** + * Returns an enumeration this node's children. + * + * @return an enumeration of {@code TreeTableNode}s + */ + @Override + Enumeration children(); + + /** + * Adds the {@code child} to this node at the specified {@code index}. This + * method calls {@code setParent} on {@code child} with {@code this} as the + * parameter. + * + * @param child + * the node to add as a child + * @param index + * the index of the child + * @throws IndexOutOfBoundsException + * if {@code index} is not a valid index + */ + void insert(MutableTreeTableNode child, int index); + + /** + * Removes the child node at the specified {@code index} from this node. + * This method calls {@code setParent} on {@code child} with a {@code null} + * parameter. + * + * @param index + * the index of the child + * @throws IndexOutOfBoundsException + * if {@code index} is not a valid index + */ + void remove(int index); + + /** + * Removes the specified child {@code node} from this node. + * This method calls {@code setParent} on {@code child} with a {@code null} + * parameter. + * + * @param node + * the index of the child + */ + void remove(MutableTreeTableNode node); + + /** + * Removes this node from it's parent. Most implementations will use + * {@code getParent().remove(this)}. + * + * @throws NullPointerException + * if {@code getParent()} returns {@code null} + */ + void removeFromParent(); + + /** + * Sets the parent of this node to {@code newParent}. This methods remove + * the node from its old parent. + * + * @param newParent + * the new parent for this node + */ + void setParent(MutableTreeTableNode newParent); +} diff --git a/src/main/java/org/jdesktop/swingx/treetable/SimpleFileSystemModel.java b/src/main/java/org/jdesktop/swingx/treetable/SimpleFileSystemModel.java new file mode 100644 index 0000000000..8cbdd187a1 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/treetable/SimpleFileSystemModel.java @@ -0,0 +1,272 @@ +/* + * $Id: SimpleFileSystemModel.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.treetable; + +import javax.swing.event.EventListenerList; +import javax.swing.event.TreeModelListener; +import javax.swing.tree.TreePath; +import java.io.File; +import java.util.Date; + +/** + * A tree table model to simulate a file system. + *

                + * This tree table model implementation does not extends + * {@code AbstractTreeTableModel}. The file system metaphor demonstrates that + * it is often easier to directly implement tree structures directly instead of + * using intermediaries, such as {@code TreeNode}. + *

                + * It would be possible to create this same class by extending + * {@code AbstractTreeTableModel}, however the number of methods that you would + * need to override almost precludes that means of implementation. + *

                + * A "full" version of this model might allow editing of file names, the + * deletion of files, and the movement of files. This simple implementation does + * not intend to tackle such problems, but this implementation may be extended + * to handle such details. + * + * @author Ramesh Gupta + * @author Karl Schaefer + */ +public class SimpleFileSystemModel implements TreeTableModel { + protected EventListenerList listenerList; + + // the returned file length for directories + private static final Long ZERO = Long.valueOf(0); + + private File root; + + /** + * Creates a file system model, using the root directory as the model root. + */ + public SimpleFileSystemModel() { + this(new File(File.separator)); + } + + /** + * Creates a file system model, using the specified {@code root} as the + * model root. + */ + public SimpleFileSystemModel(File root) { + this.root = root; + this.listenerList = new EventListenerList(); + } + + /** + * {@inheritDoc} + */ + @Override + public File getChild(Object parent, int index) { + if (parent instanceof File) { + File parentFile = (File) parent; + File[] files = parentFile.listFiles(); + + if (files != null) { + return files[index]; + } + } + + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public int getChildCount(Object parent) { + if (parent instanceof File) { + String[] children = ((File) parent).list(); + + if (children != null) { + return children.length; + } + } + + return 0; + } + + /** + * {@inheritDoc} + */ + @Override + public Class getColumnClass(int column) { + switch(column) { + case 0: + return String.class; + case 1: + return Long.class; + case 2: + return Boolean.class; + case 3: + return Date.class; + default: + return Object.class; + } + } + + /** + * {@inheritDoc} + */ + @Override + public int getColumnCount() { + return 4; + } + + /** + * {@inheritDoc} + */ + @Override + public String getColumnName(int column) { + switch (column) { + case 0: + return "Name"; + case 1: + return "Size"; + case 2: + return "Directory"; + case 3: + return "Modification Date"; + default: + return "Column " + column; + } + } + + /** + * {@inheritDoc} + */ + @Override + public Object getValueAt(Object node, int column) { + if (node instanceof File) { + File file = (File) node; + switch (column) { + case 0: + return file.getName(); + case 1: + return file.isFile() ? file.length() : ZERO; + case 2: + return file.isDirectory(); + case 3: + return new Date(file.lastModified()); + } + } + + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public int getHierarchicalColumn() { + return 0; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isCellEditable(Object node, int column) { + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public void setValueAt(Object value, Object node, int column) { + //does nothing + } + + /** + * {@inheritDoc} + */ + @Override + public void addTreeModelListener(TreeModelListener l) { + listenerList.add(TreeModelListener.class, l); + } + + /** + * {@inheritDoc} + */ + @Override + public int getIndexOfChild(Object parent, Object child) { + if (parent instanceof File && child instanceof File) { + File parentFile = (File) parent; + File[] files = parentFile.listFiles(); + + for (int i = 0, len = files.length; i < len; i++) { + if (files[i].equals(child)) { + return i; + } + } + } + + return -1; + } + + /** + * {@inheritDoc} + */ + @Override + public File getRoot() { + return root; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isLeaf(Object node) { + if (node instanceof File) { + //do not use isFile(); some system files return false + return ((File) node).list() == null; + } + + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public void removeTreeModelListener(TreeModelListener l) { + listenerList.remove(TreeModelListener.class, l); + } + + /** + * {@inheritDoc} + */ + @Override + public void valueForPathChanged(TreePath path, Object newValue) { + //does nothing + } + + /** + * Gets a an array of all the listeners attached to this model. + * + * @return an array of listeners; this array is guaranteed to be + * non-{@code null} + */ + public TreeModelListener[] getTreeModelListeners() { + return listenerList.getListeners(TreeModelListener.class); + } +} diff --git a/src/main/java/org/jdesktop/swingx/treetable/TreeTableCellEditor.java b/src/main/java/org/jdesktop/swingx/treetable/TreeTableCellEditor.java new file mode 100644 index 0000000000..f448f8ac57 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/treetable/TreeTableCellEditor.java @@ -0,0 +1,218 @@ +/* + * $Id: TreeTableCellEditor.java 3990 2011-03-31 13:41:08Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.treetable; + +import org.jdesktop.swingx.JXTable.GenericEditor; + +import javax.swing.*; +import javax.swing.tree.TreeCellRenderer; +import java.awt.*; +import java.awt.event.MouseEvent; +import java.util.EventObject; +import java.util.logging.Logger; + +/** + * An editor that can be used to edit the tree column. This extends + * DefaultCellEditor and uses a JTextField (actually, TreeTableTextField) + * to perform the actual editing. + *

                To support editing of the tree column we can not make the tree + * editable. The reason this doesn't work is that you can not use + * the same component for editing and rendering. The table may have + * the need to paint cells, while a cell is being edited. If the same + * component were used for the rendering and editing the component would + * be moved around, and the contents would change. When editing, this + * is undesirable, the contents of the text field must stay the same, + * including the caret blinking, and selections persisting. For this + * reason the editing is done via a TableCellEditor. + *

                Another interesting thing to be aware of is how tree positions + * its render and editor. The render/editor is responsible for drawing the + * icon indicating the type of node (leaf, branch...). The tree is + * responsible for drawing any other indicators, perhaps an additional + * +/- sign, or lines connecting the various nodes. So, the renderer + * is positioned based on depth. On the other hand, table always makes + * its editor fill the contents of the cell. To get the allusion + * that the table cell editor is part of the tree, we don't want the + * table cell editor to fill the cell bounds. We want it to be placed + * in the same manner as tree places it editor, and have table message + * the tree to paint any decorations the tree wants. Then, we would + * only have to worry about the editing part. The approach taken + * here is to determine where tree would place the editor, and to override + * the reshape method in the JTextField component to + * nudge the textfield to the location tree would place it. Since + * JXTreeTable will paint the tree behind the editor everything should + * just work. So, that is what we are doing here. Determining of + * the icon position will only work if the TreeCellRenderer is + * an instance of DefaultTreeCellRenderer. If you need custom + * TreeCellRenderers, that don't descend from DefaultTreeCellRenderer, + * and you want to support editing in JXTreeTable, you will have + * to do something similar. + * + * @author Scott Violet + * @author Ramesh Gupta + */ +public class TreeTableCellEditor extends GenericEditor { + //DefaultCellEditor { +// JW: changed to extends GenericEditor to fix #1365-swingx - +// borders different in hierarchical column vs. table column +// + @SuppressWarnings("unused") + private static final Logger LOG = Logger + .getLogger(TreeTableCellEditor.class.getName()); + + public TreeTableCellEditor(JTree tree) { + super(new TreeTableTextField()); + if (tree == null) { + throw new IllegalArgumentException("null tree"); + } + // JW: no need to... + this.tree = tree; // immutable + } + + /** + * Overriden to determine an offset that tree would place the editor at. The + * offset is determined from the getRowBounds JTree method, + * and additionaly from the icon DefaultTreeCellRenderer will use. + *

                + * The offset is then set on the TreeTableTextField component created in the + * constructor, and returned. + */ + @Override + public Component getTableCellEditorComponent(JTable table, Object value, + boolean isSelected, int row, int column) { + Component component = super.getTableCellEditorComponent(table, value, + isSelected, row, column); + // JW: this implementation is not bidi-compliant, need to do better + initEditorOffset(table, row, column, isSelected); + return component; + } + + + /** + * @param row + * @param isSelected + */ + protected void initEditorOffset(JTable table, int row, int column, + boolean isSelected) { + if (tree == null) + return; +// Rectangle bounds = tree.getRowBounds(row); +// int offset = bounds.x; + Object node = tree.getPathForRow(row).getLastPathComponent(); + boolean leaf = tree.getModel().isLeaf(node); + boolean expanded = tree.isExpanded(row); + TreeCellRenderer tcr = tree.getCellRenderer(); + Component editorComponent = tcr.getTreeCellRendererComponent(tree, node, + isSelected, expanded, leaf, row, false); + + ((TreeTableTextField) getComponent()).init(row, + column, table, tree, editorComponent); + } + + /** + * This is overriden to forward the event to the tree. This will + * return true if the click count >= clickCountToStart, or the event is null. + */ + @Override + public boolean isCellEditable(EventObject e) { + // JW: quick fix for #592-swingx - + // editing not started on keyEvent in hierarchical column (1.6) + if (e instanceof MouseEvent) { + return (((MouseEvent) e).getClickCount() >= clickCountToStart); + } + return true; + } + + /** + * Component used by TreeTableCellEditor. The only thing this does + * is to override the reshape method, and to ALWAYS + * make the x location be offset. + */ + static class TreeTableTextField extends JTextField { + private int iconWidth; + + void init(int row, int column, JTable table, JTree tree, Component editorComponent) { + this.column = column; + this.row = row; + this.table = table; + this.tree = tree; + updateIconWidth(editorComponent); + setComponentOrientation(table.getComponentOrientation()); + } + + /** + * @param treeComponent + */ + private void updateIconWidth(Component treeComponent) { + iconWidth = 0; + if (!(treeComponent instanceof JLabel)) return; + Icon icon = ((JLabel) treeComponent).getIcon(); + if (icon != null) { + iconWidth = icon.getIconWidth() + ((JLabel) treeComponent).getIconTextGap(); + } + + } + + private int column; + private int row; + private JTable table; + private JTree tree; + + /** + * {@inheritDoc}

                + * + * Overridden to place the textfield in the node content boundaries, + * leaving the icon to the renderer.

                + * + * PENDING JW: insets? + * + */ + @SuppressWarnings("deprecation") + @Override + public void reshape(int x, int y, int width, int height) { + // Allows precise positioning of text field in the tree cell. + // following three lines didn't work out + //Border border = this.getBorder(); // get this text field's border + //Insets insets = border == null ? null : border.getBorderInsets(this); + //int newOffset = offset - (insets == null ? 0 : insets.left); + + Rectangle cellRect = table.getCellRect(0, column, false); + Rectangle nodeRect = tree.getRowBounds(row); + nodeRect.width -= iconWidth; + if(table.getComponentOrientation().isLeftToRight()) { + int nodeStart = cellRect.x + nodeRect.x + iconWidth; + int nodeEnd = cellRect.x + cellRect.width; + super.reshape(nodeStart, y, nodeEnd - nodeStart, height); +// int newOffset = nodeLeftX - getInsets().left; +// super.reshape(x + newOffset, y, width - newOffset, height); + } else { + int nodeRightX = nodeRect.x + nodeRect.width; + nodeRect.x = 0; //Math.max(0, nodeRect.x); + // ignore the parameter + width = nodeRightX - nodeRect.x; + super.reshape(cellRect.x + nodeRect.x, y, width, height); + } + } + + } + + private final JTree tree; // immutable +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/treetable/TreeTableModel.java b/src/main/java/org/jdesktop/swingx/treetable/TreeTableModel.java new file mode 100644 index 0000000000..482960042d --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/treetable/TreeTableModel.java @@ -0,0 +1,137 @@ +/* + * $Id: TreeTableModel.java 2476 2007-11-25 15:52:59Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.treetable; + + +import javax.swing.tree.TreeModel; + +/** + * The model used by {@code JXTreeTable}. + *

                + * This model is a combination of {@link TreeModel} and + * {@link javax.swing.table.TableModel} for use with the tree table. It does not + * actually extends {@code TableModel}, but instead copies method signature as + * appropriate and alters other to work with the underlying {@code TreeModel}. + *

                + * + * @see TreeModel + * @see javax.swing.table.TableModel + */ +public interface TreeTableModel extends TreeModel { + /** + * Returns the most specific superclass for all the cell values in the + * column. This is used by the {@code JXTreeTable} to set up a default + * renderer and editor for the column. + * + * @param columnIndex + * the index of the column + * @return the common ancestor class of the object values in the model. + * @see javax.swing.table.TableModel#getColumnClass(int) + */ + public Class getColumnClass(int columnIndex); + + /** + * Returns the number of columns in the model. A {@code JXTreeTable} uses + * this method to determine how many columns it should create and display by + * default. + * + * @return the number of columns in the model + * @see javax.swing.table.TableModel#getColumnCount() + */ + public int getColumnCount(); + + /** + * Returns the name of the column at {@code columnIndex}. This is used to + * initialize the table's column header name. Note: this name does not need + * to be unique; two columns in a table can have the same name. + * + * @param column + * the index of the column + * @return the name of the column + * @see javax.swing.table.TableModel#getColumnName(int) + */ + public String getColumnName(int column); + + /** + * Returns the column that is the "tree" column. While it is not required, + * most implementations will default the first column to be the hierarchical + * one. + * + * @return the index of the hierarchical column or -1 if no column is the + * hierarchical column. + */ + public int getHierarchicalColumn(); + + /** + * Returns the value for the {@code node} at {@code columnIndex}. The + * {@code node} must be managed by this model. Unamanaged nodes should throw + * an {@code IllegalArgumentException}. + * + * @param node + * the node whose value is to be queried + * @param column + * the column whose value is to be queried + * @return the value Object at the specified cell + * @throws IllegalArgumentException + * if {@code node} is not managed by this model. + * @see #setValueAt + * @see javax.swing.table.TableModel#getValueAt(int, int) + */ + public Object getValueAt(Object node, int column); + + /** + * Returns true if the cell for the {@code node} at {@code columnIndex} is + * editable. Otherwise, {@code setValueAt} on the cell will not change the + * value of that cell. The {@code node} must be managed by this model. + * Unamanaged nodes should throw an {@code IllegalArgumentException}. + * + * @param node + * the node whose value to be queried + * @param column + * the column whose value to be queried + * @return true if the cell is editable + * @throws IllegalArgumentException + * if {@code node} is not managed by this model. + * @see #setValueAt + * @see javax.swing.table.TableModel#isCellEditable(int, int) + */ + public boolean isCellEditable(Object node, int column); + + /** + * Sets the value for the {@code node} at {@code columnIndex} to + * {@code value}. The {@code node} must be managed by this model. + * Unamanaged nodes should throw an {@code IllegalArgumentException}. + * + * + * @param value + * the new value + * @param node + * the node whose value is to be changed + * @param column + * the column whose value is to be changed + * @throws IllegalArgumentException + * if {@code node} is not managed by this model. + * @see #getValueAt + * @see #isCellEditable + * @see javax.swing.table.TableModel#setValueAt(Object, int, int) + */ + public void setValueAt(Object value, Object node, int column); +} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/treetable/TreeTableModelProvider.java b/src/main/java/org/jdesktop/swingx/treetable/TreeTableModelProvider.java new file mode 100644 index 0000000000..25ba8ffce9 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/treetable/TreeTableModelProvider.java @@ -0,0 +1,51 @@ +/* + * Created on 11.01.2011 + * + */ +package org.jdesktop.swingx.treetable; + + +/** + * Interface which guarantees access to a TreeTableModel. It is implemented by + * the internal TreeTableModelAdapter of JXTreeTable to allow direct access to + * the underlying TreeTableModel from the adapter. + *

                + * + * That's useful f.i. when trying to configure TableColumnExt in a + * ColumnFactory, like in + * + *

                + * 
                + * JXTreeTable table = new JXTreeTable();
                + * ColumnFactory factory = new ColumnFactory() {
                + * 
                + *     @Override
                + *     public void configureTableColumn(TableModel model,
                + *             TableColumnExt columnExt) {
                + *         super.configureTableColumn(model, columnExt);
                + *         if (model instanceof TreeTableModelProvider) {
                + *             TreeTableModel treeTableModel = ((TreeTableModelProvider) model).getTreeTableModel();
                + *             if (treeTableModel.getHierarchicalColumn() == columnExt.getModelIndex()) {
                + *                 columnExt.setTitle("Hierarchical: " + columnExt.getTitle());
                + *             }
                + *         }
                + *     }
                + * };
                + * table.setColumnFactory(factory);
                + * table.setTreeTableModel(new FileSystemModel());
                + * 
                + * 
                + * 
                + * + * @author Jeanette Winzenburg, Berlin + */ +public interface TreeTableModelProvider { + + /** + * Returns a TreeTableModel, guaranteed to be not null. + * + * @return a TreeTableModel, guaranteed to be not null. + */ + TreeTableModel getTreeTableModel(); + +} diff --git a/src/main/java/org/jdesktop/swingx/treetable/TreeTableNode.java b/src/main/java/org/jdesktop/swingx/treetable/TreeTableNode.java new file mode 100644 index 0000000000..f1c69a9dc9 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/treetable/TreeTableNode.java @@ -0,0 +1,116 @@ +/* + * $Id: TreeTableNode.java 3927 2011-02-22 16:34:11Z kleopatra $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.treetable; + +import javax.swing.tree.TreeNode; +import java.util.Enumeration; + +/** + * Defines the requirements for an object that can be used as a tree node in a + * {@code JXTreeTable}. + * + * @author Karl Schaefer + */ +public interface TreeTableNode extends TreeNode { + /** + * Returns an enumeration this node's children. + * + * @return an enumeration of {@code TreeTableNode}s + */ + @Override + Enumeration children(); + + /** + * Gets the value for this node that corresponds to a particular tabular + * column. + * + * @param column + * the column to query + * @return the value for the queried column + * @throws IndexOutOfBoundsException + * if {@code column} is not a valid column index + */ + Object getValueAt(int column); + + /** + * Overridden to specify the return type. Returns the child {@code TreeNode} + * at index {@code childIndex}. Models that utilize this node should verify + * the column count before querying this node, since nodes may return + * differing sizes even for the same model. + * + * @param childIndex + * the index of the child + * @return the {@code TreeTableNode} corresponding to the specified index + */ + @Override + TreeTableNode getChildAt(int childIndex); + + /** + * Returns the number of columns supported by this {@code TreeTableNode}. + * + * @return the number of columns this node supports + */ + int getColumnCount(); + + /** + * Overridden to specify the return type. Returns the parent + * {@code TreeTableNode} of the receiver. + * + * @return the parent {@code TreeTableNode} or {@code null} if this node has + * no parent (such nodes are usually root nodes). + */ + @Override + TreeTableNode getParent(); + + /** + * Determines whether the specified column is editable. + * + * @param column + * the column to query + * @return {@code true} if the column is editable, {@code false} otherwise + */ + boolean isEditable(int column); + + /** + * Sets the value for the given {@code column}. + * + * @param aValue + * the value to set + * @param column + * the column to set the value on + */ + void setValueAt(Object aValue, int column); + + /** + * Returns this node's user object. + * + * @return the Object stored at this node by the user + */ + Object getUserObject(); + + /** + * Sets the user object stored in this node. + * + * @param userObject + * the object to store + */ + void setUserObject(Object userObject); +} diff --git a/src/main/java/org/jdesktop/swingx/treetable/package-info.java b/src/main/java/org/jdesktop/swingx/treetable/package-info.java new file mode 100644 index 0000000000..5b2dcd423a --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/treetable/package-info.java @@ -0,0 +1,36 @@ +/* + * $Id: package-info.java 3167 2009-01-02 13:28:04Z rah003 $ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +/**Contains API required by the JXTreeTable component. +

                Package Specification

                + + + +

                Related Documentation

                + + + +*/ +package org.jdesktop.swingx.treetable; \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/util/Contract.java b/src/main/java/org/jdesktop/swingx/util/Contract.java new file mode 100644 index 0000000000..d56e7a1b42 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/util/Contract.java @@ -0,0 +1,70 @@ +/* + * $Id: Contract.java 4028 2011-06-03 19:32:19Z kschaefe $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.util; + + +/** + * Utility class for checking contracts. + * + * @author Jeanette Winzenburg + */ +public class Contract { + + private Contract() { + + } + + /** + * Tests the input parameter against null. If the input is + * an array, checks all of its elements as well. Returns the + * unchanged parameter if not null, throws a NullPointerException + * otherwise.

                + * + * PENDING: type of exception? there are raging debates, some + * favour an IllegalArgument?

                + * + * PENDING: the implementation uses a unchecked type cast to an array. + * can we do better, how? + * + * + * @param the type of the input parameter + * @param input the argument to check against null. + * @param message the text of the exception if the argument is null + * @return the input if not null + * @throws NullPointerException if input is null + */ + @SuppressWarnings("unchecked") + public static T asNotNull(T input, String message) { + if (input == null) + throw new NullPointerException(message); + + if (input.getClass().isArray()) { + if (!input.getClass().getComponentType().isPrimitive()) { + T[] array = (T[]) input; + for (int i = 0; i < array.length; i++) { + asNotNull(array[i], message); + } + } + } + + return input; + } +} diff --git a/src/main/java/org/jdesktop/swingx/util/GraphicsUtilities.java b/src/main/java/org/jdesktop/swingx/util/GraphicsUtilities.java new file mode 100644 index 0000000000..e068eb3f78 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/util/GraphicsUtilities.java @@ -0,0 +1,834 @@ +/* + * $Id: GraphicsUtilities.java 4193 2012-06-27 19:42:05Z kschaefe $ + * + * Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy). + * + * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * Copyright (c) 2006 Romain Guy + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jdesktop.swingx.util; + +import javax.imageio.ImageIO; +import javax.swing.*; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.Raster; +import java.awt.image.WritableRaster; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; + +/** + *

                GraphicsUtilities contains a set of tools to perform + * common graphics operations easily. These operations are divided into + * several themes, listed below.

                + * + *

                Compatible Images

                + * + *

                Compatible images can, and should, be used to increase drawing + * performance. This class provides a number of methods to load compatible + * images directly from files or to convert existing images to compatibles + * images.

                + * + *

                Creating Thumbnails

                + * + *

                This class provides a number of methods to easily scale down images. + * Some of these methods offer a trade-off between speed and result quality and + * should be used all the time. They also offer the advantage of producing + * compatible images, thus automatically resulting into better runtime + * performance.

                + * + *

                All these methods are both faster than + * {@link Image#getScaledInstance(int, int, int)} and produce + * better-looking results than the various drawImage() methods + * in {@link Graphics}, which can be used for image scaling.

                + * + *

                Image Manipulation

                + * + *

                This class provides two methods to get and set pixels in a buffered image. + * These methods try to avoid unmanaging the image in order to keep good + * performance.

                + * + * @author Romain Guy + * @author rbair + * @author Karl Schaefer + */ +@SuppressWarnings("nls") +public class GraphicsUtilities { + private GraphicsUtilities() { + } + + // Returns the graphics configuration for the primary screen + private static GraphicsConfiguration getGraphicsConfiguration() { + return GraphicsEnvironment.getLocalGraphicsEnvironment(). + getDefaultScreenDevice().getDefaultConfiguration(); + } + + private static boolean isHeadless() { + return GraphicsEnvironment.isHeadless(); + } + + /** + * Converts the specified image into a compatible buffered image. + * + * @param img + * the image to convert + * @return a compatible buffered image of the input + */ + public static BufferedImage convertToBufferedImage(Image img) { + BufferedImage buff = createCompatibleTranslucentImage( + img.getWidth(null), img.getHeight(null)); + Graphics2D g2 = buff.createGraphics(); + + try { + g2.drawImage(img, 0, 0, null); + } finally { + g2.dispose(); + } + + return buff; + } + + /** + *

                Returns a new BufferedImage using the same color model + * as the image passed as a parameter. The returned image is only compatible + * with the image passed as a parameter. This does not mean the returned + * image is compatible with the hardware.

                + * + * @param image the reference image from which the color model of the new + * image is obtained + * @return a new BufferedImage, compatible with the color model + * of image + */ + public static BufferedImage createColorModelCompatibleImage(BufferedImage image) { + ColorModel cm = image.getColorModel(); + return new BufferedImage(cm, + cm.createCompatibleWritableRaster(image.getWidth(), + image.getHeight()), + cm.isAlphaPremultiplied(), null); + } + + /** + *

                Returns a new compatible image with the same width, height and + * transparency as the image specified as a parameter. That is, the + * returned BufferedImage will be compatible with the graphics hardware. + * If this method is called in a headless environment, then + * the returned BufferedImage will be compatible with the source + * image.

                + * + * @see Transparency + * @see #createCompatibleImage(int, int) + * @see #createCompatibleImage(BufferedImage, int, int) + * @see #createCompatibleTranslucentImage(int, int) + * @see #loadCompatibleImage(URL) + * @see #toCompatibleImage(BufferedImage) + * @param image the reference image from which the dimension and the + * transparency of the new image are obtained + * @return a new compatible BufferedImage with the same + * dimension and transparency as image + */ + public static BufferedImage createCompatibleImage(BufferedImage image) { + return createCompatibleImage(image, image.getWidth(), image.getHeight()); + } + + /** + *

                Returns a new compatible image of the specified width and height, and + * the same transparency setting as the image specified as a parameter. + * That is, the returned BufferedImage is compatible with + * the graphics hardware. If the method is called in a headless + * environment, then the returned BufferedImage will be compatible with + * the source image.

                + * + * @see Transparency + * @see #createCompatibleImage(BufferedImage) + * @see #createCompatibleImage(int, int) + * @see #createCompatibleTranslucentImage(int, int) + * @see #loadCompatibleImage(URL) + * @see #toCompatibleImage(BufferedImage) + * @param width the width of the new image + * @param height the height of the new image + * @param image the reference image from which the transparency of the new + * image is obtained + * @return a new compatible BufferedImage with the same + * transparency as image and the specified dimension + */ + public static BufferedImage createCompatibleImage(BufferedImage image, + int width, int height) { + return isHeadless() ? + new BufferedImage(width, height, image.getType()) : + getGraphicsConfiguration().createCompatibleImage(width, height, + image.getTransparency()); + } + + /** + *

                Returns a new opaque compatible image of the specified width and + * height. That is, the returned BufferedImage is compatible with + * the graphics hardware. If the method is called in a headless + * environment, then the returned BufferedImage will be compatible with + * the source image.

                + * + * @see #createCompatibleImage(BufferedImage) + * @see #createCompatibleImage(BufferedImage, int, int) + * @see #createCompatibleTranslucentImage(int, int) + * @see #loadCompatibleImage(URL) + * @see #toCompatibleImage(BufferedImage) + * @param width the width of the new image + * @param height the height of the new image + * @return a new opaque compatible BufferedImage of the + * specified width and height + */ + public static BufferedImage createCompatibleImage(int width, int height) { + return isHeadless() ? + new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB) : + getGraphicsConfiguration().createCompatibleImage(width, height); + } + + /** + *

                Returns a new translucent compatible image of the specified width and + * height. That is, the returned BufferedImage is compatible with + * the graphics hardware. If the method is called in a headless + * environment, then the returned BufferedImage will be compatible with + * the source image.

                + * + * @see #createCompatibleImage(BufferedImage) + * @see #createCompatibleImage(BufferedImage, int, int) + * @see #createCompatibleImage(int, int) + * @see #loadCompatibleImage(URL) + * @see #toCompatibleImage(BufferedImage) + * @param width the width of the new image + * @param height the height of the new image + * @return a new translucent compatible BufferedImage of the + * specified width and height + */ + public static BufferedImage createCompatibleTranslucentImage(int width, + int height) { + return isHeadless() ? + new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB) : + getGraphicsConfiguration().createCompatibleImage(width, height, + Transparency.TRANSLUCENT); + } + + /** + *

                + * Returns a new compatible image from a stream. The image is loaded from + * the specified stream and then turned, if necessary into a compatible + * image. + *

                + * + * @see #createCompatibleImage(BufferedImage) + * @see #createCompatibleImage(BufferedImage, int, int) + * @see #createCompatibleImage(int, int) + * @see #createCompatibleTranslucentImage(int, int) + * @see #toCompatibleImage(BufferedImage) + * @param in + * the stream of the picture to load as a compatible image + * @return a new translucent compatible BufferedImage of the + * specified width and height + * @throws IOException + * if the image cannot be read or loaded + */ + public static BufferedImage loadCompatibleImage(InputStream in) throws IOException { + BufferedImage image = ImageIO.read(in); + if(image == null) return null; + return toCompatibleImage(image); + } + + /** + *

                Returns a new compatible image from a URL. The image is loaded from the + * specified location and then turned, if necessary into a compatible + * image.

                + * + * @see #createCompatibleImage(BufferedImage) + * @see #createCompatibleImage(BufferedImage, int, int) + * @see #createCompatibleImage(int, int) + * @see #createCompatibleTranslucentImage(int, int) + * @see #toCompatibleImage(BufferedImage) + * @param resource the URL of the picture to load as a compatible image + * @return a new translucent compatible BufferedImage of the + * specified width and height + * @throws IOException if the image cannot be read or loaded + */ + public static BufferedImage loadCompatibleImage(URL resource) + throws IOException { + BufferedImage image = ImageIO.read(resource); + return toCompatibleImage(image); + } + + /** + *

                Return a new compatible image that contains a copy of the specified + * image. This method ensures an image is compatible with the hardware, + * and therefore optimized for fast blitting operations.

                + * + *

                If the method is called in a headless environment, then the returned + * BufferedImage will be the source image.

                + * + * @see #createCompatibleImage(BufferedImage) + * @see #createCompatibleImage(BufferedImage, int, int) + * @see #createCompatibleImage(int, int) + * @see #createCompatibleTranslucentImage(int, int) + * @see #loadCompatibleImage(URL) + * @param image the image to copy into a new compatible image + * @return a new compatible copy, with the + * same width and height and transparency and content, of image + */ + public static BufferedImage toCompatibleImage(BufferedImage image) { + if (isHeadless()) { + return image; + } + + if (image.getColorModel().equals( + getGraphicsConfiguration().getColorModel())) { + return image; + } + + BufferedImage compatibleImage = + getGraphicsConfiguration().createCompatibleImage( + image.getWidth(), image.getHeight(), + image.getTransparency()); + Graphics g = compatibleImage.getGraphics(); + + try { + g.drawImage(image, 0, 0, null); + } finally { + g.dispose(); + } + + return compatibleImage; + } + + /** + *

                Returns a thumbnail of a source image. newSize defines + * the length of the longest dimension of the thumbnail. The other + * dimension is then computed according to the dimensions ratio of the + * original picture.

                + *

                This method favors speed over quality. When the new size is less than + * half the longest dimension of the source image, + * {@link #createThumbnail(BufferedImage, int)} or + * {@link #createThumbnail(BufferedImage, int, int)} should be used instead + * to ensure the quality of the result without sacrificing too much + * performance.

                + * + * @see #createThumbnailFast(BufferedImage, int, int) + * @see #createThumbnail(BufferedImage, int) + * @see #createThumbnail(BufferedImage, int, int) + * @param image the source image + * @param newSize the length of the largest dimension of the thumbnail + * @return a new compatible BufferedImage containing a + * thumbnail of image + * @throws IllegalArgumentException if newSize is larger than + * the largest dimension of image or <= 0 + */ + public static BufferedImage createThumbnailFast(BufferedImage image, + int newSize) { + float ratio; + int width = image.getWidth(); + int height = image.getHeight(); + + if (width > height) { + if (newSize >= width) { + throw new IllegalArgumentException("newSize must be lower than" + + " the image width"); + } else if (newSize <= 0) { + throw new IllegalArgumentException("newSize must" + + " be greater than 0"); + } + + ratio = (float) width / (float) height; + width = newSize; + height = (int) (newSize / ratio); + } else { + if (newSize >= height) { + throw new IllegalArgumentException("newSize must be lower than" + + " the image height"); + } else if (newSize <= 0) { + throw new IllegalArgumentException("newSize must" + + " be greater than 0"); + } + + ratio = (float) height / (float) width; + height = newSize; + width = (int) (newSize / ratio); + } + + BufferedImage temp = createCompatibleImage(image, width, height); + Graphics2D g2 = temp.createGraphics(); + + try { + g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g2.drawImage(image, 0, 0, temp.getWidth(), temp.getHeight(), null); + } finally { + g2.dispose(); + } + + return temp; + } + + /** + *

                Returns a thumbnail of a source image.

                + *

                This method favors speed over quality. When the new size is less than + * half the longest dimension of the source image, + * {@link #createThumbnail(BufferedImage, int)} or + * {@link #createThumbnail(BufferedImage, int, int)} should be used instead + * to ensure the quality of the result without sacrificing too much + * performance.

                + * + * @see #createThumbnailFast(BufferedImage, int) + * @see #createThumbnail(BufferedImage, int) + * @see #createThumbnail(BufferedImage, int, int) + * @param image the source image + * @param newWidth the width of the thumbnail + * @param newHeight the height of the thumbnail + * @return a new compatible BufferedImage containing a + * thumbnail of image + * @throws IllegalArgumentException if newWidth is larger than + * the width of image or if code>newHeight is larger + * than the height of image or if one of the dimensions + * is <= 0 + */ + public static BufferedImage createThumbnailFast(BufferedImage image, + int newWidth, int newHeight) { + if (newWidth >= image.getWidth() || + newHeight >= image.getHeight()) { + throw new IllegalArgumentException("newWidth and newHeight cannot" + + " be greater than the image" + + " dimensions"); + } else if (newWidth <= 0 || newHeight <= 0) { + throw new IllegalArgumentException("newWidth and newHeight must" + + " be greater than 0"); + } + + BufferedImage temp = createCompatibleImage(image, newWidth, newHeight); + Graphics2D g2 = temp.createGraphics(); + + try { + g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g2.drawImage(image, 0, 0, temp.getWidth(), temp.getHeight(), null); + } finally { + g2.dispose(); + } + + return temp; + } + + /** + *

                Returns a thumbnail of a source image. newSize defines + * the length of the longest dimension of the thumbnail. The other + * dimension is then computed according to the dimensions ratio of the + * original picture.

                + *

                This method offers a good trade-off between speed and quality. + * The result looks better than + * {@link #createThumbnailFast(BufferedImage, int)} when + * the new size is less than half the longest dimension of the source + * image, yet the rendering speed is almost similar.

                + * + * @see #createThumbnailFast(BufferedImage, int, int) + * @see #createThumbnailFast(BufferedImage, int) + * @see #createThumbnail(BufferedImage, int, int) + * @param image the source image + * @param newSize the length of the largest dimension of the thumbnail + * @return a new compatible BufferedImage containing a + * thumbnail of image + * @throws IllegalArgumentException if newSize is larger than + * the largest dimension of image or <= 0 + */ + public static BufferedImage createThumbnail(BufferedImage image, + int newSize) { + int width = image.getWidth(); + int height = image.getHeight(); + + boolean isTranslucent = image.getTransparency() != Transparency.OPAQUE; + boolean isWidthGreater = width > height; + + if (isWidthGreater) { + if (newSize >= width) { + throw new IllegalArgumentException("newSize must be lower than" + + " the image width"); + } + } else if (newSize >= height) { + throw new IllegalArgumentException("newSize must be lower than" + + " the image height"); + } + + if (newSize <= 0) { + throw new IllegalArgumentException("newSize must" + + " be greater than 0"); + } + + float ratioWH = (float) width / (float) height; + float ratioHW = (float) height / (float) width; + + BufferedImage thumb = image; + BufferedImage temp = null; + + Graphics2D g2 = null; + + try { + int previousWidth = width; + int previousHeight = height; + + do { + if (isWidthGreater) { + width /= 2; + if (width < newSize) { + width = newSize; + } + height = (int) (width / ratioWH); + } else { + height /= 2; + if (height < newSize) { + height = newSize; + } + width = (int) (height / ratioHW); + } + + if (temp == null || isTranslucent) { + if (g2 != null) { + //do not need to wrap with finally + //outer finally block will ensure + //that resources are properly reclaimed + g2.dispose(); + } + temp = createCompatibleImage(image, width, height); + g2 = temp.createGraphics(); + g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BILINEAR); + } + g2.drawImage(thumb, 0, 0, width, height, + 0, 0, previousWidth, previousHeight, null); + + previousWidth = width; + previousHeight = height; + + thumb = temp; + } while (newSize != (isWidthGreater ? width : height)); + } finally { + if (g2 != null) { + g2.dispose(); + } + } + + if (width != thumb.getWidth() || height != thumb.getHeight()) { + temp = createCompatibleImage(image, width, height); + g2 = temp.createGraphics(); + + try { + g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g2.drawImage(thumb, 0, 0, width, height, 0, 0, width, height, null); + } finally { + g2.dispose(); + } + + thumb = temp; + } + + return thumb; + } + + /** + *

                Returns a thumbnail of a source image.

                + *

                This method offers a good trade-off between speed and quality. + * The result looks better than + * {@link #createThumbnailFast(BufferedImage, int)} when + * the new size is less than half the longest dimension of the source + * image, yet the rendering speed is almost similar.

                + * + * @see #createThumbnailFast(BufferedImage, int) + * @see #createThumbnailFast(BufferedImage, int, int) + * @see #createThumbnail(BufferedImage, int) + * @param image the source image + * @param newWidth the width of the thumbnail + * @param newHeight the height of the thumbnail + * @return a new compatible BufferedImage containing a + * thumbnail of image + * @throws IllegalArgumentException if newWidth is larger than + * the width of image or if code>newHeight is larger + * than the height of image or if one the dimensions is not > 0 + */ + public static BufferedImage createThumbnail(BufferedImage image, + int newWidth, int newHeight) { + int width = image.getWidth(); + int height = image.getHeight(); + + boolean isTranslucent = image.getTransparency() != Transparency.OPAQUE; + + if (newWidth >= width || newHeight >= height) { + throw new IllegalArgumentException("newWidth and newHeight cannot" + + " be greater than the image" + + " dimensions"); + } else if (newWidth <= 0 || newHeight <= 0) { + throw new IllegalArgumentException("newWidth and newHeight must" + + " be greater than 0"); + } + + BufferedImage thumb = image; + BufferedImage temp = null; + + Graphics2D g2 = null; + + try { + int previousWidth = width; + int previousHeight = height; + + do { + if (width > newWidth) { + width /= 2; + if (width < newWidth) { + width = newWidth; + } + } + + if (height > newHeight) { + height /= 2; + if (height < newHeight) { + height = newHeight; + } + } + + if (temp == null || isTranslucent) { + if (g2 != null) { + //do not need to wrap with finally + //outer finally block will ensure + //that resources are properly reclaimed + g2.dispose(); + } + temp = createCompatibleImage(image, width, height); + g2 = temp.createGraphics(); + g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BILINEAR); + } + g2.drawImage(thumb, 0, 0, width, height, + 0, 0, previousWidth, previousHeight, null); + + previousWidth = width; + previousHeight = height; + + thumb = temp; + } while (width != newWidth || height != newHeight); + } finally { + if (g2 != null) { + g2.dispose(); + } + } + + if (width != thumb.getWidth() || height != thumb.getHeight()) { + temp = createCompatibleImage(image, width, height); + g2 = temp.createGraphics(); + + try { + g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g2.drawImage(thumb, 0, 0, width, height, 0, 0, width, height, null); + } finally { + g2.dispose(); + } + + thumb = temp; + } + + return thumb; + } + + /** + *

                Returns an array of pixels, stored as integers, from a + * BufferedImage. The pixels are grabbed from a rectangular + * area defined by a location and two dimensions. Calling this method on + * an image of type different from BufferedImage.TYPE_INT_ARGB + * and BufferedImage.TYPE_INT_RGB will unmanage the image.

                + * + * @param img the source image + * @param x the x location at which to start grabbing pixels + * @param y the y location at which to start grabbing pixels + * @param w the width of the rectangle of pixels to grab + * @param h the height of the rectangle of pixels to grab + * @param pixels a pre-allocated array of pixels of size w*h; can be null + * @return pixels if non-null, a new array of integers + * otherwise + * @throws IllegalArgumentException is pixels is non-null and + * of length < w*h + */ + public static int[] getPixels(BufferedImage img, + int x, int y, int w, int h, int[] pixels) { + if (w == 0 || h == 0) { + return new int[0]; + } + + if (pixels == null) { + pixels = new int[w * h]; + } else if (pixels.length < w * h) { + throw new IllegalArgumentException("pixels array must have a length" + + " >= w*h"); + } + + int imageType = img.getType(); + if (imageType == BufferedImage.TYPE_INT_ARGB || + imageType == BufferedImage.TYPE_INT_RGB) { + Raster raster = img.getRaster(); + return (int[]) raster.getDataElements(x, y, w, h, pixels); + } + + // Unmanages the image + return img.getRGB(x, y, w, h, pixels, 0, w); + } + + /** + *

                Writes a rectangular area of pixels in the destination + * BufferedImage. Calling this method on + * an image of type different from BufferedImage.TYPE_INT_ARGB + * and BufferedImage.TYPE_INT_RGB will unmanage the image.

                + * + * @param img the destination image + * @param x the x location at which to start storing pixels + * @param y the y location at which to start storing pixels + * @param w the width of the rectangle of pixels to store + * @param h the height of the rectangle of pixels to store + * @param pixels an array of pixels, stored as integers + * @throws IllegalArgumentException is pixels is non-null and + * of length < w*h + */ + public static void setPixels(BufferedImage img, + int x, int y, int w, int h, int[] pixels) { + if (pixels == null || w == 0 || h == 0) { + return; + } else if (pixels.length < w * h) { + throw new IllegalArgumentException("pixels array must have a length" + + " >= w*h"); + } + + int imageType = img.getType(); + if (imageType == BufferedImage.TYPE_INT_ARGB || + imageType == BufferedImage.TYPE_INT_RGB) { + WritableRaster raster = img.getRaster(); + raster.setDataElements(x, y, w, h, pixels); + } else { + // Unmanages the image + img.setRGB(x, y, w, h, pixels, 0, w); + } + } + + /** + * Clears the data from the image. + * + * @param img + * the image to erase + */ + public static void clear(Image img) { + Graphics g = img.getGraphics(); + + try { + if (g instanceof Graphics2D) { + ((Graphics2D) g).setComposite(AlphaComposite.Clear); + } else { + g.setColor(new Color(0, 0, 0, 0)); + } + + g.fillRect(0, 0, img.getWidth(null), img.getHeight(null)); + } finally { + g.dispose(); + } + } + + /** + * Draws an image on top of a component by doing a 3x3 grid stretch of the image + * using the specified insets. + */ + public static void tileStretchPaint(Graphics g, + JComponent comp, + BufferedImage img, + Insets ins) { + + int left = ins.left; + int right = ins.right; + int top = ins.top; + int bottom = ins.bottom; + + // top + g.drawImage(img, + 0,0,left,top, + 0,0,left,top, + null); + g.drawImage(img, + left, 0, + comp.getWidth() - right, top, + left, 0, + img.getWidth() - right, top, + null); + g.drawImage(img, + comp.getWidth() - right, 0, + comp.getWidth(), top, + img.getWidth() - right, 0, + img.getWidth(), top, + null); + + // middle + g.drawImage(img, + 0, top, + left, comp.getHeight()-bottom, + 0, top, + left, img.getHeight()-bottom, + null); + + g.drawImage(img, + left, top, + comp.getWidth()-right, comp.getHeight()-bottom, + left, top, + img.getWidth()-right, img.getHeight()-bottom, + null); + + g.drawImage(img, + comp.getWidth()-right, top, + comp.getWidth(), comp.getHeight()-bottom, + img.getWidth()-right, top, + img.getWidth(), img.getHeight()-bottom, + null); + + // bottom + g.drawImage(img, + 0,comp.getHeight()-bottom, + left, comp.getHeight(), + 0,img.getHeight()-bottom, + left,img.getHeight(), + null); + g.drawImage(img, + left, comp.getHeight()-bottom, + comp.getWidth()-right, comp.getHeight(), + left, img.getHeight()-bottom, + img.getWidth()-right, img.getHeight(), + null); + g.drawImage(img, + comp.getWidth()-right, comp.getHeight()-bottom, + comp.getWidth(), comp.getHeight(), + img.getWidth()-right, img.getHeight()-bottom, + img.getWidth(), img.getHeight(), + null); + } +} diff --git a/src/main/java/org/jdesktop/swingx/util/OS.java b/src/main/java/org/jdesktop/swingx/util/OS.java new file mode 100644 index 0000000000..a8d1f17570 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/util/OS.java @@ -0,0 +1,130 @@ +/* + * $Id: OS.java 4088 2011-11-17 19:53:49Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.util; + +import javax.swing.*; +import java.awt.*; + +/** + * Provides methods related to the runtime environment. + */ +@SuppressWarnings("nls") +public class OS { + + private static final boolean osIsMacOsX; + private static final boolean osIsWindows; + private static final boolean osIsWindowsXP; + private static final boolean osIsWindows2003; + private static final boolean osIsWindowsVista; + private static final boolean osIsLinux; + + static { + String os = System.getProperty("os.name"); + if (os != null) + os = os.toLowerCase(); + + osIsMacOsX = "mac os x".equals(os); + osIsWindows = os != null && os.indexOf("windows") != -1; + osIsWindowsXP = "windows xp".equals(os); + osIsWindows2003 = "windows 2003".equals(os); + osIsWindowsVista = "windows vista".equals(os); + osIsLinux = os != null && os.indexOf("linux") != -1; + } + + /** + * @return true if this VM is running on Mac OS X + */ + public static boolean isMacOSX() { + return osIsMacOsX; + } + + /** + * @return true if this VM is running on Windows + */ + public static boolean isWindows() { + return osIsWindows; + } + + /** + * @return true if this VM is running on Windows XP + */ + public static boolean isWindowsXP() { + return osIsWindowsXP; + } + + /** + * @return true if this VM is running on Windows Vista + */ + public static boolean isWindowsVista() { + return osIsWindowsVista; + } + + /** + * @return true if this VM is running on a Linux distribution + */ + public static boolean isLinux() { + return osIsLinux; + } + + /** + * @return true if the VM is running Windows and the Java + * application is rendered using XP Visual Styles. + */ + public static boolean isUsingWindowsVisualStyles() { + if (!isWindows()) { + return false; + } + + boolean xpthemeActive = Boolean.TRUE.equals(Toolkit.getDefaultToolkit() + .getDesktopProperty("win.xpstyle.themeActive")); + if (!xpthemeActive) { + return false; + } else { + try { + return System.getProperty("swing.noxp") == null; + } catch (RuntimeException e) { + return true; + } + } + } + + /** + * Returns the name of the current Windows visual style. + *
                  + *
                • it looks for a property name "win.xpstyle.name" in UIManager and if not found + *
                • it queries the win.xpstyle.colorName desktop property ({@link Toolkit#getDesktopProperty(String)}) + *
                + * + * @return the name of the current Windows visual style if any. + */ + public static String getWindowsVisualStyle() { + String style = UIManager.getString("win.xpstyle.name"); + if (style == null) { + // guess the name of the current XPStyle + // (win.xpstyle.colorName property found in awt_DesktopProperties.cpp in + // JDK source) + style = (String)Toolkit.getDefaultToolkit().getDesktopProperty( + "win.xpstyle.colorName"); + } + return style; + } + +} diff --git a/src/main/java/org/jdesktop/swingx/util/PaintUtils.java b/src/main/java/org/jdesktop/swingx/util/PaintUtils.java new file mode 100644 index 0000000000..8b2e9bbfcd --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/util/PaintUtils.java @@ -0,0 +1,538 @@ +/* + * $Id: PaintUtils.java 4193 2012-06-27 19:42:05Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.util; + +import java.awt.*; +import java.awt.geom.Point2D; +import java.awt.image.BufferedImage; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * A collection of utilities for working with Paints and Colors. + * + * @author Mark Davidson + * @author joshua.marinacci@sun.com + * @author Karl George Schaefer + */ +@SuppressWarnings("nls") +public class PaintUtils { + public static final GradientPaint BLUE_EXPERIENCE = new GradientPaint( + new Point2D.Double(0, 0), + new Color(168, 204, 241), + new Point2D.Double(0, 1), + new Color(44, 61, 146)); + public static final GradientPaint MAC_OSX_SELECTED = new GradientPaint( + new Point2D.Double(0, 0), + new Color(81, 141, 236), + new Point2D.Double(0, 1), + new Color(36, 96, 192)); + public static final GradientPaint MAC_OSX = new GradientPaint( + new Point2D.Double(0, 0), + new Color(167, 210, 250), + new Point2D.Double(0, 1), + new Color(99, 147, 206)); + public static final GradientPaint AERITH = new GradientPaint( + new Point2D.Double(0, 0), + Color.WHITE, + new Point2D.Double(0, 1), + new Color(64, 110, 161)); + public static final GradientPaint GRAY = new GradientPaint( + new Point2D.Double(0, 0), + new Color(226, 226, 226), + new Point2D.Double(0, 1), + new Color(250, 248, 248)); + public static final GradientPaint RED_XP = new GradientPaint( + new Point2D.Double(0, 0), + new Color(236, 81, 81), + new Point2D.Double(0, 1), + new Color(192, 36, 36)); + public static final GradientPaint NIGHT_GRAY = new GradientPaint( + new Point2D.Double(0, 0), + new Color(102, 111, 127), + new Point2D.Double(0, 1), + new Color(38, 45, 61)); + public static final GradientPaint NIGHT_GRAY_LIGHT = new GradientPaint( + new Point2D.Double(0, 0), + new Color(129, 138, 155), + new Point2D.Double(0, 1), + new Color(58, 66, 82)); + + + //originally included in LinearGradientPainter + public static final Paint ORANGE_DELIGHT = new LinearGradientPaint( + new Point2D.Double(0, 0), + new Point2D.Double(1, 0), + new float[] {0f, .5f, .51f, 1f}, + new Color[] { + new Color(248, 192, 75), + new Color(253, 152, 6), + new Color(243, 133, 0), + new Color(254, 124, 0)}); + + //originally included in LinearGradientPainter + public static final Paint BLACK_STAR = new LinearGradientPaint( + new Point2D.Double(0, 0), + new Point2D.Double(1, 0), + new float[] {0f, .5f, .51f, 1f}, + new Color[] { + new Color(54, 62, 78), + new Color(32, 39, 55), + new Color(74, 82, 96), + new Color(123, 132, 145)}); + + private PaintUtils() { + } + + /** Resizes a gradient to fill the width and height available. If the + * gradient is left to right it will be resized to fill the entire width. + * If the gradient is top to bottom it will be resized to fill the entire + * height. If the gradient is on an angle it will be resized to go from + * one corner to the other of the rectangle formed by (0,0 -> width,height). + * + * This method can resize java.awt.GradientPaint, java.awt.LinearGradientPaint, + * and the LinearGradientPaint implementation from Apache's Batik project. Note, + * this method does not require the MultipleGradientPaint.jar from Apache to + * compile or to run. MultipleGradientPaint.jar *is* required if you want + * to resize the LinearGradientPaint from that jar. + * + * Any paint passed into this method which is not a kind of gradient paint (like + * a Color or TexturePaint) will be returned unmodified. It will not throw + * an exception. If the gradient cannot be resized due to other errors the + * original paint will be returned unmodified. It will not throw an + * exception. + * + */ + public static Paint resizeGradient(Paint p, int width, int height) { + if(p == null) return p; + + if(p instanceof GradientPaint) { + GradientPaint gp = (GradientPaint)p; + Point2D[] pts = new Point2D[2]; + pts[0] = gp.getPoint1(); + pts[1] = gp.getPoint2(); + pts = adjustPoints(pts, width, height); + return new GradientPaint(pts[0], gp.getColor1(), pts[1], gp.getColor2(), gp.isCyclic()); + } + + if("java.awt.LinearGradientPaint".equals(p.getClass().getName()) || + "org.apache.batik.ext.awt.LinearGradientPaint".equals(p.getClass().getName())) { + return resizeLinearGradient(p,width,height); + } + return p; + } + + + private static Paint resizeLinearGradient(Paint p, int width, int height) { + try { + Point2D[] pts = new Point2D[2]; + pts[0] = (Point2D) invokeMethod(p,"getStartPoint"); + pts[1] = (Point2D) invokeMethod(p,"getEndPoint"); + pts = adjustPoints(pts, width, height); + float[] fractions = (float[]) invokeMethod(p,"getFractions"); + Color[] colors = (Color[]) invokeMethod(p,"getColors"); + + Constructor con = p.getClass().getDeclaredConstructor( + Point2D.class, Point2D.class, + new float[0].getClass(), + new Color[0].getClass()); + return (Paint) con.newInstance(pts[0],pts[1],fractions, colors); + } catch (Exception ex) { + ex.printStackTrace(); + } + return p; + } + + private static Object invokeMethod(final Object p, final String methodName) + throws NoSuchMethodException, InvocationTargetException, IllegalArgumentException, SecurityException, IllegalAccessException { + Method meth = p.getClass().getMethod(methodName); + return meth.invoke(p); + } + + + private static Point2D[] adjustPoints(Point2D[] pts, int width, int height) { + Point2D start = pts[0]; + Point2D end = pts[1]; + + double angle = calcAngle(start,end); + double a2 = Math.toDegrees(angle); + double e = 1; + + // if it is near 0 degrees + if(Math.abs(angle) < Math.toRadians(e) || + Math.abs(angle) > Math.toRadians(360 - e)) { + start = new Point2D.Float(0, 0); + end = new Point2D.Float(normalize(end.getX(), width), 0); + } + + // near 45 + if (isNear(a2, 45, e)) { + start = new Point2D.Float(0, 0); + end = new Point2D.Float(normalize(end.getX(), width), normalize(end.getY(), height)); + } + + // near 90 + if (isNear(a2, 90, e)) { + start = new Point2D.Float(0, 0); + end = new Point2D.Float(0, normalize(end.getY(), height)); + } + + // near 135 + if (isNear(a2, 135, e)) { + start = new Point2D.Float(normalize(start.getX(), width), 0); + end = new Point2D.Float(0, normalize(end.getY(), height)); + } + + // near 180 + if (isNear(a2, 180, e)) { + start = new Point2D.Float(normalize(start.getX(), width), 0); + end = new Point2D.Float(0, 0); + } + + // near 225 + if (isNear(a2, 225, e)) { + start = new Point2D.Float(normalize(start.getX(), width), normalize(start.getY(), height)); + end = new Point2D.Float(0, 0); + } + + // near 270 + if (isNear(a2, 270, e)) { + start = new Point2D.Float(0, normalize(start.getY(), height)); + end = new Point2D.Float(0, 0); + } + + // near 315 + if (isNear(a2, 315, e)) { + start = new Point2D.Float(0, normalize(start.getY(), height)); + end = new Point2D.Float(normalize(end.getX(), width), 0); + } + + return new Point2D[] { start, end }; + } + + private static boolean isNear(double angle, double target, double error) { + return Math.abs(target - Math.abs(angle)) < error; + } + + private static float normalize(double original, float target) { + if (original < 1f) { + return target * (float) original; + } + + return target; + } + + private static double calcAngle(Point2D p1, Point2D p2) { + double x_off = p2.getX() - p1.getX(); + double y_off = p2.getY() - p1.getY(); + double angle = Math.atan(y_off / x_off); + if (x_off < 0) { + angle = angle + Math.PI; + } + + if(angle < 0) { angle+= 2*Math.PI; } + if(angle > 2*Math.PI) { angle -= 2*Math.PI; } + return angle; + } +/* + public static void main(String ... args) { + LinearGradientPaint in = new LinearGradientPaint( + new Point(0,0), new Point(10,0), + new float[] {0f, 0.5f, 1f}, + new Color[] {Color.RED, Color.GREEN, Color.BLUE}); + log.fine("in = " + toString(in)); + Paint out = resizeGradient(in,100,100); + log.fine(("out = " + toString((MultipleGradientPaint) out)); + }*/ + /* + private static String toString(MultipleGradientPaint paint) { + StringBuffer buffer = new StringBuffer(); + buffer.append(paint.getClass().getName()); + Color[] colors = paint.getColors(); + float[] values = paint.getFractions(); + buffer.append("["); + for(int i=0; i"); + buffer.append(""+lgp.getEndPoint().getX() + ", " + lgp.getEndPoint().getY()); + } + + return buffer.toString(); + }*/ + + /** + * Creates a new {@code Paint} that is a checkered effect using the colors {@link Color#GRAY + * gray} and {@link Color#WHITE}. + * + * @return a the checkered paint + */ + public static Paint getCheckerPaint() { + return getCheckerPaint(Color.WHITE, Color.GRAY, 20); + } + + /** + * Creates a new {@code Paint} that is a checkered effect using the specified colors. + *

                + * While this method supports transparent colors, this implementation performs painting + * operations using the second color after it performs operations using the first color. This + * means that to create a checkered paint with a fully-transparent color, you MUST specify that + * color first. + * + * @param c1 + * the first color + * @param c2 + * the second color + * @param size + * the size of the paint + * @return a new {@code Paint} checkering the supplied colors + */ + public static Paint getCheckerPaint(Paint c1, Paint c2, int size) { + BufferedImage img = GraphicsUtilities.createCompatibleTranslucentImage(size, size); + Graphics2D g = img.createGraphics(); + + try { + g.setPaint(c1); + g.fillRect(0, 0, size, size); + g.setPaint(c2); + g.fillRect(0, 0, size / 2, size / 2); + g.fillRect(size / 2, size / 2, size / 2, size / 2); + } finally { + g.dispose(); + } + + return new TexturePaint(img,new Rectangle(0,0,size,size)); + } + + /** + * Creates a {@code String} that represents the supplied color as a + * hex-value RGB triplet, including the "#". The return value is suitable + * for use in HTML. The alpha (transparency) channel is neither include nor + * used in producing the string. + * + * @param color + * the color to convert + * @return the hex {@code String} + */ + public static String toHexString(Color color) { + return "#" + Integer.toHexString(color.getRGB() | 0xFF000000).substring(2); + } + + /** + * Returns a new color equal to the old one, except that there is no alpha + * (transparency) channel. + *

                + * This method is a convenience and has the same effect as {@code + * setAlpha(color, 255)}. + * + * @param color + * the color to remove the alpha (transparency) from + * @return a new non-transparent {@code Color} + * @throws NullPointerException + * if {@code color} is {@code null} + */ + public static Color removeAlpha(Color color) { + return setAlpha(color, 255); + } + + /** + * Returns a new color equal to the old one, except alpha (transparency) + * channel is set to the new value. + * + * @param color + * the color to modify + * @param alpha + * the new alpha (transparency) level. Must be an int between 0 + * and 255 + * @return a new alpha-applied {@code Color} + * @throws IllegalArgumentException + * if {@code alpha} is not between 0 and 255 inclusive + * @throws NullPointerException + * if {@code color} is {@code null} + */ + public static Color setAlpha(Color color, int alpha) { + if (alpha < 0 || alpha > 255) { + throw new IllegalArgumentException("invalid alpha value"); + } + + return new Color( + color.getRed(), color.getGreen(), color.getBlue(), alpha); + } + + /** + * Returns a new color equal to the old one, except the saturation is set to + * the new value. The new color will have the same alpha (transparency) as + * the original color. + *

                + * The color is modified using HSB calculations. The saturation must be a + * float between 0 and 1. If 0 the resulting color will be gray. If 1 the + * resulting color will be the most saturated possible form of the passed in + * color. + * + * @param color + * the color to modify + * @param saturation + * the saturation to use in the new color + * @return a new saturation-applied {@code Color} + * @throws IllegalArgumentException + * if {@code saturation} is not between 0 and 1 inclusive + * @throws NullPointerException + * if {@code color} is {@code null} + */ + public static Color setSaturation(Color color, float saturation) { + if (saturation < 0f || saturation > 1f) { + throw new IllegalArgumentException("invalid saturation value"); + } + + int alpha = color.getAlpha(); + + float[] hsb = Color.RGBtoHSB( + color.getRed(), color.getGreen(), color.getBlue(), null); + Color c = Color.getHSBColor(hsb[0], saturation, hsb[2]); + + return setAlpha(c, alpha); + } + + /** + * Returns a new color equal to the old one, except the brightness is set to + * the new value. The new color will have the same alpha (transparency) as + * the original color. + *

                + * The color is modified using HSB calculations. The brightness must be a + * float between 0 and 1. If 0 the resulting color will be black. If 1 the + * resulting color will be the brightest possible form of the passed in + * color. + * + * @param color + * the color to modify + * @param brightness + * the brightness to use in the new color + * @return a new brightness-applied {@code Color} + * @throws IllegalArgumentException + * if {@code brightness} is not between 0 and 1 inclusive + * @throws NullPointerException + * if {@code color} is {@code null} + */ + public static Color setBrightness(Color color, float brightness) { + if (brightness < 0f || brightness > 1f) { + throw new IllegalArgumentException("invalid brightness value"); + } + + int alpha = color.getAlpha(); + + float[] hsb = Color.RGBtoHSB( + color.getRed(), color.getGreen(), color.getBlue(), null); + Color c = Color.getHSBColor(hsb[0], hsb[1], brightness); + + return setAlpha(c, alpha); + } + + /** + * Blends two colors to create a new color. The {@code origin} color is the + * base for the new color and regardless of its alpha component, it is + * treated as fully opaque (alpha 255). + * + * @param origin + * the base of the new color + * @param over + * the alpha-enabled color to add to the {@code origin} color + * @return a new color comprised of the {@code origin} and {@code over} + * colors + */ + public static Color blend(Color origin, Color over) { + if (over == null) { + return origin; + } + + if (origin == null) { + return over; + } + + int a = over.getAlpha(); + + int rb = (((over.getRGB() & 0x00ff00ff) * (a + 1)) + + ((origin.getRGB() & 0x00ff00ff) * (0xff - a))) & 0xff00ff00; + int g = (((over.getRGB() & 0x0000ff00) * (a + 1)) + + ((origin.getRGB() & 0x0000ff00) * (0xff - a))) & 0x00ff0000; + + return new Color((over.getRGB() & 0xff000000) | ((rb | g) >> 8)); + } + + /** + * Interpolates a color. + * + * @param b + * the first color + * @param a + * the second color + * @param t + * the amount to interpolate + * @return a new color + */ + public static Color interpolate(Color b, Color a, float t) { + float[] acomp = a.getRGBComponents(null); + float[] bcomp = b.getRGBComponents(null); + float[] ccomp = new float[4]; + + // log.fine(("a comp "); + // for(float f : acomp) { + // log.fine((f); + // } + // for(float f : bcomp) { + // log.fine((f); + // } + for(int i=0; i<4; i++) { + ccomp[i] = acomp[i] + (bcomp[i]-acomp[i])*t; + } + // for(float f : ccomp) { + // log.fine((f); + // } + + return new Color(ccomp[0],ccomp[1],ccomp[2],ccomp[3]); + } + + /** + * Computes an appropriate foreground color (either white or black) for the + * given background color. + * + * @param bg + * the background color + * @return {@code Color.WHITE} or {@code Color.BLACK} + * @throws NullPointerException + * if {@code bg} is {@code null} + */ + public static Color computeForeground(Color bg) { + float[] rgb = bg.getRGBColorComponents(null); + float y = .3f * rgb[0] + .59f * rgb[1] + .11f * rgb[2]; + + return y > .5f ? Color.BLACK : Color.WHITE; + } +} diff --git a/src/main/java/org/jdesktop/swingx/util/Separator.java b/src/main/java/org/jdesktop/swingx/util/Separator.java new file mode 100644 index 0000000000..5da93c99ef --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/util/Separator.java @@ -0,0 +1,46 @@ +package org.jdesktop.swingx.util; + +/** + * A simple separator for adding in between each element in a list. + *

                + *

                + * for (String s : strings) {
                + *   stringBuilder.append(separator.get().append(s);
                + * }
                + * 
                + * + * @author Karl Schaefer + * @author Bruce Chapman (original idea) + * + * @param + * the type of separator + */ +public class Separator { + private T next; + private T separator; + + /** + * Constructs a separator with the specified initial value and remaining separator. + * + * @param initial + * the value to use for the first call + * @param separator + * the value to use after the first call + */ + public Separator(T initial, T separator) { + this.next = initial; + this.separator = separator; + } + + /** + * Returns the current value of the separator. + * + * @return the separator value + */ + public T get() { + T result = next; + next = separator; + + return result; + } +} diff --git a/src/main/java/org/jdesktop/swingx/util/ShapeUtils.java b/src/main/java/org/jdesktop/swingx/util/ShapeUtils.java new file mode 100644 index 0000000000..0070771a14 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/util/ShapeUtils.java @@ -0,0 +1,118 @@ +/* + * $Id: ShapeUtils.java 4082 2011-11-15 18:39:43Z kschaefe $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.util; + +import java.awt.*; +import java.awt.font.GlyphVector; +import java.awt.geom.*; +import java.awt.image.BufferedImage; + +/** + * + * @author joshy + */ +public final class ShapeUtils { + + /** Creates a new instance of ShapeUtils */ + private ShapeUtils() { + } + + public static Shape generatePolygon(int sides, int outsideRadius, boolean normalize) { + return generatePolygon(sides, outsideRadius, 0, normalize); + } + + public static Shape generatePolygon(int sides, int outsideRadius, int insideRadius, + boolean normalize) { + Shape shape = generatePolygon(sides, outsideRadius, insideRadius); + if (normalize) { + Rectangle2D bounds = shape.getBounds2D(); + GeneralPath path = new GeneralPath(shape); + shape = path.createTransformedShape(AffineTransform.getTranslateInstance( + -bounds.getX(), -bounds.getY())); + } + return shape; + } + + public static Shape generatePolygon(int sides, int outsideRadius, int insideRadius) { + if (sides < 3) { + return new Ellipse2D.Float(0, 0, 10, 10); + } + + AffineTransform trans = new AffineTransform(); + Polygon poly = new Polygon(); + for (int i = 0; i < sides; i++) { + trans.rotate(Math.PI * 2 / sides / 2); + Point2D out = trans.transform(new Point2D.Float(0, outsideRadius), null); + poly.addPoint((int) out.getX(), (int) out.getY()); + trans.rotate(Math.PI * 2 / sides / 2); + if (insideRadius > 0) { + Point2D in = trans.transform(new Point2D.Float(0, insideRadius), null); + poly.addPoint((int) in.getX(), (int) in.getY()); + } + } + + return poly; + } + + public static Shape generateShapeFromText(Font font, char ch) { + return generateShapeFromText(font, String.valueOf(ch)); + } + + public static Shape generateShapeFromText(Font font, String string) { + BufferedImage img = GraphicsUtilities.createCompatibleTranslucentImage(1, 1); + Graphics2D g2 = img.createGraphics(); + + try { + GlyphVector vect = font.createGlyphVector(g2.getFontRenderContext(), string); + Shape shape = vect.getOutline(0f, (float) -vect.getVisualBounds().getY()); + + return shape; + } finally { + g2.dispose(); + } + } + + /** + * Sets the clip on a graphics object by merging a supplied clip with the existing one. The new + * clip will be an intersection of the old clip and the supplied clip. The old clip shape will + * be returned. This is useful for resetting the old clip after an operation is performed. + * + * @param g + * the graphics object to update + * @param clip + * a new clipping region to add to the graphics clip. + * @return the current clipping region of the supplied graphics object. This may return + * {@code null} if the current clip is {@code null}. + * @throws NullPointerException + * if any parameter is {@code null} + */ + public static Shape mergeClip(Graphics g, Shape clip) { + Shape oldClip = g.getClip(); + if (oldClip == null) { + g.setClip(clip); + return null; + } + Area area = new Area(oldClip); + area.intersect(new Area(clip));// new Rectangle(0,0,width,height))); + g.setClip(area); + return oldClip; + } +} diff --git a/src/main/java/org/jdesktop/swingx/util/Utilities.java b/src/main/java/org/jdesktop/swingx/util/Utilities.java new file mode 100644 index 0000000000..917e26fa50 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/util/Utilities.java @@ -0,0 +1,964 @@ +/* + * $Id: Utilities.java 4084 2011-11-15 18:53:49Z kschaefe $ + * + * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.jdesktop.swingx.util; + + +import javax.swing.*; +import java.awt.*; +import java.awt.event.KeyEvent; +import java.lang.ref.Reference; +import java.lang.ref.SoftReference; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.text.BreakIterator; +import java.util.*; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Contribution from NetBeans: Issue #319-swingx.

                + * + * PENDING: need to reconcile with OS, JVM... added as-is + * because needed the shortcut handling to fix # + * + * @author apple + */ +public class Utilities { + private Utilities() { + } + + private static final int CTRL_WILDCARD_MASK = 32768; + private static final int ALT_WILDCARD_MASK = CTRL_WILDCARD_MASK * 2; + + /** Operating system is Windows NT. */ + public static final int OS_WINNT = 1; + + /** Operating system is Windows 95. */ + public static final int OS_WIN95 = OS_WINNT << 1; + + /** Operating system is Windows 98. */ + public static final int OS_WIN98 = OS_WIN95 << 1; + + /** Operating system is Solaris. */ + public static final int OS_SOLARIS = OS_WIN98 << 1; + + /** Operating system is Linux. */ + public static final int OS_LINUX = OS_SOLARIS << 1; + + /** Operating system is HP-UX. */ + public static final int OS_HP = OS_LINUX << 1; + + /** Operating system is IBM AIX. */ + public static final int OS_AIX = OS_HP << 1; + + /** Operating system is SGI IRIX. */ + public static final int OS_IRIX = OS_AIX << 1; + + /** Operating system is Sun OS. */ + public static final int OS_SUNOS = OS_IRIX << 1; + + /** Operating system is Compaq TRU64 Unix */ + public static final int OS_TRU64 = OS_SUNOS << 1; + + /** Operating system is OS/2. */ + public static final int OS_OS2 = OS_TRU64 << 2; + + /** Operating system is Mac. */ + public static final int OS_MAC = OS_OS2 << 1; + + /** Operating system is Windows 2000. */ + public static final int OS_WIN2000 = OS_MAC << 1; + + /** Operating system is Compaq OpenVMS */ + public static final int OS_VMS = OS_WIN2000 << 1; + + /** + *Operating system is one of the Windows variants but we don't know which + *one it is + */ + public static final int OS_WIN_OTHER = OS_VMS << 1; + + /** Operating system is unknown. */ + public static final int OS_OTHER = OS_WIN_OTHER << 1; + + /** Operating system is FreeBSD + * @since 4.50 + */ + public static final int OS_FREEBSD = OS_OTHER << 1; + + /** A mask for Windows platforms. */ + public static final int OS_WINDOWS_MASK = OS_WINNT | OS_WIN95 | OS_WIN98 | OS_WIN2000 | OS_WIN_OTHER; + + /** A mask for Unix platforms. */ + public static final int OS_UNIX_MASK = OS_SOLARIS | OS_LINUX | OS_HP | OS_AIX | OS_IRIX | OS_SUNOS | OS_TRU64 | + OS_MAC | OS_FREEBSD; + + /** A height of the windows's taskbar */ + public static final int TYPICAL_WINDOWS_TASKBAR_HEIGHT = 27; + + /** A height of the Mac OS X's menu */ + private static final int TYPICAL_MACOSX_MENU_HEIGHT = 24; + + private static int operatingSystem = -1; + + /** reference to map that maps allowed key names to their values (String, Integer) + and reference to map for mapping of values to their names */ + private static Reference namesAndValues; + + /** Get the operating system on which NetBeans is running. + * @return one of the OS_* constants (such as {@link #OS_WINNT}) + */ + public static int getOperatingSystem() { + if (operatingSystem == -1) { + String osName = System.getProperty("os.name"); + + if ("Windows NT".equals(osName)) { // NOI18N + operatingSystem = OS_WINNT; + } else if ("Windows 95".equals(osName)) { // NOI18N + operatingSystem = OS_WIN95; + } else if ("Windows 98".equals(osName)) { // NOI18N + operatingSystem = OS_WIN98; + } else if ("Windows 2000".equals(osName)) { // NOI18N + operatingSystem = OS_WIN2000; + } else if (osName.startsWith("Windows ")) { // NOI18N + operatingSystem = OS_WIN_OTHER; + } else if ("Solaris".equals(osName)) { // NOI18N + operatingSystem = OS_SOLARIS; + } else if (osName.startsWith("SunOS")) { // NOI18N + operatingSystem = OS_SOLARIS; + } + // JDK 1.4 b2 defines os.name for me as "Redhat Linux" -jglick + else if (osName.endsWith("Linux")) { // NOI18N + operatingSystem = OS_LINUX; + } else if ("HP-UX".equals(osName)) { // NOI18N + operatingSystem = OS_HP; + } else if ("AIX".equals(osName)) { // NOI18N + operatingSystem = OS_AIX; + } else if ("Irix".equals(osName)) { // NOI18N + operatingSystem = OS_IRIX; + } else if ("SunOS".equals(osName)) { // NOI18N + operatingSystem = OS_SUNOS; + } else if ("Digital UNIX".equals(osName)) { // NOI18N + operatingSystem = OS_TRU64; + } else if ("OS/2".equals(osName)) { // NOI18N + operatingSystem = OS_OS2; + } else if ("OpenVMS".equals(osName)) { // NOI18N + operatingSystem = OS_VMS; + } else if (osName.equals("Mac OS X")) { // NOI18N + operatingSystem = OS_MAC; + } else if (osName.startsWith("Darwin")) { // NOI18N + operatingSystem = OS_MAC; + } else if (osName.toLowerCase(Locale.US).startsWith("freebsd")) { // NOI18N + operatingSystem = OS_FREEBSD; + } else { + operatingSystem = OS_OTHER; + } + } + return operatingSystem; + } + + /** Test whether NetBeans is running on some variant of Windows. + * @return true if Windows, false if some other manner of operating system + */ + public static boolean isWindows() { + return (getOperatingSystem() & OS_WINDOWS_MASK) != 0; + } + + /** Test whether NetBeans is running on some variant of Unix. + * Linux is included as well as the commercial vendors, and Mac OS X. + * @return true some sort of Unix, false if some other manner of operating system + */ + public static boolean isUnix() { + return (getOperatingSystem() & OS_UNIX_MASK) != 0; + } + + /** Test whether the operating system supports icons on frames (windows). + * @return true if it does not + * + */ + public static boolean isLargeFrameIcons() { + return (getOperatingSystem() == OS_SOLARIS) || (getOperatingSystem() == OS_HP); + } + + /** + * Finds out the monitor where the user currently has the input focus. + * This method is usually used to help the client code to figure out on + * which monitor it should place newly created windows/frames/dialogs. + * + * @return the GraphicsConfiguration of the monitor which currently has the + * input focus + */ + private static GraphicsConfiguration getCurrentGraphicsConfiguration() { + Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); + if (focusOwner != null) { + Window w = SwingUtilities.getWindowAncestor(focusOwner); + if (w != null) { + return w.getGraphicsConfiguration(); + } + } + + return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration(); + } + + /** + * Returns the usable area of the screen where applications can place its + * windows. The method subtracts from the screen the area of taskbars, + * system menus and the like. The screen this method applies to is the one + * which is considered current, ussually the one where the current input + * focus is. + * + * @return the rectangle of the screen where one can place windows + * + * @since 2.5 + */ + public static Rectangle getUsableScreenBounds() { + return getUsableScreenBounds(getCurrentGraphicsConfiguration()); + } + + /** + * Returns the usable area of the screen where applications can place its + * windows. The method subtracts from the screen the area of taskbars, + * system menus and the like. + * + * @param gconf the GraphicsConfiguration of the monitor + * @return the rectangle of the screen where one can place windows + * + * @since 2.5 + */ + public static Rectangle getUsableScreenBounds(GraphicsConfiguration gconf) { + if (gconf == null) { + gconf = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration(); + } + + Rectangle bounds = new Rectangle(gconf.getBounds()); + + String str; + + str = System.getProperty("netbeans.screen.insets"); // NOI18N + + if (str != null) { + StringTokenizer st = new StringTokenizer(str, ", "); // NOI18N + + if (st.countTokens() == 4) { + try { + bounds.y = Integer.parseInt(st.nextToken()); + bounds.x = Integer.parseInt(st.nextToken()); + bounds.height -= (bounds.y + Integer.parseInt(st.nextToken())); + bounds.width -= (bounds.x + Integer.parseInt(st.nextToken())); + } catch (NumberFormatException ex) { + Logger.getAnonymousLogger().log(Level.WARNING, null, ex); + } + } + + return bounds; + } + + str = System.getProperty("netbeans.taskbar.height"); // NOI18N + + if (str != null) { + bounds.height -= Integer.getInteger(str, 0); + + return bounds; + } + + try { + Toolkit toolkit = Toolkit.getDefaultToolkit(); + Insets insets = toolkit.getScreenInsets(gconf); + bounds.y += insets.top; + bounds.x += insets.left; + bounds.height -= (insets.top + insets.bottom); + bounds.width -= (insets.left + insets.right); + } catch (Exception ex) { + Logger.getAnonymousLogger().log(Level.WARNING, null, ex); + } + + return bounds; + } + + + /** Initialization of the names and values + * @return array of two hashmaps first maps + * allowed key names to their values (String, Integer) + * and second + * hashtable for mapping of values to their names (Integer, String) + */ + private static synchronized HashMap[] initNameAndValues() { + if (namesAndValues != null) { + HashMap[] arr = (HashMap[]) namesAndValues.get(); + + if (arr != null) { + return arr; + } + } + + Field[] fields; + // JW - fix Issue #353-swingx: play nicer inside sandbox. + try { + fields = KeyEvent.class.getDeclaredFields(); +// fields = KeyEvent.class.getFields(); + } catch (SecurityException e) { + // JW: need to do better? What are the use-cases where we don't have + // any access to the fields? + fields = new Field[0]; + } + + HashMap names = new HashMap(((fields.length * 4) / 3) + 5, 0.75f); + HashMap values = new HashMap(((fields.length * 4) / 3) + 5, 0.75f); + + for (int i = 0; i < fields.length; i++) { + if (Modifier.isStatic(fields[i].getModifiers())) { + String name = fields[i].getName(); + + if (name.startsWith("VK_")) { // NOI18N + + // exclude VK + name = name.substring(3); + + try { + int numb = fields[i].getInt(null); + Integer value = numb; + names.put(name, value); + values.put(value, name); + } catch (IllegalArgumentException ex) { + } catch (IllegalAccessException ex) { + } + } + } + } + + if (names.get("CONTEXT_MENU") == null) { // NOI18N + + Integer n = 0x20C; + names.put("CONTEXT_MENU", n); // NOI18N + values.put(n, "CONTEXT_MENU"); // NOI18N + + n = 0x20D; + names.put("WINDOWS", n); // NOI18N + values.put(n, "WINDOWS"); // NOI18N + } + + HashMap[] arr = { names, values }; + + namesAndValues = new SoftReference(arr); + + return arr; + } + + /** Converts a Swing key stroke descriptor to a familiar Emacs-like name. + * @param stroke key description + * @return name of the key (e.g. CS-F1 for control-shift-function key one) + * @see #stringToKey + */ + public static String keyToString(KeyStroke stroke) { + StringBuffer sb = new StringBuffer(); + + // add modifiers that must be pressed + if (addModifiers(sb, stroke.getModifiers())) { + sb.append('-'); + } + + HashMap[] namesAndValues = initNameAndValues(); + + String c = (String) namesAndValues[1].get(stroke.getKeyCode()); + + if (c == null) { + sb.append(stroke.getKeyChar()); + } else { + sb.append(c); + } + + return sb.toString(); + } + + /** Construct a new key description from a given universal string + * description. + * Provides mapping between Emacs-like textual key descriptions and the + * KeyStroke object used in Swing. + *

                + * This format has following form: + *

                [C][A][S][M]-identifier + *

                Where: + *

                  + *
                • C stands for the Control key + *
                • A stands for the Alt key + *
                • S stands for the Shift key + *
                • M stands for the Meta key + *
                + * The format also supports two wildcard codes, to support differences in + * platforms. These are the preferred choices for registering keystrokes, + * since platform conflicts will automatically be handled: + *
                  + *
                • D stands for the default menu accelerator - the Control + * key on most platforms, the Command (meta) key on Macintosh
                • + *
                • O stands for the alternate accelerator - the Alt key on + * most platforms, the Ctrl key on Macintosh (Macintosh uses Alt as a + * secondary shift key for composing international characters - if you bind + * Alt-8 to an action, a mac user with a French keyboard will not be able + * to type the [ character, which is a significant handicap
                • + *
                + * If you use the wildcard characters, and specify a key which will conflict + * with keys the operating system consumes, it will be mapped to whichever + * choice can work - for example, on Macintosh, Command-Q is always consumed + * by the operating system, so D-Q will always map to Control-Q. + *

                + * Every modifier before the hyphen must be pressed. + * identifier can be any text constant from {@link KeyEvent} but + * without the leading VK_ characters. So {@link KeyEvent#VK_ENTER} is described as + * ENTER. + * + * @param s the string with the description of the key + * @return key description object, or null if the string does not represent any valid key + */ + public static KeyStroke stringToKey(String s) { + StringTokenizer st = new StringTokenizer(s.toUpperCase(Locale.ENGLISH), "-", true); // NOI18N + + int needed = 0; + + HashMap names = initNameAndValues()[0]; + + int lastModif = -1; + + try { + for (;;) { + String el = st.nextToken(); + + // required key + if (el.equals("-")) { // NOI18N + + if (lastModif != -1) { + needed |= lastModif; + lastModif = -1; + } + + continue; + } + + // if there is more elements + if (st.hasMoreElements()) { + // the text should describe modifiers + lastModif = readModifiers(el); + } else { + // last text must be the key code + Integer i = (Integer) names.get(el); + boolean wildcard = (needed & CTRL_WILDCARD_MASK) != 0; + + //Strip out the explicit mask - KeyStroke won't know + //what to do with it + needed = needed & ~CTRL_WILDCARD_MASK; + + boolean macAlt = (needed & ALT_WILDCARD_MASK) != 0; + needed = needed & ~ALT_WILDCARD_MASK; + + if (i != null) { + //#26854 - Default accelerator should be Command on mac + if (wildcard) { + needed |= getMenuShortCutKeyMask(); + + if ((getOperatingSystem() & OS_MAC) != 0) { + if (!usableKeyOnMac(i.intValue(), needed)) { + needed &= ~getMenuShortCutKeyMask(); + needed |= KeyEvent.CTRL_MASK; + } + } + } + + if (macAlt) { + if (getOperatingSystem() == OS_MAC) { + needed |= KeyEvent.CTRL_MASK; + } else { + needed |= KeyEvent.ALT_MASK; + } + } + + return KeyStroke.getKeyStroke(i.intValue(), needed); + } else { + return null; + } + } + } + } catch (NoSuchElementException ex) { + return null; + } + } + /** + * need to guard against headlessExceptions when testing. + * @return the acceletor mask for shortcuts. + */ + private static int getMenuShortCutKeyMask() { + if (GraphicsEnvironment.isHeadless()) { + return ((getOperatingSystem() & OS_MAC) != 0) ? + KeyEvent.META_MASK : KeyEvent.CTRL_MASK; + } + + return Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); + } + + private static boolean usableKeyOnMac(int key, int mask) { + //All permutations fail for Q except ctrl + if (key == KeyEvent.VK_Q) { + return false; + } + + boolean isMeta = ((mask & KeyEvent.META_MASK) != 0) || ((mask & KeyEvent.CTRL_DOWN_MASK) != 0); + + boolean isAlt = ((mask & KeyEvent.ALT_MASK) != 0) || ((mask & KeyEvent.ALT_DOWN_MASK) != 0); + + boolean isOnlyMeta = isMeta && ((mask & ~(KeyEvent.META_DOWN_MASK | KeyEvent.META_MASK)) == 0); + + //Mac OS consumes keys Command+ these keys - the app will never see + //them, so CTRL should not be remapped for these + if (isOnlyMeta) { + return (key != KeyEvent.VK_H) && (key != KeyEvent.VK_SPACE) && (key != KeyEvent.VK_TAB); + } else return !((key == KeyEvent.VK_D) && isMeta && isAlt); + } + + /** Convert a space-separated list of Emacs-like key binding names to a list of Swing key strokes. + * @param s the string with keys + * @return array of key strokes, or null if the string description is not valid + * @see #stringToKey + */ + public static KeyStroke[] stringToKeys(String s) { + StringTokenizer st = new StringTokenizer(s.toUpperCase(Locale.ENGLISH), " "); // NOI18N + ArrayList arr = new ArrayList(); + + while (st.hasMoreElements()) { + s = st.nextToken(); + + KeyStroke k = stringToKey(s); + + if (k == null) { + return null; + } + + arr.add(k); + } + + return arr.toArray(new KeyStroke[arr.size()]); + } + + /** Adds characters for modifiers to the buffer. + * @param buf buffer to add to + * @param modif modifiers to add (KeyEvent.XXX_MASK) + * @return true if something has been added + */ + private static boolean addModifiers(StringBuffer buf, int modif) { + boolean b = false; + + if ((modif & KeyEvent.CTRL_MASK) != 0) { + buf.append("C"); // NOI18N + b = true; + } + + if ((modif & KeyEvent.ALT_MASK) != 0) { + buf.append("A"); // NOI18N + b = true; + } + + if ((modif & KeyEvent.SHIFT_MASK) != 0) { + buf.append("S"); // NOI18N + b = true; + } + + if ((modif & KeyEvent.META_MASK) != 0) { + buf.append("M"); // NOI18N + b = true; + } + + if ((modif & CTRL_WILDCARD_MASK) != 0) { + buf.append("D"); + b = true; + } + + if ((modif & ALT_WILDCARD_MASK) != 0) { + buf.append("O"); + b = true; + } + + return b; + } + + /** Reads for modifiers and creates integer with required mask. + * @param s string with modifiers + * @return integer with mask + * @exception NoSuchElementException if some letter is not modifier + */ + private static int readModifiers(String s) throws NoSuchElementException { + int m = 0; + + for (int i = 0; i < s.length(); i++) { + switch (s.charAt(i)) { + case 'C': + m |= KeyEvent.CTRL_MASK; + break; + + case 'A': + m |= KeyEvent.ALT_MASK; + break; + + case 'M': + m |= KeyEvent.META_MASK; + break; + + case 'S': + m |= KeyEvent.SHIFT_MASK; + break; + + case 'D': + m |= CTRL_WILDCARD_MASK; + break; + + case 'O': + m |= ALT_WILDCARD_MASK; + break; + + default: + throw new NoSuchElementException(s); + } + } + + return m; + } + + /** + * Convert an array of objects to an array of primitive types. + * E.g. an Integer[] would be changed to an int[]. + * @param array the wrapper array + * @return a primitive array + * @throws IllegalArgumentException if the array element type is not a primitive wrapper + */ + public static Object toPrimitiveArray(Object[] array) { + if (array instanceof Integer[]) { + int[] r = new int[array.length]; + int i; + int k = array.length; + + for (i = 0; i < k; i++) + r[i] = (array[i] == null) ? 0 : ((Integer) array[i]).intValue(); + + return r; + } + + if (array instanceof Boolean[]) { + boolean[] r = new boolean[array.length]; + int i; + int k = array.length; + + for (i = 0; i < k; i++) + r[i] = (array[i] != null) && ((Boolean) array[i]).booleanValue(); + + return r; + } + + if (array instanceof Byte[]) { + byte[] r = new byte[array.length]; + int i; + int k = array.length; + + for (i = 0; i < k; i++) + r[i] = (array[i] == null) ? 0 : ((Byte) array[i]).byteValue(); + + return r; + } + + if (array instanceof Character[]) { + char[] r = new char[array.length]; + int i; + int k = array.length; + + for (i = 0; i < k; i++) + r[i] = (array[i] == null) ? 0 : ((Character) array[i]).charValue(); + + return r; + } + + if (array instanceof Double[]) { + double[] r = new double[array.length]; + int i; + int k = array.length; + + for (i = 0; i < k; i++) + r[i] = (array[i] == null) ? 0 : ((Double) array[i]).doubleValue(); + + return r; + } + + if (array instanceof Float[]) { + float[] r = new float[array.length]; + int i; + int k = array.length; + + for (i = 0; i < k; i++) + r[i] = (array[i] == null) ? 0 : ((Float) array[i]).floatValue(); + + return r; + } + + if (array instanceof Long[]) { + long[] r = new long[array.length]; + int i; + int k = array.length; + + for (i = 0; i < k; i++) + r[i] = (array[i] == null) ? 0 : ((Long) array[i]).longValue(); + + return r; + } + + if (array instanceof Short[]) { + short[] r = new short[array.length]; + int i; + int k = array.length; + + for (i = 0; i < k; i++) + r[i] = (array[i] == null) ? 0 : ((Short) array[i]).shortValue(); + + return r; + } + + throw new IllegalArgumentException(); + } + + /** + * Convert an array of primitive types to an array of objects. + * E.g. an int[] would be turned into an Integer[]. + * @param array the primitive array + * @return a wrapper array + * @throws IllegalArgumentException if the array element type is not primitive + */ + public static Object[] toObjectArray(Object array) { + if (array instanceof Object[]) { + return (Object[]) array; + } + + if (array instanceof int[]) { + int i; + int k = ((int[]) array).length; + Integer[] r = new Integer[k]; + + for (i = 0; i < k; i++) + r[i] = ((int[]) array)[i]; + + return r; + } + + if (array instanceof boolean[]) { + int i; + int k = ((boolean[]) array).length; + Boolean[] r = new Boolean[k]; + + for (i = 0; i < k; i++) + r[i] = ((boolean[]) array)[i] ? Boolean.TRUE : Boolean.FALSE; + + return r; + } + + if (array instanceof byte[]) { + int i; + int k = ((byte[]) array).length; + Byte[] r = new Byte[k]; + + for (i = 0; i < k; i++) + r[i] = ((byte[]) array)[i]; + + return r; + } + + if (array instanceof char[]) { + int i; + int k = ((char[]) array).length; + Character[] r = new Character[k]; + + for (i = 0; i < k; i++) + r[i] = ((char[]) array)[i]; + + return r; + } + + if (array instanceof double[]) { + int i; + int k = ((double[]) array).length; + Double[] r = new Double[k]; + + for (i = 0; i < k; i++) + r[i] = ((double[]) array)[i]; + + return r; + } + + if (array instanceof float[]) { + int i; + int k = ((float[]) array).length; + Float[] r = new Float[k]; + + for (i = 0; i < k; i++) + r[i] = ((float[]) array)[i]; + + return r; + } + + if (array instanceof long[]) { + int i; + int k = ((long[]) array).length; + Long[] r = new Long[k]; + + for (i = 0; i < k; i++) + r[i] = ((long[]) array)[i]; + + return r; + } + + if (array instanceof short[]) { + int i; + int k = ((short[]) array).length; + Short[] r = new Short[k]; + + for (i = 0; i < k; i++) + r[i] = ((short[]) array)[i]; + + return r; + } + + throw new IllegalArgumentException(); + } + + /** Wrap multi-line strings (and get the individual lines). + * @param original the original string to wrap + * @param width the maximum width of lines + * @param breakIterator breaks original to chars, words, sentences, depending on what instance you provide. + * @param removeNewLines if true, any newlines in the original string are ignored + * @return the lines after wrapping + */ + public static String[] wrapStringToArray( + String original, int width, BreakIterator breakIterator, boolean removeNewLines + ) { + if (original.length() == 0) { + return new String[] { original }; + } + + String[] workingSet; + + // substitute original newlines with spaces, + // remove newlines from head and tail + if (removeNewLines) { + original = trimString(original); + original = original.replace('\n', ' '); + workingSet = new String[] { original }; + } else { + StringTokenizer tokens = new StringTokenizer(original, "\n"); // NOI18N + int len = tokens.countTokens(); + workingSet = new String[len]; + + for (int i = 0; i < len; i++) { + workingSet[i] = tokens.nextToken(); + } + } + + if (width < 1) { + width = 1; + } + + if (original.length() <= width) { + return workingSet; + } + +widthcheck: { + boolean ok = true; + + for (int i = 0; i < workingSet.length; i++) { + ok = ok && (workingSet[i].length() < width); + + if (!ok) { + break widthcheck; + } + } + + return workingSet; + } + + ArrayList lines = new ArrayList(); + + int lineStart = 0; // the position of start of currently processed line in the original string + + for (int i = 0; i < workingSet.length; i++) { + if (workingSet[i].length() < width) { + lines.add(workingSet[i]); + } else { + breakIterator.setText(workingSet[i]); + + int nextStart = breakIterator.next(); + int prevStart = 0; + + do { + while (((nextStart - lineStart) < width) && (nextStart != BreakIterator.DONE)) { + prevStart = nextStart; + nextStart = breakIterator.next(); + } + + if (nextStart == BreakIterator.DONE) { + nextStart = prevStart = workingSet[i].length(); + } + + if (prevStart == 0) { + prevStart = nextStart; + } + + lines.add(workingSet[i].substring(lineStart, prevStart)); + + lineStart = prevStart; + prevStart = 0; + } while (lineStart < workingSet[i].length()); + + lineStart = 0; + } + } + + String[] s = new String[lines.size()]; + + return (String[]) lines.toArray(s); + } + + private static String trimString(String s) { + int idx = 0; + char c; + final int slen = s.length(); + + if (slen == 0) { + return s; + } + + do { + c = s.charAt(idx++); + } while (((c == '\n') || (c == '\r')) && (idx < slen)); + + s = s.substring(--idx); + idx = s.length() - 1; + + if (idx < 0) { + return s; + } + + do { + c = s.charAt(idx--); + } while (((c == '\n') || (c == '\r')) && (idx >= 0)); + + return s.substring(0, idx + 2); + } +} diff --git a/src/main/java/org/jdesktop/swingx/util/WindowUtils.java b/src/main/java/org/jdesktop/swingx/util/WindowUtils.java new file mode 100644 index 0000000000..a1deee7ee3 --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/util/WindowUtils.java @@ -0,0 +1,168 @@ +/* + * $Id: WindowUtils.java 4084 2011-11-15 18:53:49Z kschaefe $ + * + * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.jdesktop.swingx.util; + +import javax.swing.*; +import java.awt.*; +import java.util.ArrayList; +import java.util.List; + +import static java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment; + +/** + * Encapsulates various utilities for windows (ie: Frame and + * Dialog objects and descendants, in particular). + * + * @author Richard Bair + */ +public final class WindowUtils { + /** + * Hide the constructor - don't wan't anybody creating an instance of this + */ + private WindowUtils() { + } + + private static GraphicsConfiguration getDefaultGraphicsConfiguration() { + return getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration(); + } + + private static boolean isUnowned(Window window) { + return window.getOwner() == null || (window instanceof JDialog && JOptionPane.getRootFrame().equals(window.getOwner())); + } + + private static Rectangle getUsableDeviceBounds(GraphicsConfiguration gc) { + Rectangle bounds = gc.getBounds(); + Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(gc); + + bounds.x += insets.left; + bounds.y += insets.top; + bounds.width -= (insets.left + insets.right); + bounds.height -= (insets.top + insets.bottom); + + return bounds; + } + + /** + *

                + * Returns the Point at which a window should be placed to + * center that window on the screen. + *

                + *

                + * Some thought was taken as to whether to implement a method such as this, + * or to simply make a method that, given a window, will center it. It was + * decided that it is better to not alter an object within a method. + *

                + * + * @param window The window to calculate the center point for. This object + * can not be null. + * + * @return the Point at which the window should be placed to + * center that window on the screen. + */ + public static Point getPointForCentering(Window window) { + Window w = window.isShowing() || isUnowned(window) ? window : window.getOwner(); + GraphicsConfiguration gc = w.getGraphicsConfiguration(); + + Rectangle usableBounds = getUsableDeviceBounds(gc); + int screenWidth = usableBounds.width; + int screenHeight = usableBounds.height; + int width = window.getWidth(); + int height = window.getHeight(); + + return new Point(((screenWidth - width) / 2) + usableBounds.x, + ((screenHeight - height) / 2) + usableBounds.y); + } + + /** + *

                + * Returns the Point at which a window should be placed to + * center that window on the given desktop. + *

                + *

                + * Some thought was taken as to whether to implement a method such as this, + * or to simply make a method that, given a window, will center it. It was + * decided that it is better to not alter an object within a method. + *

                + * + * @param window The window (JInternalFrame) to calculate the center point + * for. This object can not be null. + * + * @return the Point at which the window should be placed to + * center that window on the given desktop + */ + public static Point getPointForCentering(JInternalFrame window) { + Window w = SwingUtilities.getWindowAncestor(window); + GraphicsConfiguration gc = w == null ? getDefaultGraphicsConfiguration() + : w.getGraphicsConfiguration(); + + Rectangle usableBounds = getUsableDeviceBounds(gc); + int screenWidth = usableBounds.width; + int screenHeight = usableBounds.height; + int width = window.getWidth(); + int height = window.getHeight(); + + return new Point(((screenWidth - width) / 2) + usableBounds.x, + ((screenHeight - height) / 2) + usableBounds.y); + } + + /** + *

                + * Returns the Point at which a window should be placed in + * order to be staggered slightly from another "origin" window to + * ensure that the title areas of both windows remain visible to the user. + *

                + * + * @param originWindow Window from which the staggered location will be calculated + * + * @return location staggered from the upper left location of the origin + * window + */ + public static Point getPointForStaggering(Window originWindow) { + Point origin = originWindow.getLocation(); + Insets insets = originWindow.getInsets(); + origin.x += insets.top; + origin.y += insets.top; + return origin; + } + + public static Window findWindow(Component c) { + if (c == null) { + return JOptionPane.getRootFrame(); + } else if (c instanceof Window) { + return (Window) c; + } else { + return findWindow(c.getParent()); + } + } + + public static List getAllComponents(final Container c) { + Component[] comps = c.getComponents(); + List compList = new ArrayList(); + for (Component comp : comps) { + compList.add(comp); + if (comp instanceof Container) { + compList.addAll(getAllComponents((Container) comp)); + } + } + return compList; + } +} diff --git a/src/main/java/org/jdesktop/swingx/util/package-info.java b/src/main/java/org/jdesktop/swingx/util/package-info.java new file mode 100644 index 0000000000..28f75777ee --- /dev/null +++ b/src/main/java/org/jdesktop/swingx/util/package-info.java @@ -0,0 +1,37 @@ +/* + * $Id: package-info.java 4084 2011-11-15 18:53:49Z kschaefe $ + * + * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, + * Santa Clara, California 95054, U.S.A. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +/**Contains utility API required by JDNC's Swing Extensions. + +

                Package Specification

                + + + +

                Related Documentation

                + + + +*/ +package org.jdesktop.swingx.util; \ No newline at end of file diff --git a/src/main/kotlin/org/jdesktop/swingx/VerticalLayout.kt b/src/main/kotlin/org/jdesktop/swingx/VerticalLayout.kt deleted file mode 100644 index b1dbf105a6..0000000000 --- a/src/main/kotlin/org/jdesktop/swingx/VerticalLayout.kt +++ /dev/null @@ -1,82 +0,0 @@ -package org.jdesktop.swingx - -import java.awt.Component -import java.awt.Container -import java.awt.Dimension -import java.awt.LayoutManager -import java.io.Serializable -import kotlin.math.max - -abstract class AbstractLayoutManager : LayoutManager, Serializable { - override fun addLayoutComponent(name: String, comp: Component) { - //do nothing - } - - override fun removeLayoutComponent(comp: Component) { - // do nothing - } - - override fun minimumLayoutSize(parent: Container): Dimension { - return preferredLayoutSize(parent) - } - - companion object { - private const val serialVersionUID = 1446292747820044161L - } -} - -/** - * SwingX VerticalLayout implementation recreated in Kotlin. - * Unfortunately SwingX is not maintained anymore :( - */ -class VerticalLayout -@JvmOverloads constructor(var gap: Int = 0) : AbstractLayoutManager() { - internal class Separator(private var next: Int, private val separator: Int) { - fun get(): Int { - val result = next - next = separator - return result - } - } - - override fun preferredLayoutSize(parent: Container): Dimension { - val pref = Dimension(0, 0) - val sep = Separator(0, gap) - var i = 0 - val c = parent.componentCount - while (i < c) { - val m = parent.getComponent(i) - if (m.isVisible) { - val componentPreferredSize = parent.getComponent(i).preferredSize - pref.height += componentPreferredSize.height + sep.get() - pref.width = max(pref.width, componentPreferredSize.width) - } - i++ - } - val insets = parent.insets - pref.width += insets.left + insets.right - pref.height += insets.top + insets.bottom - return pref - } - - override fun layoutContainer(parent: Container) { - val insets = parent.insets - val size = parent.size - val width = size.width - insets.left - insets.right - var height = insets.top - var i = 0 - val c = parent.componentCount - while (i < c) { - val m = parent.getComponent(i) - if (m.isVisible) { - m.setBounds(insets.left, height, width, m.preferredSize.height) - height += m.size.height + gap - } - i++ - } - } - - companion object { - private const val serialVersionUID = 5342270033773736441L - } -} \ No newline at end of file From c7b4f0ab7e6c7cc787eaaeb12cf8da9d2a09ebba Mon Sep 17 00:00:00 2001 From: Christian F Date: Tue, 5 Jul 2022 17:59:58 +0200 Subject: [PATCH 117/422] - remove JavaFX media package Former-commit-id: 0e51a9cea03d420c806955d0c924fb811bc8e734 --- pom.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pom.xml b/pom.xml index 81a7868000..d20c60f728 100755 --- a/pom.xml +++ b/pom.xml @@ -1127,11 +1127,6 @@ javafx-controls ${javafx.version} - - org.openjfx - javafx-media - ${javafx.version} - org.openjfx javafx-swing From 860c0251e09223fddf8ed60c792fa84e834473f5 Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 6 Jul 2022 07:01:38 +0200 Subject: [PATCH 118/422] - convert to swing Former-commit-id: 0bfcb114696b181a8cc75a06a5d2cf42ad695907 --- src/main/java/mediathek/config/Daten.java | 64 +++++++++-------------- 1 file changed, 24 insertions(+), 40 deletions(-) diff --git a/src/main/java/mediathek/config/Daten.java b/src/main/java/mediathek/config/Daten.java index 7ae696b3f8..980cb12221 100644 --- a/src/main/java/mediathek/config/Daten.java +++ b/src/main/java/mediathek/config/Daten.java @@ -1,9 +1,6 @@ package mediathek.config; import com.google.common.util.concurrent.*; -import javafx.scene.control.Alert; -import javafx.scene.control.ButtonBar; -import javafx.scene.control.ButtonType; import mediathek.Main; import mediathek.SplashScreen; import mediathek.controller.IoXmlLesen; @@ -14,9 +11,6 @@ import mediathek.daten.blacklist.ListeBlacklist; import mediathek.filmlisten.FilmeLaden; import mediathek.javafx.bookmark.BookmarkDataList; -import mediathek.javafx.tool.JFXHiddenApplication; -import mediathek.javafx.tool.JavaFxUtils; -import mediathek.mainwindow.MediathekGui; import mediathek.tool.ReplaceList; import mediathek.tool.notification.INotificationCenter; import org.apache.logging.log4j.LogManager; @@ -24,6 +18,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.jetbrains.annotations.NotNull; +import javax.swing.*; import java.io.File; import java.io.IOException; import java.nio.file.Files; @@ -36,7 +31,6 @@ import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; -import java.util.Optional; import java.util.concurrent.ExecutionException; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.atomic.AtomicBoolean; @@ -257,6 +251,21 @@ private boolean load() { return ret; } + private boolean askForBackupRestore() { + var text = """ + Die Einstellungen sind beschädigt und können nicht geladen werden. + Soll versucht werden diese aus einem Backup wiederherzustellen? + """; + int answer = JOptionPane.showConfirmDialog(null, text, + Konstanten.PROGRAMMNAME, JOptionPane.YES_NO_OPTION); + if (answer == JOptionPane.YES_OPTION) + return true; + else { + logger.info("User will kein Backup laden."); + return false; + } + } + private boolean loadBackup() { boolean ret = false; @@ -270,27 +279,7 @@ private boolean loadBackup() { // dann gibts ein Backup logger.info("Es gibt ein Backup"); - var loadBackup = JavaFxUtils.invokeInFxThreadAndWait(() -> { - ButtonType btnYes = new ButtonType("Ja", ButtonBar.ButtonData.OK_DONE); - ButtonType btnNo = new ButtonType("Nein", ButtonBar.ButtonData.CANCEL_CLOSE); - Alert alert = new Alert(Alert.AlertType.WARNING, - "Die Einstellungen sind beschädigt und können nicht geladen werden. " - + "Soll versucht werden diese aus einem Backup wiederherzustellen?", - btnYes, - btnNo); - - alert.setTitle(Konstanten.PROGRAMMNAME); - alert.setHeaderText("Gesicherte Einstellungen laden"); - Optional result = alert.showAndWait(); - if (result.orElse(btnNo) == btnNo) { - logger.info("User will kein Backup laden."); - return false; - } else - return true; - } - ); - - if (loadBackup) { + if (askForBackupRestore()) { for (Path p : path) { // teils geladene Reste entfernen clearKonfig(); @@ -330,18 +319,13 @@ public void allesSpeichern() { Files.deleteIfExists(path1); } catch (IOException e) { logger.error("Die Einstellungen konnten nicht zurückgesetzt werden.", e); - if (MediathekGui.ui() != null) { - JavaFxUtils.invokeInFxThreadAndWait(() -> { - Alert alert = new Alert(Alert.AlertType.ERROR); - alert.setHeaderText("Fehler beim Zurücksetzen der Einstellungen"); - alert.setContentText("Die Einstellungen konnten nicht zurückgesetzt werden.\n" - + "Sie müssen jetzt das Programm beenden und dann den Ordner:\n" - + StandardLocations.getSettingsDirectory() + '\n' - + "von Hand löschen und dann das Programm wieder starten.\n\n" - + "Im Forum erhalten Sie weitere Hilfe."); - JFXHiddenApplication.showAlert(alert, MediathekGui.ui()); - }); - } + var msg = "Die Einstellungen konnten nicht zurückgesetzt werden.\n" + + "Sie müssen jetzt das Programm beenden und dann den Ordner:\n" + + StandardLocations.getSettingsDirectory() + '\n' + + "von Hand löschen und dann das Programm wieder starten.\n\n" + + "Im Forum erhalten Sie weitere Hilfe."; + JOptionPane.showMessageDialog(null, Konstanten.PROGRAMMNAME, + msg, JOptionPane.ERROR_MESSAGE); } } } From 7532d325f291b2252728709e947a7e2f924ebe32 Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 6 Jul 2022 07:21:15 +0200 Subject: [PATCH 119/422] - remove unnecessary timer event usage - code cleanup Former-commit-id: 609d9bbe8f6bcc5afcd4cfcc686110c315ddb52b --- .../java/mediathek/gui/tabs/AGuiTabPanel.java | 6 +++++ .../DownloadStartInfoProperty.java | 6 ----- .../gui/tabs/tab_downloads/GuiDownloads.java | 8 ++----- .../mediathek/gui/tabs/tab_film/GuiFilme.java | 24 +++++++------------ 4 files changed, 17 insertions(+), 27 deletions(-) diff --git a/src/main/java/mediathek/gui/tabs/AGuiTabPanel.java b/src/main/java/mediathek/gui/tabs/AGuiTabPanel.java index de661f6428..6f5cd62e99 100644 --- a/src/main/java/mediathek/gui/tabs/AGuiTabPanel.java +++ b/src/main/java/mediathek/gui/tabs/AGuiTabPanel.java @@ -3,7 +3,9 @@ import mediathek.config.Daten; import mediathek.controller.history.SeenHistoryController; import mediathek.daten.DatenFilm; +import mediathek.gui.messages.UpdateStatusBarLeftDisplayEvent; import mediathek.mainwindow.MediathekGui; +import mediathek.tool.MessageBus; import javax.swing.*; import java.awt.event.ActionEvent; @@ -25,6 +27,10 @@ public abstract class AGuiTabPanel extends JPanel { protected abstract Optional getCurrentlySelectedFilm(); + protected void updateStartInfoProperty() { + MessageBus.getMessageBus().publishAsync(new UpdateStatusBarLeftDisplayEvent()); + } + public abstract void installMenuEntries(JMenu menu); public class MarkFilmAsSeenAction extends AbstractAction { diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/DownloadStartInfoProperty.java b/src/main/java/mediathek/gui/tabs/tab_downloads/DownloadStartInfoProperty.java index d546cb141b..1d5715c3be 100644 --- a/src/main/java/mediathek/gui/tabs/tab_downloads/DownloadStartInfoProperty.java +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/DownloadStartInfoProperty.java @@ -2,7 +2,6 @@ import mediathek.config.Daten; import mediathek.daten.DownloadStartInfo; -import mediathek.gui.messages.TimerEvent; import mediathek.gui.messages.UpdateStatusBarLeftDisplayEvent; import mediathek.tool.MessageBus; import net.engio.mbassy.listener.Handler; @@ -25,11 +24,6 @@ private void handleLeftDisplayUpdate(UpdateStatusBarLeftDisplayEvent e) { setInfo(Daten.getInstance().getListeDownloads().getStarts()); } - @Handler - private void handleTimerEvent(TimerEvent e) { - setInfo(Daten.getInstance().getListeDownloads().getStarts()); - } - public DownloadStartInfo getInfo() { return info; } diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java index eca2e0e019..e9725563e3 100644 --- a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java @@ -684,7 +684,7 @@ private void handleDownloadProgressChanged(DownloadProgressChangedEvent e) { private void handleGeoStateChangedEvent(GeoStateChangedEvent e) { SwingUtilities.invokeLater(() -> { tabelle.fireTableDataChanged(true); - setInfo(); + updateStartInfoProperty(); }); } @@ -724,7 +724,7 @@ private synchronized void reloadTable() { daten.getListeDownloads().getModel(model, onlyAbos, onlyDownloads, onlyNotStarted, onlyStarted, onlyWaiting, onlyRun, onlyFinished); tabelle.setSpalten(); updateFilmData(); - setInfo(); + updateStartInfoProperty(); } @Handler @@ -1126,10 +1126,6 @@ public void stopAllWaitingDownloads() { daten.getListeDownloads().downloadAbbrechen(listeStopDownload); } - private void setInfo() { - MessageBus.getMessageBus().publishAsync(new UpdateStatusBarLeftDisplayEvent()); - } - private void updateFilmData() { if (isShowing()) { DatenFilm aktFilm = null; diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index 22ce346989..c927712f4b 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -346,7 +346,7 @@ private void onComponentShown() { mediathekGui.tabPaneIndexProperty().setValue(TabPaneIndex.FILME); updateFilmData(); - setInfoStatusbar(); + updateStartInfoProperty(); } public int getTableRowCount() { @@ -449,7 +449,7 @@ private void handleDownloadHistoryChangedEvent(DownloadHistoryChangedEvent e) { private void handleButtonStart(ButtonStartEvent e) { SwingUtilities.invokeLater(() -> { tabelle.fireTableDataChanged(true); - setInfoStatusbar(); + updateStartInfoProperty(); }); } @@ -465,7 +465,7 @@ private void handleBlacklistChangedEvent(BlacklistChangedEvent e) { @Handler private void handleStartEvent(StartEvent msg) { - SwingUtilities.invokeLater(this::setInfoStatusbar); + SwingUtilities.invokeLater(this::updateStartInfoProperty); } private synchronized void saveFilm(DatenPset pSet) { @@ -657,10 +657,6 @@ private void updateFilmData() { } } - private void setInfoStatusbar() { - MessageBus.getMessageBus().publishAsync(new UpdateStatusBarLeftDisplayEvent()); - } - private void setupActionListeners() { Platform.runLater(() -> { reloadTableDataTransition.setOnFinished(e -> { @@ -774,7 +770,7 @@ public void onSuccess(TableModel model) { SwingUtilities.invokeLater(() -> { tabelle.setModel(model); tabelle.setEnabled(true); - setInfoStatusbar(); + updateStartInfoProperty(); tabelle.setSpalten(); updateFilmData(); stopBeob = false; @@ -787,7 +783,7 @@ public void onFailure(@NotNull Throwable thrown) { logger.error("Model filtering failed!", thrown); SwingUtilities.invokeLater(() -> { tabelle.setEnabled(true); - setInfoStatusbar(); + updateStartInfoProperty(); tabelle.setSpalten(); updateFilmData(); stopBeob = false; @@ -1218,12 +1214,10 @@ private void buttonTable(int row, int column) { }); } case DatenFilm.FILM_AUFZEICHNEN -> saveFilm(null); - case DatenFilm.FILM_MERKEN -> { - getCurrentlySelectedFilm().ifPresent(film -> { - if (!film.isLivestream()) - bookmarkFilm(); - }); - } + case DatenFilm.FILM_MERKEN -> getCurrentlySelectedFilm().ifPresent(film -> { + if (!film.isLivestream()) + bookmarkFilm(); + }); } } } From 38c0428973ea3e521b04a407e8142198ed498cac Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 6 Jul 2022 08:07:30 +0200 Subject: [PATCH 120/422] - convert to swing Former-commit-id: e10ee7882eb09710b89f7329bf3e9f632f990e64 --- .../java/mediathek/gui/abo/ManageAboPanel.java | 6 +++--- .../dialogEinstellungen/PanelDateinamen.java | 2 +- .../gui/dialogEinstellungen/PanelPsetLang.java | 16 ++++++++-------- .../gui/tabs/tab_downloads/GuiDownloads.java | 6 +++--- .../mediathek/gui/tabs/tab_film/GuiFilme.java | 4 ++-- .../mediathek/tool/NoSelectionErrorDialog.java | 18 ++++++++---------- 6 files changed, 25 insertions(+), 27 deletions(-) diff --git a/src/main/java/mediathek/gui/abo/ManageAboPanel.java b/src/main/java/mediathek/gui/abo/ManageAboPanel.java index 70391f82b1..d290d6a20b 100644 --- a/src/main/java/mediathek/gui/abo/ManageAboPanel.java +++ b/src/main/java/mediathek/gui/abo/ManageAboPanel.java @@ -255,7 +255,7 @@ private void aboLoeschen() { daten.getListeAbo().aenderungMelden(); } else { - NoSelectionErrorDialog.show(); + NoSelectionErrorDialog.show(this); } } @@ -272,7 +272,7 @@ private void selectFirstRow() { public void editAbo() { // nichts selektiert if (tabelle.getSelectedRowCount() == 0) { - NoSelectionErrorDialog.show(); + NoSelectionErrorDialog.show(this); return; } @@ -335,7 +335,7 @@ private void changeAboActiveState(boolean ein) { daten.getListeAbo().aenderungMelden(); } else { - NoSelectionErrorDialog.show(); + NoSelectionErrorDialog.show(this); } } diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/PanelDateinamen.java b/src/main/java/mediathek/gui/dialogEinstellungen/PanelDateinamen.java index ced3647d77..49ea2025b3 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/PanelDateinamen.java +++ b/src/main/java/mediathek/gui/dialogEinstellungen/PanelDateinamen.java @@ -142,7 +142,7 @@ private void upDown(boolean auf) { tabelle.setRowSelectionInterval(neu, neu); tabelle.scrollRectToVisible(tabelle.getCellRect(neu, 0, true)); } else { - NoSelectionErrorDialog.show(); + NoSelectionErrorDialog.show(this); } } diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java b/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java index 9d1d0e3f76..28f7bfd477 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java +++ b/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java @@ -165,7 +165,7 @@ private void init() { tabelleProgramme(); } } else { - NoSelectionErrorDialog.show(); + NoSelectionErrorDialog.show(this); } }); @@ -176,7 +176,7 @@ private void init() { DatenProg prog = getPset().getListeProg().get(row); progNeueZeile(prog.copy()); } else { - NoSelectionErrorDialog.show(); + NoSelectionErrorDialog.show(this); } }); @@ -331,7 +331,7 @@ private void init() { tabellePset(); notifyProgramSetChanged(); } else { - NoSelectionErrorDialog.show(); + NoSelectionErrorDialog.show(this); } }); @@ -707,7 +707,7 @@ private void setAufAb(boolean auf) { tabellePset.scrollRectToVisible(tabellePset.getCellRect(neu, 0, false)); notifyProgramSetChanged(); } else { - NoSelectionErrorDialog.show(); + NoSelectionErrorDialog.show(this); } } @@ -739,7 +739,7 @@ private void setLoeschen() { notifyProgramSetChanged(); } } else { - NoSelectionErrorDialog.show(); + NoSelectionErrorDialog.show(this); } } @@ -775,7 +775,7 @@ private void setExport() { }); } } else { - NoSelectionErrorDialog.show(); + NoSelectionErrorDialog.show(this); } } @@ -796,7 +796,7 @@ private void progAufAb(boolean auf) { tabelleProgramme.setRowSelectionInterval(neu, neu); tabelleProgramme.scrollRectToVisible(tabelleProgramme.getCellRect(neu, 0, true)); } else { - NoSelectionErrorDialog.show(); + NoSelectionErrorDialog.show(this); } } @@ -907,7 +907,7 @@ private void eingabe() { notifyProgramSetChanged(); stopBeob = false; } else { - NoSelectionErrorDialog.show(); + NoSelectionErrorDialog.show(null); } } setNamePruefen(); diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java index e9725563e3..163efa68f7 100644 --- a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java @@ -772,7 +772,7 @@ private ArrayList getSelDownloads() { arrayDownloads.add(datenDownload); } } else { - NoSelectionErrorDialog.show(); + NoSelectionErrorDialog.show(this); } return arrayDownloads; } @@ -800,7 +800,7 @@ private DatenDownload getSelDownload() { if (row != -1) { datenDownload = (DatenDownload) tabelle.getModel().getValueAt(tabelle.convertRowIndexToModel(row), DatenDownload.DOWNLOAD_REF); } else { - NoSelectionErrorDialog.show(); + NoSelectionErrorDialog.show(this); } return datenDownload; } @@ -1155,7 +1155,7 @@ protected List getSelFilme() { } } } else { - NoSelectionErrorDialog.show(); + NoSelectionErrorDialog.show(this); } return arrayFilme; } diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index c927712f4b..5501e4ada1 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -578,7 +578,7 @@ public void showBookmarkWindow() { public void playerStarten(DatenPset pSet) { // Url mit Prognr. starten if (tabelle.getSelectedRow() == -1) { - NoSelectionErrorDialog.show(); + NoSelectionErrorDialog.show(this); } else if (pSet.istSpeichern()) { // wenn das pSet zum Speichern (über die Button) gewählt wurde, // weiter mit dem Dialog "Speichern" @@ -641,7 +641,7 @@ protected List getSelFilme() { arrayFilme.add(datenFilm); } } else { - NoSelectionErrorDialog.show(); + NoSelectionErrorDialog.show(this); } return arrayFilme; } diff --git a/src/main/java/mediathek/tool/NoSelectionErrorDialog.java b/src/main/java/mediathek/tool/NoSelectionErrorDialog.java index 89a098afe2..5ee29582b7 100644 --- a/src/main/java/mediathek/tool/NoSelectionErrorDialog.java +++ b/src/main/java/mediathek/tool/NoSelectionErrorDialog.java @@ -1,18 +1,16 @@ package mediathek.tool; -import javafx.application.Platform; -import javafx.scene.control.Alert; import mediathek.config.Konstanten; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; public class NoSelectionErrorDialog { - public static void show() { - Platform.runLater(() -> { - Alert alert = new Alert(Alert.AlertType.ERROR); - alert.setTitle(Konstanten.PROGRAMMNAME); - alert.setHeaderText("Befehl kann nicht ausgeführt werden."); - alert.setContentText("Sie haben keinen Tabelleneintrag ausgewählt."); - alert.show(); - }); + public static void show(@Nullable Component parent) { + JOptionPane.showMessageDialog(parent, "Der Befehl kann nicht ausgeführt werden.\n" + + "Sie haben keinen Tabelleneintrag ausgewählt.", + Konstanten.PROGRAMMNAME, JOptionPane.ERROR_MESSAGE); } } From 4ade00fcf7ed69c4ca7c9d48b6c45789a2f71ca1 Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 6 Jul 2022 09:49:39 +0200 Subject: [PATCH 121/422] - reduce text info output due to new status bar Former-commit-id: ca18484fabe4130c8b0d07e567cc7e4ae90cdd8e --- .../gui/tabs/tab_downloads/GuiDownloads.java | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java index 163efa68f7..3d5c99ced0 100644 --- a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java @@ -552,9 +552,6 @@ private void setInfoText() { String info = HEAD; - // Downloads - info += getInfoText(); - final var downloadInfos = daten.getDownloadInfos(); // Größe final long byteAlleDownloads = downloadInfos.getByteAlleDownloads(); @@ -589,27 +586,6 @@ private void setInfoText() { txtDownload.setText(info); } - private String getInfoText() { - String textLinks; - final var info = daten.getListeDownloads().getStarts(); - textLinks = "Downloads: " + info.total_starts + "
                "; - - if (info.hasValues()) { - textLinks += "( "; - textLinks += (info.running == 1) ? "1 läuft" : info.running + " laufen"; - textLinks += (info.initialized == 1) ? ", 1 wartet" : ", " + info.initialized + " warten"; - if (info.finished > 0) - textLinks += (info.finished == 1) ? ", 1 fertig" : ", " + info.finished + " fertig"; - - if (info.error > 0) - textLinks += (info.error == 1) ? ", 1 fehlerhaft" : ", " + info.error + " fehlerhaft"; - - textLinks += " )"; - } - textLinks += "
                "; - return textLinks; - } - @Handler private void handleRestartDownloadEvent(RestartDownloadEvent e) { reloadAndSave(); From f99eb363a700b2ef58fa991f3d5fce13f8ffdff2 Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 6 Jul 2022 10:14:24 +0200 Subject: [PATCH 122/422] - remove LoginPane - remove Motif L&F Former-commit-id: fdb984aa153dbf85f4042fc74e7dad9d952d2314 --- .../java/org/jdesktop/swingx/JXLoginPane.java | 1737 ----------------- .../swingx/plaf/AbstractComponentAddon.java | 19 - .../jdesktop/swingx/plaf/LoginPaneAddon.java | 98 - .../org/jdesktop/swingx/plaf/LoginPaneUI.java | 37 - .../swingx/plaf/basic/BasicLoginPaneUI.java | 159 -- .../plaf/motif/MotifLookAndFeelAddons.java | 38 - .../swingx/plaf/motif/package-info.java | 26 - 7 files changed, 2114 deletions(-) delete mode 100644 src/main/java/org/jdesktop/swingx/JXLoginPane.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/LoginPaneAddon.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/LoginPaneUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/BasicLoginPaneUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/motif/MotifLookAndFeelAddons.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/motif/package-info.java diff --git a/src/main/java/org/jdesktop/swingx/JXLoginPane.java b/src/main/java/org/jdesktop/swingx/JXLoginPane.java deleted file mode 100644 index 34ca7b0187..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXLoginPane.java +++ /dev/null @@ -1,1737 +0,0 @@ -/* - * $Id: JXLoginPane.java 4147 2012-02-01 17:13:24Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.action.AbstractActionExt; -import org.jdesktop.swingx.auth.*; -import org.jdesktop.swingx.autocomplete.AutoCompleteDecorator; -import org.jdesktop.swingx.painter.MattePainter; -import org.jdesktop.swingx.plaf.LoginPaneAddon; -import org.jdesktop.swingx.plaf.LoginPaneUI; -import org.jdesktop.swingx.plaf.LookAndFeelAddons; -import org.jdesktop.swingx.plaf.UIManagerExt; -import org.jdesktop.swingx.plaf.basic.CapsLockSupport; -import org.jdesktop.swingx.util.WindowUtils; - -import javax.swing.*; -import javax.swing.border.EmptyBorder; -import javax.swing.plaf.basic.BasicHTML; -import javax.swing.text.View; -import java.awt.*; -import java.awt.event.*; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Locale; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - *

                JXLoginPane is a specialized JPanel that implements a Login dialog with - * support for saving passwords supplied for future use in a secure - * manner. LoginService is invoked to perform authentication - * and optional PasswordStore can be provided to store the user - * login information.

                - * - *

                In order to perform the authentication, JXLoginPane - * calls the authenticate method of the LoginService - * . In order to perform the persistence of the password, - * JXLoginPane calls the put method of the - * PasswordStore object that is supplied. If - * the PasswordStore is null, then the password - * is not saved. Similarly, if a PasswordStore is - * supplied and the password is null, then the PasswordStore - * will be queried for the password using the get method. - * - * Example: - *

                - *         final JXLoginPane panel = new JXLoginPane(new LoginService() {
                - *                      public boolean authenticate(String name, char[] password,
                - *                                      String server) throws Exception {
                - *                              // perform authentication and return true on success.
                - *                              return false;
                - *                      }});
                - *      final JFrame frame = JXLoginPane.showLoginFrame(panel);
                - * 
                - * - * @author Bino George - * @author Shai Almog - * @author rbair - * @author Karl Schaefer - * @author rah003 - * @author Jonathan Giles - */ -@JavaBean -public class JXLoginPane extends JXPanel { - - /** - * The Logger - */ - private static final Logger LOG = Logger.getLogger(JXLoginPane.class.getName()); - /** - * Comment for serialVersionUID - */ - private static final long serialVersionUID = 3544949969896288564L; - /** - * UI Class ID - */ - public final static String uiClassID = "LoginPaneUI"; - /** - * Action key for an Action in the ActionMap that initiates the Login - * procedure - */ - public static final String LOGIN_ACTION_COMMAND = "login"; - /** - * Action key for an Action in the ActionMap that cancels the Login - * procedure - */ - public static final String CANCEL_LOGIN_ACTION_COMMAND = "cancel-login"; - /** - * The JXLoginPane can attempt to save certain user information such as - * the username, password, or both to their respective stores. - * This type specifies what type of save should be performed. - */ - public static enum SaveMode {NONE, USER_NAME, PASSWORD, BOTH} - /** - * Returns the status of the login process - */ - public enum Status {NOT_STARTED, IN_PROGRESS, FAILED, CANCELLED, SUCCEEDED} - /** - * Used as a prefix when pulling data out of UIManager for i18n - */ - private static String CLASS_NAME = JXLoginPane.class.getSimpleName(); - - /** - * The current login status for this panel - */ - private Status status = Status.NOT_STARTED; - /** - * An optional banner at the top of the panel - */ - private JXImagePanel banner; - /** - * Text that should appear on the banner - */ - private String bannerText; - /** - * Custom label allowing the developer to display some message to the user - */ - private JLabel messageLabel; - /** - * Shows an error message such as "user name or password incorrect" or - * "could not contact server" or something like that if something - * goes wrong - */ - private JXLabel errorMessageLabel; - /** - * A Panel containing all of the input fields, check boxes, etc necessary - * for the user to do their job. The items on this panel change whenever - * the SaveMode changes, so this panel must be recreated at runtime if the - * SaveMode changes. Thus, I must maintain this reference so I can remove - * this panel from the content panel at runtime. - */ - private JXPanel loginPanel; - /** - * The panel on which the input fields, messageLabel, and errorMessageLabel - * are placed. While the login thread is running, this panel is removed - * from the dialog and replaced by the progressPanel - */ - private JXPanel contentPanel; - /** - * This is the area in which the name field is placed. That way it can toggle on the fly - * between text field and a combo box depending on the situation, and have a simple - * way to get the user name - */ - private NameComponent namePanel; - /** - * The password field presented allowing the user to enter their password - */ - private JPasswordField passwordField; - /** - * A combo box presenting the user with a list of servers to which they - * may log in. This is an optional feature, which is only enabled if - * the List of servers supplied to the JXLoginPane has a length greater - * than 1. - */ - private JComboBox serverCombo; - /** - * Check box presented if a PasswordStore is used, allowing the user to decide whether to - * save their password - */ - private JCheckBox saveCB; - - /** - * Label displayed whenever caps lock is on. - */ - private JLabel capsOn; - /** - * A special panel that displays a progress bar and cancel button, and - * which notify the user of the login process, and allow them to cancel - * that process. - */ - private JXPanel progressPanel; - /** - * A JLabel on the progressPanel that is used for informing the user - * of the status of the login procedure (logging in..., canceling login...) - */ - private JLabel progressMessageLabel; - /** - * The LoginService to use. This must be specified for the login dialog to operate. - * If no LoginService is defined, a default login service is used that simply - * allows all users access. This is useful for demos or prototypes where a proper login - * server is not available. - */ - private LoginService loginService; - /** - * Optional: a PasswordStore to use for storing and retrieving passwords for a specific - * user. - */ - private PasswordStore passwordStore; - /** - * Optional: a UserNameStore to use for storing user names and retrieving them - */ - private UserNameStore userNameStore; - /** - * A list of servers where each server is represented by a String. If the - * list of Servers is greater than 1, then a combo box will be presented to - * the user to choose from. If any servers are specified, the selected one - * (or the only one if servers.size() == 1) will be passed to the LoginService - */ - private List servers; - /** - * Whether to save password or username or both. - */ - private SaveMode saveMode; - /** - * Tracks the cursor at the time that authentication was started, and restores to that - * cursor after authentication ends, or is canceled; - */ - private Cursor oldCursor; - - private boolean namePanelEnabled = true; - - /** - * The default login listener used by this panel. - */ - private LoginListener defaultLoginListener; - - /** - * Login/cancel control pane; - */ - private JXBtnPanel buttonPanel; - - /** - * Card pane holding user/pwd fields view and the progress view. - */ - private JPanel contentCardPane; - private boolean isErrorMessageSet; - - /** - * Creates a default JXLoginPane instance - */ - static { - LookAndFeelAddons.contribute(new LoginPaneAddon()); - } - - /** - * Populates UIDefaults with the localizable Strings we will use - * in the Login panel. - */ - private void reinitLocales(Locale l) { - // PENDING: JW - use the locale given as parameter - // as this probably (?) should be called before super.setLocale - setBannerText(UIManagerExt.getString(CLASS_NAME + ".bannerString", getLocale())); - banner.setImage(createLoginBanner()); - if (!isErrorMessageSet) { - errorMessageLabel.setText(UIManager.getString(CLASS_NAME + ".errorMessage", getLocale())); - } - progressMessageLabel.setText(UIManagerExt.getString(CLASS_NAME + ".pleaseWait", getLocale())); - recreateLoginPanel(); - Window w = SwingUtilities.getWindowAncestor(this); - if (w instanceof JXLoginFrame) { - JXLoginFrame f = (JXLoginFrame) w; - f.setTitle(UIManagerExt.getString(CLASS_NAME + ".titleString", getLocale())); - if (buttonPanel != null) { - buttonPanel.getOk().setText(UIManagerExt.getString(CLASS_NAME + ".loginString", getLocale())); - buttonPanel.getCancel().setText(UIManagerExt.getString(CLASS_NAME + ".cancelString", getLocale())); - } - } - JLabel lbl = (JLabel) passwordField.getClientProperty("labeledBy"); - if (lbl != null) { - lbl.setText(UIManagerExt.getString(CLASS_NAME + ".passwordString", getLocale())); - } - lbl = (JLabel) namePanel.getComponent().getClientProperty("labeledBy"); - if (lbl != null) { - lbl.setText(UIManagerExt.getString(CLASS_NAME + ".nameString", getLocale())); - } - if (serverCombo != null) { - lbl = (JLabel) serverCombo.getClientProperty("labeledBy"); - if (lbl != null) { - lbl.setText(UIManagerExt.getString(CLASS_NAME + ".serverString", getLocale())); - } - } - saveCB.setText(UIManagerExt.getString(CLASS_NAME + ".rememberPasswordString", getLocale())); - // by default, caps is initialized in off state - i.e. without warning. Setting to - // whitespace preserves formatting of the panel. - capsOn.setText(isCapsLockOn() ? UIManagerExt.getString(CLASS_NAME + ".capsOnWarning", getLocale()) : " "); - - getActionMap().get(LOGIN_ACTION_COMMAND).putValue(Action.NAME, UIManagerExt.getString(CLASS_NAME + ".loginString", getLocale())); - getActionMap().get(CANCEL_LOGIN_ACTION_COMMAND).putValue(Action.NAME, UIManagerExt.getString(CLASS_NAME + ".cancelString", getLocale())); - - } - - //--------------------------------------------------------- Constructors - /** - * Create a {@code JXLoginPane} that always accepts the user, never stores - * passwords or user ids, and has no target servers. - *

                - * This constructor should NOT be used in a real application. It is - * provided for compliance to the bean specification and for use with visual - * editors. - */ - public JXLoginPane() { - this(null); - } - - /** - * Create a {@code JXLoginPane} with the specified {@code LoginService} - * that does not store user ids or passwords and has no target servers. - * - * @param service - * the {@code LoginService} to use for logging in - */ - public JXLoginPane(LoginService service) { - this(service, null, null); - } - - /** - * Create a {@code JXLoginPane} with the specified {@code LoginService}, - * {@code PasswordStore}, and {@code UserNameStore}, but without a server - * list. - *

                - * If you do not want to store passwords or user ids, those parameters can - * be {@code null}. {@code SaveMode} is autoconfigured from passed in store - * parameters. - * - * @param service - * the {@code LoginService} to use for logging in - * @param passwordStore - * the {@code PasswordStore} to use for storing password - * information - * @param userStore - * the {@code UserNameStore} to use for storing user information - */ - public JXLoginPane(LoginService service, PasswordStore passwordStore, UserNameStore userStore) { - this(service, passwordStore, userStore, null); - } - - /** - * Create a {@code JXLoginPane} with the specified {@code LoginService}, - * {@code PasswordStore}, {@code UserNameStore}, and server list. - *

                - * If you do not want to store passwords or user ids, those parameters can - * be {@code null}. {@code SaveMode} is autoconfigured from passed in store - * parameters. - *

                - * Setting the server list to {@code null} will unset all of the servers. - * The server list is guaranteed to be non-{@code null}. - * - * @param service - * the {@code LoginService} to use for logging in - * @param passwordStore - * the {@code PasswordStore} to use for storing password - * information - * @param userStore - * the {@code UserNameStore} to use for storing user information - * @param servers - * a list of servers to authenticate against - */ - public JXLoginPane(LoginService service, PasswordStore passwordStore, UserNameStore userStore, List servers) { - setLoginService(service); - setPasswordStore(passwordStore); - setUserNameStore(userStore); - setServers(servers); - - - //create the login and cancel actions, and add them to the action map - getActionMap().put(LOGIN_ACTION_COMMAND, createLoginAction()); - getActionMap().put(CANCEL_LOGIN_ACTION_COMMAND, createCancelAction()); - - //initialize the save mode - if (passwordStore != null && userStore != null) { - saveMode = SaveMode.BOTH; - } else if (passwordStore != null) { - saveMode = SaveMode.PASSWORD; - } else if (userStore != null) { - saveMode = SaveMode.USER_NAME; - } else { - saveMode = SaveMode.NONE; - } - - // #732 set all internal components opacity to false in order to allow top level (frame's content pane) background painter to have any effect. - setOpaque(false); - CapsLockSupport.getInstance().addPropertyChangeListener("capsLockEnabled", new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - if (capsOn != null) { - if (Boolean.TRUE.equals(evt.getNewValue())) { - capsOn.setText(UIManagerExt.getString(CLASS_NAME + ".capsOnWarning", getLocale())); - } else { - capsOn.setText(" "); - } - } - } - }); - initComponents(); - } - - /** - * Gets current state of the caps lock as seen by the login panel. The state seen by the login - * panel and therefore returned by this method can be delayed in comparison to the real caps - * lock state and displayed by the keyboard light. This is usually the case when component or - * its text fields are not focused. - * - * @return True when caps lock is on, false otherwise. Returns always false when - * isCapsLockDetectionSupported() returns false. - */ - public boolean isCapsLockOn() { - return CapsLockSupport.getInstance().isCapsLockEnabled(); - } - - //------------------------------------------------------------- UI Logic - - /** - * {@inheritDoc} - */ - @Override - public LoginPaneUI getUI() { - return (LoginPaneUI) super.getUI(); - } - - /** - * Sets the look and feel (L&F) object that renders this component. - * - * @param ui the LoginPaneUI L&F object - * @see javax.swing.UIDefaults#getUI - */ - public void setUI(LoginPaneUI ui) { - // initialized here due to implicit updateUI call from JPanel - if (banner == null) { - banner = new JXImagePanel(); - } - if (errorMessageLabel == null) { - errorMessageLabel = new JXLabel(UIManagerExt.getString(CLASS_NAME + ".errorMessage", getLocale())); - } - super.setUI(ui); - banner.setImage(createLoginBanner()); - } - - /** - * Notification from the UIManager that the L&F has changed. - * Replaces the current UI object with the latest version from the - * UIManager. - * - * @see JComponent#updateUI - */ - @Override - public void updateUI() { - setUI((LoginPaneUI) LookAndFeelAddons.getUI(this, LoginPaneUI.class)); - } - - /** - * Returns the name of the L&F class that renders this component. - * - * @return the string {@link #uiClassID} - * @see JComponent#getUIClassID - * @see javax.swing.UIDefaults#getUI - */ - @Override - public String getUIClassID() { - return uiClassID; - } - - /** - * Recreates the login panel, and replaces the current one with the new one - */ - protected void recreateLoginPanel() { - JXPanel old = loginPanel; - loginPanel = createLoginPanel(); - loginPanel.setBorder(BorderFactory.createEmptyBorder(0, 36, 7, 11)); - contentPanel.remove(old); - contentPanel.add(loginPanel, 1); - } - - /** - * Creates and returns a new LoginPanel, based on the SaveMode state of - * the login panel. Whenever the SaveMode changes, the panel is recreated. - * I do this rather than hiding/showing components, due to a cleaner - * implementation (no invisible components, components are not sharing - * locations in the LayoutManager, etc). - */ - private JXPanel createLoginPanel() { - JXPanel loginPanel = new JXPanel(); - - JPasswordField oldPwd = passwordField; - //create the password component - passwordField = new JPasswordField("", 15); - JLabel passwordLabel = new JLabel(UIManagerExt.getString(CLASS_NAME + ".passwordString", getLocale())); - passwordLabel.setLabelFor(passwordField); - if (oldPwd != null) { - passwordField.setText(new String(oldPwd.getPassword())); - } - - NameComponent oldPanel = namePanel; - //create the NameComponent - if (saveMode == SaveMode.NONE) { - namePanel = new SimpleNamePanel(); - } else { - namePanel = new ComboNamePanel(); - } - if (oldPanel != null) { - // need to reset here otherwise value will get lost during LAF change as panel gets recreated. - namePanel.setUserName(oldPanel.getUserName()); - namePanel.setEnabled(oldPanel.isEnabled()); - namePanel.setEditable(oldPanel.isEditable()); - } else { - namePanel.setEnabled(namePanelEnabled); - namePanel.setEditable(namePanelEnabled); - } - JLabel nameLabel = new JLabel(UIManagerExt.getString(CLASS_NAME + ".nameString", getLocale())); - nameLabel.setLabelFor(namePanel.getComponent()); - - //create the server combo box if necessary - JLabel serverLabel = new JLabel(UIManagerExt.getString(CLASS_NAME + ".serverString", getLocale())); - if (servers.size() > 1) { - serverCombo = new JComboBox(servers.toArray()); - serverLabel.setLabelFor(serverCombo); - } else { - serverCombo = null; - } - - //create the save check box. By default, it is not selected - saveCB = new JCheckBox(UIManagerExt.getString(CLASS_NAME + ".rememberPasswordString", getLocale())); - saveCB.setIconTextGap(10); - //TODO should get this from preferences!!! And, it should be based on the user - saveCB.setSelected(false); - //determine whether to show/hide the save check box based on the SaveMode - saveCB.setVisible(saveMode == SaveMode.PASSWORD || saveMode == SaveMode.BOTH); - saveCB.setOpaque(false); - - capsOn = new JLabel(); - capsOn.setText(isCapsLockOn() ? UIManagerExt.getString(CLASS_NAME + ".capsOnWarning", getLocale()) : " "); - - int lShift = 3;// lShift is used to align all other components with the checkbox - GridLayout grid = new GridLayout(2,1); - grid.setVgap(5); - JPanel fields = new JPanel(grid); - fields.setOpaque(false); - fields.add(namePanel.getComponent()); - fields.add(passwordField); - - loginPanel.setLayout(new GridBagLayout()); - GridBagConstraints gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 0; - gridBagConstraints.anchor = GridBagConstraints.LINE_START; - gridBagConstraints.insets = new Insets(4, lShift, 5, 11); - loginPanel.add(nameLabel, gridBagConstraints); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 0; - gridBagConstraints.gridwidth = 1; - gridBagConstraints.gridheight = 2; - gridBagConstraints.anchor = GridBagConstraints.LINE_START; - gridBagConstraints.fill = GridBagConstraints.BOTH; - gridBagConstraints.weightx = 1.0; - gridBagConstraints.insets = new Insets(0, 0, 5, 0); - loginPanel.add(fields, gridBagConstraints); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 1; - gridBagConstraints.anchor = GridBagConstraints.LINE_START; - gridBagConstraints.insets = new Insets(5, lShift, 5, 11); - loginPanel.add(passwordLabel, gridBagConstraints); - - if (serverCombo != null) { - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 2; - gridBagConstraints.anchor = GridBagConstraints.LINE_START; - gridBagConstraints.insets = new Insets(0, lShift, 5, 11); - loginPanel.add(serverLabel, gridBagConstraints); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 2; - gridBagConstraints.gridwidth = 1; - gridBagConstraints.anchor = GridBagConstraints.LINE_START; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.weightx = 1.0; - gridBagConstraints.insets = new Insets(0, 0, 5, 0); - loginPanel.add(serverCombo, gridBagConstraints); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 3; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = GridBagConstraints.LINE_START; - gridBagConstraints.weightx = 1.0; - gridBagConstraints.insets = new Insets(0, 0, 4, 0); - loginPanel.add(saveCB, gridBagConstraints); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 4; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = GridBagConstraints.LINE_START; - gridBagConstraints.weightx = 1.0; - gridBagConstraints.insets = new Insets(0, lShift, 0, 11); - loginPanel.add(capsOn, gridBagConstraints); - } else { - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 2; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = GridBagConstraints.LINE_START; - gridBagConstraints.weightx = 1.0; - gridBagConstraints.insets = new Insets(0, 0, 4, 0); - loginPanel.add(saveCB, gridBagConstraints); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 3; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = GridBagConstraints.LINE_START; - gridBagConstraints.weightx = 1.0; - gridBagConstraints.insets = new Insets(0, lShift, 0, 11); - loginPanel.add(capsOn, gridBagConstraints); - } - loginPanel.setOpaque(false); - return loginPanel; - } - - /** - * This method adds functionality to support bidi languages within this - * component - */ - @Override - public void setComponentOrientation(ComponentOrientation orient) { - // this if is used to avoid needless creations of the image - if(orient != super.getComponentOrientation()) { - super.setComponentOrientation(orient); - banner.setImage(createLoginBanner()); - progressPanel.applyComponentOrientation(orient); - } - } - - /** - * Create all of the UI components for the login panel - */ - private void initComponents() { - //create the default banner - banner.setImage(createLoginBanner()); - - //create the default label - messageLabel = new JLabel(" "); - messageLabel.setOpaque(false); - messageLabel.setFont(messageLabel.getFont().deriveFont(Font.BOLD)); - - //create the main components - loginPanel = createLoginPanel(); - - //create the message and hyperlink and hide them - errorMessageLabel.setIcon(UIManager.getIcon(CLASS_NAME + ".errorIcon", getLocale())); - errorMessageLabel.setVerticalTextPosition(SwingConstants.TOP); - errorMessageLabel.setLineWrap(true); - errorMessageLabel.setPaintBorderInsets(false); - errorMessageLabel.setBackgroundPainter(new MattePainter(UIManager.getColor(CLASS_NAME + ".errorBackground", getLocale()), true)); - errorMessageLabel.setMaxLineSpan(320); - errorMessageLabel.setVisible(false); - - //aggregate the optional message label, content, and error label into - //the contentPanel - contentPanel = new JXPanel(new LoginPaneLayout()); - contentPanel.setOpaque(false); - messageLabel.setBorder(BorderFactory.createEmptyBorder(12, 12, 7, 11)); - contentPanel.add(messageLabel); - loginPanel.setBorder(BorderFactory.createEmptyBorder(0, 36, 7, 11)); - contentPanel.add(loginPanel); - errorMessageLabel.setBorder(UIManager.getBorder(CLASS_NAME + ".errorBorder", getLocale())); - contentPanel.add(errorMessageLabel); - - //create the progress panel - progressPanel = new JXPanel(new GridBagLayout()); - progressPanel.setOpaque(false); - progressMessageLabel = new JLabel(UIManagerExt.getString(CLASS_NAME + ".pleaseWait", getLocale())); - progressMessageLabel.setFont(UIManager.getFont(CLASS_NAME +".pleaseWaitFont", getLocale())); - JProgressBar pb = new JProgressBar(); - pb.setIndeterminate(true); - JButton cancelButton = new JButton(getActionMap().get(CANCEL_LOGIN_ACTION_COMMAND)); - progressPanel.add(progressMessageLabel, new GridBagConstraints(0, 0, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(12, 12, 11, 11), 0, 0)); - progressPanel.add(pb, new GridBagConstraints(0, 1, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 24, 11, 7), 0, 0)); - progressPanel.add(cancelButton, new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 11, 11), 0, 0)); - - //layout the panel - setLayout(new BorderLayout()); - add(banner, BorderLayout.NORTH); - contentCardPane = new JPanel(new CardLayout()); - contentCardPane.setOpaque(false); - contentCardPane.add(contentPanel, "0"); - contentCardPane.add(progressPanel, "1"); - add(contentCardPane, BorderLayout.CENTER); - - } - - private final class LoginPaneLayout extends VerticalLayout implements LayoutManager { - @Override - public Dimension preferredLayoutSize(Container parent) { - Insets insets = parent.getInsets(); - Dimension pref = new Dimension(0, 0); - int gap = getGap(); - for (int i = 0, c = parent.getComponentCount(); i < c; i++) { - Component m = parent.getComponent(i); - if (m.isVisible()) { - Dimension componentPreferredSize = m.getPreferredSize(); - // swingx-917 - don't let jlabel to force width due to long text - if (m instanceof JLabel) { - View view = (View) ((JLabel)m).getClientProperty(BasicHTML.propertyKey); - if (view != null) { - view.setSize(pref.width, m.getHeight()); - // get fresh preferred size since we have forced new size on label - componentPreferredSize = m.getPreferredSize(); - } - } else { - pref.width = Math.max(pref.width, componentPreferredSize.width); - } - pref.height += componentPreferredSize.height + gap; - } - } - - pref.width += insets.left + insets.right; - pref.height += insets.top + insets.bottom; - - return pref; - } - } - - /** - * Create and return an image to use for the Banner. This may be overridden - * to return any image you like - */ - protected Image createLoginBanner() { - return getUI() == null ? null : getUI().getBanner(); - } - - /** - * Create and return an Action for logging in - */ - protected Action createLoginAction() { - return new LoginAction(this); - } - - /** - * Create and return an Action for canceling login - */ - protected Action createCancelAction() { - return new CancelAction(this); - } - - //------------------------------------------------------ Bean Properties - //REMEMBER: when adding new methods, they need to fire property change events!!! - /** - * @return Returns the saveMode. - */ - public SaveMode getSaveMode() { - return saveMode; - } - - /** - * The save mode indicates whether the "save" password is checked by default. This method - * makes no difference if the passwordStore is null. - * - * @param saveMode The saveMode to set either SAVE_NONE, SAVE_PASSWORD or SAVE_USERNAME - */ - public void setSaveMode(SaveMode saveMode) { - if (this.saveMode != saveMode) { - SaveMode oldMode = getSaveMode(); - this.saveMode = saveMode; - recreateLoginPanel(); - firePropertyChange("saveMode", oldMode, getSaveMode()); - } - } - - public boolean isRememberPassword() { - return saveCB.isVisible() && saveCB.isSelected(); - } - - /** - * @return the List of servers - */ - public List getServers() { - return Collections.unmodifiableList(servers); - } - - /** - * Sets the list of servers. See the servers field javadoc for more info. - */ - public void setServers(List servers) { - //only at startup - if (this.servers == null) { - this.servers = servers == null ? new ArrayList() : servers; - } else if (this.servers != servers) { - List old = getServers(); - this.servers = servers == null ? new ArrayList() : servers; - recreateLoginPanel(); - firePropertyChange("servers", old, getServers()); - } - } - - private LoginListener getDefaultLoginListener() { - if (defaultLoginListener == null) { - defaultLoginListener = new LoginListenerImpl(); - } - - return defaultLoginListener; - } - - /** - * Sets the {@code LoginService} for this panel. Setting the login service - * to {@code null} will actually set the service to use - * {@code NullLoginService}. - * - * @param service - * the service to set. If {@code service == null}, then a - * {@code NullLoginService} is used. - */ - public void setLoginService(LoginService service) { - LoginService oldService = getLoginService(); - LoginService newService = service == null ? new NullLoginService() : service; - - //newService is guaranteed to be nonnull - if (!newService.equals(oldService)) { - if (oldService != null) { - oldService.removeLoginListener(getDefaultLoginListener()); - } - - loginService = newService; - this.loginService.addLoginListener(getDefaultLoginListener()); - - firePropertyChange("loginService", oldService, getLoginService()); - } - } - - /** - * Gets the LoginService for this panel. - * - * @return service service - */ - public LoginService getLoginService() { - return loginService; - } - - /** - * Sets the PasswordStore for this panel. - * - * @param store PasswordStore - */ - public void setPasswordStore(PasswordStore store) { - PasswordStore oldStore = getPasswordStore(); - PasswordStore newStore = store == null ? new NullPasswordStore() : store; - - //newStore is guaranteed to be nonnull - if (!newStore.equals(oldStore)) { - passwordStore = newStore; - - firePropertyChange("passwordStore", oldStore, getPasswordStore()); - } - } - - /** - * Gets the {@code UserNameStore} for this panel. - * - * @return the {@code UserNameStore} - */ - public UserNameStore getUserNameStore() { - return userNameStore; - } - - /** - * Sets the user name store for this panel. - * @param store - */ - public void setUserNameStore(UserNameStore store) { - UserNameStore oldStore = getUserNameStore(); - UserNameStore newStore = store == null ? new DefaultUserNameStore() : store; - - //newStore is guaranteed to be nonnull - if (!newStore.equals(oldStore)) { - userNameStore = newStore; - - firePropertyChange("userNameStore", oldStore, getUserNameStore()); - } - } - - /** - * Gets the PasswordStore for this panel. - * - * @return store PasswordStore - */ - public PasswordStore getPasswordStore() { - return passwordStore; - } - - /** - * Sets the User name for this panel. - * - * @param username User name - */ - public void setUserName(String username) { - if (namePanel != null) { - String old = getUserName(); - namePanel.setUserName(username); - firePropertyChange("userName", old, getUserName()); - } - } - - /** - * Enables or disables User name for this panel. - * - * @param enabled - */ - public void setUserNameEnabled(boolean enabled) { - boolean old = isUserNameEnabled(); - this.namePanelEnabled = enabled; - if (namePanel != null) { - namePanel.setEnabled(enabled); - namePanel.setEditable(enabled); - } - firePropertyChange("userNameEnabled", old, isUserNameEnabled()); - } - - /** - * Gets current state of the user name field. Field can be either disabled (false) for editing or enabled (true). - * @return True when user name field is enabled and editable, false otherwise. - */ - public boolean isUserNameEnabled() { - return this.namePanelEnabled; - } - - /** - * Gets the User name for this panel. - * @return the user name - */ - public String getUserName() { - return namePanel == null ? null : namePanel.getUserName(); - } - - /** - * Sets the Password for this panel. - * - * @param password Password - */ - public void setPassword(char[] password) { - passwordField.setText(new String(password)); - } - - /** - * Gets the Password for this panel. - * - * @return password Password - */ - public char[] getPassword() { - return passwordField.getPassword(); - } - - /** - * Return the image used as the banner - */ - public Image getBanner() { - return banner.getImage(); - } - - /** - * Set the image to use for the banner. If the {@code img} is {@code null}, - * then no image will be displayed. - * - * @param img - * the image to display - */ - public void setBanner(Image img) { - // we do not expose the ImagePanel, so we will produce property change - // events here - Image oldImage = getBanner(); - - if (oldImage != img) { - banner.setImage(img); - firePropertyChange("banner", oldImage, getBanner()); - } - } - - /** - * Set the text to use when creating the banner. If a custom banner image is - * specified, then this is ignored. If {@code text} is {@code null}, then - * no text is displayed. - * - * @param text - * the text to display - */ - public void setBannerText(String text) { - if (text == null) { - text = ""; - } - - if (!text.equals(this.bannerText)) { - String oldText = this.bannerText; - this.bannerText = text; - //fix the login banner - this.banner.setImage(createLoginBanner()); - firePropertyChange("bannerText", oldText, text); - } - } - - /** - * Returns text used when creating the banner - */ - public String getBannerText() { - return bannerText; - } - - /** - * Returns the custom message for this login panel - */ - public String getMessage() { - return messageLabel.getText(); - } - - /** - * Sets a custom message for this login panel - */ - public void setMessage(String message) { - String old = messageLabel.getText(); - messageLabel.setText(message); - firePropertyChange("message", old, messageLabel.getText()); - } - - /** - * Returns the error message for this login panel - */ - public String getErrorMessage() { - return errorMessageLabel.getText(); - } - - /** - * Sets the error message for this login panel - */ - public void setErrorMessage(String errorMessage) { - isErrorMessageSet = true; - String old = errorMessageLabel.getText(); - errorMessageLabel.setText(errorMessage); - firePropertyChange("errorMessage", old, errorMessageLabel.getText()); - } - - /** - * Returns the panel's status - */ - public Status getStatus() { - return status; - } - - /** - * Change the status - */ - protected void setStatus(Status newStatus) { - if (status != newStatus) { - Status oldStatus = status; - status = newStatus; - firePropertyChange("status", oldStatus, newStatus); - } - } - - @Override - public void setLocale(Locale l) { - super.setLocale(l); - reinitLocales(l); - } - //-------------------------------------------------------------- Methods - - /** - * Initiates the login procedure. This method is called internally by - * the LoginAction. This method handles cursor management, and actually - * calling the LoginService's startAuthentication method. Method will return - * immediately if asynchronous login is enabled or will block until - * authentication finishes if getSynchronous() returns true. - */ - protected void startLogin() { - oldCursor = getCursor(); - try { - setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - progressMessageLabel.setText(UIManagerExt.getString(CLASS_NAME + ".pleaseWait", getLocale())); - String name = getUserName(); - char[] password = getPassword(); - String server = servers.size() == 1 ? servers.get(0) : serverCombo == null ? null : (String)serverCombo.getSelectedItem(); - - loginService.startAuthentication(name, password, server); - } catch(Exception ex) { - //The status is set via the loginService listener, so no need to set - //the status here. Just log the error. - LOG.log(Level.WARNING, "Authentication exception while logging in", ex); - } finally { - setCursor(oldCursor); - } - } - - /** - * Cancels the login procedure. Handles cursor management and interfacing - * with the LoginService's cancelAuthentication method. Calling this method - * has an effect only when authentication is still in progress (i.e. after - * previous call to startAuthentications() and only when - * authentication is performed asynchronously (getSynchronous() - * returns false). - */ - protected void cancelLogin() { - progressMessageLabel.setText(UIManagerExt.getString(CLASS_NAME + ".cancelWait", getLocale())); - getActionMap().get(CANCEL_LOGIN_ACTION_COMMAND).setEnabled(false); - loginService.cancelAuthentication(); - setCursor(oldCursor); - } - - /** - * Puts the password into the password store. If password store is not set, method will do - * nothing. - */ - protected void savePassword() { - if (saveCB.isSelected() - && (saveMode == SaveMode.BOTH || saveMode == SaveMode.PASSWORD) - && passwordStore != null) { - passwordStore.set(getUserName(),getLoginService().getServer(),getPassword()); - } - } - - //--------------------------------------------- Listener Implementations - /* - - For Login (initiated in LoginAction): - 0) set the status - 1) Immediately disable the login action - 2) Immediately disable the close action (part of enclosing window) - 3) initialize the progress pane - a) enable the cancel login action - b) set the message text - 4) hide the content pane, show the progress pane - - When cancelling (initiated in CancelAction): - 0) set the status - 1) Disable the cancel login action - 2) Change the message text on the progress pane - - When cancel finishes (handled in LoginListener): - 0) set the status - 1) hide the progress pane, show the content pane - 2) enable the close action (part of enclosing window) - 3) enable the login action - - When login fails (handled in LoginListener): - 0) set the status - 1) hide the progress pane, show the content pane - 2) enable the close action (part of enclosing window) - 3) enable the login action - 4) Show the error message - 5) resize the window (part of enclosing window) - - When login succeeds (handled in LoginListener): - 0) set the status - 1) close the dialog/frame (part of enclosing window) - */ - /** - * Listener class to track state in the LoginService - */ - protected class LoginListenerImpl extends LoginAdapter { - @Override - public void loginSucceeded(LoginEvent source) { - //save the user names and passwords - String userName = namePanel.getUserName(); - if ((getSaveMode() == SaveMode.USER_NAME || getSaveMode() == SaveMode.BOTH) - && userName != null && !userName.trim().equals("")) { - userNameStore.addUserName(userName); - userNameStore.saveUserNames(); - } - - // if the user and/or password store knows of this user, - // and the checkbox is unchecked, we remove them, otherwise - // we save the password - if (saveCB.isSelected()) { - savePassword(); - } else { - // remove the password from the password store - if (passwordStore != null) { - passwordStore.removeUserPassword(userName); - } - } - - setStatus(Status.SUCCEEDED); - } - - @Override - public void loginStarted(LoginEvent source) { - assert EventQueue.isDispatchThread(); - getActionMap().get(LOGIN_ACTION_COMMAND).setEnabled(false); - getActionMap().get(CANCEL_LOGIN_ACTION_COMMAND).setEnabled(true); -// remove(contentPanel); -// add(progressPanel, BorderLayout.CENTER); - ((CardLayout) contentCardPane.getLayout()).last(contentCardPane); - revalidate(); - repaint(); - setStatus(Status.IN_PROGRESS); - } - - @Override - public void loginFailed(LoginEvent source) { - assert EventQueue.isDispatchThread(); -// remove(progressPanel); -// add(contentPanel, BorderLayout.CENTER); - ((CardLayout) contentCardPane.getLayout()).first(contentCardPane); - getActionMap().get(LOGIN_ACTION_COMMAND).setEnabled(true); - errorMessageLabel.setVisible(true); - revalidate(); - repaint(); - setStatus(Status.FAILED); - } - - @Override - public void loginCanceled(LoginEvent source) { - assert EventQueue.isDispatchThread(); -// remove(progressPanel); -// add(contentPanel, BorderLayout.CENTER); - ((CardLayout) contentCardPane.getLayout()).first(contentCardPane); - getActionMap().get(LOGIN_ACTION_COMMAND).setEnabled(true); - errorMessageLabel.setVisible(false); - revalidate(); - repaint(); - setStatus(Status.CANCELLED); - } - } - - //---------------------------------------------- Default Implementations - /** - * Action that initiates a login procedure. Delegates to JXLoginPane.startLogin - */ - private static final class LoginAction extends AbstractActionExt { - private static final long serialVersionUID = 7256761187925982485L; - private JXLoginPane panel; - public LoginAction(JXLoginPane p) { - super(UIManagerExt.getString(CLASS_NAME + ".loginString", p.getLocale()), LOGIN_ACTION_COMMAND); - this.panel = p; - } - @Override - public void actionPerformed(ActionEvent e) { - panel.startLogin(); - } - @Override - public void itemStateChanged(ItemEvent e) {} - } - - /** - * Action that cancels the login procedure. - */ - private static final class CancelAction extends AbstractActionExt { - private static final long serialVersionUID = 4040029973355439229L; - private JXLoginPane panel; - public CancelAction(JXLoginPane p) { - super(UIManagerExt.getString(CLASS_NAME + ".cancelLogin", p.getLocale()), CANCEL_LOGIN_ACTION_COMMAND); - this.panel = p; - this.setEnabled(false); - } - @Override - public void actionPerformed(ActionEvent e) { - panel.cancelLogin(); - } - @Override - public void itemStateChanged(ItemEvent e) {} - } - - /** - * Simple login service that allows everybody to login. This is useful in demos and allows - * us to avoid having to check for LoginService being null - */ - private static final class NullLoginService extends LoginService { - @Override - public boolean authenticate(String name, char[] password, String server) throws Exception { - return true; - } - - @Override - public boolean equals(Object obj) { - return obj instanceof NullLoginService; - } - - @Override - public int hashCode() { - return 7; - } - } - - /** - * Simple PasswordStore that does not remember passwords - */ - private static final class NullPasswordStore extends PasswordStore { - @Override - public boolean set(String username, String server, char[] password) { - //null op - return false; - } - - @Override - public char[] get(String username, String server) { - return new char[0]; - } - - @Override - public void removeUserPassword(String username) { - return; - } - - @Override - public boolean equals(Object obj) { - return obj instanceof NullPasswordStore; - } - - @Override - public int hashCode() { - return 7; - } - } - - //--------------------------------- Default NamePanel Implementations - private static interface NameComponent { - public String getUserName(); - public boolean isEnabled(); - public boolean isEditable(); - public void setEditable(boolean enabled); - public void setEnabled(boolean enabled); - public void setUserName(String userName); - public JComponent getComponent(); - } - - private void updatePassword(final String username) { - String password = ""; - if (username != null) { - char[] pw = passwordStore.get(username, null); - password = pw == null ? "" : new String(pw); - - // if the userstore has this username, we should change the - // 'remember me' checkbox to be selected. Unselecting this will - // result in the user being 'forgotten'. - saveCB.setSelected(userNameStore.containsUserName(username)); - } - - passwordField.setText(password); - } - - /** - * If a UserNameStore is not used, then this text field is presented allowing the user - * to simply enter their user name - */ - private final class SimpleNamePanel extends JTextField implements NameComponent { - private static final long serialVersionUID = 6513437813612641002L; - - public SimpleNamePanel() { - super("", 15); - - // auto-complete based on the users input - // AutoCompleteDecorator.decorate(this, Arrays.asList(userNameStore.getUserNames()), false); - - // listen to text input, and offer password suggestion based on current - // text - if (passwordStore != null && passwordField!=null) { - addKeyListener(new KeyAdapter() { - @Override - public void keyReleased(KeyEvent e) { - updatePassword(getText()); - } - }); - } - } - - @Override - public String getUserName() { - return getText(); - } - @Override - public void setUserName(String userName) { - setText(userName); - } - @Override - public JComponent getComponent() { - return this; - } - } - - /** - * If a UserNameStore is used, then this combo box is presented allowing the user - * to select a previous login name, or type in a new login name - */ - private final class ComboNamePanel extends JComboBox implements NameComponent { - private static final long serialVersionUID = 2511649075486103959L; - - public ComboNamePanel() { - super(); - setModel(new NameComboBoxModel()); - setEditable(true); - - // auto-complete based on the users input - AutoCompleteDecorator.decorate(this); - - // listen to selection or text input, and offer password suggestion based on current - // text - if (passwordStore != null && passwordField!=null) { - final JTextField textfield = (JTextField) getEditor().getEditorComponent(); - textfield.addKeyListener(new KeyAdapter() { - @Override - public void keyReleased(KeyEvent e) { - updatePassword(textfield.getText()); - } - }); - - super.addItemListener(new ItemListener() { - @Override - public void itemStateChanged(ItemEvent e) { - updatePassword((String)getSelectedItem()); - } - }); - } - } - - @Override - public String getUserName() { - Object item = getModel().getSelectedItem(); - return item == null ? null : item.toString(); - } - @Override - public void setUserName(String userName) { - getModel().setSelectedItem(userName); - } - public void setUserNames(String[] names) { - setModel(new DefaultComboBoxModel(names)); - } - @Override - public JComponent getComponent() { - return this; - } - - private final class NameComboBoxModel extends AbstractListModel implements ComboBoxModel { - private static final long serialVersionUID = 7097674687536018633L; - private Object selectedItem; - @Override - public void setSelectedItem(Object anItem) { - selectedItem = anItem; - fireContentsChanged(this, -1, -1); - } - @Override - public Object getSelectedItem() { - return selectedItem; - } - @Override - public Object getElementAt(int index) { - if (index == -1) { - return null; - } - - return userNameStore.getUserNames()[index]; - } - @Override - public int getSize() { - return userNameStore.getUserNames().length; - } - } - } - - //------------------------------------------ Static Construction Methods - /** - * Shows a login dialog. This method blocks. - * @return The status of the login operation - */ - public static Status showLoginDialog(Component parent, LoginService svc) { - return showLoginDialog(parent, svc, null, null); - } - - /** - * Shows a login dialog. This method blocks. - * @return The status of the login operation - */ - public static Status showLoginDialog(Component parent, LoginService svc, PasswordStore ps, UserNameStore us) { - return showLoginDialog(parent, svc, ps, us, null); - } - - /** - * Shows a login dialog. This method blocks. - * @return The status of the login operation - */ - public static Status showLoginDialog(Component parent, LoginService svc, PasswordStore ps, UserNameStore us, List servers) { - JXLoginPane panel = new JXLoginPane(svc, ps, us, servers); - return showLoginDialog(parent, panel); - } - - /** - * Shows a login dialog. This method blocks. - * @return The status of the login operation - */ - public static Status showLoginDialog(Component parent, JXLoginPane panel) { - Window w = WindowUtils.findWindow(parent); - JXLoginDialog dlg = null; - if (w == null) { - dlg = new JXLoginDialog((Frame)null, panel); - } else if (w instanceof Dialog) { - dlg = new JXLoginDialog((Dialog)w, panel); - } else if (w instanceof Frame) { - dlg = new JXLoginDialog((Frame)w, panel); - } else { - throw new AssertionError("Shouldn't be able to happen"); - } - dlg.setVisible(true); - return dlg.getStatus(); - } - - /** - * Shows a login frame. A JFrame is not modal, and thus does not block - */ - public static JXLoginFrame showLoginFrame(LoginService svc) { - return showLoginFrame(svc, null, null); - } - - /** - */ - public static JXLoginFrame showLoginFrame(LoginService svc, PasswordStore ps, UserNameStore us) { - return showLoginFrame(svc, ps, us, null); - } - - /** - */ - public static JXLoginFrame showLoginFrame(LoginService svc, PasswordStore ps, UserNameStore us, List servers) { - JXLoginPane panel = new JXLoginPane(svc, ps, us, servers); - return showLoginFrame(panel); - } - - /** - */ - public static JXLoginFrame showLoginFrame(JXLoginPane panel) { - return new JXLoginFrame(panel); - } - - public static final class JXLoginDialog extends JDialog { - private static final long serialVersionUID = -3185639594267828103L; - private JXLoginPane panel; - - public JXLoginDialog(Frame parent, JXLoginPane p) { - super(parent, true); - init(p); - } - - public JXLoginDialog(Dialog parent, JXLoginPane p) { - super(parent, true); - init(p); - } - - protected void init(JXLoginPane p) { - setTitle(UIManagerExt.getString(CLASS_NAME + ".titleString", getLocale())); - this.panel = p; - initWindow(this, panel); - } - - public Status getStatus() { - return panel.getStatus(); - } - } - - public static final class JXLoginFrame extends JXFrame { - private static final long serialVersionUID = -9016407314342050807L; - private JXLoginPane panel; - - public JXLoginFrame(JXLoginPane p) { - super(UIManagerExt.getString(CLASS_NAME + ".titleString", p.getLocale())); - JXPanel cp = new JXPanel(); - cp.setOpaque(true); - setContentPane(cp); - this.panel = p; - initWindow(this, panel); - } - - @Override - public JXPanel getContentPane() { - return (JXPanel) super.getContentPane(); - } - - public Status getStatus() { - return panel.getStatus(); - } - - public JXLoginPane getPanel() { - return panel; - } - } - - /** - * Utility method for initializing a Window for displaying a LoginDialog. - * This is particularly useful because the differences between JFrame and - * JDialog are so minor. - * - * Note: This method is package private for use by JXLoginDialog (proper, - * not JXLoginPane.JXLoginDialog). Change to private if JXLoginDialog is - * removed. - */ - static void initWindow(final Window w, final JXLoginPane panel) { - w.setLayout(new BorderLayout()); - w.add(panel, BorderLayout.CENTER); - JButton okButton = new JButton(panel.getActionMap().get(LOGIN_ACTION_COMMAND)); - final JButton cancelButton = new JButton( - UIManagerExt.getString(CLASS_NAME + ".cancelString", panel.getLocale())); - cancelButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - //change panel status to canceled! - panel.status = Status.CANCELLED; - w.setVisible(false); - w.dispose(); - } - }); - panel.addPropertyChangeListener("status", new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - Status status = (Status)evt.getNewValue(); - switch (status) { - case NOT_STARTED: - break; - case IN_PROGRESS: - cancelButton.setEnabled(false); - break; - case CANCELLED: - cancelButton.setEnabled(true); - w.pack(); - break; - case FAILED: - cancelButton.setEnabled(true); - panel.passwordField.requestFocusInWindow(); - w.pack(); - break; - case SUCCEEDED: - w.setVisible(false); - w.dispose(); - } - for (PropertyChangeListener l : w.getPropertyChangeListeners("status")) { - PropertyChangeEvent pce = new PropertyChangeEvent(w, "status", evt.getOldValue(), evt.getNewValue()); - l.propertyChange(pce); - } - } - }); - // FIX for #663 - commented out two lines below. Not sure why they were here in a first place. - // cancelButton.setText(UIManager.getString(CLASS_NAME + ".cancelString")); - // okButton.setText(UIManager.getString(CLASS_NAME + ".loginString")); - JXBtnPanel buttonPanel = new JXBtnPanel(okButton, cancelButton); - buttonPanel.setOpaque(false); - panel.setButtonPanel(buttonPanel); - JXPanel controls = new JXPanel(new FlowLayout(FlowLayout.RIGHT)); - controls.setOpaque(false); - new BoxLayout(controls, BoxLayout.X_AXIS); - controls.add(Box.createHorizontalGlue()); - controls.add(buttonPanel); - w.add(controls, BorderLayout.SOUTH); - w.addWindowListener(new WindowAdapter() { - @Override - public void windowClosing(java.awt.event.WindowEvent e) { - panel.cancelLogin(); - } - }); - - if (w instanceof JFrame) { - final JFrame f = (JFrame)w; - f.getRootPane().setDefaultButton(okButton); - f.setResizable(false); - f.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); - KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); - ActionListener closeAction = new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - f.setVisible(false); - f.dispose(); - } - }; - f.getRootPane().registerKeyboardAction(closeAction, ks, JComponent.WHEN_IN_FOCUSED_WINDOW); - } else if (w instanceof JDialog) { - final JDialog d = (JDialog)w; - d.getRootPane().setDefaultButton(okButton); - d.setResizable(false); - KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); - ActionListener closeAction = new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - d.setVisible(false); - } - }; - d.getRootPane().registerKeyboardAction(closeAction, ks, JComponent.WHEN_IN_FOCUSED_WINDOW); - } - w.pack(); - w.setLocation(WindowUtils.getPointForCentering(w)); - } - - private void setButtonPanel(JXBtnPanel buttonPanel) { - this.buttonPanel = buttonPanel; - } - - private static class JXBtnPanel extends JXPanel { - private static final long serialVersionUID = 4136611099721189372L; - private JButton cancel; - private JButton ok; - - public JXBtnPanel(JButton okButton, JButton cancelButton) { - GridLayout layout = new GridLayout(1,2); - layout.setHgap(5); - setLayout(layout); - this.ok = okButton; - this.cancel = cancelButton; - add(okButton); - add(cancelButton); - setBorder(new EmptyBorder(0,0,7,11)); - } - - /** - * @return the cancel button. - */ - public JButton getCancel() { - return cancel; - } - - /** - * @return the ok button. - */ - public JButton getOk() { - return ok; - } - - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/AbstractComponentAddon.java b/src/main/java/org/jdesktop/swingx/plaf/AbstractComponentAddon.java index cd57e195e9..daa2a2f8b9 100644 --- a/src/main/java/org/jdesktop/swingx/plaf/AbstractComponentAddon.java +++ b/src/main/java/org/jdesktop/swingx/plaf/AbstractComponentAddon.java @@ -23,7 +23,6 @@ import org.jdesktop.swingx.plaf.linux.LinuxLookAndFeelAddons; import org.jdesktop.swingx.plaf.macosx.MacOSXLookAndFeelAddons; import org.jdesktop.swingx.plaf.metal.MetalLookAndFeelAddons; -import org.jdesktop.swingx.plaf.motif.MotifLookAndFeelAddons; import org.jdesktop.swingx.plaf.nimbus.NimbusLookAndFeelAddons; import org.jdesktop.swingx.plaf.windows.WindowsLookAndFeelAddons; @@ -170,10 +169,6 @@ private Object[] getDefaults(LookAndFeelAddons addon) { addMetalDefaults(addon, defaults); } else if (isMac(addon)) { addMacDefaults(addon, defaults); - } else if (isMotif(addon)) { - addMotifDefaults(addon, defaults); - // PENDING JW: the separation line here looks fishy - // what about Nimbus on Linux systems? } else if (isLinux(addon)) { addLinuxDefaults(addon, defaults); } else if (isNimbus(addon)) { @@ -210,13 +205,6 @@ protected boolean isMac(LookAndFeelAddons addon) { return addon instanceof MacOSXLookAndFeelAddons; } - /** - * @return true if the addon is the Motif addon or its subclasses - */ - protected boolean isMotif(LookAndFeelAddons addon) { - return addon instanceof MotifLookAndFeelAddons; - } - /** * @return true if the current look and feel is Linux */ @@ -239,11 +227,4 @@ protected boolean isPlastic() { .contains("Plastic"); } - /** - * @return true if the current look and feel is Synth l&f - */ - protected boolean isSynth() { - return UIManager.getLookAndFeel().getClass().getName().contains("ynth"); - } - } diff --git a/src/main/java/org/jdesktop/swingx/plaf/LoginPaneAddon.java b/src/main/java/org/jdesktop/swingx/plaf/LoginPaneAddon.java deleted file mode 100644 index f091ac21c8..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/LoginPaneAddon.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * $Id: LoginPaneAddon.java 3033 2008-08-12 05:05:44Z rah003 $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.plaf; - -import org.jdesktop.swingx.JXLoginPane; - -import javax.swing.*; -import javax.swing.plaf.BorderUIResource; -import javax.swing.plaf.ColorUIResource; -import javax.swing.plaf.FontUIResource; -import javax.swing.plaf.metal.MetalLookAndFeel; -import java.awt.*; - -/** - * - * @author rbair - */ -public class LoginPaneAddon extends AbstractComponentAddon { - - /** Creates a new instance of LoginPaneAddon */ - public LoginPaneAddon() { - super("JXLoginPane"); - } - - @Override - protected void addBasicDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addBasicDefaults(addon, defaults); - Color errorBG = new Color(255, 215, 215); - - defaults.add(JXLoginPane.uiClassID, "org.jdesktop.swingx.plaf.basic.BasicLoginPaneUI"); - defaults.add("JXLoginPane.errorIcon", - LookAndFeel.makeIcon(LoginPaneAddon.class, "basic/resources/error16.png")); - defaults.add("JXLoginPane.bannerFont", new FontUIResource("Arial Bold", Font.PLAIN, 36)); - //#911 Not every LAF has Label.font defined ... - Font labelFont = UIManager.getFont("Label.font"); - Font boldLabel = labelFont != null ? labelFont.deriveFont(Font.BOLD) : new Font("SansSerif", Font.BOLD, 12); - defaults.add("JXLoginPane.pleaseWaitFont", - new FontUIResource(boldLabel)); - defaults.add("JXLoginPane.bannerForeground", new ColorUIResource(Color.WHITE)); - defaults.add("JXLoginPane.bannerDarkBackground", new ColorUIResource(Color.GRAY)); - defaults.add("JXLoginPane.bannerLightBackground", new ColorUIResource(Color.LIGHT_GRAY)); - defaults.add("JXLoginPane.errorBackground", new ColorUIResource(errorBG)); - defaults.add("JXLoginPane.errorBorder", - new BorderUIResource(BorderFactory.createCompoundBorder( - BorderFactory.createEmptyBorder(0, 36, 0, 11), - BorderFactory.createCompoundBorder( - BorderFactory.createLineBorder(Color.GRAY.darker()), - BorderFactory.createMatteBorder(5, 7, 5, 5, errorBG))))); - - UIManagerExt.addResourceBundle( - "org.jdesktop.swingx.plaf.basic.resources.LoginPane"); - } - - @Override - protected void addMetalDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addMetalDefaults(addon, defaults); - - if (isPlastic()) { - defaults.add("JXLoginPane.bannerForeground", new ColorUIResource(Color.WHITE)); - defaults.add("JXLoginPane.bannerDarkBackground", new ColorUIResource(Color.GRAY)); - defaults.add("JXLoginPane.bannerLightBackground", new ColorUIResource(Color.LIGHT_GRAY)); - } else { - defaults.add("JXLoginPane.bannerForeground", new ColorUIResource(Color.WHITE)); - defaults.add("JXLoginPane.bannerDarkBackground", - MetalLookAndFeel.getCurrentTheme().getPrimaryControlDarkShadow()); - defaults.add("JXLoginPane.bannerLightBackground", - MetalLookAndFeel.getCurrentTheme().getPrimaryControl()); - } - } - - @Override - protected void addWindowsDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addWindowsDefaults(addon, defaults); - - defaults.add("JXLoginPane.bannerForeground", new ColorUIResource(Color.WHITE)); - defaults.add("JXLoginPane.bannerDarkBackground", new ColorUIResource(49, 121, 242)); - defaults.add("JXLoginPane.bannerLightBackground", new ColorUIResource(198, 211, 247)); - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/LoginPaneUI.java b/src/main/java/org/jdesktop/swingx/plaf/LoginPaneUI.java deleted file mode 100644 index c7ef73e420..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/LoginPaneUI.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * $Id: LoginPaneUI.java 3472 2009-08-27 13:12:42Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.plaf; - -import javax.swing.plaf.PanelUI; -import java.awt.*; - -/** - * - * @author rbair - */ -public abstract class LoginPaneUI extends PanelUI { - /** - * @return The Image to use as the banner for the JXLoginPane. If - * this method returns null, then no banner will be shown. - */ - public abstract Image getBanner(); -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicLoginPaneUI.java b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicLoginPaneUI.java deleted file mode 100644 index e1fac1cf59..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicLoginPaneUI.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * $Id: BasicLoginPaneUI.java 4082 2011-11-15 18:39:43Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf.basic; - -import org.jdesktop.swingx.JXLoginPane; -import org.jdesktop.swingx.plaf.LoginPaneUI; -import org.jdesktop.swingx.plaf.UIManagerExt; -import org.jdesktop.swingx.util.GraphicsUtilities; - -import javax.swing.*; -import javax.swing.plaf.ComponentUI; -import java.awt.*; -import java.awt.geom.GeneralPath; -import java.awt.image.BufferedImage; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -/** - * Base implementation of the JXLoginPane UI. - * - * @author rbair - */ -public class BasicLoginPaneUI extends LoginPaneUI { - private class LocaleHandler implements PropertyChangeListener { - /** - * {@inheritDoc} - */ - @Override - public void propertyChange(PropertyChangeEvent evt) { - Object src = evt.getSource(); - - if (src instanceof JComponent) { - ((JComponent) src).updateUI(); - } - } - } - - private JXLoginPane dlg; - - /** Creates a new instance of BasicLoginDialogUI */ - public BasicLoginPaneUI(JXLoginPane dlg) { - this.dlg = dlg; -// dlg.addPropertyChangeListener("locale", new LocaleHandler()); - } - - public static ComponentUI createUI(JComponent c) { - return new BasicLoginPaneUI((JXLoginPane)c); - } - - @Override - public void installUI(JComponent c) { - installDefaults(); - } - - protected void installDefaults() { - String s = dlg.getBannerText(); - if (s == null || s.equals("")) { - dlg.setBannerText(UIManagerExt.getString("JXLoginPane.bannerString", dlg.getLocale())); - } - - s = dlg.getErrorMessage(); - if (s == null || s.equals("")) { - dlg.setErrorMessage(UIManagerExt.getString("JXLoginPane.errorMessage", dlg.getLocale())); - } - } - - /** - * Creates default 400x60 banner for the login panel. - * @see LoginPaneUI#getBanner() - */ - @Override - public Image getBanner() { - int w = 400; - int h = 60; - float loginStringX = w * .05f; - float loginStringY = h * .75f; - - BufferedImage img = GraphicsUtilities.createCompatibleImage(w, h); - Graphics2D g2 = img.createGraphics(); - try { - Font font = UIManager.getFont("JXLoginPane.bannerFont"); - g2.setFont(font); - Graphics2D originalGraphics = g2; - - try { - if (!dlg.getComponentOrientation().isLeftToRight()) { - originalGraphics = (Graphics2D) g2.create(); - g2.scale(-1, 1); - g2.translate(-w, 0); - loginStringX = w - - (((float) font.getStringBounds( - dlg.getBannerText(), - originalGraphics.getFontRenderContext()) - .getWidth()) + w * .05f); - } - - g2.setRenderingHint(RenderingHints.KEY_RENDERING, - RenderingHints.VALUE_RENDER_QUALITY); - g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_ON); - g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, - RenderingHints.VALUE_TEXT_ANTIALIAS_ON); - g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, - RenderingHints.VALUE_FRACTIONALMETRICS_ON); - - // draw a big square - g2.setColor(UIManager - .getColor("JXLoginPane.bannerDarkBackground")); - g2.fillRect(0, 0, w, h); - - // create the curve shape - GeneralPath curveShape = new GeneralPath( - GeneralPath.WIND_NON_ZERO); - curveShape.moveTo(0, h * .6f); - curveShape.curveTo(w * .167f, h * 1.2f, w * .667f, h * -.5f, w, - h * .75f); - curveShape.lineTo(w, h); - curveShape.lineTo(0, h); - curveShape.lineTo(0, h * .8f); - curveShape.closePath(); - - // draw into the buffer a gradient (bottom to top), and the text - // "Login" - GradientPaint gp = new GradientPaint(0, h, UIManager - .getColor("JXLoginPane.bannerDarkBackground"), 0, 0, - UIManager.getColor("JXLoginPane.bannerLightBackground")); - g2.setPaint(gp); - g2.fill(curveShape); - - originalGraphics.setColor(UIManager - .getColor("JXLoginPane.bannerForeground")); - originalGraphics.drawString(dlg.getBannerText(), loginStringX, - loginStringY); - } finally { - originalGraphics.dispose(); - } - } finally { - g2.dispose(); - } - return img; - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/motif/MotifLookAndFeelAddons.java b/src/main/java/org/jdesktop/swingx/plaf/motif/MotifLookAndFeelAddons.java deleted file mode 100644 index 7f504c89c8..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/motif/MotifLookAndFeelAddons.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * $Id: MotifLookAndFeelAddons.java 4092 2011-11-30 18:04:36Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf.motif; - -import org.jdesktop.swingx.plaf.LookAndFeelAddons; -import org.jdesktop.swingx.plaf.basic.BasicLookAndFeelAddons; -import org.kohsuke.MetaInfServices; - -import javax.swing.*; - -@MetaInfServices(LookAndFeelAddons.class) -public class MotifLookAndFeelAddons extends BasicLookAndFeelAddons { - /** - * {@inheritDoc} - */ - @Override - protected boolean matches() { - return UIManager.getLookAndFeel().getID().equals("Motif"); - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/motif/package-info.java b/src/main/java/org/jdesktop/swingx/plaf/motif/package-info.java deleted file mode 100644 index c310a5f549..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/motif/package-info.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * $Id: package-info.java 4028 2011-06-03 19:32:19Z kschaefe $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -/** - * Provides motif laf specific implementation of pluggable look-and-feel for SwingX components together with a - * mechanism to support custom component look-and-feels. - */ -package org.jdesktop.swingx.plaf.motif; - From 72916f3d48f553312a390e5a5528705722fb66f0 Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 6 Jul 2022 10:28:45 +0200 Subject: [PATCH 123/422] - remove synth & misc L&F support Former-commit-id: e830a60e353d7ae1aa78e38bc2e8a2b65612f8be --- .../swingx/plaf/misc/GlossyTaskPaneUI.java | 156 ---------- .../swingx/plaf/misc/package-info.java | 25 -- .../swingx/plaf/synth/SynthBorder.java | 125 -------- .../jdesktop/swingx/plaf/synth/SynthUI.java | 45 --- .../swingx/plaf/synth/SynthUtils.java | 200 ------------- .../swingx/plaf/synth/SynthXListUI.java | 280 ------------------ .../jdesktop/swingx/plaf/synth/XRegion.java | 73 ----- 7 files changed, 904 deletions(-) delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/misc/GlossyTaskPaneUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/misc/package-info.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/synth/SynthBorder.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/synth/SynthUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/synth/SynthUtils.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/synth/SynthXListUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/synth/XRegion.java diff --git a/src/main/java/org/jdesktop/swingx/plaf/misc/GlossyTaskPaneUI.java b/src/main/java/org/jdesktop/swingx/plaf/misc/GlossyTaskPaneUI.java deleted file mode 100644 index 7c142e643e..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/misc/GlossyTaskPaneUI.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * $Id: GlossyTaskPaneUI.java 3448 2009-08-19 08:12:23Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf.misc; - -import org.jdesktop.swingx.JXTaskPane; -import org.jdesktop.swingx.plaf.basic.BasicTaskPaneUI; - -import javax.swing.*; -import javax.swing.border.Border; -import javax.swing.plaf.ComponentUI; -import java.awt.*; - -/** - * Paints the JXTaskPane with a gradient in the title bar. - * - * @author Frederic Lavigne - */ -public class GlossyTaskPaneUI extends BasicTaskPaneUI { - - public static ComponentUI createUI(JComponent c) { - return new GlossyTaskPaneUI(); - } - - @Override - protected Border createPaneBorder() { - return new GlossyPaneBorder(); - } - - /** - * Overriden to paint the background of the component but keeping the rounded - * corners. - */ - @Override - public void update(Graphics g, JComponent c) { - if (c.isOpaque()) { - g.setColor(c.getParent().getBackground()); - g.fillRect(0, 0, c.getWidth(), c.getHeight()); - g.setColor(c.getBackground()); - g.fillRect(0, getRoundHeight(), c.getWidth(), c.getHeight() - getRoundHeight()); - } - paint(g, c); - } - - /** - * The border of the taskpane group paints the "text", the "icon", the - * "expanded" status and the "special" type. - * - */ - class GlossyPaneBorder extends PaneBorder { - - @Override - protected void paintTitleBackground(JXTaskPane group, Graphics g) { - if (group.isSpecial()) { - g.setColor(specialTitleBackground); - g.fillRoundRect( - 0, - 0, - group.getWidth(), - getRoundHeight() * 2, - getRoundHeight(), - getRoundHeight()); - g.fillRect( - 0, - getRoundHeight(), - group.getWidth(), - getTitleHeight(group) - getRoundHeight()); - } else { - Paint oldPaint = ((Graphics2D)g).getPaint(); - GradientPaint gradient = - new GradientPaint( - 0f, - 0f, //group.getWidth() / 2, - titleBackgroundGradientStart, - 0f, //group.getWidth(), - getTitleHeight(group), - titleBackgroundGradientEnd); - - ((Graphics2D)g).setRenderingHint( - RenderingHints.KEY_COLOR_RENDERING, - RenderingHints.VALUE_COLOR_RENDER_QUALITY); - ((Graphics2D)g).setRenderingHint( - RenderingHints.KEY_INTERPOLATION, - RenderingHints.VALUE_INTERPOLATION_BILINEAR); - ((Graphics2D)g).setRenderingHint( - RenderingHints.KEY_RENDERING, - RenderingHints.VALUE_RENDER_QUALITY); - ((Graphics2D)g).setPaint(gradient); - - g.fillRoundRect( - 0, - 0, - group.getWidth(), - getRoundHeight() * 2, - getRoundHeight(), - getRoundHeight()); - g.fillRect( - 0, - getRoundHeight(), - group.getWidth(), - getTitleHeight(group) - getRoundHeight()); - ((Graphics2D)g).setPaint(oldPaint); - } - - g.setColor(borderColor); - g.drawRoundRect( - 0, - 0, - group.getWidth() - 1, - getTitleHeight(group) + getRoundHeight(), - getRoundHeight(), - getRoundHeight()); - g.drawLine(0, getTitleHeight(group) - 1, group.getWidth(), getTitleHeight(group) - 1); - } - - @Override - protected void paintExpandedControls(JXTaskPane group, Graphics g, int x, - int y, int width, int height) { - ((Graphics2D)g).setRenderingHint( - RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_ON); - - paintOvalAroundControls(group, g, x, y, width, height); - g.setColor(getPaintColor(group)); - paintChevronControls(group, g, x, y, width, height); - - ((Graphics2D)g).setRenderingHint( - RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_OFF); - } - - @Override - protected boolean isMouseOverBorder() { - return true; - } - - } - -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/misc/package-info.java b/src/main/java/org/jdesktop/swingx/plaf/misc/package-info.java deleted file mode 100644 index 379a1784ae..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/misc/package-info.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -/** - * A package to collect miscellaneous UI delegates. - */ -package org.jdesktop.swingx.plaf.misc; - diff --git a/src/main/java/org/jdesktop/swingx/plaf/synth/SynthBorder.java b/src/main/java/org/jdesktop/swingx/plaf/synth/SynthBorder.java deleted file mode 100644 index c26dd38855..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/synth/SynthBorder.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * $Id$ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.plaf.synth; - -/* - * @(#)SynthBorder.java 1.15 06/11/30 - * - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. - * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. - */ - -import javax.swing.*; -import javax.swing.border.AbstractBorder; -import javax.swing.plaf.UIResource; -import javax.swing.plaf.synth.SynthContext; -import javax.swing.plaf.synth.SynthStyle; -import java.awt.*; - -/** - * SynthBorder is a border that delegates to a Painter. The Insets - * are determined at construction time.

                - * - * Copied from core - * - * @version 1.15, 11/30/06 - * @author Scott Violet - */ -class SynthBorder extends AbstractBorder implements UIResource { - private SynthUI ui; - private Insets insets; - - SynthBorder(SynthUI ui, Insets insets) { - this.ui = ui; - this.insets = insets; - } - - SynthBorder(SynthUI ui) { - this(ui, null); - } - - @Override - public void paintBorder(Component c, Graphics g, int x, int y, - int width, int height) { - JComponent jc = (JComponent)c; - SynthContext context = ui.getContext(jc); - SynthStyle style = context.getStyle(); - if (style == null) { - assert false: "SynthBorder is being used outside after the UI " + - "has been uninstalled"; - return; - } - ui.paintBorder(context, g, x, y, width, height); - } - - /** - * This default implementation returns a new Insets - * instance where the top, left, - * bottom, and - * right fields are set to 0. - * @param c the component for which this border insets value applies - * @return the new Insets object initialized to 0 - */ - @Override - public Insets getBorderInsets(Component c) { - return getBorderInsets(c, null); - } - - /** - * Reinitializes the insets parameter with this Border's current Insets. - * @param c the component for which this border insets value applies - * @param insets the object to be reinitialized - * @return the insets object - */ - @Override - public Insets getBorderInsets(Component c, Insets insets) { - if (this.insets != null) { - if (insets == null) { - insets = new Insets(this.insets.top, this.insets.left, - this.insets.bottom, this.insets.right); - } - else { - insets.top = this.insets.top; - insets.bottom = this.insets.bottom; - insets.left = this.insets.left; - insets.right = this.insets.right; - } - } - else if (insets == null) { - insets = new Insets(0, 0, 0, 0); - } - else { - insets.top = insets.bottom = insets.left = insets.right = 0; - } - return insets; - } - - /** - * This default implementation returns false. - * @return false - */ - @Override - public boolean isBorderOpaque() { - return false; - } - -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/synth/SynthUI.java b/src/main/java/org/jdesktop/swingx/plaf/synth/SynthUI.java deleted file mode 100644 index c4f6cb54ae..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/synth/SynthUI.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * $Id$ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.plaf.synth; - -import javax.swing.*; -import javax.swing.plaf.synth.SynthContext; -import java.awt.*; - -/** - * Replacement of sun.swing.plaf.SynthUI.

                - * - * Note: this is a temporary emergency measure to make SwingX web-deployable. It is - * used internally only. Expect problems in future, as custom styles might not be - * found: SynthStyleFactory checks against type of sun SynthUI. - * - * @author Jeanette Winzenburg - */ -public interface SynthUI { - - public SynthContext getContext(JComponent arg0); - - public void paintBorder(SynthContext context, Graphics g, int x, - int y, int w, int h); - - -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/synth/SynthUtils.java b/src/main/java/org/jdesktop/swingx/plaf/synth/SynthUtils.java deleted file mode 100644 index 194620d019..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/synth/SynthUtils.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * $Id$ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.plaf.synth; - -import javax.swing.*; -import javax.swing.plaf.synth.*; -import java.awt.*; -import java.beans.PropertyChangeEvent; - -/** - * Utility class as stand-in for package private synth utility methods. - * - * @author Jeanette Winzenburg - */ -public class SynthUtils { - -//----------------------- context-related - - /** - * Used to avoid null painter checks everywhere. - */ - private static SynthPainter NULL_PAINTER = new SynthPainter() {}; - - /** - * Returns a SynthContext with the specified values. - * - * @param component JComponent - * @param region Identifies the portion of the JComponent - * @param style Style associated with the component - * @param state State of the component as defined in SynthConstants. - * @return a SynthContext with the specified values. - * - * @throws NullPointerException if component, region of style is null. - * - */ - public static SynthContext getContext(JComponent c, Region region, SynthStyle style, int state) { - return new SynthContext(c, region, style, state); - } - - /** - * @param context - * @param style - * @return - */ - public static SynthContext getContext(SynthContext context, SynthStyle style) { - if (context.getStyle().equals(style)) return context; - return getContext(context.getComponent(), context.getRegion(), style, context.getComponentState()); - } - /** - * Returns a context with the given component state and all other fields same as input context. - * - * @param context the context, must not be null - * @param state the component state. - * @return a context with the given component state and other fields as inpu context. - */ - public static SynthContext getContext(SynthContext context, int state) { - if (context.getComponentState() == state) return context; - return getContext(context.getComponent(), context.getRegion(), context.getStyle(), state); - } - - /** - * Returns a SynthPainter from the context's style. Fall-back to default if - * none available. - * - * @param context SynthContext containing the style, must not be null. - * @return a SynthPainter from the context's style, or a default if null. - */ - public static SynthPainter getPainter(SynthContext context) { - SynthPainter painter = context.getStyle().getPainter(context); - return painter != null ? painter : NULL_PAINTER; - } - -//------------------- style-related - - /** - * Returns true if the Style should be updated in response to the - * specified PropertyChangeEvent. This forwards to - * shouldUpdateStyleOnAncestorChanged as necessary. - */ - public static boolean shouldUpdateStyle(PropertyChangeEvent event) { - String eName = event.getPropertyName(); - if ("name" == eName) { - // Always update on a name change - return true; - } - else if ("componentOrientation" == eName) { - // Always update on a component orientation change - return true; - } - else if ("ancestor" == eName && event.getNewValue() != null) { - // Only update on an ancestor change when getting a valid - // parent and the LookAndFeel wants this. - LookAndFeel laf = UIManager.getLookAndFeel(); - return (laf instanceof SynthLookAndFeel && - ((SynthLookAndFeel)laf). - shouldUpdateStyleOnAncestorChanged()); - } - // Note: The following two nimbus based overrides should be refactored - // to be in the Nimbus LAF. Due to constraints in an update release, - // we couldn't actually provide the public API necessary to allow - // NimbusLookAndFeel (a subclass of SynthLookAndFeel) to provide its - // own rules for shouldUpdateStyle. - else if ("Nimbus.Overrides" == eName) { - // Always update when the Nimbus.Overrides client property has - // been changed - return true; - } - else if ("Nimbus.Overrides.InheritDefaults" == eName) { - // Always update when the Nimbus.Overrides.InheritDefaults - // client property has changed - return true; - } - else if ("JComponent.sizeVariant" == eName) { - // Always update when the JComponent.sizeVariant - // client property has changed - return true; - } - return false; - } - -//--------------- component related - - public static int getComponentState(JComponent c) { - if (c.isEnabled()) { - if (c.isFocusOwner()) { - return SynthConstants.ENABLED | SynthConstants.FOCUSED; - } - return SynthConstants.ENABLED; - } - return SynthConstants.DISABLED; - } - - // ---------------- divers ... - - /** - * A convenience method that handles painting of the background. All SynthUI - * implementations should override update and invoke this method. - * - * @param context must not be null - * @param g must not be null - */ - public static void update(SynthContext context, Graphics g) { - update(context, g, null); - } - - /** - * A convenience method that handles painting of the background. All SynthUI - * implementations should override update and invoke this method. - * - * @param context must not be null - * @param g must not be null - * @param the bounds to fill, may be null to indicate the complete size - */ - public static void update(SynthContext context, Graphics g, Rectangle bounds) { - JComponent c = context.getComponent(); - SynthStyle style = context.getStyle(); - int x, y, width, height; - - if (bounds == null) { - x = 0; - y = 0; - width = c.getWidth(); - height = c.getHeight(); - } else { - x = bounds.x; - y = bounds.y; - width = bounds.width; - height = bounds.height; - } - - // Fill in the background, if necessary. - boolean subregion = context.getRegion().isSubregion(); - if ((subregion && style.isOpaque(context)) - || (!subregion && c.isOpaque())) { - g.setColor(style.getColor(context, ColorType.BACKGROUND)); - g.fillRect(x, y, width, height); - } - } - -} - diff --git a/src/main/java/org/jdesktop/swingx/plaf/synth/SynthXListUI.java b/src/main/java/org/jdesktop/swingx/plaf/synth/SynthXListUI.java deleted file mode 100644 index 833ce6c9c0..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/synth/SynthXListUI.java +++ /dev/null @@ -1,280 +0,0 @@ -/* - * $Id$ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.plaf.synth; - -import org.jdesktop.swingx.SwingXUtilities; -import org.jdesktop.swingx.plaf.basic.core.BasicXListUI; - -import javax.swing.*; -import javax.swing.plaf.ComponentUI; -import javax.swing.plaf.UIResource; -import javax.swing.plaf.synth.*; -import java.awt.*; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; - -/** - * TODO add type doc - * - * @author Jeanette Winzenburg - */ -public class SynthXListUI extends BasicXListUI - // PENDING JW: SynthUI is sun package (here: used by c&p'ed SynthBorder) - replace? - // maybe not: SynthLookUp looks up styles from delegates of type SynthUI only - implements SynthConstants, SynthUI /*, PropertyChangeListener */{ - - private SynthStyle style; - @SuppressWarnings("unused") - private boolean useListColors; - @SuppressWarnings("unused") - private boolean useUIBorder; - - /** - * Returns a new instance of SynthXListUI. SynthXListUI delegates are - * allocated one per JList. - * - * @return A new ListUI implementation for the Synth look and feel. - */ - public static ComponentUI createUI(JComponent list) { - return new SynthXListUI(); - } - - /** - * {@inheritDoc}

                - * Overridden to fill background, Synth-style. - */ - @Override - public void update(Graphics g, JComponent c) { - SynthContext context = getContext(c); - SynthUtils.update(context, g); - paintBorder(context, g, 0, 0, c.getWidth(), c.getHeight()); - paint(g, c); - } - - - /** - * {@inheritDoc}

                - * Overridden to update style if appropriate. - */ - @Override - protected PropertyChangeListener createPropertyChangeListener() { - PropertyChangeListener l = new PropertyChangeHandler() { - - @Override - public void propertyChange(PropertyChangeEvent e) { - if (SynthUtils.shouldUpdateStyle(e)) { - updateStyle(); - } - super.propertyChange(e); - } - - }; - return l; - } - - /** - * {@inheritDoc}

                - * Overridden to install properties, Synth-style. - */ - @Override - protected void installDefaults() { - // never happens - the delegate renderer is always not-null and - // not a ui-resource -// if (list.getCellRenderer() == null || -// (list.getCellRenderer() instanceof UIResource)) { -// list.setCellRenderer(new SynthListCellRenderer()); -// } - updateStyle(); - } - - private void updateStyle() { - // compare local reference to style from factory - // nothing to do if same - if (style == getStyle()) return; - // check if this is called from init or from later update - // if from later updates, need to cleanup old - boolean refresh = style != null; - if (refresh) { - style.uninstallDefaults(getContext(ENABLED)); - } - // update local reference - style = getStyle(); - // special case border - installSynthBorder(); - // install defaults - style.installDefaults(getContext(ENABLED)); - // install selected properties - SynthContext selectedContext = getContext(SELECTED); - Color sbg = list.getSelectionBackground(); - if (sbg == null || sbg instanceof UIResource) { - list.setSelectionBackground(style.getColor( - selectedContext, ColorType.TEXT_BACKGROUND)); - } - - Color sfg = list.getSelectionForeground(); - if (sfg == null || sfg instanceof UIResource) { - list.setSelectionForeground(style.getColor( - selectedContext, ColorType.TEXT_FOREGROUND)); - } - // install cell height - int height = style.getInt(selectedContext, "List.cellHeight", -1); - if (height != -1) { - list.setFixedCellHeight(height); - } - // we do this because ... ?? - if (refresh) { - uninstallKeyboardActions(); - installKeyboardActions(); - } - // install currently unused properties of this delegate - useListColors = style.getBoolean(selectedContext, - "List.rendererUseListColors", true); - useUIBorder = style.getBoolean(selectedContext, - "List.rendererUseUIBorder", true); - - } - - /** - * Installs a SynthBorder from the current style, if ui-installable. - * - * @param context the context - */ - protected void installSynthBorder() { - if (SwingXUtilities.isUIInstallable(list.getBorder())) { - list.setBorder(new SynthBorder(this, style.getInsets(getContext(ENABLED), null))); - } - } - - /** - * {@inheritDoc}

                - * Overridden to uninstall properties, Synth-style, after calling super. - */ - @Override - protected void uninstallDefaults() { - super.uninstallDefaults(); - style.uninstallDefaults(getContext(ENABLED)); - style = null; - } - - - /** - * Paints border with the context's style's painter. - * Implemented for SynthUI interface. - */ - @Override - public void paintBorder(SynthContext context, Graphics g, int x, int y, - int w, int h) { - SynthUtils.getPainter(context).paintListBorder(context, g, x, y, w, h); - } - - /** - * {@inheritDoc}

                - * - * Returns a context for the component's current state. - * Implemented for SynthUI interface.

                - * - * PENDING JW: not entirely sure if allowed ... but need to replace SynthUI anyway?. - * - * @throws IllegalArgumentException if the component is not controlled by this - * delegate - */ - @Override - public SynthContext getContext(JComponent c) { - if (c != list) throw new IllegalArgumentException("must be ui-delegate for component"); - return getContext(); - } - - /** - * Returns the context based on current state. - * @return - */ - private SynthContext getContext() { - return getContext(getComponentState()); - } - - /** - * Returns the current component state for the controlled list. - * @return - */ - private int getComponentState() { - return SynthUtils.getComponentState(list); - } - - /** - * Returns a Context with the given component state. - * - * @param state - * @return - */ - private SynthContext getContext(int state) { - return SynthUtils.getContext(list, getRegion(), style, state); - } - - private Region getRegion() { - return XRegion.getXRegion(list, true); - } - - - /** - * Returns the style for this component from the style factory. - * @return - */ - private SynthStyle getStyle() { - return SynthLookAndFeel.getStyleFactory().getStyle(list, getRegion()); - } - - -// private class SynthListCellRenderer extends DefaultListCellRenderer.UIResource { -// public String getName() { -// return "List.cellRenderer"; -// } -// -// public void setBorder(Border b) { -// if (useUIBorder || b instanceof SynthBorder) { -// super.setBorder(b); -// } -// } -// -// public Component getListCellRendererComponent(JList list, Object value, -// int index, boolean isSelected, boolean cellHasFocus) { -// if (!useListColors && (isSelected || cellHasFocus)) { -// SynthLookAndFeel.setSelectedUI((SynthLabelUI)SynthLookAndFeel. -// getUIOfType(getUI(), SynthLabelUI.class), -// isSelected, cellHasFocus, list.isEnabled(), false); -// } -// else { -// SynthLookAndFeel.resetSelectedUI(); -// } -// -// super.getListCellRendererComponent(list, value, index, -// isSelected, cellHasFocus); -// return this; -// } -// -// public void paint(Graphics g) { -// super.paint(g); -// SynthLookAndFeel.resetSelectedUI(); -// } -// } - - -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/synth/XRegion.java b/src/main/java/org/jdesktop/swingx/plaf/synth/XRegion.java deleted file mode 100644 index 0bc72b3ee4..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/synth/XRegion.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * $Id$ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.plaf.synth; - -import javax.swing.*; -import javax.swing.plaf.synth.Region; -import java.util.HashMap; -import java.util.Map; - -/** - * Extended Region to register custom component delegates. - * - * @author Jeanette Winzenburg - */ -public class XRegion extends Region { - - static Map uiToXRegionMap = new HashMap(); - public static final Region XLIST = new XRegion("XList", null, false, "XListUI", LIST); - - /** the Region which identifies the base styles */ - private Region parent; - - /** - * Creates a XRegion with the specified name. - * - * @param name Name of the region - * @param subregion Whether or not this is a subregion. - * @param realUI String that will be returned from - * component.getUIClassID. - * @param parent the parent region which this is extending. - */ - public XRegion(String name, String dummyUI, boolean subregion, String realUI, Region parent) { - super(name, dummyUI, subregion); - this.parent = parent; - if (realUI != null) { - uiToXRegionMap.put(realUI, this); - } - } - - /** - * Returns a region appropriate for the component. - * - * @param component the component to get the region for - * @param useParent a boolean indicating whether or not to return a fallback - * of the XRegion, if available - * @return a region for the component or null if not available. - */ - public static Region getXRegion(JComponent component, boolean useParent) { - XRegion region = uiToXRegionMap.get(component.getUIClassID()); - if (region != null) - return useParent && region.parent != null ? region.parent : region; - return region; - } -} From be69053926e7014aebae6c303dd4ff584f696a44 Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 6 Jul 2022 10:41:28 +0200 Subject: [PATCH 124/422] - remove Windows Classic Settings Former-commit-id: 4c5eeb3796eea0395e661deaf7d616237ddd62a8 --- .../jdesktop/swingx/plaf/TaskPaneAddon.java | 13 --- .../swingx/plaf/TaskPaneContainerAddon.java | 6 +- .../WindowsClassicLookAndFeelAddons.java | 61 ------------- .../windows/WindowsClassicStatusBarUI.java | 87 ------------------- .../windows/WindowsClassicTaskPaneUI.java | 80 ----------------- 5 files changed, 1 insertion(+), 246 deletions(-) delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/WindowsClassicLookAndFeelAddons.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/WindowsClassicStatusBarUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/WindowsClassicTaskPaneUI.java diff --git a/src/main/java/org/jdesktop/swingx/plaf/TaskPaneAddon.java b/src/main/java/org/jdesktop/swingx/plaf/TaskPaneAddon.java index f1c472050b..c90ffe01be 100644 --- a/src/main/java/org/jdesktop/swingx/plaf/TaskPaneAddon.java +++ b/src/main/java/org/jdesktop/swingx/plaf/TaskPaneAddon.java @@ -21,7 +21,6 @@ package org.jdesktop.swingx.plaf; import org.jdesktop.swingx.JXTaskPane; -import org.jdesktop.swingx.plaf.windows.WindowsClassicLookAndFeelAddons; import org.jdesktop.swingx.plaf.windows.WindowsLookAndFeelAddons; import org.jdesktop.swingx.util.OS; @@ -153,18 +152,6 @@ protected void addWindowsDefaults(LookAndFeelAddons addon, DefaultsList defaults defaults.add("TaskPane.borderColor", new ColorUIResource(Color.WHITE)); } } - - if (addon instanceof WindowsClassicLookAndFeelAddons) { - defaults.add(JXTaskPane.uiClassID, "org.jdesktop.swingx.plaf.windows.WindowsClassicTaskPaneUI"); - defaults.add("TaskPane.foreground", new ColorUIResource(Color.BLACK)); - defaults.add("TaskPane.background", new ColorUIResource(Color.WHITE)); - defaults.add("TaskPane.specialTitleBackground", new ColorUIResource(10, 36, 106)); - defaults.add("TaskPane.titleBackgroundGradientStart", new ColorUIResource(212, 208, 200)); - defaults.add("TaskPane.titleBackgroundGradientEnd", new ColorUIResource(212, 208, 200)); - defaults.add("TaskPane.titleForeground", new ColorUIResource(Color.BLACK)); - defaults.add("TaskPane.specialTitleForeground", new ColorUIResource(Color.WHITE)); - defaults.add("TaskPane.borderColor", new ColorUIResource(212, 208, 200)); - } } @Override diff --git a/src/main/java/org/jdesktop/swingx/plaf/TaskPaneContainerAddon.java b/src/main/java/org/jdesktop/swingx/plaf/TaskPaneContainerAddon.java index 397e452da2..52b7fba679 100644 --- a/src/main/java/org/jdesktop/swingx/plaf/TaskPaneContainerAddon.java +++ b/src/main/java/org/jdesktop/swingx/plaf/TaskPaneContainerAddon.java @@ -22,7 +22,6 @@ import org.jdesktop.swingx.JXTaskPaneContainer; import org.jdesktop.swingx.painter.MattePainter; -import org.jdesktop.swingx.plaf.windows.WindowsClassicLookAndFeelAddons; import org.jdesktop.swingx.plaf.windows.WindowsLookAndFeelAddons; import org.jdesktop.swingx.util.OS; @@ -77,10 +76,7 @@ protected void addMetalDefaults(LookAndFeelAddons addon, DefaultsList defaults) @Override protected void addWindowsDefaults(LookAndFeelAddons addon, DefaultsList defaults) { super.addWindowsDefaults(addon, defaults); - if (addon instanceof WindowsClassicLookAndFeelAddons) { - defaults.add("TaskPaneContainer.background", UIManagerExt.getSafeColor("List.background", - new ColorUIResource(Color.decode("#005C5C")))); - } else if (addon instanceof WindowsLookAndFeelAddons) { + if (addon instanceof WindowsLookAndFeelAddons) { String xpStyle = OS.getWindowsVisualStyle(); ColorUIResource background; Color backgroundGradientStart; diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsClassicLookAndFeelAddons.java b/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsClassicLookAndFeelAddons.java deleted file mode 100644 index a8601f640a..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsClassicLookAndFeelAddons.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * $Id: WindowsClassicLookAndFeelAddons.java 4092 2011-11-30 18:04:36Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf.windows; - -import org.jdesktop.swingx.plaf.LookAndFeelAddons; -import org.jdesktop.swingx.util.OS; -import org.kohsuke.MetaInfServices; - -import static javax.swing.UIManager.getLookAndFeel; -import static javax.swing.UIManager.getSystemLookAndFeelClassName; - -/** - * Adds new pluggable UI following the Windows Classic look and feel. Currently - * it extends the XP look and feel and overrides the JTaskPane - * and JTaskPaneGroup UIs. - */ -@MetaInfServices(LookAndFeelAddons.class) -public class WindowsClassicLookAndFeelAddons extends WindowsLookAndFeelAddons { - - /** - * {@inheritDoc} - */ - @Override - protected boolean matches() { - String laf = getLookAndFeel().getClass().getName(); - - if (isSystemAddon()) { - //special-case the jgoodies to ensure that we can match it - return getSystemLookAndFeelClassName().equals(laf) - || "com.jgoodies.looks.windows.WindowsLookAndFeel".equals(laf); - } - - return "com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel".equals(laf); - } - - /** - * {@inheritDoc} - */ - @Override - protected boolean isSystemAddon() { - return OS.isWindows() && !OS.isUsingWindowsVisualStyles(); - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsClassicStatusBarUI.java b/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsClassicStatusBarUI.java deleted file mode 100644 index 30b8bf89e7..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsClassicStatusBarUI.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * $Id: WindowsClassicStatusBarUI.java 3472 2009-08-27 13:12:42Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.plaf.windows; - -import org.jdesktop.swingx.JXStatusBar; -import org.jdesktop.swingx.plaf.basic.BasicStatusBarUI; - -import javax.swing.*; -import javax.swing.border.BevelBorder; -import javax.swing.border.Border; -import javax.swing.plaf.BorderUIResource; -import javax.swing.plaf.ComponentUI; -import java.awt.*; - -/** - * - * @author rbair - */ -public class WindowsClassicStatusBarUI extends BasicStatusBarUI { - /** Creates a new instance of BasicStatusBarUI */ - public WindowsClassicStatusBarUI() { - } - - /** - * Returns an instance of the UI delegate for the specified component. - * Each subclass must provide its own static createUI - * method that returns an instance of that UI delegate subclass. - * If the UI delegate subclass is stateless, it may return an instance - * that is shared by multiple components. If the UI delegate is - * stateful, then it should return a new instance per component. - * The default implementation of this method throws an error, as it - * should never be invoked. - */ - public static ComponentUI createUI(JComponent c) { - return new WindowsClassicStatusBarUI(); - } - - @Override protected void paintBackground(Graphics2D g, JXStatusBar bar) { - g.setColor(bar.getBackground()); - g.fillRect(0, 0, bar.getWidth(), bar.getHeight()); - - //paint an inset border around each component. This suggests that - //there is an extra border around the status bar...! - Border b = BorderFactory.createBevelBorder(BevelBorder.LOWERED, - Color.WHITE, bar.getBackground(), bar.getBackground(), Color.GRAY); - Insets insets = new Insets(0, 0, 0, 0); - for (Component c : bar.getComponents()) { - getSeparatorInsets(insets); - int x = c.getX() - insets.right; - int y = c.getY() - 2; - int w = c.getWidth() + insets.left + insets.right; - int h = c.getHeight() + 4; - b.paintBorder(c, g, x, y, w, h); - } - } - - @Override protected void paintSeparator(Graphics2D g, JXStatusBar bar, int x, int y, int w, int h) { - //paint nothing, since paintBackground handles this - } - - @Override protected int getSeparatorWidth() { - return 11; - } - - @Override protected BorderUIResource createBorder() { - return new BorderUIResource(BorderFactory.createEmptyBorder(4, 5, 3, 22)); - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsClassicTaskPaneUI.java b/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsClassicTaskPaneUI.java deleted file mode 100644 index b316480b29..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsClassicTaskPaneUI.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * $Id: WindowsClassicTaskPaneUI.java 3448 2009-08-19 08:12:23Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf.windows; - -import org.jdesktop.swingx.JXTaskPane; -import org.jdesktop.swingx.plaf.basic.BasicTaskPaneUI; - -import javax.swing.*; -import javax.swing.border.Border; -import javax.swing.plaf.ComponentUI; -import java.awt.*; - -/** - * Windows Classic (NT/2000) implementation of the - * JXTaskPane UI. - * - * @author Frederic Lavigne - */ -public class WindowsClassicTaskPaneUI extends BasicTaskPaneUI { - - public static ComponentUI createUI(JComponent c) { - return new WindowsClassicTaskPaneUI(); - } - - @Override - protected void installDefaults() { - super.installDefaults(); - - LookAndFeel.installProperty(group, "opaque", false); - } - - @Override - protected Border createPaneBorder() { - return new ClassicPaneBorder(); - } - - /** - * The border of the taskpane group paints the "text", the "icon", the - * "expanded" status and the "special" type. - * - */ - class ClassicPaneBorder extends PaneBorder { - - @Override - protected void paintExpandedControls(JXTaskPane group, Graphics g, int x, - int y, int width, int height) { - ((Graphics2D)g).setRenderingHint( - RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_ON); - - paintRectAroundControls(group, g, x, y, width, height, Color.white, - Color.gray); - g.setColor(getPaintColor(group)); - paintChevronControls(group, g, x, y, width, height); - - ((Graphics2D)g).setRenderingHint( - RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_OFF); - } - } - -} From 172b3dc2f37974529b41d5dc0bbce0e8ccfdaaf0 Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 6 Jul 2022 11:00:04 +0200 Subject: [PATCH 125/422] - remove unneeded code - modernize OS detection code Former-commit-id: 6184d706ec431ee567493febd2ed6549eab2e863 --- .../swingx/auth/DefaultUserNameStore.java | 193 ------------ .../swingx/auth/JAASLoginService.java | 153 --------- .../swingx/auth/JDBCLoginService.java | 237 -------------- .../org/jdesktop/swingx/auth/KeyChain.java | 198 ------------ .../jdesktop/swingx/auth/LoginAdapter.java | 48 --- .../org/jdesktop/swingx/auth/LoginEvent.java | 45 --- .../jdesktop/swingx/auth/LoginListener.java | 60 ---- .../jdesktop/swingx/auth/LoginService.java | 290 ------------------ .../jdesktop/swingx/auth/PasswordStore.java | 55 ---- .../swingx/auth/SimpleLoginService.java | 60 ---- .../jdesktop/swingx/auth/UserNameStore.java | 62 ---- .../jdesktop/swingx/auth/UserPermissions.java | 126 -------- .../jdesktop/swingx/auth/package-info.java | 25 -- .../plaf/linux/LinuxLookAndFeelAddons.java | 4 +- .../windows/WindowsLookAndFeelAddons.java | 3 +- .../java/org/jdesktop/swingx/util/OS.java | 41 +-- 16 files changed, 12 insertions(+), 1588 deletions(-) delete mode 100644 src/main/java/org/jdesktop/swingx/auth/DefaultUserNameStore.java delete mode 100644 src/main/java/org/jdesktop/swingx/auth/JAASLoginService.java delete mode 100644 src/main/java/org/jdesktop/swingx/auth/JDBCLoginService.java delete mode 100644 src/main/java/org/jdesktop/swingx/auth/KeyChain.java delete mode 100644 src/main/java/org/jdesktop/swingx/auth/LoginAdapter.java delete mode 100644 src/main/java/org/jdesktop/swingx/auth/LoginEvent.java delete mode 100644 src/main/java/org/jdesktop/swingx/auth/LoginListener.java delete mode 100644 src/main/java/org/jdesktop/swingx/auth/LoginService.java delete mode 100644 src/main/java/org/jdesktop/swingx/auth/PasswordStore.java delete mode 100644 src/main/java/org/jdesktop/swingx/auth/SimpleLoginService.java delete mode 100644 src/main/java/org/jdesktop/swingx/auth/UserNameStore.java delete mode 100644 src/main/java/org/jdesktop/swingx/auth/UserPermissions.java delete mode 100644 src/main/java/org/jdesktop/swingx/auth/package-info.java diff --git a/src/main/java/org/jdesktop/swingx/auth/DefaultUserNameStore.java b/src/main/java/org/jdesktop/swingx/auth/DefaultUserNameStore.java deleted file mode 100644 index 3e26f0e1ed..0000000000 --- a/src/main/java/org/jdesktop/swingx/auth/DefaultUserNameStore.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * $Id: DefaultUserNameStore.java 4147 2012-02-01 17:13:24Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.auth; - -import org.jdesktop.beans.JavaBean; - -import java.util.prefs.Preferences; - -/** - * Saves the user names in Preferences. Because any string could be part - * of the user name, for every user name that must be saved a new Preferences - * key/value pair must be stored. - * - * @author Bino George - * @author rbair - */ -@JavaBean -public class DefaultUserNameStore extends UserNameStore { - /** - * The key for one of the preferences - */ - private static final String USER_KEY = "usernames"; - /** - */ - private static final String NUM_KEY = "usernames.length"; - /** - * The preferences node - */ - private Preferences prefs; - /** - * Contains the user names. Since the list of user names is not - * frequently updated, there is no penalty in storing the values - * in an array. - */ - private String[] userNames; - - /** - * Creates a new instance of DefaultUserNameStore - */ - public DefaultUserNameStore() { - userNames = new String[0]; - } - - /** - * Loads the user names from Preferences - */ - @Override - public void loadUserNames() { - initPrefs(); - if (prefs != null) { - int n = prefs.getInt(NUM_KEY, 0); - String[] names = new String[n]; - for (int i = 0; i < n; i++) { - names[i] = prefs.get(USER_KEY + "." + i, null); - } - setUserNames(names); - } - } - - /** - * Saves the user names to Preferences - */ - @Override - public void saveUserNames() { - initPrefs(); - if (prefs != null) { - prefs.putInt(NUM_KEY, userNames.length); - for (int i = 0; i < userNames.length; i++) { - prefs.put(USER_KEY + "." + i, userNames[i]); - } - } - } - - /** - * {@inheritDoc} - */ - @Override - public String[] getUserNames() { - String[] copy = new String[userNames.length]; - System.arraycopy(userNames, 0, copy, 0, userNames.length); - - return copy; - } - - /** - * {@inheritDoc} - */ - @Override - public void setUserNames(String[] userNames) { - userNames = userNames == null ? new String[0] : userNames; - String[] old = getUserNames(); - this.userNames = userNames; - firePropertyChange("userNames", old, getUserNames()); - } - - /** - * Add a username to the store. - * @param name - */ - @Override - public void addUserName(String name) { - if (!containsUserName(name)) { - String[] newNames = new String[userNames.length + 1]; - for (int i=0; iJAASLoginService implements a LoginService - * that uses JAAS for authentication. JAASLoginService uses the - * server name as name of the configuration for JAAS. - * - * @author Bino George - */ -@JavaBean -public class JAASLoginService extends LoginService { - private static final Logger LOG = Logger.getLogger(JAASLoginService.class - .getName()); - - protected LoginContext loginContext; - - /** - * Constructor for JAASLoginService - * @param server server name that is also used for the JAAS config name - */ - public JAASLoginService(String server) { - super(server); - } - - /** - * Default JavaBeans constructor - */ - public JAASLoginService() { - super(); - } - - - /** - * @inheritDoc - * - */ - @Override - public boolean authenticate(String name, char[] password, String server) throws Exception { - // If user has selected a different server, update the login service - if (server != null) { - if (!server.equals(getServer())) { - setServer(server); - } - } - // Clear the login context before attempting authentication - loginContext = null; - // Create a login context for the appropriate server and attempt to - // authenticate the user. - try { - loginContext = new LoginContext(getServer(), - new JAASCallbackHandler(name, password)); - loginContext.login(); - return true; - } catch (AccountExpiredException e) { - // TODO add explanation? - LOG.log(Level.WARNING, "", e); - return false; - } catch (CredentialExpiredException e) { - // TODO add explanation? - LOG.log(Level.WARNING, "", e); - return false; - } catch (FailedLoginException e) { - // TODO add explanation? - LOG.log(Level.WARNING, "", e); - return false; - } catch (LoginException e) { - // TODO add explanation? - LOG.log(Level.WARNING, "", e); - return false; - } catch (Throwable e) { - // TODO add explanation? - LOG.log(Level.WARNING, "", e); - return false; - } - } - - /** - * Returns the LoginContext used during the authentication - * process. - */ - public LoginContext getLoginContext() - { - return loginContext; - } - - /** - * Returns the Subject representing the authenticated - * individual, or null if the user has not yet been - * successfully authenticated. - */ - public Subject getSubject() - { - if (loginContext == null) - return null; - return loginContext.getSubject(); - } - - class JAASCallbackHandler implements CallbackHandler { - - private String name; - - private char[] password; - - public JAASCallbackHandler(String name, char[] passwd) { - this.name = name; - this.password = passwd; - } - - public void handle(Callback[] callbacks) throws java.io.IOException { - for (int i = 0; i < callbacks.length; i++) { - if (callbacks[i] instanceof NameCallback) { - NameCallback cb = (NameCallback) callbacks[i]; - cb.setName(name); - } else if (callbacks[i] instanceof PasswordCallback) { - PasswordCallback cb = (PasswordCallback) callbacks[i]; - cb.setPassword(password); - } - } - } - - } -} diff --git a/src/main/java/org/jdesktop/swingx/auth/JDBCLoginService.java b/src/main/java/org/jdesktop/swingx/auth/JDBCLoginService.java deleted file mode 100644 index 42a829f76d..0000000000 --- a/src/main/java/org/jdesktop/swingx/auth/JDBCLoginService.java +++ /dev/null @@ -1,237 +0,0 @@ -/* - * $Id: JDBCLoginService.java 4147 2012-02-01 17:13:24Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.auth; - -import org.jdesktop.beans.JavaBean; - -import javax.naming.InitialContext; -import java.sql.Connection; -import java.sql.DriverManager; -import java.util.Properties; -import java.util.logging.Level; -import java.util.logging.Logger; -/** - * A login service for connecting to SQL based databases via JDBC - * - * @author rbair - */ -@JavaBean -public class JDBCLoginService extends LoginService { - private static final Logger LOG = Logger.getLogger(JDBCLoginService.class - .getName()); - - /** - * The connection to the database - */ - private Connection conn; - /** - * If used, defines the JNDI context from which to get a connection to - * the data base - */ - private String jndiContext; - /** - * When using the DriverManager to connect to the database, this specifies - * any additional properties to use when connecting. - */ - private Properties properties; - - /** - * Create a new JDBCLoginService and initializes it to connect to a - * database using the given params. - * @param driver - * @param url - */ - public JDBCLoginService(String driver, String url) { - super(url); - try { - Class.forName(driver); - } catch (Exception e) { - LOG.log(Level.WARNING, "The driver passed to the " + - "JDBCLoginService constructor could not be loaded. " + - "This may be due to the driver not being on the classpath", e); - } - this.setUrl(url); - } - - /** - * Create a new JDBCLoginService and initializes it to connect to a - * database using the given params. - * @param driver - * @param url - * @param props - */ - public JDBCLoginService(String driver, String url, Properties props) { - super(url); - try { - Class.forName(driver); - } catch (Exception e) { - LOG.log(Level.WARNING, "The driver passed to the " + - "JDBCLoginService constructor could not be loaded. " + - "This may be due to the driver not being on the classpath", e); - } - this.setUrl(url); - this.setProperties(props); - } - - /** - * Create a new JDBCLoginService and initializes it to connect to a - * database using the given params. - * @param jndiContext - */ - public JDBCLoginService(String jndiContext) { - super(jndiContext); - this.jndiContext = jndiContext; - } - - /** - * Default JavaBean constructor - */ - public JDBCLoginService() { - super(); - } - - /** - * @return the JDBC connection url - */ - public String getUrl() { - return getServer(); - } - - /** - * @param url set the JDBC connection url - */ - public void setUrl(String url) { - String old = getUrl(); - setServer(url); - firePropertyChange("url", old, getUrl()); - } - - /** - * @return JDBC connection properties - */ - public Properties getProperties() { - return properties; - } - - /** - * @param properties miscellaneous JDBC properties to use when connecting - * to the database via the JDBC driver - */ - public void setProperties(Properties properties) { - Properties old = getProperties(); - this.properties = properties; - firePropertyChange("properties", old, getProperties()); - } - - public Connection getConnection() { - return conn; - } - - public void setConnection(Connection conn) { - Connection old = getConnection(); - this.conn = conn; - firePropertyChange("connection", old, getConnection()); - } - - /** - * Attempts to get a JDBC Connection from a JNDI javax.sql.DataSource, using - * that connection for interacting with the database. - * @throws Exception - */ - private void connectByJNDI(String userName, char[] password) throws Exception { - InitialContext ctx = new InitialContext(); - javax.sql.DataSource ds = (javax.sql.DataSource)ctx.lookup(jndiContext); - conn = ds.getConnection(userName, new String(password)); - conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ); - } - - /** - * Attempts to get a JDBC Connection from a DriverManager. If properties - * is not null, it tries to connect with those properties. If that fails, - * it then attempts to connect with a user name and password. If that fails, - * it attempts to connect without any credentials at all. - *

                - * If, on the other hand, properties is null, it first attempts to connect - * with a username and password. Failing that, it tries to connect without - * any credentials at all. - * @throws Exception - */ - private void connectByDriverManager(String userName, char[] password) throws Exception { - if (getProperties() != null) { - try { - conn = DriverManager.getConnection(getUrl(), getProperties()); - conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ); - } catch (Exception e) { - try { - conn = DriverManager.getConnection(getUrl(), userName, new String(password)); - conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ); - } catch (Exception ex) { - conn = DriverManager.getConnection(getUrl()); - conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ); - } - } - } else { - try { - conn = DriverManager.getConnection(getUrl(), userName, new String(password)); - } catch (Exception e) { - LOG.log(Level.WARNING, "Connection with properties failed. " + - "Tryint to connect without.", e); - //try to connect without using the userName and password - conn = DriverManager.getConnection(getUrl()); - - } - } - } - - /** - * @param name user name - * @param password user password - * @param server Must be either a valid JDBC URL for the type of JDBC driver you are using, - * or must be a valid JNDIContext from which to get the database connection - */ - @Override - public boolean authenticate(String name, char[] password, String server) throws Exception { - //try to form a connection. If it works, conn will not be null - //if the jndiContext is not null, then try to get the DataSource to use - //from jndi - if (jndiContext != null) { - try { - connectByJNDI(name, password); - } catch (Exception e) { - try { - connectByDriverManager(name, password); - } catch (Exception ex) { - LOG.log(Level.WARNING, "Login failed", ex); - //login failed - return false; - } - } - } else { - try { - connectByDriverManager(name, password); - } catch (Exception ex) { - LOG.log(Level.WARNING, "", ex); - return false; - } - } - return true; - } -} diff --git a/src/main/java/org/jdesktop/swingx/auth/KeyChain.java b/src/main/java/org/jdesktop/swingx/auth/KeyChain.java deleted file mode 100644 index db2448522b..0000000000 --- a/src/main/java/org/jdesktop/swingx/auth/KeyChain.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * $Id: KeyChain.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.auth; - -import javax.crypto.spec.SecretKeySpec; -import java.io.*; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.UnrecoverableEntryException; -import java.security.cert.CertificateException; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * KeyChain is a class that implements the "KeyChain" concept. - * Fundamentally, it allows you to store multiple keys/credentials - * in a central password store. Access to this central store is - * controlled through a master password. This mechanism is used in - * many popular client applications where you need to store credentials - * for multiple servers/accounts. The actual store for the KeyStore - * can be any OutputStream and it can work in the webstart sandbox - * using Muffins. - *

                - *

                - * To contstruct a KeyChain, you need to pass in an InputStream to the - * store and it will initialize the KeyStore from the InputStream. - * You can add and remove entries any time once you have an instance of - * KeyChain. To persist the KeyChain and reflect any changes, you need to - * call store method with an OutputStream. - *

                - * - * @author Bino George - */ -public class KeyChain { - private static final Logger LOG = Logger - .getLogger(KeyChain.class.getName()); - - private KeyStore store; - - private char[] masterPassword; - - /** - * Creates an instance of KeyChain and initializes the store - * from the InputStream. - * - * @param masterPassword - * @param inputStream - * @throws IOException - */ - public KeyChain(char[] masterPassword, InputStream inputStream) - throws IOException { - this.masterPassword = masterPassword; - - try { - store = KeyStore.getInstance("JCEKS"); - store.load(inputStream, masterPassword); - - } catch (KeyStoreException ex) { - LOG.log(Level.WARNING, "", ex); - } catch (CertificateException ex) { - LOG.log(Level.WARNING, "", ex); - } catch (NoSuchAlgorithmException ex) { - LOG.log(Level.WARNING, "", ex); - } catch (EOFException ex) { - LOG.log(Level.WARNING, "", ex); - } - - } - - /** - * Fetches the password for a given account/user and server. - * @param user - * @param server - * @return null if no password could be obtained, the password - * otherwise - */ - public String getPassword(String user, String server) { - - try { - - KeyStore.SecretKeyEntry entry2 = (KeyStore.SecretKeyEntry) store - .getEntry(user + "@" + server, - new KeyStore.PasswordProtection(masterPassword)); - return new String(entry2.getSecretKey().getEncoded()); - } catch (KeyStoreException ex) { - LOG.log(Level.WARNING, "", ex); - } catch (UnrecoverableEntryException ex) { - LOG.log(Level.WARNING, "", ex); - } catch (NoSuchAlgorithmException ex) { - LOG.log(Level.WARNING, "", ex); - } - - return null; - } - - /** - * Adds a password to the KeyChain for a given account/user and server. - * - * @param user - * @param server - * @param password - */ - public void addPassword(String user, String server, char[] password) - { - String pass = new String(password); - SecretKeySpec passwordKey = new SecretKeySpec(pass.getBytes(), "JCEKS"); - KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(passwordKey); - try { - store.setEntry(user + "@" + server, entry, - new KeyStore.PasswordProtection(masterPassword)); - } catch (KeyStoreException e) { - LOG.log(Level.WARNING, "", e); - } - } - - /** - * Removes a password for a given account/user and server. - * - * @param user - * @param server - */ - public void removePassword(String user, String server) { - try { - store.deleteEntry(user + "@" + server); - } catch (KeyStoreException e) { - LOG.log(Level.WARNING, "", e); - } - } - - /** - * Persists the KeyChain to an OutputStream - * - * @param ostream - * @throws IOException - */ - - public void store(OutputStream ostream) throws IOException { - try { - store.store(ostream, masterPassword); - } catch (KeyStoreException ex) { - LOG.log(Level.WARNING, "", ex); - } catch (CertificateException ex) { - LOG.log(Level.WARNING, "", ex); - } catch (NoSuchAlgorithmException ex) { - LOG.log(Level.WARNING, "", ex); - } - } - - - public static void main(String[] args) { - try { - File file = new File("c:\\test.txt"); - FileInputStream fis; - if (!file.exists()) { - file.createNewFile(); - fis = null; - } else { - fis = new FileInputStream(file); - } - KeyChain kc = new KeyChain("test".toCharArray(), fis); - kc.addPassword("bino", "sun-ds.sfbay", "test123".toCharArray()); - LOG.fine("pass = " - + kc.getPassword("bino", "sun-ds.sfbay")); - - LOG.fine("More testing :"); - for (int i = 0; i < 100; i++) { - kc.addPassword("" + i, "sun-ds.sfbay", ("" + i).toCharArray()); - } - for (int i = 0; i < 100; i++) { - LOG.fine("key =" + i + " pass =" - + kc.getPassword("" + i, "sun-ds.sfbay")); - } - kc.store(new FileOutputStream(file)); - } catch (Exception e) { - LOG.log(Level.WARNING, "", e); - } - } - -} diff --git a/src/main/java/org/jdesktop/swingx/auth/LoginAdapter.java b/src/main/java/org/jdesktop/swingx/auth/LoginAdapter.java deleted file mode 100644 index 51449d423d..0000000000 --- a/src/main/java/org/jdesktop/swingx/auth/LoginAdapter.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * $Id: LoginAdapter.java 603 2005-11-11 23:05:18Z rbair $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.auth; - -/** - * - * @author rbair - */ -public abstract class LoginAdapter implements LoginListener { - /** - * @inheritDoc - */ - public void loginSucceeded(LoginEvent source) {} - - /** - * @inheritDoc - */ - public void loginStarted(LoginEvent source) {} - - /** - * @inheritDoc - */ - public void loginFailed(LoginEvent source) {} - - /** - * @inheritDoc - */ - public void loginCanceled(LoginEvent source) {} -} diff --git a/src/main/java/org/jdesktop/swingx/auth/LoginEvent.java b/src/main/java/org/jdesktop/swingx/auth/LoginEvent.java deleted file mode 100644 index 2057992a91..0000000000 --- a/src/main/java/org/jdesktop/swingx/auth/LoginEvent.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * $Id: LoginEvent.java 648 2005-11-30 05:21:56Z rbair $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.auth; -import java.util.EventObject; - -/** - * This is an event object that is passed to login listener methods - * - * @author Shai Almog - */ -public class LoginEvent extends EventObject { - private Throwable cause; - - public LoginEvent(Object source) { - this(source, null); - } - - /** Creates a new instance of LoginEvent */ - public LoginEvent(Object source, Throwable cause) { - super(source); - this.cause = cause; - } - - public Throwable getCause() { - return cause; - } -} diff --git a/src/main/java/org/jdesktop/swingx/auth/LoginListener.java b/src/main/java/org/jdesktop/swingx/auth/LoginListener.java deleted file mode 100644 index d15603790a..0000000000 --- a/src/main/java/org/jdesktop/swingx/auth/LoginListener.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * $Id: LoginListener.java 3707 2010-07-08 19:19:25Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.auth; - -import java.util.EventListener; - -/** - * LoginListener provides a listener for the actual login - * process. - * - * @author Bino George - * @author Shai Almog - */ -public interface LoginListener extends EventListener { - - /** - * Called by the JXLoginPane in the event of a login failure - * - * @param source panel that fired the event - */ - public void loginFailed(LoginEvent source); - /** - * Called by the JXLoginPane when the Authentication - * operation is started. - * @param source panel that fired the event - */ - public void loginStarted(LoginEvent source); - /** - * Called by the JXLoginPane in the event of a login - * cancellation by the user. - * - * @param source panel that fired the event - */ - public void loginCanceled(LoginEvent source); - /** - * Called by the JXLoginPane in the event of a - * successful login. - * - * @param source panel that fired the event - */ - public void loginSucceeded(LoginEvent source); -} diff --git a/src/main/java/org/jdesktop/swingx/auth/LoginService.java b/src/main/java/org/jdesktop/swingx/auth/LoginService.java deleted file mode 100644 index 928bcb1a4d..0000000000 --- a/src/main/java/org/jdesktop/swingx/auth/LoginService.java +++ /dev/null @@ -1,290 +0,0 @@ -/* - * $Id: LoginService.java 3661 2010-04-13 13:19:47Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.auth; - -import org.jdesktop.beans.AbstractBean; - -import javax.swing.*; -import javax.swing.event.EventListenerList; -import java.awt.*; -import java.util.logging.Logger; - -/** - * LoginService is the abstract base class for all classes implementing - * a login mechanism. It allows you to customize the threading behaviour used to - * perform the login. Subclasses need to override the authenticate - * method. Subclasses may implement the getUserRoles() method to return a - * meaningful value this method will be called once upon a successful login to - * determine the user roles. It is not defined as abstract to simplify the task - * of implementing a login service for those who do not require this - * functionality. - * - * @author Bino George - * @author Shai Almog - * @author Karl Schaefer - */ -public abstract class LoginService extends AbstractBean { - @SuppressWarnings("unused") - private Logger LOG = Logger.getLogger(LoginService.class.getName()); - - private EventListenerList listenerList = new EventListenerList(); - - private SwingWorker loginWorker; - - /* - * Controls the authentication behaviour to be either synchronous or - * asynchronous - */ - private boolean synchronous; - - private String server; - - public LoginService() { - } - - public LoginService(String server) { - setServer(server); - } - - /** - * This method is intended to be implemented by clients wishing to - * authenticate a user with a given password. Clients should implement the - * authentication in a manner that the authentication can be cancelled at - * any time. - * - * @param name - * username - * @param password - * password - * @param server - * server (optional) - * - * @return true on authentication success - * @throws Exception - */ - public abstract boolean authenticate(String name, char[] password, - String server) throws Exception; - - /** - * Called immediately after a successful authentication. This method should - * return an array of user roles or null if role based permissions are not - * used. - * - * @return per default null - */ - public String[] getUserRoles() { - return null; - } - - /** - * Notifies the LoginService that an already running authentication request - * should be cancelled. This method is intended to be used by clients who - * want to provide user with control over cancelling a long running - * authentication request. - */ - public void cancelAuthentication() { - if (loginWorker != null) { - loginWorker.cancel(true); - } - } - - /** - * This method starts the authentication process and is either synchronous - * or asynchronous based on the synchronous property - * - * @param user - * user - * @param password - * password - * @param server - * server - * @throws Exception - */ - public void startAuthentication(final String user, final char[] password, - final String server) throws Exception { - if (getSynchronous()) { - try { - if (authenticate(user, password, server)) { - fireLoginSucceeded(new LoginEvent(this)); - } else { - fireLoginFailed(new LoginEvent(this)); - } - } catch (Throwable e) { - fireLoginFailed(new LoginEvent(this, e)); - } - } else { - loginWorker = new SwingWorker() { - @Override - protected Boolean doInBackground() throws Exception { - try { - final boolean result = authenticate(user, password, - server); - if (isCancelled()) { - EventQueue.invokeLater(new Runnable() { - public void run() { - fireLoginCanceled(new LoginEvent(this)); - } - }); - return false; - } - EventQueue.invokeLater(new Runnable() { - public void run() { - if (result) { - fireLoginSucceeded(new LoginEvent( - LoginService.this)); - } else { - fireLoginFailed(new LoginEvent( - LoginService.this)); - } - } - }); - return result; - } catch (final Throwable failed) { - if (!isCancelled()) { - SwingUtilities.invokeLater(new Runnable() { - public void run() { - fireLoginFailed(new LoginEvent( - LoginService.this, failed)); - } - }); - } else { - EventQueue.invokeLater(new Runnable() { - public void run() { - fireLoginCanceled(new LoginEvent(this)); - } - }); - } - return false; - } - } - }; - loginWorker.execute(); - fireLoginStarted(new LoginEvent(this)); - } - } - - /** - * Get the synchronous property - * - * @return the synchronous property - */ - public boolean getSynchronous() { - return synchronous; - } - - /** - * Sets the synchronous property - * - * @param synchronous - * synchronous property - */ - public void setSynchronous(boolean synchronous) { - boolean old = getSynchronous(); - this.synchronous = synchronous; - firePropertyChange("synchronous", old, getSynchronous()); - } - - /** - * Adds a LoginListener to the list of listeners - * - * @param listener - * listener - */ - - public void addLoginListener(LoginListener listener) { - listenerList.add(LoginListener.class, listener); - } - - /** - * Removes a LoginListener from the list of listeners - * - * @param listener - * listener - */ - public void removeLoginListener(LoginListener listener) { - listenerList.remove(LoginListener.class, listener); - } - - void fireLoginStarted(final LoginEvent source) { - // Guaranteed to return a non-null array - Object[] listeners = listenerList.getListenerList(); - // Process the listeners last to first, notifying - // those that are interested in this event - for (int i = listeners.length-2; i>=0; i-=2) { - if (listeners[i] == LoginListener.class) { - ((LoginListener) listeners[i+1]).loginStarted(source); - } - } - } - - void fireLoginSucceeded(final LoginEvent source) { - // Guaranteed to return a non-null array - Object[] listeners = listenerList.getListenerList(); - // Process the listeners last to first, notifying - // those that are interested in this event - for (int i = listeners.length-2; i>=0; i-=2) { - if (listeners[i] == LoginListener.class) { - ((LoginListener) listeners[i+1]).loginSucceeded(source); - } - } - } - - void fireLoginFailed(final LoginEvent source) { - // Guaranteed to return a non-null array - Object[] listeners = listenerList.getListenerList(); - // Process the listeners last to first, notifying - // those that are interested in this event - for (int i = listeners.length-2; i>=0; i-=2) { - if (listeners[i] == LoginListener.class) { - ((LoginListener) listeners[i+1]).loginFailed(source); - } - } - } - - void fireLoginCanceled(final LoginEvent source) { - // Guaranteed to return a non-null array - Object[] listeners = listenerList.getListenerList(); - // Process the listeners last to first, notifying - // those that are interested in this event - for (int i = listeners.length-2; i>=0; i-=2) { - if (listeners[i] == LoginListener.class) { - ((LoginListener) listeners[i+1]).loginCanceled(source); - } - } - } - - /** - * @return Returns the server. - */ - public String getServer() { - return server; - } - - /** - * @param server - * The server to set. - */ - public void setServer(String server) { - String old = getServer(); - this.server = server; - firePropertyChange("server", old, getServer()); - } -} diff --git a/src/main/java/org/jdesktop/swingx/auth/PasswordStore.java b/src/main/java/org/jdesktop/swingx/auth/PasswordStore.java deleted file mode 100644 index 425c72a25c..0000000000 --- a/src/main/java/org/jdesktop/swingx/auth/PasswordStore.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * $Id: PasswordStore.java 3343 2009-05-25 00:33:12Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.auth; - -/** - * PasswordStore specifies a mechanism to store passwords used to authenticate - * using the LoginService. The actual mechanism used - * to store the passwords is left up to the implementation. - * - * @author Bino George - * @author Jonathan Giles - */ -public abstract class PasswordStore { - /** - * Saves a password for future use. - * - * @param username username used to authenticate. - * @param server server used for authentication - * @param password password to save. Password can't be null. Use empty array for empty password. - */ - public abstract boolean set(String username, String server, char[] password); - - /** - * Fetches the password for a given server and username. - * @param username username - * @param server server - * @return null if not found, a character array representing the password - * otherwise. Returned array can be empty if the password is empty. - */ - public abstract char[] get(String username, String server); - - /** - * This should attempt to remove the given username from the password store, as well as any associated password. - * @param username The username to remove - */ - public abstract void removeUserPassword(String username); -} diff --git a/src/main/java/org/jdesktop/swingx/auth/SimpleLoginService.java b/src/main/java/org/jdesktop/swingx/auth/SimpleLoginService.java deleted file mode 100644 index 5e4d5c782f..0000000000 --- a/src/main/java/org/jdesktop/swingx/auth/SimpleLoginService.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * $Id: SimpleLoginService.java 3475 2009-08-28 08:30:47Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.auth; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - -/** - * An implementation of LoginService that simply matches - * the username/password against a list of known users and their passwords. - * This is useful for demos or prototypes where a proper login server is not available. - * - * This Implementation is NOT secure. DO NOT USE this in a real application - * To make this implementation more secure, the passwords should be passed in and - * stored as the result of a one way hash algorithm. That way an attacker cannot - * simply read the password in memory to crack into the system. - * - * @author rbair - */ -public final class SimpleLoginService extends LoginService { - private Map passwordMap; - - /** - * Creates a new SimpleLoginService based on the given password map. - */ - public SimpleLoginService(Map passwordMap) { - if (passwordMap == null) { - passwordMap = new HashMap(); - } - this.passwordMap = passwordMap; - } - - /** - * Attempts to authenticate the given username and password against the password map - */ - @Override - public boolean authenticate(String name, char[] password, String server) throws Exception { - char[] p = passwordMap.get(name); - return Arrays.equals(password, p); - } -} diff --git a/src/main/java/org/jdesktop/swingx/auth/UserNameStore.java b/src/main/java/org/jdesktop/swingx/auth/UserNameStore.java deleted file mode 100644 index c43f26f5d7..0000000000 --- a/src/main/java/org/jdesktop/swingx/auth/UserNameStore.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * $Id: UserNameStore.java 1387 2006-09-11 22:37:44Z rbair $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.auth; - -import org.jdesktop.beans.AbstractBean; - -/** - * UsernameStore is a class that implements persistence of usernames - * - * @author Bino George - * @author rbair - */ -public abstract class UserNameStore extends AbstractBean { - /** - * Gets the current list of users. - */ - public abstract String[] getUserNames(); - /** - */ - public abstract void setUserNames(String[] names); - /** - * lifecycle method for loading names from persistent storage - */ - public abstract void loadUserNames(); - /** - * lifecycle method for saving name to persistent storage - */ - public abstract void saveUserNames(); - /** - */ - public abstract boolean containsUserName(String name); - - /** - * Add a username to the store. - * @param userName - */ - public abstract void addUserName(String userName); - - /** - * Removes a username from the list. - * @param userName - */ - public abstract void removeUserName(String userName); -} diff --git a/src/main/java/org/jdesktop/swingx/auth/UserPermissions.java b/src/main/java/org/jdesktop/swingx/auth/UserPermissions.java deleted file mode 100644 index 03811536d9..0000000000 --- a/src/main/java/org/jdesktop/swingx/auth/UserPermissions.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * $Id: UserPermissions.java 542 2005-10-10 18:03:15Z rbair $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.auth; - -import java.beans.PropertyChangeListener; -import java.beans.PropertyChangeSupport; - -/** - * This is a singleton that marks the set of permissions for a given logged in user. - * It is one of the optional results of a successful login operation. - * The purpose of this class is to provide a central location and client side bridge - * to the server side permissions and user roles (see J2EE role based authorization). - * This class is used by gui widgets and actions to determine visibility and enabled - * status and thus a UI can adapt itself to users with a lower set of privileges. - * - * This class is not meant as a secure barrier! It is only a thin layer to supplant the - * server side permissions. This class can be compromized by the user and thus its purpose - * is only to help UI flow and navigation and not to prevent attack against a client side - * UI. A server implementation must ALWAYS recheck permissions sent by the client regardless - * of the client. - * - * @author Shai Almog - */ -public class UserPermissions { - private static final UserPermissions INSTANCE = new UserPermissions(); - private PropertyChangeSupport propertyChange = new PropertyChangeSupport(this); - private String[] roles; - - /** Creates a new instance of UserPermissions */ - private UserPermissions() { - } - - public void addPropertyChangeListener(PropertyChangeListener listener) { - propertyChange.addPropertyChangeListener(listener); - } - - public void addPropertyChangeListener(String name, PropertyChangeListener listener) { - propertyChange.addPropertyChangeListener(name, listener); - } - - public void removePropertyChangeListener(PropertyChangeListener listener) { - propertyChange.removePropertyChangeListener(listener); - } - - public void removePropertyChangeListener(String name, PropertyChangeListener listener) { - propertyChange.removePropertyChangeListener(name, listener); - } - - /** - * Returns the singleton instance of this class. A singleton is used to simplify access for - * the permissions from every point in the application. - */ - public static UserPermissions getInstance() { - return INSTANCE; - } - - /** - * Returns the roles of the currently logged in user - */ - public String[] getRoles() { - return roles; - } - - /** - * Returns true if the user is in the given role (case sensitive). - */ - public boolean isUserInRole(String role) { - if(roles != null) { - for(int iter = 0 ; iter < roles.length ; iter++) { - if(roles[iter].equals(role)) { - return true; - } - } - } - return false; - } - - /** - * Returns true if the user is in one of the given roles (case sensitive). - */ - public boolean isUserInARole(String[] roles) { - for(int iter = 0 ; iter < roles.length ; iter++) { - if(isUserInRole(roles[iter])) { - return true; - } - } - return false; - } - - /** - * Returns true if the user is in all of the given roles (case sensitive). - */ - public boolean isUserInRoles(String[] roles) { - for(int iter = 0 ; iter < roles.length ; iter++) { - if(!isUserInRole(roles[iter])) { - return false; - } - } - return true; - } - - void setRoles(String[] roles) { - String[] oldValue = this.roles; - this.roles = roles; - propertyChange.firePropertyChange("roles", oldValue, roles); - } -} - diff --git a/src/main/java/org/jdesktop/swingx/auth/package-info.java b/src/main/java/org/jdesktop/swingx/auth/package-info.java deleted file mode 100644 index c55784938b..0000000000 --- a/src/main/java/org/jdesktop/swingx/auth/package-info.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -/** - * Contains classes and interfaces used by the {@code JXLoginPane} component. - */ -package org.jdesktop.swingx.auth; - diff --git a/src/main/java/org/jdesktop/swingx/plaf/linux/LinuxLookAndFeelAddons.java b/src/main/java/org/jdesktop/swingx/plaf/linux/LinuxLookAndFeelAddons.java index 854b4a1bea..570db6471a 100644 --- a/src/main/java/org/jdesktop/swingx/plaf/linux/LinuxLookAndFeelAddons.java +++ b/src/main/java/org/jdesktop/swingx/plaf/linux/LinuxLookAndFeelAddons.java @@ -20,9 +20,9 @@ */ package org.jdesktop.swingx.plaf.linux; +import org.apache.commons.lang3.SystemUtils; import org.jdesktop.swingx.plaf.LookAndFeelAddons; import org.jdesktop.swingx.plaf.basic.BasicLookAndFeelAddons; -import org.jdesktop.swingx.util.OS; import org.kohsuke.MetaInfServices; import static javax.swing.UIManager.getLookAndFeel; @@ -43,7 +43,7 @@ protected boolean matches() { */ @Override protected boolean isSystemAddon() { - return OS.isLinux(); + return SystemUtils.IS_OS_LINUX; } /** diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsLookAndFeelAddons.java b/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsLookAndFeelAddons.java index 629994fa45..c09c489940 100644 --- a/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsLookAndFeelAddons.java +++ b/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsLookAndFeelAddons.java @@ -20,6 +20,7 @@ */ package org.jdesktop.swingx.plaf.windows; +import org.apache.commons.lang3.SystemUtils; import org.jdesktop.swingx.plaf.LookAndFeelAddons; import org.jdesktop.swingx.plaf.basic.BasicLookAndFeelAddons; import org.jdesktop.swingx.util.OS; @@ -61,7 +62,7 @@ protected boolean matches() { */ @Override protected boolean isSystemAddon() { - return OS.isWindows() && OS.isUsingWindowsVisualStyles(); + return SystemUtils.IS_OS_WINDOWS && OS.isUsingWindowsVisualStyles(); } // JW: reverting ... diff --git a/src/main/java/org/jdesktop/swingx/util/OS.java b/src/main/java/org/jdesktop/swingx/util/OS.java index a8d1f17570..04c99d1456 100644 --- a/src/main/java/org/jdesktop/swingx/util/OS.java +++ b/src/main/java/org/jdesktop/swingx/util/OS.java @@ -20,6 +20,8 @@ */ package org.jdesktop.swingx.util; +import org.apache.commons.lang3.SystemUtils; + import javax.swing.*; import java.awt.*; @@ -29,67 +31,40 @@ @SuppressWarnings("nls") public class OS { - private static final boolean osIsMacOsX; - private static final boolean osIsWindows; - private static final boolean osIsWindowsXP; - private static final boolean osIsWindows2003; - private static final boolean osIsWindowsVista; - private static final boolean osIsLinux; - - static { - String os = System.getProperty("os.name"); - if (os != null) - os = os.toLowerCase(); - - osIsMacOsX = "mac os x".equals(os); - osIsWindows = os != null && os.indexOf("windows") != -1; - osIsWindowsXP = "windows xp".equals(os); - osIsWindows2003 = "windows 2003".equals(os); - osIsWindowsVista = "windows vista".equals(os); - osIsLinux = os != null && os.indexOf("linux") != -1; - } - /** * @return true if this VM is running on Mac OS X */ public static boolean isMacOSX() { - return osIsMacOsX; + return SystemUtils.IS_OS_MAC_OSX; } /** * @return true if this VM is running on Windows */ public static boolean isWindows() { - return osIsWindows; + return SystemUtils.IS_OS_WINDOWS; } /** * @return true if this VM is running on Windows XP */ public static boolean isWindowsXP() { - return osIsWindowsXP; + return SystemUtils.IS_OS_WINDOWS_XP; } /** * @return true if this VM is running on Windows Vista */ public static boolean isWindowsVista() { - return osIsWindowsVista; + return SystemUtils.IS_OS_WINDOWS_VISTA; } - - /** - * @return true if this VM is running on a Linux distribution - */ - public static boolean isLinux() { - return osIsLinux; - } - + /** * @return true if the VM is running Windows and the Java * application is rendered using XP Visual Styles. */ public static boolean isUsingWindowsVisualStyles() { - if (!isWindows()) { + if (!SystemUtils.IS_OS_WINDOWS) { return false; } From e89e902768540b5c179a7e015b59ae0e9bfc9edb Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 6 Jul 2022 11:17:05 +0200 Subject: [PATCH 126/422] - remove old windows version support & modernize color settings Former-commit-id: 44fde7b1c979c5c70b3710957531b742e0795df4 --- .../jdesktop/swingx/plaf/DatePickerAddon.java | 10 +---- .../jdesktop/swingx/plaf/TaskPaneAddon.java | 37 +++++++----------- .../swingx/plaf/TaskPaneContainerAddon.java | 16 +++----- .../plaf/windows/resources/combo-w2k.png | Bin 2833 -> 0 bytes .../java/org/jdesktop/swingx/util/OS.java | 14 ------- 5 files changed, 21 insertions(+), 56 deletions(-) delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/combo-w2k.png diff --git a/src/main/java/org/jdesktop/swingx/plaf/DatePickerAddon.java b/src/main/java/org/jdesktop/swingx/plaf/DatePickerAddon.java index f9b35a0162..0f30a856c3 100644 --- a/src/main/java/org/jdesktop/swingx/plaf/DatePickerAddon.java +++ b/src/main/java/org/jdesktop/swingx/plaf/DatePickerAddon.java @@ -22,7 +22,6 @@ import org.jdesktop.swingx.JXDatePicker; import org.jdesktop.swingx.plaf.basic.BasicDatePickerUI; -import org.jdesktop.swingx.util.OS; import javax.swing.*; import javax.swing.border.LineBorder; @@ -59,13 +58,8 @@ protected void addBasicDefaults(LookAndFeelAddons addon, DefaultsList defaults) @Override protected void addWindowsDefaults(LookAndFeelAddons addon, DefaultsList defaults) { super.addWindowsDefaults(addon, defaults); - if (OS.isWindowsXP() && OS.isUsingWindowsVisualStyles()) { - defaults.add("JXDatePicker.arrowIcon", - LookAndFeel.makeIcon(DatePickerAddon.class, "windows/resources/combo-xp.png")); - } else { - defaults.add("JXDatePicker.arrowIcon", - LookAndFeel.makeIcon(DatePickerAddon.class, "windows/resources/combo-w2k.png")); - } + defaults.add("JXDatePicker.arrowIcon", + LookAndFeel.makeIcon(DatePickerAddon.class, "windows/resources/combo-xp.png")); } /** diff --git a/src/main/java/org/jdesktop/swingx/plaf/TaskPaneAddon.java b/src/main/java/org/jdesktop/swingx/plaf/TaskPaneAddon.java index c90ffe01be..c4485b16b2 100644 --- a/src/main/java/org/jdesktop/swingx/plaf/TaskPaneAddon.java +++ b/src/main/java/org/jdesktop/swingx/plaf/TaskPaneAddon.java @@ -126,30 +126,21 @@ protected void addWindowsDefaults(LookAndFeelAddons addon, DefaultsList defaults defaults.add("TaskPane.specialTitleForeground", new ColorUIResource(Color.BLACK)); defaults.add("TaskPane.specialTitleOver", new ColorUIResource(126, 124, 124)); defaults.add("TaskPane.borderColor", new ColorUIResource(Color.WHITE)); - } else if (OS.isWindowsVista()) { + } else { //do not need to use safe method since the properties can never return null - final Toolkit toolkit = Toolkit.getDefaultToolkit(); - - defaults.add("TaskPane.foreground", new ColorUIResource(Color.WHITE)); - defaults.add("TaskPane.background", - new ColorUIResource((Color)toolkit.getDesktopProperty("win.3d.backgroundColor"))); - defaults.add("TaskPane.specialTitleBackground", new ColorUIResource(33, 89, 201)); - defaults.add("TaskPane.titleBackgroundGradientStart", new ColorUIResource(Color.WHITE)); - defaults.add("TaskPane.titleBackgroundGradientEnd", - new ColorUIResource((Color)toolkit.getDesktopProperty("win.frame.inactiveCaptionColor"))); - defaults.add("TaskPane.titleForeground", - new ColorUIResource((Color)toolkit.getDesktopProperty("win.frame.inactiveCaptionTextColor"))); - defaults.add("TaskPane.specialTitleForeground", new ColorUIResource(Color.WHITE)); - defaults.add("TaskPane.borderColor", new ColorUIResource(Color.WHITE)); - } else { - defaults.add("TaskPane.foreground", new ColorUIResource(Color.WHITE)); - defaults.add("TaskPane.background", new ColorUIResource(214, 223, 247)); - defaults.add("TaskPane.specialTitleBackground", new ColorUIResource(33, 89, 201)); - defaults.add("TaskPane.titleBackgroundGradientStart", new ColorUIResource(Color.WHITE)); - defaults.add("TaskPane.titleBackgroundGradientEnd", new ColorUIResource(199, 212, 247)); - defaults.add("TaskPane.titleForeground", new ColorUIResource(33, 89, 201)); - defaults.add("TaskPane.specialTitleForeground", new ColorUIResource(Color.WHITE)); - defaults.add("TaskPane.borderColor", new ColorUIResource(Color.WHITE)); + final Toolkit toolkit = Toolkit.getDefaultToolkit(); + + defaults.add("TaskPane.foreground", new ColorUIResource(Color.WHITE)); + defaults.add("TaskPane.background", + new ColorUIResource((Color)toolkit.getDesktopProperty("win.3d.backgroundColor"))); + defaults.add("TaskPane.specialTitleBackground", new ColorUIResource(33, 89, 201)); + defaults.add("TaskPane.titleBackgroundGradientStart", new ColorUIResource(Color.WHITE)); + defaults.add("TaskPane.titleBackgroundGradientEnd", + new ColorUIResource((Color)toolkit.getDesktopProperty("win.frame.inactiveCaptionColor"))); + defaults.add("TaskPane.titleForeground", + new ColorUIResource((Color)toolkit.getDesktopProperty("win.frame.inactiveCaptionTextColor"))); + defaults.add("TaskPane.specialTitleForeground", new ColorUIResource(Color.WHITE)); + defaults.add("TaskPane.borderColor", new ColorUIResource(Color.WHITE)); } } } diff --git a/src/main/java/org/jdesktop/swingx/plaf/TaskPaneContainerAddon.java b/src/main/java/org/jdesktop/swingx/plaf/TaskPaneContainerAddon.java index 52b7fba679..4dfc7e027b 100644 --- a/src/main/java/org/jdesktop/swingx/plaf/TaskPaneContainerAddon.java +++ b/src/main/java/org/jdesktop/swingx/plaf/TaskPaneContainerAddon.java @@ -92,17 +92,11 @@ protected void addWindowsDefaults(LookAndFeelAddons addon, DefaultsList defaults background = new ColorUIResource(192, 195, 209); backgroundGradientStart = new Color(196, 200, 212); backgroundGradientEnd = new Color(177, 179, 200); - } else { - if (OS.isWindowsVista()) { - final Toolkit toolkit = Toolkit.getDefaultToolkit(); - background = new ColorUIResource((Color)toolkit.getDesktopProperty("win.3d.backgroundColor")); - backgroundGradientStart = (Color)toolkit.getDesktopProperty("win.frame.activeCaptionColor"); - backgroundGradientEnd = (Color)toolkit.getDesktopProperty("win.frame.inactiveCaptionColor"); - } else { - background = new ColorUIResource(117, 150, 227); - backgroundGradientStart = new ColorUIResource(123, 162, 231); - backgroundGradientEnd = new ColorUIResource(99, 117, 214); - } + } else { + final Toolkit toolkit = Toolkit.getDefaultToolkit(); + background = new ColorUIResource((Color)toolkit.getDesktopProperty("win.3d.backgroundColor")); + backgroundGradientStart = (Color)toolkit.getDesktopProperty("win.frame.activeCaptionColor"); + backgroundGradientEnd = (Color)toolkit.getDesktopProperty("win.frame.inactiveCaptionColor"); } defaults.add("TaskPaneContainer.backgroundPainter", new PainterUIResource( diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/combo-w2k.png b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/combo-w2k.png deleted file mode 100644 index e109f6653f880f195f2772be1a01a078ae62c23b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2833 zcmV+s3-0uZP)KLZ*U+5Lu!Sk^o_Z5E4Meg@_7P6crJiNL9pw)e1;Xm069{HJUZAPk55R%$-RIA z6-eL&AQ0xu!e<4=008gy@A0LT~suv4>S3ILP<0Bm`DLLvaF4FK%)Nj?Pt*r}7;7Xa9z9H|HZjR63e zC`Tj$K)V27Re@400>HumpsYY5E(E}?0f1SyGDiY{y#)Yvj#!WnKwtoXnL;eg03bL5 z07D)V%>y7z1E4U{zu>7~aD})?0RX_umCct+(lZpemCzb@^6=o|A>zVpu|i=NDG+7} zl4`aK{0#b-!z=TL9Wt0BGO&T{GJWpjryhdijfaIQ&2!o}p04JRKYg3k&Tf zVxhe-O!X z{f;To;xw^bEES6JSc$k$B2CA6xl)ltA<32E66t?3@gJ7`36pmX0IY^jz)rRYwaaY4 ze(nJRiw;=Qb^t(r^DT@T3y}a2XEZW-_W%Hszxj_qD**t_m!#tW0KDiJT&R>6OvVTR z07RgHDzHHZ48atvzz&?j9lXF70$~P3Knx_nJP<+#`N z#-MZ2bTkiLfR>_b(HgWKJ%F~Nr_oF3b#wrIijHG|(J>BYjM-sajE6;FiC7vY#};Gd zST$CUHDeuEH+B^pz@B062qXfFfD`NpUW5?BY=V%GM_5c)L#QR}BeW8_2v-S%gfYS= zB9o|3v?Y2H`NVi)In3rTB8+ej^> zQ=~r95NVuDChL%G$=>7$vVg20myx%S50Foi`^m%Pw-h?Xh~i8Mq9jtJloCocWk2Nv zrJpiFnV_ms&8eQ$2&#xWpIS+6pmtC%Q-`S&GF4Q#^mhymh7E(qNMa}%YZ-ePrx>>xFPTiH1=E+A$W$=bG8>s^ zm=Bn5Rah$aDtr}@$`X}2l~$F0mFKEdRdZE8)p@E5RI61Ft6o-prbbn>P~)iy)E2AN zsU20jsWz_8Qg>31P|s0cqrPALg8E|(vWA65poU1JRAaZs8I2(p#xiB`SVGovRs-uS zYnV-9TeA7=Om+qP8+I>yOjAR1s%ETak!GFdam@h^# z)@rS0t$wXH+Irf)+G6c;?H29p+V6F6oj{!|o%K3xI`?%6x;DB|x`n#ibhIR?(H}Q3Gzd138Ei2)WAMz7W9Vy`X}HnwgyEn!VS)>mv$8&{hQn>w4zwy3R}t;BYlZQm5)6pty=DfLrs+A-|>>;~;Q z_F?uV_HFjh9n2gO9o9Q^JA86v({H5aB!kjoO6 zc9$1ZZKsN-Zl8L~mE{`ly3)1N^`o1+o7}D0ZPeY&J;i;i`%NyJ8_8Y6J?}yE@b_5a zam?eLr<8@mESk|3$_SkmS{wQ>%qC18))9_|&j{ZT zes8AvOzF(F2#DZEY>2oYX&IRp`F#{ADl)1r>QS^)ba8a|EY_^#S^HO&t^Rgqwv=MZThqqEWH8 zxJo>d=ABlR_Bh=;eM9Tw|Ih34~oTE|= zX_mAr*D$vzw@+p(E0Yc6dFE}(8oqt`+R{gE3x4zjX+Sb3_cYE^= zgB=w+-tUy`ytONMS8KgRef4hA?t0j zufM;t32jm~jUGrkaOInTZ`zyfns>EuS}G30LFK_G-==(f<51|K&cocp&EJ`SxAh3? zNO>#LI=^+SEu(FqJ)ynt=!~PC9bO$rzPJB=?=j6w@a-(u02P7 zaQ)#(uUl{HW%tYNS3ItC^iAtK(eKlL`f9+{bJzISE?u8_z3;~C8@FyI-5j_jy7l;W z_U#vU3hqqYU3!mrul&B+{ptt$59)uk{;_4iZQ%G|z+lhASr6|H35TBkl>gI*;nGLU zN7W-nBaM%pA0HbH8olyl&XeJ%vZoWz%6?Y=dFykl=imL}`%BMQ{Mhgd`HRoLu6e2R za__6DuR6yg#~-}Tc|Gx_{H@O0eebyMy5GmWADJlpK>kqk(fVV@r_fLLKIeS?{4e)} z^ZO;zpECde00d`2O+f$vv5tKEQIh}w03c&XQcVB=dL;k=fP(-4`Tqa_faw4Lbua(` z>RI+y?e7jKeZ#YO-C04_;HK~#9!bc#U=05A-LO#c68cN&b+$hvK}|2r j0MIGk;{gBw|NjF3hvgFc{mQf)00000NkvXXu0mjf* Date: Wed, 6 Jul 2022 11:25:40 +0200 Subject: [PATCH 127/422] - code cleanup Former-commit-id: 815067c52adb88be2b5d8633f9bfb110951e38e4 --- .../swingx/JXColorSelectionButton.java | 32 ++++++------------- .../swingx/plaf/PromptTextFieldUI.java | 6 ++-- .../plaf/macosx/MacOSXLookAndFeelAddons.java | 4 +-- .../search/NativeSearchFieldSupport.java | 4 +-- .../java/org/jdesktop/swingx/util/OS.java | 14 -------- 5 files changed, 17 insertions(+), 43 deletions(-) diff --git a/src/main/java/org/jdesktop/swingx/JXColorSelectionButton.java b/src/main/java/org/jdesktop/swingx/JXColorSelectionButton.java index ef70db084f..c6aac5a90f 100644 --- a/src/main/java/org/jdesktop/swingx/JXColorSelectionButton.java +++ b/src/main/java/org/jdesktop/swingx/JXColorSelectionButton.java @@ -21,10 +21,10 @@ package org.jdesktop.swingx; +import org.apache.commons.lang3.SystemUtils; import org.jdesktop.swingx.color.EyeDropperColorChooserPanel; import org.jdesktop.swingx.plaf.UIManagerExt; import org.jdesktop.swingx.util.GraphicsUtilities; -import org.jdesktop.swingx.util.OS; import org.jdesktop.swingx.util.PaintUtils; import javax.imageio.ImageIO; @@ -36,8 +36,6 @@ import java.awt.event.ActionListener; import java.awt.geom.Ellipse2D; import java.awt.image.BufferedImage; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; import static java.awt.RenderingHints.KEY_ANTIALIASING; import static java.awt.RenderingHints.VALUE_ANTIALIAS_ON; @@ -83,11 +81,7 @@ public JXColorSelectionButton(Color col) { ex.printStackTrace(); } - this.addPropertyChangeListener("background",new PropertyChangeListener() { - public void propertyChange(PropertyChangeEvent propertyChangeEvent) { - getChooser().setColor(getBackground()); - } - }); + this.addPropertyChangeListener("background", propertyChangeEvent -> getChooser().setColor(getBackground())); } @@ -115,7 +109,7 @@ protected void paintComponent(Graphics g) { : UIManagerExt.getSafeColor("Button.disabledForeground", Color.LIGHT_GRAY); // draw the colorwell image (should only be on OSX) - if(OS.isMacOSX() && colorwell != null) { + if(SystemUtils.IS_OS_MAC_OSX && colorwell != null) { Insets ins = new Insets(5,5,5,5); GraphicsUtilities.tileStretchPaint(g, this, colorwell, ins); @@ -176,19 +170,13 @@ private void showDialog() { if (dialog == null) { dialog = JColorChooser.createDialog(JXColorSelectionButton.this, "Choose a color", true, getChooser(), - new ActionListener() { - public void actionPerformed(ActionEvent actionEvent) { - Color color = getChooser().getColor(); - if (color != null) { - setBackground(color); - } - } - }, - new ActionListener() { - public void actionPerformed(ActionEvent actionEvent) { - setBackground(initialColor); - } - }); + actionEvent -> { + Color color = getChooser().getColor(); + if (color != null) { + setBackground(color); + } + }, + actionEvent -> setBackground(initialColor)); dialog.getContentPane().add(getChooser()); getChooser().getSelectionModel().addChangeListener( new ColorChangeListener(JXColorSelectionButton.this)); diff --git a/src/main/java/org/jdesktop/swingx/plaf/PromptTextFieldUI.java b/src/main/java/org/jdesktop/swingx/plaf/PromptTextFieldUI.java index b7ac3cb070..d61db8f2e3 100644 --- a/src/main/java/org/jdesktop/swingx/plaf/PromptTextFieldUI.java +++ b/src/main/java/org/jdesktop/swingx/plaf/PromptTextFieldUI.java @@ -1,7 +1,7 @@ package org.jdesktop.swingx.plaf; +import org.apache.commons.lang3.SystemUtils; import org.jdesktop.swingx.search.NativeSearchFieldSupport; -import org.jdesktop.swingx.util.OS; import javax.swing.*; import javax.swing.border.Border; @@ -162,7 +162,7 @@ public void repaint() { */ @Override protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { - if (OS.isMacOSX()) { + if (SystemUtils.IS_OS_MAC_OSX) { super.firePropertyChange(propertyName, oldValue, newValue); } else { // Strings get interned... @@ -179,7 +179,7 @@ protected void firePropertyChange(String propertyName, Object oldValue, Object n */ @Override public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { - if (OS.isMacOSX()) { + if (SystemUtils.IS_OS_MAC_OSX) { super.firePropertyChange(propertyName, oldValue, newValue); } } diff --git a/src/main/java/org/jdesktop/swingx/plaf/macosx/MacOSXLookAndFeelAddons.java b/src/main/java/org/jdesktop/swingx/plaf/macosx/MacOSXLookAndFeelAddons.java index bf325b6b0a..7cef876040 100644 --- a/src/main/java/org/jdesktop/swingx/plaf/macosx/MacOSXLookAndFeelAddons.java +++ b/src/main/java/org/jdesktop/swingx/plaf/macosx/MacOSXLookAndFeelAddons.java @@ -20,9 +20,9 @@ */ package org.jdesktop.swingx.plaf.macosx; +import org.apache.commons.lang3.SystemUtils; import org.jdesktop.swingx.plaf.LookAndFeelAddons; import org.jdesktop.swingx.plaf.basic.BasicLookAndFeelAddons; -import org.jdesktop.swingx.util.OS; import org.kohsuke.MetaInfServices; import static javax.swing.UIManager.getLookAndFeel; @@ -43,6 +43,6 @@ protected boolean matches() { */ @Override protected boolean isSystemAddon() { - return OS.isMacOSX(); + return SystemUtils.IS_OS_MAC_OSX; } } diff --git a/src/main/java/org/jdesktop/swingx/search/NativeSearchFieldSupport.java b/src/main/java/org/jdesktop/swingx/search/NativeSearchFieldSupport.java index 1ba8e921f9..ff8552715d 100644 --- a/src/main/java/org/jdesktop/swingx/search/NativeSearchFieldSupport.java +++ b/src/main/java/org/jdesktop/swingx/search/NativeSearchFieldSupport.java @@ -1,7 +1,7 @@ package org.jdesktop.swingx.search; +import org.apache.commons.lang3.SystemUtils; import org.jdesktop.swingx.plaf.AbstractUIChangeHandler; -import org.jdesktop.swingx.util.OS; import javax.swing.*; import java.awt.event.ActionListener; @@ -32,7 +32,7 @@ public static boolean isNativeSearchFieldSupported() { // only the part 10.x is important versionString = versionString.substring(0, 4); - return OS.isMacOSX() && Float.parseFloat(versionString) >= 10.5 + return SystemUtils.IS_OS_MAC_OSX && Float.parseFloat(versionString) >= 10.5 && UIManager.getLookAndFeel().getName().equals("Mac OS X"); } catch (Exception e) { // in case the os.version cannot be parsed, we are surely not diff --git a/src/main/java/org/jdesktop/swingx/util/OS.java b/src/main/java/org/jdesktop/swingx/util/OS.java index c621fb72f3..1d21d57e8d 100644 --- a/src/main/java/org/jdesktop/swingx/util/OS.java +++ b/src/main/java/org/jdesktop/swingx/util/OS.java @@ -31,20 +31,6 @@ @SuppressWarnings("nls") public class OS { - /** - * @return true if this VM is running on Mac OS X - */ - public static boolean isMacOSX() { - return SystemUtils.IS_OS_MAC_OSX; - } - - /** - * @return true if this VM is running on Windows - */ - public static boolean isWindows() { - return SystemUtils.IS_OS_WINDOWS; - } - /** * @return true if the VM is running Windows and the Java * application is rendered using XP Visual Styles. From 9306bcc895fbb5a6a69ccfa435649f3b81971211 Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 6 Jul 2022 11:29:03 +0200 Subject: [PATCH 128/422] - remove JXGraph component as we will never use it Former-commit-id: 18ba7b4b883e88a17b416c88b09e1f3ed336ed19 --- .../java/org/jdesktop/swingx/JXGraph.java | 1748 ----------------- .../org/jdesktop/swingx/package-info.java | 4 +- 2 files changed, 1 insertion(+), 1751 deletions(-) delete mode 100644 src/main/java/org/jdesktop/swingx/JXGraph.java diff --git a/src/main/java/org/jdesktop/swingx/JXGraph.java b/src/main/java/org/jdesktop/swingx/JXGraph.java deleted file mode 100644 index 736527213c..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXGraph.java +++ /dev/null @@ -1,1748 +0,0 @@ -/* - * $Id: JXGraph.java 4147 2012-02-01 17:13:24Z kschaefe $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx; - -import org.jdesktop.beans.AbstractBean; -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.painter.Painter; - -import java.awt.*; -import java.awt.event.*; -import java.awt.geom.GeneralPath; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.text.DecimalFormat; -import java.text.NumberFormat; -import java.util.LinkedList; -import java.util.List; - -// TODO: keyboard navigation -// TODO: honor clip rect with text painting -// TODO: let client change zoom multiplier -// TODO: improve text drawing when origin is not on a multiple of majorX/majorY -// TODO: programmatically zoom in and out (or expose ZOOM_MULTIPLIER) - -/** - *

                JXGraph provides a component which can display one or more - * plots on top of a graduated background (or grid.)

                - * - *

                User input

                - * - *

                To help analyze the plots, this component allows the user to pan the - * view by left-clicking and dragging the mouse around. Using the mouse wheel, - * the user is also able to zoom in and out. Clicking the middle button resets - * the view to its original position.

                - * - *

                All user input can be disabled by calling - * {@link #setInputEnabled(boolean)} and passing false. This does not prevent - * subclasses from registering their own event listeners, such as mouse or key - * listeners.

                - * - *

                Initializing the component and setting the view

                - * - *

                Whenever a new instance of this component is created, the grid boundaries, - * or view, must be defined. The view is comprised of several elements whose - * descriptions are the following:

                - * - *
                  - *
                • minX: Minimum value initially displayed by the component on the - * X axis (horizontally.)
                • - *
                • minY: Minimum value initially displayed by the component on the - * Y axis (vertically.)
                • - *
                • maxX: Maximum value initially displayed by the component on the - * X axis (horizontally.)
                • - *
                • maxY: Maximum value initially displayed by the component on the - * Y axis (vertically.)
                • - *
                • originX: Origin on the X axis of the vertical axis.
                • - *
                • originY: Origin on the Y axis of the horizontal axis.
                • - *
                • majorX: Distance between two major vertical lines of the - * grid.
                • - *
                • majorY: Distance between two major horizontal lines of the - * grid.
                • - *
                • minCountX: Number of minor vertical lines between two major - * vertical lines in the grid.
                • - *
                • minCountY: Number of minor horizontal lines between two major - * horizontal lines in the grid.
                • - *
                - * - *

                View and origin

                - * - *

                The default constructor defines a view bounds by -1.0 and - * +1.0 on both axis, and centered on an origin at - * (0, 0).

                - * - *

                To simplify the API, the origin can be read and written with a - * Point2D instance (see {@link #getOrigin()} and - * {@link #setOrigin(Point2D)}.)

                - * - *

                Likewise, the view can be read and written with a - * Rectangle2D instance (see {@link #getView()} and - * {@link #setView(Rectangle2D)}.) In this case, you need not to define the - * maximum boundaries of the view. Instead, you need to set the origin of the - * rectangle as the minimum boundaries. The width and the height of the - * rectangle define the distance between the minimum and maximum boundaries. For - * instance, to set the view to minX=-1.0, maxX=1.0, minY=-1.0 and maxY=1.0 you - * can use the following rectangle:

                - * - *
                new Rectangle2D.Double(-1.0d, -1.0d, 2.0d, 2.0d);
                - * - *

                You can check the boundaries by calling Rectangle2D.getMaxX() - * and Rectangle2D.getMaxY() once your rectangle has been - * created.

                - * - *

                Alternatively, you can set the view and the origin at the same time by - * calling the method {@link #setViewAndOrigin(Rectangle2D)}. Calling this - * method will set the origin so as to center it in the view defined by the - * rectangle.

                - * - *

                Grid lines

                - * - *

                By default, the component defines a spacing of 0.2 units between two - * major grid lines. It also defines 4 minor grid lines between two major - * grid lines. The spacing between major grid lines and the number of minor - * grid lines can be accessed through the getters {@link #getMajorX()}, - * {@link #getMajorY()}, {@link #getMinorCountX()} and - * {@link #getMinorCountY()}.

                - * - *

                You can change the number of grid lines at runtime by calling the setters - * {@link #setMajorX(double)}, {@link #setMajorY(double)}, - * {@link #setMinorCountX(int)} and {@link #setMinorCountY(int)}.

                - * - *

                Appearance

                - * - *

                Although it provides sensible defaults, this component lets you change - * its appearance in several ways. It is possible to modify the colors of the - * graph by calling the setters {@link #setAxisColor(Color)}, - * {@link #setMajorGridColor(Color)} and {@link #setMinorGridColor(Color)}.

                - * - *

                You can also enable or disable given parts of the resulting graph by - * calling the following setters:

                - * - *
                  - *
                • {@link #setAxisPainted(boolean)}: Defines whether the main axis (see - * {@link #getOrigin()}) is painted.
                • - *
                • {@link #setBackgroundPainted(boolean)}: Defines whether the background - * is painted (see {@link #setBackground(Color)}.)
                • - *
                • {@link #setGridPainted(boolean)}: Defines whether the grid is - * painted.
                • - *
                • {@link #setTextPainted(boolean)}: Defines whether the axis labels are - * painted.
                • - *
                - * - *

                Usage example

                - * - *

                The following code snippet creates a new graph centered on - * (0, 0), bound to the view [-1.0 1.0 -1.0 1.0], with - * a major grid line every 0.5 units and a minor grid line count of 5:

                - * - *
                - * Point2D origin = new Point2D.Double(0.0d, 0.0d);
                - * Rectangle2D view = new Rectangle2D.Double(-1.0d, 1.0d, 2.0d, 2.0d);
                - * JXGraph graph = new JXGraph(origin, view, 0.5d, 5, 0.5d, 5);
                - * 
                - * - *

                Plots

                - * - *

                Definition

                - * - *

                A plot is defined by a mathematical transformation that, given a value on - * the graph's X axis, returns a value on the Y axis. The component draws the - * result by plotting a spot of color at the coordinates defined by - * (X, f(X)) where f() is the aforementionned - * mathematical transformation. Given the following transformation:

                - * - *
                - * f(X) = X * 2.0
                - * 
                - * - *

                For X=1.0, the component will show a spot of color at the - * coordinates (1.0, 2.0).

                - * - *

                Creating a new plot

                - * - *

                Every plot drawn by the component must be a subclass of - * {@link Plot}. This abstract public class defines a single method to - * be implemented by its children:

                - * - *
                - * public double compute(double value)
                - * 
                - * - *

                The previous example can be defined by a concrete - * JXGraph.Plot as follow:

                - * - *
                - * class TwiceTheValuePlot extends JXGraph.Plot {
                - *     public double compute(double value) {
                - *         return value * 2.0d;
                - *     }
                - * }
                - * 
                - * - *

                Most of the time though, a plot requires supplementary parameters. For - * instance, let's define the X axis of your graph as the mass of an object. To - * compute the weight of the object given its mass, you need to use the - * acceleration of gravity (w=m*g where g is the - * acceleration.) To let the user modify this last parameter, to compute his - * weight at the surface of the moon for instance, you need to add a parameter - * to your plot.

                - * - *

                While JXGraph.Plot does not give you an API for such a - * purpose, it does define an event dispatching API (see - * {@link JXGraph#firePropertyChange(String, double, double)}.) Whenever a - * plot is added to the graph, the component registers itself as a property - * listener of the plot. If you take care of firing events whenever the user - * changes a parameter of your plot, the graph will automatically update its - * display. While not mandatory, it is highly recommended to leverage this - * API.

                - * - *

                Adding and removing plots to and from the graph

                - * - *

                To add a plot to the graph, simply call the method - * {@link #addPlots(Color, Plot...)}. You can use it to add one or more - * plots at the same time and associate them with a color. This color is used - * when drawing the plots:

                - * - *
                - * JXGraph.Plot plot = new TwiceTheValuePlot();
                - * graph.addPlots(Color.BLUE, plot);
                - * 
                - * - *

                These two lines will display our previously defined plot in blue on - * screen. Removing one or several plots is as simple as calling the method - * {@link #removePlots(Plot...)}. You can also remove all plots at once - * with {@link #removeAllPlots()}.

                - * - *

                Painting more information

                - * - *

                How to draw on the graph

                - * - *

                If you need to add more information on the graph you need to extend - * it and override the method {@link #paintExtra(Graphics2D)}. This - * method has a default empty implementation and is called after everything - * has been drawn. Its sole parameter is a reference to the component's drawing - * surface, as configured by {@link #setupGraphics(Graphics2D)}. By default, the - * setup method activates antialising but it can be overriden to change the - * drawing surface. (Translation, rotation, new rendering hints, etc.)

                - * - *

                Getting the right coordinates

                - * - *

                To properly draw on the graph you will need to perform a translation - * between the graph's coordinates and the screen's coordinates. The component - * defines 4 methods to assist you in this task:

                - * - *
                  - *
                • {@link #xPixelToPosition(double)}: Converts a pixel coordinate on the - * X axis into a world coordinate.
                • - *
                • {@link #xPositionToPixel(double)}: Converts a world coordinate on the - * X axis into a pixel coordinate.
                • - *
                • {@link #yPixelToPosition(double)}: Converts a pixel coordinate on the - * Y axis into a world coordinate.
                • - *
                • {@link #yPositionToPixel(double)}: Converts a world coordinate on the - * Y axis into a pixel coordinate.
                • - *
                - * - *

                If you have defined a graph view centered on the origin - * (0, 0), the origin of the graph will be at the exact center of - * the screen. That means the world coordinates (0, 0) are - * equivalent to the pixel coordinates (width / 2, height / 2). - * Thus, calling xPositionToPixel(0.0d) would give you the same - * value as the expression getWidth() / 2.0d.

                - * - *

                Converting from world coordinates to pixel coordinates is mostly used to - * draw the result of a mathematical transformation. Converting from pixel - * coordinates to world coordinates is mostly used to get the position in the - * world of a mouse event.

                - * - * @see Plot - * @author Romain Guy - */ -@JavaBean -public class JXGraph extends JXPanel { - // stroke widths used to draw the main axis and the grid - // the main axis is slightly thicker - private static final float STROKE_AXIS = 1.2f; - private static final float STROKE_GRID = 1.0f; - - // defines by how much the view is shrinked or expanded everytime the - // user zooms in or out - private static final float ZOOM_MULTIPLIER = 1.1f; - - //listens to changes to plots and repaints the graph - private PropertyChangeListener plotChangeListener; - - // default color of the graph (does not include plots colors) - private Color majorGridColor = Color.GRAY.brighter(); - private Color minorGridColor = new Color(220, 220, 220); - private Color axisColor = Color.BLACK; - - // the list of plots currently known and displayed by the graph - private List plots; - - // view boundaries as defined by the user - private double minX; - private double maxX; - private double minY; - private double maxY; - - // the default view is set when the view is manually changed by the client - // it is used to reset the view in resetView() - private Rectangle2D defaultView; - - // coordinates of the major axis - private double originX; - private double originY; - - // definition of the grid - // various default values are used when the view is reset - private double majorX; - private double defaultMajorX; - private int minorCountX; - private double majorY; - private double defaultMajorY; - private int minorCountY; - - // enables painting layers - private boolean textPainted = true; - private boolean gridPainted = true; - private boolean axisPainted = true; - private boolean backPainted = true; - - // used by the PanHandler to move the view - private Point dragStart; - - // mainFormatter is used for numbers > 0.01 and < 100 - // secondFormatter uses scientific notation - private NumberFormat mainFormatter; - private NumberFormat secondFormatter; - - // input handlers - private boolean inputEnabled = true; - private ZoomHandler zoomHandler; - private PanMotionHandler panMotionHandler; - private PanHandler panHandler; - private ResetHandler resetHandler; - - /** - *

                Creates a new graph display. The following properties are - * automatically set:

                - *
                  - *
                • view: -1.0 to +1.0 on both axis
                • - *
                • origin: At (0, 0)
                • - *
                • grid: Spacing of 0.2 between major lines; minor lines - * count is 4
                • - *
                - */ - public JXGraph() { - this(0.0, 0.0, -1.0, 1.0, -1.0, 1.0, 0.2, 4, 0.2, 4); - } - - /** - *

                Creates a new graph display with the specified view. The following - * properties are automatically set:

                - *
                  - *
                • origin: Center of the specified view
                • - *
                • grid: Spacing of 0.2 between major lines; minor lines - * count is 4
                • - *
                - * - * @param view the rectangle defining the view boundaries - */ - public JXGraph(Rectangle2D view) { - this(new Point2D.Double(view.getCenterX(), view.getCenterY()), - view, 0.2, 4, 0.2, 4); - } - - /** - *

                Creates a new graph display with the specified view and grid lines. - * The origin is set at the center of the view.

                - * - * @param view the rectangle defining the view boundaries - * @param majorX the spacing between two major grid lines on the X axis - * @param minorCountX the number of minor grid lines between two major - * grid lines on the X axis - * @param majorY the spacing between two major grid lines on the Y axis - * @param minorCountY the number of minor grid lines between two major - * grid lines on the Y axis - * @throws IllegalArgumentException if minX >= maxX or minY >= maxY or - * minorCountX < 0 or minorCountY < 0 or - * majorX <= 0.0 or majorY <= 0.0 - */ - public JXGraph(Rectangle2D view, - double majorX, int minorCountX, - double majorY, int minorCountY) { - this(new Point2D.Double(view.getCenterX(), view.getCenterY()), - view, majorX, minorCountX, majorY, minorCountY); - } - - /** - *

                Creates a new graph display with the specified view and origin. - * The following properties are automatically set:

                - *
                  - *
                • grid: Spacing of 0.2 between major lines; minor lines - * count is 4
                • - *
                - * - * @param origin the coordinates of the main axis origin - * @param view the rectangle defining the view boundaries - */ - public JXGraph(Point2D origin, Rectangle2D view) { - this(origin, view, 0.2, 4, 0.2, 4); - } - - /** - *

                Creates a new graph display with the specified view, origin and grid - * lines.

                - * - * @param origin the coordinates of the main axis origin - * @param view the rectangle defining the view boundaries - * @param majorX the spacing between two major grid lines on the X axis - * @param minorCountX the number of minor grid lines between two major - * grid lines on the X axis - * @param majorY the spacing between two major grid lines on the Y axis - * @param minorCountY the number of minor grid lines between two major - * grid lines on the Y axis - * @throws IllegalArgumentException if minX >= maxX or minY >= maxY or - * minorCountX < 0 or minorCountY < 0 or - * majorX <= 0.0 or majorY <= 0.0 - */ - public JXGraph(Point2D origin, Rectangle2D view, - double majorX, int minorCountX, - double majorY, int minorCountY) { - this(origin.getX(), origin.getY(), - view.getMinX(), view.getMaxX(), view.getMinY(), view.getMaxY(), - majorX, minorCountX, majorY, minorCountY); - } - - /** - *

                Creates a new graph display with the specified view, origin and grid - * lines.

                - * - * @param originX the coordinate of the major X axis - * @param originY the coordinate of the major Y axis - * @param minX the minimum coordinate on the X axis for the view - * @param maxX the maximum coordinate on the X axis for the view - * @param minY the minimum coordinate on the Y axis for the view - * @param maxY the maximum coordinate on the Y axis for the view - * @param majorX the spacing between two major grid lines on the X axis - * @param minorCountX the number of minor grid lines between two major - * grid lines on the X axis - * @param majorY the spacing between two major grid lines on the Y axis - * @param minorCountY the number of minor grid lines between two major - * grid lines on the Y axis - * @throws IllegalArgumentException if minX >= maxX or minY >= maxY or - * minorCountX < 0 or minorCountY < 0 or - * majorX <= 0.0 or majorY <= 0.0 - */ - public JXGraph(double originX, double originY, - double minX, double maxX, - double minY, double maxY, - double majorX, int minorCountX, - double majorY, int minorCountY) { - if (minX >= maxX) { - throw new IllegalArgumentException("minX must be < to maxX"); - } - - if (minY >= maxY) { - throw new IllegalArgumentException("minY must be < to maxY"); - } - - if (minorCountX < 0) { - throw new IllegalArgumentException("minorCountX must be >= 0"); - } - - if (minorCountY < 0) { - throw new IllegalArgumentException("minorCountY must be >= 0"); - } - - if (majorX <= 0.0) { - throw new IllegalArgumentException("majorX must be > 0.0"); - } - - if (majorY <= 0.0) { - throw new IllegalArgumentException("majorY must be > 0.0"); - } - - this.originX = originX; - this.originY = originY; - - this.minX = minX; - this.maxX = maxX; - this.minY = minY; - this.maxY = maxY; - - this.defaultView = new Rectangle2D.Double(minX, minY, - maxX - minX, maxY - minY); - - this.setMajorX(this.defaultMajorX = majorX); - this.setMinorCountX(minorCountX); - this.setMajorY(this.defaultMajorY = majorY); - this.setMinorCountY(minorCountY); - - this.plots = new LinkedList(); - - this.mainFormatter = NumberFormat.getInstance(); - this.mainFormatter.setMaximumFractionDigits(2); - - this.secondFormatter = new DecimalFormat("0.##E0"); - - resetHandler = new ResetHandler(); - addMouseListener(resetHandler); - panHandler = new PanHandler(); - addMouseListener(panHandler); - panMotionHandler = new PanMotionHandler(); - addMouseMotionListener(panMotionHandler); - zoomHandler = new ZoomHandler(); - addMouseWheelListener(zoomHandler); - - setBackground(Color.WHITE); - setForeground(Color.BLACK); - - plotChangeListener = new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - repaint(); - } - }; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isOpaque() { - if (!isBackgroundPainted()) { - return false; - } - return super.isOpaque(); - } - - /** - * {@inheritDoc} - * @see #setInputEnabled(boolean) - */ - @Override - public void setEnabled(boolean enabled) { - super.setEnabled(enabled); - setInputEnabled(enabled); - } - - /** - *

                Enables or disables user input on the component. When user input is - * enabled, panning, zooming and view resetting. Disabling input will - * prevent the user from modifying the currently displayed view.

                - *

                Calling {@link #setEnabled(boolean)} disables the component in the - * Swing hierarchy and invokes this method.

                - * - * @param enabled true if user input must be enabled, false otherwise - * @see #setEnabled(boolean) - * @see #isInputEnabled() - */ - public void setInputEnabled(boolean enabled) { - if (inputEnabled != enabled) { - boolean old = isInputEnabled(); - this.inputEnabled = enabled; - - if (enabled) { - addMouseListener(resetHandler); - addMouseListener(panHandler); - addMouseMotionListener(panMotionHandler); - addMouseWheelListener(zoomHandler); - } else { - removeMouseListener(resetHandler); - removeMouseListener(panHandler); - removeMouseMotionListener(panMotionHandler); - removeMouseWheelListener(zoomHandler); - } - - firePropertyChange("inputEnabled", old, isInputEnabled()); - } - } - - /** - *

                Defines whether or not user input is accepted and managed by this - * component. The component is always created with user input enabled.

                - * - * @return true if user input is enabled, false otherwise - * @see #setInputEnabled(boolean) - */ - public boolean isInputEnabled() { - return inputEnabled; - } - - /** - *

                Defines whether or not axis labels are painted by this component. - * The component is always created with text painting enabled.

                - * - * @return true if axis labels are painted, false otherwise - * @see #setTextPainted(boolean) - * @see #getForeground() - */ - public boolean isTextPainted() { - return textPainted; - } - - /** - *

                Enables or disables the painting of axis labels depending on the - * value of the parameter. Text painting is enabled by default.

                - * - * @param textPainted if true, axis labels are painted - * @see #isTextPainted() - * @see #setForeground(Color) - */ - public void setTextPainted(boolean textPainted) { - boolean old = isTextPainted(); - this.textPainted = textPainted; - firePropertyChange("textPainted", old, this.textPainted); - } - - /** - *

                Defines whether or not grids lines are painted by this component. - * The component is always created with grid lines painting enabled.

                - * - * @return true if grid lines are painted, false otherwise - * @see #setGridPainted(boolean) - * @see #getMajorGridColor() - * @see #getMinorGridColor() - */ - public boolean isGridPainted() { - return gridPainted; - } - - /** - *

                Enables or disables the painting of grid lines depending on the - * value of the parameter. Grid painting is enabled by default.

                - * - * @param gridPainted if true, axis labels are painted - * @see #isGridPainted() - * @see #setMajorGridColor(Color) - * @see #setMinorGridColor(Color) - */ - public void setGridPainted(boolean gridPainted) { - boolean old = isGridPainted(); - this.gridPainted = gridPainted; - firePropertyChange("gridPainted", old, isGridPainted()); - } - - /** - *

                Defines whether or not the graph main axis is painted by this - * component. The component is always created with main axis painting - * enabled.

                - * - * @return true if main axis is painted, false otherwise - * @see #setTextPainted(boolean) - * @see #getAxisColor() - */ - public boolean isAxisPainted() { - return axisPainted; - } - - /** - *

                Enables or disables the painting of main axis depending on the - * value of the parameter. Axis painting is enabled by default.

                - * - * @param axisPainted if true, axis labels are painted - * @see #isAxisPainted() - * @see #setAxisColor(Color) - */ - public void setAxisPainted(boolean axisPainted) { - boolean old = isAxisPainted(); - this.axisPainted = axisPainted; - firePropertyChange("axisPainted", old, isAxisPainted()); - } - - /** - *

                Defines whether or not the background painted by this component. - * The component is always created with background painting enabled. - * When background painting is disabled, background painting is deferred - * to the parent class.

                - * - * @return true if background is painted, false otherwise - * @see #setBackgroundPainted(boolean) - * @see #getBackground() - */ - public boolean isBackgroundPainted() { - return backPainted; - } - - /** - *

                Enables or disables the painting of background depending on the - * value of the parameter. Background painting is enabled by default.

                - * - * @param backPainted if true, axis labels are painted - * @see #isBackgroundPainted() - * @see #setBackground(Color) - */ - public void setBackgroundPainted(boolean backPainted) { - boolean old = isBackgroundPainted(); - this.backPainted = backPainted; - firePropertyChange("backgroundPainted", old, isBackgroundPainted()); - } - - /** - *

                Gets the major grid lines color of this component.

                - * - * @return this component's major grid lines color - * @see #setMajorGridColor(Color) - * @see #setGridPainted(boolean) - */ - public Color getMajorGridColor() { - return majorGridColor; - } - - /** - *

                Sets the color of major grid lines on this component. The color - * can be translucent.

                - * - * @param majorGridColor the color to become this component's major grid - * lines color - * @throws IllegalArgumentException if the specified color is null - * @see #getMajorGridColor() - * @see #isGridPainted() - */ - public void setMajorGridColor(Color majorGridColor) { - if (majorGridColor == null) { - throw new IllegalArgumentException("Color cannot be null."); - } - - Color old = getMajorGridColor(); - this.majorGridColor = majorGridColor; - firePropertyChange("majorGridColor", old, getMajorGridColor()); - } - - /** - *

                Gets the minor grid lines color of this component.

                - * - * @return this component's minor grid lines color - * @see #setMinorGridColor(Color) - * @see #setGridPainted(boolean) - */ - public Color getMinorGridColor() { - return minorGridColor; - } - - /** - *

                Sets the color of minor grid lines on this component. The color - * can be translucent.

                - * - * @param minorGridColor the color to become this component's minor grid - * lines color - * @throws IllegalArgumentException if the specified color is null - * @see #getMinorGridColor() - * @see #isGridPainted() - */ - public void setMinorGridColor(Color minorGridColor) { - if (minorGridColor == null) { - throw new IllegalArgumentException("Color cannot be null."); - } - - Color old = getMinorGridColor(); - this.minorGridColor = minorGridColor; - firePropertyChange("minorGridColor", old, getMinorGridColor()); - } - - /** - *

                Gets the main axis color of this component.

                - * - * @return this component's main axis color - * @see #setAxisColor(Color) - * @see #setGridPainted(boolean) - */ - public Color getAxisColor() { - return axisColor; - } - - /** - *

                Sets the color of main axis on this component. The color - * can be translucent.

                - * - * @param axisColor the color to become this component's main axis color - * @throws IllegalArgumentException if the specified color is null - * @see #getAxisColor() - * @see #isAxisPainted() - */ - public void setAxisColor(Color axisColor) { - if (axisColor == null) { - throw new IllegalArgumentException("Color cannot be null."); - } - - Color old = getAxisColor(); - this.axisColor = axisColor; - firePropertyChange("axisColor", old, getAxisColor()); - } - - /** - *

                Gets the distance, in graph units, between two major grid lines on - * the X axis.

                - * - * @return the spacing between two major grid lines on the X axis - * @see #setMajorX(double) - * @see #getMajorY() - * @see #setMajorY(double) - * @see #getMinorCountX() - * @see #setMinorCountX(int) - */ - public double getMajorX() { - return majorX; - } - - /** - *

                Sets the distance, in graph units, between two major grid lines on - * the X axis.

                - * - * @param majorX the requested spacing between two major grid lines on the - * X axis - * @throws IllegalArgumentException if majorX is <= 0.0d - * @see #getMajorX() - * @see #getMajorY() - * @see #setMajorY(double) - * @see #getMinorCountX() - * @see #setMinorCountX(int) - */ - public void setMajorX(double majorX) { - if (majorX <= 0.0) { - throw new IllegalArgumentException("majorX must be > 0.0"); - } - - double old = getMajorX(); - this.majorX = majorX; - this.defaultMajorX = majorX; - repaint(); - firePropertyChange("majorX", old, getMajorX()); - } - - /** - *

                Gets the number of minor grid lines between two major grid lines - * on the X axis.

                - * - * @return the number of minor grid lines between two major grid lines - * @see #setMinorCountX(int) - * @see #getMinorCountY() - * @see #setMinorCountY(int) - * @see #getMajorX() - * @see #setMajorX(double) - */ - public int getMinorCountX() { - return minorCountX; - } - - /** - *

                Sets the number of minor grid lines between two major grid lines on - * the X axis.

                - * - * @param minorCountX the number of minor grid lines between two major grid - * lines on the X axis - * @throws IllegalArgumentException if minorCountX is < 0 - * @see #getMinorCountX() - * @see #getMinorCountY() - * @see #setMinorCountY(int) - * @see #getMajorX() - * @see #setMajorX(double) - */ - public void setMinorCountX(int minorCountX) { - if (minorCountX < 0) { - throw new IllegalArgumentException("minorCountX must be >= 0"); - } - - int old = getMinorCountX(); - this.minorCountX = minorCountX; - repaint(); - firePropertyChange("minorCountX", old, getMinorCountX()); - } - - /** - *

                Gets the distance, in graph units, between two major grid lines on - * the Y axis.

                - * - * @return the spacing between two major grid lines on the Y axis - * @see #setMajorY(double) - * @see #getMajorX() - * @see #setMajorX(double) - * @see #getMinorCountY() - * @see #setMinorCountY(int) - */ - public double getMajorY() { - return majorY; - } - - /** - *

                Sets the distance, in graph units, between two major grid lines on - * the Y axis.

                - * - * @param majorY the requested spacing between two major grid lines on the - * Y axis - * @throws IllegalArgumentException if majorY is <= 0.0d - * @see #getMajorY() - * @see #getMajorX() - * @see #setMajorX(double) - * @see #getMinorCountY() - * @see #setMinorCountY(int) - */ - public void setMajorY(double majorY) { - if (majorY <= 0.0) { - throw new IllegalArgumentException("majorY must be > 0.0"); - } - - double old = getMajorY(); - this.majorY = majorY; - this.defaultMajorY = majorY; - repaint(); - firePropertyChange("majorY", old, getMajorY()); - } - - /** - *

                Gets the number of minor grid lines between two major grid lines - * on the Y axis.

                - * - * @return the number of minor grid lines between two major grid lines - * @see #setMinorCountY(int) - * @see #getMinorCountX() - * @see #setMinorCountX(int) - * @see #getMajorY() - * @see #setMajorY(double) - */ - public int getMinorCountY() { - return minorCountY; - } - - /** - *

                Sets the number of minor grid lines between two major grid lines on - * the Y axis.

                - * - * @param minorCountY the number of minor grid lines between two major grid - * lines on the Y axis - * @throws IllegalArgumentException if minorCountY is < 0 - * @see #getMinorCountY() - * @see #getMinorCountX() - * @see #setMinorCountX(int) - * @see #getMajorY() - * @see #setMajorY(double) - */ - public void setMinorCountY(int minorCountY) { - if (minorCountY < 0) { - throw new IllegalArgumentException("minorCountY must be >= 0"); - } - - int old = getMinorCountY(); - this.minorCountY = minorCountY; - repaint(); - firePropertyChange("minorCountY", old, getMinorCountY()); - } - - /** - *

                Sets the view and the origin of the graph at the same time. The view - * minimum boundaries are defined by the location of the rectangle passed - * as parameter. The width and height of the rectangle define the distance - * between the minimum and maximum boundaries:

                - * - *
                  - *
                • minX: bounds.getX()
                • - *
                • minY: bounds.getY()
                • - *
                • maxY: bounds.getMaxX() (minX + bounds.getWidth())
                • - *
                • maxX: bounds.getMaxY() (minY + bounds.getHeight())
                • - *
                - * - *

                The origin is located at the center of the view. Its coordinates are - * defined by calling bounds.getCenterX() and bounds.getCenterY().

                - * - * @param bounds the rectangle defining the graph's view and its origin - * @see #getView() - * @see #setView(Rectangle2D) - * @see #getOrigin() - * @see #setOrigin(Point2D) - */ - public void setViewAndOrigin(Rectangle2D bounds) { - setView(bounds); - setOrigin(new Point2D.Double(bounds.getCenterX(), bounds.getCenterY())); - } - - /** - *

                Sets the view of the graph. The view minimum boundaries are defined by - * the location of the rectangle passed as parameter. The width and height - * of the rectangle define the distance between the minimum and maximum - * boundaries:

                - * - *
                  - *
                • minX: bounds.getX()
                • - *
                • minY: bounds.getY()
                • - *
                • maxY: bounds.getMaxX() (minX + bounds.getWidth())
                • - *
                • maxX: bounds.getMaxY() (minY + bounds.getHeight())
                • - *
                - * - *

                If the specified view is null, nothing happens.

                - * - *

                Calling this method leaves the origin intact.

                - * - * @param bounds the rectangle defining the graph's view and its origin - * @see #getView() - * @see #setViewAndOrigin(Rectangle2D) - */ - public void setView(Rectangle2D bounds) { - if (bounds == null) { - return; - } - Rectangle2D old = getView(); - defaultView = new Rectangle2D.Double(bounds.getX(), bounds.getY(), - bounds.getWidth(), bounds.getHeight()); - - minX = defaultView.getMinX(); - maxX = defaultView.getMaxX(); - minY = defaultView.getMinY(); - maxY = defaultView.getMaxY(); - - majorX = defaultMajorX; - majorY = defaultMajorY; - firePropertyChange("view", old, getView()); - repaint(); - } - - /** - *

                Gets the view of the graph. The returned rectangle defines the bounds - * of the view as follows:

                - * - *
                  - *
                • minX: bounds.getX()
                • - *
                • minY: bounds.getY()
                • - *
                • maxY: bounds.getMaxX() (minX + bounds.getWidth())
                • - *
                • maxX: bounds.getMaxY() (minY + bounds.getHeight())
                • - *
                - * - * @return the rectangle corresponding to the current view of the graph - * @see #setView(Rectangle2D) - * @see #setViewAndOrigin(Rectangle2D) - */ - public Rectangle2D getView() { - return new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY); - } - - /** - *

                Resets the view to the default view if it has been changed by the user - * by panning and zooming. The default view is defined by the view last - * specified in a constructor call or a call to the methods - * {@link #setView(Rectangle2D)} and - * {@link #setViewAndOrigin(Rectangle2D)}.

                - * - * @see #setView(Rectangle2D) - * @see #setViewAndOrigin(Rectangle2D) - */ - public void resetView() { - setView(defaultView); - } - - /** - *

                Sets the origin of the graph. The coordinates of the origin are - * defined by the coordinates of the point passed as parameter.

                - * - *

                If the specified view is null, nothing happens.

                - * - *

                Calling this method leaves the view intact.

                - * - * @param origin the coordinates of the new origin - * @see #getOrigin() - * @see #setViewAndOrigin(Rectangle2D) - */ - public void setOrigin(Point2D origin) { - if (origin == null) { - return; - } - - Point2D old = getOrigin(); - originX = origin.getX(); - originY = origin.getY(); - firePropertyChange("origin", old, getOrigin()); - repaint(); - } - - /** - *

                Gets the origin coordinates of the graph. The coordinates are - * represented as an instance of Point2D and stored in - * double format.

                - - * @return the origin coordinates in double format - * @see #setOrigin(Point2D) - * @see #setViewAndOrigin(Rectangle2D) - */ - public Point2D getOrigin() { - return new Point2D.Double(originX, originY); - } - - /** - *

                Adds one or more plots to the graph. These plots are associated to - * a color used to draw them.

                - * - *

                If plotList is null or empty, nothing happens.

                - * - *

                This method is not thread safe and should be called only from the - * EDT.

                - * - * @param color the color to be usd to draw the plots - * @param plotList the list of plots to add to the graph - * @throws IllegalArgumentException if color is null - * @see #removePlots(Plot...) - * @see #removeAllPlots() - */ - public void addPlots(Color color, Plot... plotList) { - if (color == null) { - throw new IllegalArgumentException("Plots color cannot be null."); - } - - if (plotList == null) { - return; - } - - for (Plot plot : plotList) { - DrawablePlot drawablePlot = - new DrawablePlot(plot, color); - if (plot != null && !plots.contains(drawablePlot)) { - plot.addPropertyChangeListener(plotChangeListener); - plots.add(drawablePlot); - } - } - repaint(); - } - - /** - *

                Removes the specified plots from the graph. Plots to be removed - * are identified by identity. This means you cannot remove a plot by - * passing a clone or another instance of the same subclass of - * {@link Plot}.

                - * - *

                If plotList is null or empty, nothing happens.

                - * - *

                This method is not thread safe and should be called only from the - * EDT.

                - * - * @param plotList the list of plots to be removed from the graph - * @see #removeAllPlots() - * @see #addPlots(Color, Plot...) - */ - public void removePlots(Plot... plotList) { - if (plotList == null) { - return; - } - - for (Plot plot : plotList) { - if (plot != null) { - DrawablePlot toRemove = null; - for (DrawablePlot drawable: plots) { - if (drawable.getEquation() == plot) { - toRemove = drawable; - break; - } - } - - if (toRemove != null) { - plot.removePropertyChangeListener(plotChangeListener); - plots.remove(toRemove); - } - } - } - repaint(); - } - - /** - *

                Removes all the plots currently associated with this graph.

                - * - *

                This method is not thread safe and should be called only from the - * EDT.

                - * - * @see #removePlots(Plot...) - * @see #addPlots(Color, Plot...) - */ - public void removeAllPlots() { - plots.clear(); - repaint(); - } - - /** - * {@inheritDoc} - */ - @Override - public Dimension getPreferredSize() { - return new Dimension(400, 400); - } - - /** - *

                Converts a position, in graph units, from the Y axis into a pixel - * coordinate. For instance, if you defined the origin so it appears at the - * exact center of the view, calling - * yPositionToPixel(getOriginY()) will return a value - * approximately equal to getHeight() / 2.0.

                - * - * @param position the Y position to be converted into pixels - * @return the coordinate in pixels of the specified graph Y position - * @see #xPositionToPixel(double) - * @see #yPixelToPosition(double) - */ - protected double yPositionToPixel(double position) { - double height = getHeight(); - return height - ((position - minY) * height / (maxY - minY)); - } - - /** - *

                Converts a position, in graph units, from the X axis into a pixel - * coordinate. For instance, if you defined the origin so it appears at the - * exact center of the view, calling - * xPositionToPixel(getOriginX()) will return a value - * approximately equal to getWidth() / 2.0.

                - * - * @param position the X position to be converted into pixels - * @return the coordinate in pixels of the specified graph X position - * @see #yPositionToPixel(double) - * @see #xPixelToPosition(double) - */ - protected double xPositionToPixel(double position) { - return (position - minX) * getWidth() / (maxX - minX); - } - - /** - *

                Converts a pixel coordinate from the X axis into a graph position, in - * graph units. For instance, if you defined the origin so it appears at the - * exact center of the view, calling - * xPixelToPosition(getWidth() / 2.0) will return a value - * approximately equal to getOriginX().

                - * - * @param pixel the X pixel coordinate to be converted into a graph position - * @return the graph X position of the specified pixel coordinate - * @see #yPixelToPosition(double) - * @see #xPositionToPixel(double) - */ - protected double xPixelToPosition(double pixel) { -// double axisV = xPositionToPixel(originX); -// return (pixel - axisV) * (maxX - minX) / (double) getWidth(); - return minX + pixel * (maxX - minX) / getWidth(); - } - - /** - *

                Converts a pixel coordinate from the Y axis into a graph position, in - * graph units. For instance, if you defined the origin so it appears at the - * exact center of the view, calling - * yPixelToPosition(getHeight() / 2.0) will return a value - * approximately equal to getOriginY().

                - * - * @param pixel the Y pixel coordinate to be converted into a graph position - * @return the graph Y position of the specified pixel coordinate - * @see #xPixelToPosition(double) - * @see #yPositionToPixel(double) - */ - protected double yPixelToPosition(double pixel) { -// double axisH = yPositionToPixel(originY); -// return (getHeight() - pixel - axisH) * (maxY - minY) / (double) getHeight(); - return minY + (getHeight() - pixel) * (maxY - minY) / getHeight(); - } - - /** - * {@inheritDoc} - */ - @Override - protected void paintComponent(Graphics g) { - if (!isVisible()) { - return; - } - - Graphics2D g2 = (Graphics2D) g; - setupGraphics(g2); - - paintBackground(g2); - drawGrid(g2); - drawAxis(g2); - drawPlots(g2); - drawLabels(g2); - - paintExtra(g2); - } - - /** - *

                This painting method is meant to be overridden by subclasses of - * JXGraph. This method is called after all the painting - * is done. By overriding this method, a subclass can display extra - * information on top of the graph.

                - *

                The graphics surface passed as parameter is configured by - * {@link #setupGraphics(Graphics2D)}.

                - * - * @param g2 the graphics surface on which the graph is drawn - * @see #setupGraphics(Graphics2D) - * @see #xPixelToPosition(double) - * @see #yPixelToPosition(double) - * @see #xPositionToPixel(double) - * @see #yPositionToPixel(double) - */ - protected void paintExtra(Graphics2D g2) { - } - - // Draw all the registered plots with the appropriate color. - private void drawPlots(Graphics2D g2) { - for (DrawablePlot drawable: plots) { - g2.setColor(drawable.getColor()); - drawPlot(g2, drawable.getEquation()); - } - } - - // Draw a single plot as a GeneralPath made of straight lines. - private void drawPlot(Graphics2D g2, Plot equation) { - float x = 0.0f; - float y = (float) yPositionToPixel(equation.compute(xPixelToPosition(0.0))); - - GeneralPath path = new GeneralPath(); - path.moveTo(x, y); - - float width = getWidth(); - for (x = 0.0f; x < width; x += 1.0f) { - double position = xPixelToPosition(x); - y = (float) yPositionToPixel(equation.compute(position)); - path.lineTo(x, y); - } - - g2.draw(path); - } - - // Draws the grid. First draw the vertical lines, then the horizontal lines. - private void drawGrid(Graphics2D g2) { - Stroke stroke = g2.getStroke(); - - if (isGridPainted()) { - drawVerticalGrid(g2); - drawHorizontalGrid(g2); - } - - g2.setStroke(stroke); - } - - // Draw all labels. First draws labels on the horizontal axis, then labels - // on the vertical axis. If the axis is set not to be painted, this - // method draws the origin as a straight cross. - private void drawLabels(Graphics2D g2) { - if (isTextPainted()) { - double axisH = yPositionToPixel(originY); - double axisV = xPositionToPixel(originX); - - if (isAxisPainted()) { - Stroke stroke = g2.getStroke(); - g2.setStroke(new BasicStroke(STROKE_AXIS)); - g2.setColor(getAxisColor()); - g2.drawLine((int) axisV - 3, (int) axisH, - (int) axisV + 3, (int) axisH); - g2.drawLine((int) axisV, (int) axisH - 3, - (int) axisV, (int) axisH + 3); - g2.setStroke(stroke); - } - - g2.setColor(getForeground()); - FontMetrics metrics = g2.getFontMetrics(); - g2.drawString(format(originX) + "; " + - format(originY), (int) axisV + 5, - (int) axisH + metrics.getHeight()); - - drawHorizontalAxisLabels(g2); - drawVerticalAxisLabels(g2); - } - } - - // Draws labels on the vertical axis. First draws labels below the origin, - // then draw labels on top of the origin. - private void drawVerticalAxisLabels(Graphics2D g2) { - double axisV = xPositionToPixel(originX); - -// double startY = Math.floor((minY - originY) / majorY) * majorY; - double startY = Math.floor(minY / majorY) * majorY; - for (double y = startY; y < maxY + majorY; y += majorY) { - if (((y - majorY / 2.0) < originY) && - ((y + majorY / 2.0) > originY)) { - continue; - } - - int position = (int) yPositionToPixel(y); - g2.drawString(format(y), (int) axisV + 5, position); - } - } - - // Draws the horizontal lines of the grid. Draws both minor and major - // grid lines. - private void drawHorizontalGrid(Graphics2D g2) { - double minorSpacing = majorY / getMinorCountY(); - double axisV = xPositionToPixel(originX); - - Stroke gridStroke = new BasicStroke(STROKE_GRID); - Stroke axisStroke = new BasicStroke(STROKE_AXIS); - - Rectangle clip = g2.getClipBounds(); - - int position; - - if (!isAxisPainted()) { - position = (int) xPositionToPixel(originX); - if (position >= clip.x && position <= clip.x + clip.width) { - g2.setColor(getMajorGridColor()); - g2.drawLine(position, clip.y, position, clip.y + clip.height); - } - } - -// double startY = Math.floor((minY - originY) / majorY) * majorY; - double startY = Math.floor(minY / majorY) * majorY; - for (double y = startY; y < maxY + majorY; y += majorY) { - g2.setStroke(gridStroke); - g2.setColor(getMinorGridColor()); - for (int i = 0; i < getMinorCountY(); i++) { - position = (int) yPositionToPixel(y - i * minorSpacing); - if (position >= clip.y && position <= clip.y + clip.height) { - g2.drawLine(clip.x, position, clip.x + clip.width, position); - } - } - - position = (int) yPositionToPixel(y); - if (position >= clip.y && position <= clip.y + clip.height) { - g2.setColor(getMajorGridColor()); - g2.drawLine(clip.x, position, clip.x + clip.width, position); - - if (isAxisPainted()) { - g2.setStroke(axisStroke); - g2.setColor(getAxisColor()); - g2.drawLine((int) axisV - 3, position, (int) axisV + 3, position); - } - } - } - } - - // Draws labels on the horizontal axis. First draws labels on the right of - // the origin, then on the left. - private void drawHorizontalAxisLabels(Graphics2D g2) { - double axisH = yPositionToPixel(originY); - FontMetrics metrics = g2.getFontMetrics(); - -// double startX = Math.floor((minX - originX) / majorX) * majorX; - double startX = Math.floor(minX / majorX) * majorX; - for (double x = startX; x < maxX + majorX; x += majorX) { - if (((x - majorX / 2.0) < originX) && - ((x + majorX / 2.0) > originX)) { - continue; - } - - int position = (int) xPositionToPixel(x); - g2.drawString(format(x), position, - (int) axisH + metrics.getHeight()); - } - } - - // Draws the vertical lines of the grid. Draws both minor and major - // grid lines. - private void drawVerticalGrid(Graphics2D g2) { - double minorSpacing = majorX / getMinorCountX(); - double axisH = yPositionToPixel(originY); - - Stroke gridStroke = new BasicStroke(STROKE_GRID); - Stroke axisStroke = new BasicStroke(STROKE_AXIS); - - Rectangle clip = g2.getClipBounds(); - - int position; - if (!isAxisPainted()) { - position = (int) yPositionToPixel(originY); - if (position >= clip.y && position <= clip.y + clip.height) { - g2.setColor(getMajorGridColor()); - g2.drawLine(clip.x, position, clip.x + clip.width, position); - } - } - -// double startX = Math.floor((minX - originX) / majorX) * majorX; - double startX = Math.floor(minX / majorX) * majorX; - for (double x = startX; x < maxX + majorX; x += majorX) { - g2.setStroke(gridStroke); - g2.setColor(getMinorGridColor()); - for (int i = 0; i < getMinorCountX(); i++) { - position = (int) xPositionToPixel(x - i * minorSpacing); - if (position >= clip.x && position <= clip.x + clip.width) { - g2.drawLine(position, clip.y, position, clip.y + clip.height); - } - } - - position = (int) xPositionToPixel(x); - if (position >= clip.x && position <= clip.x + clip.width) { - g2.setColor(getMajorGridColor()); - g2.drawLine(position, clip.y, position, clip.y + clip.height); - - if (isAxisPainted()) { - g2.setStroke(axisStroke); - g2.setColor(getAxisColor()); - g2.drawLine(position, (int) axisH - 3, position, (int) axisH + 3); - } - } - } - } - - // Drase the main axis. - private void drawAxis(Graphics2D g2) { - if (!isAxisPainted()) { - return; - } - - double axisH = yPositionToPixel(originY); - double axisV = xPositionToPixel(originX); - - Rectangle clip = g2.getClipBounds(); - - g2.setColor(getAxisColor()); - Stroke stroke = g2.getStroke(); - g2.setStroke(new BasicStroke(STROKE_AXIS)); - - if (axisH >= clip.y && axisH <= clip.y + clip.height) { - g2.drawLine(clip.x, (int) axisH, clip.x + clip.width, (int) axisH); - } - if (axisV >= clip.x && axisV <= clip.x + clip.width) { - g2.drawLine((int) axisV, clip.y, (int) axisV, clip.y + clip.height); - } - - g2.setStroke(stroke); - } - - /** - *

                This method is called by the component prior to any drawing operation - * to configure the drawing surface. The default implementation enables - * antialiasing on the graphics.

                - *

                This method can be overriden by subclasses to modify the drawing - * surface before any painting happens.

                - * - * @param g2 the graphics surface to set up - * @see #paintExtra(Graphics2D) - * @see #paintBackground(Graphics2D) - */ - protected void setupGraphics(Graphics2D g2) { - g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_ON); - } - - /** - *

                This method is called by the component whenever it needs to paint - * its background. The default implementation fills the background with - * a solid color as defined by {@link #getBackground()}. Background painting - * does not happen when {@link #isBackgroundPainted()} returns false.

                - *

                It is recommended to subclasses to honor the contract defined by - * {@link #isBackgroundPainted()} and {@link #setBackgroundPainted(boolean)}. - * - * @param g2 the graphics surface on which the background must be drawn - * @see #setupGraphics(Graphics2D) - * @see #paintExtra(Graphics2D) - * @see #isBackgroundPainted() - * @see #setBackgroundPainted(boolean) - */ - protected void paintBackground(Graphics2D g2) { - if (isBackgroundPainted()) { - Painter p = getBackgroundPainter(); - if (p != null) { - p.paint(g2, this, getWidth(), getHeight()); - } else { - g2.setColor(getBackground()); - g2.fill(g2.getClipBounds()); - } - } - } - - // Format a number with the appropriate number formatter. Numbers >= 0.01 - // and < 100 are formatted with a regular, 2-digits, numbers formatter. - // Other numbers use a scientific notation given by a DecimalFormat instance - private String format(double number) { - boolean farAway = (number != 0.0d && Math.abs(number) < 0.01d) || - Math.abs(number) > 99.0d; - return (farAway ? secondFormatter : mainFormatter).format(number); - } - - /** - *

                A plot represents a mathematical transformation used by - * {@link JXGraph}. When a plot belongs to a graph, the graph component - * asks for the transformation of a value along the X axis. The resulting - * value defines the Y coordinates at which the graph must draw a spot of - * color.

                - * - *

                Here is a sample implementation of this class that draws a straight line - * once added to a graph (it follows the well-known equation y=a.x+b):

                - * - *
                -     * class LinePlot extends JXGraph.Plot {
                -     *     public double compute(double value) {
                -     *         return 2.0 * value + 1.0;
                -     *     }
                -     * }
                -     * 
                - * - *

                When a plot is added to an instance of - * JXGraph, the JXGraph automatically becomes - * a new property change listener of the plot. If property change events are - * fired, the graph will be updated accordingly.

                - * - *

                More information about plots usage can be found in {@link JXGraph} in - * the section entitled Plots.

                - * - * @see JXGraph - * @see JXGraph#addPlots(Color, Plot...) - */ - public abstract static class Plot extends AbstractBean { - /** - *

                Creates a new, parameter-less plot.

                - */ - protected Plot() { - } - - /** - *

                This method must return the result of a mathematical - * transformation of its sole parameter.

                - * - * @param value a value along the X axis of the graph currently - * drawing this plot - * @return the result of the mathematical transformation of value - */ - public abstract double compute(double value); - } - - // Encapsulates a plot and its color. Avoids the use of a full-blown Map. - private static class DrawablePlot { - private final Plot equation; - private final Color color; - - private DrawablePlot(Plot equation, Color color) { - this.equation = equation; - this.color = color; - } - - private Plot getEquation() { - return equation; - } - - private Color getColor() { - return color; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - final DrawablePlot that = (DrawablePlot) o; - if (!color.equals(that.color)) { - return false; - } - return equation.equals(that.equation); - } - - @Override - public int hashCode() { - int result; - result = equation.hashCode(); - result = 29 * result + color.hashCode(); - return result; - } - } - - // Shrinks or expand the view depending on the mouse wheel direction. - // When the wheel moves down, the view is expanded. Otherwise it is shrunk. - private class ZoomHandler implements MouseWheelListener { - @Override - public void mouseWheelMoved(MouseWheelEvent e) { - double distanceX = maxX - minX; - double distanceY = maxY - minY; - - double cursorX = minX + distanceX / 2.0; - double cursorY = minY + distanceY / 2.0; - - int rotation = e.getWheelRotation(); - if (rotation < 0) { - distanceX /= ZOOM_MULTIPLIER; - distanceY /= ZOOM_MULTIPLIER; - - majorX /= ZOOM_MULTIPLIER; - majorY /= ZOOM_MULTIPLIER; - } else { - distanceX *= ZOOM_MULTIPLIER; - distanceY *= ZOOM_MULTIPLIER; - - majorX *= ZOOM_MULTIPLIER; - majorY *= ZOOM_MULTIPLIER; - } - - minX = cursorX - distanceX / 2.0; - maxX = cursorX + distanceX / 2.0; - minY = cursorY - distanceY / 2.0; - maxY = cursorY + distanceY / 2.0; - - repaint(); - } - } - - // Listens for a click on the middle button of the mouse and resets the view - private class ResetHandler extends MouseAdapter { - @Override - public void mousePressed(MouseEvent e) { - if (e.getButton() != MouseEvent.BUTTON2) { - return; - } - - resetView(); - } - } - - // Starts and ends drag gestures with mouse left button. - private class PanHandler extends MouseAdapter { - @Override - public void mousePressed(MouseEvent e) { - if (e.getButton() != MouseEvent.BUTTON1) { - return; - } - - dragStart = e.getPoint(); - setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); - } - - @Override - public void mouseReleased(MouseEvent e) { - if (e.getButton() != MouseEvent.BUTTON1) { - return; - } - - setCursor(Cursor.getDefaultCursor()); - } - } - - // Handles drag gesture with the left mouse button and relocates the view - // accordingly. - private class PanMotionHandler extends MouseMotionAdapter { - @Override - public void mouseDragged(MouseEvent e) { - Point dragEnd = e.getPoint(); - - double distance = xPixelToPosition(dragEnd.getX()) - - xPixelToPosition(dragStart.getX()); - minX = minX - distance; - maxX = maxX - distance; - - distance = yPixelToPosition(dragEnd.getY()) - - yPixelToPosition(dragStart.getY()); - minY = minY - distance; - maxY = maxY - distance; - - repaint(); - dragStart = dragEnd; - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/package-info.java b/src/main/java/org/jdesktop/swingx/package-info.java index bb9e62daee..dd988ec683 100644 --- a/src/main/java/org/jdesktop/swingx/package-info.java +++ b/src/main/java/org/jdesktop/swingx/package-info.java @@ -89,8 +89,7 @@ *
              1. {@link org.jdesktop.swingx.JXDialog Dialog} *
              2. {@link org.jdesktop.swingx.JXPanel Panel} *
              3. {@link org.jdesktop.swingx.JXErrorPane ErrorPane} - *
              4. {@link org.jdesktop.swingx.JXLoginPane LoginPane} - * + * *
              5. Search components: {@link org.jdesktop.swingx.JXFindBar FindBar} used * for incremental search (similar to FireFox), * {@link org.jdesktop.swingx.JXFindPanel FindPanel} used in a find dialog, @@ -125,7 +124,6 @@ *
              6. {@link org.jdesktop.swingx.JXColorSelectionButton} *
              7. {@link org.jdesktop.swingx.JXEditorPane} *
              8. {@link org.jdesktop.swingx.JXGradientChooser} - *
              9. {@link org.jdesktop.swingx.JXGraph} *
              10. Image containers {@link org.jdesktop.swingx.JXImageView ImageView} * and {@link org.jdesktop.swingx.JXImagePanel ImagePanel} (PENDING JW: * merge/remove one?) From 66d187515c067e2da461a3868e250f5c2a65241d Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 6 Jul 2022 11:34:24 +0200 Subject: [PATCH 129/422] - remove JGoodies Plastic support - remove JXImagePanel and JXImageView Former-commit-id: 71f7fb24fb01cfae7f313460436033862caa22cc --- .../org/jdesktop/swingx/JXImagePanel.java | 419 ---------- .../java/org/jdesktop/swingx/JXImageView.java | 771 ------------------ .../org/jdesktop/swingx/package-info.java | 3 - .../swingx/plaf/AbstractComponentAddon.java | 10 - .../swingx/plaf/TitledPanelAddon.java | 13 +- 5 files changed, 3 insertions(+), 1213 deletions(-) delete mode 100644 src/main/java/org/jdesktop/swingx/JXImagePanel.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXImageView.java diff --git a/src/main/java/org/jdesktop/swingx/JXImagePanel.java b/src/main/java/org/jdesktop/swingx/JXImagePanel.java deleted file mode 100644 index 87bcfd1bee..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXImagePanel.java +++ /dev/null @@ -1,419 +0,0 @@ -/* - * $Id: JXImagePanel.java 4017 2011-05-10 21:00:48Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx; - -import javax.imageio.ImageIO; -import javax.swing.*; -import java.awt.*; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.io.File; -import java.lang.ref.SoftReference; -import java.net.URL; -import java.util.concurrent.*; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - *

                - * A panel that draws an image. The standard mode is to draw the specified image - * centered and unscaled. The component&s preferred size is based on the - * image, unless explicitly set by the user. - *

                - *

                - * Images to be displayed can be set based on URL, Image, etc. This is - * accomplished by passing in an image loader. - * - *

                - * public class URLImageLoader extends Callable<Image> {
                - *     private URL url;
                - * 
                - *     public URLImageLoader(URL url) {
                - *         url.getClass(); //null check
                - *         this.url = url;
                - *     }
                - * 
                - *     public Image call() throws Exception {
                - *         return ImageIO.read(url);
                - *     }
                - * }
                - * 
                - * imagePanel.setImageLoader(new URLImageLoader(url));
                - * 
                - * - *

                - *

                - * This component also supports allowing the user to set the image. If the - * JXImagePanel is editable, then when the user clicks on the - * JXImagePanel a FileChooser is shown allowing the user to pick - * some other image to use within the JXImagePanel. - *

                - *

                - * TODO In the future, the JXImagePanel will also support tiling of images, - * scaling, resizing, cropping, segues etc. - *

                - *

                - * TODO other than the image loading this component can be replicated by a - * JXPanel with the appropriate Painter. What's the point? - *

                - * - * @author rbair - * @deprecated (pre-1.6.2) use a JXPanel with an ImagePainter; see Issue 988 - */ -//moved to package-private instead of deleting; needed by JXLoginPane -@Deprecated -class JXImagePanel extends JXPanel { - public static enum Style { - CENTERED, TILED, SCALED, SCALED_KEEP_ASPECT_RATIO - } - - private static final Logger LOG = Logger.getLogger(JXImagePanel.class.getName()); - - /** - * Text informing the user that clicking on this component will allow them - * to set the image - */ - private static final String TEXT = "Click here
                to set the image
                "; - - /** - * The image to draw - */ - private SoftReference img = new SoftReference(null); - - /** - * If true, then the image can be changed. Perhaps a better name is - * "readOnly", but editable was chosen to be more consistent with - * other Swing components. - */ - private boolean editable = false; - - /** - * The mouse handler that is used if the component is editable - */ - private MouseHandler mhandler = new MouseHandler(); - - /** - * Specifies how to draw the image, i.e. what kind of Style to use when - * drawing - */ - private Style style = Style.CENTERED; - - private Image defaultImage; - - private Callable imageLoader; - - private static final ExecutorService service = Executors.newFixedThreadPool(5); - - public JXImagePanel() { - } - - //TODO remove this constructor; no where else can a URL be used in this class - public JXImagePanel(URL imageUrl) { - try { - setImage(ImageIO.read(imageUrl)); - } catch (Exception e) { - // TODO need convert to something meaningful - LOG.log(Level.WARNING, "", e); - } - } - - /** - * Sets the image to use for the background of this panel. This image is - * painted whether the panel is opaque or translucent. - * - * @param image if null, clears the image. Otherwise, this will set the - * image to be painted. If the preferred size has not been explicitly - * set, then the image dimensions will alter the preferred size of - * the panel. - */ - public void setImage(Image image) { - if (image != img.get()) { - Image oldImage = img.get(); - img = new SoftReference(image); - firePropertyChange("image", oldImage, img); - invalidate(); - repaint(); - } - } - - /** - * @return the image used for painting the background of this panel - */ - public Image getImage() { - Image image = img.get(); - - //TODO perhaps we should have a default image loader? - if (image == null && imageLoader != null) { - try { - image = imageLoader.call(); - img = new SoftReference(image); - } catch (Exception e) { - LOG.log(Level.WARNING, "", e); - } - } - return image; - } - - /** - * @param editable - */ - public void setEditable(boolean editable) { - if (editable != this.editable) { - // if it was editable, remove the mouse handler - if (this.editable) { - removeMouseListener(mhandler); - } - this.editable = editable; - // if it is now editable, add the mouse handler - if (this.editable) { - addMouseListener(mhandler); - } - setToolTipText(editable ? TEXT : ""); - firePropertyChange("editable", !editable, editable); - repaint(); - } - } - - /** - * @return whether the image for this panel can be changed or not via the - * UI. setImage may still be called, even if isEditable - * returns false. - */ - public boolean isEditable() { - return editable; - } - - /** - * Sets what style to use when painting the image - * - * @param s - */ - public void setStyle(Style s) { - if (style != s) { - Style oldStyle = style; - style = s; - firePropertyChange("style", oldStyle, s); - repaint(); - } - } - - /** - * @return the Style used for drawing the image (CENTERED, TILED, etc). - */ - public Style getStyle() { - return style; - } - - /** - * {@inheritDoc} - * The old property value in PCE fired by this method might not be always correct! - */ - @Override - public Dimension getPreferredSize() { - if (!isPreferredSizeSet() && img != null) { - Image img = this.img.get(); - // was img GCed in the mean time? - if (img != null) { - // it has not been explicitly set, so return the width/height of - // the image - int width = img.getWidth(null); - int height = img.getHeight(null); - if (width == -1 || height == -1) { - return super.getPreferredSize(); - } - Insets insets = getInsets(); - width += insets.left + insets.right; - height += insets.top + insets.bottom; - return new Dimension(width, height); - } - } - return super.getPreferredSize(); - } - - /** - * Overridden to paint the image on the panel - * - * @param g - */ - @Override - protected void paintComponent(Graphics g) { - super.paintComponent(g); - Graphics2D g2 = (Graphics2D) g; - Image img = this.img.get(); - if (img == null && imageLoader != null) { - // schedule for loading (will repaint itself once loaded) - // have to use new future task every time as it holds strong - // reference to the object it retrieved and doesn't allow to reset - // it. - service.execute(new FutureTask(imageLoader) { - - @Override - protected void done() { - super.done(); - - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - try { - JXImagePanel.this.setImage(get()); - } catch (InterruptedException e) { - // ignore - canceled image load - } catch (ExecutionException e) { - LOG.log(Level.WARNING, "", e); - } - } - }); - } - - }); - img = defaultImage; - } - if (img != null) { - final int imgWidth = img.getWidth(null); - final int imgHeight = img.getHeight(null); - if (imgWidth == -1 || imgHeight == -1) { - // image hasn't completed loading, return - return; - } - - Insets insets = getInsets(); - final int pw = getWidth() - insets.left - insets.right; - final int ph = getHeight() - insets.top - insets.bottom; - - switch (style) { - case CENTERED: - Rectangle clipRect = g2.getClipBounds(); - int imageX = (pw - imgWidth) / 2 + insets.left; - int imageY = (ph - imgHeight) / 2 + insets.top; - Rectangle r = SwingUtilities.computeIntersection(imageX, imageY, imgWidth, imgHeight, clipRect); - if (r.x == 0 && r.y == 0 && (r.width == 0 || r.height == 0)) { - return; - } - // I have my new clipping rectangle "r" in clipRect space. - // It is therefore the new clipRect. - clipRect = r; - // since I have the intersection, all I need to do is adjust the - // x & y values for the image - int txClipX = clipRect.x - imageX; - int txClipY = clipRect.y - imageY; - int txClipW = clipRect.width; - int txClipH = clipRect.height; - - g2.drawImage(img, clipRect.x, clipRect.y, clipRect.x + clipRect.width, clipRect.y + clipRect.height, txClipX, txClipY, txClipX + txClipW, txClipY + txClipH, null); - break; - case TILED: - g2.translate(insets.left, insets.top); - Rectangle clip = g2.getClipBounds(); - g2.setClip(0, 0, pw, ph); - - int totalH = 0; - - while (totalH < ph) { - int totalW = 0; - - while (totalW < pw) { - g2.drawImage(img, totalW, totalH, null); - totalW += img.getWidth(null); - } - - totalH += img.getHeight(null); - } - - g2.setClip(clip); - g2.translate(-insets.left, -insets.top); - break; - case SCALED: - g2.drawImage(img, insets.left, insets.top, pw, ph, null); - break; - case SCALED_KEEP_ASPECT_RATIO: - int w = pw; - int h = ph; - final float ratioW = ((float) w) / ((float) imgWidth); - final float ratioH = ((float) h) / ((float) imgHeight); - - if (ratioW < ratioH) { - h = (int) (imgHeight * ratioW); - } else { - w = (int) (imgWidth * ratioH); - } - - final int x = (pw - w) / 2 + insets.left; - final int y = (ph - h) / 2 + insets.top; - g2.drawImage(img, x, y, w, h, null); - break; - default: - LOG.fine("unimplemented"); - g2.drawImage(img, insets.left, insets.top, this); - break; - } - } - } - - /** - * Handles click events on the component - */ - private class MouseHandler extends MouseAdapter { - private Cursor oldCursor; - - private JFileChooser chooser; - - @Override - public void mouseClicked(MouseEvent evt) { - if (chooser == null) { - chooser = new JFileChooser(); - } - int retVal = chooser.showOpenDialog(JXImagePanel.this); - if (retVal == JFileChooser.APPROVE_OPTION) { - File file = chooser.getSelectedFile(); - try { - setImage(new ImageIcon(file.toURI().toURL()).getImage()); - } catch (Exception ex) { - } - } - } - - @Override - public void mouseEntered(MouseEvent evt) { - if (oldCursor == null) { - oldCursor = getCursor(); - setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - } - } - - @Override - public void mouseExited(MouseEvent evt) { - if (oldCursor != null) { - setCursor(oldCursor); - oldCursor = null; - } - } - } - - public void setDefaultImage(Image def) { - this.defaultImage = def; - } - - public void setImageLoader(Callable loadImage) { - this.imageLoader = loadImage; - - } -} diff --git a/src/main/java/org/jdesktop/swingx/JXImageView.java b/src/main/java/org/jdesktop/swingx/JXImageView.java deleted file mode 100644 index 6266aec394..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXImageView.java +++ /dev/null @@ -1,771 +0,0 @@ -/* - * $Id: JXImageView.java 4248 2012-11-13 18:12:08Z kschaefe $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.error.ErrorListener; -import org.jdesktop.swingx.error.ErrorSupport; -import org.jdesktop.swingx.painter.MattePainter; -import org.jdesktop.swingx.util.GraphicsUtilities; -import org.jdesktop.swingx.util.PaintUtils; - -import javax.imageio.ImageIO; -import javax.swing.*; -import javax.swing.event.MouseInputAdapter; -import java.awt.*; -import java.awt.datatransfer.DataFlavor; -import java.awt.datatransfer.Transferable; -import java.awt.datatransfer.UnsupportedFlavorException; -import java.awt.event.ActionEvent; -import java.awt.event.InputEvent; -import java.awt.event.MouseEvent; -import java.awt.geom.AffineTransform; -import java.awt.geom.Point2D; -import java.awt.image.AffineTransformOp; -import java.awt.image.BufferedImage; -import java.awt.image.BufferedImageOp; -import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Logger; - -/** - *

                A panel which shows an image centered. The user can drag an image into the - * panel from other applications and move the image around within the view. - * The JXImageView has built in actions for scaling, rotating, opening a new - * image, and saving. These actions can be obtained using the relevant get*Action() - * methods. - *

                - * - *

                TODO: has dashed rect and text indicating you should drag there.

                - * - * - *

                If the user drags more than one photo at a time into the JXImageView only - * the first photo will be loaded and shown. Any errors generated internally, - * such as dragging in a list of files which are not images, will be reported - * to any attached {@link ErrorListener} added by the - * {@link #addErrorListener}() method.

                - * - * @author Joshua Marinacci joshua.marinacci@sun.com - */ -@JavaBean -public class JXImageView extends JXPanel { - - private Logger log = Logger.getLogger(JXImageView.class.getName()); - /* ======= instance variables ========= */ - // the image this view will show - private Image image; - // the url of the image, if available - private URL imageURL; - - // support for error listeners - private ErrorSupport errorSupport = new ErrorSupport(this); - - // location to draw image. if null then draw in the center - private Point2D imageLocation; - // the scale for drawing the image - private double scale = 1.0; - // controls whether the user can move images around - private boolean editable = true; - // the handler for moving the image around within the panel - private MoveHandler moveHandler = new MoveHandler(this); - // controls the drag part of drag and drop - private boolean dragEnabled = false; - // controls the filename of the dropped file - private String exportName = "UntitledImage"; - // controls the format and filename extension of the dropped file - private String exportFormat = "png"; - - /** Creates a new instance of JXImageView */ - public JXImageView() { - // fix for: java.net/jira/browse/SWINGX-1479 - setBackgroundPainter(new MattePainter(PaintUtils.getCheckerPaint(Color.white,new Color(250,250,250),50))); - setEditable(true); - } - - - - /* ========= properties ========= */ - /** - * Gets the current image location. This location can be changed programmatically - * or by the user dragging the image within the JXImageView. - * @return the current image location - */ - public Point2D getImageLocation() { - return imageLocation; - } - - /** - * Set the current image location. - * @param imageLocation The new image location. - */ - public void setImageLocation(Point2D imageLocation) { - Point2D old = getImageLocation(); - this.imageLocation = imageLocation; - firePropertyChange("imageLocation", old, getImageLocation()); - repaint(); - } - - /** - * Gets the currently set image, or null if no image is set. - * @return the currently set image, or null if no image is set. - */ - public Image getImage() { - return image; - } - - /** - * Sets the current image. Can set null if there should be no image show. - * @param image the new image to set, or null. - */ - public void setImage(Image image) { - Image oldImage = getImage(); - this.image = image; - setImageLocation(null); - setScale(1.0); - firePropertyChange("image",oldImage,image); - repaint(); - } - - /** - * Set the current image to an image pointed to by this URL. - * @param url a URL pointing to an image, or null - * @throws IOException thrown if the image cannot be loaded - */ - public void setImage(URL url) throws IOException { - setImageURL(url); - //setImage(ImageIO.read(url)); - } - - /** - * Set the current image to an image pointed to by this File. - * @param file a File pointing to an image - * @throws IOException thrown if the image cannot be loaded - */ - public void setImage(File file) throws IOException { - setImageURL(file.toURI().toURL()); - } - - /** - * Gets the current image scale . When the scale is set to 1.0 - * then one image pixel = one screen pixel. When scale < 1.0 the draw image - * will be smaller than it's real size. When scale > 1.0 the drawn image will - * be larger than it's real size. 1.0 is the default value. - * @return the current image scale - */ - public double getScale() { - return scale; - } - - /** - * Sets the current image scale . When the scale is set to 1.0 - * then one image pixel = one screen pixel. When scale < 1.0 the draw image - * will be smaller than it's real size. When scale > 1.0 the drawn image will - * be larger than it's real size. 1.0 is the default value. - * @param scale the new image scale - */ - public void setScale(double scale) { - double oldScale = this.scale; - this.scale = scale; - this.firePropertyChange("scale",oldScale,scale); - repaint(); - } - - /** - * Returns whether or not the user can drag images. - * @return whether or not the user can drag images - */ - public boolean isEditable() { - return editable; - } - - /** - * Sets whether or not the user can drag images. When set to true the user can - * drag the photo around with their mouse. Also the cursor will be set to the - * 'hand' cursor. When set to false the user cannot drag photos around - * and the cursor will be set to the default. - * @param editable whether or not the user can drag images - */ - public void setEditable(boolean editable) { - boolean old = isEditable(); - this.editable = editable; - if(editable) { - addMouseMotionListener(moveHandler); - addMouseListener(moveHandler); - this.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - try { - this.setTransferHandler(new DnDHandler()); - } catch (ClassNotFoundException ex) { - ex.printStackTrace(); - fireError(ex); - } - } else { - removeMouseMotionListener(moveHandler); - removeMouseListener(moveHandler); - this.setCursor(Cursor.getDefaultCursor()); - setTransferHandler(null); - } - firePropertyChange("editable", old, isEditable()); - } - - /** - * Sets the dragEnabled property, which determines whether or not - * the user can drag images out of the image view and into other components or - * application. Note: setting - * this to true will disable the ability to move the image around within the - * well., though it will not change the editable property directly. - * @param dragEnabled the value to set the dragEnabled property to. - */ - public void setDragEnabled(boolean dragEnabled) { - boolean old = isDragEnabled(); - this.dragEnabled = dragEnabled; - firePropertyChange("dragEnabled", old, isDragEnabled()); - } - - /** - * Gets the current value of the dragEnabled property. - * @return the current value of the dragEnabled property - */ - public boolean isDragEnabled() { - return dragEnabled; - } - - /** - * Adds an ErrorListener to the list of listeners to be notified - * of ErrorEvents - * @param el an ErrorListener to add - */ - public void addErrorListener(ErrorListener el) { - errorSupport.addErrorListener(el); - } - - /** - * Remove an ErrorListener from the list of listeners to be notified of ErrorEvents. - * @param el an ErrorListener to remove - */ - public void removeErrorListener(ErrorListener el) { - errorSupport.removeErrorListener(el); - } - - /** - * Send a new ErrorEvent to all registered ErrorListeners - * @param throwable the Error or Exception which was thrown - */ - protected void fireError(Throwable throwable) { - errorSupport.fireErrorEvent(throwable); - } - - - private static FileDialog getSafeFileDialog(Component comp) { - Window win = SwingUtilities.windowForComponent(comp); - if(win instanceof Dialog) { - return new FileDialog((Dialog)win); - } - if(win instanceof Frame) { - return new FileDialog((Frame)win); - } - return null; - } - - // an action which will open a file chooser and load the selected image - // if any. - /** - * Returns an Action which will open a file chooser, ask the user for an image file - * then load the image into the view. If the load fails an error will be fired - * to all registered ErrorListeners - * @return the action - * @see ErrorListener - * @deprecated see SwingX issue 990 - */ - @Deprecated - public Action getOpenAction() { - Action action = new AbstractAction() { - @Override - public void actionPerformed(ActionEvent actionEvent) { - FileDialog fd = getSafeFileDialog(JXImageView.this); - fd.setMode(FileDialog.LOAD); - fd.setVisible(true); - if(fd.getFile() != null) { - try { - setImage(new File(fd.getDirectory(),fd.getFile())); - } catch (IOException ex) { - fireError(ex); - } - } - /* - JFileChooser chooser = new JFileChooser(); - chooser.showOpenDialog(JXImageView.this); - File file = chooser.getSelectedFile(); - if(file != null) { - try { - setImage(file); - } catch (IOException ex) { - log.fine(ex.getMessage()); - ex.printStackTrace(); - fireError(ex); - } - } - */ - } - }; - action.putValue(Action.NAME,"Open"); - return action; - } - - // an action that will open a file chooser then save the current image to - // the selected file, if any. - /** - * Returns an Action which will open a file chooser, ask the user for an image file - * then save the image from the view. If the save fails an error will be fired - * to all registered ErrorListeners - * @return an Action - * @deprecated see SwingX issue 990 - */ - @Deprecated - public Action getSaveAction() { - Action action = new AbstractAction() { - @Override - public void actionPerformed(ActionEvent evt) { - Image img = getImage(); - BufferedImage dst = new BufferedImage( - img.getWidth(null), - img.getHeight(null), - BufferedImage.TYPE_INT_ARGB); - Graphics2D g = (Graphics2D)dst.getGraphics(); - - try { - // smooth scaling - g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, - RenderingHints.VALUE_INTERPOLATION_BICUBIC); - g.drawImage(img, 0, 0, null); - } finally { - g.dispose(); - } - FileDialog fd = new FileDialog((Frame)SwingUtilities.windowForComponent(JXImageView.this)); - fd.setMode(FileDialog.SAVE); - fd.setVisible(true); - if(fd.getFile() != null) { - try { - ImageIO.write(dst,"png",new File(fd.getDirectory(),fd.getFile())); - } catch (IOException ex) { - fireError(ex); - } - } - /* - JFileChooser chooser = new JFileChooser(); - chooser.showSaveDialog(JXImageView.this); - File file = chooser.getSelectedFile(); - if(file != null) { - try { - ImageIO.write(dst,"png",file); - } catch (IOException ex) { - log.fine(ex.getMessage()); - ex.printStackTrace(); - fireError(ex); - } - } - */ - } - }; - - action.putValue(Action.NAME,"Save"); - return action; - } - - /** - * Get an action which will rotate the currently selected image clockwise. - * @return an action - * @deprecated see SwingX issue 990 - */ - @Deprecated - public Action getRotateClockwiseAction() { - Action action = new AbstractAction() { - @Override - public void actionPerformed(ActionEvent evt) { - Image img = getImage(); - BufferedImage src = new BufferedImage( - img.getWidth(null), - img.getHeight(null), - BufferedImage.TYPE_INT_ARGB); - BufferedImage dst = new BufferedImage( - img.getHeight(null), - img.getWidth(null), - BufferedImage.TYPE_INT_ARGB); - Graphics2D g = (Graphics2D)src.getGraphics(); - - try { - // smooth scaling - g.drawImage(img, 0, 0, null); - } finally { - g.dispose(); - } - - AffineTransform trans = AffineTransform.getRotateInstance(Math.PI/2,0,0); - trans.translate(0,-src.getHeight()); - BufferedImageOp op = new AffineTransformOp(trans, AffineTransformOp.TYPE_NEAREST_NEIGHBOR); - op.filter(src,dst); - setImage(dst); - } - }; - action.putValue(Action.NAME,"Rotate Clockwise"); - return action; - } - - /** - * Gets an action which will rotate the current image counter clockwise. - * @return an Action - * @deprecated see SwingX issue 990 - */ - @Deprecated - public Action getRotateCounterClockwiseAction() { - Action action = new AbstractAction() { - @Override - public void actionPerformed(ActionEvent evt) { - Image img = getImage(); - BufferedImage src = new BufferedImage( - img.getWidth(null), - img.getHeight(null), - BufferedImage.TYPE_INT_ARGB); - BufferedImage dst = new BufferedImage( - img.getHeight(null), - img.getWidth(null), - BufferedImage.TYPE_INT_ARGB); - Graphics2D g = (Graphics2D)src.getGraphics(); - - try { - // smooth scaling - g.drawImage(img, 0, 0, null); - } finally { - g.dispose(); - } - AffineTransform trans = AffineTransform.getRotateInstance(-Math.PI/2,0,0); - trans.translate(-src.getWidth(),0); - BufferedImageOp op = new AffineTransformOp(trans, AffineTransformOp.TYPE_NEAREST_NEIGHBOR); - op.filter(src,dst); - setImage(dst); - } - }; - action.putValue(Action.NAME, "Rotate CounterClockwise"); - return action; - } - - /** - * Gets an action which will zoom the current image out by a factor of 2. - * @return an action - * @deprecated see SwingX issue 990 - */ - @Deprecated - public Action getZoomOutAction() { - Action action = new AbstractAction() { - @Override - public void actionPerformed(ActionEvent actionEvent) { - setScale(getScale()*0.5); - } - }; - action.putValue(Action.NAME,"Zoom Out"); - return action; - } - - /** - * Gets an action which will zoom the current image in by a factor of 2 - * @return an action - * @deprecated see SwingX issue 990 - */ - @Deprecated - public Action getZoomInAction() { - Action action = new AbstractAction() { - @Override - public void actionPerformed(ActionEvent actionEvent) { - setScale(getScale()*2); - } - }; - action.putValue(Action.NAME,"Zoom In"); - return action; - } - /* === overriden methods === */ - - /** - * Implementation detail. - * @param g - */ - @Override - protected void paintComponent(Graphics g) { - super.paintComponent(g); - if(getImage() != null) { - Point2D center = new Point2D.Double(getWidth()/2,getHeight()/2); - if(getImageLocation() != null) { - center = getImageLocation(); - } - Point2D loc = new Point2D.Double(); - double width = getImage().getWidth(null)*getScale(); - double height = getImage().getHeight(null)*getScale(); - loc.setLocation(center.getX()-width/2, center.getY()-height/2); - g.drawImage(getImage(), (int)loc.getX(), (int)loc.getY(), - (int)width,(int)height, - null); - } - } - - - /* === Internal helper classes === */ - - private class MoveHandler extends MouseInputAdapter { - private JXImageView panel; - private Point prev = null; - private Point start = null; - public MoveHandler(JXImageView panel) { - this.panel = panel; - } - - @Override - public void mousePressed(MouseEvent evt) { - prev = evt.getPoint(); - start = prev; - } - - @Override - public void mouseDragged(MouseEvent evt) { - Point curr = evt.getPoint(); - - if(isDragEnabled()) { - //log.fine("testing drag enabled: " + curr + " " + start); - //log.fine("distance = " + curr.distance(start)); - if(curr.distance(start) > 5) { - JXImageView.this.log.fine("starting the drag: "); - panel.getTransferHandler().exportAsDrag((JComponent)evt.getSource(),evt,TransferHandler.COPY); - return; - } - } - - int offx = curr.x - prev.x; - int offy = curr.y - prev.y; - Point2D offset = getImageLocation(); - if (offset == null) { - if (image != null) { - offset = new Point2D.Double(getWidth() / 2, getHeight() / 2); - } else { - offset = new Point2D.Double(0, 0); - } - } - offset = new Point2D.Double(offset.getX() + offx, offset.getY() + offy); - setImageLocation(offset); - prev = curr; - repaint(); - } - - @Override - public void mouseReleased(MouseEvent evt) { - prev = null; - } - } - - private class DnDHandler extends TransferHandler { - DataFlavor urlFlavor; - - public DnDHandler() throws ClassNotFoundException { - urlFlavor = new DataFlavor("application/x-java-url;class=java.net.URL"); - } - - @Override - public void exportAsDrag(JComponent c, InputEvent evt, int action) { - //log.fine("exportting as drag"); - super.exportAsDrag(c,evt,action); - } - @Override - public int getSourceActions(JComponent c) { - //log.fine("get source actions: " + c); - return COPY; - } - @Override - protected void exportDone(JComponent source, Transferable data, int action) { - //log.fine("exportDone: " + source + " " + data + " " +action); - } - - @Override - public boolean canImport(JComponent c, DataFlavor[] flavors) { - //log.fine("canImport:" + c); - for (int i = 0; i < flavors.length; i++) { - //log.fine("testing: "+flavors[i]); - if (DataFlavor.javaFileListFlavor.equals(flavors[i])) { - return true; - } - if (DataFlavor.imageFlavor.equals(flavors[i])) { - return true; - } - if (urlFlavor.match(flavors[i])) { - return true; - } - - } - return false; - } - - @Override - protected Transferable createTransferable(JComponent c) { - JXImageView view = (JXImageView)c; - return new ImageTransferable(view.getImage(), - view.getExportName(), view.getExportFormat()); - } - - @Override - @SuppressWarnings("unchecked") - public boolean importData(JComponent comp, Transferable t) { - if (canImport(comp, t.getTransferDataFlavors())) { - try { - if(t.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) { - List files = (List) t.getTransferData(DataFlavor.javaFileListFlavor); - //log.fine("doing file list flavor"); - if (files.size() > 0) { - File file = files.get(0); - //log.fine("readingt hte image: " + file.getCanonicalPath()); - /*Iterator it = ImageIO.getImageReaders(new FileInputStream(file)); - while(it.hasNext()) { - log.fine("can read: " + it.next()); - }*/ - setImageString(file.toURI().toURL().toString()); - //BufferedImage img = ImageIO.read(file.toURI().toURL()); - //setImage(img); - return true; - } - } - //log.fine("doing a uri list"); - Object obj = t.getTransferData(urlFlavor); - //log.fine("obj = " + obj + " " + obj.getClass().getPackage() + " " - // + obj.getClass().getName()); - if(obj instanceof URL) { - setImageString(((URL)obj).toString()); - } - return true; - } catch (Exception ex) { - log .severe(ex.getMessage()); - ex.printStackTrace(); - fireError(ex); - } - } - return false; - } - - } - - - private static class ImageTransferable implements Transferable { - private Image img; - private List files; - private String exportName, exportFormat; - public ImageTransferable(Image img, String exportName, String exportFormat) { - this.img = img; - this.exportName = exportName; - this.exportFormat = exportFormat; - } - - @Override - public DataFlavor[] getTransferDataFlavors() { - return new DataFlavor[] { DataFlavor.imageFlavor, - DataFlavor.javaFileListFlavor }; - } - - @Override - public boolean isDataFlavorSupported(DataFlavor flavor) { - if(flavor == DataFlavor.imageFlavor) { - return true; - } - return flavor == DataFlavor.javaFileListFlavor; - } - - @Override - public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { - //log.fine("doing get trans data: " + flavor); - if(flavor == DataFlavor.imageFlavor) { - return img; - } - if(flavor == DataFlavor.javaFileListFlavor) { - if(files == null) { - files = new ArrayList(); - File file = File.createTempFile(exportName,"."+exportFormat); - //log.fine("writing to: " + file); - ImageIO.write(GraphicsUtilities.convertToBufferedImage(img),exportFormat,file); - files.add(file); - } - //log.fine("returning: " + files); - return files; - } - return null; - } - } - - public String getExportName() { - return exportName; - } - - public void setExportName(String exportName) { - String old = getExportName(); - this.exportName = exportName; - firePropertyChange("exportName", old, getExportName()); - } - - public String getExportFormat() { - return exportFormat; - } - - public void setExportFormat(String exportFormat) { - String old = getExportFormat(); - this.exportFormat = exportFormat; - firePropertyChange("exportFormat", old, getExportFormat()); - } - - public URL getImageURL() { - return imageURL; - } - - public void setImageURL(URL imageURL) throws IOException { - URL old = getImageURL(); - this.imageURL = imageURL; - firePropertyChange("imageURL", old, getImageURL()); - setImage(ImageIO.read(getImageURL())); - } - - /** Returns the current image's URL (if available) as a string. - * If the image has no URL, or if there is no image, then this - * method will return null. - * @return the url of the image as a string - */ - public String getImageString() { - if(getImageURL() == null) { - return null; - } - return getImageURL().toString(); - } - - /** Sets the current image using a string. This string must - * contain a valid URL. - * @param url string of a URL - * @throws IOException thrown if the URL does not parse - */ - public void setImageString(String url) throws IOException { - String old = getImageString(); - setImageURL(new URL(url)); - firePropertyChange("imageString", old, url); - } - -} diff --git a/src/main/java/org/jdesktop/swingx/package-info.java b/src/main/java/org/jdesktop/swingx/package-info.java index dd988ec683..39bee0e064 100644 --- a/src/main/java/org/jdesktop/swingx/package-info.java +++ b/src/main/java/org/jdesktop/swingx/package-info.java @@ -124,9 +124,6 @@ *
              11. {@link org.jdesktop.swingx.JXColorSelectionButton} *
              12. {@link org.jdesktop.swingx.JXEditorPane} *
              13. {@link org.jdesktop.swingx.JXGradientChooser} - *
              14. Image containers {@link org.jdesktop.swingx.JXImageView ImageView} - * and {@link org.jdesktop.swingx.JXImagePanel ImagePanel} (PENDING JW: - * merge/remove one?) *
              15. {@link org.jdesktop.swingx.JXMultiThumbSlider MultiThumbSlider} * * diff --git a/src/main/java/org/jdesktop/swingx/plaf/AbstractComponentAddon.java b/src/main/java/org/jdesktop/swingx/plaf/AbstractComponentAddon.java index daa2a2f8b9..c0b646fce2 100644 --- a/src/main/java/org/jdesktop/swingx/plaf/AbstractComponentAddon.java +++ b/src/main/java/org/jdesktop/swingx/plaf/AbstractComponentAddon.java @@ -26,8 +26,6 @@ import org.jdesktop.swingx.plaf.nimbus.NimbusLookAndFeelAddons; import org.jdesktop.swingx.plaf.windows.WindowsLookAndFeelAddons; -import javax.swing.*; - /** * Ease the work of creating an addon for a component.
                * @@ -219,12 +217,4 @@ protected boolean isNimbus(LookAndFeelAddons addon) { return addon instanceof NimbusLookAndFeelAddons; } - /** - * @return true if the current look and feel is one of JGoodies Plastic l&fs - */ - protected boolean isPlastic() { - return UIManager.getLookAndFeel().getClass().getName() - .contains("Plastic"); - } - } diff --git a/src/main/java/org/jdesktop/swingx/plaf/TitledPanelAddon.java b/src/main/java/org/jdesktop/swingx/plaf/TitledPanelAddon.java index ee53e2acf4..5a8c02762f 100644 --- a/src/main/java/org/jdesktop/swingx/plaf/TitledPanelAddon.java +++ b/src/main/java/org/jdesktop/swingx/plaf/TitledPanelAddon.java @@ -71,18 +71,11 @@ protected void addLinuxDefaults(LookAndFeelAddons addon, DefaultsList defaults) protected void addMetalDefaults(LookAndFeelAddons addon, DefaultsList defaults) { super.addMetalDefaults(addon, defaults); - if (isPlastic()) { - defaults.add("JXTitledPanel.titleForeground", new ColorUIResource(Color.WHITE)); - defaults.add("JXTitledPanel.titlePainter", new PainterUIResource( - new MattePainter(new GradientPaint(0, 0, new Color(49, 121, 242), - 0, 1, new Color(198, 211, 247)), true))); - } else { - defaults.add("JXTitledPanel.titleForeground", new ColorUIResource(Color.WHITE)); - defaults.add("JXTitledPanel.titlePainter", new PainterUIResource( - new MattePainter(new GradientPaint(0, 0, + defaults.add("JXTitledPanel.titleForeground", new ColorUIResource(Color.WHITE)); + defaults.add("JXTitledPanel.titlePainter", new PainterUIResource( + new MattePainter(new GradientPaint(0, 0, MetalLookAndFeel.getCurrentTheme().getPrimaryControl(), 0, 1, MetalLookAndFeel.getCurrentTheme().getPrimaryControlDarkShadow()),true))); - } } @Override From 329d670d34085eb38c7b05893f62dd9d7a9e4f06 Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 6 Jul 2022 13:35:20 +0200 Subject: [PATCH 130/422] - modernize DescriptionPanel creation and unify in base class - cleanup JXHyperlink Former-commit-id: 3415dec754b5624d897f476063539cdc1b087307 --- .../java/mediathek/gui/tabs/AGuiTabPanel.java | 23 + .../gui/tabs/tab_downloads/GuiDownloads.java | 31 +- .../mediathek/gui/tabs/tab_film/GuiFilme.java | 19 +- .../java/org/jdesktop/swingx/JXHyperlink.java | 153 ++-- .../swingx/plaf/basic/BasicHyperlinkUI.java | 834 +++++++++--------- 5 files changed, 512 insertions(+), 548 deletions(-) diff --git a/src/main/java/mediathek/gui/tabs/AGuiTabPanel.java b/src/main/java/mediathek/gui/tabs/AGuiTabPanel.java index 6f5cd62e99..34f7bd3f3a 100644 --- a/src/main/java/mediathek/gui/tabs/AGuiTabPanel.java +++ b/src/main/java/mediathek/gui/tabs/AGuiTabPanel.java @@ -1,11 +1,18 @@ package mediathek.gui.tabs; +import javafx.application.Platform; +import javafx.embed.swing.JFXPanel; import mediathek.config.Daten; import mediathek.controller.history.SeenHistoryController; import mediathek.daten.DatenFilm; import mediathek.gui.messages.UpdateStatusBarLeftDisplayEvent; +import mediathek.javafx.descriptionPanel.DescriptionPanelController; +import mediathek.javafx.tool.JavaFxUtils; import mediathek.mainwindow.MediathekGui; import mediathek.tool.MessageBus; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.event.ActionEvent; @@ -13,6 +20,7 @@ import java.util.Optional; public abstract class AGuiTabPanel extends JPanel { + private static final Logger logger = LogManager.getLogger(); protected Daten daten; protected MediathekGui mediathekGui; @@ -31,6 +39,21 @@ protected void updateStartInfoProperty() { MessageBus.getMessageBus().publishAsync(new UpdateStatusBarLeftDisplayEvent()); } + protected void setupDescriptionPanel(@NotNull JFXPanel panel, @NotNull JTable tabelle) + { + JavaFxUtils.invokeInFxThreadAndWait(() -> { + try { + var descriptionPanelController = DescriptionPanelController.install(panel); + SwingUtilities.invokeLater(() -> tabelle.getSelectionModel().addListSelectionListener(e -> { + Optional optFilm = getCurrentlySelectedFilm(); + Platform.runLater(() -> descriptionPanelController.showFilmDescription(optFilm)); + })); + } catch (Exception ex) { + logger.error("setupDescriptionPanel", ex); + } + }); + } + public abstract void installMenuEntries(JMenu menu); public class MarkFilmAsSeenAction extends AbstractAction { diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java index 3d5c99ced0..3bdaf723a6 100644 --- a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java @@ -3,11 +3,7 @@ import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.GlazedLists; import ca.odell.glazedlists.swing.GlazedListsSwing; -import javafx.application.Platform; import javafx.embed.swing.JFXPanel; -import javafx.fxml.FXMLLoader; -import javafx.scene.Scene; -import javafx.scene.control.TabPane; import mediathek.config.Daten; import mediathek.config.Konstanten; import mediathek.config.MVConfig; @@ -26,7 +22,6 @@ import mediathek.gui.dialog.DialogEditDownload; import mediathek.gui.messages.*; import mediathek.gui.tabs.AGuiTabPanel; -import mediathek.javafx.descriptionPanel.DescriptionPanelController; import mediathek.mainwindow.MediathekGui; import mediathek.tool.*; import mediathek.tool.cellrenderer.CellRendererDownloads; @@ -157,7 +152,7 @@ public GuiDownloads(Daten aDaten, MediathekGui mediathekGui) { setupF4Key(mediathekGui); setupDownloadListTable(); - setupDescriptionPanel(); + setupDescriptionPanel(fxDescriptionPanel, tabelle); showDescriptionPanel(); @@ -407,30 +402,6 @@ public void fertig(ListenerFilmeLadenEvent event) { menu.add(shutdownAfterDownloadAction); } - private void setupDescriptionPanel() { - Platform.runLater(() -> { - try { - FXMLLoader loader = new FXMLLoader(); - loader.setLocation(Konstanten.FXML_FILM_DESCRIPTION_PANEL_URL); - - TabPane descriptionPane = loader.load(); - final DescriptionPanelController descriptionPanelController = loader.getController(); - descriptionPanelController.setOnCloseRequest(e -> { - SwingUtilities.invokeLater(() -> fxDescriptionPanel.setVisible(false)); - e.consume(); - }); - - fxDescriptionPanel.setScene(new Scene(descriptionPane)); - SwingUtilities.invokeLater(() -> tabelle.getSelectionModel().addListSelectionListener(e -> { - Optional optFilm = getCurrentlySelectedFilm(); - Platform.runLater(() -> descriptionPanelController.showFilmDescription(optFilm)); - })); - } catch (Exception ex) { - ex.printStackTrace(); - } - }); - } - public void onComponentShown() { mediathekGui.tabPaneIndexProperty().setValue(TabPaneIndex.DOWNLOAD); updateFilmData(); diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index 5501e4ada1..3d0226c91c 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -37,7 +37,6 @@ import mediathek.gui.tabs.AGuiTabPanel; import mediathek.javafx.bookmark.BookmarkWindowController; import mediathek.javafx.buttonsPanel.ButtonsPanelController; -import mediathek.javafx.descriptionPanel.DescriptionPanelController; import mediathek.javafx.filterpanel.FXSearchControlFieldMode; import mediathek.javafx.filterpanel.FilmActionPanel; import mediathek.javafx.tool.JavaFxUtils; @@ -85,7 +84,7 @@ public class GuiFilme extends AGuiTabPanel { private static final String ACTION_MAP_KEY_MARK_UNSEEN = "unseen"; private static final int[] HIDDEN_COLUMNS = {DatenFilm.FILM_ABSPIELEN, DatenFilm.FILM_AUFZEICHNEN, DatenFilm.FILM_MERKEN}; - private static final Logger logger = LogManager.getLogger(GuiFilme.class); + private static final Logger logger = LogManager.getLogger(); private static final int[] BUTTON_COLUMNS = {DatenFilm.FILM_ABSPIELEN, DatenFilm.FILM_AUFZEICHNEN, DatenFilm.FILM_MERKEN}; public static boolean[] VISIBLE_COLUMNS = new boolean[DatenFilm.MAX_ELEM]; @@ -145,7 +144,7 @@ public GuiFilme(Daten aDaten, MediathekGui mediathekGui) { setupFilmSelectionPropertyListener(mediathekGui); - setupDescriptionPanel(); + setupDescriptionPanel(fxDescriptionPanel, tabelle); setupPsetButtonsPanel(); setupFilmActionPanel(); @@ -320,20 +319,6 @@ public void componentHidden(ComponentEvent e) { fxPsetButtonsPanel.setVisible(visible); } - private void setupDescriptionPanel() { - JavaFxUtils.invokeInFxThreadAndWait(() -> { - try { - var descriptionPanelController = DescriptionPanelController.install(fxDescriptionPanel); - SwingUtilities.invokeLater(() -> tabelle.getSelectionModel().addListSelectionListener(e -> { - Optional optFilm = getCurrentlySelectedFilm(); - Platform.runLater(() -> descriptionPanelController.showFilmDescription(optFilm)); - })); - } catch (Exception ex) { - logger.error("setupDescriptionPanel", ex); - } - }); - } - /** * Show description panel based on settings. */ diff --git a/src/main/java/org/jdesktop/swingx/JXHyperlink.java b/src/main/java/org/jdesktop/swingx/JXHyperlink.java index 6b16361417..3c93258ff9 100644 --- a/src/main/java/org/jdesktop/swingx/JXHyperlink.java +++ b/src/main/java/org/jdesktop/swingx/JXHyperlink.java @@ -8,12 +8,12 @@ * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. - * + * * This library 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 * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA @@ -38,18 +38,18 @@ * A hyperlink component that derives from JButton to provide compatibility * mostly for binding actions enabled/disabled behavior accessibility i18n etc... *

                - * - * This button has visual state related to a notion of "clicked": - * foreground color is unclickedColor or clickedColor depending on + *

                + * This button has visual state related to a notion of "clicked": + * foreground color is unclickedColor or clickedColor depending on * its boolean bound property clicked being false or true, respectively. - * If the hyperlink has an action, it guarantees to synchronize its - * "clicked" state to an action value with key LinkAction.VISITED_KEY. + * If the hyperlink has an action, it guarantees to synchronize its + * "clicked" state to an action value with key LinkAction.VISITED_KEY. * Synchronization happens on setAction() and on propertyChange notification - * from the action. JXHyperlink accepts any type of action - + * from the action. JXHyperlink accepts any type of action - * {@link AbstractHyperlinkAction} is a convenience implementation to * simplify clicked control. *

                - * + * *

                 
                  *      LinkAction linkAction = new LinkAction("http://swinglabs.org") {
                  *            public void actionPerformed(ActionEvent e) {
                @@ -59,19 +59,19 @@
                  *      };
                  *      JXHyperlink hyperlink = new JXHyperlink(linkAction);
                  *  
                - * - * The hyperlink can be configured to always update its clicked + *

                + * The hyperlink can be configured to always update its clicked * property after firing the actionPerformed: - * + * *

                 
                  *      JXHyperlink hyperlink = new JXHyperlink(action);
                  *      hyperlink.setOverrulesActionOnClick(true);
                  *  
                - * - * By default, this property is false. The hyperlink will + *

                + * By default, this property is false. The hyperlink will * auto-click only if it has no action. Developers can change the * behaviour by overriding {@link JXHyperlink#isAutoSetClicked()}; - * + * * @author Richard Bair * @author Shai Almog * @author Jeanette Winzenburg @@ -87,7 +87,7 @@ public class JXHyperlink extends JButton { // ensure at least the default ui is registered static { - LookAndFeelAddons.contribute(new HyperlinkAddon()); + LookAndFeelAddons.contribute(new HyperlinkAddon()); } private boolean hasBeenVisited = false; @@ -118,8 +118,8 @@ public JXHyperlink() { /** * Creates a new instance of JHyperLink and configures it from provided Action. * - * @param action Action whose parameters will be borrowed to configure newly - * created JXHyperLink + * @param action Action whose parameters will be borrowed to configure newly + * created JXHyperLink */ public JXHyperlink(Action action) { super(); @@ -129,23 +129,19 @@ public JXHyperlink(Action action) { /** * Convenience method to create and install a HyperlinkAction for the given URI. - * - * @param uri - * to uri to create a HyperlinkAction for, maybe null. - * @throws HeadlessException - * if {@link GraphicsEnvironment#isHeadless()} returns {@code true} - * @throws UnsupportedOperationException - * if the current platform doesn't support Desktop - * + * + * @param uri to uri to create a HyperlinkAction for, maybe null. + * @throws HeadlessException if {@link GraphicsEnvironment#isHeadless()} returns {@code true} + * @throws UnsupportedOperationException if the current platform doesn't support Desktop * @see HyperlinkAction#createHyperlinkAction(URI) */ public void setURI(URI uri) { setAction(HyperlinkAction.createHyperlinkAction(uri)); } - + /** * Returns the foreground color for unvisited links. - * + * * @return Color for the hyper link if it has not yet been clicked. */ public Color getUnclickedColor() { @@ -153,23 +149,23 @@ public Color getUnclickedColor() { } /** - * Sets the color for the previously visited link. This value will override the one - * set by the "JXHyperlink.clickedColor" UIManager property and defaults. + * Sets the color for the previously not visited link. This value will override the one + * set by the "JXHyperlink.unclickedColor" UIManager property and defaults. * - * @param color Color for the hyper link if it has already been clicked. + * @param color Color for the hyper link if it has not yet been clicked. */ - public void setClickedColor(Color color) { - Color old = getClickedColor(); - clickedColor = color; - if (isClicked()) { - setForeground(getClickedColor()); + public void setUnclickedColor(Color color) { + Color old = getUnclickedColor(); + unclickedColor = color; + if (!isClicked()) { + setForeground(getUnclickedColor()); } - firePropertyChange("clickedColor", old, getClickedColor()); + firePropertyChange("unclickedColor", old, getUnclickedColor()); } /** * Returns the foreground color for visited links. - * + * * @return Color for the hyper link if it has already been clicked. */ public Color getClickedColor() { @@ -177,30 +173,40 @@ public Color getClickedColor() { } /** - * Sets the color for the previously not visited link. This value will override the one - * set by the "JXHyperlink.unclickedColor" UIManager property and defaults. + * Sets the color for the previously visited link. This value will override the one + * set by the "JXHyperlink.clickedColor" UIManager property and defaults. * - * @param color Color for the hyper link if it has not yet been clicked. + * @param color Color for the hyper link if it has already been clicked. */ - public void setUnclickedColor(Color color) { - Color old = getUnclickedColor(); - unclickedColor = color; - if (!isClicked()) { - setForeground(getUnclickedColor()); + public void setClickedColor(Color color) { + Color old = getClickedColor(); + clickedColor = color; + if (isClicked()) { + setForeground(getClickedColor()); } - firePropertyChange("unclickedColor", old, getUnclickedColor()); + firePropertyChange("clickedColor", old, getClickedColor()); + } + + /** + * Returns a boolean indicating if this link has already been visited. + * + * @return true if hyper link has already been clicked. + * @see #setClicked(boolean) + */ + public boolean isClicked() { + return hasBeenVisited; } /** * Sets the clicked property and updates visual state depending on clicked. * This implementation updated the foreground color. *

                - * + *

                * NOTE: as with all button's visual properties, this will not update the * backing action's "visited" state. - * + * * @param clicked flag to indicate if the button should be regarded as - * having been clicked or not. + * having been clicked or not. * @see #isClicked() */ public void setClicked(boolean clicked) { @@ -211,24 +217,27 @@ public void setClicked(boolean clicked) { } /** - * Returns a boolean indicating if this link has already been visited. - * - * @return true if hyper link has already been clicked. + * Returns a boolean indicating whether the clicked property should be set + * always on clicked. + * + * @return overrulesActionOnClick false if his button clicked property + * respects the Action's visited property. True if the clicked + * should be updated on every actionPerformed. + * @see #setOverrulesActionOnClick(boolean) * @see #setClicked(boolean) */ - public boolean isClicked() { - return hasBeenVisited; + public boolean getOverrulesActionOnClick() { + return overrulesActionOnClick; } /** * Sets the overrulesActionOnClick property. It controls whether this * button should overrule the Action's visited property on actionPerformed.

                - * + *

                * The default value is false. - * + * * @param overrule if true, fireActionPerformed will set clicked to true - * independent of action. - * + * independent of action. * @see #getOverrulesActionOnClick() * @see #setClicked(boolean) */ @@ -237,21 +246,6 @@ public void setOverrulesActionOnClick(boolean overrule) { this.overrulesActionOnClick = overrule; firePropertyChange("overrulesActionOnClick", old, getOverrulesActionOnClick()); } - - /** - * Returns a boolean indicating whether the clicked property should be set - * always on clicked. - * - * @return overrulesActionOnClick false if his button clicked property - * respects the Action's visited property. True if the clicked - * should be updated on every actionPerformed. - * - * @see #setOverrulesActionOnClick(boolean) - * @see #setClicked(boolean) - */ - public boolean getOverrulesActionOnClick() { - return overrulesActionOnClick; - } /** * {@inheritDoc}

                @@ -266,9 +260,10 @@ protected void fireActionPerformed(ActionEvent event) { } /** - * Returns a boolean indicating whether the clicked property should be set + * Returns a boolean indicating whether the clicked property should be set * after firing action events. * Here: true if no action or overrulesAction property is true. + * * @return true if fireActionEvent should force a clicked, false if not. */ protected boolean isAutoSetClicked() { @@ -316,7 +311,7 @@ private void configureClickedPropertyFromAction(Action a) { boolean clicked = false; if (a != null) { clicked = Boolean.TRUE.equals(a.getValue(AbstractHyperlinkAction.VISITED_KEY)); - + } setClicked(clicked); } @@ -333,15 +328,15 @@ private void init() { public String getUIClassID() { return uiClassID; } - + /** * Notification from the UIManager that the L&F has changed. * Replaces the current UI object with the latest version from the UIManager. - * + * * @see javax.swing.JComponent#updateUI */ @Override public void updateUI() { - setUI((ButtonUI)LookAndFeelAddons.getUI(this, ButtonUI.class)); + setUI((ButtonUI) LookAndFeelAddons.getUI(this, ButtonUI.class)); } } diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicHyperlinkUI.java b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicHyperlinkUI.java index ac94f78d31..f9967cb7b6 100644 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicHyperlinkUI.java +++ b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicHyperlinkUI.java @@ -8,12 +8,12 @@ * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. - * + * * This library 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 * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA @@ -51,62 +51,52 @@ public class BasicHyperlinkUI extends BasicButtonUI { @SuppressWarnings("unused") - private static final Logger LOG = Logger.getLogger(BasicHyperlinkUI.class - .getName()); - - public static ComponentUI createUI(JComponent c) { - return new BasicHyperlinkUI(); - } - - private static Rectangle viewRect = new Rectangle(); - - private static Rectangle textRect = new Rectangle(); - - private static Rectangle iconRect = new Rectangle(); - -// private static MouseListener handCursorListener = new HandCursor(); - + private static final Logger LOG = Logger.getLogger(BasicHyperlinkUI.class.getName()); + private static final Rectangle viewRect = new Rectangle(); + private static final Rectangle textRect = new Rectangle(); + private static final Rectangle iconRect = new Rectangle(); protected int dashedRectGapX; + // private static MouseListener handCursorListener = new HandCursor(); protected int dashedRectGapY; - protected int dashedRectGapWidth; - protected int dashedRectGapHeight; - private Color focusColor; - private View ulv; - - private PropertyChangeListener pcListener = new PropertyChangeListener() { + private final PropertyChangeListener pcListener = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { - // this method is called from the edt. only other place where ulv is used is in + // this method is called from the edt. only other place where ulv is used is in // painting which also happens on edt so it should be safe even without synchronization // sole purpose of this call is to reinitialize view on every property change ulv = null; - }}; + } + }; + + public static ComponentUI createUI(JComponent c) { + return new BasicHyperlinkUI(); + } @Override protected void installDefaults(AbstractButton b) { super.installDefaults(b); JXHyperlink link = (JXHyperlink) b; - + LookAndFeel.installProperty(b, "opaque", false); - + if (SwingXUtilities.isUIInstallable(link.getUnclickedColor())) { link.setUnclickedColor(UIManager.getColor("Hyperlink.linkColor")); } - + if (SwingXUtilities.isUIInstallable(link.getClickedColor())) { link.setClickedColor(UIManager.getColor("Hyperlink.visitedColor")); } - + b.setBorderPainted(false); b.setRolloverEnabled(true); - + if (SwingXUtilities.isUIInstallable(b.getBorder())) { b.setBorder(new BorderUIResource(BorderFactory.createEmptyBorder(0, 1, 0, 0))); } @@ -184,7 +174,7 @@ public void paint(Graphics g, JComponent c) { // AlphaComposite.SRC_OVER, 0.5f)); // } - if (text != null && !text.equals("")) { + if (text != null && !text.isEmpty()) { View v = (View) c.getClientProperty(BasicHTML.propertyKey); if (v != null) { paintHTMLText(g, b, textRect, text, v); @@ -204,14 +194,15 @@ public void paint(Graphics g, JComponent c) { /** * Method which renders the text of the current button if html. *

                - * @param g Graphics context - * @param b Current button to render + * + * @param g Graphics context + * @param b Current button to render * @param textRect Bounding rectangle to render the text. - * @param text String to render - * @param v the View to use. + * @param text String to render + * @param v the View to use. */ - protected void paintHTMLText(Graphics g, AbstractButton b, - Rectangle textRect, String text, View v) { + protected void paintHTMLText(Graphics g, AbstractButton b, + Rectangle textRect, String text, View v) { textRect.x += getTextShiftOffset(); textRect.y += getTextShiftOffset(); // fix #441-swingx - underline not painted for html @@ -234,7 +225,7 @@ protected void paintHTMLText(Graphics g, AbstractButton b, */ @Override protected void paintText(Graphics g, AbstractButton b, Rectangle textRect, - String text) { + String text) { //kgs -- SwingX #415: pixel-shift when disabled //BasicButtonUI shifts disabled text to the left by 1 pixel //we compensate for that here, so that all Hyperlinks paint @@ -242,7 +233,7 @@ protected void paintText(Graphics g, AbstractButton b, Rectangle textRect, if (!b.getModel().isEnabled()) { textRect.x += 1; } - + super.paintText(g, b, textRect, text); if (b.getModel().isRollover()) { paintUnderline(g, b, textRect, text); @@ -250,7 +241,7 @@ protected void paintText(Graphics g, AbstractButton b, Rectangle textRect, } private void paintUnderline(Graphics g, AbstractButton b, Rectangle rect, - String text) { + String text) { // JW: copied from JXTable.LinkRenderer FontMetrics fm = g.getFontMetrics(); int descent = fm.getDescent(); @@ -258,14 +249,14 @@ private void paintUnderline(Graphics g, AbstractButton b, Rectangle rect, // REMIND(aim): should we be basing the underline on // the font's baseline instead of the text bounds? g.drawLine(rect.x + getTextShiftOffset(), - (rect.y + rect.height) - descent + 1 + getTextShiftOffset(), - rect.x + rect.width + getTextShiftOffset(), - (rect.y + rect.height) - descent + 1 + getTextShiftOffset()); + (rect.y + rect.height) - descent + 1 + getTextShiftOffset(), + rect.x + rect.width + getTextShiftOffset(), + (rect.y + rect.height) - descent + 1 + getTextShiftOffset()); } @Override protected void paintFocus(Graphics g, AbstractButton b, Rectangle viewRect, - Rectangle textRect, Rectangle iconRect) { + Rectangle textRect, Rectangle iconRect) { if (b.getParent() instanceof JToolBar) { // Windows doesn't draw the focus rect for buttons in a toolbar. return; @@ -275,10 +266,10 @@ protected void paintFocus(Graphics g, AbstractButton b, Rectangle viewRect, g.setColor(getFocusColor()); // paint the focus rect around the union of text rect and icon rect // PENDING JW: duplicated to handle insets - Rectangle iconTextRect = getIconTextRect(b); + Rectangle iconTextRect = getIconTextRect(b); // PENDING JW: better factor handling of insets - the bare union doesn't respect insets // Rectangle iconTextRect = textRect.union(iconRect); - BasicGraphicsUtils.drawDashedRect(g, iconTextRect.x, iconTextRect.y, + BasicGraphicsUtils.drawDashedRect(g, iconTextRect.x, iconTextRect.y, iconTextRect.width, iconTextRect.height); // pre-#167-swingx: active area too large // int width = b.getWidth(); @@ -289,10 +280,10 @@ protected void paintFocus(Graphics g, AbstractButton b, Rectangle viewRect, @Override protected void paintButtonPressed(Graphics g, AbstractButton b) { - setTextShiftOffset(); + setTextShiftOffset(); } - + @Override protected BasicButtonListener createButtonListener(AbstractButton b) { return new BasicHyperlinkListener(b); @@ -300,7 +291,7 @@ protected BasicButtonListener createButtonListener(AbstractButton b) { /** * {@inheritDoc}

                - * + *

                * Overridden to return true if the position is inside the union of the * text and icon rectangle, false otherwise. */ @@ -322,10 +313,10 @@ private boolean isInside(Rectangle iconTextRect, int x, int y) { /** * C&p'ed from BasicGraphicsUtils (getPreferredButtonSize). - * + * * @param b the button to analyse. * @return the union of the text and icon rectangle of the AbstractButton - * or null if the button has children (??) + * or null if the button has children (??) */ protected Rectangle getIconTextRect(AbstractButton b) { if (b.getComponentCount() > 0) { @@ -365,8 +356,8 @@ protected Rectangle getIconTextRect(AbstractButton b) { } /** - * A BasicButtonListener specialized to the needs of a Hyperlink. - * + * A BasicButtonListener specialized to the needs of a Hyperlink. + * * @author Jeanette Winzenburg */ public static class BasicHyperlinkListener extends BasicButtonListener { @@ -378,20 +369,37 @@ public BasicHyperlinkListener(AbstractButton b) { super(b); } - + @Override public void stateChanged(ChangeEvent e) { AbstractButton button = (AbstractButton) e.getSource(); if (button.isRolloverEnabled()) { - button.setCursor(button.getModel().isRollover() ? + button.setCursor(button.getModel().isRollover() ? // PENDING JW: support customizable cursor Cursor.getPredefinedCursor(Cursor.HAND_CURSOR) : null); } super.stateChanged(e); } } - + static class ULHtml extends BasicHTML { + /** + * Overrides to the default stylesheet. Should consider + * just creating a completely fresh stylesheet. + */ + private static final String styleChanges = + "p { margin-top: 0; margin-bottom: 0; margin-left: 0; margin-right: 0; text-decoration: underline }" + + "body { margin-top: 0; margin-bottom: 0; margin-left: 0; margin-right: 0; text-decoration: underline }" + + "font {text-decoration: underline}"; + /** + * The source of the html renderers + */ + private static BasicEditorKit basicHTMLFactory; + /** + * Creates the Views that visually represent the model. + */ + private static ViewFactory basicHTMLViewFactory; + /** * Create an html renderer for the given component and * string of html. @@ -399,87 +407,69 @@ static class ULHtml extends BasicHTML { public static View createHTMLView(JComponent c, String html) { BasicEditorKit kit = getFactory(); Document doc = kit.createDefaultDocument(c.getFont(), - c.getForeground()); + c.getForeground()); Object base = c.getClientProperty(documentBaseKey); if (base instanceof URL) { - ((HTMLDocument)doc).setBase((URL)base); + ((HTMLDocument) doc).setBase((URL) base); } Reader r = new StringReader(html); try { kit.read(r, doc, 0); - } catch (Throwable e) { + } catch (Throwable ignored) { } ViewFactory f = kit.getViewFactory(); View hview = f.create(doc.getDefaultRootElement()); - View v = new Renderer(c, f, hview); - return v; + return new Renderer(c, f, hview); } static BasicEditorKit getFactory() { if (basicHTMLFactory == null) { - basicHTMLViewFactory = new BasicHTMLViewFactory(); + basicHTMLViewFactory = new BasicHTMLViewFactory(); basicHTMLFactory = new BasicEditorKit(); } return basicHTMLFactory; } - /** - * The source of the html renderers - */ - private static BasicEditorKit basicHTMLFactory; - - /** - * Creates the Views that visually represent the model. - */ - private static ViewFactory basicHTMLViewFactory; - - /** - * Overrides to the default stylesheet. Should consider - * just creating a completely fresh stylesheet. - */ - private static final String styleChanges = - "p { margin-top: 0; margin-bottom: 0; margin-left: 0; margin-right: 0; text-decoration: underline }" + - "body { margin-top: 0; margin-bottom: 0; margin-left: 0; margin-right: 0; text-decoration: underline }"+ - "font {text-decoration: underline}"; - static class BasicEditorKit extends HTMLEditorKit { - /** Shared base style for all documents created by us use. */ + /** + * Shared base style for all documents created by us use. + */ private static StyleSheet defaultStyles; - + /** * Overriden to return our own slimmed down style sheet. */ @Override public StyleSheet getStyleSheet() { if (defaultStyles == null) { - defaultStyles = new StyleSheet(); - StringReader r = new StringReader(styleChanges); - try { - defaultStyles.loadRules(r, null); - } catch (Throwable e) { - // don't want to die in static initialization... - // just display things wrong. + defaultStyles = new StyleSheet(); + StringReader r = new StringReader(styleChanges); + try { + defaultStyles.loadRules(r, null); + } catch (Throwable e) { + // don't want to die in static initialization... + // just display things wrong. + } + r.close(); + defaultStyles.addStyleSheet(super.getStyleSheet()); } - r.close(); - defaultStyles.addStyleSheet(super.getStyleSheet()); + return defaultStyles; } - return defaultStyles; - } - /** - * Sets the async policy to flush everything in one chunk, and - * to not display unknown tags. - */ - public Document createDefaultDocument(Font defaultFont, + /** + * Sets the async policy to flush everything in one chunk, and + * to not display unknown tags. + */ + public Document createDefaultDocument(Font defaultFont, Color foreground) { - StyleSheet styles = getStyleSheet(); - StyleSheet ss = new StyleSheet(); - ss.addStyleSheet(styles); - BasicDocument doc = new BasicDocument(ss, defaultFont, foreground); - doc.setAsynchronousLoadPriority(Integer.MAX_VALUE); - doc.setPreservesUnknownTags(false); - return doc; - } + StyleSheet styles = getStyleSheet(); + StyleSheet ss = new StyleSheet(); + ss.addStyleSheet(styles); + BasicDocument doc = new BasicDocument(ss, defaultFont, foreground); + doc.setAsynchronousLoadPriority(Integer.MAX_VALUE); + doc.setPreservesUnknownTags(false); + return doc; + } /** * Returns the ViewFactory that is used to make sure the Views don't @@ -502,7 +492,7 @@ public View create(Element elem) { View view = super.create(elem); if (view instanceof ImageView) { - ((ImageView)view).setLoadsSynchronously(true); + ((ImageView) view).setLoadsSynchronously(true); } return view; } @@ -516,353 +506,353 @@ public View create(Element elem) { */ static class BasicDocument extends HTMLDocument { - private static String displayPropertiesToCSS(Font f, Color c) { - StringBuilder rule = new StringBuilder("body {"); - if (f != null) { - rule.append(" font-family: "); - rule.append(f.getFamily()); - rule.append(" ; "); - rule.append(" font-size: "); - rule.append(f.getSize()); - rule.append("pt ;"); - if (f.isBold()) { - rule.append(" font-weight: 700 ; "); - } - if (f.isItalic()) { - rule.append(" font-style: italic ; "); - } + BasicDocument(StyleSheet s, Font defaultFont, Color foreground) { + super(s); + setPreservesUnknownTags(false); + setFontAndColor(defaultFont, foreground); } - if (c != null) { - rule.append(" color: #"); - if (c.getRed() < 16) { - rule.append('0'); - } - rule.append(Integer.toHexString(c.getRed())); - if (c.getGreen() < 16) { - rule.append('0'); + + // --------- EO 1.5 x 1.6 incompatibility handling .... + + private static String displayPropertiesToCSS(Font f, Color c) { + StringBuilder rule = new StringBuilder("body {"); + if (f != null) { + rule.append(" font-family: "); + rule.append(f.getFamily()); + rule.append(" ; "); + rule.append(" font-size: "); + rule.append(f.getSize()); + rule.append("pt ;"); + if (f.isBold()) { + rule.append(" font-weight: 700 ; "); + } + if (f.isItalic()) { + rule.append(" font-style: italic ; "); + } } - rule.append(Integer.toHexString(c.getGreen())); - if (c.getBlue() < 16) { - rule.append('0'); + if (c != null) { + rule.append(" color: #"); + if (c.getRed() < 16) { + rule.append('0'); + } + rule.append(Integer.toHexString(c.getRed())); + if (c.getGreen() < 16) { + rule.append('0'); + } + rule.append(Integer.toHexString(c.getGreen())); + if (c.getBlue() < 16) { + rule.append('0'); + } + rule.append(Integer.toHexString(c.getBlue())); + rule.append(" ; "); } - rule.append(Integer.toHexString(c.getBlue())); - rule.append(" ; "); + rule.append(" }"); + return rule.toString(); } - rule.append(" }"); - return rule.toString(); - } - // --------- EO 1.5 x 1.6 incompatibility handling .... - - BasicDocument(StyleSheet s, Font defaultFont, Color foreground) { - super(s); - setPreservesUnknownTags(false); - setFontAndColor(defaultFont, foreground); + /** + * Sets the default font and default color. These are set by + * adding a rule for the body that specifies the font and color. + * This allows the html to override these should it wish to have + * a custom font or color. + */ + private void setFontAndColor(Font font, Color fg) { + getStyleSheet().addRule(displayPropertiesToCSS(font, fg)); + } } + /** - * Sets the default font and default color. These are set by - * adding a rule for the body that specifies the font and color. - * This allows the html to override these should it wish to have - * a custom font or color. + * Root text view that acts as an HTML renderer. */ - private void setFontAndColor(Font font, Color fg) { - getStyleSheet().addRule(displayPropertiesToCSS(font,fg)); - } - } + static class Renderer extends View { + private final View view; + private final ViewFactory factory; + private final JComponent host; + private int width; - /** - * Root text view that acts as an HTML renderer. - */ - static class Renderer extends View { - - Renderer(JComponent c, ViewFactory f, View v) { + Renderer(JComponent c, ViewFactory f, View v) { super(null); - host = c; - factory = f; - view = v; - view.setParent(this); - // initially layout to the preferred size - setSize(view.getPreferredSpan(X_AXIS), view.getPreferredSpan(Y_AXIS)); - } + host = c; + factory = f; + view = v; + view.setParent(this); + // initially layout to the preferred size + setSize(view.getPreferredSpan(X_AXIS), view.getPreferredSpan(Y_AXIS)); + } - /** - * Fetches the attributes to use when rendering. At the root - * level there are no attributes. If an attribute is resolved - * up the view hierarchy this is the end of the line. - */ + /** + * Fetches the attributes to use when rendering. At the root + * level there are no attributes. If an attribute is resolved + * up the view hierarchy this is the end of the line. + */ @Override public AttributeSet getAttributes() { - return null; - } + return null; + } - /** - * Determines the preferred span for this view along an axis. - * - * @param axis may be either X_AXIS or Y_AXIS - * @return the span the view would like to be rendered into. - * Typically the view is told to render into the span - * that is returned, although there is no guarantee. - * The parent may choose to resize or break the view. - */ - @Override - public float getPreferredSpan(int axis) { - if (axis == X_AXIS) { - // width currently laid out to - return width; + /** + * Determines the preferred span for this view along an axis. + * + * @param axis may be either X_AXIS or Y_AXIS + * @return the span the view would like to be rendered into. + * Typically the view is told to render into the span + * that is returned, although there is no guarantee. + * The parent may choose to resize or break the view. + */ + @Override + public float getPreferredSpan(int axis) { + if (axis == X_AXIS) { + // width currently laid out to + return width; + } + return view.getPreferredSpan(axis); } - return view.getPreferredSpan(axis); - } - /** - * Determines the minimum span for this view along an axis. - * - * @param axis may be either X_AXIS or Y_AXIS - * @return the span the view would like to be rendered into. - * Typically the view is told to render into the span - * that is returned, although there is no guarantee. - * The parent may choose to resize or break the view. - */ - @Override - public float getMinimumSpan(int axis) { - return view.getMinimumSpan(axis); - } + /** + * Determines the minimum span for this view along an axis. + * + * @param axis may be either X_AXIS or Y_AXIS + * @return the span the view would like to be rendered into. + * Typically the view is told to render into the span + * that is returned, although there is no guarantee. + * The parent may choose to resize or break the view. + */ + @Override + public float getMinimumSpan(int axis) { + return view.getMinimumSpan(axis); + } - /** - * Determines the maximum span for this view along an axis. - * - * @param axis may be either X_AXIS or Y_AXIS - * @return the span the view would like to be rendered into. - * Typically the view is told to render into the span - * that is returned, although there is no guarantee. - * The parent may choose to resize or break the view. - */ - @Override - public float getMaximumSpan(int axis) { - return Integer.MAX_VALUE; - } + /** + * Determines the maximum span for this view along an axis. + * + * @param axis may be either X_AXIS or Y_AXIS + * @return the span the view would like to be rendered into. + * Typically the view is told to render into the span + * that is returned, although there is no guarantee. + * The parent may choose to resize or break the view. + */ + @Override + public float getMaximumSpan(int axis) { + return Integer.MAX_VALUE; + } - /** - * Specifies that a preference has changed. - * Child views can call this on the parent to indicate that - * the preference has changed. The root view routes this to - * invalidate on the hosting component. - *

                - * This can be called on a different thread from the - * event dispatching thread and is basically unsafe to - * propagate into the component. To make this safe, - * the operation is transferred over to the event dispatching - * thread for completion. It is a design goal that all view - * methods be safe to call without concern for concurrency, - * and this behavior helps make that true. - * - * @param child the child view - * @param width true if the width preference has changed - * @param height true if the height preference has changed - */ - @Override - public void preferenceChanged(View child, boolean width, boolean height) { - host.revalidate(); - host.repaint(); - } + /** + * Specifies that a preference has changed. + * Child views can call this on the parent to indicate that + * the preference has changed. The root view routes this to + * invalidate on the hosting component. + *

                + * This can be called on a different thread from the + * event dispatching thread and is basically unsafe to + * propagate into the component. To make this safe, + * the operation is transferred over to the event dispatching + * thread for completion. It is a design goal that all view + * methods be safe to call without concern for concurrency, + * and this behavior helps make that true. + * + * @param child the child view + * @param width true if the width preference has changed + * @param height true if the height preference has changed + */ + @Override + public void preferenceChanged(View child, boolean width, boolean height) { + host.revalidate(); + host.repaint(); + } - /** - * Determines the desired alignment for this view along an axis. - * - * @param axis may be either X_AXIS or Y_AXIS - * @return the desired alignment, where 0.0 indicates the origin - * and 1.0 the full span away from the origin - */ - @Override - public float getAlignment(int axis) { - return view.getAlignment(axis); - } + /** + * Determines the desired alignment for this view along an axis. + * + * @param axis may be either X_AXIS or Y_AXIS + * @return the desired alignment, where 0.0 indicates the origin + * and 1.0 the full span away from the origin + */ + @Override + public float getAlignment(int axis) { + return view.getAlignment(axis); + } - /** - * Renders the view. - * - * @param g the graphics context - * @param allocation the region to render into - */ - @Override - public void paint(Graphics g, Shape allocation) { - Rectangle alloc = allocation.getBounds(); - view.setSize(alloc.width, alloc.height); - view.paint(g, allocation); - } - - /** - * Sets the view parent. - * - * @param parent the parent view - */ - @Override - public void setParent(View parent) { - throw new Error("Can't set parent on root view"); - } + /** + * Renders the view. + * + * @param g the graphics context + * @param allocation the region to render into + */ + @Override + public void paint(Graphics g, Shape allocation) { + Rectangle alloc = allocation.getBounds(); + view.setSize(alloc.width, alloc.height); + view.paint(g, allocation); + } - /** - * Returns the number of views in this view. Since - * this view simply wraps the root of the view hierarchy - * it has exactly one child. - * - * @return the number of views - * @see #getView - */ - @Override - public int getViewCount() { - return 1; - } + /** + * Sets the view parent. + * + * @param parent the parent view + */ + @Override + public void setParent(View parent) { + throw new Error("Can't set parent on root view"); + } - /** - * Gets the n-th view in this container. - * - * @param n the number of the view to get - * @return the view - */ - @Override - public View getView(int n) { - return view; - } + /** + * Returns the number of views in this view. Since + * this view simply wraps the root of the view hierarchy + * it has exactly one child. + * + * @return the number of views + * @see #getView + */ + @Override + public int getViewCount() { + return 1; + } - /** - * Provides a mapping from the document model coordinate space - * to the coordinate space of the view mapped to it. - * - * @param pos the position to convert - * @param a the allocated region to render into - * @return the bounding box of the given position - */ - @Override - public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException { - return view.modelToView(pos, a, b); - } + /** + * Gets the n-th view in this container. + * + * @param n the number of the view to get + * @return the view + */ + @Override + public View getView(int n) { + return view; + } - /** - * Provides a mapping from the document model coordinate space - * to the coordinate space of the view mapped to it. - * - * @param p0 the position to convert >= 0 - * @param b0 the bias toward the previous character or the - * next character represented by p0, in case the - * position is a boundary of two views. - * @param p1 the position to convert >= 0 - * @param b1 the bias toward the previous character or the - * next character represented by p1, in case the - * position is a boundary of two views. - * @param a the allocated region to render into - * @return the bounding box of the given position is returned - * @exception BadLocationException if the given position does - * not represent a valid location in the associated document - * @exception IllegalArgumentException for an invalid bias argument - * @see View#viewToModel - */ - @Override - public Shape modelToView(int p0, Position.Bias b0, int p1, - Position.Bias b1, Shape a) throws BadLocationException { - return view.modelToView(p0, b0, p1, b1, a); - } + /** + * Provides a mapping from the document model coordinate space + * to the coordinate space of the view mapped to it. + * + * @param pos the position to convert + * @param a the allocated region to render into + * @return the bounding box of the given position + */ + @Override + public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException { + return view.modelToView(pos, a, b); + } - /** - * Provides a mapping from the view coordinate space to the logical - * coordinate space of the model. - * - * @param x x coordinate of the view location to convert - * @param y y coordinate of the view location to convert - * @param a the allocated region to render into - * @return the location within the model that best represents the - * given point in the view - */ - @Override - public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) { - return view.viewToModel(x, y, a, bias); - } + /** + * Provides a mapping from the document model coordinate space + * to the coordinate space of the view mapped to it. + * + * @param p0 the position to convert >= 0 + * @param b0 the bias toward the previous character or the + * next character represented by p0, in case the + * position is a boundary of two views. + * @param p1 the position to convert >= 0 + * @param b1 the bias toward the previous character or the + * next character represented by p1, in case the + * position is a boundary of two views. + * @param a the allocated region to render into + * @return the bounding box of the given position is returned + * @throws BadLocationException if the given position does + * not represent a valid location in the associated document + * @throws IllegalArgumentException for an invalid bias argument + * @see View#viewToModel + */ + @Override + public Shape modelToView(int p0, Position.Bias b0, int p1, + Position.Bias b1, Shape a) throws BadLocationException { + return view.modelToView(p0, b0, p1, b1, a); + } - /** - * Returns the document model underlying the view. - * - * @return the model - */ - @Override - public Document getDocument() { - return view.getDocument(); - } - - /** - * Returns the starting offset into the model for this view. - * - * @return the starting offset - */ - @Override - public int getStartOffset() { - return view.getStartOffset(); - } + /** + * Provides a mapping from the view coordinate space to the logical + * coordinate space of the model. + * + * @param x x coordinate of the view location to convert + * @param y y coordinate of the view location to convert + * @param a the allocated region to render into + * @return the location within the model that best represents the + * given point in the view + */ + @Override + public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) { + return view.viewToModel(x, y, a, bias); + } - /** - * Returns the ending offset into the model for this view. - * - * @return the ending offset - */ - @Override - public int getEndOffset() { - return view.getEndOffset(); - } + /** + * Returns the document model underlying the view. + * + * @return the model + */ + @Override + public Document getDocument() { + return view.getDocument(); + } - /** - * Gets the element that this view is mapped to. - * - * @return the view - */ - @Override - public Element getElement() { - return view.getElement(); - } + /** + * Returns the starting offset into the model for this view. + * + * @return the starting offset + */ + @Override + public int getStartOffset() { + return view.getStartOffset(); + } - /** - * Sets the view size. - * - * @param width the width - * @param height the height - */ - @Override - public void setSize(float width, float height) { - this.width = (int) width; - view.setSize(width, height); - } + /** + * Returns the ending offset into the model for this view. + * + * @return the ending offset + */ + @Override + public int getEndOffset() { + return view.getEndOffset(); + } - /** - * Fetches the container hosting the view. This is useful for - * things like scheduling a repaint, finding out the host - * components font, etc. The default implementation - * of this is to forward the query to the parent view. - * - * @return the container - */ - @Override - public Container getContainer() { - return host; - } - - /** - * Fetches the factory to be used for building the - * various view fragments that make up the view that - * represents the model. This is what determines - * how the model will be represented. This is implemented - * to fetch the factory provided by the associated - * EditorKit. - * - * @return the factory - */ - @Override - public ViewFactory getViewFactory() { - return factory; - } + /** + * Gets the element that this view is mapped to. + * + * @return the view + */ + @Override + public Element getElement() { + return view.getElement(); + } - private int width; - private View view; - private ViewFactory factory; - private JComponent host; + /** + * Sets the view size. + * + * @param width the width + * @param height the height + */ + @Override + public void setSize(float width, float height) { + this.width = (int) width; + view.setSize(width, height); + } + + /** + * Fetches the container hosting the view. This is useful for + * things like scheduling a repaint, finding out the host + * components font, etc. The default implementation + * of this is to forward the query to the parent view. + * + * @return the container + */ + @Override + public Container getContainer() { + return host; + } + + /** + * Fetches the factory to be used for building the + * various view fragments that make up the view that + * represents the model. This is what determines + * how the model will be represented. This is implemented + * to fetch the factory provided by the associated + * EditorKit. + * + * @return the factory + */ + @Override + public ViewFactory getViewFactory() { + return factory; + } } } From 3a6df88109126d17445c4367b7fc2978728144c6 Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 6 Jul 2022 20:59:20 +0200 Subject: [PATCH 131/422] - rewrite film description panel and code cleanup Former-commit-id: d5a79aa44527b0d84847b86dad0f7f9c1b8994e7 --- .../java/mediathek/config/Konstanten.java | 2 - .../java/mediathek/gui/tabs/AGuiTabPanel.java | 52 +++-- .../gui/tabs/tab_downloads/GuiDownloads.java | 55 +++-- .../tabs/tab_film/FilmDescriptionPanel.java | 205 ++++++++++++++++++ .../mediathek/gui/tabs/tab_film/GuiFilme.java | 112 +++++----- .../DescriptionPanelController.java | 177 --------------- .../res/programm/fxml/filmdescription.fxml | 23 -- 7 files changed, 309 insertions(+), 317 deletions(-) create mode 100644 src/main/java/mediathek/gui/tabs/tab_film/FilmDescriptionPanel.java delete mode 100644 src/main/java/mediathek/javafx/descriptionPanel/DescriptionPanelController.java delete mode 100644 src/main/resources/mediathek/res/programm/fxml/filmdescription.fxml diff --git a/src/main/java/mediathek/config/Konstanten.java b/src/main/java/mediathek/config/Konstanten.java index fdd7268eff..f6106d10a7 100644 --- a/src/main/java/mediathek/config/Konstanten.java +++ b/src/main/java/mediathek/config/Konstanten.java @@ -23,7 +23,6 @@ import mediathek.tool.Version; import okhttp3.HttpUrl; -import java.net.URL; import java.util.concurrent.TimeUnit; public class Konstanten { @@ -35,7 +34,6 @@ public class Konstanten { public static final boolean APP_IS_NIGHTLY = true; public static final String EXTERNAL_UPDATE_PROPERTY = "externalUpdateCheck"; public static final String MACOS_OFFICIAL_APP = "OSX_OFFICIAL_APP"; - public static final URL FXML_FILM_DESCRIPTION_PANEL_URL = Konstanten.class.getResource("/mediathek/res/programm/fxml/filmdescription.fxml"); public static final String FORMAT_ZIP = ".zip"; public static final String FORMAT_XZ = ".xz"; diff --git a/src/main/java/mediathek/gui/tabs/AGuiTabPanel.java b/src/main/java/mediathek/gui/tabs/AGuiTabPanel.java index 34f7bd3f3a..764d886b49 100644 --- a/src/main/java/mediathek/gui/tabs/AGuiTabPanel.java +++ b/src/main/java/mediathek/gui/tabs/AGuiTabPanel.java @@ -1,17 +1,13 @@ package mediathek.gui.tabs; -import javafx.application.Platform; -import javafx.embed.swing.JFXPanel; import mediathek.config.Daten; import mediathek.controller.history.SeenHistoryController; import mediathek.daten.DatenFilm; import mediathek.gui.messages.UpdateStatusBarLeftDisplayEvent; -import mediathek.javafx.descriptionPanel.DescriptionPanelController; -import mediathek.javafx.tool.JavaFxUtils; +import mediathek.gui.tabs.tab_film.FilmDescriptionPanel; import mediathek.mainwindow.MediathekGui; +import mediathek.tool.ApplicationConfiguration; import mediathek.tool.MessageBus; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -20,9 +16,34 @@ import java.util.Optional; public abstract class AGuiTabPanel extends JPanel { - private static final Logger logger = LogManager.getLogger(); protected Daten daten; protected MediathekGui mediathekGui; + protected JTabbedPane descriptionTab = new JTabbedPane(); + protected FilmDescriptionPanel descriptionPanel; + + /** + * Show description panel based on settings. + */ + protected void makeDescriptionTabVisible(boolean visible) { + if (visible) { + if (descriptionTab.indexOfComponent(descriptionPanel) == -1) { + descriptionTab.add(descriptionPanel, 0); + descriptionTab.setTitleAt(0, "Beschreibung"); + } + } else { + if (descriptionTab.indexOfComponent(descriptionPanel) != -1) { + descriptionTab.remove(descriptionPanel); + } + } + } + + protected abstract void setupShowFilmDescriptionMenuItem(); + + protected void initDescriptionTabVisibility(@NotNull String configKey) { + boolean visible = ApplicationConfiguration.getConfiguration().getBoolean(configKey, true); + + makeDescriptionTabVisible(visible); + } public abstract void tabelleSpeichern(); @@ -33,27 +54,12 @@ public abstract class AGuiTabPanel extends JPanel { */ protected abstract List getSelFilme(); - protected abstract Optional getCurrentlySelectedFilm(); + public abstract Optional getCurrentlySelectedFilm(); protected void updateStartInfoProperty() { MessageBus.getMessageBus().publishAsync(new UpdateStatusBarLeftDisplayEvent()); } - protected void setupDescriptionPanel(@NotNull JFXPanel panel, @NotNull JTable tabelle) - { - JavaFxUtils.invokeInFxThreadAndWait(() -> { - try { - var descriptionPanelController = DescriptionPanelController.install(panel); - SwingUtilities.invokeLater(() -> tabelle.getSelectionModel().addListSelectionListener(e -> { - Optional optFilm = getCurrentlySelectedFilm(); - Platform.runLater(() -> descriptionPanelController.showFilmDescription(optFilm)); - })); - } catch (Exception ex) { - logger.error("setupDescriptionPanel", ex); - } - }); - } - public abstract void installMenuEntries(JMenu menu); public class MarkFilmAsSeenAction extends AbstractAction { diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java index 3bdaf723a6..4f401a80a9 100644 --- a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java @@ -3,7 +3,6 @@ import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.GlazedLists; import ca.odell.glazedlists.swing.GlazedListsSwing; -import javafx.embed.swing.JFXPanel; import mediathek.config.Daten; import mediathek.config.Konstanten; import mediathek.config.MVConfig; @@ -22,6 +21,7 @@ import mediathek.gui.dialog.DialogEditDownload; import mediathek.gui.messages.*; import mediathek.gui.tabs.AGuiTabPanel; +import mediathek.gui.tabs.tab_film.FilmDescriptionPanel; import mediathek.mainwindow.MediathekGui; import mediathek.tool.*; import mediathek.tool.cellrenderer.CellRendererDownloads; @@ -55,6 +55,7 @@ import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; +import java.util.function.IntConsumer; public class GuiDownloads extends AGuiTabPanel { public static final String NAME = "Downloads"; @@ -139,7 +140,6 @@ public class GuiDownloads extends AGuiTabPanel { private JSpinner jSpinner1; private JEditorPane txtDownload; private JScrollPane downloadListScrollPane; - private JFXPanel fxDescriptionPanel; public GuiDownloads(Daten aDaten, MediathekGui mediathekGui) { super(); @@ -152,9 +152,8 @@ public GuiDownloads(Daten aDaten, MediathekGui mediathekGui) { setupF4Key(mediathekGui); setupDownloadListTable(); - setupDescriptionPanel(fxDescriptionPanel, tabelle); - showDescriptionPanel(); + setupDescriptionTab(); init(); @@ -177,6 +176,21 @@ public GuiDownloads(Daten aDaten, MediathekGui mediathekGui) { setupTaskbarMenu(); } + protected void setupDescriptionTab() { + descriptionPanel = new FilmDescriptionPanel(this); + descriptionPanel.install(descriptionTab, tabelle); + descriptionTab.putClientProperty("JTabbedPane.tabClosable", true); + descriptionTab.putClientProperty("JTabbedPane.tabCloseCallback", + (IntConsumer) tabIndex -> { + // close description tab here + // must use doClick to trigger model change + cbShowDownloadDescription.doClick(); + }); + + setupShowFilmDescriptionMenuItem(); + initDescriptionTabVisibility(ApplicationConfiguration.DOWNLOAD_SHOW_DESCRIPTION); + } + private void setupDownloadListStatusBar() { statusBar.add(totalDownloadsLabel); statusBar.add(lblAbos); @@ -613,8 +627,6 @@ public void ping() { } } }); - - setupShowFilmDescriptionMenuItem(); } @Handler @@ -640,30 +652,16 @@ private void handleGeoStateChangedEvent(GeoStateChangedEvent e) { * Most of the setup is done in {@link GuiDownloads} function. * Here we just display the panel */ - private void setupShowFilmDescriptionMenuItem() { + @Override + protected void setupShowFilmDescriptionMenuItem() { cbShowDownloadDescription.setSelected(ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.DOWNLOAD_SHOW_DESCRIPTION, true)); - cbShowDownloadDescription.addActionListener(l -> fxDescriptionPanel.setVisible(cbShowDownloadDescription.isSelected())); - cbShowDownloadDescription.addItemListener(e -> ApplicationConfiguration.getConfiguration().setProperty(ApplicationConfiguration.DOWNLOAD_SHOW_DESCRIPTION, cbShowDownloadDescription.isSelected())); - fxDescriptionPanel.addComponentListener(new ComponentAdapter() { - @Override - public void componentShown(ComponentEvent e) { - cbShowDownloadDescription.setSelected(true); - } - - @Override - public void componentHidden(ComponentEvent e) { - cbShowDownloadDescription.setSelected(false); - } + cbShowDownloadDescription.addActionListener(l -> { + boolean visible = cbShowDownloadDescription.isSelected(); + makeDescriptionTabVisible(visible); + config.setProperty(ApplicationConfiguration.DOWNLOAD_SHOW_DESCRIPTION, visible); }); } - /** - * Show description panel based on settings. - */ - private void showDescriptionPanel() { - fxDescriptionPanel.setVisible(ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.DOWNLOAD_SHOW_DESCRIPTION, true)); - } - private synchronized void reloadTable() { // nur Downloads die schon in der Liste sind werden geladen tabelle.getSpalten(); @@ -725,7 +723,7 @@ private ArrayList getSelDownloads() { } @Override - protected Optional getCurrentlySelectedFilm() { + public Optional getCurrentlySelectedFilm() { final int selectedTableRow = tabelle.getSelectedRow(); if (selectedTableRow != -1) { Optional optRet; @@ -1126,7 +1124,6 @@ private void initComponents() { txtDownload = new JEditorPane(); var downloadListArea = new JPanel(); downloadListScrollPane = new JScrollPane(); - fxDescriptionPanel = new JFXPanel(); //======== this ======== setLayout(new BorderLayout()); @@ -1237,7 +1234,7 @@ private void initComponents() { tempPanel.add(downloadListScrollPane, BorderLayout.CENTER); tempPanel.add(statusBar, BorderLayout.SOUTH); downloadListArea.add(tempPanel, BorderLayout.CENTER); - downloadListArea.add(fxDescriptionPanel, BorderLayout.SOUTH); + downloadListArea.add(descriptionTab, BorderLayout.SOUTH); } jSplitPane1.setRightComponent(downloadListArea); } diff --git a/src/main/java/mediathek/gui/tabs/tab_film/FilmDescriptionPanel.java b/src/main/java/mediathek/gui/tabs/tab_film/FilmDescriptionPanel.java new file mode 100644 index 0000000000..edf41957c0 --- /dev/null +++ b/src/main/java/mediathek/gui/tabs/tab_film/FilmDescriptionPanel.java @@ -0,0 +1,205 @@ +package mediathek.gui.tabs.tab_film; + +import mediathek.daten.DatenFilm; +import mediathek.gui.dialog.DialogFilmBeschreibung; +import mediathek.gui.tabs.AGuiTabPanel; +import mediathek.mainwindow.MediathekGui; +import mediathek.tool.GuiFunktionen; +import mediathek.tool.sender_icon_cache.MVSenderIconCache; +import net.miginfocom.layout.AC; +import net.miginfocom.layout.CC; +import net.miginfocom.layout.LC; +import net.miginfocom.swing.MigLayout; +import org.jdesktop.swingx.JXHyperlink; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.net.URL; + +public class FilmDescriptionPanel extends JPanel { + private final AGuiTabPanel currentTab; + private final JScrollPane scrollPane1 = new JScrollPane(); + private final JPopupMenu popupMenu = new JPopupMenu(); + private final JLabel lblIcon = new JLabel(); + private final JLabel lblThema = new JLabel(); + private final JLabel lblTitel = new JLabel(); + private final JTextArea textArea = new JTextArea(); + private final JXHyperlink hyperlink = new JXHyperlink(); + + private DatenFilm currentFilm; + + public FilmDescriptionPanel(@NotNull AGuiTabPanel currentTab) { + this.currentTab = currentTab; + + initComponents(); + + createPopupMenu(); + + setAllFieldsEmpty(); + } + + private void createPopupMenu() { + var item = new JMenuItem("Beschreibung ändern..."); + item.addActionListener(l -> { + DialogFilmBeschreibung dialog = new DialogFilmBeschreibung(MediathekGui.ui(), currentFilm); + dialog.setVisible(true); + }); + popupMenu.add(item); + popupMenu.addSeparator(); + + item = new JMenuItem("Beschreibung in Zwischenablage kopieren"); + item.addActionListener(l -> GuiFunktionen.copyToClipboard(currentFilm.getDescription())); + popupMenu.add(item); + + item = new JMenuItem("Filmbasisinformationen in Zwischenablage kopieren"); + item.addActionListener(l -> { + String sb = currentFilm.getSender() + + " - " + + currentFilm.getThema() + + " - " + + currentFilm.getTitle(); + + GuiFunktionen.copyToClipboard(sb); + }); + popupMenu.add(item); + + setComponentPopupMenu(popupMenu); + textArea.setComponentPopupMenu(popupMenu); + } + + private void initComponents() { + setLayout(new MigLayout( + new LC().hideMode(3), + new AC() + .fill().gap() + .grow().fill(), + new AC() + .gap() + .gap() + .gap() + )); + + lblIcon.setMaximumSize(new Dimension(96, 96)); + lblIcon.setPreferredSize(new Dimension(96, 96)); + lblIcon.setVerticalAlignment(SwingConstants.TOP); + add(lblIcon, new CC().cell(0, 0, 1, 3).alignX("center").alignY("top").grow(0, 0)); //NON-NLS + + lblThema.setFont(lblThema.getFont().deriveFont(lblThema.getFont().getStyle() | Font.BOLD)); + add(lblThema, new CC().cell(1, 0)); + + lblTitel.setFont(lblTitel.getFont().deriveFont(lblTitel.getFont().getStyle() | Font.BOLD)); + add(lblTitel, new CC().cell(1, 1)); + + scrollPane1.setPreferredSize(new Dimension(299, 75)); + scrollPane1.setMaximumSize(new Dimension(Integer.MAX_VALUE, 75)); + scrollPane1.setMinimumSize(new Dimension(23, 75)); + scrollPane1.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + + textArea.setEditable(false); + textArea.setWrapStyleWord(true); + textArea.setLineWrap(true); + scrollPane1.setViewportView(textArea); + add(scrollPane1, new CC().cell(1, 2).grow()); + + //add(hyperlink, new CC().cell(0, 3, 2, 1)); + add(hyperlink, new CC().cell(1, 3)); + } + + /** + * Determine the image's size while keeping aspect ratio within a given boundary box. + * @param imgSize The size of the original image. + * @param boundary The bounds where the image needs to fit into. + * @return The calculated image dimensions for fitting into boundary. + */ + public Dimension getScaledDimension(Dimension imgSize, Dimension boundary) { + + int original_width = imgSize.width; + int original_height = imgSize.height; + int bound_width = boundary.width; + int bound_height = boundary.height; + int new_width = original_width; + int new_height = original_height; + + // first check if we need to scale width + if (original_width > bound_width) { + //scale width to fit + new_width = bound_width; + //scale height to maintain aspect ratio + new_height = (new_width * original_height) / original_width; + } + + // then check if we need to scale even with the new height + if (new_height > bound_height) { + //scale height to fit instead + new_height = bound_height; + //scale width to maintain aspect ratio + new_width = (new_height * original_width) / original_height; + } + + return new Dimension(new_width, new_height); + } + + private Image getScaledImage(Image srcImg, int w, int h) { + BufferedImage resizedImg = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); + Graphics2D g2 = resizedImg.createGraphics(); + + g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g2.drawImage(srcImg, 0, 0, w, h, null); + g2.dispose(); + + return resizedImg; + } + + public void install(@NotNull JTabbedPane tabbedPane, @NotNull JTable tabelle) { + tabbedPane.add("Beschreibung", this); + + tabelle.getSelectionModel().addListSelectionListener(e -> + currentTab.getCurrentlySelectedFilm().ifPresentOrElse(film -> { + showFilmDescription(film); + currentFilm = film; + }, () -> { + setAllFieldsEmpty(); + currentFilm = null; + })); + } + + private void setAllFieldsEmpty() { + hyperlink.setVisible(false); + hyperlink.setText(""); + hyperlink.setToolTipText(""); + + textArea.setText(""); + lblIcon.setIcon(null); + lblThema.setText(""); + lblTitel.setText(""); + } + + private void showFilmDescription(@NotNull DatenFilm film) { + lblThema.setText(film.getThema()); + lblTitel.setText(film.getTitle()); + + hyperlink.setVisible(true); + try { + hyperlink.setURI(new URL(film.getWebsiteLink()).toURI()); + hyperlink.setText("Link zur Webseite"); + hyperlink.setClicked(false); + } + catch (Exception e) { + //logger + hyperlink.setText("Link nicht verfügbar"); + } + hyperlink.setToolTipText(film.getWebsiteLink()); + + textArea.setText(film.getDescription()); + SwingUtilities.invokeLater(() -> scrollPane1.getVerticalScrollBar().setValue(0)); + MVSenderIconCache.get(film.getSender()).ifPresentOrElse(icon -> { + var imageDim = new Dimension(icon.getIconWidth(), icon.getIconHeight()); + var boundary = new Dimension(96, 96); + var destDim = getScaledDimension(imageDim, boundary); + var newIcon = getScaledImage(icon.getImage(), destDim.width, destDim.height); + lblIcon.setIcon(new ImageIcon(newIcon)); + }, () -> lblIcon.setIcon(null)); + } +} diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index 3d0226c91c..05d5048c37 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -70,6 +70,7 @@ import java.util.List; import java.util.*; import java.util.function.Consumer; +import java.util.function.IntConsumer; public class GuiFilme extends AGuiTabPanel { @@ -97,7 +98,6 @@ public class GuiFilme extends AGuiTabPanel { private final JPanel extensionArea = new JPanel(); private final JCheckBoxMenuItem cbkShowDescription = new JCheckBoxMenuItem("Beschreibung anzeigen"); - private final JFXPanel fxDescriptionPanel = new JFXPanel(); private final JFXPanel fxPsetButtonsPanel = new JFXPanel(); private final SeenHistoryController historyController = new SeenHistoryController(); private final JToolBar toolBar = new JToolBar(); @@ -108,7 +108,6 @@ public class GuiFilme extends AGuiTabPanel { public ToggleFilterDialogVisibilityAction toggleFilterDialogVisibilityAction = new ToggleFilterDialogVisibilityAction(); protected SearchField searchField = new SearchField(); protected JComboBox filterSelectionComboBox = new JComboBox<>(new FilterSelectionComboBoxModel()); - protected PauseTransition reloadTableDataTransition = new PauseTransition(Duration.millis(250d)); protected FilterVisibilityToggleButton btnToggleFilterDialogVisibility = new FilterVisibilityToggleButton(toggleFilterDialogVisibilityAction); private Optional bookmarkWindowController = Optional.empty(); @@ -137,14 +136,12 @@ public GuiFilme(Daten aDaten, MediathekGui mediathekGui) { createToolBar(); // add film description panel - extensionArea.add(fxDescriptionPanel); + extensionArea.add(descriptionTab); extensionArea.add(fxPsetButtonsPanel); setupFilmListTable(); - setupFilmSelectionPropertyListener(mediathekGui); - - setupDescriptionPanel(fxDescriptionPanel, tabelle); + setupDescriptionTab(); setupPsetButtonsPanel(); setupFilmActionPanel(); @@ -155,6 +152,20 @@ public GuiFilme(Daten aDaten, MediathekGui mediathekGui) { setupActionListeners(); } + private void setupDescriptionTab() { + descriptionPanel = new FilmDescriptionPanel(this); + descriptionPanel.install(descriptionTab, tabelle); + descriptionTab.putClientProperty("JTabbedPane.tabClosable", true); + descriptionTab.putClientProperty("JTabbedPane.tabCloseCallback", + (IntConsumer) tabIndex -> { + // close description tab here + // must use doClick to trigger model change + cbkShowDescription.doClick(); + }); + setupShowFilmDescriptionMenuItem(); + initDescriptionTabVisibility(ApplicationConfiguration.FILM_SHOW_DESCRIPTION); + } + private void createToolBar() { add(toolBar, BorderLayout.NORTH); @@ -165,7 +176,7 @@ private void createToolBar() { toolBar.add(saveFilmAction); toolBar.add(bookmarkFilmAction); toolBar.addSeparator(); - filterSelectionComboBox.setMaximumSize(new Dimension(150,100)); + filterSelectionComboBox.setMaximumSize(new Dimension(150, 100)); toolBar.add(filterSelectionComboBox); toolBar.addSeparator(); toolBar.add(new JLabel("Suche:")); @@ -186,8 +197,7 @@ public void handleTableModelChange(TableModelChangeEvent e) { searchField.setEnabled(false); filterSelectionComboBox.setEnabled(false); }); - } - else { + } else { SwingUtilities.invokeLater(() -> { playFilmAction.setEnabled(true); saveFilmAction.setEnabled(true); @@ -259,9 +269,6 @@ public void installViewMenuEntry(JMenu jMenuAnsicht) { @Override public void installMenuEntries(JMenu menu) { - if (!SystemUtils.IS_OS_MAC_OSX) - cbkShowDescription.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F10, 0)); - JMenuItem miMarkFilmAsSeen = new JMenuItem("Filme als gesehen markieren"); miMarkFilmAsSeen.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_G, KeyEvent.CTRL_DOWN_MASK)); miMarkFilmAsSeen.addActionListener(markFilmAsSeenAction); @@ -291,7 +298,7 @@ private void setupFilmActionPanel() { private void setupPsetButtonsPanel() { JavaFxUtils.invokeInFxThreadAndWait(() -> { try { - psetController = ButtonsPanelController.install(fxPsetButtonsPanel,this); + psetController = ButtonsPanelController.install(fxPsetButtonsPanel, this); psetController.setOnCloseRequest(e -> { MessageBus.getMessageBus().publishAsync(new ButtonsPanelVisibilityChangedEvent(false)); e.consume(); @@ -319,14 +326,6 @@ public void componentHidden(ComponentEvent e) { fxPsetButtonsPanel.setVisible(visible); } - /** - * Show description panel based on settings. - */ - private void showDescriptionPanel() { - fxDescriptionPanel.setVisible(ApplicationConfiguration.getConfiguration() - .getBoolean(ApplicationConfiguration.FILM_SHOW_DESCRIPTION, true)); - } - private void onComponentShown() { mediathekGui.tabPaneIndexProperty().setValue(TabPaneIndex.FILME); @@ -367,14 +366,13 @@ private void setupKeyMapping() { } private void setupCellRenderer() { - final CellRendererFilme cellRenderer = new CellRendererFilme(); + CellRendererFilme cellRenderer = new CellRendererFilme(); tabelle.setDefaultRenderer(Object.class, cellRenderer); tabelle.setDefaultRenderer(DatumFilm.class, cellRenderer); tabelle.setDefaultRenderer(Integer.class, cellRenderer); } private void start_init() { - showDescriptionPanel(); daten.getFilmeLaden().addAdListener(new ListenerFilmeLaden() { @Override public void fertig(ListenerFilmeLadenEvent event) { @@ -605,7 +603,7 @@ private Optional getFilm(final int zeileTabelle) { } @Override - protected Optional getCurrentlySelectedFilm() { + public Optional getCurrentlySelectedFilm() { final int selectedTableRow = tabelle.getSelectedRow(); if (selectedTableRow != -1) { final int modelIndex = tabelle.convertRowIndexToModel(selectedTableRow); @@ -683,33 +681,22 @@ private void setupActionListeners() { } }); }); - - setupShowFilmDescriptionMenuItem(); } /** * Setup and show film description panel. Most of the setup is done in {@link GuiFilme} function. * Here we just display the panel */ - private void setupShowFilmDescriptionMenuItem() { - cbkShowDescription.setSelected( - ApplicationConfiguration.getConfiguration() - .getBoolean(ApplicationConfiguration.FILM_SHOW_DESCRIPTION, true)); - cbkShowDescription.addActionListener( - l -> fxDescriptionPanel.setVisible(cbkShowDescription.isSelected())); - cbkShowDescription.addItemListener(e -> ApplicationConfiguration.getConfiguration().setProperty( - ApplicationConfiguration.FILM_SHOW_DESCRIPTION, - cbkShowDescription.isSelected())); - fxDescriptionPanel.addComponentListener(new ComponentAdapter() { - @Override - public void componentShown(ComponentEvent e) { - cbkShowDescription.setSelected(true); - } + @Override + protected void setupShowFilmDescriptionMenuItem() { + var config = ApplicationConfiguration.getConfiguration(); - @Override - public void componentHidden(ComponentEvent e) { - cbkShowDescription.setSelected(false); - } + cbkShowDescription.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F10, 0)); + cbkShowDescription.setSelected(config.getBoolean(ApplicationConfiguration.FILM_SHOW_DESCRIPTION, true)); + cbkShowDescription.addActionListener(l -> { + boolean visible = cbkShowDescription.isSelected(); + makeDescriptionTabVisible(visible); + config.setProperty(ApplicationConfiguration.FILM_SHOW_DESCRIPTION, visible); }); } @@ -745,10 +732,10 @@ private void loadTable() { var decoratedPool = daten.getDecoratedPool(); modelFuture = decoratedPool.submit(() -> { - final GuiFilmeModelHelper helper = new GuiFilmeModelHelper(filmActionPanel, historyController, searchField); - //Thread.sleep(5000); - return helper.getFilteredTableModel(); - }); + final GuiFilmeModelHelper helper = new GuiFilmeModelHelper(filmActionPanel, historyController, searchField); + //Thread.sleep(5000); + return helper.getFilteredTableModel(); + }); Futures.addCallback(modelFuture, new FutureCallback<>() { public void onSuccess(TableModel model) { @@ -812,7 +799,7 @@ public class SearchField extends JTextField { private static final String SEARCHMODE_PROPERTY_STRING = "searchMode"; private final SearchHistoryButton searchHistoryButton = new SearchHistoryButton(); private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); - private final Color DEFAULT_FOREGROUND_COLOR = (Color)UIManager.get("TextField.foreground"); + private final Color DEFAULT_FOREGROUND_COLOR = (Color) UIManager.get("TextField.foreground"); private FXSearchControlFieldMode searchMode; public SearchField() { @@ -846,7 +833,7 @@ public SearchField() { createTrailingComponents(); - putClientProperty( FlatClientProperties.TEXT_FIELD_LEADING_COMPONENT, searchHistoryButton ); + putClientProperty(FlatClientProperties.TEXT_FIELD_LEADING_COMPONENT, searchHistoryButton); putClientProperty("JTextField.clearCallback", (Consumer) textField -> clearSearchField()); } @@ -891,9 +878,11 @@ private void setForegroundTextColor(String text) { else setForeground(DEFAULT_FOREGROUND_COLOR); } + private boolean isPatternValid(String text) { return Filter.makePatternNoCache(text) != null; } + private void checkPatternValidity(String text) { if (Filter.isPattern(text)) showErrorIndication(!isPatternValid(text)); @@ -922,7 +911,7 @@ private void setupHelperTexts() { text = "Thema/Titel"; else text = "Thema/Titel/Beschreibung"; - putClientProperty( FlatClientProperties.PLACEHOLDER_TEXT, text); + putClientProperty(FlatClientProperties.PLACEHOLDER_TEXT, text); setToolTipText(text + " durchsuchen"); } @@ -993,7 +982,7 @@ public class SearchHistoryButton extends JButton { private final JMenuItem miClearHistory = new JMenuItem("Historie löschen"); public SearchHistoryButton() { - super(new FlatSearchWithHistoryIcon( true )); + super(new FlatSearchWithHistoryIcon(true)); setToolTipText("Vorherige Suchen"); miClearHistory.addActionListener(l -> { @@ -1014,7 +1003,7 @@ public SearchHistoryButton() { popupMenu.add(historyItem); } } - popupMenu.show( this, 0, this.getHeight() ); + popupMenu.show(this, 0, this.getHeight()); }); loadHistory(); @@ -1034,14 +1023,14 @@ private void loadHistory() { ObjectMapper mapper = new ObjectMapper(); var json = ApplicationConfiguration.getConfiguration().getString(SEARCH_HISTORY_CONFIG, ""); if (!json.isEmpty()) { - List entries = mapper.readValue(json, new TypeReference<>() {}); + List entries = mapper.readValue(json, new TypeReference<>() { + }); if (!entries.isEmpty()) { historyList.addAll(entries); Collections.sort(historyList); } } - } - catch (JsonProcessingException ex) { + } catch (JsonProcessingException ex) { logger.error("Failed to load search history", ex); } } @@ -1060,14 +1049,13 @@ private void saveHistory() { public class SaveFilmAction extends AbstractAction { public SaveFilmAction() { - putValue(Action.SHORT_DESCRIPTION,"Film downloaden"); + putValue(Action.SHORT_DESCRIPTION, "Film downloaden"); putValue(Action.NAME, "Film downloaden"); putValue(Action.SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/download.svg")); KeyStroke keyStroke; if (SystemUtils.IS_OS_MAC_OSX) { keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_F7, GuiFunktionen.getPlatformControlKey()); - } - else + } else keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_D, GuiFunktionen.getPlatformControlKey()); putValue(Action.ACCELERATOR_KEY, keyStroke); } @@ -1265,8 +1253,7 @@ private void showMenu(MouseEvent evt) { // update Bookmark state if (film.isLivestream()) { jPopupMenu.remove(miBookmark); - } - else { + } else { miBookmark.setText(film.isBookmarked() ? "Film aus Merkliste entfernen" : "Film merken"); } }); @@ -1333,8 +1320,7 @@ private void showMenu(MouseEvent evt) { jPopupMenu.show(evt.getComponent(), evt.getX(), evt.getY()); } - private void setupHistoryContextActions(@NotNull JPopupMenu popupMenu, @NotNull DatenFilm film) - { + private void setupHistoryContextActions(@NotNull JPopupMenu popupMenu, @NotNull DatenFilm film) { if (!film.isLivestream()) { JMenuItem miHistory; try (var history = new SeenHistoryController()) { diff --git a/src/main/java/mediathek/javafx/descriptionPanel/DescriptionPanelController.java b/src/main/java/mediathek/javafx/descriptionPanel/DescriptionPanelController.java deleted file mode 100644 index 3635e479fe..0000000000 --- a/src/main/java/mediathek/javafx/descriptionPanel/DescriptionPanelController.java +++ /dev/null @@ -1,177 +0,0 @@ -package mediathek.javafx.descriptionPanel; - -import javafx.embed.swing.JFXPanel; -import javafx.event.Event; -import javafx.event.EventHandler; -import javafx.fxml.FXML; -import javafx.fxml.FXMLLoader; -import javafx.scene.Scene; -import javafx.scene.control.*; -import javafx.scene.text.*; -import mediathek.config.Konstanten; -import mediathek.daten.DatenFilm; -import mediathek.gui.actions.UrlHyperlinkAction; -import mediathek.gui.dialog.DialogFilmBeschreibung; -import mediathek.mainwindow.MediathekGui; -import mediathek.tool.GuiFunktionen; -import org.apache.commons.lang3.SystemUtils; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.jetbrains.annotations.NotNull; - -import javax.swing.*; -import java.io.IOException; -import java.net.URISyntaxException; -import java.util.Optional; - -/** - * The controller for the film description panel - */ -public class DescriptionPanelController { - private static final Logger logger = LogManager.getLogger(DescriptionPanelController.class); - private static final String SPACER = " - "; - @FXML - private Hyperlink websiteLink; - @FXML - private TextFlow textField; - @FXML - private Tab descriptionTab; - @FXML - private ScrollPane scrollPane; - private DatenFilm currentFilm; - private ContextMenu contextMenu; - - public static DescriptionPanelController install(JFXPanel fxDescriptionPanel) throws IOException { - FXMLLoader loader = new FXMLLoader(); - loader.setLocation(Konstanten.FXML_FILM_DESCRIPTION_PANEL_URL); - - TabPane descriptionPane = loader.load(); - final DescriptionPanelController descriptionPanelController = loader.getController(); - descriptionPanelController.setOnCloseRequest(e -> { - SwingUtilities.invokeLater(() -> fxDescriptionPanel.setVisible(false)); - e.consume(); - }); - - fxDescriptionPanel.setScene(new Scene(descriptionPane)); - return descriptionPanelController; - } - - public static String getStringFromTextFlow(TextFlow tf) { - StringBuilder sb = new StringBuilder(); - tf.getChildren().stream() - .filter(t -> Text.class.equals(t.getClass())) - .forEach(t -> sb.append(((Text) t).getText())); - return sb.toString(); - } - - private void setupWebsiteLink() { - websiteLink.setOnAction(e -> { - final var link = currentFilm.getWebsiteLink(); - SwingUtilities.invokeLater(() -> { - try { - UrlHyperlinkAction.openURL(MediathekGui.ui(), link); - } catch (URISyntaxException ex) { - logger.error("Failed to launch web browser for URL: {}", link); - } - }); - }); - - // create context menu - var contextMenu = new ContextMenu(); - var mi = new MenuItem("URL kopieren"); - mi.setOnAction(e -> SwingUtilities.invokeLater(() -> GuiFunktionen.copyToClipboard(currentFilm.getWebsiteLink()))); - contextMenu.getItems().add(mi); - websiteLink.setContextMenu(contextMenu); - } - - private Font getFont() { - /* - Thank you Oracle for not fixing the bold font handling for years in JavaFX... - As of version 16 bold font handling ist STILL BROKEN on macOS. - So we need a little workaround which will not die before 2027 based on bug fix rate - of non-paying customers... - */ - Font defaultFont; - if (SystemUtils.IS_OS_MAC_OSX) { - defaultFont = Font.font("Arial", 14d); - } - else - defaultFont = Font.getDefault(); - - return defaultFont; - } - - public void showFilmDescription(@NotNull Optional optFilm) { - textField.getChildren().clear(); - - optFilm.ifPresentOrElse(film -> { - currentFilm = film; - websiteLink.setVisible(true); - websiteLink.setVisited(false); - websiteLink.setTooltip(new Tooltip(film.getWebsiteLink())); - - Font defaultFont = getFont(); - Text headLine = new Text((film.getSender().isEmpty() ? "" : film.getSender() + SPACER) + film.getThema() + SPACER + film.getTitle()); - headLine.setFont(Font.font(defaultFont.getName(), FontWeight.BOLD, FontPosture.REGULAR, defaultFont.getSize())); - - Text description = new Text(film.getDescription()); - description.setFont(Font.font(defaultFont.getName(), FontWeight.NORMAL, FontPosture.REGULAR, defaultFont.getSize())); - - textField.getChildren().addAll(headLine, - new Text("\n"), - new Text("\n"), - description); - }, () -> { - websiteLink.setTooltip(null); - websiteLink.setVisible(false); - currentFilm = null; - }); - } - - private ContextMenu createContextMenu() { - ContextMenu contextMenu = new ContextMenu(); - - MenuItem edit = new MenuItem("Beschreibung ändern"); - edit.setOnAction(e -> SwingUtilities.invokeLater(() -> { - DialogFilmBeschreibung dialog = new DialogFilmBeschreibung(MediathekGui.ui(), currentFilm); - dialog.setVisible(true); - })); - - MenuItem copyToClipboard = new MenuItem("In Zwischenablage kopieren"); - copyToClipboard.setOnAction(e -> { - var text = getStringFromTextFlow(textField); - GuiFunktionen.copyToClipboard(text); - }); - - var items = contextMenu.getItems(); - items.add(edit); - items.add(new SeparatorMenuItem()); - items.add(copyToClipboard); - - contextMenu.setOnShowing(e -> { - //if there is no film do not offer edit or copy capability - if (currentFilm == null) { - edit.setDisable(true); - copyToClipboard.setDisable(true); - } - else { - edit.setDisable(false); - copyToClipboard.setDisable(false); - } - }); - - return contextMenu; - } - - public void setOnCloseRequest(EventHandler e) { - descriptionTab.setOnCloseRequest(e); - } - - public void initialize() { - websiteLink.setVisible(false); - setupWebsiteLink(); - - contextMenu = createContextMenu(); - scrollPane.setOnContextMenuRequested(event -> contextMenu.show(textField, event.getScreenX(), event.getScreenY())); - } -} diff --git a/src/main/resources/mediathek/res/programm/fxml/filmdescription.fxml b/src/main/resources/mediathek/res/programm/fxml/filmdescription.fxml deleted file mode 100644 index 33003beb68..0000000000 --- a/src/main/resources/mediathek/res/programm/fxml/filmdescription.fxml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - From bda743e21d10c42b2435d9224d08978d046a529e Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 6 Jul 2022 23:10:21 +0200 Subject: [PATCH 132/422] delete incorrect javadoc Former-commit-id: 3786f4fce25495a3facd6ad8ff77c1b03aeead8f --- src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index 05d5048c37..80fd1b71ec 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -683,10 +683,6 @@ private void setupActionListeners() { }); } - /** - * Setup and show film description panel. Most of the setup is done in {@link GuiFilme} function. - * Here we just display the panel - */ @Override protected void setupShowFilmDescriptionMenuItem() { var config = ApplicationConfiguration.getConfiguration(); From ceb81dbf0bd81e2d1a1b3ba7f5c7a4ada13ec0c6 Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 6 Jul 2022 23:26:11 +0200 Subject: [PATCH 133/422] - consolidate common code Former-commit-id: 7bc17f289ee3cff6ad57ca2133573ada5f5f1c31 --- .../java/mediathek/gui/tabs/AGuiTabPanel.java | 16 ++++++++++++++ .../gui/tabs/tab_downloads/GuiDownloads.java | 22 +++---------------- .../mediathek/gui/tabs/tab_film/GuiFilme.java | 18 ++------------- 3 files changed, 21 insertions(+), 35 deletions(-) diff --git a/src/main/java/mediathek/gui/tabs/AGuiTabPanel.java b/src/main/java/mediathek/gui/tabs/AGuiTabPanel.java index 764d886b49..fa33c491c6 100644 --- a/src/main/java/mediathek/gui/tabs/AGuiTabPanel.java +++ b/src/main/java/mediathek/gui/tabs/AGuiTabPanel.java @@ -14,6 +14,7 @@ import java.awt.event.ActionEvent; import java.util.List; import java.util.Optional; +import java.util.function.IntConsumer; public abstract class AGuiTabPanel extends JPanel { protected Daten daten; @@ -37,6 +38,21 @@ protected void makeDescriptionTabVisible(boolean visible) { } } + protected void setupDescriptionTab(@NotNull JTable table, @NotNull JCheckBoxMenuItem cbmi, + @NotNull String configKey) { + descriptionPanel.install(descriptionTab, table); + descriptionTab.putClientProperty("JTabbedPane.tabClosable", true); + descriptionTab.putClientProperty("JTabbedPane.tabCloseCallback", + (IntConsumer) tabIndex -> { + // close description tab here + // must use doClick to trigger model change + cbmi.doClick(); + }); + + setupShowFilmDescriptionMenuItem(); + initDescriptionTabVisibility(configKey); + } + protected abstract void setupShowFilmDescriptionMenuItem(); protected void initDescriptionTabVisibility(@NotNull String configKey) { diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java index 4f401a80a9..e7fb05e36d 100644 --- a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java @@ -55,7 +55,6 @@ import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; -import java.util.function.IntConsumer; public class GuiDownloads extends AGuiTabPanel { public static final String NAME = "Downloads"; @@ -129,8 +128,6 @@ public class GuiDownloads extends AGuiTabPanel { */ private TModelDownload model; private MVDownloadsTable tabelle; - // Variables declaration - do not modify//GEN-BEGIN:variables - // Generated using JFormDesigner non-commercial license private JSplitPane jSplitPane1; private JPanel jPanelFilterExtern; private JComboBox cbDisplayCategories; @@ -145,6 +142,8 @@ public GuiDownloads(Daten aDaten, MediathekGui mediathekGui) { super(); daten = aDaten; this.mediathekGui = mediathekGui; + descriptionPanel = new FilmDescriptionPanel(this); + initComponents(); setupDownloadListStatusBar(); @@ -153,7 +152,7 @@ public GuiDownloads(Daten aDaten, MediathekGui mediathekGui) { setupDownloadListTable(); - setupDescriptionTab(); + setupDescriptionTab(tabelle, cbShowDownloadDescription, ApplicationConfiguration.DOWNLOAD_SHOW_DESCRIPTION); init(); @@ -176,21 +175,6 @@ public GuiDownloads(Daten aDaten, MediathekGui mediathekGui) { setupTaskbarMenu(); } - protected void setupDescriptionTab() { - descriptionPanel = new FilmDescriptionPanel(this); - descriptionPanel.install(descriptionTab, tabelle); - descriptionTab.putClientProperty("JTabbedPane.tabClosable", true); - descriptionTab.putClientProperty("JTabbedPane.tabCloseCallback", - (IntConsumer) tabIndex -> { - // close description tab here - // must use doClick to trigger model change - cbShowDownloadDescription.doClick(); - }); - - setupShowFilmDescriptionMenuItem(); - initDescriptionTabVisibility(ApplicationConfiguration.DOWNLOAD_SHOW_DESCRIPTION); - } - private void setupDownloadListStatusBar() { statusBar.add(totalDownloadsLabel); statusBar.add(lblAbos); diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index 80fd1b71ec..3e2a11307e 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -70,7 +70,6 @@ import java.util.List; import java.util.*; import java.util.function.Consumer; -import java.util.function.IntConsumer; public class GuiFilme extends AGuiTabPanel { @@ -128,6 +127,7 @@ public GuiFilme(Daten aDaten, MediathekGui mediathekGui) { super(); daten = aDaten; this.mediathekGui = mediathekGui; + descriptionPanel = new FilmDescriptionPanel(this); setLayout(new BorderLayout()); @@ -141,7 +141,7 @@ public GuiFilme(Daten aDaten, MediathekGui mediathekGui) { setupFilmListTable(); setupFilmSelectionPropertyListener(mediathekGui); - setupDescriptionTab(); + setupDescriptionTab(tabelle, cbkShowDescription, ApplicationConfiguration.FILM_SHOW_DESCRIPTION); setupPsetButtonsPanel(); setupFilmActionPanel(); @@ -152,20 +152,6 @@ public GuiFilme(Daten aDaten, MediathekGui mediathekGui) { setupActionListeners(); } - private void setupDescriptionTab() { - descriptionPanel = new FilmDescriptionPanel(this); - descriptionPanel.install(descriptionTab, tabelle); - descriptionTab.putClientProperty("JTabbedPane.tabClosable", true); - descriptionTab.putClientProperty("JTabbedPane.tabCloseCallback", - (IntConsumer) tabIndex -> { - // close description tab here - // must use doClick to trigger model change - cbkShowDescription.doClick(); - }); - setupShowFilmDescriptionMenuItem(); - initDescriptionTabVisibility(ApplicationConfiguration.FILM_SHOW_DESCRIPTION); - } - private void createToolBar() { add(toolBar, BorderLayout.NORTH); From bfe1fbed252b5b42a30b0af3a20c537baff75fb4 Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 6 Jul 2022 23:42:20 +0200 Subject: [PATCH 134/422] - code cleanup Former-commit-id: 89d6563be8e8c3bc334220c6702d18e3d7774ae9 --- .../mediathek/gui/tabs/tab_downloads/GuiDownloads.java | 8 ++++---- src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java index e7fb05e36d..2c554bb996 100644 --- a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java @@ -1057,17 +1057,17 @@ public void stopAllWaitingDownloads() { private void updateFilmData() { if (isShowing()) { - DatenFilm aktFilm = null; + DatenFilm selectedFilm = null; final int selectedTableRow = tabelle.getSelectedRow(); - if (selectedTableRow >= 0) { + if (selectedTableRow != -1) { final DatenDownload datenDownload = (DatenDownload) tabelle.getModel().getValueAt(tabelle.convertRowIndexToModel(selectedTableRow), DatenDownload.DOWNLOAD_REF); if (datenDownload != null) { - aktFilm = datenDownload.film; + selectedFilm = datenDownload.film; } } var infoDialog = mediathekGui.getFilmInfoDialog(); if (infoDialog != null) { - infoDialog.updateCurrentFilm(aktFilm); + infoDialog.updateCurrentFilm(selectedFilm); } } } diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index 3e2a11307e..b710a9c215 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -622,7 +622,7 @@ private void updateFilmData() { var infoDialog = mediathekGui.getFilmInfoDialog(); if (infoDialog != null) { final Optional filmSelection = getCurrentlySelectedFilm(); - filmSelection.ifPresent(mediathekGui.getFilmInfoDialog()::updateCurrentFilm); + filmSelection.ifPresent(infoDialog::updateCurrentFilm); } } From 487f2de7ea45f114c52a6a9677a3b7697afe62f5 Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 6 Jul 2022 23:57:31 +0200 Subject: [PATCH 135/422] - code cleanup Former-commit-id: 4176e82c60ced26ffa1c162da43638962510543d --- .../java/mediathek/gui/tabs/AGuiTabPanel.java | 5 +++++ .../gui/tabs/tab_downloads/GuiDownloads.java | 10 ++++------ .../mediathek/gui/tabs/tab_film/GuiFilme.java | 16 +++++----------- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/main/java/mediathek/gui/tabs/AGuiTabPanel.java b/src/main/java/mediathek/gui/tabs/AGuiTabPanel.java index fa33c491c6..e68aade68b 100644 --- a/src/main/java/mediathek/gui/tabs/AGuiTabPanel.java +++ b/src/main/java/mediathek/gui/tabs/AGuiTabPanel.java @@ -53,6 +53,11 @@ protected void setupDescriptionTab(@NotNull JTable table, @NotNull JCheckBoxMenu initDescriptionTabVisibility(configKey); } + protected void updateSelectedListItemsCount(@NotNull JTable table) { + final int sel = table.getSelectedRowCount(); + mediathekGui.selectedListItemsProperty.setSelectedItems(sel); + } + protected abstract void setupShowFilmDescriptionMenuItem(); protected void initDescriptionTabVisibility(@NotNull String configKey) { diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java index 2c554bb996..c1c7dd9100 100644 --- a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java @@ -156,7 +156,7 @@ public GuiDownloads(Daten aDaten, MediathekGui mediathekGui) { init(); - setupFilmSelectionPropertyListener(mediathekGui); + setupFilmSelectionPropertyListener(); initTable(); @@ -210,18 +210,16 @@ public void actionPerformed(ActionEvent e) { /** * Update the property with the current number of selected entries from the JTable. */ - private void setupFilmSelectionPropertyListener(MediathekGui mediathekGui) { + private void setupFilmSelectionPropertyListener() { tabelle.getSelectionModel().addListSelectionListener(e -> { if (!e.getValueIsAdjusting()) { - final int sel = tabelle.getSelectedRowCount(); - mediathekGui.selectedListItemsProperty.setSelectedItems(sel); + updateSelectedListItemsCount(tabelle); } }); addComponentListener(new ComponentAdapter() { @Override public void componentShown(ComponentEvent e) { - final int sel = tabelle.getSelectedRowCount(); - mediathekGui.selectedListItemsProperty.setSelectedItems(sel); + updateSelectedListItemsCount(tabelle); onComponentShown(); } }); diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index b710a9c215..1cd965e17b 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -131,7 +131,7 @@ public GuiFilme(Daten aDaten, MediathekGui mediathekGui) { setLayout(new BorderLayout()); - createFilmListArea(); + add(filmListScrollPane, BorderLayout.CENTER); createExtensionArea(); createToolBar(); @@ -140,7 +140,7 @@ public GuiFilme(Daten aDaten, MediathekGui mediathekGui) { extensionArea.add(fxPsetButtonsPanel); setupFilmListTable(); - setupFilmSelectionPropertyListener(mediathekGui); + setupFilmSelectionPropertyListener(); setupDescriptionTab(tabelle, cbkShowDescription, ApplicationConfiguration.FILM_SHOW_DESCRIPTION); setupPsetButtonsPanel(); setupFilmActionPanel(); @@ -210,28 +210,22 @@ private void setupFilmListTable() { /** * Update the property with the current number of selected entries from the JTable. */ - private void setupFilmSelectionPropertyListener(MediathekGui mediathekGui) { + private void setupFilmSelectionPropertyListener() { tabelle.getSelectionModel().addListSelectionListener(e -> { if (!e.getValueIsAdjusting()) { - final int sel = tabelle.getSelectedRowCount(); - mediathekGui.selectedListItemsProperty.setSelectedItems(sel); + updateSelectedListItemsCount(tabelle); } }); addComponentListener(new ComponentAdapter() { @Override public void componentShown(ComponentEvent e) { - final int sel = tabelle.getSelectedRowCount(); - mediathekGui.selectedListItemsProperty.setSelectedItems(sel); + updateSelectedListItemsCount(tabelle); onComponentShown(); } }); } - private void createFilmListArea() { - add(filmListScrollPane, BorderLayout.CENTER); - } - private void createExtensionArea() { extensionArea.setLayout(new VerticalLayout()); add(extensionArea, BorderLayout.SOUTH); From f01d4530547c87e648c1e9a378033be6f3f847e2 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 7 Jul 2022 07:00:47 +0200 Subject: [PATCH 136/422] - use JVM provided way to open browser - no more custom stuff Former-commit-id: 929652a70323d2080ed0987dd7c7ca196838efee --- .../gui/actions/ShowOnlineHelpAction.java | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/main/java/mediathek/gui/actions/ShowOnlineHelpAction.java b/src/main/java/mediathek/gui/actions/ShowOnlineHelpAction.java index b4811250ee..0d1c9ea79a 100644 --- a/src/main/java/mediathek/gui/actions/ShowOnlineHelpAction.java +++ b/src/main/java/mediathek/gui/actions/ShowOnlineHelpAction.java @@ -1,13 +1,15 @@ package mediathek.gui.actions; -import javafx.application.Platform; import mediathek.config.Konstanten; import mediathek.mainwindow.MediathekGui; import mediathek.tool.SVGIconUtilities; -import mediathek.tool.javafx.FXErrorDialog; +import mediathek.tool.SwingErrorDialog; import javax.swing.*; +import java.awt.*; import java.awt.event.ActionEvent; +import java.io.IOException; +import java.net.URI; import java.net.URISyntaxException; public class ShowOnlineHelpAction extends AbstractAction { @@ -19,13 +21,23 @@ public ShowOnlineHelpAction() { @Override public void actionPerformed(ActionEvent e) { - try { - UrlHyperlinkAction.openURL(MediathekGui.ui(),Konstanten.ADRESSE_ONLINE_HELP); - } catch (URISyntaxException ex) { - Platform.runLater(() -> FXErrorDialog.showErrorDialog("Online-Hilfe", - "Fehler beim Öffnen der Online-Hilfe", - "Es trat ein Fehler beim Öffnen der Online-Hilfe auf.\nSollte dies häufiger auftreten kontaktieren Sie bitte das Entwicklerteam.", - ex)); + if (Desktop.isDesktopSupported()) { + Desktop d = Desktop.getDesktop(); + if (d.isSupported(Desktop.Action.BROWSE)) { + try { + d.browse(new URI(Konstanten.ADRESSE_ONLINE_HELP)); + } catch (IOException | URISyntaxException ex) { + SwingErrorDialog.showExceptionMessage(MediathekGui.ui(), + "Es trat ein Fehler beim Öffnen der Online-Hilfe auf.\nSollte dies häufiger auftreten kontaktieren Sie bitte das Entwicklerteam.", + ex); + } + } + else { + JOptionPane.showMessageDialog(MediathekGui.ui(), + "Ihr Betriebssystem unterstützt das Öffnen des Browsers nicht.
                " + + "Bitte öffnen Sie " + Konstanten.ADRESSE_ONLINE_HELP + " selbst in Ihrem Browser.", + Konstanten.PROGRAMMNAME, JOptionPane.ERROR_MESSAGE); + } } } } From 56ca1e3fa9ca950d7d189a90cb37f2254456ba82 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 7 Jul 2022 11:27:34 +0200 Subject: [PATCH 137/422] - fix icon scaling Former-commit-id: 70523b8b4a3ce3b8a73efd3cd5231b46f14d064e --- .../tabs/tab_film/FilmDescriptionPanel.java | 21 ++++--------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/src/main/java/mediathek/gui/tabs/tab_film/FilmDescriptionPanel.java b/src/main/java/mediathek/gui/tabs/tab_film/FilmDescriptionPanel.java index edf41957c0..18e1c126dc 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/FilmDescriptionPanel.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/FilmDescriptionPanel.java @@ -1,5 +1,6 @@ package mediathek.gui.tabs.tab_film; +import com.formdev.flatlaf.util.ScaledImageIcon; import mediathek.daten.DatenFilm; import mediathek.gui.dialog.DialogFilmBeschreibung; import mediathek.gui.tabs.AGuiTabPanel; @@ -15,7 +16,6 @@ import javax.swing.*; import java.awt.*; -import java.awt.image.BufferedImage; import java.net.URL; public class FilmDescriptionPanel extends JPanel { @@ -113,7 +113,7 @@ private void initComponents() { * @param boundary The bounds where the image needs to fit into. * @return The calculated image dimensions for fitting into boundary. */ - public Dimension getScaledDimension(Dimension imgSize, Dimension boundary) { + public Dimension calculateFittedDimension(Dimension imgSize, Dimension boundary) { int original_width = imgSize.width; int original_height = imgSize.height; @@ -140,18 +140,6 @@ public Dimension getScaledDimension(Dimension imgSize, Dimension boundary) { return new Dimension(new_width, new_height); } - - private Image getScaledImage(Image srcImg, int w, int h) { - BufferedImage resizedImg = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); - Graphics2D g2 = resizedImg.createGraphics(); - - g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); - g2.drawImage(srcImg, 0, 0, w, h, null); - g2.dispose(); - - return resizedImg; - } - public void install(@NotNull JTabbedPane tabbedPane, @NotNull JTable tabelle) { tabbedPane.add("Beschreibung", this); @@ -197,9 +185,8 @@ private void showFilmDescription(@NotNull DatenFilm film) { MVSenderIconCache.get(film.getSender()).ifPresentOrElse(icon -> { var imageDim = new Dimension(icon.getIconWidth(), icon.getIconHeight()); var boundary = new Dimension(96, 96); - var destDim = getScaledDimension(imageDim, boundary); - var newIcon = getScaledImage(icon.getImage(), destDim.width, destDim.height); - lblIcon.setIcon(new ImageIcon(newIcon)); + var destDim = calculateFittedDimension(imageDim, boundary); + lblIcon.setIcon(new ScaledImageIcon(icon, destDim.width, destDim.height)); }, () -> lblIcon.setIcon(null)); } } From abec3a6e3438cb4e908122e3aadc999a6cb667de Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 7 Jul 2022 11:47:26 +0200 Subject: [PATCH 138/422] - fix sender icon scaling Former-commit-id: 99920f049936ef08b27df7d018da634d414a9b83 --- .../tabs/tab_film/FilmDescriptionPanel.java | 35 +----------- .../java/mediathek/tool/GuiFunktionen.java | 37 +++++++++++++ .../tool/cellrenderer/CellRendererBase.java | 55 +++++-------------- 3 files changed, 51 insertions(+), 76 deletions(-) diff --git a/src/main/java/mediathek/gui/tabs/tab_film/FilmDescriptionPanel.java b/src/main/java/mediathek/gui/tabs/tab_film/FilmDescriptionPanel.java index 18e1c126dc..4ec9b776e8 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/FilmDescriptionPanel.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/FilmDescriptionPanel.java @@ -107,39 +107,6 @@ private void initComponents() { add(hyperlink, new CC().cell(1, 3)); } - /** - * Determine the image's size while keeping aspect ratio within a given boundary box. - * @param imgSize The size of the original image. - * @param boundary The bounds where the image needs to fit into. - * @return The calculated image dimensions for fitting into boundary. - */ - public Dimension calculateFittedDimension(Dimension imgSize, Dimension boundary) { - - int original_width = imgSize.width; - int original_height = imgSize.height; - int bound_width = boundary.width; - int bound_height = boundary.height; - int new_width = original_width; - int new_height = original_height; - - // first check if we need to scale width - if (original_width > bound_width) { - //scale width to fit - new_width = bound_width; - //scale height to maintain aspect ratio - new_height = (new_width * original_height) / original_width; - } - - // then check if we need to scale even with the new height - if (new_height > bound_height) { - //scale height to fit instead - new_height = bound_height; - //scale width to maintain aspect ratio - new_width = (new_height * original_width) / original_height; - } - - return new Dimension(new_width, new_height); - } public void install(@NotNull JTabbedPane tabbedPane, @NotNull JTable tabelle) { tabbedPane.add("Beschreibung", this); @@ -185,7 +152,7 @@ private void showFilmDescription(@NotNull DatenFilm film) { MVSenderIconCache.get(film.getSender()).ifPresentOrElse(icon -> { var imageDim = new Dimension(icon.getIconWidth(), icon.getIconHeight()); var boundary = new Dimension(96, 96); - var destDim = calculateFittedDimension(imageDim, boundary); + var destDim = GuiFunktionen.calculateFittedDimension(imageDim, boundary); lblIcon.setIcon(new ScaledImageIcon(icon, destDim.width, destDim.height)); }, () -> lblIcon.setIcon(null)); } diff --git a/src/main/java/mediathek/tool/GuiFunktionen.java b/src/main/java/mediathek/tool/GuiFunktionen.java index d974caf661..64932a0b28 100644 --- a/src/main/java/mediathek/tool/GuiFunktionen.java +++ b/src/main/java/mediathek/tool/GuiFunktionen.java @@ -26,6 +26,41 @@ public class GuiFunktionen { private static final Logger logger = LogManager.getLogger(); + /** + * Determine the image's size while keeping aspect ratio within a given boundary box. + * + * @param imgSize The size of the original image. + * @param boundary The bounds where the image needs to fit into. + * @return The calculated image dimensions for fitting into boundary. + */ + public static Dimension calculateFittedDimension(Dimension imgSize, Dimension boundary) { + + int original_width = imgSize.width; + int original_height = imgSize.height; + int bound_width = boundary.width; + int bound_height = boundary.height; + int new_width = original_width; + int new_height = original_height; + + // first check if we need to scale width + if (original_width > bound_width) { + //scale width to fit + new_width = bound_width; + //scale height to maintain aspect ratio + new_height = (new_width * original_height) / original_width; + } + + // then check if we need to scale even with the new height + if (new_height > bound_height) { + //scale height to fit instead + new_height = bound_height; + //scale width to maintain aspect ratio + new_width = (new_height * original_width) / original_height; + } + + return new Dimension(new_width, new_height); + } + public static void copyToClipboard(String s) { Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(s), null); } @@ -201,6 +236,7 @@ public static int getPlatformControlKey() { /** * Get the the user set filmlist update type. + * * @return MANUAL or AUTOMATIC based on config. Default is AUTOMATIC. */ public static FilmListUpdateType getFilmListUpdateType() { @@ -225,6 +261,7 @@ public static FilmListUpdateType getFilmListUpdateType() { /** * Store filmlist update mode in config. + * * @param type MANUAL or AUTOMATIC mode. */ public static void setFilmListUpdateType(FilmListUpdateType type) { diff --git a/src/main/java/mediathek/tool/cellrenderer/CellRendererBase.java b/src/main/java/mediathek/tool/cellrenderer/CellRendererBase.java index a8399f4d4d..daf3116430 100644 --- a/src/main/java/mediathek/tool/cellrenderer/CellRendererBase.java +++ b/src/main/java/mediathek/tool/cellrenderer/CellRendererBase.java @@ -1,13 +1,14 @@ package mediathek.tool.cellrenderer; import com.formdev.flatlaf.util.ScaledImageIcon; +import mediathek.tool.GuiFunktionen; import mediathek.tool.sender_icon_cache.MVSenderIconCache; -import org.apache.commons.lang3.SystemUtils; import org.jetbrains.annotations.NotNull; import javax.swing.*; import javax.swing.table.DefaultTableCellRenderer; import java.awt.*; +import java.util.concurrent.atomic.AtomicReference; /** * Base class for all cell renderer. @@ -26,32 +27,21 @@ public class CellRendererBase extends DefaultTableCellRenderer { */ protected void setSenderIcon(@NotNull String sender, @NotNull Dimension targetDim) { var key = new SenderCacheKey(sender, targetDim); - var cachedIcon = senderCellIconCache.getOrDefault(key, null); - if (cachedIcon == null) { - //creeate icon and store in cache - var origIcon = MVSenderIconCache.get(sender); - if (origIcon.isPresent()) { - var icon = origIcon.get(); - Dimension iconDim = new Dimension(icon.getIconWidth(), icon.getIconHeight()); - var scaleDim = getScaledDimension(iconDim, targetDim); - Icon imgIcon; - //Of course Windows is again the only OS which sucks at automatic scaling... - if (SystemUtils.IS_OS_WINDOWS) { - imgIcon = new ScaledImageIcon(new ImageIcon(icon.getImage()), scaleDim.width, scaleDim.height); - } else { - Image newimg = icon.getImage().getScaledInstance(scaleDim.width, scaleDim.height, Image.SCALE_SMOOTH); - imgIcon = new ImageIcon(newimg); - } - - cachedIcon = imgIcon; - senderCellIconCache.put(key, cachedIcon); - } + final AtomicReference cachedIcon = new AtomicReference<>(); + cachedIcon.set(senderCellIconCache.getOrDefault(key, null)); + if (cachedIcon.get() == null) { + MVSenderIconCache.get(sender).ifPresentOrElse(icon -> { + var imageDim = new Dimension(icon.getIconWidth(), icon.getIconHeight()); + var destDim = GuiFunktionen.calculateFittedDimension(imageDim, targetDim); + cachedIcon.set(new ScaledImageIcon(icon, destDim.width, destDim.height)); + senderCellIconCache.put(key, cachedIcon.get()); + }, () -> cachedIcon.set(null)); } - if (cachedIcon != null) { + if (cachedIcon.get() != null) { setHorizontalAlignment(SwingConstants.CENTER); setText(""); - setIcon(cachedIcon); + setIcon(cachedIcon.get()); } } @@ -71,23 +61,4 @@ protected Dimension getSenderCellDimension(@NotNull JTable table, int row, int c targetDim.width -= 4; return targetDim; } - - /** - * Calculate the target dimensions based on image size and a boundary. - * - * @param imageSize the size of the original image. - * @param boundary the boundary size. - * @return the target boundary while maintaining aspect ratio. - */ - protected Dimension getScaledDimension(@NotNull Dimension imageSize, @NotNull Dimension boundary) { - - double widthRatio = boundary.getWidth() / imageSize.getWidth(); - double heightRatio = boundary.getHeight() / imageSize.getHeight(); - double ratio = Math.min(widthRatio, heightRatio); - - return new Dimension((int) (imageSize.width * ratio), - (int) (imageSize.height * ratio)); - } - - } From 0ec6736db5e73daf469121cc192541fc5d60e9ea Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 7 Jul 2022 14:43:48 +0200 Subject: [PATCH 139/422] - remove swingx source code and use FlatLaf supported maven repo - use SwingX FlatLaf UI support Former-commit-id: 70f3c4a76845675d995c1d82a1db9e508fdeff55 --- pom.xml | 14 +- .../java/org/jdesktop/beans/AbstractBean.java | 497 -- .../beans/AbstractSerializableBean.java | 104 - .../java/org/jdesktop/beans/package-info.java | 26 - .../jdesktop/swingx/AbstractLayoutManager.kt | 51 - .../jdesktop/swingx/AbstractPatternPanel.java | 443 -- .../org/jdesktop/swingx/AlphaPaintable.java | 58 - .../jdesktop/swingx/BackgroundPaintable.java | 60 - .../swingx/ForwardingRepaintManager.java | 214 - .../org/jdesktop/swingx/HorizontalLayout.java | 98 - .../java/org/jdesktop/swingx/JXBusyLabel.java | 369 -- .../java/org/jdesktop/swingx/JXButton.java | 736 --- .../jdesktop/swingx/JXCollapsiblePane.java | 1143 ----- .../swingx/JXColorSelectionButton.java | 240 - .../java/org/jdesktop/swingx/JXComboBox.java | 885 ---- .../org/jdesktop/swingx/JXDatePicker.java | 1005 ---- .../java/org/jdesktop/swingx/JXDialog.java | 403 -- .../org/jdesktop/swingx/JXEditorPane.java | 869 ---- .../java/org/jdesktop/swingx/JXErrorPane.java | 654 --- .../java/org/jdesktop/swingx/JXFindBar.java | 178 - .../java/org/jdesktop/swingx/JXFindPanel.java | 296 -- .../jdesktop/swingx/JXFormattedTextField.java | 150 - .../java/org/jdesktop/swingx/JXFrame.java | 629 --- .../jdesktop/swingx/JXGradientChooser.java | 787 --- .../java/org/jdesktop/swingx/JXHeader.java | 358 -- .../java/org/jdesktop/swingx/JXHyperlink.java | 342 -- .../java/org/jdesktop/swingx/JXLabel.java | 1318 ----- src/main/java/org/jdesktop/swingx/JXList.java | 1568 ------ .../java/org/jdesktop/swingx/JXMonthView.java | 1889 ------- .../org/jdesktop/swingx/JXMultiSplitPane.java | 590 --- .../jdesktop/swingx/JXMultiThumbSlider.java | 404 -- .../java/org/jdesktop/swingx/JXPanel.java | 693 --- .../org/jdesktop/swingx/JXRadioGroup.java | 338 -- .../java/org/jdesktop/swingx/JXRootPane.java | 458 -- .../org/jdesktop/swingx/JXSearchField.java | 841 ---- .../org/jdesktop/swingx/JXSearchPanel.java | 275 -- .../java/org/jdesktop/swingx/JXStatusBar.java | 318 -- .../java/org/jdesktop/swingx/JXTable.java | 4399 ----------------- .../org/jdesktop/swingx/JXTableHeader.java | 752 --- .../java/org/jdesktop/swingx/JXTaskPane.java | 625 --- .../jdesktop/swingx/JXTaskPaneContainer.java | 169 - .../java/org/jdesktop/swingx/JXTextArea.java | 108 - .../java/org/jdesktop/swingx/JXTextField.java | 152 - .../org/jdesktop/swingx/JXTipOfTheDay.java | 460 -- .../org/jdesktop/swingx/JXTitledPanel.java | 301 -- .../jdesktop/swingx/JXTitledSeparator.java | 410 -- src/main/java/org/jdesktop/swingx/JXTree.java | 1651 ------- .../java/org/jdesktop/swingx/JXTreeTable.java | 3336 ------------- .../org/jdesktop/swingx/Mnemonicable.java | 79 - .../org/jdesktop/swingx/MultiSplitLayout.java | 2211 --------- .../org/jdesktop/swingx/RepaintManagerX.java | 66 - .../jdesktop/swingx/ScrollableSizeHint.java | 146 - .../java/org/jdesktop/swingx/StackLayout.java | 196 - .../org/jdesktop/swingx/SwingXUtilities.java | 603 --- .../swingx/TranslucentRepaintManager.java | 51 - .../org/jdesktop/swingx/VerticalLayout.java | 123 - .../java/org/jdesktop/swingx/WrapLayout.java | 176 - .../swingx/action/AbstractActionExt.java | 443 -- .../swingx/action/ActionContainerFactory.java | 586 --- .../jdesktop/swingx/action/ActionFactory.java | 157 - .../jdesktop/swingx/action/ActionManager.java | 383 -- .../jdesktop/swingx/action/BoundAction.java | 329 -- .../swingx/action/CompositeAction.java | 137 - .../swingx/action/OpenBrowserAction.java | 122 - .../jdesktop/swingx/action/ServerAction.java | 324 -- .../jdesktop/swingx/action/TargetManager.java | 280 -- .../jdesktop/swingx/action/Targetable.java | 64 - .../swingx/action/TargetableAction.java | 140 - .../swingx/action/TargetableSupport.java | 72 - .../ToggleActionPropertyChangeListener.java | 168 - .../jdesktop/swingx/action/package-info.java | 102 - .../AbstractAutoCompleteAdaptor.java | 118 - .../swingx/autocomplete/AutoComplete.java | 225 - .../AutoCompleteComboBoxEditor.java | 122 - .../autocomplete/AutoCompleteDecorator.java | 391 -- .../autocomplete/AutoCompleteDocument.java | 547 -- .../AutoCompleteStyledDocument.java | 168 - .../swingx/autocomplete/ComboBoxAdaptor.java | 117 - .../autocomplete/ComboBoxCellEditor.java | 110 - .../autocomplete/DelegatingDocumentEvent.java | 63 - .../swingx/autocomplete/ListAdaptor.java | 106 - .../autocomplete/ObjectToStringConverter.java | 105 - .../autocomplete/TextComponentAdaptor.java | 80 - .../swingx/autocomplete/package-info.java | 91 - .../workarounds/MacOSXPopupLocationFix.java | 213 - .../workarounds/package-info.java | 26 - .../swingx/border/DropShadowBorder.java | 440 -- .../jdesktop/swingx/border/IconBorder.java | 288 -- .../swingx/border/MatteBorderExt.java | 303 -- .../jdesktop/swingx/border/package-info.java | 25 - .../calendar/AbstractDateSelectionModel.java | 329 -- .../swingx/calendar/CalendarUtils.java | 649 --- .../swingx/calendar/DatePickerFormatter.java | 248 - .../swingx/calendar/DateSelectionModel.java | 374 -- .../jdesktop/swingx/calendar/DateSpan.java | 221 - .../jdesktop/swingx/calendar/DateUtils.java | 405 -- .../swingx/calendar/DaySelectionModel.java | 296 -- .../calendar/DefaultDateSelectionModel.java | 273 - .../calendar/SingleDaySelectionModel.java | 345 -- .../swingx/calendar/package-info.java | 26 - .../color/EyeDropperColorChooserPanel.form | 125 - .../color/EyeDropperColorChooserPanel.java | 289 -- .../swingx/color/GradientPreviewPanel.java | 285 -- .../swingx/color/GradientThumbRenderer.java | 67 - .../swingx/color/GradientTrackRenderer.java | 103 - .../org/jdesktop/swingx/color/colorwell.png | Bin 725 -> 0 bytes .../java/org/jdesktop/swingx/color/mag.png | Bin 1058 -> 0 bytes .../jdesktop/swingx/color/package-info.java | 25 - .../swingx/combobox/EnumComboBoxModel.java | 192 - .../combobox/JXTextFieldComboBoxEditor.java | 60 - .../swingx/combobox/ListComboBoxModel.java | 124 - .../combobox/ListModelComboBoxWrapper.java | 51 - .../swingx/combobox/MapComboBoxModel.java | 133 - .../swingx/combobox/package-info.java | 25 - .../swingx/decorator/AbstractHighlighter.java | 285 -- .../decorator/AlignmentHighlighter.java | 160 - .../swingx/decorator/BorderHighlighter.java | 255 - .../swingx/decorator/ColorHighlighter.java | 267 - .../swingx/decorator/ComponentAdapter.java | 567 --- .../ComponentOrientationHighlighter.java | 93 - .../swingx/decorator/CompoundHighlighter.java | 243 - .../swingx/decorator/EnabledHighlighter.java | 112 - .../swingx/decorator/FontHighlighter.java | 117 - .../swingx/decorator/HighlightPredicate.java | 825 ---- .../swingx/decorator/Highlighter.java | 103 - .../swingx/decorator/HighlighterFactory.java | 292 -- .../swingx/decorator/IconHighlighter.java | 152 - .../swingx/decorator/PainterHighlighter.java | 253 - .../swingx/decorator/PatternPredicate.java | 223 - .../decorator/ResetDTCRColorHighlighter.java | 111 - .../swingx/decorator/SearchPredicate.java | 223 - .../decorator/ShadingColorHighlighter.java | 85 - .../swingx/decorator/ToolTipHighlighter.java | 131 - .../swingx/decorator/package-info.java | 33 - .../org/jdesktop/swingx/error/ErrorEvent.java | 55 - .../org/jdesktop/swingx/error/ErrorInfo.java | 259 - .../org/jdesktop/swingx/error/ErrorLevel.java | 51 - .../jdesktop/swingx/error/ErrorListener.java | 45 - .../jdesktop/swingx/error/ErrorReporter.java | 49 - .../jdesktop/swingx/error/ErrorSupport.java | 89 - .../jdesktop/swingx/error/package-info.java | 25 - .../swingx/event/CompoundFocusListener.java | 174 - .../swingx/event/DateSelectionEvent.java | 100 - .../swingx/event/DateSelectionListener.java | 31 - .../swingx/event/EventListenerMap.java | 136 - .../event/TableColumnModelExtListener.java | 78 - .../event/TreeExpansionBroadcaster.java | 115 - .../swingx/event/WeakEventListenerList.java | 275 -- .../jdesktop/swingx/event/package-info.java | 38 - .../org/jdesktop/swingx/geom/Morphing2D.java | 705 --- .../java/org/jdesktop/swingx/geom/Star2D.java | 340 -- .../jdesktop/swingx/geom/package-info.java | 25 - .../swingx/graphics/BlendComposite.java | 976 ---- .../swingx/graphics/ColorUtilities.java | 263 - .../swingx/graphics/FilterComposite.java | 119 - .../swingx/graphics/Graphics2DFacade.java | 452 -- .../swingx/graphics/ReflectionRenderer.java | 512 -- .../swingx/graphics/ShadowRenderer.java | 429 -- .../swingx/graphics/package-info.java | 25 - .../hyperlink/AbstractHyperlinkAction.java | 144 - .../hyperlink/EditorPaneLinkVisitor.java | 137 - .../swingx/hyperlink/HyperlinkAction.java | 310 -- .../jdesktop/swingx/hyperlink/LinkModel.java | 317 -- .../swingx/hyperlink/LinkModelAction.java | 153 - .../swingx/icon/ColumnControlIcon.java | 84 - .../org/jdesktop/swingx/icon/EmptyIcon.java | 59 - .../org/jdesktop/swingx/icon/PainterIcon.java | 69 - .../jdesktop/swingx/icon/package-info.java | 36 - .../jdesktop/swingx/image/AbstractFilter.java | 97 - .../swingx/image/ColorTintFilter.java | 160 - .../jdesktop/swingx/image/FastBlurFilter.java | 216 - .../swingx/image/GaussianBlurFilter.java | 191 - .../swingx/image/StackBlurFilter.java | 154 - .../jdesktop/swingx/image/package-info.java | 25 - .../multislider/AbstractMultiThumbModel.java | 91 - .../multislider/DefaultMultiThumbModel.java | 116 - .../swingx/multislider/MultiThumbModel.java | 50 - .../jdesktop/swingx/multislider/Thumb.java | 55 - .../swingx/multislider/ThumbDataEvent.java | 57 - .../swingx/multislider/ThumbDataListener.java | 33 - .../swingx/multislider/ThumbListener.java | 29 - .../swingx/multislider/ThumbRenderer.java | 29 - .../swingx/multislider/TrackRenderer.java | 30 - .../swingx/multislider/package-info.java | 25 - .../multisplitpane/DefaultSplitPaneModel.java | 52 - .../swingx/multisplitpane/package-info.java | 24 - .../org/jdesktop/swingx/package-info.java | 139 - .../swingx/painter/AbstractAreaPainter.java | 267 - .../swingx/painter/AbstractLayoutPainter.java | 270 - .../swingx/painter/AbstractPainter.java | 438 -- .../jdesktop/swingx/painter/AlphaPainter.java | 102 - .../jdesktop/swingx/painter/BusyPainter.java | 622 --- .../swingx/painter/CheckerboardPainter.java | 197 - .../swingx/painter/CompoundPainter.java | 389 -- .../jdesktop/swingx/painter/GlossPainter.java | 171 - .../jdesktop/swingx/painter/ImagePainter.java | 400 -- .../jdesktop/swingx/painter/MattePainter.java | 114 - .../org/jdesktop/swingx/painter/Painter.java | 103 - .../jdesktop/swingx/painter/PainterPaint.java | 116 - .../jdesktop/swingx/painter/PainterUtils.java | 39 - .../org/jdesktop/swingx/painter/Painters.java | 40 - .../swingx/painter/PinstripePainter.java | 277 -- .../swingx/painter/RectanglePainter.java | 265 - .../jdesktop/swingx/painter/ShapePainter.java | 211 - .../jdesktop/swingx/painter/TextPainter.java | 208 - .../painter/effects/AbstractAreaEffect.java | 399 -- .../swingx/painter/effects/AreaEffect.java | 41 - .../painter/effects/GlowPathEffect.java | 45 - .../painter/effects/InnerGlowPathEffect.java | 44 - .../effects/InnerShadowPathEffect.java | 41 - .../painter/effects/NeonBorderEffect.java | 193 - .../painter/effects/ShadowPathEffect.java | 42 - .../swingx/painter/effects/package-info.java | 25 - .../jdesktop/swingx/painter/package-info.java | 25 - .../swingx/plaf/AbstractComponentAddon.java | 220 - .../swingx/plaf/AbstractUIChangeHandler.java | 30 - .../swingx/plaf/BuddyLayoutAndBorder.java | 264 - .../swingx/plaf/BuddyTextFieldUI.java | 88 - .../jdesktop/swingx/plaf/BusyLabelAddon.java | 59 - .../org/jdesktop/swingx/plaf/BusyLabelUI.java | 44 - .../swingx/plaf/ColumnControlButtonAddon.java | 50 - .../jdesktop/swingx/plaf/ComponentAddon.java | 55 - .../jdesktop/swingx/plaf/DatePickerAddon.java | 127 - .../jdesktop/swingx/plaf/DatePickerUI.java | 73 - .../jdesktop/swingx/plaf/DefaultsList.java | 142 - .../jdesktop/swingx/plaf/ErrorPaneAddon.java | 62 - .../org/jdesktop/swingx/plaf/ErrorPaneUI.java | 60 - .../org/jdesktop/swingx/plaf/HeaderAddon.java | 81 - .../org/jdesktop/swingx/plaf/HeaderUI.java | 31 - .../jdesktop/swingx/plaf/HyperlinkAddon.java | 47 - .../swingx/plaf/LookAndFeelAddons.java | 533 -- .../swingx/plaf/LookAndFeelUtils.java | 72 - .../jdesktop/swingx/plaf/MonthViewAddon.java | 60 - .../org/jdesktop/swingx/plaf/MonthViewUI.java | 67 - .../swingx/plaf/MultiThumbSliderAddon.java | 44 - .../swingx/plaf/MultiThumbSliderUI.java | 31 - .../swingx/plaf/PainterUIResource.java | 61 - .../swingx/plaf/PromptTextAreaUI.java | 52 - .../swingx/plaf/PromptTextFieldUI.java | 187 - .../jdesktop/swingx/plaf/PromptTextUI.java | 457 -- .../org/jdesktop/swingx/plaf/SafeBorder.java | 95 - .../swingx/plaf/SearchFieldAddon.java | 123 - .../jdesktop/swingx/plaf/SearchFieldUI.java | 482 -- .../jdesktop/swingx/plaf/ShapeUIResource.java | 94 - .../jdesktop/swingx/plaf/StatusBarAddon.java | 85 - .../org/jdesktop/swingx/plaf/StatusBarUI.java | 32 - .../org/jdesktop/swingx/plaf/TableAddon.java | 121 - .../swingx/plaf/TableHeaderAddon.java | 64 - .../jdesktop/swingx/plaf/TaskPaneAddon.java | 211 - .../swingx/plaf/TaskPaneContainerAddon.java | 133 - .../swingx/plaf/TaskPaneContainerUI.java | 32 - .../org/jdesktop/swingx/plaf/TaskPaneUI.java | 45 - .../jdesktop/swingx/plaf/TextUIWrapper.java | 164 - .../swingx/plaf/TipOfTheDayAddon.java | 87 - .../jdesktop/swingx/plaf/TipOfTheDayUI.java | 48 - .../swingx/plaf/TitledPanelAddon.java | 100 - .../jdesktop/swingx/plaf/TitledPanelUI.java | 50 - .../org/jdesktop/swingx/plaf/UIAction.java | 111 - .../swingx/plaf/UIColorHighlighterAddon.java | 131 - .../org/jdesktop/swingx/plaf/UIDependent.java | 36 - .../jdesktop/swingx/plaf/UIManagerExt.java | 721 --- .../org/jdesktop/swingx/plaf/XListAddon.java | 100 - .../swingx/plaf/basic/BasicBusyLabelUI.java | 68 - .../basic/BasicCalendarHeaderHandler.java | 262 - .../basic/BasicCalendarRenderingHandler.java | 346 -- .../swingx/plaf/basic/BasicDatePickerUI.java | 1670 ------- .../swingx/plaf/basic/BasicErrorPaneUI.java | 1098 ---- .../swingx/plaf/basic/BasicHeaderUI.java | 454 -- .../swingx/plaf/basic/BasicHyperlinkUI.java | 859 ---- .../plaf/basic/BasicLookAndFeelAddons.java | 53 - .../swingx/plaf/basic/BasicMonthViewUI.java | 2323 --------- .../plaf/basic/BasicMultiThumbSliderUI.java | 95 - .../swingx/plaf/basic/BasicStatusBarUI.java | 625 --- .../plaf/basic/BasicTaskPaneContainerUI.java | 136 - .../swingx/plaf/basic/BasicTaskPaneUI.java | 874 ---- .../swingx/plaf/basic/BasicTipOfTheDayUI.java | 352 -- .../swingx/plaf/basic/BasicTitledPanelUI.java | 339 -- .../swingx/plaf/basic/CalendarAdapter.java | 127 - .../plaf/basic/CalendarCellContext.java | 214 - .../plaf/basic/CalendarHeaderHandler.java | 312 -- .../plaf/basic/CalendarRenderingHandler.java | 60 - .../swingx/plaf/basic/CalendarState.java | 35 - .../swingx/plaf/basic/CapsLockSupport.java | 142 - .../basic/SpinningCalendarHeaderHandler.java | 512 -- .../plaf/basic/TextCrossingPainter.java | 96 - .../plaf/basic/core/BasicTransferable.java | 283 -- .../swingx/plaf/basic/core/BasicXListUI.java | 3119 ------------ .../basic/core/DragRecognitionSupport.java | 205 - .../swingx/plaf/basic/core/LazyActionMap.java | 165 - .../swingx/plaf/basic/core/ListSortUI.java | 503 -- .../swingx/plaf/basic/package-info.java | 26 - .../basic/resources/DatePicker.properties | 11 - .../basic/resources/DatePicker_cs.properties | 9 - .../basic/resources/DatePicker_da.properties | 12 - .../basic/resources/DatePicker_de.properties | 8 - .../basic/resources/DatePicker_en.properties | 0 .../resources/DatePicker_en_GB.properties | 11 - .../resources/DatePicker_en_US.properties | 8 - .../basic/resources/DatePicker_es.properties | 4 - .../basic/resources/DatePicker_fr.properties | 8 - .../basic/resources/DatePicker_it.properties | 8 - .../basic/resources/DatePicker_nl.properties | 8 - .../resources/DatePicker_pl_PL.properties | 8 - .../resources/DatePicker_pt_BR.properties | 8 - .../basic/resources/DatePicker_sv.properties | 8 - .../plaf/basic/resources/ErrorPane.properties | 9 - .../basic/resources/ErrorPane_cs.properties | 9 - .../basic/resources/ErrorPane_da.properties | 9 - .../basic/resources/ErrorPane_de.properties | 9 - .../basic/resources/ErrorPane_en.properties | 0 .../basic/resources/ErrorPane_es.properties | 10 - .../basic/resources/ErrorPane_fr.properties | 9 - .../basic/resources/ErrorPane_it.properties | 9 - .../basic/resources/ErrorPane_nl.properties | 9 - .../basic/resources/ErrorPane_pl.properties | 9 - .../resources/ErrorPane_pt_BR.properties | 9 - .../basic/resources/ErrorPane_sv.properties | 9 - .../plaf/basic/resources/LoginPane.properties | 26 - .../basic/resources/LoginPane_cs.properties | 26 - .../basic/resources/LoginPane_de.properties | 25 - .../basic/resources/LoginPane_en.properties | 0 .../basic/resources/LoginPane_es.properties | 28 - .../basic/resources/LoginPane_fr.properties | 25 - .../basic/resources/LoginPane_it.properties | 26 - .../basic/resources/LoginPane_nl.properties | 26 - .../basic/resources/LoginPane_pl.properties | 24 - .../resources/LoginPane_pt_BR.properties | 23 - .../basic/resources/LoginPane_sv.properties | 26 - .../basic/resources/SearchField.properties | 4 - .../basic/resources/SearchField_de.properties | 4 - .../basic/resources/SearchField_en.properties | 0 .../basic/resources/TipOfTheDay.properties | 6 - .../plaf/basic/resources/TipOfTheDay24.gif | Bin 742 -> 0 bytes .../basic/resources/TipOfTheDay_cs.properties | 7 - .../basic/resources/TipOfTheDay_de.properties | 6 - .../basic/resources/TipOfTheDay_en.properties | 0 .../basic/resources/TipOfTheDay_es.properties | 6 - .../basic/resources/TipOfTheDay_fr.properties | 6 - .../basic/resources/TipOfTheDay_it.properties | 6 - .../basic/resources/TipOfTheDay_nl.properties | 6 - .../basic/resources/TipOfTheDay_pl.properties | 6 - .../resources/TipOfTheDay_pt_BR.properties | 6 - .../basic/resources/TipOfTheDay_sv.properties | 6 - .../swingx/plaf/basic/resources/clear.gif | Bin 1026 -> 0 bytes .../plaf/basic/resources/clear_pressed.gif | Bin 1032 -> 0 bytes .../plaf/basic/resources/clear_rollover.gif | Bin 1025 -> 0 bytes .../swingx/plaf/basic/resources/error16.png | Bin 3582 -> 0 bytes .../plaf/basic/resources/month-down.png | Bin 3138 -> 0 bytes .../swingx/plaf/basic/resources/month-up.png | Bin 3139 -> 0 bytes .../swingx/plaf/basic/resources/search.gif | Bin 547 -> 0 bytes .../plaf/basic/resources/search_popup.gif | Bin 556 -> 0 bytes .../plaf/basic/resources/swingx.properties | 59 - .../plaf/basic/resources/swingx_cs.properties | 61 - .../plaf/basic/resources/swingx_da.properties | 59 - .../plaf/basic/resources/swingx_de.properties | 60 - .../plaf/basic/resources/swingx_en.properties | 0 .../plaf/basic/resources/swingx_es.properties | 57 - .../plaf/basic/resources/swingx_fr.properties | 60 - .../plaf/basic/resources/swingx_it.properties | 59 - .../plaf/basic/resources/swingx_nl.properties | 59 - .../plaf/basic/resources/swingx_pl.properties | 59 - .../basic/resources/swingx_pt_BR.properties | 59 - .../plaf/basic/resources/swingx_sv.properties | 59 - .../plaf/linux/LinuxLookAndFeelAddons.java | 65 - .../swingx/plaf/linux/package-info.java | 26 - .../swingx/plaf/linux/resources/combo-gtk.png | Bin 2904 -> 0 bytes .../swingx/plaf/macosx/MacOSXErrorPaneUI.java | 215 - .../plaf/macosx/MacOSXLookAndFeelAddons.java | 48 - .../swingx/plaf/macosx/MacOSXStatusBarUI.java | 58 - .../swingx/plaf/macosx/package-info.java | 26 - .../macosx/resources/ErrorPane.properties | 7 - .../macosx/resources/ErrorPane_cs.properties | 7 - .../macosx/resources/ErrorPane_da.properties | 7 - .../macosx/resources/ErrorPane_de.properties | 7 - .../macosx/resources/ErrorPane_es.properties | 3 - .../macosx/resources/ErrorPane_fr.properties | 7 - .../macosx/resources/ErrorPane_it.properties | 7 - .../macosx/resources/ErrorPane_nl.properties | 7 - .../resources/ErrorPane_pt_BR.properties | 7 - .../macosx/resources/ErrorPane_sv.properties | 7 - .../swingx/plaf/macosx/resources/clear.png | Bin 322 -> 0 bytes .../plaf/macosx/resources/clear_pressed.png | Bin 412 -> 0 bytes .../plaf/macosx/resources/clear_rollover.png | Bin 326 -> 0 bytes .../plaf/macosx/resources/combo-osx.png | Bin 2850 -> 0 bytes .../swingx/plaf/macosx/resources/search.png | Bin 331 -> 0 bytes .../plaf/macosx/resources/search_popup.png | Bin 954 -> 0 bytes .../plaf/metal/MetalLookAndFeelAddons.java | 72 - .../swingx/plaf/metal/MetalStatusBarUI.java | 94 - .../swingx/plaf/metal/MetalTaskPaneUI.java | 84 - .../swingx/plaf/metal/package-info.java | 26 - .../plaf/nimbus/NimbusLookAndFeelAddons.java | 38 - .../swingx/plaf/nimbus/NimbusTaskPaneUI.java | 167 - .../swingx/plaf/nimbus/package-info.java | 26 - .../jdesktop/swingx/plaf/package-info.java | 32 - .../windows/WindowsLookAndFeelAddons.java | 94 - .../plaf/windows/WindowsStatusBarUI.java | 101 - .../plaf/windows/WindowsTaskPaneUI.java | 148 - .../plaf/windows/WindowsTipOfTheDayUI.java | 116 - .../swingx/plaf/windows/package-info.java | 26 - .../windows/resources/TipOfTheDay.properties | 2 - .../resources/TipOfTheDay_cs.properties | 3 - .../resources/TipOfTheDay_de.properties | 2 - .../resources/TipOfTheDay_es.properties | 2 - .../resources/TipOfTheDay_fr.properties | 2 - .../resources/TipOfTheDay_it.properties | 2 - .../resources/TipOfTheDay_nl.properties | 2 - .../resources/TipOfTheDay_pl.properties | 2 - .../resources/TipOfTheDay_pt_BR.properties | 2 - .../resources/TipOfTheDay_sv.properties | 2 - .../swingx/plaf/windows/resources/clear.gif | Bin 222 -> 0 bytes .../plaf/windows/resources/clear_pressed.gif | Bin 660 -> 0 bytes .../plaf/windows/resources/clear_rollover.gif | Bin 701 -> 0 bytes .../plaf/windows/resources/combo-xp.png | Bin 2930 -> 0 bytes .../swingx/plaf/windows/resources/search.gif | Bin 609 -> 0 bytes .../plaf/windows/resources/search_popup.gif | Bin 273 -> 0 bytes .../resources/search_popup_pressed.gif | Bin 228 -> 0 bytes .../resources/search_popup_rollover.gif | Bin 363 -> 0 bytes .../plaf/windows/resources/search_pressed.gif | Bin 1161 -> 0 bytes .../windows/resources/search_rollover.gif | Bin 1207 -> 0 bytes .../resources/silver-statusbar-left.png | Bin 223 -> 0 bytes .../resources/silver-statusbar-middle.png | Bin 196 -> 0 bytes .../resources/silver-statusbar-right.png | Bin 370 -> 0 bytes .../plaf/windows/resources/statusbar-left.png | Bin 3200 -> 0 bytes .../windows/resources/statusbar-middle.png | Bin 2864 -> 0 bytes .../windows/resources/statusbar-right.png | Bin 3194 -> 0 bytes .../plaf/windows/resources/tipoftheday.png | Bin 1184 -> 0 bytes .../jdesktop/swingx/prompt/BuddyButton.java | 62 - .../jdesktop/swingx/prompt/BuddySupport.java | 151 - .../jdesktop/swingx/prompt/PromptSupport.java | 327 -- .../swingx/renderer/AbstractRenderer.java | 126 - .../swingx/renderer/BooleanValue.java | 32 - .../jdesktop/swingx/renderer/CellContext.java | 436 -- .../swingx/renderer/CheckBoxProvider.java | 184 - .../swingx/renderer/ComponentProvider.java | 383 -- .../swingx/renderer/DefaultListRenderer.java | 205 - .../swingx/renderer/DefaultTableRenderer.java | 186 - .../swingx/renderer/DefaultTreeRenderer.java | 165 - .../swingx/renderer/DefaultVisuals.java | 264 - .../swingx/renderer/FormatStringValue.java | 93 - .../swingx/renderer/HyperlinkProvider.java | 285 -- .../jdesktop/swingx/renderer/IconAware.java | 33 - .../jdesktop/swingx/renderer/IconValue.java | 80 - .../jdesktop/swingx/renderer/IconValues.java | 85 - .../swingx/renderer/JRendererCheckBox.java | 311 -- .../swingx/renderer/JRendererLabel.java | 299 -- .../swingx/renderer/JRendererPanel.java | 85 - .../swingx/renderer/JXRendererHyperlink.java | 214 - .../swingx/renderer/LabelProvider.java | 121 - .../swingx/renderer/ListCellContext.java | 110 - .../renderer/LocalizableStringValue.java | 119 - .../jdesktop/swingx/renderer/MappedValue.java | 98 - .../swingx/renderer/MappedValues.java | 82 - .../swingx/renderer/PainterAware.java | 36 - .../jdesktop/swingx/renderer/StringValue.java | 79 - .../swingx/renderer/StringValues.java | 189 - .../swingx/renderer/TableCellContext.java | 187 - .../swingx/renderer/TreeCellContext.java | 278 -- .../swingx/renderer/WrappingIconPanel.java | 304 -- .../swingx/renderer/WrappingProvider.java | 398 -- .../swingx/renderer/package-info.java | 25 - .../rollover/ListRolloverController.java | 112 - .../swingx/rollover/ListRolloverProducer.java | 49 - .../swingx/rollover/RolloverController.java | 235 - .../swingx/rollover/RolloverProducer.java | 281 -- .../swingx/rollover/RolloverRenderer.java | 47 - .../rollover/TableRolloverController.java | 168 - .../rollover/TableRolloverProducer.java | 47 - .../rollover/TreeRolloverController.java | 108 - .../swingx/rollover/TreeRolloverProducer.java | 87 - .../swingx/search/AbstractSearchable.java | 661 --- .../swingx/search/ListSearchable.java | 162 - .../search/NativeSearchFieldSupport.java | 111 - .../swingx/search/PatternMatcher.java | 34 - .../jdesktop/swingx/search/PatternModel.java | 620 --- .../swingx/search/RecentSearches.java | 364 -- .../jdesktop/swingx/search/SearchFactory.java | 549 -- .../jdesktop/swingx/search/Searchable.java | 90 - .../swingx/search/TableSearchable.java | 335 -- .../swingx/search/TreeSearchable.java | 167 - .../swingx/sort/DefaultSortController.java | 405 -- .../swingx/sort/ListSortController.java | 93 - .../org/jdesktop/swingx/sort/RowFilters.java | 216 - .../jdesktop/swingx/sort/SortController.java | 279 -- .../org/jdesktop/swingx/sort/SortUtils.java | 90 - .../swingx/sort/StringValueProvider.java | 50 - .../swingx/sort/StringValueRegistry.java | 193 - .../swingx/sort/TableSortController.java | 164 - .../swingx/table/ColumnControlButton.java | 1040 ---- .../swingx/table/ColumnControlPopup.java | 105 - .../jdesktop/swingx/table/ColumnFactory.java | 465 -- .../swingx/table/DatePickerCellEditor.java | 334 -- .../table/DefaultTableColumnModelExt.java | 388 -- .../swingx/table/NumberEditorExt.java | 335 -- .../table/NumberEditorNumberFormat.java | 82 - .../swingx/table/NumberFormatExt.java | 102 - .../swingx/table/StrictNumberFormatter.java | 227 - .../jdesktop/swingx/table/TableColumnExt.java | 761 --- .../swingx/table/TableColumnModelExt.java | 232 - .../table/TableRowHeightController.java | 215 - .../jdesktop/swingx/table/TableUtilities.java | 172 - .../jdesktop/swingx/table/package-info.java | 36 - .../jdesktop/swingx/text/NumberFormatExt.java | 99 - .../swingx/text/StrictNumberFormatter.java | 226 - .../org/jdesktop/swingx/tips/DefaultTip.java | 67 - .../swingx/tips/DefaultTipOfTheDayModel.java | 75 - .../org/jdesktop/swingx/tips/TipLoader.java | 86 - .../swingx/tips/TipOfTheDayModel.java | 65 - .../jdesktop/swingx/tips/package-info.java | 27 - .../swingx/tree/DefaultXTreeCellEditor.java | 178 - .../swingx/tree/DefaultXTreeCellRenderer.java | 98 - .../swingx/tree/TreeModelSupport.java | 338 -- .../jdesktop/swingx/tree/TreeUtilities.java | 423 -- .../jdesktop/swingx/tree/package-info.java | 25 - .../AbstractMutableTreeTableNode.java | 282 -- .../treetable/AbstractTreeTableModel.java | 214 - .../DefaultMutableTreeTableNode.java | 88 - .../treetable/DefaultTreeTableModel.java | 453 -- .../swingx/treetable/FileSystemModel.java | 234 - .../treetable/MutableTreeTableNode.java | 99 - .../treetable/SimpleFileSystemModel.java | 272 - .../swingx/treetable/TreeTableCellEditor.java | 218 - .../swingx/treetable/TreeTableModel.java | 137 - .../treetable/TreeTableModelProvider.java | 51 - .../swingx/treetable/TreeTableNode.java | 116 - .../swingx/treetable/package-info.java | 36 - .../org/jdesktop/swingx/util/Contract.java | 70 - .../swingx/util/GraphicsUtilities.java | 834 ---- .../java/org/jdesktop/swingx/util/OS.java | 77 - .../org/jdesktop/swingx/util/PaintUtils.java | 538 -- .../org/jdesktop/swingx/util/Separator.java | 46 - .../org/jdesktop/swingx/util/ShapeUtils.java | 118 - .../org/jdesktop/swingx/util/Utilities.java | 964 ---- .../org/jdesktop/swingx/util/WindowUtils.java | 168 - .../jdesktop/swingx/util/package-info.java | 37 - 534 files changed, 6 insertions(+), 111813 deletions(-) delete mode 100644 src/main/java/org/jdesktop/beans/AbstractBean.java delete mode 100644 src/main/java/org/jdesktop/beans/AbstractSerializableBean.java delete mode 100644 src/main/java/org/jdesktop/beans/package-info.java delete mode 100644 src/main/java/org/jdesktop/swingx/AbstractLayoutManager.kt delete mode 100644 src/main/java/org/jdesktop/swingx/AbstractPatternPanel.java delete mode 100644 src/main/java/org/jdesktop/swingx/AlphaPaintable.java delete mode 100644 src/main/java/org/jdesktop/swingx/BackgroundPaintable.java delete mode 100644 src/main/java/org/jdesktop/swingx/ForwardingRepaintManager.java delete mode 100644 src/main/java/org/jdesktop/swingx/HorizontalLayout.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXBusyLabel.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXButton.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXCollapsiblePane.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXColorSelectionButton.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXComboBox.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXDatePicker.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXDialog.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXEditorPane.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXErrorPane.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXFindBar.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXFindPanel.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXFormattedTextField.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXFrame.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXGradientChooser.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXHeader.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXHyperlink.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXLabel.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXList.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXMonthView.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXMultiSplitPane.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXMultiThumbSlider.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXPanel.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXRadioGroup.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXRootPane.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXSearchField.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXSearchPanel.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXStatusBar.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXTable.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXTableHeader.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXTaskPane.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXTaskPaneContainer.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXTextArea.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXTextField.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXTipOfTheDay.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXTitledPanel.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXTitledSeparator.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXTree.java delete mode 100644 src/main/java/org/jdesktop/swingx/JXTreeTable.java delete mode 100644 src/main/java/org/jdesktop/swingx/Mnemonicable.java delete mode 100644 src/main/java/org/jdesktop/swingx/MultiSplitLayout.java delete mode 100644 src/main/java/org/jdesktop/swingx/RepaintManagerX.java delete mode 100644 src/main/java/org/jdesktop/swingx/ScrollableSizeHint.java delete mode 100644 src/main/java/org/jdesktop/swingx/StackLayout.java delete mode 100644 src/main/java/org/jdesktop/swingx/SwingXUtilities.java delete mode 100644 src/main/java/org/jdesktop/swingx/TranslucentRepaintManager.java delete mode 100644 src/main/java/org/jdesktop/swingx/VerticalLayout.java delete mode 100644 src/main/java/org/jdesktop/swingx/WrapLayout.java delete mode 100644 src/main/java/org/jdesktop/swingx/action/AbstractActionExt.java delete mode 100644 src/main/java/org/jdesktop/swingx/action/ActionContainerFactory.java delete mode 100644 src/main/java/org/jdesktop/swingx/action/ActionFactory.java delete mode 100644 src/main/java/org/jdesktop/swingx/action/ActionManager.java delete mode 100644 src/main/java/org/jdesktop/swingx/action/BoundAction.java delete mode 100644 src/main/java/org/jdesktop/swingx/action/CompositeAction.java delete mode 100644 src/main/java/org/jdesktop/swingx/action/OpenBrowserAction.java delete mode 100644 src/main/java/org/jdesktop/swingx/action/ServerAction.java delete mode 100644 src/main/java/org/jdesktop/swingx/action/TargetManager.java delete mode 100644 src/main/java/org/jdesktop/swingx/action/Targetable.java delete mode 100644 src/main/java/org/jdesktop/swingx/action/TargetableAction.java delete mode 100644 src/main/java/org/jdesktop/swingx/action/TargetableSupport.java delete mode 100644 src/main/java/org/jdesktop/swingx/action/ToggleActionPropertyChangeListener.java delete mode 100644 src/main/java/org/jdesktop/swingx/action/package-info.java delete mode 100644 src/main/java/org/jdesktop/swingx/autocomplete/AbstractAutoCompleteAdaptor.java delete mode 100644 src/main/java/org/jdesktop/swingx/autocomplete/AutoComplete.java delete mode 100644 src/main/java/org/jdesktop/swingx/autocomplete/AutoCompleteComboBoxEditor.java delete mode 100644 src/main/java/org/jdesktop/swingx/autocomplete/AutoCompleteDecorator.java delete mode 100644 src/main/java/org/jdesktop/swingx/autocomplete/AutoCompleteDocument.java delete mode 100644 src/main/java/org/jdesktop/swingx/autocomplete/AutoCompleteStyledDocument.java delete mode 100644 src/main/java/org/jdesktop/swingx/autocomplete/ComboBoxAdaptor.java delete mode 100644 src/main/java/org/jdesktop/swingx/autocomplete/ComboBoxCellEditor.java delete mode 100644 src/main/java/org/jdesktop/swingx/autocomplete/DelegatingDocumentEvent.java delete mode 100644 src/main/java/org/jdesktop/swingx/autocomplete/ListAdaptor.java delete mode 100644 src/main/java/org/jdesktop/swingx/autocomplete/ObjectToStringConverter.java delete mode 100644 src/main/java/org/jdesktop/swingx/autocomplete/TextComponentAdaptor.java delete mode 100644 src/main/java/org/jdesktop/swingx/autocomplete/package-info.java delete mode 100644 src/main/java/org/jdesktop/swingx/autocomplete/workarounds/MacOSXPopupLocationFix.java delete mode 100644 src/main/java/org/jdesktop/swingx/autocomplete/workarounds/package-info.java delete mode 100644 src/main/java/org/jdesktop/swingx/border/DropShadowBorder.java delete mode 100644 src/main/java/org/jdesktop/swingx/border/IconBorder.java delete mode 100644 src/main/java/org/jdesktop/swingx/border/MatteBorderExt.java delete mode 100644 src/main/java/org/jdesktop/swingx/border/package-info.java delete mode 100644 src/main/java/org/jdesktop/swingx/calendar/AbstractDateSelectionModel.java delete mode 100644 src/main/java/org/jdesktop/swingx/calendar/CalendarUtils.java delete mode 100644 src/main/java/org/jdesktop/swingx/calendar/DatePickerFormatter.java delete mode 100644 src/main/java/org/jdesktop/swingx/calendar/DateSelectionModel.java delete mode 100644 src/main/java/org/jdesktop/swingx/calendar/DateSpan.java delete mode 100644 src/main/java/org/jdesktop/swingx/calendar/DateUtils.java delete mode 100644 src/main/java/org/jdesktop/swingx/calendar/DaySelectionModel.java delete mode 100644 src/main/java/org/jdesktop/swingx/calendar/DefaultDateSelectionModel.java delete mode 100644 src/main/java/org/jdesktop/swingx/calendar/SingleDaySelectionModel.java delete mode 100644 src/main/java/org/jdesktop/swingx/calendar/package-info.java delete mode 100644 src/main/java/org/jdesktop/swingx/color/EyeDropperColorChooserPanel.form delete mode 100644 src/main/java/org/jdesktop/swingx/color/EyeDropperColorChooserPanel.java delete mode 100644 src/main/java/org/jdesktop/swingx/color/GradientPreviewPanel.java delete mode 100644 src/main/java/org/jdesktop/swingx/color/GradientThumbRenderer.java delete mode 100644 src/main/java/org/jdesktop/swingx/color/GradientTrackRenderer.java delete mode 100644 src/main/java/org/jdesktop/swingx/color/colorwell.png delete mode 100644 src/main/java/org/jdesktop/swingx/color/mag.png delete mode 100644 src/main/java/org/jdesktop/swingx/color/package-info.java delete mode 100644 src/main/java/org/jdesktop/swingx/combobox/EnumComboBoxModel.java delete mode 100644 src/main/java/org/jdesktop/swingx/combobox/JXTextFieldComboBoxEditor.java delete mode 100644 src/main/java/org/jdesktop/swingx/combobox/ListComboBoxModel.java delete mode 100644 src/main/java/org/jdesktop/swingx/combobox/ListModelComboBoxWrapper.java delete mode 100644 src/main/java/org/jdesktop/swingx/combobox/MapComboBoxModel.java delete mode 100644 src/main/java/org/jdesktop/swingx/combobox/package-info.java delete mode 100644 src/main/java/org/jdesktop/swingx/decorator/AbstractHighlighter.java delete mode 100644 src/main/java/org/jdesktop/swingx/decorator/AlignmentHighlighter.java delete mode 100644 src/main/java/org/jdesktop/swingx/decorator/BorderHighlighter.java delete mode 100644 src/main/java/org/jdesktop/swingx/decorator/ColorHighlighter.java delete mode 100644 src/main/java/org/jdesktop/swingx/decorator/ComponentAdapter.java delete mode 100644 src/main/java/org/jdesktop/swingx/decorator/ComponentOrientationHighlighter.java delete mode 100644 src/main/java/org/jdesktop/swingx/decorator/CompoundHighlighter.java delete mode 100644 src/main/java/org/jdesktop/swingx/decorator/EnabledHighlighter.java delete mode 100644 src/main/java/org/jdesktop/swingx/decorator/FontHighlighter.java delete mode 100644 src/main/java/org/jdesktop/swingx/decorator/HighlightPredicate.java delete mode 100644 src/main/java/org/jdesktop/swingx/decorator/Highlighter.java delete mode 100644 src/main/java/org/jdesktop/swingx/decorator/HighlighterFactory.java delete mode 100644 src/main/java/org/jdesktop/swingx/decorator/IconHighlighter.java delete mode 100644 src/main/java/org/jdesktop/swingx/decorator/PainterHighlighter.java delete mode 100644 src/main/java/org/jdesktop/swingx/decorator/PatternPredicate.java delete mode 100644 src/main/java/org/jdesktop/swingx/decorator/ResetDTCRColorHighlighter.java delete mode 100644 src/main/java/org/jdesktop/swingx/decorator/SearchPredicate.java delete mode 100644 src/main/java/org/jdesktop/swingx/decorator/ShadingColorHighlighter.java delete mode 100644 src/main/java/org/jdesktop/swingx/decorator/ToolTipHighlighter.java delete mode 100644 src/main/java/org/jdesktop/swingx/decorator/package-info.java delete mode 100644 src/main/java/org/jdesktop/swingx/error/ErrorEvent.java delete mode 100644 src/main/java/org/jdesktop/swingx/error/ErrorInfo.java delete mode 100644 src/main/java/org/jdesktop/swingx/error/ErrorLevel.java delete mode 100644 src/main/java/org/jdesktop/swingx/error/ErrorListener.java delete mode 100644 src/main/java/org/jdesktop/swingx/error/ErrorReporter.java delete mode 100644 src/main/java/org/jdesktop/swingx/error/ErrorSupport.java delete mode 100644 src/main/java/org/jdesktop/swingx/error/package-info.java delete mode 100644 src/main/java/org/jdesktop/swingx/event/CompoundFocusListener.java delete mode 100644 src/main/java/org/jdesktop/swingx/event/DateSelectionEvent.java delete mode 100644 src/main/java/org/jdesktop/swingx/event/DateSelectionListener.java delete mode 100644 src/main/java/org/jdesktop/swingx/event/EventListenerMap.java delete mode 100644 src/main/java/org/jdesktop/swingx/event/TableColumnModelExtListener.java delete mode 100644 src/main/java/org/jdesktop/swingx/event/TreeExpansionBroadcaster.java delete mode 100644 src/main/java/org/jdesktop/swingx/event/WeakEventListenerList.java delete mode 100644 src/main/java/org/jdesktop/swingx/event/package-info.java delete mode 100644 src/main/java/org/jdesktop/swingx/geom/Morphing2D.java delete mode 100644 src/main/java/org/jdesktop/swingx/geom/Star2D.java delete mode 100644 src/main/java/org/jdesktop/swingx/geom/package-info.java delete mode 100644 src/main/java/org/jdesktop/swingx/graphics/BlendComposite.java delete mode 100644 src/main/java/org/jdesktop/swingx/graphics/ColorUtilities.java delete mode 100644 src/main/java/org/jdesktop/swingx/graphics/FilterComposite.java delete mode 100644 src/main/java/org/jdesktop/swingx/graphics/Graphics2DFacade.java delete mode 100644 src/main/java/org/jdesktop/swingx/graphics/ReflectionRenderer.java delete mode 100644 src/main/java/org/jdesktop/swingx/graphics/ShadowRenderer.java delete mode 100644 src/main/java/org/jdesktop/swingx/graphics/package-info.java delete mode 100644 src/main/java/org/jdesktop/swingx/hyperlink/AbstractHyperlinkAction.java delete mode 100644 src/main/java/org/jdesktop/swingx/hyperlink/EditorPaneLinkVisitor.java delete mode 100644 src/main/java/org/jdesktop/swingx/hyperlink/HyperlinkAction.java delete mode 100644 src/main/java/org/jdesktop/swingx/hyperlink/LinkModel.java delete mode 100644 src/main/java/org/jdesktop/swingx/hyperlink/LinkModelAction.java delete mode 100644 src/main/java/org/jdesktop/swingx/icon/ColumnControlIcon.java delete mode 100644 src/main/java/org/jdesktop/swingx/icon/EmptyIcon.java delete mode 100644 src/main/java/org/jdesktop/swingx/icon/PainterIcon.java delete mode 100644 src/main/java/org/jdesktop/swingx/icon/package-info.java delete mode 100644 src/main/java/org/jdesktop/swingx/image/AbstractFilter.java delete mode 100644 src/main/java/org/jdesktop/swingx/image/ColorTintFilter.java delete mode 100644 src/main/java/org/jdesktop/swingx/image/FastBlurFilter.java delete mode 100644 src/main/java/org/jdesktop/swingx/image/GaussianBlurFilter.java delete mode 100644 src/main/java/org/jdesktop/swingx/image/StackBlurFilter.java delete mode 100644 src/main/java/org/jdesktop/swingx/image/package-info.java delete mode 100644 src/main/java/org/jdesktop/swingx/multislider/AbstractMultiThumbModel.java delete mode 100644 src/main/java/org/jdesktop/swingx/multislider/DefaultMultiThumbModel.java delete mode 100644 src/main/java/org/jdesktop/swingx/multislider/MultiThumbModel.java delete mode 100644 src/main/java/org/jdesktop/swingx/multislider/Thumb.java delete mode 100644 src/main/java/org/jdesktop/swingx/multislider/ThumbDataEvent.java delete mode 100644 src/main/java/org/jdesktop/swingx/multislider/ThumbDataListener.java delete mode 100644 src/main/java/org/jdesktop/swingx/multislider/ThumbListener.java delete mode 100644 src/main/java/org/jdesktop/swingx/multislider/ThumbRenderer.java delete mode 100644 src/main/java/org/jdesktop/swingx/multislider/TrackRenderer.java delete mode 100644 src/main/java/org/jdesktop/swingx/multislider/package-info.java delete mode 100644 src/main/java/org/jdesktop/swingx/multisplitpane/DefaultSplitPaneModel.java delete mode 100644 src/main/java/org/jdesktop/swingx/multisplitpane/package-info.java delete mode 100644 src/main/java/org/jdesktop/swingx/package-info.java delete mode 100644 src/main/java/org/jdesktop/swingx/painter/AbstractAreaPainter.java delete mode 100644 src/main/java/org/jdesktop/swingx/painter/AbstractLayoutPainter.java delete mode 100644 src/main/java/org/jdesktop/swingx/painter/AbstractPainter.java delete mode 100644 src/main/java/org/jdesktop/swingx/painter/AlphaPainter.java delete mode 100644 src/main/java/org/jdesktop/swingx/painter/BusyPainter.java delete mode 100644 src/main/java/org/jdesktop/swingx/painter/CheckerboardPainter.java delete mode 100644 src/main/java/org/jdesktop/swingx/painter/CompoundPainter.java delete mode 100644 src/main/java/org/jdesktop/swingx/painter/GlossPainter.java delete mode 100644 src/main/java/org/jdesktop/swingx/painter/ImagePainter.java delete mode 100644 src/main/java/org/jdesktop/swingx/painter/MattePainter.java delete mode 100644 src/main/java/org/jdesktop/swingx/painter/Painter.java delete mode 100644 src/main/java/org/jdesktop/swingx/painter/PainterPaint.java delete mode 100644 src/main/java/org/jdesktop/swingx/painter/PainterUtils.java delete mode 100644 src/main/java/org/jdesktop/swingx/painter/Painters.java delete mode 100644 src/main/java/org/jdesktop/swingx/painter/PinstripePainter.java delete mode 100644 src/main/java/org/jdesktop/swingx/painter/RectanglePainter.java delete mode 100644 src/main/java/org/jdesktop/swingx/painter/ShapePainter.java delete mode 100644 src/main/java/org/jdesktop/swingx/painter/TextPainter.java delete mode 100644 src/main/java/org/jdesktop/swingx/painter/effects/AbstractAreaEffect.java delete mode 100644 src/main/java/org/jdesktop/swingx/painter/effects/AreaEffect.java delete mode 100644 src/main/java/org/jdesktop/swingx/painter/effects/GlowPathEffect.java delete mode 100644 src/main/java/org/jdesktop/swingx/painter/effects/InnerGlowPathEffect.java delete mode 100644 src/main/java/org/jdesktop/swingx/painter/effects/InnerShadowPathEffect.java delete mode 100644 src/main/java/org/jdesktop/swingx/painter/effects/NeonBorderEffect.java delete mode 100644 src/main/java/org/jdesktop/swingx/painter/effects/ShadowPathEffect.java delete mode 100644 src/main/java/org/jdesktop/swingx/painter/effects/package-info.java delete mode 100644 src/main/java/org/jdesktop/swingx/painter/package-info.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/AbstractComponentAddon.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/AbstractUIChangeHandler.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/BuddyLayoutAndBorder.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/BuddyTextFieldUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/BusyLabelAddon.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/BusyLabelUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/ColumnControlButtonAddon.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/ComponentAddon.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/DatePickerAddon.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/DatePickerUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/DefaultsList.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/ErrorPaneAddon.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/ErrorPaneUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/HeaderAddon.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/HeaderUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/HyperlinkAddon.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/LookAndFeelAddons.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/LookAndFeelUtils.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/MonthViewAddon.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/MonthViewUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/MultiThumbSliderAddon.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/MultiThumbSliderUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/PainterUIResource.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/PromptTextAreaUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/PromptTextFieldUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/PromptTextUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/SafeBorder.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/SearchFieldAddon.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/SearchFieldUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/ShapeUIResource.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/StatusBarAddon.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/StatusBarUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/TableAddon.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/TableHeaderAddon.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/TaskPaneAddon.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/TaskPaneContainerAddon.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/TaskPaneContainerUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/TaskPaneUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/TextUIWrapper.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/TipOfTheDayAddon.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/TipOfTheDayUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/TitledPanelAddon.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/TitledPanelUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/UIAction.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/UIColorHighlighterAddon.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/UIDependent.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/UIManagerExt.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/XListAddon.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/BasicBusyLabelUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/BasicCalendarHeaderHandler.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/BasicCalendarRenderingHandler.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/BasicDatePickerUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/BasicErrorPaneUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/BasicHeaderUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/BasicHyperlinkUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/BasicLookAndFeelAddons.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/BasicMonthViewUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/BasicMultiThumbSliderUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/BasicStatusBarUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/BasicTaskPaneContainerUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/BasicTaskPaneUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/BasicTipOfTheDayUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/BasicTitledPanelUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/CalendarAdapter.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/CalendarCellContext.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/CalendarHeaderHandler.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/CalendarRenderingHandler.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/CalendarState.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/CapsLockSupport.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/SpinningCalendarHeaderHandler.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/TextCrossingPainter.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/core/BasicTransferable.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/core/BasicXListUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/core/DragRecognitionSupport.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/core/LazyActionMap.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/core/ListSortUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/package-info.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_cs.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_da.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_de.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_en.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_en_GB.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_en_US.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_es.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_fr.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_it.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_nl.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_pl_PL.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_pt_BR.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_sv.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_cs.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_da.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_de.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_en.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_es.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_fr.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_it.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_nl.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_pl.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_pt_BR.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_sv.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_cs.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_de.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_en.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_es.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_fr.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_it.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_nl.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_pl.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_pt_BR.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_sv.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/SearchField.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/SearchField_de.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/SearchField_en.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay24.gif delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_cs.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_de.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_en.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_es.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_fr.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_it.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_nl.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_pl.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_pt_BR.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_sv.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/clear.gif delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/clear_pressed.gif delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/clear_rollover.gif delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/error16.png delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/month-down.png delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/month-up.png delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/search.gif delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/search_popup.gif delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_cs.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_da.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_de.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_en.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_es.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_fr.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_it.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_nl.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_pl.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_pt_BR.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_sv.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/linux/LinuxLookAndFeelAddons.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/linux/package-info.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/linux/resources/combo-gtk.png delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/MacOSXErrorPaneUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/MacOSXLookAndFeelAddons.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/MacOSXStatusBarUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/package-info.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_cs.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_da.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_de.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_es.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_fr.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_it.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_nl.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_pt_BR.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_sv.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/resources/clear.png delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/resources/clear_pressed.png delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/resources/clear_rollover.png delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/resources/combo-osx.png delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/resources/search.png delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/macosx/resources/search_popup.png delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/metal/MetalLookAndFeelAddons.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/metal/MetalStatusBarUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/metal/MetalTaskPaneUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/metal/package-info.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/nimbus/NimbusLookAndFeelAddons.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/nimbus/NimbusTaskPaneUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/nimbus/package-info.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/package-info.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/WindowsLookAndFeelAddons.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/WindowsStatusBarUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/WindowsTaskPaneUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/WindowsTipOfTheDayUI.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/package-info.java delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_cs.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_de.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_es.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_fr.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_it.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_nl.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_pl.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_pt_BR.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_sv.properties delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/clear.gif delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/clear_pressed.gif delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/clear_rollover.gif delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/combo-xp.png delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/search.gif delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/search_popup.gif delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/search_popup_pressed.gif delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/search_popup_rollover.gif delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/search_pressed.gif delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/search_rollover.gif delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/silver-statusbar-left.png delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/silver-statusbar-middle.png delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/silver-statusbar-right.png delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/statusbar-left.png delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/statusbar-middle.png delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/statusbar-right.png delete mode 100644 src/main/java/org/jdesktop/swingx/plaf/windows/resources/tipoftheday.png delete mode 100644 src/main/java/org/jdesktop/swingx/prompt/BuddyButton.java delete mode 100644 src/main/java/org/jdesktop/swingx/prompt/BuddySupport.java delete mode 100644 src/main/java/org/jdesktop/swingx/prompt/PromptSupport.java delete mode 100644 src/main/java/org/jdesktop/swingx/renderer/AbstractRenderer.java delete mode 100644 src/main/java/org/jdesktop/swingx/renderer/BooleanValue.java delete mode 100644 src/main/java/org/jdesktop/swingx/renderer/CellContext.java delete mode 100644 src/main/java/org/jdesktop/swingx/renderer/CheckBoxProvider.java delete mode 100644 src/main/java/org/jdesktop/swingx/renderer/ComponentProvider.java delete mode 100644 src/main/java/org/jdesktop/swingx/renderer/DefaultListRenderer.java delete mode 100644 src/main/java/org/jdesktop/swingx/renderer/DefaultTableRenderer.java delete mode 100644 src/main/java/org/jdesktop/swingx/renderer/DefaultTreeRenderer.java delete mode 100644 src/main/java/org/jdesktop/swingx/renderer/DefaultVisuals.java delete mode 100644 src/main/java/org/jdesktop/swingx/renderer/FormatStringValue.java delete mode 100644 src/main/java/org/jdesktop/swingx/renderer/HyperlinkProvider.java delete mode 100644 src/main/java/org/jdesktop/swingx/renderer/IconAware.java delete mode 100644 src/main/java/org/jdesktop/swingx/renderer/IconValue.java delete mode 100644 src/main/java/org/jdesktop/swingx/renderer/IconValues.java delete mode 100644 src/main/java/org/jdesktop/swingx/renderer/JRendererCheckBox.java delete mode 100644 src/main/java/org/jdesktop/swingx/renderer/JRendererLabel.java delete mode 100644 src/main/java/org/jdesktop/swingx/renderer/JRendererPanel.java delete mode 100644 src/main/java/org/jdesktop/swingx/renderer/JXRendererHyperlink.java delete mode 100644 src/main/java/org/jdesktop/swingx/renderer/LabelProvider.java delete mode 100644 src/main/java/org/jdesktop/swingx/renderer/ListCellContext.java delete mode 100644 src/main/java/org/jdesktop/swingx/renderer/LocalizableStringValue.java delete mode 100644 src/main/java/org/jdesktop/swingx/renderer/MappedValue.java delete mode 100644 src/main/java/org/jdesktop/swingx/renderer/MappedValues.java delete mode 100644 src/main/java/org/jdesktop/swingx/renderer/PainterAware.java delete mode 100644 src/main/java/org/jdesktop/swingx/renderer/StringValue.java delete mode 100644 src/main/java/org/jdesktop/swingx/renderer/StringValues.java delete mode 100644 src/main/java/org/jdesktop/swingx/renderer/TableCellContext.java delete mode 100644 src/main/java/org/jdesktop/swingx/renderer/TreeCellContext.java delete mode 100644 src/main/java/org/jdesktop/swingx/renderer/WrappingIconPanel.java delete mode 100644 src/main/java/org/jdesktop/swingx/renderer/WrappingProvider.java delete mode 100644 src/main/java/org/jdesktop/swingx/renderer/package-info.java delete mode 100644 src/main/java/org/jdesktop/swingx/rollover/ListRolloverController.java delete mode 100644 src/main/java/org/jdesktop/swingx/rollover/ListRolloverProducer.java delete mode 100644 src/main/java/org/jdesktop/swingx/rollover/RolloverController.java delete mode 100644 src/main/java/org/jdesktop/swingx/rollover/RolloverProducer.java delete mode 100644 src/main/java/org/jdesktop/swingx/rollover/RolloverRenderer.java delete mode 100644 src/main/java/org/jdesktop/swingx/rollover/TableRolloverController.java delete mode 100644 src/main/java/org/jdesktop/swingx/rollover/TableRolloverProducer.java delete mode 100644 src/main/java/org/jdesktop/swingx/rollover/TreeRolloverController.java delete mode 100644 src/main/java/org/jdesktop/swingx/rollover/TreeRolloverProducer.java delete mode 100644 src/main/java/org/jdesktop/swingx/search/AbstractSearchable.java delete mode 100644 src/main/java/org/jdesktop/swingx/search/ListSearchable.java delete mode 100644 src/main/java/org/jdesktop/swingx/search/NativeSearchFieldSupport.java delete mode 100644 src/main/java/org/jdesktop/swingx/search/PatternMatcher.java delete mode 100644 src/main/java/org/jdesktop/swingx/search/PatternModel.java delete mode 100644 src/main/java/org/jdesktop/swingx/search/RecentSearches.java delete mode 100644 src/main/java/org/jdesktop/swingx/search/SearchFactory.java delete mode 100644 src/main/java/org/jdesktop/swingx/search/Searchable.java delete mode 100644 src/main/java/org/jdesktop/swingx/search/TableSearchable.java delete mode 100644 src/main/java/org/jdesktop/swingx/search/TreeSearchable.java delete mode 100644 src/main/java/org/jdesktop/swingx/sort/DefaultSortController.java delete mode 100644 src/main/java/org/jdesktop/swingx/sort/ListSortController.java delete mode 100644 src/main/java/org/jdesktop/swingx/sort/RowFilters.java delete mode 100644 src/main/java/org/jdesktop/swingx/sort/SortController.java delete mode 100644 src/main/java/org/jdesktop/swingx/sort/SortUtils.java delete mode 100644 src/main/java/org/jdesktop/swingx/sort/StringValueProvider.java delete mode 100644 src/main/java/org/jdesktop/swingx/sort/StringValueRegistry.java delete mode 100644 src/main/java/org/jdesktop/swingx/sort/TableSortController.java delete mode 100644 src/main/java/org/jdesktop/swingx/table/ColumnControlButton.java delete mode 100644 src/main/java/org/jdesktop/swingx/table/ColumnControlPopup.java delete mode 100644 src/main/java/org/jdesktop/swingx/table/ColumnFactory.java delete mode 100644 src/main/java/org/jdesktop/swingx/table/DatePickerCellEditor.java delete mode 100644 src/main/java/org/jdesktop/swingx/table/DefaultTableColumnModelExt.java delete mode 100644 src/main/java/org/jdesktop/swingx/table/NumberEditorExt.java delete mode 100644 src/main/java/org/jdesktop/swingx/table/NumberEditorNumberFormat.java delete mode 100644 src/main/java/org/jdesktop/swingx/table/NumberFormatExt.java delete mode 100644 src/main/java/org/jdesktop/swingx/table/StrictNumberFormatter.java delete mode 100644 src/main/java/org/jdesktop/swingx/table/TableColumnExt.java delete mode 100644 src/main/java/org/jdesktop/swingx/table/TableColumnModelExt.java delete mode 100644 src/main/java/org/jdesktop/swingx/table/TableRowHeightController.java delete mode 100644 src/main/java/org/jdesktop/swingx/table/TableUtilities.java delete mode 100644 src/main/java/org/jdesktop/swingx/table/package-info.java delete mode 100644 src/main/java/org/jdesktop/swingx/text/NumberFormatExt.java delete mode 100644 src/main/java/org/jdesktop/swingx/text/StrictNumberFormatter.java delete mode 100644 src/main/java/org/jdesktop/swingx/tips/DefaultTip.java delete mode 100644 src/main/java/org/jdesktop/swingx/tips/DefaultTipOfTheDayModel.java delete mode 100644 src/main/java/org/jdesktop/swingx/tips/TipLoader.java delete mode 100644 src/main/java/org/jdesktop/swingx/tips/TipOfTheDayModel.java delete mode 100644 src/main/java/org/jdesktop/swingx/tips/package-info.java delete mode 100644 src/main/java/org/jdesktop/swingx/tree/DefaultXTreeCellEditor.java delete mode 100644 src/main/java/org/jdesktop/swingx/tree/DefaultXTreeCellRenderer.java delete mode 100644 src/main/java/org/jdesktop/swingx/tree/TreeModelSupport.java delete mode 100644 src/main/java/org/jdesktop/swingx/tree/TreeUtilities.java delete mode 100644 src/main/java/org/jdesktop/swingx/tree/package-info.java delete mode 100644 src/main/java/org/jdesktop/swingx/treetable/AbstractMutableTreeTableNode.java delete mode 100644 src/main/java/org/jdesktop/swingx/treetable/AbstractTreeTableModel.java delete mode 100644 src/main/java/org/jdesktop/swingx/treetable/DefaultMutableTreeTableNode.java delete mode 100644 src/main/java/org/jdesktop/swingx/treetable/DefaultTreeTableModel.java delete mode 100644 src/main/java/org/jdesktop/swingx/treetable/FileSystemModel.java delete mode 100644 src/main/java/org/jdesktop/swingx/treetable/MutableTreeTableNode.java delete mode 100644 src/main/java/org/jdesktop/swingx/treetable/SimpleFileSystemModel.java delete mode 100644 src/main/java/org/jdesktop/swingx/treetable/TreeTableCellEditor.java delete mode 100644 src/main/java/org/jdesktop/swingx/treetable/TreeTableModel.java delete mode 100644 src/main/java/org/jdesktop/swingx/treetable/TreeTableModelProvider.java delete mode 100644 src/main/java/org/jdesktop/swingx/treetable/TreeTableNode.java delete mode 100644 src/main/java/org/jdesktop/swingx/treetable/package-info.java delete mode 100644 src/main/java/org/jdesktop/swingx/util/Contract.java delete mode 100644 src/main/java/org/jdesktop/swingx/util/GraphicsUtilities.java delete mode 100644 src/main/java/org/jdesktop/swingx/util/OS.java delete mode 100644 src/main/java/org/jdesktop/swingx/util/PaintUtils.java delete mode 100644 src/main/java/org/jdesktop/swingx/util/Separator.java delete mode 100644 src/main/java/org/jdesktop/swingx/util/ShapeUtils.java delete mode 100644 src/main/java/org/jdesktop/swingx/util/Utilities.java delete mode 100644 src/main/java/org/jdesktop/swingx/util/WindowUtils.java delete mode 100644 src/main/java/org/jdesktop/swingx/util/package-info.java diff --git a/pom.xml b/pom.xml index d20c60f728..7d1415ee32 100755 --- a/pom.xml +++ b/pom.xml @@ -150,17 +150,10 @@ org.swinglabs.swingx - swingx-mavensupport + swingx-all 1.6.5-1 - - org.kohsuke.metainf-services - metainf-services - 1.9 - provided - - com.formdev flatlaf @@ -171,6 +164,11 @@ flatlaf-extras ${flatlaf.version} + + com.formdev + flatlaf-swingx + ${flatlaf.version} + es.blackleg diff --git a/src/main/java/org/jdesktop/beans/AbstractBean.java b/src/main/java/org/jdesktop/beans/AbstractBean.java deleted file mode 100644 index 2f8d099379..0000000000 --- a/src/main/java/org/jdesktop/beans/AbstractBean.java +++ /dev/null @@ -1,497 +0,0 @@ -/* - * $Id: AbstractBean.java 4088 2011-11-17 19:53:49Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.beans; - -import java.beans.*; - -/** - *

                - * A convenience class from which to extend all non-visual AbstractBeans. It - * manages the PropertyChange notification system, making it relatively trivial - * to add support for property change events in getters/setters. - *

                - * - *

                - * A non-visual java bean is a Java class that conforms to the AbstractBean - * patterns to allow visual manipulation of the bean's properties and event - * handlers at design-time. - *

                - * - *

                - * Here is a simple example bean that contains one property, foo, and the proper - * pattern for implementing property change notification: - * - *

                
                - * public class ABean extends AbstractBean {
                - *     private String foo;
                - * 
                - *     public void setFoo(String newFoo) {
                - *         String old = getFoo();
                - *         this.foo = newFoo;
                - *         firePropertyChange("foo", old, getFoo());
                - *     }
                - * 
                - *     public String getFoo() {
                - *         return foo;
                - *     }
                - * }
                - * 
                - * - *

                - * - *

                - * You will notice that "getFoo()" is used in the setFoo method rather than - * accessing "foo" directly for the gets. This is done intentionally so that if - * a subclass overrides getFoo() to return, for instance, a constant value the - * property change notification system will continue to work properly. - *

                - * - *

                - * The firePropertyChange method takes into account the old value and the new - * value. Only if the two differ will it fire a property change event. So you - * can be assured from the above code fragment that a property change event will - * only occur if old is indeed different from getFoo() - *

                - * - *

                - * AbstractBean also supports vetoable - * {@link PropertyChangeEvent} events. These events are similar to - * PropertyChange events, except a special exception can be used - * to veto changing the property. For example, perhaps the property is changing - * from "fred" to "red", but a listener deems that "red" is unexceptable. In - * this case, the listener can fire a veto exception and the property must - * remain "fred". For example: - * - *

                
                - *  public class ABean extends AbstractBean {
                - *    private String foo;
                - *    
                - *    public void setFoo(String newFoo) throws PropertyVetoException {
                - *      String old = getFoo();
                - *      this.foo = newFoo;
                - *      fireVetoableChange("foo", old, getFoo());
                - *    }
                - *    public String getFoo() {
                - *      return foo;
                - *    }
                - *  }
                - * 
                - *  public class Tester {
                - *    public static void main(String... args) {
                - *      try {
                - *        ABean a = new ABean();
                - *        a.setFoo("fred");
                - *        a.addVetoableChangeListener(new VetoableChangeListener() {
                - *          public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException {
                - *            if ("red".equals(evt.getNewValue()) {
                - *              throw new PropertyVetoException("Cannot be red!", evt);
                - *            }
                - *          }
                - *        }
                - *        a.setFoo("red");
                - *      } catch (Exception e) {
                - *        e.printStackTrace(); // this will be executed
                - *      }
                - *    }
                - *  }
                - * 
                - * - *

                - *

                - * {@code AbstractBean} is not {@link java.io.Serializable}. Special care must - * be taken when creating {@code Serializable} subclasses, as the - * {@code Serializable} listeners will not be saved. Subclasses will need to - * manually save the serializable listeners. The {@link AbstractSerializableBean} - * is {@code Serializable} and already handles the listeners correctly. If - * possible, it is recommended that {@code Serializable} beans should extend - * {@code AbstractSerializableBean}. If it is not possible, the - * {@code AbstractSerializableBean} bean implementation provides details on - * how to correctly serialize an {@code AbstractBean} subclass. - *

                - * - * @see AbstractSerializableBean - * @status REVIEWED - * @author rbair - */ -@SuppressWarnings("nls") -public abstract class AbstractBean { - /** - * Helper class that manages all the property change notification machinery. - * PropertyChangeSupport cannot be extended directly because it requires - * a bean in the constructor, and the "this" argument is not valid until - * after super construction. Hence, delegation instead of extension - */ - private transient PropertyChangeSupport pcs; - - /** - * Helper class that manages all the veto property change notification machinery. - */ - private transient VetoableChangeSupport vcs; - - /** Creates a new instance of AbstractBean */ - protected AbstractBean() { - pcs = new PropertyChangeSupport(this); - vcs = new VetoableChangeSupport(this); - } - - /** - * Creates a new instance of AbstractBean, using the supplied PropertyChangeSupport and - * VetoableChangeSupport delegates. Neither of these may be null. - */ - protected AbstractBean(PropertyChangeSupport pcs, VetoableChangeSupport vcs) { - if (pcs == null) { - throw new NullPointerException("PropertyChangeSupport must not be null"); - } - if (vcs == null) { - throw new NullPointerException("VetoableChangeSupport must not be null"); - } - - this.pcs = pcs; - this.vcs = vcs; - } - - /** - * Add a PropertyChangeListener to the listener list. - * The listener is registered for all properties. - * The same listener object may be added more than once, and will be called - * as many times as it is added. - * If listener is null, no exception is thrown and no action - * is taken. - * - * @param listener The PropertyChangeListener to be added - */ - public final void addPropertyChangeListener(PropertyChangeListener listener) { - pcs.addPropertyChangeListener(listener); - } - - /** - * Remove a PropertyChangeListener from the listener list. - * This removes a PropertyChangeListener that was registered - * for all properties. - * If listener was added more than once to the same event - * source, it will be notified one less time after being removed. - * If listener is null, or was never added, no exception is - * thrown and no action is taken. - * - * @param listener The PropertyChangeListener to be removed - */ - public final void removePropertyChangeListener(PropertyChangeListener listener) { - pcs.removePropertyChangeListener(listener); - } - - /** - * Returns an array of all the listeners that were added to the - * PropertyChangeSupport object with addPropertyChangeListener(). - *

                - * If some listeners have been added with a named property, then - * the returned array will be a mixture of PropertyChangeListeners - * and PropertyChangeListenerProxys. If the calling - * method is interested in distinguishing the listeners then it must - * test each element to see if it's a - * PropertyChangeListenerProxy, perform the cast, and examine - * the parameter. - * - *

                -     * PropertyChangeListener[] listeners = bean.getPropertyChangeListeners();
                -     * for (int i = 0; i < listeners.length; i++) {
                -     *     if (listeners[i] instanceof PropertyChangeListenerProxy) {
                -     *     PropertyChangeListenerProxy proxy = 
                -     *                    (PropertyChangeListenerProxy)listeners[i];
                -     *     if (proxy.getPropertyName().equals("foo")) {
                -     *       // proxy is a PropertyChangeListener which was associated
                -     *       // with the property named "foo"
                -     *     }
                -     *   }
                -     * }
                -     *
                - * - * @see java.beans.PropertyChangeListenerProxy - * @return all of the PropertyChangeListeners added or an - * empty array if no listeners have been added - */ - public final PropertyChangeListener[] getPropertyChangeListeners() { - return pcs.getPropertyChangeListeners(); - } - - /** - * Add a PropertyChangeListener for a specific property. The listener - * will be invoked only when a call on firePropertyChange names that - * specific property. - * The same listener object may be added more than once. For each - * property, the listener will be invoked the number of times it was added - * for that property. - * If propertyName or listener is null, no - * exception is thrown and no action is taken. - * - * @param propertyName The name of the property to listen on. - * @param listener The PropertyChangeListener to be added - */ - public final void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { - pcs.addPropertyChangeListener(propertyName, listener); - } - - /** - * Remove a PropertyChangeListener for a specific property. - * If listener was added more than once to the same event - * source for the specified property, it will be notified one less time - * after being removed. - * If propertyName is null, no exception is thrown and no - * action is taken. - * If listener is null, or was never added for the specified - * property, no exception is thrown and no action is taken. - * - * @param propertyName The name of the property that was listened on. - * @param listener The PropertyChangeListener to be removed - */ - public final void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { - pcs.removePropertyChangeListener(propertyName, listener); - } - - /** - * Returns an array of all the listeners which have been associated - * with the named property. - * - * @param propertyName The name of the property being listened to - * @return all of the PropertyChangeListeners associated with - * the named property. If no such listeners have been added, - * or if propertyName is null, an empty array is - * returned. - */ - public final PropertyChangeListener[] getPropertyChangeListeners(String propertyName) { - return pcs.getPropertyChangeListeners(propertyName); - } - - /** - * Report a bound property update to any registered listeners. - * No event is fired if old and new are equal and non-null. - * - *

                - * This is merely a convenience wrapper around the more general - * firePropertyChange method that takes {@code - * PropertyChangeEvent} value. - * - * @param propertyName The programmatic name of the property - * that was changed. - * @param oldValue The old value of the property. - * @param newValue The new value of the property. - */ - protected final void firePropertyChange(String propertyName, Object oldValue, Object newValue) { - pcs.firePropertyChange(propertyName, oldValue, newValue); - } - - /** - * Fire an existing PropertyChangeEvent to any registered listeners. - * No event is fired if the given event's old and new values are - * equal and non-null. - * @param evt The PropertyChangeEvent object. - */ - protected final void firePropertyChange(PropertyChangeEvent evt) { - pcs.firePropertyChange(evt); - } - - - /** - * Report a bound indexed property update to any registered - * listeners. - *

                - * No event is fired if old and new values are equal - * and non-null. - * - *

                - * This is merely a convenience wrapper around the more general - * firePropertyChange method that takes {@code PropertyChangeEvent} value. - * - * @param propertyName The programmatic name of the property that - * was changed. - * @param index index of the property element that was changed. - * @param oldValue The old value of the property. - * @param newValue The new value of the property. - */ - protected final void fireIndexedPropertyChange(String propertyName, int index, - Object oldValue, Object newValue) { - pcs.fireIndexedPropertyChange(propertyName, index, oldValue, newValue); - } - - /** - * Check if there are any listeners for a specific property, including - * those registered on all properties. If propertyName - * is null, only check for listeners registered on all properties. - * - * @param propertyName the property name. - * @return true if there are one or more listeners for the given property - */ - protected final boolean hasPropertyChangeListeners(String propertyName) { - return pcs.hasListeners(propertyName); - } - - /** - * Check if there are any listeners for a specific property, including - * those registered on all properties. If propertyName - * is null, only check for listeners registered on all properties. - * - * @param propertyName the property name. - * @return true if there are one or more listeners for the given property - */ - protected final boolean hasVetoableChangeListeners(String propertyName) { - return vcs.hasListeners(propertyName); - } - - /** - * Add a VetoableListener to the listener list. - * The listener is registered for all properties. - * The same listener object may be added more than once, and will be called - * as many times as it is added. - * If listener is null, no exception is thrown and no action - * is taken. - * - * @param listener The VetoableChangeListener to be added - */ - - public final void addVetoableChangeListener(VetoableChangeListener listener) { - vcs.addVetoableChangeListener(listener); - } - - /** - * Remove a VetoableChangeListener from the listener list. - * This removes a VetoableChangeListener that was registered - * for all properties. - * If listener was added more than once to the same event - * source, it will be notified one less time after being removed. - * If listener is null, or was never added, no exception is - * thrown and no action is taken. - * - * @param listener The VetoableChangeListener to be removed - */ - public final void removeVetoableChangeListener(VetoableChangeListener listener) { - vcs.removeVetoableChangeListener(listener); - } - - /** - * Returns the list of VetoableChangeListeners. If named vetoable change listeners - * were added, then VetoableChangeListenerProxy wrappers will returned - *

                - * @return List of VetoableChangeListeners and VetoableChangeListenerProxys - * if named property change listeners were added. - */ - public final VetoableChangeListener[] getVetoableChangeListeners(){ - return vcs.getVetoableChangeListeners(); - } - - /** - * Add a VetoableChangeListener for a specific property. The listener - * will be invoked only when a call on fireVetoableChange names that - * specific property. - * The same listener object may be added more than once. For each - * property, the listener will be invoked the number of times it was added - * for that property. - * If propertyName or listener is null, no - * exception is thrown and no action is taken. - * - * @param propertyName The name of the property to listen on. - * @param listener The VetoableChangeListener to be added - */ - - public final void addVetoableChangeListener(String propertyName, - VetoableChangeListener listener) { - vcs.addVetoableChangeListener(propertyName, listener); - } - - /** - * Remove a VetoableChangeListener for a specific property. - * If listener was added more than once to the same event - * source for the specified property, it will be notified one less time - * after being removed. - * If propertyName is null, no exception is thrown and no - * action is taken. - * If listener is null, or was never added for the specified - * property, no exception is thrown and no action is taken. - * - * @param propertyName The name of the property that was listened on. - * @param listener The VetoableChangeListener to be removed - */ - - public final void removeVetoableChangeListener(String propertyName, - VetoableChangeListener listener) { - vcs.removeVetoableChangeListener(propertyName, listener); - } - - /** - * Returns an array of all the listeners which have been associated - * with the named property. - * - * @param propertyName The name of the property being listened to - * @return all the VetoableChangeListeners associated with - * the named property. If no such listeners have been added, - * or if propertyName is null, an empty array is - * returned. - */ - public final VetoableChangeListener[] getVetoableChangeListeners(String propertyName) { - return vcs.getVetoableChangeListeners(propertyName); - } - - /** - * Report a vetoable property update to any registered listeners. If - * anyone vetos the change, then fire a new event reverting everyone to - * the old value and then rethrow the PropertyVetoException. - *

                - * No event is fired if old and new are equal and non-null. - * - * @param propertyName The programmatic name of the property - * that is about to change.. - * @param oldValue The old value of the property. - * @param newValue The new value of the property. - * @exception PropertyVetoException if the recipient wishes the property - * change to be rolled back. - */ - protected final void fireVetoableChange(String propertyName, - Object oldValue, Object newValue) - throws PropertyVetoException { - vcs.fireVetoableChange(propertyName, oldValue, newValue); - } - - /** - * Fire a vetoable property update to any registered listeners. If - * anyone vetos the change, then fire a new event reverting everyone to - * the old value and then rethrow the PropertyVetoException. - *

                - * No event is fired if old and new are equal and non-null. - * - * @param evt The PropertyChangeEvent to be fired. - * @exception PropertyVetoException if the recipient wishes the property - * change to be rolled back. - */ - protected final void fireVetoableChange(PropertyChangeEvent evt) - throws PropertyVetoException { - vcs.fireVetoableChange(evt); - } - - /** - * {@inheritDoc} - */ - @Override - public Object clone() throws CloneNotSupportedException { - AbstractBean result = (AbstractBean) super.clone(); - result.pcs = new PropertyChangeSupport(result); - result.vcs = new VetoableChangeSupport(result); - return result; - } -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/beans/AbstractSerializableBean.java b/src/main/java/org/jdesktop/beans/AbstractSerializableBean.java deleted file mode 100644 index 31c6c14a9e..0000000000 --- a/src/main/java/org/jdesktop/beans/AbstractSerializableBean.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * $Id: AbstractSerializableBean.java 4088 2011-11-17 19:53:49Z kschaefe $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.beans; - -import java.beans.PropertyChangeListener; -import java.beans.PropertyChangeSupport; -import java.beans.VetoableChangeListener; -import java.beans.VetoableChangeSupport; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; - -/** - * This subclass enhances {@code AbstractBean} by implementing the - * {@code Serializable} interface. {@code AbstractSerializableBean} correctly - * serializes all {@code Serializable} listeners that it contains. Implementors - * that need to extends {@code AbstractBean} or one of its subclasses and - * require serialization should use this class if possible. If it is not - * possible to extend this class, the implementation can guide implementors on - * how to properly serialize the listeners. - * - * @author Karl George Schaefer - * - * @see Serializable - * @see ObjectInputStream - * @see ObjectOutputStream - */ -@SuppressWarnings("serial") -public abstract class AbstractSerializableBean extends AbstractBean implements - Serializable { - /** - * Creates a new instance of {@code AbstractSerializableBean}. - */ - protected AbstractSerializableBean() { - super(); - } - - /** - * Creates a new instance of {@code AbstractSerializableBean}, using the - * supplied support delegates. Neither of these may be {@code null}. - * - * @param pcs - * the property change support class to use - * @param vcs - * the vetoable change support class to use - * @throws NullPointerException - * if any parameter is {@code null} - */ - protected AbstractSerializableBean(PropertyChangeSupport pcs, - VetoableChangeSupport vcs) { - super(pcs, vcs); - } - - private void writeObject(ObjectOutputStream s) throws IOException { - s.defaultWriteObject(); - - for (PropertyChangeListener l : getPropertyChangeListeners()) { - if (l instanceof Serializable) { - s.writeObject(l); - } - } - - for (VetoableChangeListener l : getVetoableChangeListeners()) { - if (l instanceof Serializable) { - s.writeObject(l); - } - } - - s.writeObject(null); - } - - private void readObject(ObjectInputStream s) throws ClassNotFoundException, - IOException { - s.defaultReadObject(); - - Object listenerOrNull; - while (null != (listenerOrNull = s.readObject())) { - if (listenerOrNull instanceof PropertyChangeListener) { - addPropertyChangeListener((PropertyChangeListener) listenerOrNull); - } else if (listenerOrNull instanceof VetoableChangeListener) { - addVetoableChangeListener((VetoableChangeListener) listenerOrNull); - } - } - } -} diff --git a/src/main/java/org/jdesktop/beans/package-info.java b/src/main/java/org/jdesktop/beans/package-info.java deleted file mode 100644 index f42ceaf8ea..0000000000 --- a/src/main/java/org/jdesktop/beans/package-info.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -/** - * Contains classes to extend the functionality defined in the - * {@code java.beans} package. - */ -package org.jdesktop.beans; - diff --git a/src/main/java/org/jdesktop/swingx/AbstractLayoutManager.kt b/src/main/java/org/jdesktop/swingx/AbstractLayoutManager.kt deleted file mode 100644 index 4de450edfd..0000000000 --- a/src/main/java/org/jdesktop/swingx/AbstractLayoutManager.kt +++ /dev/null @@ -1,51 +0,0 @@ -package org.jdesktop.swingx - -import java.awt.Component -import java.awt.Container -import java.awt.Dimension -import java.awt.LayoutManager -import java.io.Serial -import java.io.Serializable - -/** - * A simple abstract class to handle common layout implementations. Package-private as we do NOT - * want to export this as part of the public API. - * - * @author kschaefer - */ -abstract class AbstractLayoutManager : LayoutManager, Serializable { - /** - * {@inheritDoc} - * - * - * This implementation does nothing. - */ - override fun addLayoutComponent(name: String, comp: Component) { - //does nothing - } - - /** - * {@inheritDoc} - * - * - * This implementation does nothing. - */ - override fun removeLayoutComponent(comp: Component) { - // does nothing - } - - /** - * {@inheritDoc} - * - * - * This implementation defers to [.preferredLayoutSize]. - */ - override fun minimumLayoutSize(parent: Container): Dimension { - return preferredLayoutSize(parent) - } - - companion object { - @Serial - private val serialVersionUID = 1446292747820044161L - } -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/AbstractPatternPanel.java b/src/main/java/org/jdesktop/swingx/AbstractPatternPanel.java deleted file mode 100644 index 779b8a2af2..0000000000 --- a/src/main/java/org/jdesktop/swingx/AbstractPatternPanel.java +++ /dev/null @@ -1,443 +0,0 @@ -/* - * $Id: AbstractPatternPanel.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx; - -import org.jdesktop.swingx.action.AbstractActionExt; -import org.jdesktop.swingx.action.ActionContainerFactory; -import org.jdesktop.swingx.action.BoundAction; -import org.jdesktop.swingx.plaf.LookAndFeelAddons; -import org.jdesktop.swingx.plaf.UIManagerExt; -import org.jdesktop.swingx.search.PatternModel; - -import javax.swing.*; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; -import java.awt.*; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.Locale; - -/** - * Common base class of ui clients. - * - * Implements basic synchronization between PatternModel state and - * actions bound to it. - * - * - * - * PENDING: extending JXPanel is a convenience measure, should be extracted - * into a dedicated controller. - * PENDING: should be re-visited when swingx goes binding-aware - * - * @author Jeanette Winzenburg - */ -public abstract class AbstractPatternPanel extends JXPanel { - - public static final String SEARCH_FIELD_LABEL = "searchFieldLabel"; - public static final String SEARCH_FIELD_MNEMONIC = SEARCH_FIELD_LABEL + ".mnemonic"; - public static final String SEARCH_TITLE = "searchTitle"; - public static final String MATCH_ACTION_COMMAND = "match"; - - static { - // Hack to enforce loading of SwingX framework ResourceBundle - LookAndFeelAddons.getAddon(); - } - - protected JLabel searchLabel; - protected JTextField searchField; - protected JCheckBox matchCheck; - - protected PatternModel patternModel; - private ActionContainerFactory actionFactory; - - -//------------------------ actions - - /** - * Callback action bound to MATCH_ACTION_COMMAND. - */ - public abstract void match(); - - /** - * convenience method for type-cast to AbstractActionExt. - * - * @param key Key to retrieve action - * @return Action bound to this key - * @see AbstractActionExt - */ - protected AbstractActionExt getAction(String key) { - // PENDING: outside clients might add different types? - return (AbstractActionExt) getActionMap().get(key); - } - - /** - * creates and registers all actions for the default the actionMap. - */ - protected void initActions() { - initPatternActions(); - initExecutables(); - } - - /** - * creates and registers all "executable" actions. - * Meaning: the actions bound to a callback method on this. - * - * PENDING: not quite correctly factored? Name? - * - */ - protected void initExecutables() { - Action execute = createBoundAction(MATCH_ACTION_COMMAND, "match"); - getActionMap().put(JXDialog.EXECUTE_ACTION_COMMAND, - execute); - getActionMap().put(MATCH_ACTION_COMMAND, execute); - refreshEmptyFromModel(); - } - - /** - * creates actions bound to PatternModel's state. - */ - protected void initPatternActions() { - ActionMap map = getActionMap(); - map.put(PatternModel.MATCH_CASE_ACTION_COMMAND, - createModelStateAction(PatternModel.MATCH_CASE_ACTION_COMMAND, - "setCaseSensitive", getPatternModel().isCaseSensitive())); - map.put(PatternModel.MATCH_WRAP_ACTION_COMMAND, - createModelStateAction(PatternModel.MATCH_WRAP_ACTION_COMMAND, - "setWrapping", getPatternModel().isWrapping())); - map.put(PatternModel.MATCH_BACKWARDS_ACTION_COMMAND, - createModelStateAction(PatternModel.MATCH_BACKWARDS_ACTION_COMMAND, - "setBackwards", getPatternModel().isBackwards())); - map.put(PatternModel.MATCH_INCREMENTAL_ACTION_COMMAND, - createModelStateAction(PatternModel.MATCH_INCREMENTAL_ACTION_COMMAND, - "setIncremental", getPatternModel().isIncremental())); - } - - /** - * Returns a potentially localized value from the UIManager. The given key - * is prefixed by this component|s UIPREFIX before doing the - * lookup. The lookup respects this table's current locale - * property. Returns the key, if no value is found. - * - * @param key the bare key to look up in the UIManager. - * @return the value mapped to UIPREFIX + key or key if no value is found. - */ - protected String getUIString(String key) { - return getUIString(key, getLocale()); - } - - /** - * Returns a potentially localized value from the UIManager for the - * given locale. The given key - * is prefixed by this component's UIPREFIX before doing the - * lookup. Returns the key, if no value is found. - * - * @param key the bare key to look up in the UIManager. - * @param locale the locale use for lookup - * @return the value mapped to UIPREFIX + key in the given locale, - * or key if no value is found. - */ - protected String getUIString(String key, Locale locale) { - String text = UIManagerExt.getString(PatternModel.SEARCH_PREFIX + key, locale); - return text != null ? text : key; - } - - - /** - * creates, configures and returns a bound state action on a boolean property - * of the PatternModel. - * - * @param command the actionCommand - same as key to find localizable resources - * @param methodName the method on the PatternModel to call on item state changed - * @param initial the initial value of the property - * @return newly created action - */ - protected AbstractActionExt createModelStateAction(String command, String methodName, boolean initial) { - String actionName = getUIString(command); - BoundAction action = new BoundAction(actionName, - command); - action.setStateAction(); - action.registerCallback(getPatternModel(), methodName); - action.setSelected(initial); - return action; - } - - /** - * creates, configures and returns a bound action to the given method of - * this. - * - * @param actionCommand the actionCommand, same as key to find localizable resources - * @param methodName the method to call an actionPerformed. - * @return newly created action - */ - protected AbstractActionExt createBoundAction(String actionCommand, String methodName) { - String actionName = getUIString(actionCommand); - BoundAction action = new BoundAction(actionName, - actionCommand); - action.registerCallback(this, methodName); - return action; - } - -//------------------------ dynamic locale support - - - /** - * {@inheritDoc}

                - * Overridden to update locale-dependent properties. - * - * @see #updateLocaleState(Locale) - */ - @Override - public void setLocale(Locale l) { - updateLocaleState(l); - super.setLocale(l); - } - - /** - * Updates locale-dependent state. - * - * Here: updates registered column actions' locale-dependent state. - *

                - * - * PENDING: Try better to find all column actions including custom - * additions? Or move to columnControl? - * - * @see #setLocale(Locale) - */ - protected void updateLocaleState(Locale locale) { - for (Object key : getActionMap().allKeys()) { - if (key instanceof String) { - String keyString = getUIString((String) key, locale); - if (!key.equals(keyString)) { - getActionMap().get(key).putValue(Action.NAME, keyString); - - } - } - } - bindSearchLabel(locale); - } - - - //---------------------- synch patternModel <--> components - - /** - * called from listening to pattern property of PatternModel. - * - * This implementation calls match() if the model is in - * incremental state. - * - */ - protected void refreshPatternFromModel() { - if (getPatternModel().isIncremental()) { - match(); - } - } - - - /** - * returns the patternModel. Lazyly creates and registers a - * propertyChangeListener if null. - * - * @return current PatternModel if it exists or newly created - * one if it was not initialized before this call - */ - protected PatternModel getPatternModel() { - if (patternModel == null) { - patternModel = createPatternModel(); - patternModel.addPropertyChangeListener(getPatternModelListener()); - } - return patternModel; - } - - - /** - * factory method to create the PatternModel. - * Hook for subclasses to install custom models. - * - * @return newly created PatternModel - */ - protected PatternModel createPatternModel() { - return new PatternModel(); - } - - /** - * creates and returns a PropertyChangeListener to the PatternModel. - * - * NOTE: the patternModel is totally under control of this class - currently - * there's no need to keep a reference to the listener. - * - * @return created and bound to appropriate callback methods - * PropertyChangeListener - */ - protected PropertyChangeListener getPatternModelListener() { - return new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - String property = evt.getPropertyName(); - if ("pattern".equals(property)) { - refreshPatternFromModel(); - } else if ("rawText".equals(property)) { - refreshDocumentFromModel(); - } else if ("caseSensitive".equals(property)){ - getAction(PatternModel.MATCH_CASE_ACTION_COMMAND). - setSelected(((Boolean) evt.getNewValue()).booleanValue()); - } else if ("wrapping".equals(property)) { - getAction(PatternModel.MATCH_WRAP_ACTION_COMMAND). - setSelected(((Boolean) evt.getNewValue()).booleanValue()); - } else if ("backwards".equals(property)) { - getAction(PatternModel.MATCH_BACKWARDS_ACTION_COMMAND). - setSelected(((Boolean) evt.getNewValue()).booleanValue()); - } else if ("incremental".equals(property)) { - getAction(PatternModel.MATCH_INCREMENTAL_ACTION_COMMAND). - setSelected(((Boolean) evt.getNewValue()).booleanValue()); - - } else if ("empty".equals(property)) { - refreshEmptyFromModel(); - } - - } - - }; - } - - /** - * called from listening to empty property of PatternModel. - * - * this implementation synch's the enabled state of the action with - * MATCH_ACTION_COMMAND to !empty. - * - */ - protected void refreshEmptyFromModel() { - boolean enabled = !getPatternModel().isEmpty(); - getAction(MATCH_ACTION_COMMAND).setEnabled(enabled); - - } - - /** - * callback method from listening to searchField. - * - */ - protected void refreshModelFromDocument() { - getPatternModel().setRawText(searchField.getText()); - } - - /** - * callback method that updates document from the search field - * - */ - protected void refreshDocumentFromModel() { - if (searchField.getText().equals(getPatternModel().getRawText())) return; - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - searchField.setText(getPatternModel().getRawText()); - } - }); - } - - /** - * Create DocumentListener for the search field that calls - * corresponding callback method whenever the search field contents is being changed - * - * @return newly created DocumentListener - */ - protected DocumentListener getSearchFieldListener() { - return new DocumentListener() { - @Override - public void changedUpdate(DocumentEvent ev) { - // JW - really?? we've a PlainDoc without Attributes - refreshModelFromDocument(); - } - - @Override - public void insertUpdate(DocumentEvent ev) { - refreshModelFromDocument(); - } - - @Override - public void removeUpdate(DocumentEvent ev) { - refreshModelFromDocument(); - } - - }; - } - -//-------------------------- config helpers - - /** - * configure and bind components to/from PatternModel - */ - protected void bind() { - bindSearchLabel(getLocale()); - searchField.getDocument().addDocumentListener(getSearchFieldListener()); - getActionContainerFactory().configureButton(matchCheck, - (AbstractActionExt) getActionMap().get(PatternModel.MATCH_CASE_ACTION_COMMAND), - null); - - } - - /** - * Configures the searchLabel. - * Here: sets text and mnenomic properties form ui values, - * configures as label for searchField. - */ - protected void bindSearchLabel(Locale locale) { - searchLabel.setText(getUIString(SEARCH_FIELD_LABEL, locale)); - String mnemonic = getUIString(SEARCH_FIELD_MNEMONIC, locale); - if (mnemonic != SEARCH_FIELD_MNEMONIC) { - searchLabel.setDisplayedMnemonic(mnemonic.charAt(0)); - } - searchLabel.setLabelFor(searchField); - } - - /** - * @return current ActionContainerFactory. - * Will lazily create new factory if it does not exist - */ - protected ActionContainerFactory getActionContainerFactory() { - if (actionFactory == null) { - actionFactory = new ActionContainerFactory(null); - } - return actionFactory; - } - - /** - * Initialize all the incorporated components and models - */ - protected void initComponents() { - searchLabel = new JLabel(); - searchField = new JTextField(getSearchFieldWidth()) { - @Override - public Dimension getMaximumSize() { - Dimension superMax = super.getMaximumSize(); - superMax.height = getPreferredSize().height; - return superMax; - } - }; - matchCheck = new JCheckBox(); - } - - /** - * @return width in characters of the search field - */ - protected int getSearchFieldWidth() { - return 15; - } -} diff --git a/src/main/java/org/jdesktop/swingx/AlphaPaintable.java b/src/main/java/org/jdesktop/swingx/AlphaPaintable.java deleted file mode 100644 index a24e4e7e2a..0000000000 --- a/src/main/java/org/jdesktop/swingx/AlphaPaintable.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.jdesktop.swingx; - -/** - * An interface to describe an object that is capable of painting with an alpha value. - * - * @author kschaefer - */ -interface AlphaPaintable { - /** - * Get the current alpha value. - * - * @return the alpha translucency level for this component. This will be a value between 0 and - * 1, inclusive. - */ - float getAlpha(); - - /** - * Set the alpha transparency level for this component. This automatically causes a repaint of - * the component. - * - * @param alpha - * must be a value between 0 and 1 inclusive - * @throws IllegalArgumentException - * if the value is invalid - */ - void setAlpha(float alpha); - - /** - * Returns the state of the panel with respect to inheriting alpha values. - * - * @return {@code true} if this panel inherits alpha values; {@code false} - * otherwise - * @see #setInheritAlpha(boolean) - */ - boolean isInheritAlpha(); - - /** - * Determines if the effective alpha of this component should include the - * alpha of ancestors. - * - * @param inheritAlpha - * {@code true} to include ancestral alpha data; {@code false} - * otherwise - * @see #isInheritAlpha() - * @see #getEffectiveAlpha() - */ - void setInheritAlpha(boolean inheritAlpha); - - /** - * Unlike other properties, alpha can be set on a component, or on one of - * its parents. If the alpha of a parent component is .4, and the alpha on - * this component is .5, effectively the alpha for this component is .4 - * because the lowest alpha in the hierarchy "wins." - * - * @return the lowest alpha value in the hierarchy - */ - float getEffectiveAlpha(); -} diff --git a/src/main/java/org/jdesktop/swingx/BackgroundPaintable.java b/src/main/java/org/jdesktop/swingx/BackgroundPaintable.java deleted file mode 100644 index 2ec3596db9..0000000000 --- a/src/main/java/org/jdesktop/swingx/BackgroundPaintable.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * $Id: BackgroundPaintable.java 4188 2012-06-27 14:21:10Z kschaefe $ - * - * Copyright 2010 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx; - -import org.jdesktop.swingx.painter.Painter; - -/** - * An interface to define the common methods that are required for defining a background painter. - * - * @author kschaefer - */ -@SuppressWarnings("rawtypes") -public interface BackgroundPaintable { - /** - * Returns the current background painter. - * - * @return the current painter - * @see #setBackgroundPainter(Painter) - * @see #isPaintBorderInsets() - */ - Painter getBackgroundPainter(); - - /** - * Sets the new background painter. - * - * @param painter the new background painter; may be {@code null} - */ - void setBackgroundPainter(Painter painter); - - /** - * Determines whether this component paints its background paint underneath the border. - * - * @return {@code true} to paint under the border; {@code false} otherwise - */ - boolean isPaintBorderInsets(); - - /** - * - * @param paintBorderInsets - */ - void setPaintBorderInsets(boolean paintBorderInsets); -} diff --git a/src/main/java/org/jdesktop/swingx/ForwardingRepaintManager.java b/src/main/java/org/jdesktop/swingx/ForwardingRepaintManager.java deleted file mode 100644 index d85579518e..0000000000 --- a/src/main/java/org/jdesktop/swingx/ForwardingRepaintManager.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * $Id: ForwardingRepaintManager.java 3690 2010-05-03 17:55:44Z kschaefe $ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx; - -import org.jdesktop.swingx.util.Contract; - -import javax.swing.*; -import java.applet.Applet; -import java.awt.*; - -/** - * A {@code RepaintManager} that is designed to forward all calls to a contained - * delegate. This class is designed for extension, such that subclasses should - * override method as appropriate and allow the original repaint manager to - * handle the rest of the work. - *

                - * Install a forwarding repaint manager: - * - *

                - * RepaintManager manager = RepaintManager.currentManager(this);
                - * RepaintManager frm = new ForwardingRepaintManager(manager);
                - * RepaintManager.setCurrentManager(frm);
                - * 
                - * - * @author Karl George Schaefer - * @author pietblok (original facade/delegate idea) - */ -public class ForwardingRepaintManager extends RepaintManager { - private RepaintManager delegate; - - /** - * Creates a new forwarding manager that forwards all calls to the delegate. - * - * @param delegate - * the manager backing this {@code ForwardingRepaintManager} - * @throws NullPointerException - * if {@code delegate} is {@code null} - */ - public ForwardingRepaintManager(RepaintManager delegate) { - this.delegate = Contract.asNotNull(delegate, "delegate is null"); - } - - /** - * {@inheritDoc} - */ - @Override - public void addDirtyRegion(Applet applet, int x, int y, int w, int h) { - delegate.addDirtyRegion(applet, x, y, w, h); - } - - /** - * {@inheritDoc} - */ - @Override - public void addDirtyRegion(JComponent c, int x, int y, int w, int h) { - delegate.addDirtyRegion(c, x, y, w, h); - } - - /** - * {@inheritDoc} - */ - @Override - public void addDirtyRegion(Window window, int x, int y, int w, int h) { - delegate.addDirtyRegion(window, x, y, w, h); - } - - /** - * {@inheritDoc} - */ - @Override - public synchronized void addInvalidComponent(JComponent invalidComponent) { - delegate.addInvalidComponent(invalidComponent); - } - - /** - * {@inheritDoc} - */ - @Override - public Rectangle getDirtyRegion(JComponent component) { - return delegate.getDirtyRegion(component); - } - - /** - * {@inheritDoc} - */ - @Override - public Dimension getDoubleBufferMaximumSize() { - return delegate.getDoubleBufferMaximumSize(); - } - - /** - * {@inheritDoc} - */ - @Override - public Image getOffscreenBuffer(Component c, int proposedWidth, int proposedHeight) { - return delegate.getOffscreenBuffer(c, proposedWidth, proposedHeight); - } - - /** - * {@inheritDoc} - */ - @Override - public Image getVolatileOffscreenBuffer(Component c, int proposedWidth, int proposedHeight) { - return delegate.getVolatileOffscreenBuffer(c, proposedWidth, proposedHeight); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isCompletelyDirty(JComponent component) { - return delegate.isCompletelyDirty(component); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isDoubleBufferingEnabled() { - return delegate.isDoubleBufferingEnabled(); - } - - /** - * {@inheritDoc} - */ - @Override - public void markCompletelyClean(JComponent component) { - delegate.markCompletelyClean(component); - } - - /** - * {@inheritDoc} - */ - @Override - public void markCompletelyDirty(JComponent component) { - delegate.markCompletelyDirty(component); - } - - /** - * {@inheritDoc} - */ - @Override - public void paintDirtyRegions() { - delegate.paintDirtyRegions(); - } - - /** - * {@inheritDoc} - */ - @Override - public synchronized void removeInvalidComponent(JComponent component) { - delegate.removeInvalidComponent(component); - } - - /** - * {@inheritDoc} - */ - @Override - public void setDoubleBufferingEnabled(boolean flag) { - delegate.setDoubleBufferingEnabled(flag); - } - - /** - * {@inheritDoc} - */ - @Override - public void setDoubleBufferMaximumSize(Dimension d) { - delegate.setDoubleBufferMaximumSize(d); - } - - /** - * {@inheritDoc} - */ - @Override - public synchronized String toString() { - return delegate.toString(); - } - - /** - * {@inheritDoc} - */ - @Override - public void validateInvalidComponents() { - delegate.validateInvalidComponents(); - } - - /** - * Gets the delegate repaint manager backing this forwarding repaint - * manager. - * - * @return the delegate for this forwarding manager - */ - public final RepaintManager getDelegateManager() { - return delegate; - } -} diff --git a/src/main/java/org/jdesktop/swingx/HorizontalLayout.java b/src/main/java/org/jdesktop/swingx/HorizontalLayout.java deleted file mode 100644 index bc6f1ecf79..0000000000 --- a/src/main/java/org/jdesktop/swingx/HorizontalLayout.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * $Id: HorizontalLayout.java 4147 2012-02-01 17:13:24Z kschaefe $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.util.Separator; - -import java.awt.*; - -/** - * Organizes components in a horizontal layout. - * - * @author Romain Guy - * @author Karl Schaefer - */ -@JavaBean -public class HorizontalLayout extends AbstractLayoutManager { - private static final long serialVersionUID = 8640046926840737487L; - - private int gap; - - public HorizontalLayout() { - this(0); - } - - //TODO should we allow negative gaps? - public HorizontalLayout(int gap) { - this.gap = gap; - } - - public int getGap() { - return gap; - } - - //TODO should we allow negative gaps? - public void setGap(int gap) { - this.gap = gap; - } - - @Override - public void layoutContainer(Container parent) { - Insets insets = parent.getInsets(); - Dimension size = parent.getSize(); - - int height = size.height - insets.top - insets.bottom; - int width = insets.left; - - for (int i = 0, c = parent.getComponentCount(); i < c; i++) { - Component m = parent.getComponent(i); - - if (m.isVisible()) { - m.setBounds(width, insets.top, m.getPreferredSize().width, height); - width += m.getSize().width + gap; - } - } - } - - @Override - public Dimension preferredLayoutSize(Container parent) { - Dimension pref = new Dimension(0, 0); - Separator sep = new Separator(0, gap); - - for (int i = 0, c = parent.getComponentCount(); i < c; i++) { - Component m = parent.getComponent(i); - if (m.isVisible()) { - Dimension componentPreferredSize = - parent.getComponent(i).getPreferredSize(); - pref.height = Math.max(pref.height, componentPreferredSize.height); - pref.width += componentPreferredSize.width + sep.get(); - } - } - - Insets insets = parent.getInsets(); - pref.width += insets.left + insets.right; - pref.height += insets.top + insets.bottom; - - return pref; - } -} diff --git a/src/main/java/org/jdesktop/swingx/JXBusyLabel.java b/src/main/java/org/jdesktop/swingx/JXBusyLabel.java deleted file mode 100644 index 5c7cd79b81..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXBusyLabel.java +++ /dev/null @@ -1,369 +0,0 @@ -/* - * $Id: JXBusyLabel.java 4147 2012-02-01 17:13:24Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.icon.PainterIcon; -import org.jdesktop.swingx.painter.BusyPainter; -import org.jdesktop.swingx.plaf.BusyLabelAddon; -import org.jdesktop.swingx.plaf.BusyLabelUI; -import org.jdesktop.swingx.plaf.LookAndFeelAddons; - -import javax.swing.*; -import javax.swing.plaf.LabelUI; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -/** - *

                A simple circular animation, useful for denoting an action is taking - * place that may take an unknown length of time to complete. Similar to an - * indeterminant JProgressBar, but with a different look.

                - * - *

                For example: - *

                
                - *     JXFrame frame = new JXFrame("test", true);
                - *     JXBusyLabel label = new JXBusyLabel();
                - *     frame.add(label);
                - *     //...
                - *     label.setBusy(true);
                - * 

                - * Another more complicated example: - *
                
                - * JXBusyLabel label = new JXBusyLabel(new Dimension(100,84));
                - * BusyPainter painter = new BusyPainter(
                - * new Rectangle2D.Float(0, 0,13.500001f,1),
                - * new RoundRectangle2D.Float(12.5f,12.5f,59.0f,59.0f,10,10));
                - * painter.setTrailLength(5);
                - * painter.setPoints(31);
                - * painter.setFrame(1);
                - * label.setPreferredSize(new Dimension(100,84));
                - * label.setIcon(new EmptyIcon(100,84));
                - * label.setBusyPainter(painter);
                - *
                - * - * Another example: - *
                
                - *     JXBusyLabel label = new MyBusyLabel(new Dimension(100, 84));
                - * 
                - * - * where MyBusyLabel is:
                - *
                
                - * public class MyBusyLabel extends JXBusyLabel {
                - *     public MyBusyLabel(Dimension prefSize) {
                - *         super(prefSize);
                - *     }
                - *     
                - *     protected BusyLabel createBusyLabel(Dimension dim) {
                - *         BusyPainter painter = new BusyPainter(
                - *         new Rectangle2D.Float(0, 0,13.500001f,1),
                - *         new RoundRectangle2D.Float(12.5f,12.5f,59.0f,59.0f,10,10));
                - *         painter.setTrailLength(5);
                - *         painter.setPoints(31);
                - *         painter.setFrame(1);
                - *         
                - *         return painter;
                - *     }
                - * }
                - * 
                - * - * @author rbair - * @author joshy - * @author rah003 - * @author headw01 - */ -@JavaBean -public class JXBusyLabel extends JLabel { - - private static final long serialVersionUID = 5979268460848257147L; - private BusyPainter busyPainter; - private Timer busy; - private int delay; - /** Status flag to save/restore status of timer when moving component between containers. */ - private boolean wasBusyOnNotify = false; - - /** - * UI Class ID - */ - public final static String uiClassID = "BusyLabelUI"; - - /** - * Sets direction of rotation. Direction.RIGHT is the default - * value. Direction is taken from the very top point so Direction.RIGHT enables rotation clockwise. - * @param dir Direction of rotation. - */ - public void setDirection(BusyPainter.Direction dir) { - direction = dir; - getBusyPainter().setDirection(dir); - } - - private BusyPainter.Direction direction; - - /** - * Creates a default JXLoginPane instance - */ - static { - LookAndFeelAddons.contribute(new BusyLabelAddon()); - } - - { - // Initialize the delay from the UI class. - BusyLabelUI ui = (BusyLabelUI)getUI(); - if (ui != null) { - delay = ui.getDelay(); - } - } - - /** Creates a new instance of JXBusyLabel initialized to circular shape in bounds of 26 by 26 points.*/ - public JXBusyLabel() { - this(null); - } - - /** - * Creates a new instance of JXBusyLabel initialized to the arbitrary size and using default circular progress indicator. - * @param dim Preferred size of the label. - */ - public JXBusyLabel(Dimension dim) { - super(); - this.setPreferredSize(dim); - - // Initialize the BusyPainter. - getBusyPainter(); - } - - /** - * Initialize the BusyPainter and (this) JXBusyLabel with the given - * preferred size. This method is called automatically when the - * BusyPainter is set/changed. - * - * @param dim The new Preferred Size for the BusyLabel. - * - * @see #getBusyPainter() - * @see #setBusyPainter(BusyPainter) - */ - protected void initPainter(Dimension dim) { - BusyPainter busyPainter = getBusyPainter(); - - // headw01 - // TODO: Should we force the busyPainter to NOT be cached? - // I think we probably should, otherwise the UI will never - // be updated after the first paint. - if (null != busyPainter) { - busyPainter.setCacheable(false); - } - - PainterIcon icon = new PainterIcon(dim); - icon.setPainter(busyPainter); - this.setIcon(icon); - } - /** - * Create and return a BusyPpainter to use for the Label. This may - * be overridden to return any painter you like. By default, this - * method uses the UI (BusyLabelUI)to create a BusyPainter. - * @param dim Painter size. - * - * @see #getUI() - */ - protected BusyPainter createBusyPainter(Dimension dim) { - BusyPainter busyPainter = null; - - BusyLabelUI ui = (BusyLabelUI)getUI(); - if (ui != null) { - busyPainter = ui.getBusyPainter(dim); - - } - - return busyPainter; - } - - /** - *

                Gets whether this JXBusyLabel is busy. If busy, then - * the JXBusyLabel instance will indicate that it is busy, - * generally by animating some state.

                - * - * @return true if this instance is busy - */ - public boolean isBusy() { - return busy != null; - } - - /** - *

                Sets whether this JXBusyLabel instance should consider - * itself busy. A busy component may indicate that it is busy via animation, - * or some other means.

                - * - * @param busy whether this JXBusyLabel instance should - * consider itself busy - */ - public void setBusy(boolean busy) { - boolean old = isBusy(); - if (!old && busy) { - startAnimation(); - firePropertyChange("busy", old, isBusy()); - } else if (old && !busy) { - stopAnimation(); - firePropertyChange("busy", old, isBusy()); - } - } - - private void startAnimation() { - if(busy != null) { - stopAnimation(); - } - - busy = new Timer(delay, new ActionListener() { - BusyPainter busyPainter = getBusyPainter(); - int frame = busyPainter.getPoints(); - @Override - public void actionPerformed(ActionEvent e) { - frame = (frame+1)%busyPainter.getPoints(); - busyPainter.setFrame(direction == BusyPainter.Direction.LEFT ? busyPainter.getPoints() - frame : frame); - frameChanged(); - } - }); - busy.start(); - } - - - - - private void stopAnimation() { - if (busy != null) { - busy.stop(); - getBusyPainter().setFrame(-1); - repaint(); - busy = null; - } - } - - @Override - public void removeNotify() { - // fix for #698 - wasBusyOnNotify = isBusy(); - // fix for #626 - stopAnimation(); - super.removeNotify(); - } - - @Override - public void addNotify() { - super.addNotify(); - // fix for #698 - if (wasBusyOnNotify) { - // fix for #626 - startAnimation(); - } - } - - protected void frameChanged() { - repaint(); - } - - /** - * Returns the current BusyPainter. If no BusyPainter is currently - * set on this BusyLabel, the {@link #createBusyPainter(Dimension)} - * method is called to create one. Afterwards, - * {@link #initPainter(Dimension)} is called to update the BusyLabel - * with the created BusyPainter. - * - * @return the busyPainter - * - * @see #createBusyPainter(Dimension) - * @see #initPainter(Dimension) - */ - public final BusyPainter getBusyPainter() { - if (null == busyPainter) { - Dimension prefSize = getPreferredSize(); - - busyPainter = createBusyPainter((prefSize.width == 0 && prefSize.height == 0 && !isPreferredSizeSet()) ? null : prefSize); - - if (null != busyPainter) { - if (!isPreferredSizeSet() && (null == prefSize || prefSize.width == 0 || prefSize.height == 0)) { - Rectangle rt = busyPainter.getTrajectory().getBounds(); - Rectangle rp = busyPainter.getPointShape().getBounds(); - int max = Math.max(rp.width, rp.height); - prefSize = new Dimension(rt.width + max, rt.height + max); - } - - initPainter(prefSize); - } - } - return busyPainter; - } - - /** - * @param busyPainter the busyPainter to set - */ - public final void setBusyPainter(BusyPainter busyPainter) { - this.busyPainter = busyPainter; - initPainter(new Dimension(getIcon().getIconWidth(), getIcon().getIconHeight())); - } - - /** - * @return the delay - */ - public int getDelay() { - return delay; - } - - /** - * @param delay the delay to set - */ - public void setDelay(int delay) { - int old = getDelay(); - this.delay = delay; - if (old != getDelay()) { - if (busy != null && busy.isRunning()) { - busy.setDelay(getDelay()); - } - firePropertyChange("delay", old, getDelay()); - } - } - //------------------------------------------------------------- UI Logic - - /** - * Notification from the UIManager that the L&F has changed. - * Replaces the current UI object with the latest version from the - * UIManager. - * - * @see javax.swing.JComponent#updateUI - */ - @Override - public void updateUI() { - setUI((LabelUI) LookAndFeelAddons.getUI(this, BusyLabelUI.class)); - } - - /** - * Returns the name of the L&F class that renders this component. - * - * @return the string {@link #uiClassID} - * @see javax.swing.JComponent#getUIClassID - * @see javax.swing.UIDefaults#getUI - */ - @Override - public String getUIClassID() { - return uiClassID; - } - - -} - diff --git a/src/main/java/org/jdesktop/swingx/JXButton.java b/src/main/java/org/jdesktop/swingx/JXButton.java deleted file mode 100644 index cb33bdf5e3..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXButton.java +++ /dev/null @@ -1,736 +0,0 @@ -/* - * $Id: JXButton.java 4257 2012-11-14 17:08:20Z kschaefe $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.graphics.FilterComposite; -import org.jdesktop.swingx.painter.AbstractPainter; -import org.jdesktop.swingx.painter.Painter; -import org.jdesktop.swingx.painter.PainterPaint; -import org.jdesktop.swingx.util.GraphicsUtilities; -import org.jdesktop.swingx.util.PaintUtils; - -import javax.swing.*; -import javax.swing.plaf.basic.BasicGraphicsUtils; -import java.awt.*; -import java.awt.image.BufferedImage; -import java.awt.image.BufferedImageOp; - -/** - *

                A {@link Painter} enabled subclass of {@link JButton}. - * This class supports setting the foreground and background painters of the button separately.

                - * - *

                For example, if you wanted to blur just the text on the button, and let everything else be - * handled by the UI delegate for your look and feel, then you could: - *

                
                - *  JXButton b = new JXButton("Execute");
                - *  AbstractPainter fgPainter = (AbstractPainter)b.getForegroundPainter();
                - *  StackBlurFilter filter = new StackBlurFilter();
                - *  fgPainter.setFilters(filter);
                - * 
                - * - *

                If either the foreground painter or the background painter is set, - * then super.paintComponent() is not called. By setting both the foreground and background - * painters to null, you get exactly the same painting behavior as JButton.

                - * - * @author rbair - * @author rah003 - * @author Jan Stola - * @author Karl George Schaefer - */ -@JavaBean -@SuppressWarnings({ "nls", "serial" }) -public class JXButton extends JButton implements BackgroundPaintable { - private class BackgroundButton extends JButton { - - /** - * {@inheritDoc} - */ - @Override - public boolean isDefaultButton() { - return JXButton.this.isDefaultButton(); - } - - /** - * {@inheritDoc} - */ - @Override - public Icon getDisabledIcon() { - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public Icon getDisabledSelectedIcon() { - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public int getDisplayedMnemonicIndex() { - return -1; - } - - /** - * {@inheritDoc} - */ - @Override - public int getHorizontalAlignment() { - return JXButton.this.getHorizontalAlignment(); - } - - /** - * {@inheritDoc} - */ - @Override - public int getHorizontalTextPosition() { - return JXButton.this.getHorizontalTextPosition(); - } - - /** - * {@inheritDoc} - */ - @Override - public Icon getIcon() { - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public int getIconTextGap() { - return JXButton.this.getIconTextGap(); - } - - /** - * {@inheritDoc} - */ - @Override - public Insets getMargin() { - return JXButton.this.getMargin(); - } - - /** - * {@inheritDoc} - */ - @Override - public int getMnemonic() { - return -1; - } - - /** - * {@inheritDoc} - */ - @Override - public ButtonModel getModel() { - return JXButton.this.getModel(); - } - - /** - * {@inheritDoc} - */ - @Override - public Icon getPressedIcon() { - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public Icon getRolloverIcon() { - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public Icon getRolloverSelectedIcon() { - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public Icon getSelectedIcon() { - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public String getText() { - return ""; - } - - /** - * {@inheritDoc} - */ - @Override - public int getVerticalAlignment() { - return JXButton.this.getVerticalAlignment(); - } - - /** - * {@inheritDoc} - */ - @Override - public int getVerticalTextPosition() { - return JXButton.this.getVerticalTextPosition(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isBorderPainted() { - return JXButton.this.isBorderPainted(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isContentAreaFilled() { - return JXButton.this.isContentAreaFilled(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isFocusPainted() { - return false; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isRolloverEnabled() { - return JXButton.this.isRolloverEnabled(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isSelected() { - return JXButton.this.isSelected(); - } - - } - - private class ForegroundButton extends JButton { - @Override - public Font getFont() { - return JXButton.this.getFont(); - } - - /** - * {@inheritDoc} - */ - @Override - public Color getForeground() { - if (fgPainter == null) { - return JXButton.this.getForeground(); - } - - return PaintUtils.setAlpha(JXButton.this.getForeground(), 0); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isDefaultButton() { - return JXButton.this.isDefaultButton(); - } - - /** - * {@inheritDoc} - */ - @Override - public Icon getDisabledIcon() { - return JXButton.this.getDisabledIcon(); - } - - /** - * {@inheritDoc} - */ - @Override - public Icon getDisabledSelectedIcon() { - return JXButton.this.getDisabledSelectedIcon(); - } - - /** - * {@inheritDoc} - */ - @Override - public int getDisplayedMnemonicIndex() { - return JXButton.this.getDisplayedMnemonicIndex(); - } - - /** - * {@inheritDoc} - */ - @Override - public int getHorizontalAlignment() { - return JXButton.this.getHorizontalAlignment(); - } - - /** - * {@inheritDoc} - */ - @Override - public int getHorizontalTextPosition() { - return JXButton.this.getHorizontalTextPosition(); - } - - /** - * {@inheritDoc} - */ - @Override - public Icon getIcon() { - return JXButton.this.getIcon(); - } - - /** - * {@inheritDoc} - */ - @Override - public int getIconTextGap() { - return JXButton.this.getIconTextGap(); - } - - /** - * {@inheritDoc} - */ - @Override - public Insets getMargin() { - return JXButton.this.getMargin(); - } - - /** - * {@inheritDoc} - */ - @Override - public int getMnemonic() { - return JXButton.this.getMnemonic(); - } - - /** - * {@inheritDoc} - */ - @Override - public ButtonModel getModel() { - return JXButton.this.getModel(); - } - - /** - * {@inheritDoc} - */ - @Override - public Icon getPressedIcon() { - return JXButton.this.getPressedIcon(); - } - - /** - * {@inheritDoc} - */ - @Override - public Icon getRolloverIcon() { - return JXButton.this.getRolloverIcon(); - } - - /** - * {@inheritDoc} - */ - @Override - public Icon getRolloverSelectedIcon() { - return JXButton.this.getRolloverSelectedIcon(); - } - - /** - * {@inheritDoc} - */ - @Override - public Icon getSelectedIcon() { - return JXButton.this.getSelectedIcon(); - } - - /** - * {@inheritDoc} - */ - @Override - public String getText() { - return JXButton.this.getText(); - } - - /** - * {@inheritDoc} - */ - @Override - public int getVerticalAlignment() { - return JXButton.this.getVerticalAlignment(); - } - - /** - * {@inheritDoc} - */ - @Override - public int getVerticalTextPosition() { - return JXButton.this.getVerticalTextPosition(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isBorderPainted() { - return JXButton.this.isBorderPainted(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isContentAreaFilled() { - return false; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean hasFocus() { - return JXButton.this.hasFocus(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isFocusPainted() { - return JXButton.this.isFocusPainted(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isRolloverEnabled() { - return JXButton.this.isRolloverEnabled(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isSelected() { - return JXButton.this.isSelected(); - } - } - - private ForegroundButton fgStamp; - @SuppressWarnings("rawtypes") - private Painter fgPainter; - @SuppressWarnings("rawtypes") - private PainterPaint fgPaint; - private BackgroundButton bgStamp; - @SuppressWarnings("rawtypes") - private Painter bgPainter; - - private boolean paintBorderInsets = true; - - private Rectangle viewRect = new Rectangle(); - private Rectangle textRect = new Rectangle(); - private Rectangle iconRect = new Rectangle(); - - /** - * Creates a button with no set text or icon. - */ - public JXButton() { - init(); - } - - /** - * Creates a button with text. - * - * @param text - * the text of the button - */ - public JXButton(String text) { - super(text); - init(); - } - - /** - * Creates a button where properties are taken from the {@code Action} supplied. - * - * @param a - * the {@code Action} used to specify the new button - */ - public JXButton(Action a) { - super(a); - init(); - } - - /** - * Creates a button with an icon. - * - * @param icon - * the Icon image to display on the button - */ - public JXButton(Icon icon) { - super(icon); - init(); - } - - /** - * Creates a button with initial text and an icon. - * - * @param text - * the text of the button - * @param icon - * the Icon image to display on the button - */ - public JXButton(String text, Icon icon) { - super(text, icon); - init(); - } - - private void init() { - fgStamp = new ForegroundButton(); - } - - /** - * {@inheritDoc} - */ - @Override - @SuppressWarnings("rawtypes") - public Painter getBackgroundPainter() { - return bgPainter; - } - - /** - * {@inheritDoc} - */ - @Override - @SuppressWarnings("rawtypes") - public void setBackgroundPainter(Painter p) { - Painter old = getBackgroundPainter(); - this.bgPainter = p; - firePropertyChange("backgroundPainter", old, getBackgroundPainter()); - repaint(); - } - - /** - * @return the foreground painter for this button - */ - @SuppressWarnings("rawtypes") - public Painter getForegroundPainter() { - return fgPainter; - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - public void setForegroundPainter(Painter p) { - Painter old = getForegroundPainter(); - this.fgPainter = p; - - if (fgPainter == null) { - fgPaint = null; - } else { - fgPaint = new PainterPaint(fgPainter, this); - - if (bgStamp == null) { - bgStamp = new BackgroundButton(); - } - } - - firePropertyChange("foregroundPainter", old, getForegroundPainter()); - repaint(); - } - - /** - * Returns true if the background painter should paint where the border is - * or false if it should only paint inside the border. This property is - * true by default. This property affects the width, height, - * and initial transform passed to the background painter. - */ - @Override - public boolean isPaintBorderInsets() { - return paintBorderInsets; - } - - /** - * Sets the paintBorderInsets property. - * Set to true if the background painter should paint where the border is - * or false if it should only paint inside the border. This property is true by default. - * This property affects the width, height, - * and initial transform passed to the background painter. - * - * This is a bound property. - */ - @Override - public void setPaintBorderInsets(boolean paintBorderInsets) { - boolean old = this.isPaintBorderInsets(); - this.paintBorderInsets = paintBorderInsets; - firePropertyChange("paintBorderInsets", old, isPaintBorderInsets()); - } - - /** - * {@inheritDoc} - */ - @Override - public Dimension getPreferredSize() { - if (getComponentCount() == 1 && getComponent(0) instanceof CellRendererPane) { - return BasicGraphicsUtils.getPreferredButtonSize(fgStamp, getIconTextGap()); - } - - return super.getPreferredSize(); - } - - /** - * {@inheritDoc} - */ - @Override - protected void paintComponent(Graphics g) { - if (fgPainter == null && bgPainter == null) { - super.paintComponent(g); - } else { - if (fgPainter == null) { - Graphics2D g2d = (Graphics2D) g.create(); - - try{ - paintWithoutForegroundPainter(g2d); - } finally { - g2d.dispose(); - } - } else if (fgPainter instanceof AbstractPainter && ((AbstractPainter) fgPainter).getFilters().length > 0) { - paintWithForegroundPainterWithFilters(g); - } else { - Graphics2D g2d = (Graphics2D) g.create(); - - try { - paintWithForegroundPainterWithoutFilters(g2d); - } finally { - g2d.dispose(); - } - } - } - } - - private void paintWithoutForegroundPainter(Graphics2D g2d) { - if (bgPainter == null) { - SwingUtilities.paintComponent(g2d, bgStamp, this, 0, 0, getWidth(), getHeight()); - } else { - SwingXUtilities.paintBackground(this, g2d); - } - - SwingUtilities.paintComponent(g2d, fgStamp, this, 0, 0, getWidth(), getHeight()); - } - - private void paintWithForegroundPainterWithoutFilters(Graphics2D g2d) { - paintWithoutForegroundPainter(g2d); - - if (getText() != null && !getText().isEmpty()) { - Insets i = getInsets(); - viewRect.x = i.left; - viewRect.y = i.top; - viewRect.width = getWidth() - (i.right + viewRect.x); - viewRect.height = getHeight() - (i.bottom + viewRect.y); - - textRect.x = textRect.y = textRect.width = textRect.height = 0; - iconRect.x = iconRect.y = iconRect.width = iconRect.height = 0; - - // layout the text and icon - String text = SwingUtilities.layoutCompoundLabel( - this, g2d.getFontMetrics(), getText(), getIcon(), - getVerticalAlignment(), getHorizontalAlignment(), - getVerticalTextPosition(), getHorizontalTextPosition(), - viewRect, iconRect, textRect, - getText() == null ? 0 : getIconTextGap()); - - if (!isPaintBorderInsets()) { - g2d.translate(i.left, i.top); - } - - g2d.setPaint(fgPaint); - BasicGraphicsUtils.drawStringUnderlineCharAt(g2d, text, getDisplayedMnemonicIndex(), - textRect.x, textRect.y + g2d.getFontMetrics().getAscent()); - } - } - - private void paintWithForegroundPainterWithFilters(Graphics g) { - BufferedImage im = GraphicsUtilities.createCompatibleTranslucentImage(getWidth(), getHeight()); - Graphics2D g2d = im.createGraphics(); - - try { - Graphics gfx = getComponentGraphics(g2d); - assert gfx == g2d; - - paintWithForegroundPainterWithoutFilters(g2d); - } finally { - g2d.dispose(); - } - - Graphics2D filtered = (Graphics2D) g.create(); - - try { - for (BufferedImageOp filter : ((AbstractPainter) fgPainter).getFilters()) { - filtered.setComposite(new FilterComposite(filtered.getComposite(), filter)); - } - - filtered.drawImage(im, 0, 0, this); - } finally { - filtered.dispose(); - } - } - - /** - * Notification from the UIManager that the L&F has changed. - * Replaces the current UI object with the latest version from the UIManager. - * - * @see javax.swing.JComponent#updateUI - */ - @Override - public void updateUI() { - super.updateUI(); - - if (bgStamp != null) { - bgStamp.updateUI(); - } - - if (fgStamp != null) { - fgStamp.updateUI(); - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/JXCollapsiblePane.java b/src/main/java/org/jdesktop/swingx/JXCollapsiblePane.java deleted file mode 100644 index 382db15ba5..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXCollapsiblePane.java +++ /dev/null @@ -1,1143 +0,0 @@ -/* - * $Id: JXCollapsiblePane.java 4265 2012-11-28 18:15:57Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.util.GraphicsUtilities; - -import javax.swing.*; -import javax.swing.border.Border; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.image.BufferedImage; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; - -/** - * JXCollapsiblePane provides a component which can collapse or - * expand its content area with animation and fade in/fade out effects. - * It also acts as a standard container for other Swing components. - *

                - * The {@code JXCollapsiblePane} has a "content pane" that actually holds the - * displayed contents. This means that colors, fonts, and other display - * configuration items must be set on the content pane. - * - *

                
                - * // to set the font
                - * collapsiblePane.getContentPane().setFont(font);
                - * // to set the background color
                - * collapsiblePane.getContentPane().setBackground(Color.RED);
                - * 
                - * 
                - * - * For convenience, the {@code add} and {@code remove} methods forward to the - * content pane. The following code shows to ways to add a child to the - * content pane. - * - *
                
                - * // to add a child
                - * collapsiblePane.getContentPane().add(component);
                - * // to add a child
                - * collapsiblePane.add(component);
                - * 
                - * 
                - * - * To set the content pane, do not use {@code add}, use {@link #setContentPane(Container)}. - * - *

                - * In this example, the JXCollapsiblePane is used to build - * a Search pane which can be shown and hidden on demand. - * - *

                - * 
                - * JXCollapsiblePane cp = new JXCollapsiblePane();
                - *
                - * // JXCollapsiblePane can be used like any other container
                - * cp.setLayout(new BorderLayout());
                - *
                - * // the Controls panel with a textfield to filter the tree
                - * JPanel controls = new JPanel(new FlowLayout(FlowLayout.LEFT, 4, 0));
                - * controls.add(new JLabel("Search:"));
                - * controls.add(new JTextField(10));
                - * controls.add(new JButton("Refresh"));
                - * controls.setBorder(new TitledBorder("Filters"));
                - * cp.add("Center", controls);
                - *
                - * JXFrame frame = new JXFrame();
                - * frame.setLayout(new BorderLayout());
                - *
                - * // Put the "Controls" first
                - * frame.add("North", cp);
                - *
                - * // Then the tree - we assume the Controls would somehow filter the tree
                - * JScrollPane scroll = new JScrollPane(new JTree());
                - * frame.add("Center", scroll);
                - *
                - * // Show/hide the "Controls"
                - * JButton toggle = new JButton(cp.getActionMap().get(JXCollapsiblePane.TOGGLE_ACTION));
                - * toggle.setText("Show/Hide Search Panel");
                - * frame.add("South", toggle);
                - *
                - * frame.pack();
                - * frame.setVisible(true);
                - * 
                - * 
                - * - *

                - * The JXCollapsiblePane has a default toggle action registered - * under the name {@link #TOGGLE_ACTION}. Bind this action to a button and - * pressing the button will automatically toggle the pane between expanded - * and collapsed states. Additionally, you can define the icons to use through - * the {@link #EXPAND_ICON} and {@link #COLLAPSE_ICON} properties on the action. - * Example - *

                - * 
                - * // get the built-in toggle action
                - * Action toggleAction = collapsible.getActionMap().
                - *   get(JXCollapsiblePane.TOGGLE_ACTION);
                - *
                - * // use the collapse/expand icons from the JTree UI
                - * toggleAction.putValue(JXCollapsiblePane.COLLAPSE_ICON,
                - *                       UIManager.getIcon("Tree.expandedIcon"));
                - * toggleAction.putValue(JXCollapsiblePane.EXPAND_ICON,
                - *                       UIManager.getIcon("Tree.collapsedIcon"));
                - * 
                - * 
                - * - *

                - * Note: JXCollapsiblePane requires its parent container to have a - * {@link LayoutManager} using {@link #getPreferredSize()} when - * calculating its layout (example {@link VerticalLayout}, - * {@link BorderLayout}). - * - * @javabean.attribute - * name="isContainer" - * value="Boolean.TRUE" - * rtexpr="true" - * - * @javabean.attribute - * name="containerDelegate" - * value="getContentPane" - * - * @javabean.class - * name="JXCollapsiblePane" - * shortDescription="A pane which hides its content with an animation." - * stopClass="java.awt.Component" - * - * @author rbair (from the JDNC project) - * @author Frederic Lavigne - * @author Karl George Schaefer - */ -@JavaBean -public class JXCollapsiblePane extends JXPanel { - /** - * The direction defines how the collapsible pane will collapse. The - * constant names were designed by choosing a fixed point and then - * determining the collapsing direction from that fixed point. This means - * {@code RIGHT} expands to the right and this is probably the best - * expansion for a component in {@link BorderLayout#EAST}. - */ - public enum Direction { - /** - * Collapses left. Suitable for {@link BorderLayout#WEST}. - */ - LEFT(false), - - /** - * Collapses right. Suitable for {@link BorderLayout#EAST}. - */ - RIGHT(false), - - /** - * Collapses up. Suitable for {@link BorderLayout#NORTH}. - */ - UP(true), - - /** - * Collapses down. Suitable for {@link BorderLayout#SOUTH}. - */ - DOWN(true), - - /** - * Collapses toward the leading edge. Suitable for {@link BorderLayout#LINE_START}. - */ - LEADING(false) { - @Override - Direction getFixedDirection(ComponentOrientation co) { - return co.isLeftToRight() ? LEFT : RIGHT; - } - }, - - /** - * Collapses toward the trailing edge. Suitable for {@link BorderLayout#LINE_END}. - */ - TRAILING(false) { - @Override - Direction getFixedDirection(ComponentOrientation co) { - return co.isLeftToRight() ? RIGHT : LEFT; - } - }, - - /** - * Collapses toward the starting edge. Suitable for {@link BorderLayout#PAGE_START}. - */ - START(true) { - @Override - Direction getFixedDirection(ComponentOrientation co) { - return UP; - } - }, - - /** - * Collapses toward the ending edge. Suitable for {@link BorderLayout#PAGE_END}. - */ - END(true) { - @Override - Direction getFixedDirection(ComponentOrientation co) { - return DOWN; - } - }, - ; - - private final boolean vertical; - - private Direction(boolean vertical) { - this.vertical = vertical; - } - - /** - * Gets the orientation for this direction. - * - * @return {@code true} if the direction is vertical, {@code false} - * otherwise - */ - public boolean isVertical() { - return vertical; - } - - /** - * Gets the fixed direction equivalent to this direction for the specified orientation. - * - * @param co - * the component's orientation - * @return the fixed direction corresponding to the component's orietnation - */ - Direction getFixedDirection(ComponentOrientation co) { - return this; - } - } - - /** - * Toggles the JXCollapsiblePane state and updates its icon based on the - * JXCollapsiblePane "collapsed" status. - */ - private class ToggleAction extends AbstractAction implements - PropertyChangeListener { - public ToggleAction() { - super(TOGGLE_ACTION); - // the action must track the collapsed status of the pane to update its icon - JXCollapsiblePane.this.addPropertyChangeListener("collapsed", this); - } - - @Override - public void putValue(String key, Object newValue) { - super.putValue(key, newValue); - if (EXPAND_ICON.equals(key) || COLLAPSE_ICON.equals(key)) { - updateIcon(); - } - } - - @Override - public void actionPerformed(ActionEvent e) { - setCollapsed(!isCollapsed()); - } - - @Override - public void propertyChange(PropertyChangeEvent evt) { - updateIcon(); - } - - void updateIcon() { - if (isCollapsed()) { - putValue(SMALL_ICON, getValue(EXPAND_ICON)); - } else { - putValue(SMALL_ICON, getValue(COLLAPSE_ICON)); - } - } - } - - /** - * JXCollapsible has a built-in toggle action which can be bound to buttons. - * Accesses the action through - * collapsiblePane.getActionMap().get(JXCollapsiblePane.TOGGLE_ACTION). - */ - public final static String TOGGLE_ACTION = "toggle"; - - /** - * The icon used by the "toggle" action when the JXCollapsiblePane is - * expanded, i.e the icon which indicates the pane can be collapsed. - */ - public final static String COLLAPSE_ICON = "collapseIcon"; - - /** - * The icon used by the "toggle" action when the JXCollapsiblePane is - * collapsed, i.e the icon which indicates the pane can be expanded. - */ - public final static String EXPAND_ICON = "expandIcon"; - - /** - * Indicates whether the component is collapsed or expanded - */ - private boolean collapsed = false; - - /** - * Defines the orientation of the component. - */ - private Direction direction = Direction.UP; - - /** - * Timer used for doing the transparency animation (fade-in) - */ - private Timer animateTimer; - private AnimationListener animator; - private int currentDimension = -1; - private WrapperContainer wrapper; - private boolean useAnimation = true; - private AnimationParams animationParams; - private boolean collapseFiringState; - - /** - * Constructs a new JXCollapsiblePane with a {@link JXPanel} as content pane - * and a vertical {@link VerticalLayout} with a gap of 2 pixels as layout - * manager and a vertical orientation. - */ - public JXCollapsiblePane() { - this(Direction.UP); - } - - /** - * Constructs a new JXCollapsiblePane with a {@link JXPanel} as content pane and the specified - * direction. - * - * @param direction - * the direction to collapse the container - */ - public JXCollapsiblePane(Direction direction) { - super.setLayout(new BorderLayout()); - this.direction = direction; - animator = new AnimationListener(); - setAnimationParams(new AnimationParams(30, 8, 0.01f, 1.0f)); - - setContentPane(createContentPane()); - setDirection(direction); - - // add an action to automatically toggle the state of the pane - getActionMap().put(TOGGLE_ACTION, new ToggleAction()); - } - - /** - * Creates the content pane used by this collapsible pane. - * - * @return the content pane - */ - protected Container createContentPane() { - return new JXPanel(); - } - - /** - * @return the content pane - */ - public Container getContentPane() { - if (wrapper == null) { - return null; - } - - return (Container) wrapper.getView(); - } - - /** - * Sets the content pane of this JXCollapsiblePane. The {@code contentPanel} - * should implement {@code Scrollable} and return {@code true} from - * {@link Scrollable#getScrollableTracksViewportHeight()} and - * {@link Scrollable#getScrollableTracksViewportWidth()}. If the content - * pane fails to do so and a {@code JScrollPane} is added as a child, it is - * likely that the scroll pane will never correctly size. While it is not - * strictly necessary to implement {@code Scrollable} in this way, the - * default content pane does so. - * - * @param contentPanel - * the container delegate used to hold all of the contents - * for this collapsible pane - * @throws IllegalArgumentException - * if contentPanel is null - */ - public void setContentPane(Container contentPanel) { - if (contentPanel == null) { - throw new IllegalArgumentException("Content pane can't be null"); - } - - if (wrapper != null) { - //these next two lines are as they are because if I try to remove - //the "wrapper" component directly, then super.remove(comp) ends up - //calling remove(int), which is overridden in this class, leading to - //improper behavior. - assert super.getComponent(0) == wrapper; - super.remove(0); - } - - wrapper = new WrapperContainer(contentPanel); - wrapper.collapsedState = isCollapsed(); - wrapper.getView().setVisible(!wrapper.collapsedState); - super.addImpl(wrapper, BorderLayout.CENTER, -1); - } - - /** - * Overridden to redirect call to the content pane. - */ - @Override - public void setLayout(LayoutManager mgr) { - // wrapper can be null when setLayout is called by "super()" constructor - if (wrapper != null) { - getContentPane().setLayout(mgr); - } - } - - /** - * Overridden to redirect call to the content pane. - */ - @Override - protected void addImpl(Component comp, Object constraints, int index) { - getContentPane().add(comp, constraints, index); - } - - /** - * Overridden to redirect call to the content pane - */ - @Override - public void remove(Component comp) { - getContentPane().remove(comp); - } - - /** - * Overridden to redirect call to the content pane. - */ - @Override - public void remove(int index) { - getContentPane().remove(index); - } - - /** - * Overridden to redirect call to the content pane. - */ - @Override - public void removeAll() { - getContentPane().removeAll(); - } - - /** - * If true, enables the animation when pane is collapsed/expanded. If false, - * animation is turned off. - * - *

                - * When animated, the JXCollapsiblePane will progressively - * reduce (when collapsing) or enlarge (when expanding) the height of its - * content area until it becomes 0 or until it reaches the preferred height of - * the components it contains. The transparency of the content area will also - * change during the animation. - * - *

                - * If not animated, the JXCollapsiblePane will simply hide - * (collapsing) or show (expanding) its content area. - * - * @param animated - * @javabean.property bound="true" preferred="true" - */ - public void setAnimated(boolean animated) { - if (animated != useAnimation) { - useAnimation = animated; - - if (!animated) { - if (animateTimer.isRunning()) { - //TODO should we listen for animation state change? - //yes, but we're best off creating a UI delegate for these changes - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - currentDimension = -1; - } - }); - } else { - currentDimension = -1; - } - } - firePropertyChange("animated", !useAnimation, useAnimation); - } - } - - /** - * @return true if the pane is animated, false otherwise - * @see #setAnimated(boolean) - */ - public boolean isAnimated() { - return useAnimation; - } - - /** - * {@inheritDoc} - */ - @Override - public void setComponentOrientation(ComponentOrientation o) { - if (animateTimer.isRunning()) { - throw new IllegalStateException("cannot be change component orientation while collapsing."); - } - - super.setComponentOrientation(o); - } - - /** - * Changes the direction of this collapsible pane. Doing so changes the - * layout of the underlying content pane. If the chosen direction is - * vertical, a vertical layout with a gap of 2 pixels is chosen. Otherwise, - * a horizontal layout with a gap of 2 pixels is chosen. - * - * @see #getDirection() - * @param direction the new {@link Direction} for this collapsible pane - * @throws IllegalStateException when this method is called while a - * collapsing/restore operation is running - * @javabean.property - * bound="true" - * preferred="true" - */ - public void setDirection(Direction direction) { - if (animateTimer.isRunning()) { - throw new IllegalStateException("cannot be change direction while collapsing."); - } - - Direction oldValue = getDirection(); - this.direction = direction; - - if (direction.isVertical()) { - getContentPane().setLayout(new VerticalLayout(2)); - } else { - getContentPane().setLayout(new HorizontalLayout(2)); - } - - firePropertyChange("direction", oldValue, getDirection()); - } - - /** - * @return the current {@link Direction}. - * @see #setDirection(Direction) - */ - public Direction getDirection() { - return direction; - } - - /** - * @return true if the pane is collapsed, false if expanded - */ - public boolean isCollapsed() { - return collapsed; - } - - /** - * Expands or collapses this JXCollapsiblePane. - * - *

                - * If the component is collapsed and val is false, then this - * call expands the JXCollapsiblePane, such that the entire JXCollapsiblePane - * will be visible. If {@link #isAnimated()} returns true, the expansion will - * be accompanied by an animation. - * - *

                - * However, if the component is expanded and val is true, then - * this call collapses the JXCollapsiblePane, such that the entire - * JXCollapsiblePane will be invisible. If {@link #isAnimated()} returns true, - * the collapse will be accompanied by an animation. - * - *

                - * As of SwingX 1.6.3, JXCollapsiblePane only fires property change events when - * the component's state is accurate. This means that animated collapsible - * pane's only fire events once the animation is complete. - * - * @see #isAnimated() - * @see #setAnimated(boolean) - * @javabean.property - * bound="true" - * preferred="true" - */ - public void setCollapsed(boolean val) { - boolean oldValue = isCollapsed(); - this.collapsed = val; - - if (isAnimated() && isShowing()) { - if (oldValue == isCollapsed()) { - return; - } - - // this ensures that if the user reverses the animation - // before completion that no property change is fired - if (!animateTimer.isRunning()) { - collapseFiringState = oldValue; - } - - if (oldValue) { - int dimension = direction.isVertical() ? wrapper.getHeight() : wrapper.getWidth(); - int preferredDimension = direction.isVertical() ? getContentPane() - .getPreferredSize().height : getContentPane().getPreferredSize().width; - int delta = Math.max(8, preferredDimension / 10); - - setAnimationParams(new AnimationParams(30, delta, 0.01f, 1.0f)); - animator.reinit(dimension, preferredDimension); - wrapper.getView().setVisible(true); - } else { - int dimension = direction.isVertical() ? wrapper.getHeight() : wrapper.getWidth(); - setAnimationParams(new AnimationParams(30, Math.max(8, dimension / 10), 1.0f, 0.01f)); - animator.reinit(dimension, 0); - } - - animateTimer.start(); - } else { - wrapper.collapsedState = isCollapsed(); - wrapper.getView().setVisible(!isCollapsed()); - revalidate(); - repaint(); - - firePropertyChange("collapsed", oldValue, isCollapsed()); - } - } - - /** - * {@inheritDoc} - */ - @Override - public Border getBorder() { - if (getContentPane() instanceof JComponent) { - return ((JComponent) getContentPane()).getBorder(); - } - - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public void setBorder(Border border) { - if (getContentPane() instanceof JComponent) { - ((JComponent) getContentPane()).setBorder(border); - } - } - - /** - * {@inheritDoc} - *

                - * Internals of JXCollasiplePane are designed to be opaque because some Look and Feel - * implementations having painting issues otherwise. JXCollapsiblePane and its internals will - * respect {@code setOpaque}, calling this method will not only update the collapsible pane, but - * also all internals. This method does not modify the {@link #getContentPane() content pane}, - * as it is not considered an internal. - */ - @Override - public void setOpaque(boolean opaque) { - super.setOpaque(opaque); - - if (wrapper != null) { - wrapper.setOpaque(opaque); - } - } - - /** - * A collapsible pane always returns its preferred size for the minimum size - * to ensure that the collapsing happens correctly. - *

                - * To query the minimum size of the contents user {@code - * getContentPane().getMinimumSize()}. - * - * @return the preferred size of the component - */ - @Override - public Dimension getMinimumSize() { - return getPreferredSize(); - } - - /** - * Forwards to the content pane. - * - * @param minimumSize - * the size to set on the content pane - */ - @Override - public void setMinimumSize(Dimension minimumSize) { - getContentPane().setMinimumSize(minimumSize); - } - - /** - * The critical part of the animation of this JXCollapsiblePane - * relies on the calculation of its preferred size. During the animation, its - * preferred size (specially its height) will change, when expanding, from 0 - * to the preferred size of the content pane, and the reverse when collapsing. - * - * @return this component preferred size - */ - @Override - public Dimension getPreferredSize() { - /* - * The preferred size is calculated based on the current position of the - * component in its animation sequence. If the Component is expanded, then - * the preferred size will be the preferred size of the top component plus - * the preferred size of the embedded content container.

                However, if the - * scroll up is in any state of animation, the height component of the - * preferred size will be the current height of the component (as contained - * in the currentDimension variable and when orientation is VERTICAL, otherwise - * the same applies to the width) - */ - Dimension dim = getContentPane().getPreferredSize(); - if (currentDimension != -1) { - if (direction.isVertical()) { - dim.height = currentDimension; - } else { - dim.width = currentDimension; - } - } else if(wrapper.collapsedState) { - if (direction.isVertical()) { - dim.height = 0; - } else { - dim.width = 0; - } - } - return dim; - } - - @Override - public void setPreferredSize(Dimension preferredSize) { - getContentPane().setPreferredSize(preferredSize); - } - - /** - * Sets the parameters controlling the animation - * - * @param params - * @throws IllegalArgumentException - * if params is null - */ - private void setAnimationParams(AnimationParams params) { - if (params == null) { throw new IllegalArgumentException( - "params can't be null"); } - if (animateTimer != null) { - animateTimer.stop(); - } - animationParams = params; - animateTimer = new Timer(animationParams.waitTime, animator); - animateTimer.setInitialDelay(0); - } - - /** - * Tagging interface for containers in a JXCollapsiblePane hierarchy who needs - * to be revalidated (invalidate/validate/repaint) when the pane is expanding - * or collapsing. Usually validating only the parent of the JXCollapsiblePane - * is enough but there might be cases where the parent's parent must be - * validated. - */ - public static interface CollapsiblePaneContainer { - Container getValidatingContainer(); - } - - /** - * Parameters controlling the animations - */ - private static class AnimationParams { - final int waitTime; - final int delta; - final float alphaStart; - final float alphaEnd; - - /** - * @param waitTime - * the amount of time in milliseconds to wait between calls to the - * animation thread - * @param delta - * the delta, in the direction as specified by the orientation, - * to inc/dec the size of the scroll up by - * @param alphaStart - * the starting alpha transparency level - * @param alphaEnd - * the ending alpha transparency level - */ - public AnimationParams(int waitTime, int delta, float alphaStart, - float alphaEnd) { - this.waitTime = waitTime; - this.delta = delta; - this.alphaStart = alphaStart; - this.alphaEnd = alphaEnd; - } - } - - /** - * This class actual provides the animation support for scrolling up/down this - * component. This listener is called whenever the animateTimer fires off. It - * fires off in response to scroll up/down requests. This listener is - * responsible for modifying the size of the content container and causing it - * to be repainted. - * - * @author Richard Bair - */ - private final class AnimationListener implements ActionListener { - /** - * Mutex used to ensure that the startDimension/finalDimension are not changed - * during a repaint operation. - */ - private final Object ANIMATION_MUTEX = "Animation Synchronization Mutex"; - /** - * This is the starting dimension when animating. If > finalDimension, then the - * animation is going to be to scroll up the component. If it is less than - * finalDimension, then the animation will scroll down the component. - */ - private int startDimension = 0; - /** - * This is the final dimension that the content container is going to be when - * scrolling is finished. - */ - private int finalDimension = 0; - /** - * The current alpha setting used during "animation" (fade-in/fade-out) - */ - @SuppressWarnings({"FieldCanBeLocal"}) - private float animateAlpha = 1.0f; - - @Override - public void actionPerformed(ActionEvent e) { - /* - * Pre-1) If startDimension == finalDimension, then we're done so stop the timer - * 1) Calculate whether we're contracting or expanding. 2) Calculate the - * delta (which is either positive or negative, depending on the results - * of (1)) 3) Calculate the alpha value 4) Resize the ContentContainer 5) - * Revalidate/Repaint the content container - */ - synchronized (ANIMATION_MUTEX) { - if (startDimension == finalDimension) { - animateTimer.stop(); - animateAlpha = animationParams.alphaEnd; - // keep the content pane hidden when it is collapsed, other it may - // still receive focus. - if (finalDimension > 0) { - currentDimension = -1; - wrapper.collapsedState = false; - validate(); - JXCollapsiblePane.this.firePropertyChange("collapsed", collapseFiringState, false); - return; - } else { - wrapper.collapsedState = true; - wrapper.getView().setVisible(false); - JXCollapsiblePane.this.firePropertyChange("collapsed", collapseFiringState, true); - } - } - - final boolean contracting = startDimension > finalDimension; - final int delta = contracting?-1 * animationParams.delta - :animationParams.delta; - int newDimension; - if (direction.isVertical()) { - newDimension = wrapper.getHeight() + delta; - } else { - newDimension = wrapper.getWidth() + delta; - } - if (contracting) { - if (newDimension < finalDimension) { - newDimension = finalDimension; - } - } else { - if (newDimension > finalDimension) { - newDimension = finalDimension; - } - } - int dimension; - if (direction.isVertical()) { - dimension = wrapper.getView().getPreferredSize().height; - } else { - dimension = wrapper.getView().getPreferredSize().width; - } - animateAlpha = (float)newDimension / (float)dimension; - - Rectangle bounds = wrapper.getBounds(); - - if (direction.isVertical()) { - int oldHeight = bounds.height; - bounds.height = newDimension; - wrapper.setBounds(bounds); - - if (direction.getFixedDirection(getComponentOrientation()) == Direction.DOWN) { - wrapper.setViewPosition(new Point(0, wrapper.getView().getPreferredSize().height - newDimension)); - } else { - wrapper.setViewPosition(new Point(0, newDimension)); - } - - bounds = getBounds(); - bounds.height = (bounds.height - oldHeight) + newDimension; - currentDimension = bounds.height; - } else { - int oldWidth = bounds.width; - bounds.width = newDimension; - wrapper.setBounds(bounds); - - if (direction.getFixedDirection(getComponentOrientation()) == Direction.RIGHT) { - wrapper.setViewPosition(new Point(wrapper.getView().getPreferredSize().width - newDimension, 0)); - } else { - wrapper.setViewPosition(new Point(newDimension, 0)); - } - - bounds = getBounds(); - bounds.width = (bounds.width - oldWidth) + newDimension; - currentDimension = bounds.width; - } - - setBounds(bounds); - startDimension = newDimension; - - // it happens the animateAlpha goes over the alphaStart/alphaEnd range - // this code ensures it stays in bounds. This behavior is seen when - // component such as JTextComponents are used in the container. - if (contracting) { - // alphaStart > animateAlpha > alphaEnd - if (animateAlpha < animationParams.alphaEnd) { - animateAlpha = animationParams.alphaEnd; - } - if (animateAlpha > animationParams.alphaStart) { - animateAlpha = animationParams.alphaStart; - } - } else { - // alphaStart < animateAlpha < alphaEnd - if (animateAlpha > animationParams.alphaEnd) { - animateAlpha = animationParams.alphaEnd; - } - if (animateAlpha < animationParams.alphaStart) { - animateAlpha = animationParams.alphaStart; - } - } - - wrapper.setAlpha(animateAlpha); - - validate(); - } - } - - void validate() { - Container parent = SwingUtilities.getAncestorOfClass( - CollapsiblePaneContainer.class, JXCollapsiblePane.this); - if (parent != null) { - parent = ((CollapsiblePaneContainer)parent).getValidatingContainer(); - } else { - parent = getParent(); - } - - if (parent != null) { - if (parent instanceof JComponent) { - ((JComponent)parent).revalidate(); - } else { - parent.invalidate(); - } - parent.doLayout(); - parent.repaint(); - } - } - - /** - * Reinitializes the timer for scrolling up/down the component. This method - * is properly synchronized, so you may make this call regardless of whether - * the timer is currently executing or not. - * - * @param startDimension - * @param stopDimension - */ - public void reinit(int startDimension, int stopDimension) { - synchronized (ANIMATION_MUTEX) { - this.startDimension = startDimension; - this.finalDimension = stopDimension; - animateAlpha = animationParams.alphaStart; - currentDimension = -1; - } - } - } - - private final class WrapperContainer extends JViewport implements AlphaPaintable { - boolean collapsedState; - private volatile float alpha; - private boolean oldOpaque; - - public WrapperContainer(Container c) { - alpha = 1.0f; - collapsedState = false; - setView(c); - - // we must ensure the container is opaque. It is not opaque it introduces - // painting glitches specially on Linux with JDK 1.5 and GTK look and feel. - // GTK look and feel calls setOpaque(false) - if (c instanceof JComponent && !c.isOpaque()) { - ((JComponent) c).setOpaque(true); - } - } - - /** - * {@inheritDoc}

                - * - * Overridden to not have JViewPort behaviour (that is scroll the view) - * but delegate to parent scrollRectToVisible just a JComponent does.

                - */ - @Override - public void scrollRectToVisible(Rectangle aRect) { - //avoids JViewport's implementation - //by using JXCollapsiblePane's it will delegate upward - //getting any core fixes, by avoiding c&p - JXCollapsiblePane.this.scrollRectToVisible(aRect); - } - - @Override - public float getAlpha() { - return alpha; - } - - @Override - public void setAlpha(float alpha) { - if (alpha < 0f || alpha > 1f) { - throw new IllegalArgumentException("invalid alpha value " + alpha); - } - - float oldValue = getAlpha(); - this.alpha = alpha; - - if (getAlpha() < 1f) { - if (oldValue == 1) { - //it used to be 1, but now is not. Save the oldOpaque - oldOpaque = isOpaque(); - setOpaque(false); - } - } else { - //restore the oldOpaque if it was true (since opaque is false now) - if (oldOpaque) { - setOpaque(true); - } - } - - firePropertyChange("alpha", oldValue, getAlpha()); - repaint(); - } - - @Override - public boolean isInheritAlpha() { - return false; - } - - @Override - public void setInheritAlpha(boolean inheritAlpha) { - //does nothing; always false; - } - - @Override - public float getEffectiveAlpha() { - return getAlpha(); - } - - //support for Java 7 painting improvements - protected boolean isPaintingOrigin() { - return getAlpha() < 1f; - } - - /** - * Overridden paint method to take into account the alpha setting. - * - * @param g - * the Graphics context in which to paint - */ - @Override - public void paint(Graphics g) { - //short circuit painting if no transparency - if (getAlpha() == 1f) { - super.paint(g); - } else { - //the component is translucent, so we need to render to - //an intermediate image before painting - // TODO should we cache this image? repaint to same image unless size changes? - BufferedImage img = GraphicsUtilities.createCompatibleTranslucentImage(getWidth(), getHeight()); - Graphics2D gfx = img.createGraphics(); - - try { - super.paint(gfx); - } finally { - gfx.dispose(); - } - - Graphics2D g2d = (Graphics2D) g; - Composite oldComp = g2d.getComposite(); - - try { - Composite alphaComp = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, getEffectiveAlpha()); - g2d.setComposite(alphaComp); - //TODO should we cache the image? - g2d.drawImage(img, null, 0, 0); - } finally { - g2d.setComposite(oldComp); - } - } - } - } - -// TEST CASE -// public static void main(String[] args) { -// SwingUtilities.invokeLater(new Runnable() { -// public void run() { -// JFrame f = new JFrame("Test Oriented Collapsible Pane"); -// -// f.add(new JLabel("Press Ctrl+F or Ctrl+G to collapse panes."), -// BorderLayout.NORTH); -// -// JTree tree1 = new JTree(); -// tree1.setBorder(BorderFactory.createEtchedBorder()); -// f.add(tree1); -// -// JXCollapsiblePane pane = new JXCollapsiblePane(Orientation.VERTICAL); -// pane.setCollapsed(true); -// JTree tree2 = new JTree(); -// tree2.setBorder(BorderFactory.createEtchedBorder()); -// pane.add(tree2); -// f.add(pane, BorderLayout.SOUTH); -// -// pane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( -// KeyStroke.getKeyStroke("ctrl F"), -// JXCollapsiblePane.TOGGLE_ACTION); -// -// pane = new JXCollapsiblePane(Orientation.HORIZONTAL); -// JTree tree3 = new JTree(); -// pane.add(tree3); -// tree3.setBorder(BorderFactory.createEtchedBorder()); -// f.add(pane, BorderLayout.WEST); -// -// pane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( -// KeyStroke.getKeyStroke("ctrl G"), -// JXCollapsiblePane.TOGGLE_ACTION); -// -// f.setSize(640, 480); -// f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); -// f.setVisible(true); -// } -// }); -// } -} diff --git a/src/main/java/org/jdesktop/swingx/JXColorSelectionButton.java b/src/main/java/org/jdesktop/swingx/JXColorSelectionButton.java deleted file mode 100644 index c6aac5a90f..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXColorSelectionButton.java +++ /dev/null @@ -1,240 +0,0 @@ -/* - * $Id: JXColorSelectionButton.java 4082 2011-11-15 18:39:43Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx; - -import org.apache.commons.lang3.SystemUtils; -import org.jdesktop.swingx.color.EyeDropperColorChooserPanel; -import org.jdesktop.swingx.plaf.UIManagerExt; -import org.jdesktop.swingx.util.GraphicsUtilities; -import org.jdesktop.swingx.util.PaintUtils; - -import javax.imageio.ImageIO; -import javax.swing.*; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.geom.Ellipse2D; -import java.awt.image.BufferedImage; - -import static java.awt.RenderingHints.KEY_ANTIALIASING; -import static java.awt.RenderingHints.VALUE_ANTIALIAS_ON; - -/** - * A button which allows the user to select a single color. The button has a platform - * specific look. Ex: on Mac OS X it will mimic an NSColorWell. When the user - * clicks the button it will open a color chooser set to the current background - * color of the button. The new selected color will be stored in the background - * property and can be retrieved using the getBackground() method. As the user is - * choosing colors within the color chooser the background property will be updated. - * By listening to this property developers can make other parts of their programs - * update. - * - * @author joshua@marinacci.org - */ -public class JXColorSelectionButton extends JButton { - private BufferedImage colorwell; - private JDialog dialog = null; - private JColorChooser chooser = null; - private Color initialColor = null; - - /** - * Creates a new instance of JXColorSelectionButton - */ - public JXColorSelectionButton() { - this(Color.red); - } - - /** - * Creates a new instance of JXColorSelectionButton set to the specified color. - * @param col The default color - */ - public JXColorSelectionButton(Color col) { - setBackground(col); - this.addActionListener(new ActionHandler()); - this.setContentAreaFilled(false); - this.setOpaque(false); - - try { - colorwell = ImageIO.read(JXColorSelectionButton.class.getResourceAsStream("color/colorwell.png")); - } catch (Exception ex) { - ex.printStackTrace(); - } - - this.addPropertyChangeListener("background", propertyChangeEvent -> getChooser().setColor(getBackground())); - } - - - /** - * A listener class to update the button's background when the selected - * color changes. - */ - private class ColorChangeListener implements ChangeListener { - public JXColorSelectionButton button; - public ColorChangeListener(JXColorSelectionButton button) { - this.button = button; - } - public void stateChanged(ChangeEvent changeEvent) { - button.setBackground(button.getChooser().getColor()); - } - } - - /** - * {@inheritDoc} - */ - @Override - protected void paintComponent(Graphics g) { - // want disabledForeground when disabled, current colour otherwise - final Color FILL_COLOR = isEnabled() ? PaintUtils.removeAlpha(getBackground()) - : UIManagerExt.getSafeColor("Button.disabledForeground", Color.LIGHT_GRAY); - - // draw the colorwell image (should only be on OSX) - if(SystemUtils.IS_OS_MAC_OSX && colorwell != null) { - Insets ins = new Insets(5,5,5,5); - GraphicsUtilities.tileStretchPaint(g, this, colorwell, ins); - - // fill in the color area - g.setColor(FILL_COLOR); - g.fillRect(ins.left, ins.top, - getWidth() - ins.left - ins.right, - getHeight() - ins.top - ins.bottom); - // draw the borders - g.setColor(PaintUtils.setBrightness(FILL_COLOR,0.85f)); - g.drawRect(ins.left, ins.top, - getWidth() - ins.left - ins.right - 1, - getHeight() - ins.top - ins.bottom - 1); - g.drawRect(ins.left + 1, ins.top + 1, - getWidth() - ins.left - ins.right - 3, - getHeight() - ins.top - ins.bottom - 3); - }else{ - Graphics2D g2 = (Graphics2D) g.create(); - - try { - g2.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON); - g2.setColor(Color.LIGHT_GRAY); - final int DIAM = Math.min(getWidth(), getHeight()); - final int inset = 3; - g2.fill(new Ellipse2D.Float(inset, inset, DIAM-2*inset, DIAM-2*inset)); - g2.setColor(FILL_COLOR); - final int border = 1; - g2.fill(new Ellipse2D.Float(inset+border, inset+border, DIAM-2*inset-2*border, DIAM-2*inset-2*border)); - } finally { - g2.dispose(); - } - - } - } - -// /** -// * Sample usage of JXColorSelectionButton -// * @param args not used -// */ -// public static void main(String[] args) { -// javax.swing.JFrame frame = new javax.swing.JFrame("Color Button Test"); -// frame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE); -// javax.swing.JPanel panel = new javax.swing.JPanel(); -// javax.swing.JComponent btn = new JXColorSelectionButton(); -// btn.setEnabled(true); -// panel.add(btn); -// panel.add(new javax.swing.JLabel("ColorSelectionButton test")); -// -// frame.add(panel); -// frame.pack(); -// frame.setVisible(true); -// } - - /** - * Conditionally create and show the color chooser dialog. - */ - private void showDialog() { - if (dialog == null) { - dialog = JColorChooser.createDialog(JXColorSelectionButton.this, - "Choose a color", true, getChooser(), - actionEvent -> { - Color color = getChooser().getColor(); - if (color != null) { - setBackground(color); - } - }, - actionEvent -> setBackground(initialColor)); - dialog.getContentPane().add(getChooser()); - getChooser().getSelectionModel().addChangeListener( - new ColorChangeListener(JXColorSelectionButton.this)); - } - - initialColor = getBackground(); - dialog.setVisible(true); - - } - - /** - * Get the JColorChooser that is used by this JXColorSelectionButton. This - * chooser instance is shared between all invocations of the chooser, but is unique to - * this instance of JXColorSelectionButton. - * @return the JColorChooser used by this JXColorSelectionButton - */ - public JColorChooser getChooser() { - if(chooser == null) { - chooser = new JColorChooser(); - // add the eyedropper color chooser panel - chooser.addChooserPanel(new EyeDropperColorChooserPanel()); - } - return chooser; - } - - /** - * Set the JColorChooser that is used by this JXColorSelectionButton. - * chooser instance is shared between all invocations of the chooser, - * but is unique to - * this instance of JXColorSelectionButton. - * @param chooser The new JColorChooser to use. - */ - public void setChooser(JColorChooser chooser) { - JColorChooser oldChooser = getChooser(); - this.chooser = chooser; - firePropertyChange("chooser",oldChooser,chooser); - } - - /** - * {@inheritDoc} - */ - @Override - public Dimension getPreferredSize() { - if (isPreferredSizeSet() || colorwell == null) { - return super.getPreferredSize(); - } - - return new Dimension(colorwell.getWidth(), colorwell.getHeight()); - } - - /** - * A private class to conditionally create and show the color chooser - * dialog. - */ - private class ActionHandler implements ActionListener { - - public void actionPerformed(ActionEvent actionEvent) { - showDialog(); - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/JXComboBox.java b/src/main/java/org/jdesktop/swingx/JXComboBox.java deleted file mode 100644 index 226b280adb..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXComboBox.java +++ /dev/null @@ -1,885 +0,0 @@ -/* - * $Id: JXComboBox.java 4158 2012-02-03 18:29:40Z kschaefe $ - * - * Copyright 2010 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx; - -import org.jdesktop.swingx.decorator.ComponentAdapter; -import org.jdesktop.swingx.decorator.CompoundHighlighter; -import org.jdesktop.swingx.decorator.Highlighter; -import org.jdesktop.swingx.plaf.UIDependent; -import org.jdesktop.swingx.renderer.DefaultListRenderer; -import org.jdesktop.swingx.renderer.JRendererPanel; -import org.jdesktop.swingx.renderer.StringValue; -import org.jdesktop.swingx.rollover.RolloverRenderer; -import org.jdesktop.swingx.sort.StringValueRegistry; -import org.jdesktop.swingx.util.Contract; - -import javax.accessibility.Accessible; -import javax.swing.*; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import javax.swing.plaf.UIResource; -import javax.swing.plaf.basic.ComboPopup; -import java.awt.*; -import java.awt.event.KeyEvent; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; -import java.util.Vector; - -/** - * An enhanced {@code JComboBox} that provides the following additional functionality: - *

                - * Auto-starts edits correctly for AutoCompletion when inside a {@code JTable}. A normal {@code - * JComboBox} fails to recognize the first key stroke when it has been - * {@link org.jdesktop.swingx.autocomplete.AutoCompleteDecorator#decorate(JComboBox) decorated}. - *

                - * Adds highlighting support. - * - * @author Karl Schaefer - * @author Jeanette Winzenburg - */ -@SuppressWarnings({"nls", "serial"}) -public class JXComboBox extends JComboBox { - /** - * A decorator for the original ListCellRenderer. Needed to hook highlighters - * after messaging the delegate.

                - */ - public class DelegatingRenderer implements ListCellRenderer, RolloverRenderer, UIDependent { - /** the delegate. */ - private ListCellRenderer delegateRenderer; - private JRendererPanel wrapper; - - /** - * Instantiates a DelegatingRenderer with combo box's default renderer as delegate. - */ - public DelegatingRenderer() { - this(null); - } - - /** - * Instantiates a DelegatingRenderer with the given delegate. If the - * delegate is {@code null}, the default is created via the combo box's factory method. - * - * @param delegate the delegate to use, if {@code null} the combo box's default is - * created and used. - */ - public DelegatingRenderer(ListCellRenderer delegate) { - wrapper = new JRendererPanel(new BorderLayout()); - setDelegateRenderer(delegate); - } - - /** - * Sets the delegate. If the delegate is {@code null}, the default is created via the combo - * box's factory method. - * - * @param delegate - * the delegate to use, if null the list's default is created and used. - */ - public void setDelegateRenderer(ListCellRenderer delegate) { - if (delegate == null) { - delegate = createDefaultCellRenderer(); - } - delegateRenderer = delegate; - } - - /** - * Returns the delegate. - * - * @return the delegate renderer used by this renderer, guaranteed to - * not-null. - */ - public ListCellRenderer getDelegateRenderer() { - return delegateRenderer; - } - - /** - * {@inheritDoc} - */ - @Override - public void updateUI() { - wrapper.updateUI(); - - if (delegateRenderer instanceof UIDependent) { - ((UIDependent) delegateRenderer).updateUI(); - } else if (delegateRenderer instanceof Component) { - SwingUtilities.updateComponentTreeUI((Component) delegateRenderer); - } else if (delegateRenderer != null) { - try { - Component comp = delegateRenderer.getListCellRendererComponent( - getPopupListFor(JXComboBox.this), null, -1, false, false); - SwingUtilities.updateComponentTreeUI(comp); - } catch (Exception e) { - // nothing to do - renderer barked on off-range row - } - } - } - - // --------- implement ListCellRenderer - /** - * {@inheritDoc}

                - * - * Overridden to apply the highlighters, if any, after calling the delegate. - * The decorators are not applied if the row is invalid. - */ - @Override - public Component getListCellRendererComponent(JList list, Object value, int index, - boolean isSelected, boolean cellHasFocus) { - Component comp = null; - - if (index == -1) { - comp = delegateRenderer.getListCellRendererComponent(list, value, - getSelectedIndex(), isSelected, cellHasFocus); - - if (isUseHighlightersForCurrentValue() && compoundHighlighter != null && getSelectedIndex() != -1) { - comp = compoundHighlighter.highlight(comp, getComponentAdapter(getSelectedIndex())); - - // this is done to "trick" BasicComboBoxUI.paintCurrentValue which resets all of - // the painted information after asking the list to render the value. the panel - // wrappers receives all of the post-rendering configuration, which is dutifully - // ignored by the real rendering component - wrapper.add(comp); - comp = wrapper; - } - } else { - comp = delegateRenderer.getListCellRendererComponent(list, value, index, - isSelected, cellHasFocus); - - if ((compoundHighlighter != null) && (index >= 0) && (index < getItemCount())) { - comp = compoundHighlighter.highlight(comp, getComponentAdapter(index)); - } - } - - return comp; - } - - // implement RolloverRenderer - - /** - * {@inheritDoc} - * - */ - @Override - public boolean isEnabled() { - return (delegateRenderer instanceof RolloverRenderer) && - ((RolloverRenderer) delegateRenderer).isEnabled(); - } - - /** - * {@inheritDoc} - */ - @Override - public void doClick() { - if (isEnabled()) { - ((RolloverRenderer) delegateRenderer).doClick(); - } - } - } - - @SuppressWarnings("hiding") - protected static class ComboBoxAdapter extends ComponentAdapter { - private final JXComboBox comboBox; - - /** - * Constructs a ListAdapter for the specified target - * JXList. - * - * @param component the target list. - */ - public ComboBoxAdapter(JXComboBox component) { - super(component); - comboBox = component; - } - - /** - * Typesafe accessor for the target component. - * - * @return the target component as a {@link JXComboBox} - */ - public JXComboBox getComboBox() { - return comboBox; - } - - /** - * A safe way to access the combo box's popup visibility. - * - * @return {@code true} if the popup is visible; {@code false} otherwise - */ - protected boolean isPopupVisible() { - if (comboBox.updatingUI) { - return false; - } - - return comboBox.isPopupVisible(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean hasFocus() { - if (isPopupVisible()) { - JList list = getPopupListFor(comboBox); - - return list != null && list.isFocusOwner() && (row == list.getLeadSelectionIndex()); - } - - return comboBox.isFocusOwner(); - } - - /** - * {@inheritDoc} - */ - @Override - public int getRowCount() { - return comboBox.getModel().getSize(); - } - - /** - * {@inheritDoc} - */ - @Override - public Object getValueAt(int row, int column) { - return comboBox.getModel().getElementAt(row); - } - - /** - * {@inheritDoc} - * This is implemented to query the table's StringValueRegistry for an appropriate - * StringValue and use that for getting the string representation. - */ - @Override - public String getStringAt(int row, int column) { - StringValue sv = comboBox.getStringValueRegistry().getStringValue(row, column); - - return sv.getString(getValueAt(row, column)); - } - - /** - * {@inheritDoc} - */ - @Override - public Rectangle getCellBounds() { - JList list = getPopupListFor(comboBox); - - if (list == null) { - assert false; - return new Rectangle(comboBox.getSize()); - } - - return list.getCellBounds(row, row); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isCellEditable(int row, int column) { - return row == -1 && comboBox.isEditable(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isEditable() { - return isCellEditable(row, column); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isSelected() { - if (isPopupVisible()) { - JList list = getPopupListFor(comboBox); - - return list != null && row == list.getLeadSelectionIndex(); - } - - return comboBox.isFocusOwner(); - } - } - - class StringValueKeySelectionManager implements KeySelectionManager, Serializable, UIDependent { - private long timeFactor; - private long lastTime = 0L; - private String prefix = ""; - private String typedString = ""; - - public StringValueKeySelectionManager() { - updateUI(); - } - - @Override - public int selectionForKey(char aKey, ComboBoxModel aModel) { - if (lastTime == 0L) { - prefix = ""; - typedString = ""; - } - - int startIndex = getSelectedIndex(); - - if (EventQueue.getMostRecentEventTime() - lastTime < timeFactor) { - typedString += aKey; - if ((prefix.length() == 1) && (aKey == prefix.charAt(0))) { - // Subsequent same key presses move the keyboard focus to the next - // object that starts with the same letter. - startIndex++; - } else { - prefix = typedString; - } - } else { - startIndex++; - typedString = "" + aKey; - prefix = typedString; - } - - lastTime = EventQueue.getMostRecentEventTime(); - - if (startIndex < 0 || startIndex >= aModel.getSize()) { - startIndex = 0; - } - - for (int i = startIndex, c = aModel.getSize(); i < c; i++) { - String v = getStringAt(i).toLowerCase(); - - if (v.length() > 0 && v.charAt(0) == aKey) { - return i; - } - } - - for (int i = startIndex, c = aModel.getSize(); i < c; i++) { - String v = getStringAt(i).toLowerCase(); - - if (v.length() > 0 && v.charAt(0) == aKey) { - return i; - } - } - - for (int i = 0; i < startIndex; i++) { - String v = getStringAt(i).toLowerCase(); - - if (v.length() > 0 && v.charAt(0) == aKey) { - return i; - } - } - - return -1; - } - - @Override - public void updateUI() { - Long l = (Long) UIManager.get("ComboBox.timeFactor"); - timeFactor = l == null ? 1000L : l.longValue(); - } - } - - private ComboBoxAdapter dataAdapter; - - private DelegatingRenderer delegatingRenderer; - - private StringValueRegistry stringValueRegistry; - - private boolean useHighlightersForCurrentValue = true; - - private CompoundHighlighter compoundHighlighter; - - private ChangeListener highlighterChangeListener; - - private List pendingEvents; - - private boolean isDispatching; - - private boolean updatingUI; - - /** - * Creates a JXComboBox with a default data model. The default data model is an - * empty list of objects. Use addItem to add items. By default the first item in - * the data model becomes selected. - * - * @see DefaultComboBoxModel - */ - public JXComboBox() { - super(); - init(); - } - - /** - * Creates a JXComboBox that takes its items from an existing - * ComboBoxModel. Since the ComboBoxModel is provided, a combo box - * created using this constructor does not create a default combo box model and may impact how - * the insert, remove and add methods behave. - * - * @param model - * the ComboBoxModel that provides the displayed list of items - * @see DefaultComboBoxModel - */ - public JXComboBox(ComboBoxModel model) { - super(model); - init(); - } - - /** - * Creates a JXComboBox that contains the elements in the specified array. By - * default the first item in the array (and therefore the data model) becomes selected. - * - * @param items - * an array of objects to insert into the combo box - * @see DefaultComboBoxModel - */ - public JXComboBox(Object[] items) { - super(items); - init(); - } - - /** - * Creates a JXComboBox that contains the elements in the specified Vector. By - * default the first item in the vector (and therefore the data model) becomes selected. - * - * @param items - * an array of vectors to insert into the combo box - * @see DefaultComboBoxModel - */ - public JXComboBox(Vector items) { - super(items); - init(); - } - - private void init() { - pendingEvents = new ArrayList(); - - if (keySelectionManager == null || keySelectionManager instanceof UIResource) { - setKeySelectionManager(createDefaultKeySelectionManager()); - } - } - - protected static JList getPopupListFor(JComboBox comboBox) { - int count = comboBox.getUI().getAccessibleChildrenCount(comboBox); - - for (int i = 0; i < count; i++) { - Accessible a = comboBox.getUI().getAccessibleChild(comboBox, i); - - if (a instanceof ComboPopup) { - return ((ComboPopup) a).getList(); - } - } - - return null; - } - - /** - * {@inheritDoc} - *

                - * This implementation uses the {@code StringValue} representation of the elements to determine - * the selected item. - */ - @Override - protected KeySelectionManager createDefaultKeySelectionManager() { - return new StringValueKeySelectionManager(); - } - - /** - * {@inheritDoc} - */ - @Override - protected boolean processKeyBinding(KeyStroke ks, final KeyEvent e, int condition, - boolean pressed) { - boolean retValue = super.processKeyBinding(ks, e, condition, pressed); - - if (!retValue && editor != null) { - if (isStartingCellEdit(e)) { - pendingEvents.add(e); - } else if (pendingEvents.size() == 2) { - pendingEvents.add(e); - isDispatching = true; - - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - try { - for (KeyEvent event : pendingEvents) { - editor.getEditorComponent().dispatchEvent(event); - } - - pendingEvents.clear(); - } finally { - isDispatching = false; - } - } - }); - } - } - return retValue; - } - - private boolean isStartingCellEdit(KeyEvent e) { - if (isDispatching) { - return false; - } - - JTable table = (JTable) SwingUtilities.getAncestorOfClass(JTable.class, this); - boolean isOwned = table != null - && !Boolean.FALSE.equals(table.getClientProperty("JTable.autoStartsEdit")); - - return isOwned && e.getComponent() == table; - } - - /** - * @return the unconfigured ComponentAdapter. - */ - protected ComponentAdapter getComponentAdapter() { - if (dataAdapter == null) { - dataAdapter = new ComboBoxAdapter(this); - } - return dataAdapter; - } - - /** - * Convenience to access a configured ComponentAdapter. - * Note: the column index of the configured adapter is always 0. - * - * @param index the row index in view coordinates, must be valid. - * @return the configured ComponentAdapter. - */ - protected ComponentAdapter getComponentAdapter(int index) { - ComponentAdapter adapter = getComponentAdapter(); - adapter.column = 0; - adapter.row = index; - return adapter; - } - - /** - * Returns the StringValueRegistry which defines the string representation for - * each cells. This is strictly for internal use by the table, which has the - * responsibility to keep in synch with registered renderers.

                - * - * Currently exposed for testing reasons, client code is recommended to not use nor override. - * - * @return the current string value registry - */ - protected StringValueRegistry getStringValueRegistry() { - if (stringValueRegistry == null) { - stringValueRegistry = createDefaultStringValueRegistry(); - } - return stringValueRegistry; - } - - /** - * Creates and returns the default registry for StringValues.

                - * - * @return the default registry for StringValues. - */ - protected StringValueRegistry createDefaultStringValueRegistry() { - return new StringValueRegistry(); - } - - /** - * Returns the string representation of the cell value at the given position. - * - * @param row the row index of the cell in view coordinates - * @return the string representation of the cell value as it will appear in the - * table. - */ - public String getStringAt(int row) { - // changed implementation to use StringValueRegistry - StringValue stringValue = getStringValueRegistry().getStringValue(row, 0); - - return stringValue.getString(getItemAt(row)); - } - - private DelegatingRenderer getDelegatingRenderer() { - if (delegatingRenderer == null) { - // only called once... to get hold of the default? - delegatingRenderer = new DelegatingRenderer(); - } - return delegatingRenderer; - } - - /** - * Creates and returns the default cell renderer to use. Subclasses - * may override to use a different type. Here: returns a DefaultListRenderer. - * - * @return the default cell renderer to use with this list. - */ - protected ListCellRenderer createDefaultCellRenderer() { - return new DefaultListRenderer(); - } - - /** - * {@inheritDoc}

                - * - * Overridden to return the delegating renderer which is wrapped around the - * original to support highlighting. The returned renderer is of type - * DelegatingRenderer and guaranteed to not-null

                - * - * @see #setRenderer(ListCellRenderer) - * @see DelegatingRenderer - */ - @Override - public ListCellRenderer getRenderer() { - // PENDING JW: something wrong here - why exactly can't we return super? - // not even if we force the initial setting in init? -// return super.getCellRenderer(); - return getDelegatingRenderer(); - } - - /** - * Returns the renderer installed by client code or the default if none has - * been set. - * - * @return the wrapped renderer. - * @see #setRenderer(ListCellRenderer) - */ - public ListCellRenderer getWrappedRenderer() { - return getDelegatingRenderer().getDelegateRenderer(); - } - - /** - * {@inheritDoc}

                - * - * Overridden to wrap the given renderer in a DelegatingRenderer to support - * highlighting.

                - * - * Note: the wrapping implies that the renderer returned from the getCellRenderer - * is not the renderer as given here, but the wrapper. To access the original, - * use getWrappedCellRenderer. - * - * @see #getWrappedRenderer() - * @see #getRenderer() - */ - @Override - public void setRenderer(ListCellRenderer renderer) { - // PENDING: do something against recursive setting - // == multiple delegation... - ListCellRenderer oldValue = super.getRenderer(); - getDelegatingRenderer().setDelegateRenderer(renderer); - getStringValueRegistry().setStringValue( - renderer instanceof StringValue ? (StringValue) renderer : null, 0); - super.setRenderer(delegatingRenderer); - - if (oldValue == delegatingRenderer) { - firePropertyChange("renderer", null, delegatingRenderer); - } - } - - /** - * PENDING JW to KS: review method naming - doesn't sound like valid English to me (no - * native speaker of course :-). Options are to - * change the property name to usingHighlightersForCurrentValue (as we did in JXMonthView - * after some debate) or stick to getXX. Thinking about it: maybe then the property should be - * usesHighlightersXX, that is third person singular instead of imperative, - * like in tracksVerticalViewport of JTable? - * - * @return {@code true} if the combo box decorates the current value with highlighters; {@code false} otherwise - */ - public boolean isUseHighlightersForCurrentValue() { - return useHighlightersForCurrentValue; - } - - public void setUseHighlightersForCurrentValue(boolean useHighlightersForCurrentValue) { - boolean oldValue = isUseHighlightersForCurrentValue(); - this.useHighlightersForCurrentValue = useHighlightersForCurrentValue; - repaint(); - firePropertyChange("useHighlightersForCurrentValue", oldValue, - isUseHighlightersForCurrentValue()); - } - - /** - * Returns the CompoundHighlighter assigned to the table, null if none. PENDING: open up for - * subclasses again?. - * - * @return the CompoundHighlighter assigned to the table. - * @see #setCompoundHighlighter(CompoundHighlighter) - */ - private CompoundHighlighter getCompoundHighlighter() { - return compoundHighlighter; - } - - /** - * Assigns a CompoundHighlighter to the table, maybe null to remove all Highlighters. - *

                - * - * The default value is null. - *

                - * - * PENDING: open up for subclasses again?. - * - * @param pipeline - * the CompoundHighlighter to use for renderer decoration. - * @see #getCompoundHighlighter() - * @see #addHighlighter(Highlighter) - * @see #removeHighlighter(Highlighter) - * - */ - private void setCompoundHighlighter(CompoundHighlighter pipeline) { - CompoundHighlighter old = getCompoundHighlighter(); - if (old != null) { - old.removeChangeListener(getHighlighterChangeListener()); - } - compoundHighlighter = pipeline; - if (compoundHighlighter != null) { - compoundHighlighter.addChangeListener(getHighlighterChangeListener()); - } - // PENDING: wrong event - the property is either "compoundHighlighter" - // or "highlighters" with the old/new array as value - firePropertyChange("highlighters", old, getCompoundHighlighter()); - } - - /** - * Sets the Highlighters to the column, replacing any old settings. None of the - * given Highlighters must be null. - *

                - * - * @param highlighters - * zero or more not null highlighters to use for renderer decoration. - * - * @see #getHighlighters() - * @see #addHighlighter(Highlighter) - * @see #removeHighlighter(Highlighter) - * - */ - public void setHighlighters(Highlighter... highlighters) { - Contract.asNotNull(highlighters, "highlighters cannot be null or contain null"); - - CompoundHighlighter pipeline = null; - if (highlighters.length > 0) { - pipeline = new CompoundHighlighter(highlighters); - } - - setCompoundHighlighter(pipeline); - } - - /** - * Returns the Highlighters used by this column. Maybe empty, but guarantees to be - * never null. - * - * @return the Highlighters used by this column, guaranteed to never null. - * @see #setHighlighters(Highlighter[]) - */ - public Highlighter[] getHighlighters() { - return getCompoundHighlighter() != null ? getCompoundHighlighter().getHighlighters() - : CompoundHighlighter.EMPTY_HIGHLIGHTERS; - } - - /** - * Adds a Highlighter. Appends to the end of the list of used Highlighters. - *

                - * - * @param highlighter - * the Highlighter to add. - * @throws NullPointerException - * if Highlighter is null. - * - * @see #removeHighlighter(Highlighter) - * @see #setHighlighters(Highlighter[]) - */ - public void addHighlighter(Highlighter highlighter) { - CompoundHighlighter pipeline = getCompoundHighlighter(); - if (pipeline == null) { - setCompoundHighlighter(new CompoundHighlighter(highlighter)); - } else { - pipeline.addHighlighter(highlighter); - } - } - - /** - * Removes the given Highlighter. - *

                - * - * Does nothing if the Highlighter is not contained. - * - * @param highlighter - * the Highlighter to remove. - * @see #addHighlighter(Highlighter) - * @see #setHighlighters(Highlighter...) - */ - public void removeHighlighter(Highlighter highlighter) { - if ((getCompoundHighlighter() == null)) { - return; - } - getCompoundHighlighter().removeHighlighter(highlighter); - } - - /** - * Returns the ChangeListener to use with highlighters. Lazily creates the - * listener. - * - * @return the ChangeListener for observing changes of highlighters, guaranteed to be - * not-null - */ - protected ChangeListener getHighlighterChangeListener() { - if (highlighterChangeListener == null) { - highlighterChangeListener = createHighlighterChangeListener(); - } - - return highlighterChangeListener; - } - - /** - * Creates and returns the ChangeListener observing Highlighters. - *

                - * A property change event is create for a state change. - * - * @return the ChangeListener defining the reaction to changes of highlighters. - */ - protected ChangeListener createHighlighterChangeListener() { - return new ChangeListener() { - @Override - public void stateChanged(ChangeEvent e) { - // need to fire change so JXComboBox can update - firePropertyChange("highlighters", null, getHighlighters()); - repaint(); - } - }; - } - - /** - * {@inheritDoc} - *

                - * Overridden to update renderer and highlighters. - */ - @Override - public void updateUI() { - updatingUI = true; - - try { - super.updateUI(); - - if (keySelectionManager instanceof UIDependent) { - ((UIDependent) keySelectionManager).updateUI(); - } - - ListCellRenderer renderer = getRenderer(); - - if (renderer instanceof UIDependent) { - ((UIDependent) renderer).updateUI(); - } else if (renderer instanceof Component) { - SwingUtilities.updateComponentTreeUI((Component) renderer); - } - - if (compoundHighlighter != null) { - compoundHighlighter.updateUI(); - } - } finally { - updatingUI = false; - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/JXDatePicker.java b/src/main/java/org/jdesktop/swingx/JXDatePicker.java deleted file mode 100644 index 55b6be85c4..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXDatePicker.java +++ /dev/null @@ -1,1005 +0,0 @@ -/* - * $Id: JXDatePicker.java 4147 2012-02-01 17:13:24Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.calendar.DatePickerFormatter; -import org.jdesktop.swingx.event.EventListenerMap; -import org.jdesktop.swingx.painter.MattePainter; -import org.jdesktop.swingx.plaf.DatePickerAddon; -import org.jdesktop.swingx.plaf.DatePickerUI; -import org.jdesktop.swingx.plaf.LookAndFeelAddons; -import org.jdesktop.swingx.plaf.UIManagerExt; -import org.jdesktop.swingx.util.Contract; - -import javax.swing.*; -import javax.swing.JFormattedTextField.AbstractFormatter; -import javax.swing.JFormattedTextField.AbstractFormatterFactory; -import javax.swing.event.PopupMenuListener; -import javax.swing.text.DefaultFormatterFactory; -import java.awt.*; -import java.awt.event.*; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.beans.PropertyVetoException; -import java.text.*; -import java.util.*; -import java.util.logging.Logger; - -/** - * A component for entering dates with a user interaction similar to a - * JComboBox. The dates can be typed into a text field or selected from a - * JXMonthView which opens in a JXPopupMenu on user's request. - *

                - * - * The date selection is controlled by the JXMonthView's DateSelectionModel. - * This allows the use of all its functionality in the JXDatePicker as well. - * F.i. restrict the selection to a date in the current or next week: - *

                - * - *

                
                - * Appointment appointment = new Appointment(director,
                - *         "Be sure to have polished shoes!");
                - * JXDatePicker picker = new JXDatePicker();
                - * Calendar calendar = picker.getMonthView().getCalendar();
                - * // starting today if we are in a hurry
                - * calendar.setTime(new Date());
                - * picker.getMonthView().setLowerBound(calendar.getTime());
                - * // end of next week
                - * CalendarUtils.endOfWeek(calendar);
                - * calendar.add(Calendar.WEEK_OF_YEAR);
                - * picker.getMonthView().setUpperBound(calendar.getTime());
                - * 
                - * - * Similar to a JXMonthView, the JXDatePicker fires an ActionEvent when the user - * actively commits or cancels a selection. Interested client code can add a - * ActionListener to be notified by the user action. - * - *
                
                - * JXDatePicker picker = new JXDatePicker(new Date());
                - * ActionListener l = new ActionListener() {
                - *     public void actionPerformed(ActionEvent e) {
                - *         if (JXDatePicker.COMMIT_KEY.equals(e.getActionCommand)) {
                - *             saveDate(picker.getDate());
                - *         }
                - *     }
                - * };
                - * picker.addActionListener(l);
                - * 
                - * - * Note that ActionListener will not be notified if the user - * edits the date text without hitting the Enter key afterwards. To detect both kinds of - * date change, interested client code can add a PropertyChangeListener. - * - *
                
                - * JXDatePicker picker = new JXDatePicker(new Date());
                - * PropertyChangeListener listener = new PropertyChangeListener() {
                - *     public void propertyChange(PropertyChangeEvent e) {
                - *         if ("date".equals(e.getPropertyName()) {
                - *              saveDate(picker.getDate());
                - *         }
                - *     }
                - * };
                - * picker.addPropertyChangeListener(listener);
                - * 
                - - * - *

                - * The DateFormats used in the JXDatePicker's are initialized to the default - * formats of the DatePickerFormatter, as defined by the picker's resourceBundle - * DatePicker.properties. Application code can overwrite the picker's default - * - *

                
                - * picker.setDateFormats(myCustomFormat, myAlternativeCustomFormat);
                - * 
                - * - * PENDING JW: explain what the alternatives are for (after understanding it - * myself ;-) - *

                - * - * The selected Date is a bound property of the JXDatePicker. This allows easy - * binding to a property of a custom bean when using a binding framework. - *

                - * - * Keybindings (as installed by the UI-Delegate) - *

                  - *
                • ENTER commits the edited or selected value - *
                • ESCAPE reverts the edited or selected value - *
                • alt-DOWN opens the monthView popup - *
                • shift-F5 if monthView is visible, navigates the monthView to today - * (no effect otherwise) - *
                • F5 commits today - *
                - * - * PENDNG JW: support per-OS keybindings to be installed, currently they are - * hardcoded in our (single) BasicDatePickerUI. - * - * @author Joshua Outwater - * @author Jeanette Winzenburg - * - * @see JXMonthView - * @see org.jdesktop.swingx.calendar.DateSelectionModel - * @see DatePickerFormatter - * - */ -@JavaBean -public class JXDatePicker extends JComponent { - @SuppressWarnings("unused") - private static final Logger LOG = Logger.getLogger(JXDatePicker.class - .getName()); - static { - LookAndFeelAddons.contribute(new DatePickerAddon()); - } - - /** - * UI Class ID - */ - public static final String uiClassID = "DatePickerUI"; - - public static final String EDITOR = "editor"; - public static final String MONTH_VIEW = "monthView"; - - public static final String LINK_PANEL = "linkPanel"; - - /** action command used for commit actionEvent. */ - public static final String COMMIT_KEY = "datePickerCommit"; - /** action command used for cancel actionEvent. */ - public static final String CANCEL_KEY = "datePickerCancel"; - /** action key for navigate home action */ - public static final String HOME_NAVIGATE_KEY = "navigateHome"; - /** action key for commit home action */ - public static final String HOME_COMMIT_KEY = "commitHome"; - - private static final DateFormat[] EMPTY_DATE_FORMATS = new DateFormat[0]; - - /** - * The editable date field that displays the date - */ - private JFormattedTextField _dateField; - - /** - * Popup that displays the month view with controls for - * traversing/selecting dates. - */ - private JPanel _linkPanel; - private MessageFormat _linkFormat; - private Date linkDate; - - private JXMonthView _monthView; - private boolean editable = true; - // PENDING JW: remove - duplication, we have access to super's listenerlist - private EventListenerMap listenerMap; - protected boolean lightWeightPopupEnabled = JPopupMenu.getDefaultLightWeightPopupEnabled(); - - private Date date; - - private PropertyChangeListener monthViewListener; - - - /** - * Intantiates a date picker with no selection and the default - * DatePickerFormatter. - *

                - * The date picker is configured with the default time zone and locale - * - * @see #setTimeZone - * @see #getTimeZone - */ - public JXDatePicker() { - this(null, null); - } - - - - /** - * Intantiates a date picker using the specified time as the initial - * selection and the default - * DatePickerFormatter. - *

                - * The date picker is configured with the default time zone and locale - * - * @param selected the initially selected date - * @see #setTimeZone - * @see #getTimeZone - */ - public JXDatePicker(Date selected) { - this(selected, null); - } - - /** - * Intantiates a date picker with no selection and the default - * DatePickerFormatter. - *

                - * The date picker is configured with the default time zone and specified - * locale - * - * @param locale initial Locale - * @see #setTimeZone - * @see #getTimeZone - */ - public JXDatePicker(Locale locale) { - this(null, locale); - } - - /** - * Intantiates a date picker using the specified time as the initial - * selection and the default - * DatePickerFormatter. - *

                - * The date picker is configured with the default time zone and specified locale - * - * @param selection initially selected Date - * @param locale initial Locale - * @see #setTimeZone - * @see #getTimeZone - * - */ - public JXDatePicker(Date selection, Locale locale) { - init(); - if (locale != null) { - setLocale(locale); - } - // install the controller before setting the date - updateUI(); - setDate(selection); - } - - /** - * Sets the date property.

                - * - * Does nothing if the ui vetos the new date - as might happen if - * the code tries to set a date which is unselectable in the - * monthView's context. The actual value of the new Date is controlled - * by the JXMonthView's DateSelectionModel. The default implementation - * normalizes the date to the start of the day in the model's calendar's - * coordinates, that is all time fields are zeroed. To keep the time fields, - * configure the monthView with a SingleDaySelectionModel. - *

                - * - * At all "stable" (= not editing in date input field nor - * in the monthView) times the date is the same in the - * JXMonthView, this JXDatePicker and the editor. If a new Date - * is set, this invariant is enforced by the DatePickerUI. - *

                - * - * This is a bound property. - * - * - * @param date the new date to set. - * @see #getDate() - * @see org.jdesktop.swingx.calendar.DateSelectionModel - * @see org.jdesktop.swingx.calendar.SingleDaySelectionModel - */ - public void setDate(Date date) { - /* - * JW: - * this is a poor woman's constraint property. - * Introduces explicit coupling to the ui. - * Which is unusual at this place in code. - * - * If needed the date can be made a formal - * constraint property and let the ui add a - * VetoablePropertyListener. - */ - try { - date = getUI().getSelectableDate(date); - } catch (PropertyVetoException e) { - return; - } - Date old = getDate(); - this.date = date; - firePropertyChange("date", old, getDate()); - } - - - - /** - * Returns the currently selected date. - * - * @return Date - */ - public Date getDate() { - return date; - } - - /** - * - */ - private void init() { - listenerMap = new EventListenerMap(); - initMonthView(); - - updateLinkFormat(); - linkDate = _monthView.getToday(); - _linkPanel = new TodayPanel(); - } - - private void initMonthView() { - _monthView = new JXMonthView(); -// _monthView.setSelectionModel(new SingleDaySelectionModel()); - _monthView.setTraversable(true); - _monthView.addPropertyChangeListener(getMonthViewListener()); - } - - /** - * Lazily creates and returns the PropertyChangeListener which listens - * for model's calendar properties. - * - * @return a PropertyChangeListener for monthView's property change notification. - */ - private PropertyChangeListener getMonthViewListener() { - if (monthViewListener == null) { - monthViewListener = new PropertyChangeListener() { - - @Override - public void propertyChange(PropertyChangeEvent evt) { - if ("timeZone".equals(evt.getPropertyName())) { - updateTimeZone((TimeZone) evt.getOldValue(), (TimeZone) evt.getNewValue()); - } - - } - - }; - } - return monthViewListener; - } - - /** - * Callback from monthView timezone changes.

                - * - * NOTE: as timeZone is a bound property of this class we need to - * guarantee the propertyChangeNotification. As this class doesn't - * own this property it must listen to the owner (monthView) and - * re-fire the change. - * - * @param oldValue the old timezone. - * @param newValue the new timezone. - */ - protected void updateTimeZone(TimeZone oldValue, TimeZone newValue) { - firePropertyChange("timeZone", oldValue, newValue); - } - - /** - * Returns the look and feel (L&F) object that renders this component. - * - * @return the DatePickerUI object that renders this component - */ - public DatePickerUI getUI() { - return (DatePickerUI) ui; - } - - /** - * Sets the L&F object that renders this component. - * - * @param ui UI to use for this {@code JXDatePicker} - */ - public void setUI(DatePickerUI ui) { - super.setUI(ui); - } - - /** - * Resets the UI property with the value from the current look and feel. - * - * @see UIManager#getUI - */ - @Override - public void updateUI() { - setUI((DatePickerUI) LookAndFeelAddons.getUI(this, DatePickerUI.class)); - // JW: quick hack around #706-swingx - monthView not updated - // is this complete? how about editor (if not uiResource), linkPanel? - SwingUtilities.updateComponentTreeUI(getMonthView()); - invalidate(); - } - - /** - * @inheritDoc - */ - @Override - public String getUIClassID() { - return uiClassID; - } - - /** - * Replaces the currently installed formatter and factory used by the - * editor. These string formats are defined by the - * java.text.SimpleDateFormat class.

                - * - * Note: The given formats are internally synched to the picker's current - * TimeZone. - * - * @param formats zero or more not null string formats to use. Note that a - * null array is allowed and resets the formatter to use the - * localized default formats. - * @throws NullPointerException any array element is null. - * @see SimpleDateFormat - */ - public void setFormats(String... formats) { - DateFormat[] dateFormats = null; - if (formats != null) { - Contract.asNotNull(formats, - "the array of format strings must not " - + "must not contain null elements"); - dateFormats = new DateFormat[formats.length]; - for (int counter = formats.length - 1; counter >= 0; counter--) { - dateFormats[counter] = new SimpleDateFormat(formats[counter], getLocale()); - } - } - setFormats(dateFormats); - } - - /** - * Replaces the currently installed formatter and factory used by the - * editor.

                - * - * Note: The given formats are internally synched to the picker's current - * TimeZone. - * - * @param formats zero or more not null formats to use. Note that a - * null array is allowed and resets the formatter to use the - * localized default formats. - * @throws NullPointerException any of its elements is null. - */ - public void setFormats(DateFormat... formats) { - if (formats != null) { - Contract.asNotNull(formats, "the array of formats " + "must not contain null elements"); - } - - DateFormat[] old = getFormats(); - _dateField.setFormatterFactory(new DefaultFormatterFactory( - new DatePickerFormatter(formats, getLocale()))); - firePropertyChange("formats", old, getFormats()); - } - - /** - * Returns an array of the formats used by the installed formatter - * if it is a subclass of JXDatePickerFormatter. - * javax.swing.JFormattedTextField.AbstractFormatter - * and javax.swing.text.DefaultFormatter do not have - * support for accessing the formats used. - * - * @return array of formats guaranteed to be not null, but might be empty. - */ - public DateFormat[] getFormats() { - // Dig this out from the factory, if possible, otherwise return null. - AbstractFormatterFactory factory = _dateField.getFormatterFactory(); - if (factory != null) { - AbstractFormatter formatter = factory.getFormatter(_dateField); - if (formatter instanceof DatePickerFormatter) { - return ((DatePickerFormatter) formatter).getFormats(); - } - } - return EMPTY_DATE_FORMATS; - } - - /** - * Return the JXMonthView used in the popup to - * select dates from. - * - * @return the month view component - */ - public JXMonthView getMonthView() { - return _monthView; - } - - /** - * Set the component to use the specified JXMonthView. If the new JXMonthView - * is configured to a different time zone it will affect the time zone of this - * component. - * - * @param monthView month view comopnent. - * @throws NullPointerException if view component is null - * - * @see #setTimeZone - * @see #getTimeZone - */ - public void setMonthView(JXMonthView monthView) { - Contract.asNotNull(monthView, "monthView must not be null"); - JXMonthView oldMonthView = getMonthView(); - TimeZone oldTZ = getTimeZone(); - oldMonthView.removePropertyChangeListener(getMonthViewListener()); - _monthView = monthView; - getMonthView().addPropertyChangeListener(getMonthViewListener()); - firePropertyChange(MONTH_VIEW, oldMonthView, getMonthView()); - firePropertyChange("timeZone", oldTZ, getTimeZone()); - } - - /** - * Gets the time zone. This is a convenience method which returns the time zone - * of the JXMonthView being used. - * - * @return The TimeZone used by the JXMonthView. - */ - public TimeZone getTimeZone() { - return _monthView.getTimeZone(); - } - - /** - * Sets the time zone with the given time zone value. This is a convenience - * method which returns the time zone of the JXMonthView being used.

                - * - * PENDING JW: currently this property is the only property of the monthView - * which is exposed in this api. Not sure why it is here at all. - * It's asymetric (to the other properties) and as such should be either removed - * or the others which might be relevant to a datePicker exposed as well (probably - * hiding the monthView itself as an implementation detail of the ui delegate). - * - * @param tz The TimeZone. - */ - public void setTimeZone(TimeZone tz) { - _monthView.setTimeZone(tz); - } - - - /** - * Returns the date shown in the LinkPanel. - *

                - * PENDING JW: the property should be named linkDate - but that's held by the - * deprecated long returning method. Maybe revisit if we actually remove the other. - * - * @return the date shown in the LinkPanel. - */ - public Date getLinkDay() { - return linkDate; - } - - /** - * Set the date the link will use and the string defining a MessageFormat - * to format the link. If no valid date is in the editor when the popup - * is displayed the popup will focus on the month the linkDate is in. Calling - * this method will replace the currently installed linkPanel and install - * a new one with the requested date and format. - * - * - * @param linkDay the Date to set on the LinkPanel - * @param linkFormatString String used to format the link - * @see MessageFormat - */ - public void setLinkDay(Date linkDay, String linkFormatString) { - setLinkFormat(new MessageFormat(linkFormatString)); - setLinkDay(linkDay); - } - - /** - * Sets the date shown in the TodayPanel. - * - * PENDING JW ... quick api hack for testing. Don't recreate the panel if - * it had been used - * - * @param linkDay the date used in the TodayPanel - */ - public void setLinkDay(Date linkDay) { - this.linkDate = linkDay; - Format[] formats = getLinkFormat().getFormatsByArgumentIndex(); - for (Format format : formats) { - if (format instanceof DateFormat) { - ((DateFormat) format).setTimeZone(getTimeZone()); - } - } - setLinkPanel(new TodayPanel()); - } - - - /** - * @param _linkFormat the _linkFormat to set - */ - protected void setLinkFormat(MessageFormat _linkFormat) { - this._linkFormat = _linkFormat; - } - - /** - * @return the _linkFormat - */ - protected MessageFormat getLinkFormat() { - return _linkFormat; - } - - /** - * Update text on the link panel. - * - */ - private void updateLinkFormat() { - // PENDING JW: move to ui - String linkFormat = UIManagerExt.getString( - "JXDatePicker.linkFormat", getLocale()); - - if (linkFormat != null) { - setLinkFormat(new MessageFormat(linkFormat)); - } else { - setLinkFormat(new MessageFormat("{0,date, dd MMMM yyyy}")); - } - } - - /** - * Return the panel that is used at the bottom of the popup. The default - * implementation shows a link that displays the current month. - * - * @return The currently installed link panel - */ - public JPanel getLinkPanel() { - return _linkPanel; - } - - /** - * Set the panel that will be used at the bottom of the popup. - * PENDING JW: why insist on JPanel? JComponent would be enough? - * - * @param linkPanel The new panel to install in the popup - */ - public void setLinkPanel(JPanel linkPanel) { - JPanel oldLinkPanel = _linkPanel; - _linkPanel = linkPanel; - firePropertyChange(LINK_PANEL, oldLinkPanel, _linkPanel); - } - - /** - * Returns the formatted text field used to edit the date selection. - *

                - * Clients should NOT use this method. It is provided to temporarily support - * the PLAF code. - * - * @return the formatted text field - */ -// @Deprecated - public JFormattedTextField getEditor() { - return _dateField; - } - - /** - * Sets the editor. The editor's editable and enabled properties are - * set the corresponding properties of the JXDatePicker.

                - * - * The default is created and set by the UI delegate. - *

                - * Clients should NOT use this method. It is provided to temporarily support - * the PLAF code. - * - * @param editor the formatted input. - * @throws NullPointerException if editor is null. - * - * @see #getEditor() - */ -// @Deprecated - public void setEditor(JFormattedTextField editor) { - Contract.asNotNull(editor, "editor must not be null"); - JFormattedTextField oldEditor = _dateField; - _dateField = editor; - firePropertyChange(EDITOR, oldEditor, _dateField); - } - - @Override - public void setComponentOrientation(ComponentOrientation orientation) { - super.setComponentOrientation(orientation); - _monthView.setComponentOrientation(orientation); - } - - /** - * Returns true if the current value being edited is valid. - * - * @return true if the current value being edited is valid. - */ - public boolean isEditValid() { - return _dateField.isEditValid(); - } - - /** - * Commits the editor's changes and notifies ActionListeners. - * - * Forces the current value to be taken from the AbstractFormatter and - * set as the current value. This has no effect if there is no current - * AbstractFormatter installed. - * - * @throws ParseException Throws parse exception if the date - * can not be parsed. - */ - public void commitEdit() throws ParseException { - try { - _dateField.commitEdit(); - fireActionPerformed(COMMIT_KEY); - } catch (ParseException e) { - // re-throw - throw e; - } - } - - /** - * Cancels the editor's changes and notifies ActionListeners. - * - */ - public void cancelEdit() { - // hmmm... no direct api? - _dateField.setValue(_dateField.getValue()); - fireActionPerformed(CANCEL_KEY); - } - - /** - * Sets the editable property. If false, ...? - * - * The default value is true. - * - * @param value - * @see #isEditable() - */ - public void setEditable(boolean value) { - boolean oldEditable = isEditable(); - editable = value; - firePropertyChange("editable", oldEditable, editable); - if (editable != oldEditable) { - repaint(); - } - } - - /** - * Returns the editable property. - * - * @return {@code true} if the picker is editable; {@code false} otherwise - */ - public boolean isEditable() { - return editable; - } - - /** - * Returns the font that is associated with the editor of this date picker. - */ - @Override - public Font getFont() { - return getEditor().getFont(); - } - - /** - * Set the font for the editor associated with this date picker. - */ - @Override - public void setFont(final Font font) { - getEditor().setFont(font); - } - - /** - * Sets the lightWeightPopupEnabled property, which - * provides a hint as to whether or not a lightweight - * Component should be used to contain the - * JXDatePicker, versus a heavyweight - * Component such as a Panel - * or a Window. The decision of lightweight - * versus heavyweight is ultimately up to the - * JXDatePicker. Lightweight windows are more - * efficient than heavyweight windows, but lightweight - * and heavyweight components do not mix well in a GUI. - * If your application mixes lightweight and heavyweight - * components, you should disable lightweight popups. - * The default value for the lightWeightPopupEnabled - * property is true, unless otherwise specified - * by the look and feel. Some look and feels always use - * heavyweight popups, no matter what the value of this property. - *

                - * See the article Mixing Heavy and Light Components - * on - * The Swing Connection - * This method fires a property changed event. - * - * @param aFlag if true, lightweight popups are desired - * @beaninfo bound: true - * expert: true - * description: Set to false to require heavyweight popups. - */ - public void setLightWeightPopupEnabled(boolean aFlag) { - boolean oldFlag = lightWeightPopupEnabled; - lightWeightPopupEnabled = aFlag; - firePropertyChange("lightWeightPopupEnabled", oldFlag, lightWeightPopupEnabled); - } - - /** - * Gets the value of the lightWeightPopupEnabled - * property. - * - * @return the value of the lightWeightPopupEnabled - * property - * @see #setLightWeightPopupEnabled - */ - public boolean isLightWeightPopupEnabled() { - return lightWeightPopupEnabled; - } - - /** - * Get the baseline for the specified component, or a value less - * than 0 if the baseline can not be determined. The baseline is measured - * from the top of the component. - * - * @param width Width of the component to determine baseline for. - * @param height Height of the component to determine baseline for. - * @return baseline for the specified component - */ - @Override - public int getBaseline(int width, int height) { - return ((DatePickerUI) ui).getBaseline(width, height); - } - - - /** - * Adds an ActionListener. - *

                - * The ActionListener will receive an ActionEvent when a selection has - * been made. - * - * @param l The ActionListener that is to be notified - */ - public void addActionListener(ActionListener l) { - listenerMap.add(ActionListener.class, l); - } - - /** - * Removes an ActionListener. - * - * @param l The action listener to remove. - */ - public void removeActionListener(ActionListener l) { - listenerMap.remove(ActionListener.class, l); - } - - @Override - @SuppressWarnings("unchecked") - public T[] getListeners(Class listenerType) { - java.util.List listeners = listenerMap.getListeners(listenerType); - T[] result; - if (!listeners.isEmpty()) { - //noinspection unchecked - result = (T[]) java.lang.reflect.Array.newInstance(listenerType, listeners.size()); - result = listeners.toArray(result); - } else { - result = super.getListeners(listenerType); - } - return result; - } - - - /** - * Fires an ActionEvent with the given actionCommand - * to all listeners. - */ - protected void fireActionPerformed(String actionCommand) { - ActionListener[] listeners = getListeners(ActionListener.class); - ActionEvent e = null; - - for (ActionListener listener : listeners) { - if (e == null) { - e = new ActionEvent(this, - ActionEvent.ACTION_PERFORMED, - actionCommand); - } - listener.actionPerformed(e); - } - } - - /** - * Adds a PopupMenuListener.

                - * - * PENDING JW: the canceled method is never called due to internal - * interference in BasicDatePickerUI. Probably need to re-visit that. - * - * @param l the PopupMenuListener to add. - */ - public void addPopupMenuListener(PopupMenuListener l) { - listenerMap.add(PopupMenuListener.class, l); - } - - /** - * Removes a PopupMenuListener. - * - * @param l the PopupMenuListener to remove. - */ - public void removePopupMenuListener(PopupMenuListener l) { - listenerMap.remove(PopupMenuListener.class, l); - } - - /** - * Returns an array containing all PopupMenuListeners which are - * registered to this picker. - * - * @return an array containing all PopupMenuListeners which are - * registered to this picker, guaranteed to be never null. - */ - public PopupMenuListener[] getPopupMenuListeners() { - return getListeners(PopupMenuListener.class); - } - - /** - * Pes: added setLocale method to refresh link text on locale changes - */ - private final class TodayPanel extends JXPanel { - private TodayAction todayAction; - private JXHyperlink todayLink; - - TodayPanel() { - super(new FlowLayout()); - setBackgroundPainter(new MattePainter(new GradientPaint(0, 0, new Color(238, 238, 238), 0, 1, Color.WHITE))); - todayAction = new TodayAction(); - todayLink = new JXHyperlink(todayAction); - todayLink.addMouseListener(createDoubleClickListener()); - Color textColor = new Color(16, 66, 104); - todayLink.setUnclickedColor(textColor); - todayLink.setClickedColor(textColor); - add(todayLink); - } - - /** - * @return - */ - private MouseListener createDoubleClickListener() { - MouseAdapter adapter = new MouseAdapter() { - - @Override - public void mousePressed(MouseEvent e) { - if (e.getClickCount() != 2) return; - todayAction.select = true; - } - - }; - return adapter; - } - - @Override - protected void paintComponent(Graphics g) { - super.paintComponent(g); - g.setColor(new Color(187, 187, 187)); - g.drawLine(0, 0, getWidth(), 0); - g.setColor(new Color(221, 221, 221)); - g.drawLine(0, 1, getWidth(), 1); - } - - /** - * {@inheritDoc}

                - * Overridden to update the link format and hyperlink text. - */ - @Override - public void setLocale(Locale l) { - super.setLocale(l); - updateLinkFormat(); - todayLink.setText(getLinkFormat().format(new Object[]{getLinkDay()})); - } - - private final class TodayAction extends AbstractAction { - boolean select; - TodayAction() { - super(getLinkFormat().format(new Object[]{getLinkDay()})); - Calendar cal = _monthView.getCalendar(); - cal.setTime(getLinkDay()); - putValue(NAME, getLinkFormat().format(new Object[] {cal.getTime()})); - } - - @Override - public void actionPerformed(ActionEvent ae) { - String key = select ? JXDatePicker.HOME_COMMIT_KEY : JXDatePicker.HOME_NAVIGATE_KEY; - select = false; - Action delegate = getActionMap().get(key); - /* - * PatrykRy: Commit today date only when commit action is enabled. - * Home navigate is always enabled. - */ - if (delegate != null && delegate.isEnabled()) { - delegate.actionPerformed(null); - } - - } - } - } - - -} - \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/JXDialog.java b/src/main/java/org/jdesktop/swingx/JXDialog.java deleted file mode 100644 index 0efa520725..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXDialog.java +++ /dev/null @@ -1,403 +0,0 @@ -/* - * $Id: JXDialog.java 4172 2012-04-17 14:54:00Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.action.BoundAction; -import org.jdesktop.swingx.plaf.LookAndFeelAddons; -import org.jdesktop.swingx.plaf.UIManagerExt; - -import javax.swing.*; -import javax.swing.plaf.basic.BasicOptionPaneUI; -import java.awt.*; -import java.util.Locale; - -/** - * First cut for enhanced Dialog. The idea is to have a pluggable content - * from which the dialog auto-configures all its "dialogueness". - * - *

                  - *
                • accepts a content and configures itself from content's properties - - * replaces the execute action from the appropriate action in content's action map (if any) - * and set's its title from the content's name. - *
                • registers stand-in actions for close/execute with the dialog's RootPane - *
                • registers keyStrokes for esc/enter to trigger the close/execute actions - *
                • takes care of building the button panel using the close/execute actions. - *
                - * - *
                  - *
                • TODO: add link to forum discussion, wiki summary? - *
                • PENDING: add support for vetoing the close. - *
                • PENDING: add complete set of constructors - *
                • PENDING: add windowListener to delegate to close action - *
                - * - * @author Jeanette Winzenburg - * @author Karl Schaefer - */ -@JavaBean -public class JXDialog extends JDialog { - - static { - // Hack to enforce loading of SwingX framework ResourceBundle - LookAndFeelAddons.getAddon(); - } - - public static final String EXECUTE_ACTION_COMMAND = "execute"; - public static final String CLOSE_ACTION_COMMAND = "close"; - public static final String UIPREFIX = "XDialog."; - - protected JComponent content; - - /** - * Creates a non-modal dialog with the given component as - * content and without specified owner. A shared, hidden frame will be - * set as the owner of the dialog. - *

                - * @param content the component to show and to auto-configure from. - */ - public JXDialog(JComponent content) { - super(); - setContent(content); - } - - - /** - * Creates a non-modal dialog with the given component as content and the - * specified Frame as owner. - *

                - * @param frame the owner - * @param content the component to show and to auto-configure from. - */ - public JXDialog(Frame frame, JComponent content) { - super(frame); - setContent(content); - } - - /** - * Creates a non-modal dialog with the given component as content and the - * specified Dialog as owner. - *

                - * @param dialog the owner - * @param content the component to show and to auto-configure from. - */ - public JXDialog(Dialog dialog, JComponent content) { - super(dialog); - setContent(content); - } - - /** - * Creates a non-modal dialog with the given component as content and the - * specified Window as owner. - *

                - * @param window the owner - * @param content the component to show and to auto-configure from. - */ - public JXDialog(Window window, JComponent content) { - super(window); - setContentPane(content); - } - - /** - * {@inheritDoc} - */ - @Override - protected JXRootPane createRootPane() { - return new JXRootPane(); - } - - /** - * {@inheritDoc} - */ - @Override - public JXRootPane getRootPane() { - return (JXRootPane) super.getRootPane(); - } - - /** - * Sets the status bar property on the underlying {@code JXRootPane}. - * - * @param statusBar - * the {@code JXStatusBar} which is to be the status bar - * @see #getStatusBar() - * @see JXRootPane#setStatusBar(JXStatusBar) - */ - public void setStatusBar(JXStatusBar statusBar) { - getRootPane().setStatusBar(statusBar); - } - - /** - * Returns the value of the status bar property from the underlying - * {@code JXRootPane}. - * - * @return the {@code JXStatusBar} which is the current status bar - * @see #setStatusBar(JXStatusBar) - * @see JXRootPane#getStatusBar() - */ - public JXStatusBar getStatusBar() { - return getRootPane().getStatusBar(); - } - - /** - * Sets the tool bar property on the underlying {@code JXRootPane}. - * - * @param toolBar - * the {@code JToolBar} which is to be the tool bar - * @see #getToolBar() - * @see JXRootPane#setToolBar(JToolBar) - */ - public void setToolBar(JToolBar toolBar) { - getRootPane().setToolBar(toolBar); - } - - /** - * Returns the value of the tool bar property from the underlying - * {@code JXRootPane}. - * - * @return the {@code JToolBar} which is the current tool bar - * @see #setToolBar(JToolBar) - * @see JXRootPane#getToolBar() - */ - public JToolBar getToolBar() { - return getRootPane().getToolBar(); - } - - /** - * PENDING: widen access - this could be public to make the content really - * pluggable? - * - * @param content - */ - private void setContent(JComponent content) { - if (this.content != null) { - throw new IllegalStateException("content must not be set more than once"); - } - initActions(); - Action contentCloseAction = content.getActionMap().get(CLOSE_ACTION_COMMAND); - if (contentCloseAction != null) { - putAction(CLOSE_ACTION_COMMAND, contentCloseAction); - } - Action contentExecuteAction = content.getActionMap().get(EXECUTE_ACTION_COMMAND); - if (contentExecuteAction != null) { - putAction(EXECUTE_ACTION_COMMAND, contentExecuteAction); - } - this.content = content; - build(); - setTitleFromContent(); - } - - /** - * Infers and sets this dialog's title from the the content. - * Does nothing if content is null. - * - * Here: uses the content's name as title. - */ - protected void setTitleFromContent() { - if (content == null) return; - setTitle(content.getName()); - } - - /** - * pre: content != null. - * - */ - private void build() { - JComponent contentBox = new Box(BoxLayout.PAGE_AXIS); - contentBox.add(content); - JComponent buttonPanel = createButtonPanel(); - contentBox.add(buttonPanel); - contentBox.setBorder(BorderFactory.createEmptyBorder(14, 14, 14, 14)); -// content.applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); - -// fieldPanel.setAlignmentX(); -// buttonPanel.setAlignmentX(Component.RIGHT_ALIGNMENT); - add(contentBox); - - } - - /** - * {@inheritDoc} - * - * Overridden to check if content is available.

                - * PENDING: doesn't make sense - the content is immutable and guaranteed - * to be not null. - */ - @Override - public void setVisible(boolean visible) { - if (content == null) throw - new IllegalStateException("content must be built before showing the dialog"); - super.setVisible(visible); - } - -//------------------------ dynamic locale support - - - /** - * {@inheritDoc}

                - * - * Overridden to set the content's Locale and then updated - * this dialog's internal state.

                - * - * - */ - @Override - public void setLocale(Locale l) { - /* - * NOTE: this is called from super's constructor as one of the - * first methods (prior to setting the rootPane!). So back out - * - */ - if (content != null) { - content.setLocale(l); - updateLocaleState(l); - } - super.setLocale(l); - } - - /** - * Updates this dialog's locale-dependent state. - * - * Here: updates title and actions. - *

                - * - * - * @see #setLocale(Locale) - */ - protected void updateLocaleState(Locale locale) { - setTitleFromContent(); - for (Object key : getRootPane().getActionMap().allKeys()) { - if (key instanceof String) { - Action contentAction = content.getActionMap().get(key); - Action rootPaneAction = getAction(key); - if ((!rootPaneAction.equals(contentAction))) { - String keyString = getUIString((String) key, locale); - if (!key.equals(keyString)) { - rootPaneAction.putValue(Action.NAME, keyString); - } - } - } - } - } - - /** - * The callback method executed when closing the dialog.

                - * Here: calls dispose. - * - */ - public void doClose() { - dispose(); - } - - private void initActions() { - Action defaultAction = createCloseAction(); - putAction(CLOSE_ACTION_COMMAND, defaultAction); - putAction(EXECUTE_ACTION_COMMAND, defaultAction); - } - - private Action createCloseAction() { - String actionName = getUIString(CLOSE_ACTION_COMMAND); - BoundAction action = new BoundAction(actionName, - CLOSE_ACTION_COMMAND); - action.registerCallback(this, "doClose"); - return action; - } - - /** - * create the dialog button controls. - * - * - * @return panel containing button controls - */ - protected JComponent createButtonPanel() { - // PENDING: this is a hack until we have a dedicated ButtonPanel! - JPanel panel = new JPanel(new BasicOptionPaneUI.ButtonAreaLayout(true, 6)) - { - @Override - public Dimension getMaximumSize() { - return getPreferredSize(); - } - }; - - panel.setBorder(BorderFactory.createEmptyBorder(9, 0, 0, 0)); - Action executeAction = getAction(EXECUTE_ACTION_COMMAND); - Action closeAction = getAction(CLOSE_ACTION_COMMAND); - - JButton defaultButton = new JButton(executeAction); - panel.add(defaultButton); - getRootPane().setDefaultButton(defaultButton); - - if (executeAction != closeAction) { - JButton b = new JButton(closeAction); - panel.add(b); - getRootPane().setCancelButton(b); - } - - return panel; - } - - /** - * convenience wrapper to access rootPane's actionMap. - * @param key - * @param action - */ - private void putAction(Object key, Action action) { - getRootPane().getActionMap().put(key, action); - } - - /** - * convenience wrapper to access rootPane's actionMap. - * - * @param key - * @return root pane's ActionMap - */ - private Action getAction(Object key) { - return getRootPane().getActionMap().get(key); - } - - /** - * Returns a potentially localized value from the UIManager. The given key - * is prefixed by this component|s UIPREFIX before doing the - * lookup. The lookup respects this table's current locale - * property. Returns the key, if no value is found. - * - * @param key the bare key to look up in the UIManager. - * @return the value mapped to UIPREFIX + key or key if no value is found. - */ - protected String getUIString(String key) { - return getUIString(key, getLocale()); - } - - /** - * Returns a potentially localized value from the UIManager for the - * given locale. The given key - * is prefixed by this component's UIPREFIX before doing the - * lookup. Returns the key, if no value is found. - * - * @param key the bare key to look up in the UIManager. - * @param locale the locale use for lookup - * @return the value mapped to UIPREFIX + key in the given locale, - * or key if no value is found. - */ - protected String getUIString(String key, Locale locale) { - String text = UIManagerExt.getString(UIPREFIX + key, locale); - return text != null ? text : key; - } -} diff --git a/src/main/java/org/jdesktop/swingx/JXEditorPane.java b/src/main/java/org/jdesktop/swingx/JXEditorPane.java deleted file mode 100644 index 564b5a6d4c..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXEditorPane.java +++ /dev/null @@ -1,869 +0,0 @@ -/* - * $Id: JXEditorPane.java 4147 2012-02-01 17:13:24Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.action.ActionManager; -import org.jdesktop.swingx.action.Targetable; -import org.jdesktop.swingx.action.TargetableSupport; -import org.jdesktop.swingx.plaf.UIAction; -import org.jdesktop.swingx.search.SearchFactory; -import org.jdesktop.swingx.search.Searchable; - -import javax.swing.*; -import javax.swing.event.CaretEvent; -import javax.swing.event.CaretListener; -import javax.swing.event.UndoableEditEvent; -import javax.swing.event.UndoableEditListener; -import javax.swing.text.*; -import javax.swing.text.html.HTML; -import javax.swing.text.html.HTMLDocument; -import javax.swing.text.html.HTMLEditorKit; -import javax.swing.undo.CannotRedoException; -import javax.swing.undo.CannotUndoException; -import javax.swing.undo.UndoManager; -import java.awt.*; -import java.awt.datatransfer.Clipboard; -import java.awt.datatransfer.DataFlavor; -import java.awt.datatransfer.Transferable; -import java.awt.event.ActionEvent; -import java.awt.event.ItemEvent; -import java.awt.event.ItemListener; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.io.IOException; -import java.io.Reader; -import java.io.StringReader; -import java.net.URL; -import java.util.HashMap; -import java.util.Map; -import java.util.Vector; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.MatchResult; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - *

                - * {@code JXEditorPane} offers enhanced functionality over the standard {@code - * JEditorPane}. Unlike its parent, {@code JXEdtiorPane} {@link - * JEditorPane#HONOR_DISPLAY_PROPERTIES honors display properties} by default. - * Users can revert to the behavior of {@code JEditorPane} by setting the - * property to {@code false}. - *

                - *

                Additional Features

                - *
                - *
                - * Improved text editing
                - *
                - * The standard text component commands for cut, copy, and - * paste used enhanced selection methods. The commands will only be - * active if there is text to cut or copy selected or valid text in the - * clipboard to paste.
                - *
                - * Improved HTML editing
                - *
                - * Using the context-sensitive approach for the standard text commands, {@code - * JXEditorPane} provides HTML editing commands that alter functionality - * depending on the document state. Currently, the user can quick-format the - * document with headers (H# tags), paragraphs, and breaks.
                - *
                - * Built-in UndoManager
                - *
                - * Text components provide {@link UndoableEditEvent}s. {@code JXEditorPane} - * places those events in an {@link UndoManager} and provides - * undo/redo commands. Undo and redo are context-sensitive (like - * the text commands) and will only be active if it is possible to perform the - * command.
                - *
                - * Built-in search
                - *
                - * Using SwingX {@linkplain SearchFactory search mechanisms}, {@code - * JXEditorPane} provides search capabilities, allowing the user to find text - * within the document.
                - *
                - *

                Example

                - *

                - * Creating a {@code JXEditorPane} is no different than creating a {@code - * JEditorPane}. However, the following example demonstrates the best way to - * access the improved command functionality. - * - *

                - * JXEditorPane editorPane = new JXEditorPane("some URL");
                - * add(editorPane);
                - * JToolBar toolBar = ActionContainerFactory.createToolBar(editorPane.getCommands[]);
                - * toolBar.addSeparator();
                - * toolBar.add(editorPane.getParagraphSelector());
                - * setToolBar(toolBar);
                - * 
                - *

                - * - * @author Mark Davidson - */ -@JavaBean -public class JXEditorPane extends JEditorPane implements /*Searchable, */Targetable { - - private static final Logger LOG = Logger.getLogger(JXEditorPane.class - .getName()); - - private UndoableEditListener undoHandler; - private UndoManager undoManager; - private CaretListener caretHandler; - private JComboBox selector; - - // The ids of supported actions. Perhaps this should be public. - private final static String ACTION_FIND = "find"; - private final static String ACTION_UNDO = "undo"; - private final static String ACTION_REDO = "redo"; - /* - * These next 3 actions are part of a *HACK* to get cut/copy/paste - * support working in the same way as find, undo and redo. in JTextComponent - * the cut/copy/paste actions are _not_ added to the ActionMap. Instead, - * a default "transfer handler" system is used, apparently to get the text - * onto the system clipboard. - * Since there aren't any CUT/COPY/PASTE actions in the JTextComponent's action - * map, they cannot be referenced by the action framework the same way that - * find/undo/redo are. So, I added the actions here. The really hacky part - * is that by defining an Action to go along with the cut/copy/paste keys, - * I loose the default handling in the cut/copy/paste routines. So, I have - * to remove cut/copy/paste from the action map, call the appropriate - * method (cut, copy, or paste) and then add the action back into the - * map. Yuck! - */ - private final static String ACTION_CUT = "cut"; - private final static String ACTION_COPY = "copy"; - private final static String ACTION_PASTE = "paste"; - - private TargetableSupport targetSupport = new TargetableSupport(this); - private Searchable searchable; - - /** - * Creates a new JXEditorPane. - * The document model is set to null. - */ - public JXEditorPane() { - init(); - } - - /** - * Creates a JXEditorPane based on a string containing - * a URL specification. - * - * @param url the URL - * @exception IOException if the URL is null or - * cannot be accessed - */ - public JXEditorPane(String url) throws IOException { - super(url); - init(); - } - - /** - * Creates a JXEditorPane that has been initialized - * to the given text. This is a convenience constructor that calls the - * setContentType and setText methods. - * - * @param type mime type of the given text - * @param text the text to initialize with; may be null - * @exception NullPointerException if the type parameter - * is null - */ - public JXEditorPane(String type, String text) { - super(type, text); - init(); - } - - /** - * Creates a JXEditorPane based on a specified URL for input. - * - * @param initialPage the URL - * @exception IOException if the URL is null - * or cannot be accessed - */ - public JXEditorPane(URL initialPage) throws IOException { - super(initialPage); - init(); - } - - private void init() { - putClientProperty(HONOR_DISPLAY_PROPERTIES, true); - setEditorKitForContentType("text/html", new SloppyHTMLEditorKit()); - addPropertyChangeListener(new PropertyHandler()); - getDocument().addUndoableEditListener(getUndoableEditListener()); - initActions(); - } - - private class PropertyHandler implements PropertyChangeListener { - @Override - public void propertyChange(PropertyChangeEvent evt) { - String name = evt.getPropertyName(); - if (name.equals("document")) { - Document doc = (Document)evt.getOldValue(); - if (doc != null) { - doc.removeUndoableEditListener(getUndoableEditListener()); - } - - doc = (Document)evt.getNewValue(); - if (doc != null) { - doc.addUndoableEditListener(getUndoableEditListener()); - } - } - } - - } - - // pp for testing - CaretListener getCaretListener() { - return caretHandler; - } - - // pp for testing - UndoableEditListener getUndoableEditListener() { - if (undoHandler == null) { - undoHandler = new UndoHandler(); - undoManager = new UndoManager(); - } - return undoHandler; - } - - /** - * Overidden to perform document initialization based on type. - */ - @Override - public void setEditorKit(EditorKit kit) { - super.setEditorKit(kit); - - if (kit instanceof StyledEditorKit) { - if (caretHandler == null) { - caretHandler = new CaretHandler(); - } - addCaretListener(caretHandler); - } - } - - /** - * Register the actions that this class can handle. - */ - protected void initActions() { - ActionMap map = getActionMap(); - map.put(ACTION_FIND, new Actions(ACTION_FIND)); - map.put(ACTION_UNDO, new Actions(ACTION_UNDO)); - map.put(ACTION_REDO, new Actions(ACTION_REDO)); - map.put(ACTION_CUT, new Actions(ACTION_CUT)); - map.put(ACTION_COPY, new Actions(ACTION_COPY)); - map.put(ACTION_PASTE, new Actions(ACTION_PASTE)); - - KeyStroke findStroke = SearchFactory.getInstance().getSearchAccelerator(); - getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(findStroke, "find"); - } - - // undo/redo implementation - - private class UndoHandler implements UndoableEditListener { - @Override - public void undoableEditHappened(UndoableEditEvent evt) { - undoManager.addEdit(evt.getEdit()); - updateActionState(); - } - } - - /** - * Updates the state of the actions in response to an undo/redo operation.

                - * - */ - private void updateActionState() { - // Update the state of the undo and redo actions - // JW: fiddling with actionManager's actions state? I'm pretty sure - // we don't want that: the manager will get nuts with multiple - // components with different state. - // It's up to whatever manager to listen - // to our changes and update itself accordingly. Which is not - // well supported with the current design ... nobody - // really cares about enabled as it should. - // - Runnable doEnabled = new Runnable() { - @Override - public void run() { - ActionManager manager = ActionManager.getInstance(); - manager.setEnabled(ACTION_UNDO, undoManager.canUndo()); - manager.setEnabled(ACTION_REDO, undoManager.canRedo()); - } - }; - SwingUtilities.invokeLater(doEnabled); - } - - /** - * A small class which dispatches actions. - * TODO: Is there a way that we can make this static? - * JW: these if-constructs are totally crazy ... we live in OO world! - * - */ - private class Actions extends UIAction { - Actions(String name) { - super(name); - } - - @Override - public void actionPerformed(ActionEvent evt) { - String name = getName(); - if (ACTION_FIND.equals(name)) { - find(); - } - else if (ACTION_UNDO.equals(name)) { - try { - undoManager.undo(); - } catch (CannotUndoException ex) { - LOG.info("Could not undo"); - } - updateActionState(); - } - else if (ACTION_REDO.equals(name)) { - try { - undoManager.redo(); - } catch (CannotRedoException ex) { - LOG.info("Could not redo"); - } - updateActionState(); - } else if (ACTION_CUT.equals(name)) { - ActionMap map = getActionMap(); - map.remove(ACTION_CUT); - cut(); - map.put(ACTION_CUT, this); - } else if (ACTION_COPY.equals(name)) { - ActionMap map = getActionMap(); - map.remove(ACTION_COPY); - copy(); - map.put(ACTION_COPY, this); - } else if (ACTION_PASTE.equals(name)) { - ActionMap map = getActionMap(); - map.remove(ACTION_PASTE); - paste(); - map.put(ACTION_PASTE, this); - } - else { - LOG.fine("ActionHandled: " + name); - } - - } - - @Override - public boolean isEnabled(Object sender) { - String name = getName(); - if (ACTION_UNDO.equals(name)) { - return isEditable() && undoManager.canUndo(); - } - if (ACTION_REDO.equals(name)) { - return isEditable() && undoManager.canRedo(); - } - if (ACTION_PASTE.equals(name)) { - if (!isEditable()) return false; - // is this always possible? - boolean dataOnClipboard = false; - try { - dataOnClipboard = getToolkit() - .getSystemClipboard().getContents(null) != null; - } catch (Exception e) { - // can't do anything - clipboard unaccessible - } - return dataOnClipboard; - } - boolean selectedText = getSelectionEnd() - - getSelectionStart() > 0; - if (ACTION_CUT.equals(name)) { - return isEditable() && selectedText; - } - if (ACTION_COPY.equals(name)) { - return selectedText; - } - if (ACTION_FIND.equals(name)) { - return getDocument().getLength() > 0; - } - return true; - } - - - } - - /** - * Retrieves a component which will be used as the paragraph selector. - * This can be placed in the toolbar. - *

                - * Note: This is only valid for the HTMLEditorKit - */ - public JComboBox getParagraphSelector() { - if (selector == null) { - selector = new ParagraphSelector(); - } - return selector; - } - - /** - * A control which should be placed in the toolbar to enable - * paragraph selection. - */ - private class ParagraphSelector extends JComboBox implements ItemListener { - - private Map itemMap; - - public ParagraphSelector() { - - // The item map is for rendering - itemMap = new HashMap(); - itemMap.put(HTML.Tag.P, "Paragraph"); - itemMap.put(HTML.Tag.H1, "Heading 1"); - itemMap.put(HTML.Tag.H2, "Heading 2"); - itemMap.put(HTML.Tag.H3, "Heading 3"); - itemMap.put(HTML.Tag.H4, "Heading 4"); - itemMap.put(HTML.Tag.H5, "Heading 5"); - itemMap.put(HTML.Tag.H6, "Heading 6"); - itemMap.put(HTML.Tag.PRE, "Preformatted"); - - // The list of items - Vector items = new Vector(); - items.addElement(HTML.Tag.P); - items.addElement(HTML.Tag.H1); - items.addElement(HTML.Tag.H2); - items.addElement(HTML.Tag.H3); - items.addElement(HTML.Tag.H4); - items.addElement(HTML.Tag.H5); - items.addElement(HTML.Tag.H6); - items.addElement(HTML.Tag.PRE); - - setModel(new DefaultComboBoxModel(items)); - setRenderer(new ParagraphRenderer()); - addItemListener(this); - setFocusable(false); - } - - @Override - public void itemStateChanged(ItemEvent evt) { - if (evt.getStateChange() == ItemEvent.SELECTED) { - applyTag((HTML.Tag)evt.getItem()); - } - } - - private class ParagraphRenderer extends DefaultListCellRenderer { - - public ParagraphRenderer() { - setOpaque(true); - } - - @Override - public Component getListCellRendererComponent(JList list, - Object value, - int index, - boolean isSelected, - boolean cellHasFocus) { - super.getListCellRendererComponent(list, value, index, isSelected, - cellHasFocus); - - setText((String)itemMap.get(value)); - - return this; - } - } - - - // TODO: Should have a rendererer which does stuff like: - // Paragraph, Heading 1, etc... - } - - /** - * Applys the tag to the current selection - */ - protected void applyTag(HTML.Tag tag) { - Document doc = getDocument(); - if (!(doc instanceof HTMLDocument)) { - return; - } - HTMLDocument hdoc = (HTMLDocument)doc; - int start = getSelectionStart(); - int end = getSelectionEnd(); - - Element element = hdoc.getParagraphElement(start); - MutableAttributeSet newAttrs = new SimpleAttributeSet(element.getAttributes()); - newAttrs.addAttribute(StyleConstants.NameAttribute, tag); - - hdoc.setParagraphAttributes(start, end - start, newAttrs, true); - } - - /** - * The paste method has been overloaded to strip off the tags - * This doesn't really work. - */ - @Override - public void paste() { - Clipboard clipboard = getToolkit().getSystemClipboard(); - Transferable content = clipboard.getContents(this); - if (content != null) { - DataFlavor[] flavors = content.getTransferDataFlavors(); - try { - for (int i = 0; i < flavors.length; i++) { - if (String.class.equals(flavors[i].getRepresentationClass())) { - Object data = content.getTransferData(flavors[i]); - - if (flavors[i].isMimeTypeEqual("text/plain")) { - // This works but we lose all the formatting. - replaceSelection(data.toString()); - break; - } - } - } - } catch (Exception ex) { - // TODO change to something meaningful - when can this acutally happen? - LOG.log(Level.FINE, "What can produce a problem with data flavor?", ex); - } - } - } - - private void find() { - SearchFactory.getInstance().showFindInput(this, getSearchable()); - } - - /** - * - * @return a not-null Searchable for this editor. - */ - public Searchable getSearchable() { - if (searchable == null) { - searchable = new DocumentSearchable(); - } - return searchable; - } - - /** - * sets the Searchable for this editor. If null, a default - * searchable will be used. - * - * @param searchable - */ - public void setSearchable(Searchable searchable) { - this.searchable = searchable; - } - - /** - * A {@code Searchable} implementation for {@code Document}s. - */ - public class DocumentSearchable implements Searchable { - @Override - public int search(String searchString) { - return search(searchString, -1); - } - - @Override - public int search(String searchString, int columnIndex) { - return search(searchString, columnIndex, false); - } - - @Override - public int search(String searchString, int columnIndex, boolean backward) { - Pattern pattern = null; - if (!isEmpty(searchString)) { - pattern = Pattern.compile(searchString, 0); - } - return search(pattern, columnIndex, backward); - } - - /** - * checks if the searchString should be interpreted as empty. - * here: returns true if string is null or has zero length. - * - * TODO: This should be in a utility class. - * - * @param searchString - * @return true if string is null or has zero length - */ - protected boolean isEmpty(String searchString) { - return (searchString == null) || searchString.length() == 0; - } - - @Override - public int search(Pattern pattern) { - return search(pattern, -1); - } - - @Override - public int search(Pattern pattern, int startIndex) { - return search(pattern, startIndex, false); - } - - int lastFoundIndex = -1; - - MatchResult lastMatchResult; - String lastRegEx; - /** - * @return start position of matching string or -1 - */ - @Override - public int search(Pattern pattern, final int startIndex, - boolean backwards) { - if ((pattern == null) - || (getDocument().getLength() == 0) - || ((startIndex > -1) && (getDocument().getLength() < startIndex))) { - updateStateAfterNotFound(); - return -1; - } - - int start = startIndex; - if (maybeExtendedMatch(startIndex)) { - if (foundExtendedMatch(pattern, start)) { - return lastFoundIndex; - } - start++; - } - - int length; - if (backwards) { - start = 0; - if (startIndex < 0) { - length = getDocument().getLength() - 1; - } else { - length = -1 + startIndex; - } - } else { - // start = startIndex + 1; - if (start < 0) - start = 0; - length = getDocument().getLength() - start; - } - Segment segment = new Segment(); - - try { - getDocument().getText(start, length, segment); - } catch (BadLocationException ex) { - LOG.log(Level.FINE, - "this should not happen (calculated the valid start/length) " , ex); - } - - Matcher matcher = pattern.matcher(segment.toString()); - MatchResult currentResult = getMatchResult(matcher, !backwards); - if (currentResult != null) { - updateStateAfterFound(currentResult, start); - } else { - updateStateAfterNotFound(); - } - return lastFoundIndex; - - } - - /** - * Search from same startIndex as the previous search. - * Checks if the match is different from the last (either - * extended/reduced) at the same position. Returns true - * if the current match result represents a different match - * than the last, false if no match or the same. - * - * @param pattern - * @param start - * @return true if the current match result represents a different - * match than the last, false if no match or the same. - */ - private boolean foundExtendedMatch(Pattern pattern, int start) { - // JW: logic still needs cleanup... - if (pattern.pattern().equals(lastRegEx)) { - return false; - } - int length = getDocument().getLength() - start; - Segment segment = new Segment(); - - try { - getDocument().getText(start, length, segment); - } catch (BadLocationException ex) { - LOG.log(Level.FINE, - "this should not happen (calculated the valid start/length) " , ex); - } - Matcher matcher = pattern.matcher(segment.toString()); - MatchResult currentResult = getMatchResult(matcher, true); - if (currentResult != null) { - // JW: how to compare match results reliably? - // the group().equals probably isn't the best idea... - // better check pattern? - if ((currentResult.start() == 0) && - (!lastMatchResult.group().equals(currentResult.group()))) { - updateStateAfterFound(currentResult, start); - return true; - } - } - return false; - } - - /** - * Checks if the startIndex is a candidate for trying a re-match. - * - * - * @param startIndex - * @return true if the startIndex should be re-matched, false if not. - */ - private boolean maybeExtendedMatch(final int startIndex) { - return (startIndex >= 0) && (startIndex == lastFoundIndex); - } - - /** - * @param currentResult - * @param offset - * @return the start position of the selected text - */ - private int updateStateAfterFound(MatchResult currentResult, final int offset) { - int end = currentResult.end() + offset; - int found = currentResult.start() + offset; - select(found, end); - getCaret().setSelectionVisible(true); - lastFoundIndex = found; - lastMatchResult = currentResult; - lastRegEx = ((Matcher) lastMatchResult).pattern().pattern(); - return found; - } - - /** - * @param matcher - * @param useFirst whether or not to return after the first match is found. - * @return MatchResult or null - */ - private MatchResult getMatchResult(Matcher matcher, boolean useFirst) { - MatchResult currentResult = null; - while (matcher.find()) { - currentResult = matcher.toMatchResult(); - if (useFirst) break; - } - return currentResult; - } - - /** - */ - private void updateStateAfterNotFound() { - lastFoundIndex = -1; - lastMatchResult = null; - lastRegEx = null; - setCaretPosition(getSelectionEnd()); - } - - } - - @Override - public boolean hasCommand(Object command) { - return targetSupport.hasCommand(command); - } - - @Override - public Object[] getCommands() { - return targetSupport.getCommands(); - } - - @Override - public boolean doCommand(Object command, Object value) { - return targetSupport.doCommand(command, value); - } - - /** - * {@inheritDoc} - */ - @Override - public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { - switch(orientation) { - case SwingConstants.VERTICAL: - return getFontMetrics(getFont()).getHeight(); - case SwingConstants.HORIZONTAL: - return getFontMetrics(getFont()).charWidth('M'); - default: - throw new IllegalArgumentException("Invalid orientation: " + orientation); - } - } - - /** - * Listens to the caret placement and adjusts the editing - * properties as appropriate. - * - * Should add more attributes as required. - */ - private class CaretHandler implements CaretListener { - @Override - public void caretUpdate(CaretEvent evt) { - StyledDocument document = (StyledDocument)getDocument(); - int dot = evt.getDot(); - //SwingX #257--ensure display shows the valid attributes - dot = dot > 0 ? dot - 1 : dot; - - Element elem = document.getCharacterElement(dot); - AttributeSet set = elem.getAttributes(); - - // JW: see comment in updateActionState - ActionManager manager = ActionManager.getInstance(); - manager.setSelected("font-bold", StyleConstants.isBold(set)); - manager.setSelected("font-italic", StyleConstants.isItalic(set)); - manager.setSelected("font-underline", StyleConstants.isUnderline(set)); - - elem = document.getParagraphElement(dot); - set = elem.getAttributes(); - - // Update the paragraph selector if applicable. - if (selector != null) { - selector.setSelectedItem(set.getAttribute(StyleConstants.NameAttribute)); - } - - switch (StyleConstants.getAlignment(set)) { - // XXX There is a bug here. the setSelected method - // should only affect the UI actions rather than propagate - // down into the action map actions. - case StyleConstants.ALIGN_LEFT: - manager.setSelected("left-justify", true); - break; - - case StyleConstants.ALIGN_CENTER: - manager.setSelected("center-justify", true); - break; - - case StyleConstants.ALIGN_RIGHT: - manager.setSelected("right-justify", true); - break; - } - } - } - - /** - * Handles sloppy HTML. This implementation currently only looks for - * tags that have a / at the end (self-closing tags) and fixes them - * to work with the version of HTML supported by HTMLEditorKit - *

                TODO: Need to break this functionality out so it can take pluggable - * replacement code blocks, allowing people to write custom replacement - * routines. The idea is that with some simple modifications a lot more - * sloppy HTML can be rendered correctly. - * - * @author rbair - */ - private static final class SloppyHTMLEditorKit extends HTMLEditorKit { - @Override - public void read(Reader in, Document doc, int pos) throws IOException, BadLocationException { - //read the reader into a String - StringBuffer buffer = new StringBuffer(); - int length; - char[] data = new char[1024]; - while ((length = in.read(data)) != -1) { - buffer.append(data, 0, length); - } - //TODO is this regex right? - StringReader reader = new StringReader(buffer.toString().replaceAll("/>", ">")); - super.read(reader, doc, pos); - } - } -} - diff --git a/src/main/java/org/jdesktop/swingx/JXErrorPane.java b/src/main/java/org/jdesktop/swingx/JXErrorPane.java deleted file mode 100644 index 4abde6853f..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXErrorPane.java +++ /dev/null @@ -1,654 +0,0 @@ -/* - * $Id: JXErrorPane.java 4158 2012-02-03 18:29:40Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.error.ErrorInfo; -import org.jdesktop.swingx.error.ErrorReporter; -import org.jdesktop.swingx.plaf.ErrorPaneAddon; -import org.jdesktop.swingx.plaf.ErrorPaneUI; -import org.jdesktop.swingx.plaf.LookAndFeelAddons; - -import javax.swing.*; -import java.awt.*; -import java.lang.reflect.InvocationTargetException; - -/** - *

                JXErrorPane is a common error component suitable for displaying errors, - * warnings, and exceptional application behavior to users.

                - * - *

                User interaction with the JXErrorPane includes the ability to - * view details associated with the error. This is the primary feature that differentiates - * JXErrorPane from JOptionPane. In addition, - * JXErrorPane specializes in handling unrecoverable errors. If you - * need an error dialog that allows the user to take some action to recover - * from an error (such as "Repair Disk", "Replace All", etc) then you should - * use JOptionPane.

                - * - *

                Data and application state associated with an error are encapsulated - * in the {@link ErrorInfo} class. The - * {@code JXErrorPane} displays the data contained in the {@code ErrorInfo}. - * In addition, {@code ErrorInfo} is passed to the - * {@link ErrorReporter} if the user decides to report - * the incident.

                - * - *

                Basic Usage

                - *

                Typically, the JXErrorPane - * is not created and displayed directly. Instead, one of the static showXXX methods - * are called that create and display the JXErrorPane in a - * JDialog, JFrame, or JInternalFrame.

                - * - *

                These static showXXX methods all follow the same pattern, namely ( - * where XXX could be one of Dialog, Frame, or InternalFrame): - *

                  - *
                • showXXX(Throwable e): This usage allows you to show a default error - * window, detailing the error
                • - *
                • showXXX(Component owner, ErrorInfo info): This usage shows an - * error dialog based on the given ErrorInfo. The component - * argument is the component over which the dialog should be centered.
                • - *
                • showXXX(Component owner, JXErrorPane pane): This usage shows - * an error dialog using the given error pane. This allows you to completely - * modify the pane (perhaps installing a custom UI delegate, etc) to present - * to the user
                • - *
                • createXXX(Component owner, JXErrorPane pane): Creates and returns - * a dialog for presenting the given JXErrorPane, but does not - * show it. This allows the developer to modify properties of the dialog - * prior to display
                • - *

                - * - *

                Following are some examples and further discussion regarding some of these - * static methods. Example of the most basic usage: - *

                
                - *      try {
                - *          //do stuff.... something throws an exception in here
                - *      } catch (Exception e) {
                - *          JXErrorPane.showDialog(e);
                - *      }
                - * 
                . Alternatively there are showFrame and - * showInternalFrame variants of each of the showDialog - * methods described in this API.

                - * - *

                While this is the simplest usage, it is not the recommended approach for - * most errors since it yields the most difficult messages for users to understand. - * Instead it is recommended to provide a more useful message for users. For example: - *

                
                - *      URL url = null;
                - *      try {
                - *          url = new URL(userSuppliedUrl);
                - *      } catch (MalformedURLException e) {
                - *          String msg = "The web resource you entered is not formatted"
                - *                      + " correctly.";
                - *          String details = "<html>Web resources should begin with \"http://\""
                - *                      + " and cannot contain any spaces. Below are a few"
                - *                      + " more guidelines.<ul>"
                - *                      + getURLGuidelines()
                - *                      + "</ul></html>";
                - *          JXErrorPane.showDialog(myWindow, "Unknown Resource", msg, details, e);
                - *          return false;
                - *      }
                - * 

                - * - *

                Before showing the JXErrorPane in a frame or dialog, you may modify - * the appearance and behavior of the JXErrorPane by setting one or more of its bean - * properties. For example, to modify the icon shown with a particular - * instance of a JXErrorPane, you might do the following: - *

                
                - *      JXErrorPane pane = new JXErrorPane();
                - *      pane.setErrorIcon(myErrorIcon);
                - *      pane.setErrorInfo(new ErrorInfo("Fatal Error", exception));
                - *      JXErrorPane.showDialog(null, pane);
                - * 

                - * - *

                JXErrorPane may also be configured with a "Report" button which allows - * the user to send a bug report, typically through email. This is done through - * the pluggable {@link ErrorReporter} class. Simply instantiate - * some custom subclass of ErrorReporter and pass the instance into the - * {@link #setErrorReporter} method.

                - * - *

                JXErrorPane can also be used for displaying fatal error messages to - * users. Fatal messages indicate a serious error in the application that cannot - * be corrected and that must result in the termination of the application. - * After the close of a fatal error dialog, the application should - * automatically exit. Fatal messages are identified by the Level - * of the ErrorInfo being - * {@link org.jdesktop.swingx.error.ErrorLevel}.FATAL.

                - * - *

                By default, when Fatal error dialogs are closed the application exits with - * a code of "1". In other words, System.exit(1). If you wish to implement - * custom handling, you can replace the default fatal action in the ActionMap - * of the JXErrorPane instance. If you specify a custom fatal - * action, then the default action of calling - * System.exit will not occur. You are therefore responsible for shutting down - * the application.

                - * - *

                UI Default Keys

                - *

                TODO

                - * JXErrorPane.errorIcon - * or, if not specified, JOptionPane.errorIcon - * JXErrorPane.warningIcon - * or, if not specified, JOptionPane.warningIcon - * JXErrorPane.details_contract_text (ignored on Mac OS X) - * JXErrorPane.details_expand_text (ignored on Mac OS X) - * JXErrorPane.mac.details_contract_text - * JXErrorPane.mac.details_expand_text - * Tree.expandedIcon (on Mac OS X) - * Tree.collapsedIcon (on Mac OS X) - * - *

                Customizing the Look and Feel

                - *

                TODO

                - * - * - * @status REVIEWED - * - * @author Richard Bair - * @author Alexander Zuev - * @author Shai Almog - * @author rah003 - */ -@JavaBean -public class JXErrorPane extends JComponent { - //---------------------------------------------------- static properties - /** - * Name of the Action used for reporting errors - */ - public static final String REPORT_ACTION_KEY = "report-action"; - /** - * Name of the Action used for fatal errors - */ - public static final String FATAL_ACTION_KEY = "fatal-action"; - /** - * UI Class ID - */ - public final static String uiClassID = "ErrorPaneUI"; - - /** - */ - static { - LookAndFeelAddons.contribute(new ErrorPaneAddon()); - } - - //-------------------------------------------------- instance properties - - /** - * ErrorInfo that contains all the information prepared for - * reporting. - */ - private ErrorInfo errorInfo = new ErrorInfo("Error", "Normally this place contains problem description.\n You see this text because one of the following reasons:\n * Either it is a test\n * Developer have not provided error details\n * This error message was invoked unexpectedly and there are no more details available", null, null, null, null, null); - /** - * The Icon to use, regardless of the error message. The UI delegate is - * responsible for setting this icon, if the developer has not specified - * the icon. - */ - private Icon icon; - /** - * The delegate to use for reporting errors. - */ - private ErrorReporter reporter; - - //--------------------------------------------------------- constructors - - /** - * Create a new JXErrorPane. - */ - public JXErrorPane() { - super(); - updateUI(); - } - - //------------------------------------------------------------- UI Logic - - /** - * Returns the look and feel (L&F) object that renders this component. - * - * @return the {@link ErrorPaneUI} object that renders this component - */ - public ErrorPaneUI getUI() { - return (ErrorPaneUI)ui; - } - - /** - * Sets the look and feel (L&F) object that renders this component. - * - * @param ui - * the ErrorPaneUI L&F object - * @see javax.swing.UIDefaults#getUI - * @beaninfo bound: true hidden: true attribute: visualUpdate true - * description: The UI object that implements the Component's - * LookAndFeel. - */ - public void setUI(ErrorPaneUI ui) { - super.setUI(ui); - } - - /** - * Returns the name of the L&F class that renders this component. - * - * @return the string {@link #uiClassID} - * @see JComponent#getUIClassID - * @see javax.swing.UIDefaults#getUI - */ - @Override - public String getUIClassID() { - return uiClassID; - } - - /** - * Notification from the UIManager that the L&F has changed. - * Replaces the current UI object with the latest version from the - * UIManager. - * - * @see JComponent#updateUI - */ - @Override - public void updateUI() { - setUI((ErrorPaneUI) LookAndFeelAddons - .getUI(this, ErrorPaneUI.class)); - } - - //-------------------------------------------- public methods/properties - - /** - * Sets the ErrorInfo for this dialog. ErrorInfo can't be null. - * - * @param info ErrorInfo that incorporates all the details about the error. Null value is not supported. - */ - public void setErrorInfo(ErrorInfo info) { - if (info == null) { - throw new NullPointerException("ErrorInfo can't be null. Provide valid ErrorInfo object."); - } - ErrorInfo old = this.errorInfo; - this.errorInfo = info; - firePropertyChange("errorInfo", old, this.errorInfo); - } - - /** - * Gets the JXErrorPane's ErrorInfo - * - * @return ErrorInfo assigned to this dialog - */ - public ErrorInfo getErrorInfo() { - return errorInfo; - } - - /** - * Specifies the icon to use - * - * @param icon the Icon to use. May be null. - */ - public void setIcon(Icon icon) { - Icon old = this.icon; - this.icon = icon; - firePropertyChange("icon", old, this.icon); - } - - /** - * Returns the Icon used - * - * @return the Icon - */ - public Icon getIcon() { - return icon; - } - - /** - * Sets the {@link ErrorReporter} delegate to use. This delegate is called - * automatically when the report action is fired. - * - * @param reporter the ErrorReporter to use. If null, the report button will - * not be shown in the error dialog. - */ - public void setErrorReporter(ErrorReporter reporter) { - ErrorReporter old = getErrorReporter(); - this.reporter = reporter; - firePropertyChange("errorReporter", old, getErrorReporter()); - } - - /** - * Gets the {@link ErrorReporter} delegate in use. - * - * @return the ErrorReporter. May be null. - */ - public ErrorReporter getErrorReporter() { - return reporter; - } - - //------------------------------------------------------- static methods - - /** - *

                Constructs and shows the error dialog for the given exception. The - * exceptions message will be the errorMessage, and the stacktrace will form - * the details for the error dialog.

                - * - *

                This method may be called from any thread. It will actually show the error - * dialog on the AWT event dispatch thread. This method blocks. If called - * on the EDT, the dialog shown will be modal. Otherwise, this thread will - * block until the error dialog has been shown and hidden on the EDT.

                - * - * @param e Exception that contains information about the error cause and stack trace - */ - public static void showDialog(Throwable e) { - ErrorInfo ii = new ErrorInfo(null, null, null, null, e, null, null); - showDialog(null, ii); - } - - /** - *

                Constructs and shows the error dialog, using the given - * ErrorInfo to initialize the view.

                - * - *

                This method may be called from any thread. It will actually show the error - * dialog on the AWT event dispatch thread. This method blocks. If called - * on the EDT, the dialog shown will be modal. Otherwise, this thread will - * block until the error dialog has been shown and hidden on the EDT.

                - * - * @param owner Owner of this error dialog. Determines the Window in which the dialog - * is displayed; if the owner has - * no Window, a default Frame is used - * @param info ErrorInfo that incorporates all the information about the error - */ - public static void showDialog(Component owner, ErrorInfo info) { - JXErrorPane pane = new JXErrorPane(); - pane.setErrorInfo(info); - showDialog(owner, pane); - } - - /** - *

                Constructs and shows the error dialog, using the given - * JXErrorPane for the view portion of the dialog.

                - * - *

                This method may be called from any thread. It will actually show the error - * dialog on the AWT event dispatch thread. This method blocks. If called - * on the EDT, the dialog shown will be modal. Otherwise, this thread will - * block until the error dialog has been shown and hidden on the EDT.

                - * - * @param owner Owner of this error dialog. Determines the Window in which the dialog - * is displayed; if the owner has - * no Window, a default Frame is used - * @param pane JXErrorPane which will form the content area - * of the dialog. - */ - public static void showDialog(final Component owner, final JXErrorPane pane) { - Runnable r = new Runnable() { - @Override - public void run() { - JDialog dlg = createDialog(owner, pane); - dlg.setVisible(true); - } - }; - - if (!SwingUtilities.isEventDispatchThread()) { - try { - SwingUtilities.invokeAndWait(r); - } catch (InvocationTargetException ex) { - ex.printStackTrace(); - } catch (InterruptedException ex) { - ex.printStackTrace(); - } - } else { - r.run(); - } - } - - /** - *

                Constructs and returns an error dialog, using the given - * JXErrorPane for the view portion of the dialog.

                - * - *

                This method may be called from any thread. It does not block. The - * caller is responsible for ensuring that the dialog is shown and manipulated - * on the AWT event dispatch thread. A common way to do this is to use - * SwingUtilities.invokeAndWait or - * SwingUtilities.invokeLater().

                - * - * @param owner Owner of this error dialog. Determines the Window in which the dialog - * is displayed; if the owner has - * no Window, a default Frame is used - * @param pane JXErrorPane which will form the content area - * of the dialog. - * @return a JDialog configured to display the error. - */ - public static JDialog createDialog(Component owner, JXErrorPane pane) { - JDialog window = pane.getUI().getErrorDialog(owner); - // If the owner is null applies orientation of the shared - // hidden window used as owner. - if(owner != null) { - pane.applyComponentOrientation(owner.getComponentOrientation()); - } else { - pane.applyComponentOrientation(window.getComponentOrientation()); - } - window.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); - window.pack(); - window.setLocationRelativeTo(owner); - return window; - } - - /** - *

                Constructs and shows the error frame for the given exception. The - * exceptions message will be the errorMessage, and the stacktrace will form - * the details for the error dialog.

                - * - *

                This method may be called from any thread. It will actually show the error - * dialog on the AWT event dispatch thread. This method blocks. If called - * on the EDT, the frame shown will be modal. Otherwise, this thread will - * block until the error frame has been shown and hidden on the EDT.

                - * - * @param e Exception that contains information about the error cause and stack trace - */ - public static void showFrame(Throwable e) { - ErrorInfo ii = new ErrorInfo(null, null, null, null, e, null, null); - showFrame(null, ii); - } - - /** - *

                Constructs and shows the error frame, using the given - * ErrorInfo to initialize the view.

                - * - *

                This method may be called from any thread. It will actually show the error - * dialog on the AWT event dispatch thread. This method blocks. If called - * on the EDT, the frame shown will be modal. Otherwise, this thread will - * block until the error frame has been shown and hidden on the EDT.

                - * - * @param owner Owner of this error frame. Determines the Window in which the frame - * is displayed; if the owner has - * no Window, a default Frame is used - * @param info ErrorInfo that incorporates all the information about the error - */ - public static void showFrame(Component owner, ErrorInfo info) { - JXErrorPane pane = new JXErrorPane(); - pane.setErrorInfo(info); - showFrame(owner, pane); - } - - /** - *

                Constructs and shows the error frame, using the given - * JXErrorPane for the view portion of the frame.

                - * - *

                This method may be called from any thread. It will actually show the error - * dialog on the AWT event dispatch thread. This method blocks. If called - * on the EDT, the frame shown will be modal. Otherwise, this thread will - * block until the error frame has been shown and hidden on the EDT.

                - * - * @param owner Owner of this error frame. Determines the Window in which the dialog - * is displayed; if the owner has - * no Window, a default Frame is used - * @param pane JXErrorPane which will form the content area - * of the frame. - */ - public static void showFrame(final Component owner, final JXErrorPane pane) { - Runnable r = new Runnable() { - @Override - public void run() { - JFrame window = createFrame(owner, pane); - window.setVisible(true); - } - }; - - if (!SwingUtilities.isEventDispatchThread()) { - try { - SwingUtilities.invokeAndWait(r); - } catch (InvocationTargetException ex) { - ex.printStackTrace(); - } catch (InterruptedException ex) { - ex.printStackTrace(); - } - } else { - r.run(); - } - } - - /** - *

                Constructs and returns an error frame, using the given - * JXErrorPane for the view portion of the frame.

                - * - *

                This method may be called from any thread. It does not block. The - * caller is responsible for ensuring that the frame is shown and manipulated - * on the AWT event dispatch thread. A common way to do this is to use - * SwingUtilities.invokeAndWait or - * SwingUtilities.invokeLater().

                - * - * @param owner Owner of this error frame. Determines the Window in which the frame - * is displayed; if the owner has - * no Window, a default Frame is used - * @param pane JXErrorPane which will form the content area - * of the frame. - * @return a JFrame configured to display the error. - */ - public static JFrame createFrame(Component owner, JXErrorPane pane) { - JFrame window = pane.getUI().getErrorFrame(owner); - // If the owner is null applies orientation of the shared - // hidden window used as owner. - if(owner != null) { - pane.applyComponentOrientation(owner.getComponentOrientation()); - } else { - pane.applyComponentOrientation(window.getComponentOrientation()); - } - window.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); - window.pack(); -// window.setLocationRelativeTo(owner); - return window; - } - - /** - *

                Constructs and shows the error frame for the given exception. The - * exceptions message will be the errorMessage, and the stacktrace will form - * the details for the error dialog.

                - * - *

                This method may be called from any thread. It will actually show the error - * dialog on the AWT event dispatch thread. This method blocks. If called - * on the EDT, the frame shown will be modal. Otherwise, this thread will - * block until the error frame has been shown and hidden on the EDT.

                - * - * @param e Exception that contains information about the error cause and stack trace - */ - public static void showInternalFrame(Throwable e) { - ErrorInfo ii = new ErrorInfo(null, null, null, null, e, null, null); - showInternalFrame(null, ii); - } - - /** - *

                Constructs and shows the error frame, using the given - * ErrorInfo to initialize the view.

                - * - *

                This method may be called from any thread. It will actually show the error - * dialog on the AWT event dispatch thread. This method blocks. If called - * on the EDT, the frame shown will be modal. Otherwise, this thread will - * block until the error frame has been shown and hidden on the EDT.

                - * - * @param owner Owner of this error frame. Determines the Window in which the frame - * is displayed; if the owner has - * no Window, a default Frame is used - * @param info ErrorInfo that incorporates all the information about the error - */ - public static void showInternalFrame(Component owner, ErrorInfo info) { - JXErrorPane pane = new JXErrorPane(); - pane.setErrorInfo(info); - showInternalFrame(owner, pane); - } - - /** - *

                Constructs and shows the error frame, using the given - * JXErrorPane for the view portion of the frame.

                - * - *

                This method may be called from any thread. It will actually show the error - * dialog on the AWT event dispatch thread. This method blocks. If called - * on the EDT, the frame shown will be modal. Otherwise, this thread will - * block until the error frame has been shown and hidden on the EDT.

                - * - * @param owner Owner of this error frame. Determines the Window in which the dialog - * is displayed; if the owner has - * no Window, a default Frame is used - * @param pane JXErrorPane which will form the content area - * of the frame. - */ - public static void showInternalFrame(final Component owner, final JXErrorPane pane) { - Runnable r = new Runnable() { - @Override - public void run() { - JInternalFrame window = createInternalFrame(owner, pane); - window.setVisible(true); - } - }; - - if (!SwingUtilities.isEventDispatchThread()) { - try { - SwingUtilities.invokeAndWait(r); - } catch (InvocationTargetException ex) { - ex.printStackTrace(); - } catch (InterruptedException ex) { - ex.printStackTrace(); - } - } else { - r.run(); - } - } - - /** - *

                Constructs and returns an error frame, using the given - * JXErrorPane for the view portion of the frame.

                - * - *

                This method may be called from any thread. It does not block. The - * caller is responsible for ensuring that the frame is shown and manipulated - * on the AWT event dispatch thread. A common way to do this is to use - * SwingUtilities.invokeAndWait or - * SwingUtilities.invokeLater().

                - * - * @param owner Owner of this error frame. Determines the Window in which the frame - * is displayed; if the owner has - * no Window, a default Frame is used - * @param pane JXErrorPane which will form the content area - * of the frame. - * @return a JInternalFrame configured to display the error. - */ - public static JInternalFrame createInternalFrame(Component owner, JXErrorPane pane) { - JInternalFrame window = pane.getUI().getErrorInternalFrame(owner); - // If the owner is null applies orientation of the shared - // hidden window used as owner. - if(owner != null) { - pane.applyComponentOrientation(owner.getComponentOrientation()); - } else { - pane.applyComponentOrientation(window.getComponentOrientation()); - } - window.setDefaultCloseOperation(JInternalFrame.DISPOSE_ON_CLOSE); - window.pack(); - //TODO! -// window.setLocationRelativeTo(owner); - return window; - } - -} diff --git a/src/main/java/org/jdesktop/swingx/JXFindBar.java b/src/main/java/org/jdesktop/swingx/JXFindBar.java deleted file mode 100644 index e13f58866e..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXFindBar.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * $Id: JXFindBar.java 4147 2012-02-01 17:13:24Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.search.Searchable; - -import javax.swing.*; -import java.awt.*; - -/** - * A simple low-intrusion default widget for incremental search. - * - * Actions registered (in addition to super): - *
                  - *
                • {@link JXDialog#CLOSE_ACTION_COMMAND} - an action bound to this - * component's cancel method. The method itself is an empty implementation: - * Subclassing clients can override the method, all clients can register a - * custom action. - *
                - * - * Key bindings: - *
                  - *
                • ESCAPE - calls action registered for - * {@link JXDialog#CLOSE_ACTION_COMMAND} - *
                - * - * This implementation uses textfield coloring as not-found visualization. - * - *

                - * PENDING: the coloring needs to be read from the UIManager instead of - * hardcoding. - * - *

                - * PENDING: the state transition of found/non-found coloring needs clean-up - - * there are spurious problems when re-using the same instance (as SearchFactory - * does). - * - * @author Jeanette Winzenburg - * - */ -@JavaBean -public class JXFindBar extends JXFindPanel { - - protected Color previousBackgroundColor; - - protected Color previousForegroundColor; - - // PENDING: need to read from UIManager - protected Color notFoundBackgroundColor = Color.decode("#FF6666"); - - protected Color notFoundForegroundColor = Color.white; - - protected JButton findNext; - - protected JButton findPrevious; - - public JXFindBar() { - this(null); - } - - public JXFindBar(Searchable searchable) { - super(searchable); - getPatternModel().setIncremental(true); - getPatternModel().setWrapping(true); - } - - @Override - public void setSearchable(Searchable searchable) { - super.setSearchable(searchable); - match(); - } - - /** - * here: set textfield colors to not-found colors. - */ - @Override - protected void showNotFoundMessage() { - //JW: quick hack around #487-swingx - NPE in setSearchable - if (searchField == null) return; - searchField.setForeground(notFoundForegroundColor); - searchField.setBackground(notFoundBackgroundColor); - } - - /** - * here: set textfield colors to normal. - */ - @Override - protected void showFoundMessage() { - //JW: quick hack around #487-swingx - NPE in setSearchable - if (searchField == null) return; - searchField.setBackground(previousBackgroundColor); - searchField.setForeground(previousForegroundColor); - } - - @Override - public void addNotify() { - super.addNotify(); - if (previousBackgroundColor == null) { - previousBackgroundColor = searchField.getBackground(); - previousForegroundColor = searchField.getForeground(); - } else { - searchField.setBackground(previousBackgroundColor); - searchField.setForeground(previousForegroundColor); - } - } - - // --------------------------- action call back - /** - * Action callback method for bound action JXDialog.CLOSE_ACTION_COMMAND. - * - * Here: does nothing. Subclasses can override to define custom "closing" - * behaviour. Alternatively, any client can register a custom action with - * the actionMap. - * - * - */ - public void cancel() { - } - - // -------------------- init - - @Override - protected void initExecutables() { - getActionMap().put(JXDialog.CLOSE_ACTION_COMMAND, - createBoundAction(JXDialog.CLOSE_ACTION_COMMAND, "cancel")); - super.initExecutables(); - } - - @Override - protected void bind() { - super.bind(); - searchField - .addActionListener(getAction(JXDialog.EXECUTE_ACTION_COMMAND)); - findNext.setAction(getAction(FIND_NEXT_ACTION_COMMAND)); - findPrevious.setAction(getAction(FIND_PREVIOUS_ACTION_COMMAND)); - KeyStroke stroke = KeyStroke.getKeyStroke("ESCAPE"); - getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(stroke, - JXDialog.CLOSE_ACTION_COMMAND); - } - - @Override - protected void build() { - setLayout(new FlowLayout(SwingConstants.LEADING)); - add(searchLabel); - add(new JLabel(":")); - add(new JLabel(" ")); - add(searchField); - add(findNext); - add(findPrevious); - } - - @Override - protected void initComponents() { - super.initComponents(); - findNext = new JButton(); - findPrevious = new JButton(); - } - -} diff --git a/src/main/java/org/jdesktop/swingx/JXFindPanel.java b/src/main/java/org/jdesktop/swingx/JXFindPanel.java deleted file mode 100644 index 5efc890f61..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXFindPanel.java +++ /dev/null @@ -1,296 +0,0 @@ -/* - * $Id: JXFindPanel.java 4158 2012-02-03 18:29:40Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.search.PatternModel; -import org.jdesktop.swingx.search.Searchable; - -import javax.swing.*; -import java.awt.*; -import java.util.Locale; -import java.util.regex.Pattern; - -/** - * {@code JXFindPanel} is a basic find panel suitable for use in dialogs. It - * offers case-sensitivity, wrapped searching, and reverse searching. - * - * @author unascribed from JDNC - * @author Jeanette Winzenburg - */ -@JavaBean -public class JXFindPanel extends AbstractPatternPanel { - - public static final String FIND_NEXT_ACTION_COMMAND = "findNext"; - public static final String FIND_PREVIOUS_ACTION_COMMAND = "findPrevious"; - - protected Searchable searchable; - - protected JCheckBox wrapCheck; - protected JCheckBox backCheck; - private boolean initialized; - - /** - * Default constructor for the find panel. Constructs panel not targeted to - * any component. - */ - public JXFindPanel() { - this(null); - } - - /** - * Construct search panel targeted to specific Searchable component. - * - * @param searchable Component where search widget will try to locate and select - * information using methods of the Searchable interface. - */ - public JXFindPanel(Searchable searchable) { - setName(getUIString(SEARCH_TITLE)); - setSearchable(searchable); - initActions(); - } - - /** - * Sets the Searchable targeted of this find widget. - * Triggers a search with null pattern to release the old - * searchable, if any. - * - * @param searchable Component where search widget will try to locate and select - * information using methods of the {@link Searchable Searchable} interface. - */ - public void setSearchable(Searchable searchable) { - if ((this.searchable != null) && this.searchable.equals(searchable)) return; - Searchable old = this.searchable; - if (old != null) { - old.search((Pattern) null); - } - this.searchable = searchable; - getPatternModel().setFoundIndex(-1); - firePropertyChange("searchable", old, this.searchable); - } - - /** - * Notifies this component that it now has a parent component. - * When this method is invoked, the chain of parent components is - * set up with KeyboardAction event listeners. - */ - @Override - public void addNotify() { - init(); - super.addNotify(); - } - - /** - * Initializes component and its listeners and models. - */ - protected void init() { - if (initialized) return; - initialized = true; - initComponents(); - build(); - bind(); - } - - //------------------ support synch the model <--> components - - - /** - * Configure and bind components to/from PatternModel. - */ - @Override - protected void bind() { - super.bind(); - getActionContainerFactory().configureButton(wrapCheck, - getAction(PatternModel.MATCH_WRAP_ACTION_COMMAND), - null); - getActionContainerFactory().configureButton(backCheck, - getAction(PatternModel.MATCH_BACKWARDS_ACTION_COMMAND), - null); - } - - - /** - * called from listening to empty property of PatternModel. - * - * this implementation calls super and additionally synchs the - * enabled state of FIND_NEXT_ACTION_COMMAND, FIND_PREVIOUS_ACTION_COMMAND - * to !empty. - */ - @Override - protected void refreshEmptyFromModel() { - super.refreshEmptyFromModel(); - boolean enabled = !getPatternModel().isEmpty(); - getAction(FIND_NEXT_ACTION_COMMAND).setEnabled(enabled); - getAction(FIND_PREVIOUS_ACTION_COMMAND).setEnabled(enabled); - } - - //--------------------- action callbacks - /** - * Action callback for Find action. - * Find next/previous match using current setting of direction flag. - * - */ - @Override - public void match() { - doFind(); - } - - /** - * Action callback for FindNext action. - * Sets direction flag to forward and calls find. - */ - public void findNext() { - getPatternModel().setBackwards(false); - doFind(); - } - - /** - * Action callback for FindPrevious action. - * Sets direction flag to previous and calls find. - */ - public void findPrevious() { - getPatternModel().setBackwards(true); - doFind(); - } - - /** - * Common standalone method to perform search. Used by the action callback methods - * for Find/FindNext/FindPrevious actions. Finds next/previous match using current - * setting of direction flag. Result is being reporred using showFoundMessage and - * showNotFoundMessage methods respectively. - * - * @see #match - * @see #findNext - * @see #findPrevious - */ - protected void doFind() { - if (searchable == null) - return; - int foundIndex = doSearch(); - boolean notFound = (foundIndex == -1) && !getPatternModel().isEmpty(); - if (notFound) { - if (getPatternModel().isWrapping()) { - notFound = doSearch() == -1; - } - } - if (notFound) { - showNotFoundMessage(); - } else { - showFoundMessage(); - } - } - - /** - * Performs search and returns index of the next match. - * - * @return Index of the next match in document. - */ - protected int doSearch() { - int foundIndex = searchable.search(getPatternModel().getPattern(), - getPatternModel().getFoundIndex(), getPatternModel().isBackwards()); - getPatternModel().setFoundIndex(foundIndex); - return getPatternModel().getFoundIndex(); -// first try on #236-swingx - foundIndex wrong in backwards search. -// re-think: autoIncrement in PatternModel? -// return foundIndex; - } - - /** - * Report that suitable match is found. - */ - protected void showFoundMessage() { - - } - - /** - * Report that no match is found. - */ - protected void showNotFoundMessage() { - JOptionPane.showMessageDialog(this, getUIString("notFound")); - } - - -//-------------- dynamic Locale support - - - - @Override - protected void updateLocaleState(Locale locale) { - super.updateLocaleState(locale); - setName(getUIString(SEARCH_TITLE, locale)); - } - - //-------------------------- initial - - - /** - * creates and registers all "executable" actions. - * Meaning: the actions bound to a callback method on this. - */ - @Override - protected void initExecutables() { - getActionMap().put(FIND_NEXT_ACTION_COMMAND, - createBoundAction(FIND_NEXT_ACTION_COMMAND, "findNext")); - getActionMap().put(FIND_PREVIOUS_ACTION_COMMAND, - createBoundAction(FIND_PREVIOUS_ACTION_COMMAND, "findPrevious")); - super.initExecutables(); - } - - - -//----------------------------- init ui - - /** - * Create and initialize components. - */ - @Override - protected void initComponents() { - super.initComponents(); - wrapCheck = new JCheckBox(); - backCheck = new JCheckBox(); - } - - - - /** - * Compose and layout all the subcomponents. - */ - protected void build() { - Box lBox = new Box(BoxLayout.LINE_AXIS); - lBox.add(searchLabel); - lBox.add(new JLabel(":")); - lBox.add(new JLabel(" ")); - lBox.setAlignmentY(Component.TOP_ALIGNMENT); - Box rBox = new Box(BoxLayout.PAGE_AXIS); - rBox.add(searchField); - rBox.add(matchCheck); - rBox.add(wrapCheck); - rBox.add(backCheck); - rBox.setAlignmentY(Component.TOP_ALIGNMENT); - - setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS)); - - add(lBox); - add(rBox); - } - -} diff --git a/src/main/java/org/jdesktop/swingx/JXFormattedTextField.java b/src/main/java/org/jdesktop/swingx/JXFormattedTextField.java deleted file mode 100644 index 805ef90498..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXFormattedTextField.java +++ /dev/null @@ -1,150 +0,0 @@ -package org.jdesktop.swingx; - -import org.jdesktop.swingx.prompt.BuddySupport; -import org.jdesktop.swingx.prompt.BuddySupport.Position; -import org.jdesktop.swingx.prompt.PromptSupport; -import org.jdesktop.swingx.prompt.PromptSupport.FocusBehavior; - -import javax.swing.*; -import java.awt.*; -import java.util.List; - - -/** - * {@link JFormattedTextField}, with integrated support for prompts and buddies. - * - * @see PromptSupport - * @see BuddySupport - * @author Peter Weishapl - * - */ -public class JXFormattedTextField extends JFormattedTextField { - public JXFormattedTextField() { - this(null); - } - - public JXFormattedTextField(String promptText) { - this(promptText, null); - } - - public JXFormattedTextField(String promptText, Color promptForeground) { - this(promptText, promptForeground, null); - } - - public JXFormattedTextField(String promptText, Color promptForeground, Color promptBackground) { - PromptSupport.init(promptText, promptForeground, promptBackground, this); - } - - /** - * @see PromptSupport#getFocusBehavior(javax.swing.text.JTextComponent) - */ - public FocusBehavior getFocusBehavior() { - return PromptSupport.getFocusBehavior(this); - } - - /** - * @see PromptSupport#getPrompt(javax.swing.text.JTextComponent) - */ - public String getPrompt() { - return PromptSupport.getPrompt(this); - } - - /** - * @see PromptSupport#getForeground(javax.swing.text.JTextComponent) - */ - public Color getPromptForeground() { - return PromptSupport.getForeground(this); - } - - /** - * @see PromptSupport#getForeground(javax.swing.text.JTextComponent) - */ - public Color getPromptBackground() { - return PromptSupport.getBackground(this); - } - - /** - * @see PromptSupport#getFontStyle(javax.swing.text.JTextComponent) - */ - public Integer getPromptFontStyle() { - return PromptSupport.getFontStyle(this); - } - - /** - * @see PromptSupport#getFocusBehavior(javax.swing.text.JTextComponent) - */ - public void setFocusBehavior(FocusBehavior focusBehavior) { - PromptSupport.setFocusBehavior(focusBehavior, this); - } - - /** - * @see PromptSupport#setPrompt(String, javax.swing.text.JTextComponent) - */ - public void setPrompt(String labelText) { - PromptSupport.setPrompt(labelText, this); - } - - /** - * @see PromptSupport#setForeground(Color, javax.swing.text.JTextComponent) - */ - public void setPromptForeground(Color promptTextColor) { - PromptSupport.setForeground(promptTextColor, this); - } - - /** - * @see PromptSupport#setBackground(Color, javax.swing.text.JTextComponent) - */ - public void setPromptBackround(Color promptTextColor) { - PromptSupport.setBackground(promptTextColor, this); - } - - /** - * @see PromptSupport#setFontStyle(Integer, - * javax.swing.text.JTextComponent) - */ - public void setPromptFontStyle(Integer fontStyle) { - PromptSupport.setFontStyle(fontStyle, this); - } - - /** - * @see BuddySupport#setOuterMargin(JTextField, Insets) - */ - public void setOuterMargin(Insets margin) { - BuddySupport.setOuterMargin(this, margin); - } - - /** - * @see BuddySupport#getOuterMargin(JTextField) - */ - public Insets getOuterMargin() { - return BuddySupport.getOuterMargin(this); - } - - /** - * @see BuddySupport#add(Component, Position, JTextField) - */ - public void addBuddy(Component buddy, Position pos) { - BuddySupport.add(buddy, pos, this); - } - - /** - * @see BuddySupport#addGap(int, Position, JTextField) - */ - public void addGap(int width, Position pos) { - BuddySupport.addGap(width, pos, this); - } - - /** - * @see BuddySupport#getBuddies(Position, JTextField) - */ - public List getBuddies(Position pos) { - return BuddySupport.getBuddies(pos, this); - } - - /** - * @see BuddySupport#removeAll(JTextField) - */ - public void removeAllBuddies() { - BuddySupport.removeAll(this); - } -} diff --git a/src/main/java/org/jdesktop/swingx/JXFrame.java b/src/main/java/org/jdesktop/swingx/JXFrame.java deleted file mode 100644 index 53d340d062..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXFrame.java +++ /dev/null @@ -1,629 +0,0 @@ -/* - * $Id: JXFrame.java 4158 2012-02-03 18:29:40Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.util.WindowUtils; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.*; - -/** - *

                - * {@code JXFrame} is an enhanced {@link JFrame}. While {@code JXFrame} can - * replace any {@code JFrame}, it has features that make it particularly useful - * as the "main" frame for an application. - *

                - *

                Additional Features

                - *

                - * Root pane: {@code JXFrame} uses {@link JXRootPane} as its default root pane. - * The frame provide several convenience methods to provide easy access to the - * additional features. - *

                - *

                - * Idle: {@code JXFrame} offers an idle timer. Registering a - * {@link java.beans.PropertyChangeListener} for "idle" will notify when the - * user has not interacted with the JVM. A primary use for this type of - * functionality is to secure the application, blocking access and requiring the - * user to login again. - *

                - *

                - * Wait (busy) glass pane: The {@code JXFrame} can be configured with an - * alternate glass pane. Typically, this glass pane is used to notify the user - * that the application is busy, but the glass pane could be for any purpose. - * This secondary glass pane can be quickly enabled or disabled by - * {@linkplain #setWaitPaneVisible(boolean) setting the wait pane visible}. - *

                - * - * @author unascribed from JDNC - */ -@JavaBean -@SuppressWarnings({ "nls", "serial" }) -public class JXFrame extends JFrame { - /** - * An enumeration of {@link JXFrame} starting locations. - * - * @author unascribed from JDNC - */ - public enum StartPosition {CenterInScreen, CenterInParent, Manual} - - private Component waitPane = null; - private Component glassPane = null; - private boolean waitPaneVisible = false; - private Cursor realCursor = null; - private boolean waitCursorVisible = false; - private boolean waiting = false; - private StartPosition startPosition; - private boolean hasBeenVisible = false; //startPosition is only used the first time the window is shown - private AWTEventListener keyEventListener; //for listening to KeyPreview events - private boolean keyPreview = false; - private AWTEventListener idleListener; //for listening to events. If no events happen for a specific amount of time, mark as idle - private Timer idleTimer; - private long idleThreshold = 0; - private boolean idle; - - /** - * Creates a {@code JXFrame} with no title and standard closing behavior. - */ - public JXFrame() { - this(null, false); - } - - /** - * Creates a {@code JXFrame} with the specified title and default closing - * behavior. - * - * @param title - * the frame title - */ - public JXFrame(String title) { - this(title, false); - } - - /** - * Creates a JXFrame in the specified - * GraphicsConfiguration of - * a screen device, a blank title and default closing behaviour. - *

                - * - * @param gc the GraphicsConfiguration that is used - * to construct the new Frame; - * if gc is null, the system - * default GraphicsConfiguration is assumed - * @exception IllegalArgumentException if gc is not from - * a screen device. This exception is always thrown when - * GraphicsEnvironment.isHeadless() returns true. - */ - public JXFrame(GraphicsConfiguration gc) { - this(null, gc, false); - } - - - /** - * Creates a JXFrame with the specified title, the - * specified GraphicsConfiguration of a screen device and - * default closing behaviour. - *

                - * - * @param title the title to be displayed in the - * frame's border. A null value is treated as - * an empty string, "". - * @param gc the GraphicsConfiguration that is used - * to construct the new JFrame with; - * if gc is null, the system - * default GraphicsConfiguration is assumed - * @exception IllegalArgumentException if gc is not from - * a screen device. This exception is always thrown when - * GraphicsEnvironment.isHeadless() returns true. - */ - public JXFrame(String title, GraphicsConfiguration gc) { - this(title, gc, false); - } - - /** - * Creates a {@code JXFrame} with the specified title and closing behavior. - * - * @param title - * the frame title - * @param exitOnClose - * {@code true} to override the default ({@link JFrame}) closing - * behavior and use {@link JFrame#EXIT_ON_CLOSE EXIT_ON_CLOSE} - * instead; {@code false} to use the default behavior - */ - public JXFrame(String title, boolean exitOnClose) { - this(title, null, exitOnClose); - } - - /** - * Creates a {@code JXFrame} with the specified title, GraphicsConfiguration - * and closing behavior. - * - * @param title the frame title - * @param gc the GraphicsConfiguration of the target screen - * device. If gc is null, the system - * default GraphicsConfiguration is assumed. - * @param exitOnClose {@code true} to override the default ({@link JFrame}) - * closing behavior and use {@link JFrame#EXIT_ON_CLOSE - * EXIT_ON_CLOSE} instead; {@code false} to use the default behavior - * @exception IllegalArgumentException if gc is not from a - * screen device. - * - */ - public JXFrame(String title, GraphicsConfiguration gc, boolean exitOnClose) { - super(title, gc); - if (exitOnClose) { - setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - } - - //create the event handler for key preview functionality - keyEventListener = new AWTEventListener() { - @Override - public void eventDispatched(AWTEvent aWTEvent) { - if (aWTEvent instanceof KeyEvent) { - KeyEvent evt = (KeyEvent)aWTEvent; - for (KeyListener kl : getKeyListeners()) { - int id = aWTEvent.getID(); - switch (id) { - case KeyEvent.KEY_PRESSED: - kl.keyPressed(evt); - break; - case KeyEvent.KEY_RELEASED: - kl.keyReleased(evt); - break; - case KeyEvent.KEY_TYPED: - kl.keyTyped(evt); - break; - default: - System.err.println("Unhandled Key ID: " + id); - } - } - } - } - }; - - idleTimer = new Timer(100, new ActionListener() { - @Override - public void actionPerformed(ActionEvent actionEvent) { - setIdle(true); - } - }); - - //create the event handler for key preview functionality - idleListener = new AWTEventListener() { - @Override - public void eventDispatched(AWTEvent aWTEvent) { - //reset the timer - idleTimer.stop(); - //if the user is idle, then change to not idle - if (isIdle()) { - setIdle(false); - } - //start the timer - idleTimer.restart(); - } - }; - } - - /** - * Sets the cancel button property on the underlying {@code JXRootPane}. - * - * @param button - * the {@code JButton} which is to be the cancel button - * @see #getCancelButton() - * @see JXRootPane#setCancelButton(JButton) - */ - public void setCancelButton(JButton button) { - getRootPaneExt().setCancelButton(button); - } - - /** - * Returns the value of the cancel button property from the underlying - * {@code JXRootPane}. - * - * @return the {@code JButton} which is the cancel button - * @see #setCancelButton(JButton) - * @see JXRootPane#getCancelButton() - */ - public JButton getCancelButton() { - return getRootPaneExt().getCancelButton(); - } - - /** - * Sets the default button property on the underlying {@code JRootPane}. - * - * @param button - * the {@code JButton} which is to be the default button - * @see #getDefaultButton() - * @see JXRootPane#setDefaultButton(JButton) - */ - public void setDefaultButton(JButton button) { - JButton old = getDefaultButton(); - getRootPane().setDefaultButton(button); - firePropertyChange("defaultButton", old, getDefaultButton()); - } - - /** - * Returns the value of the default button property from the underlying - * {@code JRootPane}. - * - * @return the {@code JButton} which is the default button - * @see #setDefaultButton(JButton) - * @see JXRootPane#getDefaultButton() - */ - public JButton getDefaultButton() { - return getRootPane().getDefaultButton(); - } - - /** - * If enabled the {@code KeyListener}s will receive a preview of the {@code - * KeyEvent} prior to normal viewing. - * - * @param flag {@code true} to enable previewing; {@code false} otherwise - * @see #getKeyPreview() - * @see #addKeyListener(KeyListener) - */ - public void setKeyPreview(boolean flag) { - Toolkit.getDefaultToolkit().removeAWTEventListener(keyEventListener); - if (flag) { - Toolkit.getDefaultToolkit().addAWTEventListener(keyEventListener, AWTEvent.KEY_EVENT_MASK); - } - boolean old = keyPreview; - keyPreview = flag; - firePropertyChange("keyPreview", old, keyPreview); - } - - /** - * Returns the value for the key preview. - * - * @return if {@code true} previewing is enabled; otherwise it is not - * @see #setKeyPreview(boolean) - */ - public final boolean getKeyPreview() { - return keyPreview; - } - - /** - * Sets the start position for this frame. Setting this value only has an - * effect is the frame has never been displayed. - * - * @param position - * the position to display the frame at - * @see #getStartPosition() - * @see #setVisible(boolean) - */ - public void setStartPosition(StartPosition position) { - StartPosition old = getStartPosition(); - this.startPosition = position; - firePropertyChange("startPosition", old, getStartPosition()); - } - - /** - * Returns the start position for this frame. - * - * @return the start position of the frame - * @see #setStartPosition(StartPosition) - */ - public StartPosition getStartPosition() { - return startPosition == null ? StartPosition.Manual : startPosition; - } - - /** - * Switches the display cursor to or from the wait cursor. - * - * @param flag - * {@code true} to enable the wait cursor; {@code false} to - * enable the previous cursor - * @see #isWaitCursorVisible() - * @see Cursor#WAIT_CURSOR - */ - public void setWaitCursorVisible(boolean flag) { - boolean old = isWaitCursorVisible(); - if (flag != old) { - waitCursorVisible = flag; - if (isWaitCursorVisible()) { - realCursor = getCursor(); - super.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - } else { - super.setCursor(realCursor); - } - firePropertyChange("waitCursorVisible", old, isWaitCursorVisible()); - } - } - - /** - * Returns the state of the wait cursor visibility. - * - * @return {@code true} if the current cursor is the wait cursor; {@code - * false} otherwise - */ - public boolean isWaitCursorVisible() { - return waitCursorVisible; - } - - /** - * {@inheritDoc} - */ - @Override - public void setCursor(Cursor c) { - if (!isWaitCursorVisible()) { - super.setCursor(c); - } else { - this.realCursor = c; - } - } - - /** - * Sets the component to use as a wait glass pane. This component is not - * part of the display hierarchy unless {@code isWaitPaneVisible() == true}. - * - * @param c - * the wait glass pane for this frame - * @see #getWaitPane() - * @see #setWaitPaneVisible(boolean) - */ - public void setWaitPane(Component c) { - Component old = getWaitPane(); - this.waitPane = c; - firePropertyChange("waitPane", old, getWaitPane()); - } - - /** - * Returns the current wait pane for this frame. This component may or may - * not be part of the display hierarchy. - * - * @return the current wait pane - * @see #setWaitPane(Component) - */ - public Component getWaitPane() { - return waitPane; - } - - /** - * Enabled or disabled the display of the normal or wait glass pane. If - * {@code true} the wait pane is be displayed. Altering this property alters - * the display hierarchy. - * - * @param flag - * {@code true} to display the wait glass pane; {@code false} to - * display the normal glass pane - * @see #isWaitPaneVisible() - * @see #setWaitPane(Component) - */ - public void setWaitPaneVisible(boolean flag) { - boolean old = isWaitPaneVisible(); - if (flag != old) { - this.waitPaneVisible = flag; - Component wp = getWaitPane(); - if (isWaitPaneVisible()) { - glassPane = getRootPane().getGlassPane(); - if (wp != null) { - getRootPane().setGlassPane(wp); - wp.setVisible(true); - } - } else { - if (wp != null) { - wp.setVisible(false); - } - getRootPane().setGlassPane(glassPane); - } - firePropertyChange("waitPaneVisible", old, isWaitPaneVisible()); - } - } - - /** - * Returns the current visibility of the wait glass pane. - * - * @return {@code true} if the wait glass pane is visible; {@code false} - * otherwise - */ - public boolean isWaitPaneVisible() { - return waitPaneVisible; - } - - /** - * Sets the frame into a wait state or restores the frame from a wait state. - * - * @param waiting - * {@code true} to place the frame in a wait state; {@code false} - * otherwise - * @see #isWaiting() - * @see #setWaitCursorVisible(boolean) - * @see #setWaitPaneVisible(boolean) - */ - public void setWaiting(boolean waiting) { - boolean old = isWaiting(); - this.waiting = waiting; - firePropertyChange("waiting", old, isWaiting()); - setWaitPaneVisible(waiting); - setWaitCursorVisible(waiting); - } - - /** - * Determines if the frame is in a wait state or not. - * - * @return {@code true} if the frame is in the wait state; {@code false} - * otherwise - * @see #setWaiting(boolean) - */ - public boolean isWaiting() { - return waiting; - } - - /** - * {@inheritDoc} - */ - @Override - public void setVisible(boolean visible) { - if (!hasBeenVisible && visible) { - //move to the proper start position - StartPosition pos = getStartPosition(); - switch (pos) { - case CenterInParent: - setLocationRelativeTo(getParent()); - break; - case CenterInScreen: - setLocation(WindowUtils.getPointForCentering(this)); - break; - case Manual: - default: - //nothing to do! - } - } - super.setVisible(visible); - } - - public boolean isIdle() { - return idle; - } - - /** - * Sets the frame into an idle state or restores the frame from an idle state. - * - * @param idle - * {@code true} to place the frame in an idle state; {@code false} - * otherwise - * @see #isIdle() - * @see #setIdleThreshold(long) - */ - public void setIdle(boolean idle) { - boolean old = isIdle(); - this.idle = idle; - firePropertyChange("idle", old, isIdle()); - } - - /** - * Sets a threshold for user interaction before automatically placing the - * frame in an idle state. - * - * @param threshold - * the time (in milliseconds) to elapse before setting the frame - * idle - * @see #getIdleThreshold() - * @see #setIdle(boolean) - */ - public void setIdleThreshold(long threshold) { - long old = getIdleThreshold(); - this.idleThreshold = threshold; - firePropertyChange("idleThreshold", old, getIdleThreshold()); - - threshold = getIdleThreshold(); // in case the getIdleThreshold method has been overridden - - Toolkit.getDefaultToolkit().removeAWTEventListener(idleListener); - if (threshold > 0) { - Toolkit.getDefaultToolkit().addAWTEventListener(idleListener, AWTEvent.KEY_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK | AWTEvent.MOUSE_WHEEL_EVENT_MASK); - } - idleTimer.stop(); - idleTimer.setInitialDelay((int)threshold); - idleTimer.restart(); - } - - /** - * Returns the amount of time that must elapse before the frame - * automatically enters an idle state. - * - * @return the time in milliseconds - */ - public long getIdleThreshold() { - return idleThreshold; - } - - /** - * Sets the status bar property on the underlying {@code JXRootPane}. - * - * @param statusBar - * the {@code JXStatusBar} which is to be the status bar - * @see #getStatusBar() - * @see JXRootPane#setStatusBar(JXStatusBar) - */ - public void setStatusBar(JXStatusBar statusBar) { - getRootPaneExt().setStatusBar(statusBar); - } - - /** - * Returns the value of the status bar property from the underlying - * {@code JXRootPane}. - * - * @return the {@code JXStatusBar} which is the current status bar - * @see #setStatusBar(JXStatusBar) - * @see JXRootPane#getStatusBar() - */ - public JXStatusBar getStatusBar() { - return getRootPaneExt().getStatusBar(); - } - - /** - * Sets the tool bar property on the underlying {@code JXRootPane}. - * - * @param toolBar - * the {@code JToolBar} which is to be the tool bar - * @see #getToolBar() - * @see JXRootPane#setToolBar(JToolBar) - */ - public void setToolBar(JToolBar toolBar) { - getRootPaneExt().setToolBar(toolBar); - } - - /** - * Returns the value of the tool bar property from the underlying - * {@code JXRootPane}. - * - * @return the {@code JToolBar} which is the current tool bar - * @see #setToolBar(JToolBar) - * @see JXRootPane#getToolBar() - */ - public JToolBar getToolBar() { - return getRootPaneExt().getToolBar(); - } - - //---------------------------------------------------- Root Pane Methods - /** - * Overridden to create a JXRootPane. - */ - @Override - protected JRootPane createRootPane() { - return new JXRootPane(); - } - - /** - * Overridden to make this public. - */ - @Override - public void setRootPane(JRootPane root) { - super.setRootPane(root); - } - - /** - * Return the extended root pane. If this frame doesn't contain - * an extended root pane the root pane should be accessed with - * getRootPane(). - * - * @return the extended root pane or null. - */ - public JXRootPane getRootPaneExt() { - if (rootPane instanceof JXRootPane) { - return (JXRootPane)rootPane; - } - return null; - } -} - diff --git a/src/main/java/org/jdesktop/swingx/JXGradientChooser.java b/src/main/java/org/jdesktop/swingx/JXGradientChooser.java deleted file mode 100644 index 3104857076..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXGradientChooser.java +++ /dev/null @@ -1,787 +0,0 @@ -/* - * $Id: JXGradientChooser.java 4147 2012-02-01 17:13:24Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.action.AbstractActionExt; -import org.jdesktop.swingx.color.GradientPreviewPanel; -import org.jdesktop.swingx.color.GradientThumbRenderer; -import org.jdesktop.swingx.color.GradientTrackRenderer; -import org.jdesktop.swingx.multislider.Thumb; -import org.jdesktop.swingx.multislider.ThumbListener; -import org.jdesktop.swingx.util.PaintUtils; - -import javax.swing.*; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import java.awt.*; -import java.awt.event.*; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.logging.Logger; - -/** - *

                A specialized JXPanel that allows the user to construct and choose a Gradient. - * The returned values will be one of: LinearGradientPaint or RadialGradientPaint.

                - * - *

                Dependency: Because this class relies on LinearGradientPaint and - * RadialGradientPaint, it requires the optional MultipleGradientPaint.jar

                - * - * @author joshy - */ -@JavaBean -public class JXGradientChooser extends JXPanel { - private enum GradientStyle { Linear, Radial } - - /** - * The multi-thumb slider to use for the gradient stops - */ - private JXMultiThumbSlider slider; - private JButton deleteThumbButton; - private JButton addThumbButton; - - private JTextField colorField; - private JXColorSelectionButton changeColorButton; - private JSpinner colorLocationSpinner; - private JSpinner alphaSpinner; - private JSlider alphaSlider; - - private JComboBox styleCombo; - private GradientPreviewPanel gradientPreview; - - private JRadioButton noCycleRadio; - private JRadioButton reflectedRadio; - private JRadioButton repeatedRadio; - private JCheckBox reversedCheck; - private MultipleGradientPaint gradient; - - /** - * Creates new JXGradientChooser - */ - public JXGradientChooser() { - initComponents2(); - } - - /** - * Returns the MultipleGradientPaint currently choosen by the user. - * @return the currently selected gradient - */ - public MultipleGradientPaint getGradient() { - return gradient; - } - - private boolean thumbsMoving = false; - private Logger log = Logger.getLogger(JXGradientChooser.class.getName()); - - /** - * Sets the gradient within this panel to the new gradient. This will delete - * the old gradient all of it's settings, resetting the slider, gradient - * type selection, and other gradient configuration options to match the - * new gradient. - * - * @param mgrad The desired gradient. - */ - public void setGradient(MultipleGradientPaint mgrad) { - if(gradient == mgrad) { - return; - } - float[] fracts = mgrad.getFractions(); - Color[] colors = mgrad.getColors(); - - if(!thumbsMoving) { - // update the slider properly - if(slider.getModel().getThumbCount() != - mgrad.getColors().length) { - // removing all thumbs; - while(slider.getModel().getThumbCount() > 0) { - slider.getModel().removeThumb(0); - } - // add them back - for(int i=0; i thumb) { - if(thumb == null) { - updateFromStop(-1,-1,Color.black); - } else { - updateFromStop(1,thumb.getPosition(),thumb.getObject()); - } - } - - private void updateFromStop(int thumb, float position, Color color) { - log.fine("updating: " + thumb + " " + position + " " + color); - if(thumb == -1) { - colorLocationSpinner.setEnabled(false); - alphaSpinner.setEnabled(false); - alphaSlider.setEnabled(false); - colorField.setEnabled(false); - changeColorButton.setEnabled(false); - changeColorButton.setBackground(Color.black); - deleteThumbButton.setEnabled(false); - } else { - colorLocationSpinner.setEnabled(true); - alphaSpinner.setEnabled(true); - alphaSlider.setEnabled(true); - colorField.setEnabled(true); - changeColorButton.setEnabled(true); - colorLocationSpinner.setValue((int)(100*position)); - colorField.setText(Integer.toHexString(color.getRGB()).substring(2)); - alphaSpinner.setValue(color.getAlpha()*100/255); - alphaSlider.setValue(color.getAlpha()*100/255); - changeColorButton.setBackground(color); - deleteThumbButton.setEnabled(true); - } - updateDeleteButtons(); - recalcGradientFromStops(); - } - - private void updateDeleteButtons() { - if(slider.getModel().getThumbCount() <= 2) { - deleteThumbButton.setEnabled(false); - } - } - - private void updateGradientProperty() { - firePropertyChange("gradient",null,getGradient()); - gradientPreview.repaint(); - } - - - /** This method is called from within the constructor to - * initialize the form. - */ - - private JPanel topPanel, previewPanel; - private void initComponents() { - // declarations for anonymous components - JPanel jPanel1, jPanel2, jPanel3, jPanel4; - JLabel jLabel1, jLabel5, jLabel2, jLabel6, jLabel4, jLabel7, jLabel8, jLabel9; - ButtonGroup typeGroup; - // pre-init stuff - slider = new JXMultiThumbSlider(); - gradientPreview = new GradientPreviewPanel(); - gradientPreview.setMultiThumbModel(slider.getModel()); - - GridBagConstraints gridBagConstraints; - - typeGroup = new ButtonGroup(); - jPanel1 = new JPanel(); - topPanel = new JPanel(); - jPanel2 = new JPanel(); - jLabel1 = new JLabel(); - jLabel5 = new JLabel(); - colorField = new JTextField(); - jLabel2 = new JLabel(); - jLabel6 = new JLabel(); - colorLocationSpinner = new JSpinner(); - jLabel4 = new JLabel(); - jLabel7 = new JLabel(); - alphaSpinner = new JSpinner(); - changeColorButton = new JXColorSelectionButton(); - alphaSlider = new JSlider(); - //slider = new javax.swing.JSlider(); - jPanel4 = new JPanel(); - addThumbButton = new JButton(); - deleteThumbButton = new JButton(); - previewPanel = new JPanel(); - jPanel3 = new JPanel(); - jLabel8 = new JLabel(); - styleCombo = new JComboBox(); - jLabel9 = new JLabel(); - noCycleRadio = new JRadioButton(); - reflectedRadio = new JRadioButton(); - repeatedRadio = new JRadioButton(); - reversedCheck = new JCheckBox(); - //gradientPreview = new javax.swing.JPanel(); - - //setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); - jPanel1.setLayout(new GridBagLayout()); - - topPanel.setLayout(new GridBagLayout()); - - topPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Gradient")); - jPanel2.setLayout(new GridBagLayout()); - - jLabel1.setText("Color:"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 0; - gridBagConstraints.ipadx = 2; - gridBagConstraints.ipady = 2; - gridBagConstraints.anchor = GridBagConstraints.EAST; - gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4); - jPanel2.add(jLabel1, gridBagConstraints); - - jLabel5.setText("#"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 0; - gridBagConstraints.insets = new java.awt.Insets(4, 0, 4, 4); - jPanel2.add(jLabel5, gridBagConstraints); - - colorField.setColumns(6); - colorField.setEnabled(false); - colorField.setPreferredSize(null); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridy = 0; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - jPanel2.add(colorField, gridBagConstraints); - - jLabel2.setText("Location:"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridy = 1; - gridBagConstraints.anchor = GridBagConstraints.EAST; - gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4); - jPanel2.add(jLabel2, gridBagConstraints); - - jLabel6.setText("%"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridy = 1; - jPanel2.add(jLabel6, gridBagConstraints); - - colorLocationSpinner.setEnabled(false); - colorLocationSpinner.setPreferredSize(null); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridy = 1; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - jPanel2.add(colorLocationSpinner, gridBagConstraints); - - jLabel4.setText("Opacity:"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridy = 2; - gridBagConstraints.anchor = GridBagConstraints.EAST; - gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4); - jPanel2.add(jLabel4, gridBagConstraints); - - jLabel7.setText("%"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridy = 2; - jPanel2.add(jLabel7, gridBagConstraints); - - alphaSpinner.setEnabled(false); - alphaSpinner.setPreferredSize(null); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridy = 2; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - jPanel2.add(alphaSpinner, gridBagConstraints); - - changeColorButton.setText("00"); - changeColorButton.setEnabled(false); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.weightx = 1.0; - gridBagConstraints.anchor = GridBagConstraints.WEST; - gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 0); - jPanel2.add(changeColorButton, gridBagConstraints); - - alphaSlider.setEnabled(false); - alphaSlider.setPreferredSize(new Dimension(20, 25)); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridy = 2; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.weightx = 1.0; - jPanel2.add(alphaSlider, gridBagConstraints); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 2; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.weightx = 1.0; - gridBagConstraints.weighty = 1.0; - gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); - topPanel.add(jPanel2, gridBagConstraints); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridy = 0; - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - topPanel.add(slider, gridBagConstraints); - - jPanel4.setLayout(new GridLayout(1, 0, 2, 0)); - - addThumbButton.setText("Add"); - jPanel4.add(addThumbButton); - - deleteThumbButton.setText("Delete"); - jPanel4.add(deleteThumbButton); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridy = 1; - gridBagConstraints.anchor = GridBagConstraints.EAST; - gridBagConstraints.weightx = 1.0; - gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); - topPanel.add(jPanel4, gridBagConstraints); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.weightx = 1.0; - jPanel1.add(topPanel, gridBagConstraints); - - previewPanel.setLayout(new GridBagLayout()); - - previewPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Preview")); - jPanel3.setLayout(new GridBagLayout()); - - jLabel8.setText("Style:"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridy = 0; - gridBagConstraints.anchor = GridBagConstraints.EAST; - gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4); - jPanel3.add(jLabel8, gridBagConstraints); - - styleCombo.setModel(new DefaultComboBoxModel(new String[] { "Linear", "Radial" })); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridy = 0; - gridBagConstraints.anchor = GridBagConstraints.WEST; - gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4); - jPanel3.add(styleCombo, gridBagConstraints); - - jLabel9.setText("Type:"); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridy = 1; - gridBagConstraints.anchor = GridBagConstraints.EAST; - gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4); - jPanel3.add(jLabel9, gridBagConstraints); - - typeGroup.add(noCycleRadio); - noCycleRadio.setSelected(true); - noCycleRadio.setText("None"); - noCycleRadio.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); - noCycleRadio.setMargin(new java.awt.Insets(0, 0, 0, 0)); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridy = 1; - gridBagConstraints.anchor = GridBagConstraints.WEST; - gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4); - jPanel3.add(noCycleRadio, gridBagConstraints); - - typeGroup.add(reflectedRadio); - reflectedRadio.setText("Reflect"); - reflectedRadio.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); - reflectedRadio.setMargin(new java.awt.Insets(0, 0, 0, 0)); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 2; - gridBagConstraints.anchor = GridBagConstraints.WEST; - gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4); - jPanel3.add(reflectedRadio, gridBagConstraints); - - typeGroup.add(repeatedRadio); - repeatedRadio.setText("Repeat"); - repeatedRadio.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); - repeatedRadio.setMargin(new java.awt.Insets(0, 0, 0, 0)); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 3; - gridBagConstraints.anchor = GridBagConstraints.WEST; - gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4); - jPanel3.add(repeatedRadio, gridBagConstraints); - - reversedCheck.setText("Reverse"); - reversedCheck.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); - reversedCheck.setMargin(new java.awt.Insets(0, 0, 0, 0)); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 4; - gridBagConstraints.anchor = GridBagConstraints.WEST; - gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4); - jPanel3.add(reversedCheck, gridBagConstraints); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - previewPanel.add(jPanel3, gridBagConstraints); - - gradientPreview.setBorder(javax.swing.BorderFactory.createEtchedBorder()); - gradientPreview.setPreferredSize(new Dimension(130, 130)); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.fill = GridBagConstraints.BOTH; - gridBagConstraints.weightx = 10.0; - gridBagConstraints.weighty = 10.0; - previewPanel.add(gradientPreview, gridBagConstraints); - - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridy = 1; - gridBagConstraints.fill = GridBagConstraints.BOTH; - gridBagConstraints.anchor = GridBagConstraints.NORTH; - gridBagConstraints.weightx = 1.0; - gridBagConstraints.weighty = 1.0; - jPanel1.add(previewPanel, gridBagConstraints); - - }// - private void initComponents2() { - this.initComponents(); - setLayout(new BorderLayout()); - add(topPanel, BorderLayout.NORTH); - add(previewPanel, BorderLayout.CENTER); - - - // do event handling stuff - //create the actions and load them in the action map - AddThumbAction addThumbAction = new AddThumbAction(); - DeleteThumbAction deleteThumbAction = new DeleteThumbAction(); - deleteThumbAction.setEnabled(false); //disabled to begin with - //TODO Add to the action map with proper keys, etc - ActionMap actions = getActionMap(); - actions.put("add-thumb", addThumbAction); - actions.put("delete-thumb", deleteThumbAction); - //actions.put("change-color", changeColorAction); - addThumbButton.setAction(addThumbAction); - deleteThumbButton.setAction(deleteThumbAction); - changeColorButton.addPropertyChangeListener("background", new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - selectColorForThumb(); - updateGradientProperty(); - } - }); - colorLocationSpinner.addChangeListener(new ChangeLocationListener()); - ChangeAlphaListener changeAlphaListener = new ChangeAlphaListener(); - alphaSpinner.addChangeListener(changeAlphaListener); - alphaSlider.addChangeListener(changeAlphaListener); - RepaintOnEventListener repaintListener = new RepaintOnEventListener(); - styleCombo.addItemListener(repaintListener); - styleCombo.setModel(new DefaultComboBoxModel(GradientStyle.values())); - noCycleRadio.addActionListener(repaintListener); - reflectedRadio.addActionListener(repaintListener); - repeatedRadio.addActionListener(repaintListener); - reversedCheck.addActionListener(repaintListener); - gradientPreview.picker = this; //wow, nasty - - - ///To still refactor below:: - SpinnerNumberModel alpha_model = new SpinnerNumberModel(100,0,100,1); - alphaSpinner.setModel(alpha_model); - SpinnerNumberModel location_model = new SpinnerNumberModel(100,0,100,1); - colorLocationSpinner.setModel(location_model); - - slider.setOpaque(false); - slider.setPreferredSize(new Dimension(100,35)); - slider.getModel().setMinimumValue(0f); - slider.getModel().setMaximumValue(1.0f); - - slider.getModel().addThumb(0,Color.black); - slider.getModel().addThumb(0.5f,Color.red); - slider.getModel().addThumb(1.0f,Color.white); - - slider.setThumbRenderer(new GradientThumbRenderer()); - slider.setTrackRenderer(new GradientTrackRenderer()); - slider.addMultiThumbListener(new StopListener()); - - // called when the gradient property of the preview pane changes - gradientPreview.addPropertyChangeListener("gradient", new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent propertyChangeEvent) { - recalcGradientFromStops(); - } - }); - - recalcGradientFromStops(); - - } - - // called whenever the color location spinner is changed - private final class ChangeLocationListener implements ChangeListener { - @Override - public void stateChanged(ChangeEvent evt) { - if(slider.getSelectedIndex() >= 0) { - Thumb thumb = slider.getModel().getThumbAt(slider.getSelectedIndex()); - thumb.setPosition((Integer)colorLocationSpinner.getValue()/100f); - updateFromStop(thumb); - updateGradientProperty(); - } - } - } - - // called when the alpha slider moves - private final class ChangeAlphaListener implements ChangeListener { - @Override - public void stateChanged(ChangeEvent changeEvent) { - if(slider.getSelectedIndex() >= 0 && !thumbsMoving) { - // get the selected thumb - Thumb thumb = slider.getModel().getThumbAt(slider.getSelectedIndex()); - // get the new alpha value - int alpha = changeEvent.getSource() == alphaSpinner ? - (Integer)alphaSpinner.getValue() - : alphaSlider.getValue(); - - - // calc new color and set it on thumb - Color col = thumb.getObject(); - col = PaintUtils.setAlpha(col, alpha*255/100); - thumb.setObject(col); - - // set the new alpha value on the other alpha control - if (changeEvent.getSource() == alphaSpinner) { - alphaSlider.setValue(alpha); - } else { - alphaSpinner.setValue(alpha); - } - - recalcGradientFromStops(); - } - } - } - - - private final class AddThumbAction extends AbstractActionExt { - public AddThumbAction() { - super("Add"); - } - - @Override - public void actionPerformed(ActionEvent actionEvent) { - float pos = 0.2f; - Color color = Color.black; - int num = slider.getModel().addThumb(pos,color); - log.fine("new number = " + num); - /* - for (int i = 0; i < slider.getModel().getThumbCount(); i++) { - float pos2 = slider.getModel().getThumbAt(i).getPosition(); - if (pos2 < pos) { - continue; - } - slider.getModel().insertThumb(pos, color, i); - updateFromStop(i,pos,color); - break; - } - */ - - } - } - - private final class DeleteThumbAction extends AbstractActionExt { - public DeleteThumbAction() { - super("Delete"); - } - - @Override - public void actionPerformed(ActionEvent actionEvent) { - int index = slider.getSelectedIndex(); - if (index >= 0) { - slider.getModel().removeThumb(index); - updateFromStop(-1,-1,null); - } - } - } - - private class StopListener implements ThumbListener { - - public StopListener() { - super(); - } - - @Override - public void thumbMoved(int thumb, float pos) { - log.fine("moved: " + thumb + " " + pos); - Color color = slider.getModel().getThumbAt(thumb).getObject(); - thumbsMoving = true; - updateFromStop(thumb,pos,color); - updateDeleteButtons(); - thumbsMoving = false; - - } - - @Override - public void thumbSelected(int thumb) { - - if(thumb == -1) { - updateFromStop(-1,-1,Color.black); - return; - } - thumbsMoving = true; - float pos = slider.getModel().getThumbAt(thumb).getPosition(); - Color color = slider.getModel().getThumbAt(thumb).getObject(); - log.fine("selected = " + thumb + " " + pos + " " + color); - updateFromStop(thumb,pos,color); - updateDeleteButtons(); - slider.repaint(); - thumbsMoving = false; - - } - - @Override - public void mousePressed(MouseEvent e) { - if(e.getClickCount() > 1) { - selectColorForThumb(); - } - } - } - - private final class RepaintOnEventListener implements ActionListener, ItemListener { - @Override - public void actionPerformed(ActionEvent e) { - gradientPreview.setReflected(reflectedRadio.isSelected()); - gradientPreview.setReversed(reversedCheck.isSelected()); - gradientPreview.setRepeated(repeatedRadio.isSelected()); - //updateGradientProperty(); - recalcGradientFromStops(); - gradientPreview.repaint(); - } - - @Override - public void itemStateChanged(ItemEvent e) { - if(styleCombo.getSelectedItem() == GradientStyle.Radial) { - gradientPreview.setRadial(true); - } else { - gradientPreview.setRadial(false); - } - recalcGradientFromStops(); - } - } - - private void selectColorForThumb() { - int index = slider.getSelectedIndex(); - if (index >= 0) { - Color color = changeColorButton.getBackground(); - slider.getModel().getThumbAt(index).setObject(color); - updateFromStop(index, slider.getModel().getThumbAt(index).getPosition(), color); - } - } - - /** - * This static utility method cannot be called from the - * ETD, or your application will lock up. Call it from a separate - * thread or create a new Thread with a Runnable. - * @param comp The component to use when finding a top level window or frame for - * the dialog. - * @param title The desired title of the gradient chooser dialog. - * @param mgrad The gradient to initialize the chooser too. - * @return The gradient the user chose. - */ - public static MultipleGradientPaint showDialog(Component comp, String title, MultipleGradientPaint mgrad) { - Component root = SwingUtilities.getRoot(comp); - final JDialog dialog = new JDialog((JFrame)root,title,true); - final JXGradientChooser picker = new JXGradientChooser(); - if(mgrad != null) { - picker.setGradient(mgrad); - } - dialog.add(picker); - - - JPanel panel = new JPanel(); - JButton cancel = new JButton("Cancel"); - cancel.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent actionEvent) { - dialog.setVisible(false); - } - }); - JButton okay = new JButton("Ok"); - okay.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent actionEvent) { - dialog.setVisible(false); - } - }); - okay.setDefaultCapable(true); - - - GridLayout gl = new GridLayout(); - gl.setHgap(2); - panel.setLayout(gl); - panel.add(cancel); - panel.add(okay); - - JPanel p2 = new JPanel(); - p2.setLayout(new GridBagLayout()); - GridBagConstraints gbc = new GridBagConstraints(); - gbc.anchor = GridBagConstraints.EAST; - gbc.weightx = 1.0; - p2.add(panel,gbc); - dialog.add(p2,"South"); - - dialog.getRootPane().setDefaultButton(okay); - dialog.pack(); - dialog.setResizable(false); - dialog.setVisible(true); - - return picker.getGradient(); - } - - /** - * Creates a string representation of a {@code MultipleGradientPaint}. This - * string is used for debugging purposes. Its contents cannot be guaranteed - * between releases. - * - * @param paint - * the {@code paint} to create a string for - * @return a string representing the supplied {@code paint} - */ - public static String toString(MultipleGradientPaint paint) { - StringBuffer buffer = new StringBuffer(); - buffer.append(paint.getClass().getName()); - Color[] colors = paint.getColors(); - float[] values = paint.getFractions(); - buffer.append("["); - for(int i=0; iJXHeader is a simple component consisting of a title, a description, - * and an icon. An example of such a component can be seen on - * Romain Guys ProgX website

                - * - *

                JXHeader is a simple component that is also sufficiently - * configurable to be usable. The description area - * accepts HTML conforming to version 3.2 of the HTML standard. The icon, title, - * and description are all configurable. JXHeader itself extends - * {@link JXPanel}, providing translucency and painting delegates.

                - * - *

                If I were to reconstruct the ui shown in the above screenshot, I might - * do so like this:
                - *

                
                - *      JXHeader header = new JXHeader();
                - *      header.setTitle("Timing Framework Spline Editor");
                - *      header.setDescription("Drag control points in the display to change the " +
                - *          "shape of the spline\n" +
                - *          "Click the Copy Code button to generate the corresponding Java code.");
                - *      Icon icon = new ImageIcon(getClass().getResource("tools.png"));
                - *      header.setIcon(icon);
                - * 

                - * - * Note: The HTML support doesn't exist yet. The UI delegate needs to discover whether - * the text supplied is HTML or not, and change the content type of the editor pane - * being used. The problem is that if "text/html" is always used, the font is wrong. - * This same situation will be found in other parts of the code (JXErrorPane, for instance), - * so this needs to be dealt with. - * - *

                Defaults

                - *

                BasicHeaderUI uses the following UI defaults: - *

                  - *
                • Header.defaultIcon: The default icon to use when creating a new JXHeader.
                • - *
                - *

                - * - * @status REVIEWED - * @author rbair - * @author rah003 - */ -@JavaBean -public class JXHeader extends JXPanel { - /** - * SerialVersionUID. - */ - private static final long serialVersionUID = 3593838231433068954L; - - /** - * JXHeader pluggable UI key HeaderUI - */ - public final static String uiClassID = "HeaderUI"; - - // ensure at least the default ui is registered - static { - LookAndFeelAddons.contribute(new HeaderAddon()); - } - - /** - * Specifies desired location of the icon relative to the title/description text. - */ - public static enum IconPosition { - /** - * Positions icon left from the text. - */ - LEFT, - /** - * Positions icon right from the text. - */ - RIGHT - } - private String title; - private String description; - private Icon icon; - private Font titleFont; - private Font descriptionFont; - private Color titleForeground; - private Color descriptionForeground; - private IconPosition iconPosition = IconPosition.RIGHT; - - /** Creates a new instance of JXHeader */ - public JXHeader() { - } - - /** - * Creates a new instance of JXHeader. PropertyChangeEvents are fired - * when the title and description properties are set. - * - * @param title specifies the title property for this JXHeader - * @param description specifies the description property for this JXHeader - */ - public JXHeader(String title, String description) { - this(title, description, null); - } - - /** Creates a new instance of JXHeader. PropertyChangeEvents are fired - * when the title and description properties are set. - * - * @param title specifies the title property for this JXHeader - * @param description specifies the description property for this JXHeader - * @param icon specifies the icon property for this JXHeader - */ - public JXHeader(String title, String description, Icon icon) { - setTitle(title); - setDescription(description); - setIcon(icon); - } - - //------------------------------------------------------------- UI Logic - - /** - * {@inheritDoc} - */ - @Override - public HeaderUI getUI() { - return (HeaderUI)super.getUI(); - } - - /** - * Sets the look and feel (L&F) object that renders this component. - * - * @param ui the HeaderUI L&F object - * @see javax.swing.UIDefaults#getUI - */ - public void setUI(HeaderUI ui) { - super.setUI(ui); - } - - /** - * Returns the name of the L&F class that renders this component. - * - * @return the string {@link #uiClassID} - * @see javax.swing.JComponent#getUIClassID - * @see javax.swing.UIDefaults#getUI - */ - @Override - public String getUIClassID() { - return uiClassID; - } - - /** - * Notification from the UIManager that the L&F has changed. - * Replaces the current UI object with the latest version from the - * UIManager. - * - * @see javax.swing.JComponent#updateUI - */ - @Override - public void updateUI() { - setUI((HeaderUI) LookAndFeelAddons - .getUI(this, HeaderUI.class)); - } - - /** - * Sets the title to use. This may be either plain text, or a simplified - * version of HTML, as JLabel would use. - * - * @param title the Title. May be null. - */ - public void setTitle(String title) { - String old = getTitle(); - this.title = title; - firePropertyChange("title", old, getTitle()); - } - - /** - * Gets the title. This may use HTML, such as - * that supported by JLabel (version 3.2 of the HTML spec). - * @return the title. May be null. - */ - public String getTitle() { - return title; - } - - /** - * Sets the description for this header. This may use HTML, such as - * that supported by JLabel (version 3.2 of the HTML spec). - * - * @param description the description. May be null, may be HTML or plain text. - */ - public void setDescription(String description) { - String old = getDescription(); - this.description = description; - firePropertyChange("description", old, getDescription()); - } - - /** - * Gets the description. - * - * @return description - */ - public String getDescription() { - return description; - } - - /** - * Sets the icon to use for the header. It is generally recommended that this - * be an image 64x64 pixels in size, and that the icon have no gaps at the top. - * - * @param icon may be null - */ - public void setIcon(Icon icon) { - Icon old = getIcon(); - this.icon = icon; - firePropertyChange("icon", old, getIcon()); - } - - /** - * Gets the icon. - * - * @return the Icon being used. May be null. - */ - public Icon getIcon() { - return icon; - } - - /** - * Sets new font for both, title and description line of the header. - * @see javax.swing.JComponent#setFont(Font) - */ - @Override - public void setFont(Font font) { - super.setFont(font); - setTitleFont(font); - setDescriptionFont(font); - } - - /** - * Sets new font for title. - * @param font New title font. - */ - public void setTitleFont(Font font) { - Font old = getTitleFont(); - this.titleFont = font; - firePropertyChange("titleFont", old, getTitleFont()); - } - - /** - * Gets title font. - * - * @return the Font being used. May be null. - */ - public Font getTitleFont() { - return titleFont; - } - - /** - * Sets font for the description line of header. - * @param font New description font. - */ - public void setDescriptionFont(Font font) { - Font old = getDescriptionFont(); - this.descriptionFont = font; - firePropertyChange("descriptionFont", old, getDescriptionFont()); - } - - /** - * Gets description font. - * - * @return the Font being used. May be null. - */ - public Font getDescriptionFont() { - return descriptionFont; - } - - /** - * Gets current title foreground color. - * @return the Color used to paint title. May be null. - */ - public Color getTitleForeground() { - return titleForeground; - } - - /** - * Sets title foreground color. - * @param titleForeground the Color to be used to paint title. - */ - public void setTitleForeground(Color titleForeground) { - Color old = getTitleForeground(); - this.titleForeground = titleForeground; - firePropertyChange("titleForeground", old, getTitleForeground()); - } - - /** - * Gets current description foreground color. - * @return the Color used to paint description. May be null. - */ - public Color getDescriptionForeground() { - return descriptionForeground; - } - - /** - * Sets description foreground color. - * @param descriptionForeground the Color to be used to paint description. - */ - public void setDescriptionForeground(Color descriptionForeground) { - Color old = getDescriptionForeground(); - this.descriptionForeground = descriptionForeground; - firePropertyChange("descriptionForeground", old, getDescriptionForeground()); - } - - /** - * Gets current icon position. Default is RIGHT. - * @return Current Icon position. - */ - public IconPosition getIconPosition() { - return iconPosition; - } - - /** - * Sets new Icon position. Position is relative to the text. Default value is RIGHT. - * @see #getIconPosition() - * @param iconPosition new desired icon position - */ - public void setIconPosition(IconPosition iconPosition) { - IconPosition old = getIconPosition(); - this.iconPosition = iconPosition; - firePropertyChange("iconPosition", old, getIconPosition()); - } - - @Override - public Dimension getPreferredSize() { - Dimension s = super.getPreferredSize(); - // TODO: hack for JXLabel issue ... see JXHeaderVisualCheck.interactiveCustomProperties(); - s.width += 5; - return s; - } -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/JXHyperlink.java b/src/main/java/org/jdesktop/swingx/JXHyperlink.java deleted file mode 100644 index 3c93258ff9..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXHyperlink.java +++ /dev/null @@ -1,342 +0,0 @@ -/* - * $Id: JXHyperlink.java 4162 2012-02-08 16:21:35Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.hyperlink.AbstractHyperlinkAction; -import org.jdesktop.swingx.hyperlink.HyperlinkAction; -import org.jdesktop.swingx.plaf.HyperlinkAddon; -import org.jdesktop.swingx.plaf.LookAndFeelAddons; - -import javax.swing.*; -import javax.swing.plaf.ButtonUI; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.net.URI; - -/** - * A hyperlink component that derives from JButton to provide compatibility - * mostly for binding actions enabled/disabled behavior accessibility i18n etc... - *

                - *

                - * This button has visual state related to a notion of "clicked": - * foreground color is unclickedColor or clickedColor depending on - * its boolean bound property clicked being false or true, respectively. - * If the hyperlink has an action, it guarantees to synchronize its - * "clicked" state to an action value with key LinkAction.VISITED_KEY. - * Synchronization happens on setAction() and on propertyChange notification - * from the action. JXHyperlink accepts any type of action - - * {@link AbstractHyperlinkAction} is a convenience implementation to - * simplify clicked control. - *

                - * - *

                 
                - *      LinkAction linkAction = new LinkAction("http://swinglabs.org") {
                - *            public void actionPerformed(ActionEvent e) {
                - *                doSomething(getTarget());
                - *                setVisited(true);
                - *            }
                - *      };
                - *      JXHyperlink hyperlink = new JXHyperlink(linkAction);
                - *  
                - *

                - * The hyperlink can be configured to always update its clicked - * property after firing the actionPerformed: - * - *

                 
                - *      JXHyperlink hyperlink = new JXHyperlink(action);
                - *      hyperlink.setOverrulesActionOnClick(true);
                - *  
                - *

                - * By default, this property is false. The hyperlink will - * auto-click only if it has no action. Developers can change the - * behaviour by overriding {@link JXHyperlink#isAutoSetClicked()}; - * - * @author Richard Bair - * @author Shai Almog - * @author Jeanette Winzenburg - */ -@JavaBean -public class JXHyperlink extends JButton { - - /** - * @see #getUIClassID - * @see #readObject - */ - public static final String uiClassID = "HyperlinkUI"; - - // ensure at least the default ui is registered - static { - LookAndFeelAddons.contribute(new HyperlinkAddon()); - } - - private boolean hasBeenVisited = false; - - /** - * Color for the hyper link if it has not yet been clicked. This color can - * be set both in code, and through the UIManager with the property - * "JXHyperlink.unclickedColor". - */ - private Color unclickedColor; - - /** - * Color for the hyper link if it has already been clicked. This color can - * be set both in code, and through the UIManager with the property - * "JXHyperlink.clickedColor". - */ - private Color clickedColor; - - private boolean overrulesActionOnClick; - - /** - * Creates a new instance of JXHyperlink with default parameters - */ - public JXHyperlink() { - this(null); - } - - /** - * Creates a new instance of JHyperLink and configures it from provided Action. - * - * @param action Action whose parameters will be borrowed to configure newly - * created JXHyperLink - */ - public JXHyperlink(Action action) { - super(); - setAction(action); - init(); - } - - /** - * Convenience method to create and install a HyperlinkAction for the given URI. - * - * @param uri to uri to create a HyperlinkAction for, maybe null. - * @throws HeadlessException if {@link GraphicsEnvironment#isHeadless()} returns {@code true} - * @throws UnsupportedOperationException if the current platform doesn't support Desktop - * @see HyperlinkAction#createHyperlinkAction(URI) - */ - public void setURI(URI uri) { - setAction(HyperlinkAction.createHyperlinkAction(uri)); - } - - /** - * Returns the foreground color for unvisited links. - * - * @return Color for the hyper link if it has not yet been clicked. - */ - public Color getUnclickedColor() { - return unclickedColor; - } - - /** - * Sets the color for the previously not visited link. This value will override the one - * set by the "JXHyperlink.unclickedColor" UIManager property and defaults. - * - * @param color Color for the hyper link if it has not yet been clicked. - */ - public void setUnclickedColor(Color color) { - Color old = getUnclickedColor(); - unclickedColor = color; - if (!isClicked()) { - setForeground(getUnclickedColor()); - } - firePropertyChange("unclickedColor", old, getUnclickedColor()); - } - - /** - * Returns the foreground color for visited links. - * - * @return Color for the hyper link if it has already been clicked. - */ - public Color getClickedColor() { - return clickedColor; - } - - /** - * Sets the color for the previously visited link. This value will override the one - * set by the "JXHyperlink.clickedColor" UIManager property and defaults. - * - * @param color Color for the hyper link if it has already been clicked. - */ - public void setClickedColor(Color color) { - Color old = getClickedColor(); - clickedColor = color; - if (isClicked()) { - setForeground(getClickedColor()); - } - firePropertyChange("clickedColor", old, getClickedColor()); - } - - /** - * Returns a boolean indicating if this link has already been visited. - * - * @return true if hyper link has already been clicked. - * @see #setClicked(boolean) - */ - public boolean isClicked() { - return hasBeenVisited; - } - - /** - * Sets the clicked property and updates visual state depending on clicked. - * This implementation updated the foreground color. - *

                - *

                - * NOTE: as with all button's visual properties, this will not update the - * backing action's "visited" state. - * - * @param clicked flag to indicate if the button should be regarded as - * having been clicked or not. - * @see #isClicked() - */ - public void setClicked(boolean clicked) { - boolean old = isClicked(); - hasBeenVisited = clicked; - setForeground(isClicked() ? getClickedColor() : getUnclickedColor()); - firePropertyChange("clicked", old, isClicked()); - } - - /** - * Returns a boolean indicating whether the clicked property should be set - * always on clicked. - * - * @return overrulesActionOnClick false if his button clicked property - * respects the Action's visited property. True if the clicked - * should be updated on every actionPerformed. - * @see #setOverrulesActionOnClick(boolean) - * @see #setClicked(boolean) - */ - public boolean getOverrulesActionOnClick() { - return overrulesActionOnClick; - } - - /** - * Sets the overrulesActionOnClick property. It controls whether this - * button should overrule the Action's visited property on actionPerformed.

                - *

                - * The default value is false. - * - * @param overrule if true, fireActionPerformed will set clicked to true - * independent of action. - * @see #getOverrulesActionOnClick() - * @see #setClicked(boolean) - */ - public void setOverrulesActionOnClick(boolean overrule) { - boolean old = getOverrulesActionOnClick(); - this.overrulesActionOnClick = overrule; - firePropertyChange("overrulesActionOnClick", old, getOverrulesActionOnClick()); - } - - /** - * {@inheritDoc}

                - * Overridden to respect the overrulesActionOnClick property. - */ - @Override - protected void fireActionPerformed(ActionEvent event) { - super.fireActionPerformed(event); - if (isAutoSetClicked()) { - setClicked(true); - } - } - - /** - * Returns a boolean indicating whether the clicked property should be set - * after firing action events. - * Here: true if no action or overrulesAction property is true. - * - * @return true if fireActionEvent should force a clicked, false if not. - */ - protected boolean isAutoSetClicked() { - return getAction() == null || getOverrulesActionOnClick(); - } - - /** - * Creates and returns a listener that will watch the changes of the - * provided Action and will update JXHyperlink's properties - * accordingly. - */ - @Override - protected PropertyChangeListener createActionPropertyChangeListener( - final Action a) { - final PropertyChangeListener superListener = super - .createActionPropertyChangeListener(a); - // JW: need to do something better - only weak refs allowed! - // no way to hook into super - return new PropertyChangeListener() { - - @Override - public void propertyChange(PropertyChangeEvent evt) { - if (AbstractHyperlinkAction.VISITED_KEY.equals(evt.getPropertyName())) { - configureClickedPropertyFromAction(a); - } else { - superListener.propertyChange(evt); - } - - } - - }; - } - - /** - * Read all the essential properties from the provided Action - * and apply it to the JXHyperlink - */ - @Override - protected void configurePropertiesFromAction(Action a) { - super.configurePropertiesFromAction(a); - configureClickedPropertyFromAction(a); - } - - private void configureClickedPropertyFromAction(Action a) { - boolean clicked = false; - if (a != null) { - clicked = Boolean.TRUE.equals(a.getValue(AbstractHyperlinkAction.VISITED_KEY)); - - } - setClicked(clicked); - } - - private void init() { - setForeground(isClicked() ? getClickedColor() : getUnclickedColor()); - } - - /** - * Returns a string that specifies the name of the L&F class - * that renders this component. - */ - @Override - public String getUIClassID() { - return uiClassID; - } - - /** - * Notification from the UIManager that the L&F has changed. - * Replaces the current UI object with the latest version from the UIManager. - * - * @see javax.swing.JComponent#updateUI - */ - @Override - public void updateUI() { - setUI((ButtonUI) LookAndFeelAddons.getUI(this, ButtonUI.class)); - } -} diff --git a/src/main/java/org/jdesktop/swingx/JXLabel.java b/src/main/java/org/jdesktop/swingx/JXLabel.java deleted file mode 100644 index 9c4da90c6d..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXLabel.java +++ /dev/null @@ -1,1318 +0,0 @@ -/* - * $Id: JXLabel.java 4225 2012-08-07 15:37:57Z kschaefe $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.painter.AbstractPainter; -import org.jdesktop.swingx.painter.Painter; - -import javax.swing.*; -import javax.swing.border.Border; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentEvent.ElementChange; -import javax.swing.plaf.basic.BasicHTML; -import javax.swing.text.*; -import java.awt.*; -import java.awt.event.HierarchyBoundsAdapter; -import java.awt.event.HierarchyEvent; -import java.awt.font.TextAttribute; -import java.awt.geom.Point2D; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.io.Reader; -import java.io.StringReader; - -/** - *

                - * A {@link JLabel} subclass which supports {@link Painter}s, multi-line text, - * and text rotation. - *

                - * - *

                - * Painter support consists of the foregroundPainter and backgroundPainter properties. The - * backgroundPainter refers to a painter responsible for painting beneath the text and icon. This - * painter, if set, will paint regardless of the opaque property. If the background painter does not - * fully paint each pixel, then you should make sure the opaque property is set to false. - *

                - * - *

                - * The foregroundPainter is responsible for painting the icon and the text label. If no foregroundPainter - * is specified, then the look and feel will paint the label. Note that if opaque is set to true and the look and feel - * is rendering the foreground, then the foreground may paint over the background. Most look and feels will - * paint a background when opaque is true. To avoid this behavior, set opaque to false. - *

                - * - *

                - * Since JXLabel is not opaque by default (isOpaque() returns false), neither of these problems - * typically present themselves. - *

                - * - *

                - * Multi-line text is enabled via the lineWrap property. Simply set it to true. By default, line wrapping - * occurs on word boundaries. - *

                - * - *

                - * The text (actually, the entire foreground and background) of the JXLabel may be rotated. Set the - * rotation property to specify what the rotation should be. Specify rotation angle in radian units. - *

                - * - * @author joshua.marinacci@sun.com - * @author rbair - * @author rah - * @author mario_cesar - */ -@JavaBean -public class JXLabel extends JLabel implements BackgroundPaintable { - - /** - * Text alignment enums. Controls alignment of the text when line wrapping is enabled. - */ - public enum TextAlignment implements IValue { - LEFT(StyleConstants.ALIGN_LEFT), CENTER(StyleConstants.ALIGN_CENTER), RIGHT(StyleConstants.ALIGN_RIGHT), JUSTIFY(StyleConstants.ALIGN_JUSTIFIED); - - private int value; - private TextAlignment(int val) { - value = val; - } - - @Override - public int getValue() { - return value; - } - - } - - protected interface IValue { - int getValue(); - } - - // textOrientation value declarations... - public static final double NORMAL = 0; - - public static final double INVERTED = Math.PI; - - public static final double VERTICAL_LEFT = 3 * Math.PI / 2; - - public static final double VERTICAL_RIGHT = Math.PI / 2; - - private double textRotation = NORMAL; - - private boolean painting = false; - - private Painter foregroundPainter; - - private Painter backgroundPainter; - - private boolean multiLine; - - private int pWidth; - - private int pHeight; - - // using reverse logic ... some methods causing re-flow of text are called from super constructor, but private variables are initialized only after call to super so have to rely on default for boolean being false - private boolean dontIgnoreRepaint = false; - - private int occupiedWidth; - - private static final String oldRendererKey = "was" + BasicHTML.propertyKey; - -// private static final Logger log = Logger.getAnonymousLogger(); -// static { -// log.setLevel(Level.FINEST); -// } - - /** - * Create a new JXLabel. This has the same semantics as creating a new JLabel. - */ - public JXLabel() { - super(); - initPainterSupport(); - initLineWrapSupport(); - } - - /** - * Creates new JXLabel with given icon. - * @param image the icon to set. - */ - public JXLabel(Icon image) { - super(image); - initPainterSupport(); - initLineWrapSupport(); - } - - /** - * Creates new JXLabel with given icon and alignment. - * @param image the icon to set. - * @param horizontalAlignment the text alignment. - */ - public JXLabel(Icon image, int horizontalAlignment) { - super(image, horizontalAlignment); - initPainterSupport(); - initLineWrapSupport(); - } - - /** - * Create a new JXLabel with the given text as the text for the label. This is shorthand for: - * - *
                
                -     * JXLabel label = new JXLabel();
                -     * label.setText("Some Text");
                -     * 
                - * - * @param text the text to set. - */ - public JXLabel(String text) { - super(text); - initPainterSupport(); - initLineWrapSupport(); - } - - /** - * Creates new JXLabel with given text, icon and alignment. - * @param text the test to set. - * @param image the icon to set. - * @param horizontalAlignment the text alignment relative to the icon. - */ - public JXLabel(String text, Icon image, int horizontalAlignment) { - super(text, image, horizontalAlignment); - initPainterSupport(); - initLineWrapSupport(); - } - - /** - * Creates new JXLabel with given text and alignment. - * @param text the test to set. - * @param horizontalAlignment the text alignment. - */ - public JXLabel(String text, int horizontalAlignment) { - super(text, horizontalAlignment); - initPainterSupport(); - initLineWrapSupport(); - } - - private void initPainterSupport() { - foregroundPainter = new AbstractPainter() { - @Override - protected void doPaint(Graphics2D g, JXLabel label, int width, int height) { - Insets i = getInsets(); - g = (Graphics2D) g.create(-i.left, -i.top, width, height); - - try { - label.paint(g); - } finally { - g.dispose(); - } - } - //if any of the state of the JButton that affects the foreground has changed, - //then I must clear the cache. This is really hard to get right, there are - //bound to be bugs. An alternative is to NEVER cache. - @Override - protected boolean shouldUseCache() { - return false; - } - - @Override - public boolean equals(Object obj) { - return obj != null && this.getClass().equals(obj.getClass()); - } - - }; - ((AbstractPainter) foregroundPainter).setAntialiasing(false); - } - - /** - * Helper method for initializing multi line support. - */ - private void initLineWrapSupport() { - addPropertyChangeListener(new MultiLineSupport()); - // FYI: no more listening for componentResized. Those events are delivered out - // of order and without old values are meaningless and forcing us to react when - // not necessary. Instead overriding reshape() ensures we have control over old AND new size. - addHierarchyBoundsListener(new HierarchyBoundsAdapter() { - @Override - public void ancestorResized(HierarchyEvent e) { - // if one of the parents is viewport, resized events will not be propagated down unless viewport is changing visibility of scrollbars. - // To make sure Label is able to re-wrap text when viewport size changes, initiate re-wrapping here by changing size of view - if (e.getChanged() instanceof JViewport) { - Rectangle viewportBounds = e.getChanged().getBounds(); - if (viewportBounds.getWidth() < getWidth()) { - View view = getWrappingView(); - if (view != null) { - view.setSize(viewportBounds.width, viewportBounds.height); - } - } - } - }}); - } - - /** - * Returns the current foregroundPainter. This is a bound property. By default the foregroundPainter will be an - * internal painter which executes the standard painting code (paintComponent()). - * - * @return the current foreground painter. - */ - public final Painter getForegroundPainter() { - return foregroundPainter; - } - - @Override - @SuppressWarnings("deprecation") - public void reshape(int x, int y, int w, int h) { - int oldH = getHeight(); - super.reshape(x, y, w, h); - if (!isLineWrap()) { - return; - } - if (oldH == 0) { - return; - } - if (w > getVisibleRect().width) { - w = getVisibleRect().width; - } - View view = (View) getClientProperty(BasicHTML.propertyKey); - if (view != null && view instanceof Renderer) { - view.setSize(w - occupiedWidth, h); - } - } - - /** - * Sets a new foregroundPainter on the label. This will replace the existing foreground painter. Existing painters - * can be wrapped by using a CompoundPainter. - * - * @param painter - */ - public void setForegroundPainter(Painter painter) { - Painter old = this.getForegroundPainter(); - if (painter == null) { - //restore default painter - initPainterSupport(); - } else { - this.foregroundPainter = painter; - } - firePropertyChange("foregroundPainter", old, getForegroundPainter()); - repaint(); - } - - /** - * Sets a Painter to use to paint the background of this component By default there is already a single painter - * installed which draws the normal background for this component according to the current Look and Feel. Calling - * setBackgroundPainter will replace that existing painter. - * - * @param p the new painter - * @see #getBackgroundPainter() - */ - @Override - public void setBackgroundPainter(Painter p) { - Painter old = getBackgroundPainter(); - backgroundPainter = p; - firePropertyChange("backgroundPainter", old, getBackgroundPainter()); - repaint(); - } - - /** - * Returns the current background painter. The default value of this property is a painter which draws the normal - * JPanel background according to the current look and feel. - * - * @return the current painter - * @see #setBackgroundPainter(Painter) - */ - @Override - public final Painter getBackgroundPainter() { - return backgroundPainter; - } - - /** - * Gets current value of text rotation in rads. - * - * @return a double representing the current rotation of the text - * @see #setTextRotation(double) - */ - public double getTextRotation() { - return textRotation; - } - - @Override - public Dimension getPreferredSize() { - Dimension size = super.getPreferredSize(); - //if (true) return size; - if (isPreferredSizeSet()) { - //log.fine("ret 0"); - return size; - } else if (this.textRotation != NORMAL) { - // #swingx-680 change the preferred size when rotation is set ... ideally this would be solved in the LabelUI rather then here - double theta = getTextRotation(); - size.setSize(rotateWidth(size, theta), rotateHeight(size, - theta)); - } else { - // #swingx-780 preferred size is not set properly when parent container doesn't enforce the width - View view = getWrappingView(); - if (view == null) { - if (isLineWrap() && !MultiLineSupport.isHTML(getText())) { - getMultiLineSupport(); - // view might get lost on LAF change ... - putClientProperty(BasicHTML.propertyKey, - MultiLineSupport.createView(this)); - view = (View) getClientProperty(BasicHTML.propertyKey); - } else { - return size; - } - } - Insets insets = getInsets(); - int dx = insets.left + insets.right; - int dy = insets.top + insets.bottom; - //log.fine("INSETS:" + insets); - //log.fine("BORDER:" + this.getBorder()); - Rectangle textR = new Rectangle(); - Rectangle viewR = new Rectangle(); - textR.x = textR.y = textR.width = textR.height = 0; - viewR.x = dx; - viewR.y = dy; - viewR.width = viewR.height = Short.MAX_VALUE; - // layout label - // 1) icon - Rectangle iconR = calculateIconRect(); - // 2) init textR - boolean textIsEmpty = (getText() == null) || getText().equals(""); - int lsb = 0; - /* Unless both text and icon are non-null, we effectively ignore - * the value of textIconGap. - */ - int gap; - if (textIsEmpty) { - textR.width = textR.height = 0; - gap = 0; - } - else { - int availTextWidth; - gap = (iconR.width == 0) ? 0 : getIconTextGap(); - - occupiedWidth = dx + iconR.width + gap; - Object parent = getParent(); - if (parent != null && (parent instanceof JPanel)) { - JPanel panel = ((JPanel) parent); - Border b = panel.getBorder(); - if (b != null) { - Insets in = b.getBorderInsets(panel); - occupiedWidth += in.left + in.right; - } - } - if (getHorizontalTextPosition() == CENTER) { - availTextWidth = viewR.width; - } - else { - availTextWidth = viewR.width - (iconR.width + gap); - } - float xPrefSpan = view.getPreferredSpan(View.X_AXIS); - //log.fine("atw:" + availTextWidth + ", vps:" + xPrefSpan); - textR.width = Math.min(availTextWidth, (int) xPrefSpan); - if (maxLineSpan > 0) { - textR.width = Math.min(textR.width, maxLineSpan); - if (xPrefSpan > maxLineSpan) { - view.setSize(maxLineSpan, textR.height); - } - } - textR.height = (int) view.getPreferredSpan(View.Y_AXIS); - if (textR.height == 0) { - textR.height = getFont().getSize(); - } - //log.fine("atw:" + availTextWidth + ", vps:" + xPrefSpan + ", h:" + textR.height); - - } - // 3) set text xy based on h/v text pos - if (getVerticalTextPosition() == TOP) { - if (getHorizontalTextPosition() != CENTER) { - textR.y = 0; - } - else { - textR.y = -(textR.height + gap); - } - } - else if (getVerticalTextPosition() == CENTER) { - textR.y = (iconR.height / 2) - (textR.height / 2); - } - else { // (verticalTextPosition == BOTTOM) - if (getVerticalTextPosition() != CENTER) { - textR.y = iconR.height - textR.height; - } - else { - textR.y = (iconR.height + gap); - } - } - - if (getHorizontalTextPosition() == LEFT) { - textR.x = -(textR.width + gap); - } - else if (getHorizontalTextPosition() == CENTER) { - textR.x = (iconR.width / 2) - (textR.width / 2); - } - else { // (horizontalTextPosition == RIGHT) - textR.x = (iconR.width + gap); - } - - // 4) shift label around based on its alignment - int labelR_x = Math.min(iconR.x, textR.x); - int labelR_width = Math.max(iconR.x + iconR.width, - textR.x + textR.width) - labelR_x; - int labelR_y = Math.min(iconR.y, textR.y); - int labelR_height = Math.max(iconR.y + iconR.height, - textR.y + textR.height) - labelR_y; - - int dax, day; - - if (getVerticalAlignment() == TOP) { - day = viewR.y - labelR_y; - } - else if (getVerticalAlignment() == CENTER) { - day = (viewR.y + (viewR.height / 2)) - (labelR_y + (labelR_height / 2)); - } - else { // (verticalAlignment == BOTTOM) - day = (viewR.y + viewR.height) - (labelR_y + labelR_height); - } - - if (getHorizontalAlignment() == LEFT) { - dax = viewR.x - labelR_x; - } - else if (getHorizontalAlignment() == RIGHT) { - dax = (viewR.x + viewR.width) - (labelR_x + labelR_width); - } - else { // (horizontalAlignment == CENTER) - dax = (viewR.x + (viewR.width / 2)) - - (labelR_x + (labelR_width / 2)); - } - - textR.x += dax; - textR.y += day; - - iconR.x += dax; - iconR.y += day; - - if (lsb < 0) { - // lsb is negative. Shift the x location so that the text is - // visually drawn at the right location. - textR.x -= lsb; - } - // EO layout label - - int x1 = Math.min(iconR.x, textR.x); - int x2 = Math.max(iconR.x + iconR.width, textR.x + textR.width); - int y1 = Math.min(iconR.y, textR.y); - int y2 = Math.max(iconR.y + iconR.height, textR.y + textR.height); - Dimension rv = new Dimension(x2 - x1, y2 - y1); - - rv.width += dx; - rv.height += dy; - //log.fine("returning: " + rv); - return rv; - } - //log.fine("ret 3"); - return size; - } - - private View getWrappingView() { - if (super.getTopLevelAncestor() == null) { - return null; - } - View view = (View) getClientProperty(BasicHTML.propertyKey); - if (!(view instanceof Renderer)) { - return null; - } - return view; - } - - private Container getViewport() { - for(Container p = this; p != null; p = p.getParent()) { - if(p instanceof Window || SwingXUtilities.isApplet(p) || p instanceof JViewport) { - return p; - } - } - return null; - } - - private Rectangle calculateIconRect() { - Rectangle iconR = new Rectangle(); - Icon icon = isEnabled() ? getIcon() : getDisabledIcon(); - iconR.x = iconR.y = iconR.width = iconR.height = 0; - if (icon != null) { - iconR.width = icon.getIconWidth(); - iconR.height = icon.getIconHeight(); - } - else { - iconR.width = iconR.height = 0; - } - return iconR; - } - - public int getMaxLineSpan() { - return maxLineSpan ; - } - - public void setMaxLineSpan(int maxLineSpan) { - int old = getMaxLineSpan(); - this.maxLineSpan = maxLineSpan; - firePropertyChange("maxLineSpan", old, getMaxLineSpan()); - } - - private static int rotateWidth(Dimension size, double theta) { - return (int)Math.round(size.width*Math.abs(Math.cos(theta)) + - size.height*Math.abs(Math.sin(theta))); - } - - private static int rotateHeight(Dimension size, double theta) { - return (int)Math.round(size.width*Math.abs(Math.sin(theta)) + - size.height*Math.abs(Math.cos(theta))); - } - - /** - * Sets new value for text rotation. The value can be anything in range <0,2PI>. Note that although property name - * suggests only text rotation, the whole foreground painter is rotated in fact. Due to various reasons it is - * strongly discouraged to access any size related properties of the label from other threads then EDT when this - * property is set. - * - * @param textOrientation Value for text rotation in range <0,2PI> - * @see #getTextRotation() - */ - public void setTextRotation(double textOrientation) { - double old = getTextRotation(); - this.textRotation = textOrientation; - if (old != getTextRotation()) { - firePropertyChange("textRotation", old, getTextRotation()); - } - repaint(); - } - - /** - * Enables line wrapping support for plain text. By default this support is disabled to mimic default of the JLabel. - * Value of this property has no effect on HTML text. - * - * @param b the new value - */ - public void setLineWrap(boolean b) { - boolean old = isLineWrap(); - this.multiLine = b; - if (isLineWrap() != old) { - firePropertyChange("lineWrap", old, isLineWrap()); - if (getForegroundPainter() != null) { - // XXX There is a bug here. In order to make painter work with this, caching has to be disabled - ((AbstractPainter) getForegroundPainter()).setCacheable(!b); - } - //repaint(); - } - } - - /** - * Returns the current status of line wrap support. The default value of this property is false to mimic default - * JLabel behavior. Value of this property has no effect on HTML text. - * - * @return the current multiple line splitting status - */ - public boolean isLineWrap() { - return this.multiLine; - } - - private boolean paintBorderInsets = true; - - private int maxLineSpan = -1; - - public boolean painted; - - private TextAlignment textAlignment = TextAlignment.LEFT; - - /** - * Gets current text wrapping style. - * - * @return the text alignment for this label - */ - public TextAlignment getTextAlignment() { - return textAlignment; - } - - /** - * Sets style of wrapping the text. - * @see TextAlignment for accepted values. - * @param alignment - */ - public void setTextAlignment(TextAlignment alignment) { - TextAlignment old = getTextAlignment(); - this.textAlignment = alignment; - firePropertyChange("textAlignment", old, getTextAlignment()); - } - - /** - * Returns true if the background painter should paint where the border is - * or false if it should only paint inside the border. This property is - * true by default. This property affects the width, height, - * and initial transform passed to the background painter. - * @return current value of the paintBorderInsets property - */ - @Override - public boolean isPaintBorderInsets() { - return paintBorderInsets; - } - - @Override - public boolean isOpaque() { - return painting ? false : super.isOpaque(); - } - - /** - * Sets the paintBorderInsets property. - * Set to true if the background painter should paint where the border is - * or false if it should only paint inside the border. This property is true by default. - * This property affects the width, height, - * and initial transform passed to the background painter. - * - * This is a bound property. - * @param paintBorderInsets new value of the paintBorderInsets property - */ - @Override - public void setPaintBorderInsets(boolean paintBorderInsets) { - boolean old = this.isPaintBorderInsets(); - this.paintBorderInsets = paintBorderInsets; - firePropertyChange("paintBorderInsets", old, isPaintBorderInsets()); - } - - /** - * @param g graphics to paint on - */ - @Override - protected void paintComponent(Graphics g) { - //log.fine("in"); - // resizing the text view causes recursive callback to the paint down the road. In order to prevent such - // computationally intensive series of repaints every call to paint is skipped while top most call is being - // executed. -// if (!dontIgnoreRepaint) { -// return; -// } - painted = true; - if (painting || backgroundPainter == null && foregroundPainter == null) { - super.paintComponent(g); - } else { - pWidth = getWidth(); - pHeight = getHeight(); - if (backgroundPainter != null) { - Graphics2D tmp = (Graphics2D) g.create(); - - try { - SwingXUtilities.paintBackground(this, tmp); - } finally { - tmp.dispose(); - } - } - if (foregroundPainter != null) { - Insets i = getInsets(); - pWidth = getWidth() - i.left - i.right; - pHeight = getHeight() - i.top - i.bottom; - - Point2D tPoint = calculateT(); - double wx = Math.sin(textRotation) * tPoint.getY() + Math.cos(textRotation) * tPoint.getX(); - double wy = Math.sin(textRotation) * tPoint.getX() + Math.cos(textRotation) * tPoint.getY(); - double x = (getWidth() - wx) / 2 + Math.sin(textRotation) * tPoint.getY(); - double y = (getHeight() - wy) / 2; - Graphics2D tmp = (Graphics2D) g.create(); - if (i != null) { - tmp.translate(i.left + x, i.top + y); - } else { - tmp.translate(x, y); - } - tmp.rotate(textRotation); - - painting = true; - // uncomment to highlight text area - // Color c = g2.getColor(); - // g2.setColor(Color.RED); - // g2.fillRect(0, 0, getWidth(), getHeight()); - // g2.setColor(c); - //log.fine("PW:" + pWidth + ", PH:" + pHeight); - foregroundPainter.paint(tmp, this, pWidth, pHeight); - tmp.dispose(); - painting = false; - pWidth = 0; - pHeight = 0; - } - } - } - - private Point2D calculateT() { - double tx = getWidth(); - double ty = getHeight(); - - // orthogonal cases are most likely the most often used ones, so give them preferential treatment. - if ((textRotation > 4.697 && textRotation < 4.727) || (textRotation > 1.555 && textRotation < 1.585)) { - // vertical - int tmp = pHeight; - pHeight = pWidth; - pWidth = tmp; - tx = pWidth; - ty = pHeight; - } else if ((textRotation > -0.015 && textRotation < 0.015) - || (textRotation > 3.140 && textRotation < 3.1430)) { - // normal & inverted - pHeight = getHeight(); - pWidth = getWidth(); - } else { - // the rest of it. Calculate best rectangle that fits the bounds. "Best" is considered one that - // allows whole text to fit in, spanned on preferred axis (X). If that doesn't work, fit the text - // inside square with diagonal equal min(height, width) (Should be the largest rectangular area that - // fits in, math proof available upon request) - - dontIgnoreRepaint = false; - double square = Math.min(getHeight(), getWidth()) * Math.cos(Math.PI / 4d); - - View v = (View) getClientProperty(BasicHTML.propertyKey); - if (v == null) { - // no html and no wrapline enabled means no view - // ... find another way to figure out the heigh - ty = getFontMetrics(getFont()).getHeight(); - double cw = (getWidth() - Math.abs(ty * Math.sin(textRotation))) - / Math.abs(Math.cos(textRotation)); - double ch = (getHeight() - Math.abs(ty * Math.cos(textRotation))) - / Math.abs(Math.sin(textRotation)); - // min of whichever is above 0 (!!! no min of abs values) - tx = cw < 0 ? ch : ch > 0 ? Math.min(cw, ch) : cw; - } else { - float w = v.getPreferredSpan(View.X_AXIS); - float h = v.getPreferredSpan(View.Y_AXIS); - double c = w; - double alpha = textRotation;// % (Math.PI/2d); - boolean ready = false; - while (!ready) { - // shorten the view len until line break is forced - while (h == v.getPreferredSpan(View.Y_AXIS)) { - w -= 10; - v.setSize(w, h); - } - if (w < square || h > square) { - // text is too long to fit no matter what. Revert shape to square since that is the - // best option (1st derivation for area size of rotated rect in rect is equal 0 for - // rotated rect with equal w and h i.e. for square) - w = h = (float) square; - // set view height to something big to prevent recursive resize/repaint requests - v.setSize(w, 100000); - break; - } - // calc avail width with new view height - h = v.getPreferredSpan(View.Y_AXIS); - double cw = (getWidth() - Math.abs(h * Math.sin(alpha))) / Math.abs(Math.cos(alpha)); - double ch = (getHeight() - Math.abs(h * Math.cos(alpha))) / Math.abs(Math.sin(alpha)); - // min of whichever is above 0 (!!! no min of abs values) - c = cw < 0 ? ch : ch > 0 ? Math.min(cw, ch) : cw; - // make it one pix smaller to ensure text is not cut on the left - c--; - if (c > w) { - v.setSize((float) c, 10 * h); - ready = true; - } else { - v.setSize((float) c, 10 * h); - if (v.getPreferredSpan(View.Y_AXIS) > h) { - // set size back to figure out new line break and height after - v.setSize(w, 10 * h); - } else { - w = (float) c; - ready = true; - } - } - } - - tx = Math.floor(w);// xxx: watch out for first letter on each line missing some pixs!!! - ty = h; - } - pWidth = (int) tx; - pHeight = (int) ty; - dontIgnoreRepaint = true; - } - return new Point2D.Double(tx,ty); - } - - @Override - public void repaint() { - if (!dontIgnoreRepaint) { - return; - } - super.repaint(); - } - - @Override - public void repaint(int x, int y, int width, int height) { - if (!dontIgnoreRepaint) { - return; - } - super.repaint(x, y, width, height); - } - - @Override - public void repaint(long tm) { - if (!dontIgnoreRepaint) { - return; - } - super.repaint(tm); - } - - @Override - public void repaint(long tm, int x, int y, int width, int height) { - if (!dontIgnoreRepaint) { - return; - } - super.repaint(tm, x, y, width, height); - } - - // ---------------------------------------------------------- - // textOrientation magic - @Override - public int getHeight() { - int retValue = super.getHeight(); - if (painting) { - retValue = pHeight; - } - return retValue; - } - - @Override - public int getWidth() { - int retValue = super.getWidth(); - if (painting) { - retValue = pWidth; - } - return retValue; - } - - protected MultiLineSupport getMultiLineSupport() { - return new MultiLineSupport(); - } - // ---------------------------------------------------------- - // WARNING: - // Anything below this line is related to lineWrap support and can be safely ignored unless - // in need to mess around with the implementation details. - // ---------------------------------------------------------- - // FYI: This class doesn't reinvent line wrapping. Instead it makes use of existing support - // made for JTextComponent/JEditorPane. - // All the classes below named Alter* are verbatim copy of swing.text.* classes made to - // overcome package visibility of some of the code. All other classes here, when their name - // matches corresponding class from swing.text.* package are copy of the class with removed - // support for highlighting selection. In case this is ever merged back to JDK all of this - // can be safely removed as long as corresponding swing.text.* classes make appropriate checks - // before casting JComponent into JTextComponent to find out selected region since - // JLabel/JXLabel does not support selection of the text. - - public static class MultiLineSupport implements PropertyChangeListener { - - private static final String HTML = ""; - - private static ViewFactory basicViewFactory; - - private static BasicEditorKit basicFactory; - - @Override - public void propertyChange(PropertyChangeEvent evt) { - String name = evt.getPropertyName(); - JXLabel src = (JXLabel) evt.getSource(); - if ("ancestor".equals(name)) { - src.dontIgnoreRepaint = true; - } - if (src.isLineWrap()) { - if ("font".equals(name) || "foreground".equals(name) || "maxLineSpan".equals(name) || "textAlignment".equals(name) || "icon".equals(name) || "iconTextGap".equals(name)) { - if (evt.getOldValue() != null && !isHTML(src.getText())) { - updateRenderer(src); - } - } else if ("text".equals(name)) { - if (isHTML((String) evt.getOldValue()) && evt.getNewValue() != null - && !isHTML((String) evt.getNewValue())) { - // was html , but is not - if (src.getClientProperty(oldRendererKey) == null - && src.getClientProperty(BasicHTML.propertyKey) != null) { - src.putClientProperty(oldRendererKey, src.getClientProperty(BasicHTML.propertyKey)); - } - src.putClientProperty(BasicHTML.propertyKey, createView(src)); - } else if (!isHTML((String) evt.getOldValue()) && evt.getNewValue() != null - && !isHTML((String) evt.getNewValue())) { - // wasn't html and isn't - updateRenderer(src); - } else { - // either was html and is html or wasn't html, but is html - restoreHtmlRenderer(src); - } - } else if ("lineWrap".equals(name) && !isHTML(src.getText())) { - src.putClientProperty(BasicHTML.propertyKey, createView(src)); - } - } else if ("lineWrap".equals(name) && !((Boolean)evt.getNewValue())) { - restoreHtmlRenderer(src); - } - } - - private static void restoreHtmlRenderer(JXLabel src) { - Object current = src.getClientProperty(BasicHTML.propertyKey); - if (current == null || current instanceof Renderer) { - src.putClientProperty(BasicHTML.propertyKey, src.getClientProperty(oldRendererKey)); - } - } - - private static boolean isHTML(String s) { - return s != null && s.toLowerCase().startsWith(HTML); - } - - public static View createView(JXLabel c) { - BasicEditorKit kit = getFactory(); - float rightIndent = 0; - if (c.getIcon() != null && c.getHorizontalTextPosition() != SwingConstants.CENTER) { - rightIndent = c.getIcon().getIconWidth() + c.getIconTextGap(); - } - Document doc = kit.createDefaultDocument(c.getFont(), c.getForeground(), c.getTextAlignment(), rightIndent); - Reader r = new StringReader(c.getText() == null ? "" : c.getText()); - try { - kit.read(r, doc, 0); - } catch (Throwable e) { - } - ViewFactory f = kit.getViewFactory(); - View hview = f.create(doc.getDefaultRootElement()); - View v = new Renderer(c, f, hview, true); - return v; - } - - public static void updateRenderer(JXLabel c) { - View value = null; - View oldValue = (View) c.getClientProperty(BasicHTML.propertyKey); - if (oldValue == null || oldValue instanceof Renderer) { - value = createView(c); - } - if (value != oldValue && oldValue != null) { - for (int i = 0; i < oldValue.getViewCount(); i++) { - oldValue.getView(i).setParent(null); - } - } - c.putClientProperty(BasicHTML.propertyKey, value); - } - - private static BasicEditorKit getFactory() { - if (basicFactory == null) { - basicViewFactory = new BasicViewFactory(); - basicFactory = new BasicEditorKit(); - } - return basicFactory; - } - - private static class BasicEditorKit extends StyledEditorKit { - public Document createDefaultDocument(Font defaultFont, Color foreground, TextAlignment textAlignment, float rightIndent) { - BasicDocument doc = new BasicDocument(defaultFont, foreground, textAlignment, rightIndent); - doc.setAsynchronousLoadPriority(Integer.MAX_VALUE); - return doc; - } - - @Override - public ViewFactory getViewFactory() { - return basicViewFactory; - } - } - } - - private static class BasicViewFactory implements ViewFactory { - @Override - public View create(Element elem) { - - String kind = elem.getName(); - View view = null; - if (kind == null) { - // default to text display - view = new LabelView(elem); - } else if (kind.equals(AbstractDocument.ContentElementName)) { - view = new LabelView(elem); - } else if (kind.equals(AbstractDocument.ParagraphElementName)) { - view = new ParagraphView(elem); - } else if (kind.equals(AbstractDocument.SectionElementName)) { - view = new BoxView(elem, View.Y_AXIS); - } else if (kind.equals(StyleConstants.ComponentElementName)) { - view = new ComponentView(elem); - } else if (kind.equals(StyleConstants.IconElementName)) { - view = new IconView(elem); - } - return view; - } - } - - static class BasicDocument extends DefaultStyledDocument { - BasicDocument(Font defaultFont, Color foreground, TextAlignment textAlignment, float rightIndent) { - setFontAndColor(defaultFont, foreground); - - MutableAttributeSet attr = new SimpleAttributeSet(); - StyleConstants.setAlignment(attr, textAlignment.getValue()); - getStyle("default").addAttributes(attr); - - attr = new SimpleAttributeSet(); - StyleConstants.setRightIndent(attr, rightIndent); - getStyle("default").addAttributes(attr); - } - - private void setFontAndColor(Font font, Color fg) { - if (fg != null) { - - MutableAttributeSet attr = new SimpleAttributeSet(); - StyleConstants.setForeground(attr, fg); - getStyle("default").addAttributes(attr); - } - - if (font != null) { - MutableAttributeSet attr = new SimpleAttributeSet(); - StyleConstants.setFontFamily(attr, font.getFamily()); - getStyle("default").addAttributes(attr); - - attr = new SimpleAttributeSet(); - StyleConstants.setFontSize(attr, font.getSize()); - getStyle("default").addAttributes(attr); - - attr = new SimpleAttributeSet(); - StyleConstants.setBold(attr, font.isBold()); - getStyle("default").addAttributes(attr); - - attr = new SimpleAttributeSet(); - StyleConstants.setItalic(attr, font.isItalic()); - getStyle("default").addAttributes(attr); - - attr = new SimpleAttributeSet(); - Object underline = font.getAttributes().get(TextAttribute.UNDERLINE); - boolean canUnderline = underline instanceof Integer && (Integer) underline != -1; - StyleConstants.setUnderline(attr, canUnderline); - getStyle("default").addAttributes(attr); - } - - MutableAttributeSet attr = new SimpleAttributeSet(); - StyleConstants.setSpaceAbove(attr, 0f); - getStyle("default").addAttributes(attr); - - } - } - - /** - * Root text view that acts as an renderer. - */ - static class Renderer extends WrappedPlainView { - - JXLabel host; - - boolean invalidated = false; - - private float width; - - private float height; - - Renderer(JXLabel c, ViewFactory f, View v, boolean wordWrap) { - super(null, wordWrap); - factory = f; - view = v; - view.setParent(this); - host = c; - //log.fine("vir: " + host.getVisibleRect()); - int w; - if (host.getVisibleRect().width == 0) { - invalidated = true; - return; - } else { - w = host.getVisibleRect().width; - } - //log.fine("w:" + w); - // initially layout to the preferred size - //setSize(c.getMaxLineSpan() > -1 ? c.getMaxLineSpan() : view.getPreferredSpan(X_AXIS), view.getPreferredSpan(Y_AXIS)); - setSize(c.getMaxLineSpan() > -1 ? c.getMaxLineSpan() : w, host.getVisibleRect().height); - } - - @Override - protected void updateLayout(ElementChange ec, DocumentEvent e, Shape a) { - if ( (a != null)) { - // should damage more intelligently - preferenceChanged(null, true, true); - Container host = getContainer(); - if (host != null) { - host.repaint(); - } - } - } - - @Override - public void preferenceChanged(View child, boolean width, boolean height) { - if (host != null && host.painted) { - host.revalidate(); - host.repaint(); - } - } - - - /** - * Fetches the attributes to use when rendering. At the root level there are no attributes. If an attribute is - * resolved up the view hierarchy this is the end of the line. - */ - @Override - public AttributeSet getAttributes() { - return null; - } - - /** - * Renders the view. - * - * @param g the graphics context - * @param allocation the region to render into - */ - @Override - public void paint(Graphics g, Shape allocation) { - Rectangle alloc = allocation.getBounds(); - //log.fine("aloc:" + alloc + "::" + host.getVisibleRect() + "::" + host.getBounds()); - //view.setSize(alloc.width, alloc.height); - //this.width = alloc.width; - //this.height = alloc.height; - if (g.getClipBounds() == null) { - g.setClip(alloc); - view.paint(g, allocation); - g.setClip(null); - } else { - //g.translate(alloc.x, alloc.y); - view.paint(g, allocation); - //g.translate(-alloc.x, -alloc.y); - } - } - - /** - * Sets the view parent. - * - * @param parent the parent view - */ - @Override - public void setParent(View parent) { - throw new Error("Can't set parent on root view"); - } - - /** - * Returns the number of views in this view. Since this view simply wraps the root of the view hierarchy it has - * exactly one child. - * - * @return the number of views - * @see #getView - */ - @Override - public int getViewCount() { - return 1; - } - - /** - * Gets the n-th view in this container. - * - * @param n the number of the view to get - * @return the view - */ - @Override - public View getView(int n) { - return view; - } - - /** - * Returns the document model underlying the view. - * - * @return the model - */ - @Override - public Document getDocument() { - return view == null ? null : view.getDocument(); - } - - /** - * Sets the view size. - * - * @param width the width - * @param height the height - */ - @Override - public void setSize(float width, float height) { - if (host.maxLineSpan > 0) { - width = Math.min(width, host.maxLineSpan); - } - if (width == this.width && height == this.height) { - return; - } - this.width = (int) width; - this.height = (int) height; - view.setSize(width, height == 0 ? Short.MAX_VALUE : height); - if (this.height == 0) { - this.height = view.getPreferredSpan(View.Y_AXIS); - } - } - - @Override - public float getPreferredSpan(int axis) { - if (axis == X_AXIS) { - //log.fine("inv: " + invalidated + ", w:" + width + ", vw:" + host.getVisibleRect()); - // width currently laid out to - if (invalidated) { - int w = host.getVisibleRect().width; - if (w != 0) { - //log.fine("vrh: " + host.getVisibleRect().height); - invalidated = false; - // JXLabelTest4 works - setSize(w - (host.getOccupiedWidth()), host.getVisibleRect().height); - // JXLabelTest3 works; 20 == width of the parent border!!! ... why should this screw with us? - //setSize(w - (host.getOccupiedWidth()+20), host.getVisibleRect().height); - } - } - return width > 0 ? width : view.getPreferredSpan(axis); - } else { - return view.getPreferredSpan(axis); - } - } - - /** - * Fetches the container hosting the view. This is useful for things like scheduling a repaint, finding out the - * host components font, etc. The default implementation of this is to forward the query to the parent view. - * - * @return the container - */ - @Override - public Container getContainer() { - return host; - } - - /** - * Fetches the factory to be used for building the various view fragments that make up the view that represents - * the model. This is what determines how the model will be represented. This is implemented to fetch the - * factory provided by the associated EditorKit. - * - * @return the factory - */ - @Override - public ViewFactory getViewFactory() { - return factory; - } - - private View view; - - private ViewFactory factory; - - @Override - public int getWidth() { - return (int) width; - } - - @Override - public int getHeight() { - return (int) height; - } - - } - - protected int getOccupiedWidth() { - return occupiedWidth; - } -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/JXList.java b/src/main/java/org/jdesktop/swingx/JXList.java deleted file mode 100644 index e3201d5597..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXList.java +++ /dev/null @@ -1,1568 +0,0 @@ -/* - * $Id: JXList.java 4158 2012-02-03 18:29:40Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.decorator.ComponentAdapter; -import org.jdesktop.swingx.decorator.CompoundHighlighter; -import org.jdesktop.swingx.decorator.Highlighter; -import org.jdesktop.swingx.plaf.LookAndFeelAddons; -import org.jdesktop.swingx.plaf.UIAction; -import org.jdesktop.swingx.plaf.XListAddon; -import org.jdesktop.swingx.plaf.basic.core.BasicXListUI; -import org.jdesktop.swingx.renderer.AbstractRenderer; -import org.jdesktop.swingx.renderer.DefaultListRenderer; -import org.jdesktop.swingx.renderer.StringValue; -import org.jdesktop.swingx.rollover.ListRolloverController; -import org.jdesktop.swingx.rollover.ListRolloverProducer; -import org.jdesktop.swingx.rollover.RolloverProducer; -import org.jdesktop.swingx.rollover.RolloverRenderer; -import org.jdesktop.swingx.search.ListSearchable; -import org.jdesktop.swingx.search.SearchFactory; -import org.jdesktop.swingx.search.Searchable; -import org.jdesktop.swingx.sort.DefaultSortController; -import org.jdesktop.swingx.sort.ListSortController; -import org.jdesktop.swingx.sort.SortController; -import org.jdesktop.swingx.sort.StringValueRegistry; -import org.jdesktop.swingx.table.TableColumnExt; - -import javax.swing.*; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import javax.swing.plaf.ListUI; -import javax.swing.text.Position.Bias; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.util.Arrays; -import java.util.Comparator; -import java.util.Vector; -import java.util.logging.Logger; -import java.util.regex.Pattern; - -/** - * Enhanced List component with support for general SwingX sorting/filtering, - * rendering, highlighting, rollover and search functionality. List specific - * enhancements include ?? PENDING JW ... - * - *

                Sorting and Filtering

                - * JXList supports sorting and filtering. - * - * Changed to use core support. Usage is very similar to J/X/Table. - * It provides api to apply a specific sort order, to toggle the sort order and to reset a sort. - * Sort sequence can be configured by setting a custom comparator. - * - *
                
                - * list.setAutoCreateRowSorter(true);
                - * list.setComparator(myComparator);
                - * list.setSortOrder(SortOrder.DESCENDING);
                - * list.toggleSortOder();
                - * list.resetSortOrder();
                - * 
                - * - *

                - * JXList provides api to access items of the underlying model in view coordinates - * and to convert from/to model coordinates. - * - * Note: JXList needs a specific ui-delegate - BasicXListUI and subclasses - which - * is aware of model vs. view coordiate systems and which controls the synchronization of - * selection/dataModel and sorter state. SwingX comes with a subclass for Synth. - * - *

                Rendering and Highlighting

                - * - * As all SwingX collection views, a JXList is a HighlighterClient (PENDING JW: - * formally define and implement, like in AbstractTestHighlighter), that is it - * provides consistent api to add and remove Highlighters which can visually - * decorate the rendering component. - *

                - * - *

                
                - * 
                - * JXList list = new JXList(new Contributors());
                - * // implement a custom string representation, concated from first-, lastName
                - * StringValue sv = new StringValue() {
                - *     public String getString(Object value) {
                - *        if (value instanceof Contributor) {
                - *           Contributor contributor = (Contributor) value;
                - *           return contributor.lastName() + ", " + contributor.firstName(); 
                - *        }
                - *        return StringValues.TO_STRING(value);
                - *     }
                - * };
                - * list.setCellRenderer(new DefaultListRenderer(sv); 
                - * // highlight condition: gold merits
                - * HighlightPredicate predicate = new HighlightPredicate() {
                - *    public boolean isHighlighted(Component renderer,
                - *                     ComponentAdapter adapter) {
                - *       if (!(value instanceof Contributor)) return false;              
                - *       return ((Contributor) value).hasGold();
                - *    }
                - * };
                - * // highlight with foreground color 
                - * list.addHighlighter(new PainterHighlighter(predicate, goldStarPainter);      
                - * 
                - * 
                - * - * Note: to support the highlighting this implementation wraps the - * ListCellRenderer set by client code with a DelegatingRenderer which applies - * the Highlighter after delegating the default configuration to the wrappee. As - * a side-effect, getCellRenderer does return the wrapper instead of the custom - * renderer. To access the latter, client code must call getWrappedCellRenderer. - *

                - * - *

                Rollover

                - * - * As all SwingX collection views, a JXList supports per-cell rollover. If - * enabled, the component fires rollover events on enter/exit of a cell which by - * default is promoted to the renderer if it implements RolloverRenderer, that - * is simulates live behaviour. The rollover events can be used by client code - * as well, f.i. to decorate the rollover row using a Highlighter. - * - *
                
                - * 
                - * JXList list = new JXList();
                - * list.setRolloverEnabled(true);
                - * list.setCellRenderer(new DefaultListRenderer());
                - * list.addHighlighter(new ColorHighlighter(HighlightPredicate.ROLLOVER_ROW, 
                - *      null, Color.RED);      
                - * 
                - * 
                - * - * - *

                Search

                - * - * As all SwingX collection views, a JXList is searchable. A search action is - * registered in its ActionMap under the key "find". The default behaviour is to - * ask the SearchFactory to open a search component on this component. The - * default keybinding is retrieved from the SearchFactory, typically ctrl-f (or - * cmd-f for Mac). Client code can register custom actions and/or bindings as - * appropriate. - *

                - * - * JXList provides api to vend a renderer-controlled String representation of - * cell content. This allows the Searchable and Highlighters to use WYSIWYM - * (What-You-See-Is-What-You-Match), that is pattern matching against the actual - * string as seen by the user. - * - * - * @author Ramesh Gupta - * @author Jeanette Winzenburg - */ -@JavaBean -public class JXList extends JList { - @SuppressWarnings("all") - private static final Logger LOG = Logger.getLogger(JXList.class.getName()); - - /** - * UI Class ID - */ - public final static String uiClassID = "XListUI"; - - /** - * Registers a Addon for JXList. - */ - static { - LookAndFeelAddons.contribute(new XListAddon()); - } - - - - public static final String EXECUTE_BUTTON_ACTIONCOMMAND = "executeButtonAction"; - - /** - * The pipeline holding the highlighters. - */ - protected CompoundHighlighter compoundHighlighter; - - /** listening to changeEvents from compoundHighlighter. */ - private ChangeListener highlighterChangeListener; - - /** The ComponentAdapter for model data access. */ - protected ComponentAdapter dataAdapter; - - /** - * Mouse/Motion/Listener keeping track of mouse moved in cell coordinates. - */ - private RolloverProducer rolloverProducer; - - /** - * RolloverController: listens to cell over events and repaints - * entered/exited rows. - */ - private ListRolloverController linkController; - - /** A wrapper around the default renderer enabling decoration. */ - private transient DelegatingRenderer delegatingRenderer; - - private Searchable searchable; - - private Comparator comparator; - - private boolean autoCreateRowSorter; - - private RowSorter rowSorter; - - private boolean sortable; - - private boolean sortsOnUpdates; - - private StringValueRegistry stringValueRegistry; - - private SortOrder[] sortOrderCycle; - - /** - * Constructs a JXList with an empty model and filters disabled. - * - */ - public JXList() { - this(false); - } - - /** - * Constructs a JXList that displays the elements in the - * specified, non-null model and automatic creation of a RowSorter disabled. - * - * @param dataModel the data model for this list - * @exception IllegalArgumentException if dataModel - * is null - */ - public JXList(ListModel dataModel) { - this(dataModel, false); - } - - /** - * Constructs a JXList that displays the elements in - * the specified array and automatic creation of a RowSorter disabled. - * - * @param listData the array of Objects to be loaded into the data model - * @throws IllegalArgumentException if listData - * is null - */ - public JXList(Object[] listData) { - this(listData, false); - } - - /** - * Constructs a JXList that displays the elements in - * the specified Vector and automatic creation of a RowSorter disabled. - * - * @param listData the Vector to be loaded into the - * data model - * @throws IllegalArgumentException if listData - * is null - */ - public JXList(Vector listData) { - this(listData, false); - } - - - /** - * Constructs a JXList with an empty model and - * automatic creation of a RowSorter as given. - * - * @param autoCreateRowSorter boolean to determine if - * a RowSorter should be created automatically. - */ - public JXList(boolean autoCreateRowSorter) { - init(autoCreateRowSorter); - } - - /** - * Constructs a JXList with the specified model and - * automatic creation of a RowSorter as given. - * - * @param dataModel the data model for this list - * @param autoCreateRowSorter boolean to determine if - * a RowSorter should be created automatically. - * @throws IllegalArgumentException if dataModel - * is null - */ - public JXList(ListModel dataModel, boolean autoCreateRowSorter) { - super(dataModel); - init(autoCreateRowSorter); - } - - /** - * Constructs a JXList that displays the elements in - * the specified array and automatic creation of a RowSorter as given. - * - * @param listData the array of Objects to be loaded into the data model - * @param autoCreateRowSorter boolean to determine if - * a RowSorter should be created automatically. - * @throws IllegalArgumentException if listData - * is null - */ - public JXList(Object[] listData, boolean autoCreateRowSorter) { - super(listData); - if (listData == null) - throw new IllegalArgumentException("listData must not be null"); - init(autoCreateRowSorter); - } - - /** - * Constructs a JXList that displays the elements in - * the specified Vector and filtersEnabled property. - * - * @param listData the Vector to be loaded into the - * data model - * @param autoCreateRowSorter boolean to determine if - * a RowSorter should be created automatically. - * @throws IllegalArgumentException if listData is null - */ - public JXList(Vector listData, boolean autoCreateRowSorter) { - super(listData); - if (listData == null) - throw new IllegalArgumentException("listData must not be null"); - init(autoCreateRowSorter); - } - - - private void init(boolean autoCreateRowSorter) { - sortOrderCycle = DefaultSortController.getDefaultSortOrderCycle(); - setSortable(true); - setSortsOnUpdates(true); - setAutoCreateRowSorter(autoCreateRowSorter); - Action findAction = createFindAction(); - getActionMap().put("find", findAction); - - KeyStroke findStroke = SearchFactory.getInstance().getSearchAccelerator(); - getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(findStroke, "find"); - } - - private Action createFindAction() { - return new UIAction("find") { - @Override - public void actionPerformed(ActionEvent e) { - doFind(); - } - }; - } - - /** - * Starts a search on this List's visible items. This implementation asks the - * SearchFactory to open a find widget on itself. - */ - protected void doFind() { - SearchFactory.getInstance().showFindInput(this, getSearchable()); - } - - /** - * Returns a Searchable for this component, guaranteed to be not null. This - * implementation lazily creates a ListSearchable if necessary. - * - * @return a not-null Searchable for this list. - * - * @see #setSearchable(Searchable) - * @see ListSearchable - */ - public Searchable getSearchable() { - if (searchable == null) { - searchable = new ListSearchable(this); - } - return searchable; - } - - /** - * Sets the Searchable for this component. If null, a default - * Searchable will be created and used. - * - * @param searchable the Searchable to use for this component, may be null to indicate - * using the list's default searchable. - * @see #getSearchable() - */ - public void setSearchable(Searchable searchable) { - this.searchable = searchable; - } - - - /** - * {@inheritDoc}

                - * - * Overridden to cope with sorting/filtering, taking over completely. - */ - @Override - public int getNextMatch(String prefix, int startIndex, Bias bias) { - Pattern pattern = Pattern.compile("^" + prefix, Pattern.CASE_INSENSITIVE); - return getSearchable().search(pattern, startIndex, bias ==Bias.Backward); - } -//--------------------- Rollover support - - /** - * Sets the property to enable/disable rollover support. If enabled, the list - * fires property changes on per-cell mouse rollover state, i.e. - * when the mouse enters/leaves a list cell.

                - * - * This can be enabled to show "live" rollover behaviour, f.i. the cursor over a cell - * rendered by a JXHyperlink.

                - * - * Default value is disabled. - * - * @param rolloverEnabled a boolean indicating whether or not the rollover - * functionality should be enabled. - * - * @see #isRolloverEnabled() - * @see #getLinkController() - * @see #createRolloverProducer() - * @see RolloverRenderer - * - */ - public void setRolloverEnabled(boolean rolloverEnabled) { - boolean old = isRolloverEnabled(); - if (rolloverEnabled == old) - return; - if (rolloverEnabled) { - rolloverProducer = createRolloverProducer(); - rolloverProducer.install(this); - getLinkController().install(this); - } else { - rolloverProducer.release(this); - rolloverProducer = null; - getLinkController().release(); - } - firePropertyChange("rolloverEnabled", old, isRolloverEnabled()); - } - - /** - * Returns a boolean indicating whether or not rollover support is enabled. - * - * @return a boolean indicating whether or not rollover support is enabled. - * - * @see #setRolloverEnabled(boolean) - */ - public boolean isRolloverEnabled() { - return rolloverProducer != null; - } - - /** - * Returns the RolloverController for this component. Lazyly creates the - * controller if necessary, that is the return value is guaranteed to be - * not null.

                - * - * PENDING JW: rename to getRolloverController - * - * @return the RolloverController for this tree, guaranteed to be not null. - * - * @see #setRolloverEnabled(boolean) - * @see #createLinkController() - * @see org.jdesktop.swingx.rollover.RolloverController - */ - protected ListRolloverController getLinkController() { - if (linkController == null) { - linkController = createLinkController(); - } - return linkController; - } - - /** - * Creates and returns a RolloverController appropriate for this component. - * - * @return a RolloverController appropriate for this component. - * - * @see #getLinkController() - * @see org.jdesktop.swingx.rollover.RolloverController - */ - protected ListRolloverController createLinkController() { - return new ListRolloverController(); - } - - - /** - * Creates and returns the RolloverProducer to use with this tree. - *

                - * - * @return RolloverProducer to use with this tree - * - * @see #setRolloverEnabled(boolean) - */ - protected RolloverProducer createRolloverProducer() { - return new ListRolloverProducer(); - } - - //--------------------- public sort api - - /** - * Returns {@code true} if whenever the model changes, a new - * {@code RowSorter} should be created and installed - * as the table's sorter; otherwise, returns {@code false}. - * - * @return true if a {@code RowSorter} should be created when - * the model changes - * @since 1.6 - */ - public boolean getAutoCreateRowSorter() { - return autoCreateRowSorter; - } - - /** - * Specifies whether a {@code RowSorter} should be created for the - * list whenever its model changes. - *

                - * When {@code setAutoCreateRowSorter(true)} is invoked, a {@code - * RowSorter} is immediately created and installed on the - * list. While the {@code autoCreateRowSorter} property remains - * {@code true}, every time the model is changed, a new {@code - * RowSorter} is created and set as the list's row sorter.

                - * - * The default value is false. - * - * @param autoCreateRowSorter whether or not a {@code RowSorter} - * should be automatically created - * @beaninfo - * bound: true - * preferred: true - * description: Whether or not to turn on sorting by default. - */ - public void setAutoCreateRowSorter(boolean autoCreateRowSorter) { - if (getAutoCreateRowSorter() == autoCreateRowSorter) return; - boolean oldValue = getAutoCreateRowSorter(); - this.autoCreateRowSorter = autoCreateRowSorter; - if (autoCreateRowSorter) { - setRowSorter(createDefaultRowSorter()); - } - firePropertyChange("autoCreateRowSorter", oldValue, - getAutoCreateRowSorter()); - } - - /** - * Creates and returns the default RowSorter. Note that this is already - * configured to the current ListModel. - * - * PENDING JW: review method signature - better expose the need for the - * model by adding a parameter? - * - * @return the default RowSorter. - */ - protected RowSorter createDefaultRowSorter() { - return new ListSortController(getModel()); - } - /** - * Returns the object responsible for sorting. - * - * @return the object responsible for sorting - * @since 1.6 - */ - public RowSorter getRowSorter() { - return rowSorter; - } - - /** - * Sets the RowSorter. RowSorter is used - * to provide sorting and filtering to a JXList. - *

                - * This method clears the selection and resets any variable row heights. - *

                - * If the underlying model of the RowSorter differs from - * that of this JXList undefined behavior will result. - * - * @param sorter the RowSorter; null turns - * sorting off - */ - public void setRowSorter(RowSorter sorter) { - RowSorter oldRowSorter = getRowSorter(); - this.rowSorter = sorter; - configureSorterProperties(); - firePropertyChange("rowSorter", oldRowSorter, sorter); - } - - /** - * Propagates sort-related properties from table/columns to the sorter if it - * is of type SortController, does nothing otherwise. - * - */ - protected void configureSorterProperties() { - if (!getControlsSorterProperties()) return; - // configure from table properties - getSortController().setSortable(sortable); - getSortController().setSortsOnUpdates(sortsOnUpdates); - getSortController().setComparator(0, comparator); - getSortController().setSortOrderCycle(getSortOrderCycle()); - getSortController().setStringValueProvider(getStringValueRegistry()); - } - - /** - * Sets "sortable" property indicating whether or not this list - * isSortable. - * - * Note: as of post-1.0 this property is propagated to the SortController. - * Whether or not a change triggers a re-sort is up to either the concrete controller - * implementation (the default doesn't) or client code. This behaviour is - * different from old SwingX style sorting. - * - * @see TableColumnExt#isSortable() - * @param sortable boolean indicating whether or not this table supports - * sortable columns - */ - public void setSortable(boolean sortable) { - boolean old = isSortable(); - this.sortable = sortable; - if (getControlsSorterProperties()) { - getSortController().setSortable(sortable); - } - firePropertyChange("sortable", old, isSortable()); - } - - /** - * Returns the table's sortable property.

                - * - * @return true if the table is sortable. - */ - public boolean isSortable() { - return sortable; - } - - /** - * If true, specifies that a sort should happen when the underlying - * model is updated (rowsUpdated is invoked). For - * example, if this is true and the user edits an entry the - * location of that item in the view may change. The default is - * true. - * - * @param sortsOnUpdates whether or not to sort on update events - */ - public void setSortsOnUpdates(boolean sortsOnUpdates) { - boolean old = getSortsOnUpdates(); - this.sortsOnUpdates = sortsOnUpdates; - if (getControlsSorterProperties()) { - getSortController().setSortsOnUpdates(sortsOnUpdates); - } - firePropertyChange("sortsOnUpdates", old, getSortsOnUpdates()); - } - - /** - * Returns true if a sort should happen when the underlying - * model is updated; otherwise, returns false. - * - * @return whether or not to sort when the model is updated - */ - public boolean getSortsOnUpdates() { - return sortsOnUpdates; - } - - /** - * Sets the sortorder cycle used when toggle sorting this table's columns. - * This property is propagated to the SortController - * if controlsSorterProperties is true. - * - * @param cycle the sequence of zero or more not-null SortOrders to cycle through. - * @throws NullPointerException if the array or any of its elements are null - * - */ - public void setSortOrderCycle(SortOrder... cycle) { - SortOrder[] old = getSortOrderCycle(); - if (getControlsSorterProperties()) { - getSortController().setSortOrderCycle(cycle); - } - this.sortOrderCycle = Arrays.copyOf(cycle, cycle.length); - firePropertyChange("sortOrderCycle", old, getSortOrderCycle()); - } - - /** - * Returns the sortOrder cycle used when toggle sorting this table's columns, guaranteed - * to be not null. - * - * @return the sort order cycle used in toggle sort, not null - */ - public SortOrder[] getSortOrderCycle() { - return Arrays.copyOf(sortOrderCycle, sortOrderCycle.length); - } - - /** - * - * @return the comparator used. - * @see #setComparator(Comparator) - */ - public Comparator getComparator() { - return comparator; - } - - /** - * Sets the comparator to use for sorting.

                - * - * Note: as of post-1.0 the property is propagated to the SortController, - * if available. - * Whether or not a change triggers a re-sort is up to either the concrete controller - * implementation (the default doesn't) or client code. This behaviour is - * different from old SwingX style sorting. - * - * @param comparator the comparator to use. - */ - public void setComparator(Comparator comparator) { - Comparator old = getComparator(); - this.comparator = comparator; - updateSortAfterComparatorChange(); - firePropertyChange("comparator", old, getComparator()); - } - - /** - * Updates the SortController's comparator, if available. Does nothing otherwise. - * - */ - protected void updateSortAfterComparatorChange() { - if (getControlsSorterProperties()) { - getSortController().setComparator(0, getComparator()); - } - } - -//------------------------- sort: do sort/filter - - /** - * Sets the filter to the sorter, if available and of type SortController. - * Does nothing otherwise. - *

                - * - * @param filter the filter used to determine what entries should be - * included - */ - @SuppressWarnings("unchecked") - public void setRowFilter(RowFilter filter) { - if (hasSortController()) { - // all fine, because R is a ListModel (R extends ListModel) - SortController controller = (SortController) getSortController(); - controller.setRowFilter(filter); - } - } - - /** - * Returns the filter of the sorter, if available and of type SortController. - * Returns null otherwise.

                - * - * PENDING JW: generics? had to remove return type from getSortController to - * make this compilable, so probably wrong. - * - * @return the filter used in the sorter. - */ - @SuppressWarnings("unchecked") - public RowFilter getRowFilter() { - return hasSortController() ? getSortController().getRowFilter() : null; - } - - /** - * Resets sorting of all columns. - * Delegates to the SortController if available, or does nothing if not.

                - * - * PENDING JW: method name - consistent in SortController and here. - * - */ - public void resetSortOrder() { - if (hasSortController()) - getSortController().resetSortOrders(); - } - - /** - * - * Toggles the sort order of the list. - * Delegates to the SortController if available, or does nothing if not.

                - * - *

                - * The exact behaviour is defined by the SortController's toggleSortOrder - * implementation. Typically a unsorted list is sorted in ascending order, - * a sorted list's order is reversed. - *

                - * - * - */ - public void toggleSortOrder() { - if (hasSortController()) - getSortController().toggleSortOrder(0); - } - - /** - * Sorts the list using SortOrder. - * Delegates to the SortController if available, or does nothing if not.

                - * - * @param sortOrder the sort order to use. - * - */ - public void setSortOrder(SortOrder sortOrder) { - if (hasSortController()) - getSortController().setSortOrder(0, sortOrder); - } - - - /** - * Returns the SortOrder. - * Delegates to the SortController if available, or returns SortOrder.UNSORTED if not.

                - * - * @return the current SortOrder - */ - public SortOrder getSortOrder() { - if (hasSortController()) - return getSortController().getSortOrder(0); - return SortOrder.UNSORTED; - } - - - /** - * Returns the currently active SortController. May be null if RowSorter - * is null or not of type SortController.

                - * - * PENDING JW: swaying about hiding or not - currently the only way to - * make the view not configure a RowSorter of type SortController is to - * let this return null. - * - * @return the currently active SortController may be null - */ - @SuppressWarnings("unchecked") - protected SortController getSortController() { - if (hasSortController()) { - // JW: the RowSorter is always of type - // so the unchecked cast is safe - return (SortController) getRowSorter(); - } - return null; - } - - /** - * Returns a boolean indicating whether the table has a SortController. - * If true, the call to getSortController is guaranteed to return a not-null - * value. - * - * @return a boolean indicating whether the table has a SortController. - * - * @see #getSortController() - */ - protected boolean hasSortController() { - return getRowSorter() instanceof SortController; - } - - /** - * Returns a boolean indicating whether the table configures the sorter's - * properties. If true, guaranteed that table's and the columns' sort related - * properties are propagated to the sorter. If false, guaranteed to not - * touch the sorter's configuration.

                - * - * This implementation returns true if the sorter is of type SortController. - * - * Note: the synchronization is unidirection from the table to the sorter. - * Changing the sorter under the table's feet might lead to undefined - * behaviour. - * - * @return a boolean indicating whether the table configurers the sorter's - * properties. - */ - protected boolean getControlsSorterProperties() { - return hasSortController() && getAutoCreateRowSorter(); - } - - // ---------------------------- filters - - /** - * Returns the element at the given index. The index is in view coordinates - * which might differ from model coordinates if filtering is enabled and - * filters/sorters are active. - * - * @param viewIndex the index in view coordinates - * @return the element at the index - * @throws IndexOutOfBoundsException if viewIndex < 0 or viewIndex >= - * getElementCount() - */ - public Object getElementAt(int viewIndex) { - return getModel().getElementAt(convertIndexToModel(viewIndex)); - } - - /** - * Returns the value for the smallest selected cell index; - * the selected value when only a single item is selected in the - * list. When multiple items are selected, it is simply the value for the - * smallest selected index. Returns {@code null} if there is no selection. - *

                - * This is a convenience method that simply returns the model value for - * {@code getMinSelectionIndex}, taking into account sorting and filtering. - * - * @return the first selected value - * @see #getMinSelectionIndex - * @see #getModel - * @see #addListSelectionListener - */ - @Override - public Object getSelectedValue() { - int i = getSelectedIndex(); - return (i == -1) ? null : getElementAt(i); - } - - /** - * Selects the specified object from the list, taking into account - * sorting and filtering. - * - * @param anObject the object to select - * @param shouldScroll {@code true} if the list should scroll to display - * the selected object, if one exists; otherwise {@code false} - */ - @Override - public void setSelectedValue(Object anObject,boolean shouldScroll) { - // Note: this method is a copy of JList.setSelectedValue, - // including comments. It simply usues getElementCount() and getElementAt() - // instead of the model. - if(anObject == null) - setSelectedIndex(-1); - else if(!anObject.equals(getSelectedValue())) { - int i,c; - for(i=0,c=getElementCount();i= getElementCount() - */ - public int convertIndexToModel(int viewIndex) { - return getRowSorter() != null ? - getRowSorter().convertRowIndexToModel(viewIndex):viewIndex; - } - - /** - * Convert index from model coordinates to view coordinates accounting - * for the presence of sorters and filters. - * - * @param modelIndex index in model coordinates - * @return index in view coordinates if the model index maps to a view coordinate - * or -1 if not contained in the view. - * - */ - public int convertIndexToView(int modelIndex) { - return getRowSorter() != null - ? getRowSorter().convertRowIndexToView(modelIndex) : modelIndex; - } - - /** - * {@inheritDoc}

                - * - * Sets the underlying data model. Note that if isFilterEnabled you must - * call getWrappedModel to access the model given here. In this case - * getModel returns a wrapper around the data! - * - * @param model the data model for this list. - * - */ - @Override - public void setModel(ListModel model) { - super.setModel(model); - if (getAutoCreateRowSorter()) { - setRowSorter(createDefaultRowSorter()); - } - } - - - // ---------------------------- uniform data model - - /** - * @return the unconfigured ComponentAdapter. - */ - protected ComponentAdapter getComponentAdapter() { - if (dataAdapter == null) { - dataAdapter = new ListAdapter(this); - } - return dataAdapter; - } - - /** - * Convenience to access a configured ComponentAdapter. - * Note: the column index of the configured adapter is always 0. - * - * @param index the row index in view coordinates, must be valid. - * @return the configured ComponentAdapter. - */ - protected ComponentAdapter getComponentAdapter(int index) { - ComponentAdapter adapter = getComponentAdapter(); - adapter.column = 0; - adapter.row = index; - return adapter; - } - - /** - * A component adapter targeted at a JXList. - */ - protected static class ListAdapter extends ComponentAdapter { - private final JXList list; - - /** - * Constructs a ListAdapter for the specified target - * JXList. - * - * @param component the target list. - */ - public ListAdapter(JXList component) { - super(component); - list = component; - } - - /** - * Typesafe accessor for the target component. - * - * @return the target component as a {@link JXList} - */ - public JXList getList() { - return list; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean hasFocus() { - /** TODO: Think through printing implications */ - return list.isFocusOwner() && (row == list.getLeadSelectionIndex()); - } - - /** - * {@inheritDoc} - */ - @Override - public int getRowCount() { - return list.getModel().getSize(); - } - - /** - * {@inheritDoc} - */ - @Override - public Object getValueAt(int row, int column) { - return list.getModel().getElementAt(row); - } - - /** - * {@inheritDoc} - * This is implemented to query the table's StringValueRegistry for an appropriate - * StringValue and use that for getting the string representation. - */ - @Override - public String getStringAt(int row, int column) { - StringValue sv = list.getStringValueRegistry().getStringValue(row, column); - return sv.getString(getValueAt(row, column)); - } - - /** - * {@inheritDoc} - */ - @Override - public Rectangle getCellBounds() { - return list.getCellBounds(row, row); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isCellEditable(int row, int column) { - return false; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isEditable() { - return false; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isSelected() { - /** TODO: Think through printing implications */ - return list.isSelectedIndex(row); - } - - /** - * {@inheritDoc} - */ - @Override - public int convertRowIndexToView(int rowModelIndex) { - return list.convertIndexToView(rowModelIndex); - } - - /** - * {@inheritDoc} - */ - @Override - public int convertRowIndexToModel(int rowViewIndex) { - return list.convertIndexToModel(rowViewIndex); - } - } - - // ------------------------------ renderers - - - - /** - * Sets the Highlighters to the table, replacing any old settings. - * None of the given Highlighters must be null.

                - * - * This is a bound property.

                - * - * Note: as of version #1.257 the null constraint is enforced strictly. To remove - * all highlighters use this method without param. - * - * @param highlighters zero or more not null highlighters to use for renderer decoration. - * @throws NullPointerException if array is null or array contains null values. - * - * @see #getHighlighters() - * @see #addHighlighter(Highlighter) - * @see #removeHighlighter(Highlighter) - * - */ - public void setHighlighters(Highlighter... highlighters) { - Highlighter[] old = getHighlighters(); - getCompoundHighlighter().setHighlighters(highlighters); - firePropertyChange("highlighters", old, getHighlighters()); - } - - /** - * Returns the Highlighters used by this table. - * Maybe empty, but guarantees to be never null. - * - * @return the Highlighters used by this table, guaranteed to never null. - * @see #setHighlighters(Highlighter[]) - */ - public Highlighter[] getHighlighters() { - return getCompoundHighlighter().getHighlighters(); - } - /** - * Appends a Highlighter to the end of the list of used - * Highlighters. The argument must not be null. - *

                - * - * @param highlighter the Highlighter to add, must not be null. - * @throws NullPointerException if Highlighter is null. - * - * @see #removeHighlighter(Highlighter) - * @see #setHighlighters(Highlighter[]) - */ - public void addHighlighter(Highlighter highlighter) { - Highlighter[] old = getHighlighters(); - getCompoundHighlighter().addHighlighter(highlighter); - firePropertyChange("highlighters", old, getHighlighters()); - } - - /** - * Removes the given Highlighter.

                - * - * Does nothing if the Highlighter is not contained. - * - * @param highlighter the Highlighter to remove. - * @see #addHighlighter(Highlighter) - * @see #setHighlighters(Highlighter...) - */ - public void removeHighlighter(Highlighter highlighter) { - Highlighter[] old = getHighlighters(); - getCompoundHighlighter().removeHighlighter(highlighter); - firePropertyChange("highlighters", old, getHighlighters()); - } - - /** - * Returns the CompoundHighlighter assigned to the table, null if none. - * PENDING: open up for subclasses again?. - * - * @return the CompoundHighlighter assigned to the table. - */ - protected CompoundHighlighter getCompoundHighlighter() { - if (compoundHighlighter == null) { - compoundHighlighter = new CompoundHighlighter(); - compoundHighlighter.addChangeListener(getHighlighterChangeListener()); - } - return compoundHighlighter; - } - - /** - * Returns the ChangeListener to use with highlighters. Lazily - * creates the listener. - * - * @return the ChangeListener for observing changes of highlighters, - * guaranteed to be not-null - */ - protected ChangeListener getHighlighterChangeListener() { - if (highlighterChangeListener == null) { - highlighterChangeListener = createHighlighterChangeListener(); - } - return highlighterChangeListener; - } - - /** - * Creates and returns the ChangeListener observing Highlighters. - *

                - * Here: repaints the table on receiving a stateChanged. - * - * @return the ChangeListener defining the reaction to changes of - * highlighters. - */ - protected ChangeListener createHighlighterChangeListener() { - return new ChangeListener() { - @Override - public void stateChanged(ChangeEvent e) { - repaint(); - } - }; - } - - /** - * Returns the StringValueRegistry which defines the string representation for - * each cells. This is strictly for internal use by the table, which has the - * responsibility to keep in synch with registered renderers.

                - * - * Currently exposed for testing reasons, client code is recommended to not use nor override. - * - * @return the current string value registry - */ - protected StringValueRegistry getStringValueRegistry() { - if (stringValueRegistry == null) { - stringValueRegistry = createDefaultStringValueRegistry(); - } - return stringValueRegistry; - } - - /** - * Creates and returns the default registry for StringValues.

                - * - * @return the default registry for StringValues. - */ - protected StringValueRegistry createDefaultStringValueRegistry() { - return new StringValueRegistry(); - } - - - - /** - * Returns the string representation of the cell value at the given position. - * - * @param row the row index of the cell in view coordinates - * @return the string representation of the cell value as it will appear in the - * table. - */ - public String getStringAt(int row) { - // changed implementation to use StringValueRegistry - StringValue stringValue = getStringValueRegistry().getStringValue( - convertIndexToModel(row), 0); - return stringValue.getString(getElementAt(row)); - } - - private DelegatingRenderer getDelegatingRenderer() { - if (delegatingRenderer == null) { - // only called once... to get hold of the default? - delegatingRenderer = new DelegatingRenderer(); - } - return delegatingRenderer; - } - - /** - * Creates and returns the default cell renderer to use. Subclasses - * may override to use a different type. Here: returns a DefaultListRenderer. - * - * @return the default cell renderer to use with this list. - */ - protected ListCellRenderer createDefaultCellRenderer() { - return new DefaultListRenderer(); - } - - /** - * {@inheritDoc}

                - * - * Overridden to return the delegating renderer which is wrapped around the - * original to support highlighting. The returned renderer is of type - * DelegatingRenderer and guaranteed to not-null

                - * - * @see #setCellRenderer(ListCellRenderer) - * @see DelegatingRenderer - */ - @Override - public ListCellRenderer getCellRenderer() { - return getDelegatingRenderer(); - } - - /** - * Returns the renderer installed by client code or the default if none has - * been set. - * - * @return the wrapped renderer. - * @see #setCellRenderer(ListCellRenderer) - */ - public ListCellRenderer getWrappedCellRenderer() { - return getDelegatingRenderer().getDelegateRenderer(); - } - - /** - * {@inheritDoc}

                - * - * Overridden to wrap the given renderer in a DelegatingRenderer to support - * highlighting.

                - * - * Note: the wrapping implies that the renderer returned from the getCellRenderer - * is not the renderer as given here, but the wrapper. To access the original, - * use getWrappedCellRenderer. - * - * @see #getWrappedCellRenderer() - * @see #getCellRenderer() - * - */ - @Override - public void setCellRenderer(ListCellRenderer renderer) { - // PENDING JW: super fires for very first setting - // as defaults are automagically set (by delegatingRenderer - // using this list's factory method) there is no - // easy way to _not_ force, this isn't working - // but then ... it's only the very first time around. - // Safe enough to wait for complaints ;-) - boolean forceFire = (delegatingRenderer != null) ; - // JW: Pending - probably fires propertyChangeEvent with wrong newValue? - // how about fixedCellWidths? - // need to test!! - getDelegatingRenderer().setDelegateRenderer(renderer); - getStringValueRegistry().setStringValue( - renderer instanceof StringValue ? (StringValue) renderer: null, - 0); - super.setCellRenderer(delegatingRenderer); - if (forceFire) - firePropertyChange("cellRenderer", null, delegatingRenderer); - } - - /** - * A decorator for the original ListCellRenderer. Needed to hook highlighters - * after messaging the delegate.

                - * - * PENDING JW: formally implement UIDependent? - */ - public class DelegatingRenderer implements ListCellRenderer, RolloverRenderer { - /** the delegate. */ - private ListCellRenderer delegateRenderer; - - /** - * Instantiates a DelegatingRenderer with list's default renderer as delegate. - */ - public DelegatingRenderer() { - this(null); - } - - /** - * Instantiates a DelegatingRenderer with the given delegate. If the - * delegate is null, the default is created via the list's factory method. - * - * @param delegate the delegate to use, if null the list's default is - * created and used. - */ - public DelegatingRenderer(ListCellRenderer delegate) { - setDelegateRenderer(delegate); - } - - /** - * Sets the delegate. If the - * delegate is null, the default is created via the list's factory method. - * - * @param delegate the delegate to use, if null the list's default is - * created and used. - */ - public void setDelegateRenderer(ListCellRenderer delegate) { - if (delegate == null) { - delegate = createDefaultCellRenderer(); - } - delegateRenderer = delegate; - } - - /** - * Returns the delegate. - * - * @return the delegate renderer used by this renderer, guaranteed to - * not-null. - */ - public ListCellRenderer getDelegateRenderer() { - return delegateRenderer; - } - - /** - * Updates the ui of the delegate. - */ - public void updateUI() { - updateRendererUI(delegateRenderer); - } - - /** - * - * @param renderer the renderer to update the ui of. - */ - private void updateRendererUI(ListCellRenderer renderer) { - if (renderer == null) return; - Component comp = null; - if (renderer instanceof AbstractRenderer) { - comp = ((AbstractRenderer) renderer).getComponentProvider().getRendererComponent(null); - } else if (renderer instanceof Component) { - comp = (Component) renderer; - } else { - try { - comp = renderer.getListCellRendererComponent( - JXList.this, null, -1, false, false); - } catch (Exception e) { - // nothing to do - renderer barked on off-range row - } - } - if (comp != null) { - SwingUtilities.updateComponentTreeUI(comp); - } - - } - - // --------- implement ListCellRenderer - /** - * {@inheritDoc}

                - * - * Overridden to apply the highlighters, if any, after calling the delegate. - * The decorators are not applied if the row is invalid. - */ - @Override - public Component getListCellRendererComponent(JList list, Object value, - int index, boolean isSelected, boolean cellHasFocus) { - Component comp = delegateRenderer.getListCellRendererComponent(list, value, index, - isSelected, cellHasFocus); - if ((compoundHighlighter != null) && (index >= 0) && (index < getElementCount())) { - comp = compoundHighlighter.highlight(comp, getComponentAdapter(index)); - } - return comp; - } - - - // implement RolloverRenderer - - /** - * {@inheritDoc} - * - */ - @Override - public boolean isEnabled() { - return (delegateRenderer instanceof RolloverRenderer) && - ((RolloverRenderer) delegateRenderer).isEnabled(); - } - - /** - * {@inheritDoc} - */ - @Override - public void doClick() { - if (isEnabled()) { - ((RolloverRenderer) delegateRenderer).doClick(); - } - } - - } - - /** - * Invalidates cell size caching in the ui delegate. May do nothing if there's no - * safe (i.e. without reflection) way to message the delegate.

                - * - * This implementation calls the corresponding method on BasicXListUI if available, - * does nothing otherwise. - * - */ - public void invalidateCellSizeCache() { - if (getUI() instanceof BasicXListUI) { - ((BasicXListUI) getUI()).invalidateCellSizeCache(); - } - } - - // --------------------------- updateUI - - - /** - * {@inheritDoc}

                - * - * Overridden to update renderer and Highlighters. - */ - @Override - public void updateUI() { - // PENDING JW: temporary during dev to quickly switch between default and custom ui - if (getUIClassID() == super.getUIClassID()) { - super.updateUI(); - } else { - setUI((ListUI) LookAndFeelAddons.getUI(this, ListUI.class)); - } - updateRendererUI(); - updateHighlighterUI(); - } - - @Override - public String getUIClassID() { - // PENDING JW: temporary during dev to quickly switch between default and custom ui -// return super.getUIClassID(); - return uiClassID; - } - - private void updateRendererUI() { - if (delegatingRenderer != null) { - delegatingRenderer.updateUI(); - } else { - ListCellRenderer renderer = getCellRenderer(); - if (renderer instanceof Component) { - SwingUtilities.updateComponentTreeUI((Component) renderer); - } - } - } - - /** - * Updates highlighter after updateUI changes. - * - * @see org.jdesktop.swingx.plaf.UIDependent - */ - protected void updateHighlighterUI() { - if (compoundHighlighter == null) return; - compoundHighlighter.updateUI(); - } - -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/JXMonthView.java b/src/main/java/org/jdesktop/swingx/JXMonthView.java deleted file mode 100644 index f81263f9c2..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXMonthView.java +++ /dev/null @@ -1,1889 +0,0 @@ -/* - * $Id: JXMonthView.java 4147 2012-02-01 17:13:24Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.calendar.CalendarUtils; -import org.jdesktop.swingx.calendar.DateSelectionModel; -import org.jdesktop.swingx.calendar.DateSelectionModel.SelectionMode; -import org.jdesktop.swingx.calendar.DaySelectionModel; -import org.jdesktop.swingx.event.DateSelectionEvent; -import org.jdesktop.swingx.event.DateSelectionEvent.EventType; -import org.jdesktop.swingx.event.DateSelectionListener; -import org.jdesktop.swingx.event.EventListenerMap; -import org.jdesktop.swingx.plaf.LookAndFeelAddons; -import org.jdesktop.swingx.plaf.MonthViewAddon; -import org.jdesktop.swingx.plaf.MonthViewUI; -import org.jdesktop.swingx.util.Contract; - -import javax.swing.Timer; -import javax.swing.*; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.*; -import java.util.logging.Logger; - - -/** - * Component that displays a month calendar which can be used to select a day - * or range of days. By default the JXMonthView will display a - * single calendar using the current month and year, using - * Calendar.SUNDAY as the first day of the week. - *

                - * The JXMonthView can be configured to display more than one - * calendar at a time by calling - * setPreferredCalCols/setPreferredCalRows. These - * methods will set the preferred number of calendars to use in each - * column/row. As these values change, the Dimension returned - * from getMinimumSize and getPreferredSize will - * be updated. The following example shows how to create a 2x2 view which is - * contained within a JFrame: - *

                - *     JXMonthView monthView = new JXMonthView();
                - *     monthView.setPreferredCols(2);
                - *     monthView.setPreferredRows(2);
                - *
                - *     JFrame frame = new JFrame();
                - *     frame.getContentPane().add(monthView);
                - *     frame.pack();
                - *     frame.setVisible(true);
                - * 
                - *

                - * JXMonthView can be further configured to allow any day of the - * week to be considered the first day of the week. Character - * representation of those days may also be set by providing an array of - * strings. - *

                - *    monthView.setFirstDayOfWeek(Calendar.MONDAY);
                - *    monthView.setDaysOfTheWeek(
                - *            new String[]{"S", "M", "T", "W", "Th", "F", "S"});
                - * 
                - *

                - * This component supports flagging days. These flagged days are displayed - * in a bold font. This can be used to inform the user of such things as - * scheduled appointment. - *

                
                - *    // Create some dates that we want to flag as being important.
                - *    Calendar cal1 = Calendar.getInstance();
                - *    cal1.set(2004, 1, 1);
                - *    Calendar cal2 = Calendar.getInstance();
                - *    cal2.set(2004, 1, 5);
                - *
                - *    monthView.setFlaggedDates(cal1.getTime(), cal2.getTime(), new Date());
                - * 
                - * Applications may have the need to allow users to select different ranges of - * dates. There are three modes of selection that are supported, single, single interval - * and multiple interval selection. Once a selection is made a DateSelectionEvent is - * fired to inform listeners of the change. - *
                - *    // Change the selection mode to select full weeks.
                - *    monthView.setSelectionMode(SelectionMode.SINGLE_INTERVAL_SELECTION);
                - *
                - *    // Register a date selection listener to get notified about
                - *    // any changes in the date selection model.
                - *    monthView.getSelectionModel().addDateSelectionListener(new DateSelectionListener {
                - *        public void valueChanged(DateSelectionEvent e) {
                - *            log.info(e.getSelection());
                - *        }
                - *    });
                - * 
                - * - * NOTE (for users of earlier versions): as of version 1.19 control about selection - * dates is moved completely into the model. The default model used is of type - * DaySelectionModel, which handles dates in the same way the JXMonthView did earlier - * (that is, normalize all to the start of the day, which means zeroing all time - * fields).

                - * - * @author Joshua Outwater - * @author Jeanette Winzenburg - * @version $Revision: 4147 $ - */ -@JavaBean -public class JXMonthView extends JComponent { - @SuppressWarnings("unused") - private static final Logger LOG = Logger.getLogger(JXMonthView.class - .getName()); - /* - * moved from package calendar to swingx at version 1.51 - */ - - /** action command used for commit actionEvent. */ - public static final String COMMIT_KEY = "monthViewCommit"; - /** action command used for cancel actionEvent. */ - public static final String CANCEL_KEY = "monthViewCancel"; - - public static final String BOX_PADDING_X = "boxPaddingX"; - public static final String BOX_PADDING_Y = "boxPaddingY"; - public static final String DAYS_OF_THE_WEEK = "daysOfTheWeek"; - public static final String SELECTION_MODEL = "selectionModel"; - public static final String TRAVERSABLE = "traversable"; - public static final String FLAGGED_DATES = "flaggedDates"; - - static { - LookAndFeelAddons.contribute(new MonthViewAddon()); - } - - /** - * UI Class ID - */ - public static final String uiClassID = "MonthViewUI"; - - public static final int DAYS_IN_WEEK = 7; - public static final int MONTHS_IN_YEAR = 12; - - - /** - * Keeps track of the first date we are displaying. We use this as a - * restore point for the calendar. This is normalized to the start of the - * first day of the month given in setFirstDisplayedDate. - */ - private Date firstDisplayedDay; - /** - * the calendar to base all selections, flagging upon. - * NOTE: the time of this calendar is undefined - before using, internal - * code must explicitly set it. - * PENDING JW: as of version 1.26 all calendar/properties are controlled by the model. - * We keep a clone of the model's calendar here for notification reasons: - * model fires DateSelectionEvent of type CALENDAR_CHANGED which neiter carry the - * oldvalue nor the property name needed to map into propertyChange notification. - */ - private Calendar cal; - /** calendar to store the real input of firstDisplayedDate. */ - private Calendar anchor; - /** - * Start of the day which contains System.millis() in the current calendar. - * Kept in synch via a timer started in addNotify. - */ - private Date today; - /** - * The timer used to keep today in synch with system time. - */ - private Timer todayTimer; - // PENDING JW: why kept apart from cal? Why writable? - shouldn't the calendar have complete - // control? - private int firstDayOfWeek; - //-------------- selection/flagging - /** - * The DateSelectionModel driving this component. This model's calendar - * is the reference for all dates. - */ - private DateSelectionModel model; - /** - * Listener registered with the current model to keep Calendar dependent - * state synched. - */ - private DateSelectionListener modelListener; - /** - * The manager of the flagged dates. Note - * that the type of this is an implementation detail. - */ - private DaySelectionModel flaggedDates; - /** - * Storage of actionListeners registered with the monthView. - */ - private EventListenerMap listenerMap; - - private boolean traversable; - private boolean leadingDays; - private boolean trailingDays; - private boolean showWeekNumber; - private boolean componentInputMapEnabled; - - //------------------- - // PENDING JW: ?? - @SuppressWarnings({"FieldCanBeLocal"}) - protected Date modifiedStartDate; - @SuppressWarnings({"FieldCanBeLocal"}) - protected Date modifiedEndDate; - - //------------- visuals - - /** - * Localizable day column headers. Default typically installed by the uidelegate. - */ - private String[] _daysOfTheWeek; - - protected Insets _monthStringInsets = new Insets(0, 0, 0, 0); - private int boxPaddingX; - private int boxPaddingY; - private int minCalCols = 1; - private int minCalRows = 1; - private Color todayBackgroundColor; - private Color monthStringBackground; - private Color monthStringForeground; - private Color daysOfTheWeekForeground; - private Color selectedBackground; - private Hashtable dayToColorTable = new Hashtable(); - private Color flaggedDayForeground; - - private Color selectedForeground; - private boolean zoomable; - - /** - * Create a new instance of the JXMonthView class using the - * default Locale and the current system time as the first date to - * display. - */ - public JXMonthView() { - this(null, null, null); - } - - /** - * Create a new instance of the JXMonthView class using the - * default Locale and the current system time as the first date to - * display. - * - * @param locale desired locale, if null the system default locale is used - */ - public JXMonthView(final Locale locale) { - this(null, null, locale); - } - - /** - * Create a new instance of the JXMonthView class using the - * default Locale and the given time as the first date to - * display. - * - * @param firstDisplayedDay a day of the first month to display; if null, the current - * System time is used. - */ - public JXMonthView(Date firstDisplayedDay) { - this(firstDisplayedDay, null, null); - } - - /** - * Create a new instance of the JXMonthView class using the - * default Locale, the given time as the first date to - * display and the given selection model. - * - * @param firstDisplayedDay a day of the first month to display; if null, the current - * System time is used. - * @param model the selection model to use, if null a DefaultSelectionModel is - * created. - */ - public JXMonthView(Date firstDisplayedDay, final DateSelectionModel model) { - this(firstDisplayedDay, model, null); - } - - - /** - * Create a new instance of the JXMonthView class using the - * given Locale, the given time as the first date to - * display and the given selection model. - * - * @param firstDisplayedDay a day of the first month to display; if null, the current - * System time is used. - * @param model the selection model to use, if null a DefaultSelectionModel is - * created. - * @param locale desired locale, if null the system default locale is used - */ - public JXMonthView(Date firstDisplayedDay, final DateSelectionModel model, final Locale locale) { - super(); - listenerMap = new EventListenerMap(); - - initModel(model, locale); - superSetLocale(locale); - setFirstDisplayedDay(firstDisplayedDay != null ? firstDisplayedDay : getCurrentDate()); - // Keep track of today - updateTodayFromCurrentTime(); - - // install the controller - updateUI(); - - setFocusable(true); - todayBackgroundColor = getForeground(); - - } - - -//------------------ Calendar related properties - - /** - * Sets locale and resets text and format used to display months and days. - * Also resets firstDayOfWeek.

                - * - * PENDING JW: the following warning should be obsolete (installCalendar - * should take care) - check if it really is! - * - *

                - * Warning: Since this resets any string labels that are cached in UI - * (month and day names) and firstDayofWeek, use setDaysOfTheWeek and/or - * setFirstDayOfWeek after (re)setting locale. - *

                - * - * @param locale new Locale to be used for formatting - * @see #setDaysOfTheWeek(String[]) - * @see #setFirstDayOfWeek(int) - */ - @Override - public void setLocale(Locale locale) { - model.setLocale(locale); - } - - /** - * - * @param locale - */ - private void superSetLocale(Locale locale) { - // PENDING JW: formally, a null value is allowed and must be passed on to super - // I suspect this is not done here to keep the logic out off the constructor? - // - if (locale != null) { - super.setLocale(locale); - repaint(); - } - } - - /** - * Returns a clone of the internal calendar, with it's time set to firstDisplayedDate. - * - * PENDING: firstDisplayed useful as reference time? It's timezone dependent anyway. - * Think: could return with monthView's today instead? - * - * @return a clone of internal calendar, configured to the current firstDisplayedDate - * @throws IllegalStateException if called before instantitation is completed - */ - public Calendar getCalendar() { - // JW: this is to guard against a regression of not-fully understood - // problems in constructor (UI used to call back into this before we were ready) - if (cal == null) throw - new IllegalStateException("must not be called before instantiation is complete"); - Calendar calendar = (Calendar) cal.clone(); - calendar.setTime(firstDisplayedDay); - return calendar; - } - - /** - * Gets the time zone. - * - * @return The TimeZone used by the JXMonthView. - */ - public TimeZone getTimeZone() { - // PENDING JW: looks fishy (left-over?) .. why not ask the model? - return cal.getTimeZone(); - } - - /** - * Sets the time zone with the given time zone value. - * - * This is a bound property. - * - * @param tz The TimeZone. - */ - public void setTimeZone(TimeZone tz) { - model.setTimeZone(tz); - } - - /** - * Gets what the first day of the week is; e.g., - * Calendar.SUNDAY in the U.S., Calendar.MONDAY - * in France. - * - * @return int The first day of the week. - */ - public int getFirstDayOfWeek() { - return firstDayOfWeek; - } - - /** - * Sets what the first day of the week is; e.g., - * Calendar.SUNDAY in US, Calendar.MONDAY - * in France. - * - * @param firstDayOfWeek The first day of the week. - * @see Calendar - */ - public void setFirstDayOfWeek(int firstDayOfWeek) { - getSelectionModel().setFirstDayOfWeek(firstDayOfWeek); - } - - - -//---------------------- synch to model's calendar - - /** - * Initializes selection model related internals. If the Locale is - * null, it falls back to JComponent.defaultLocale. If the model - * is null it creates a default model with the locale. - * - * PENDING JW: leave default locale fallback to model? - * - * @param model the DateSelectionModel which should drive the monthView. - * If null, a default model is created and initialized with the given locale. - * @param locale the Locale to use with the selectionModel. If null, - * JComponent.getDefaultLocale is used. - */ - private void initModel(DateSelectionModel model, Locale locale) { - if (locale == null) { - locale = JComponent.getDefaultLocale(); - } - if (model == null) { - model = new DaySelectionModel(locale); - } - this.model = model; - // PENDING JW: do better to synchronize Calendar related - // properties of flaggedDates to those of the selection model. - // plus: should use the same normalization? - this.flaggedDates = new DaySelectionModel(locale); - flaggedDates.setSelectionMode(SelectionMode.MULTIPLE_INTERVAL_SELECTION); - - installCalendar(); - model.addDateSelectionListener(getDateSelectionListener()); - } - - /** - * Lazily creates and returns the DateSelectionListener which listens - * for model's calendar properties. - * - * @return a DateSelectionListener for model's CALENDAR_CHANGED notification. - */ - private DateSelectionListener getDateSelectionListener() { - if (modelListener == null) { - modelListener = new DateSelectionListener() { - - @Override - public void valueChanged(DateSelectionEvent ev) { - if (EventType.CALENDAR_CHANGED.equals(ev.getEventType())) { - updateCalendar(); - } - - } - - }; - } - return modelListener; - } - - /** - * Installs the internal calendars from the selection model.

                - * - * PENDING JW: in fixing #11433, added update of firstDisplayedDay and - * today here - check if correct place to do so. - * - */ - private void installCalendar() { - cal = model.getCalendar(); - firstDayOfWeek = cal.getFirstDayOfWeek(); - Date anchorDate = getAnchorDate(); - anchor = (Calendar) cal.clone(); - if (anchorDate != null) { - setFirstDisplayedDay(anchorDate); - } - updateTodayFromCurrentTime(); - } - - /** - * Returns the anchor date. Currently, this is the "uncleaned" input date - * of setFirstDisplayedDate. This is a quick hack for Issue #618-swingx, to - * have some invariant for testing. Do not use in client code, may change - * without notice! - * - * @return the "uncleaned" first display date or null if the firstDisplayedDay - * is not yet set. - */ - protected Date getAnchorDate() { - return anchor != null ? anchor.getTime() : null; - } - - /** - * Callback from selection model calendar changes. - */ - private void updateCalendar() { - if (!getLocale().equals(model.getLocale())) { - installCalendar(); - superSetLocale(model.getLocale()); - } else { - if (!model.getTimeZone().equals(getTimeZone())) { - updateTimeZone(); - } - if (cal.getMinimalDaysInFirstWeek() != model.getMinimalDaysInFirstWeek()) { - updateMinimalDaysOfFirstWeek(); - } - if (cal.getFirstDayOfWeek() != model.getFirstDayOfWeek()) { - updateFirstDayOfWeek(); - } - } - } - - - /** - * Callback from changing timezone in model. - */ - private void updateTimeZone() { - TimeZone old = getTimeZone(); - TimeZone tz = model.getTimeZone(); - cal.setTimeZone(tz); - anchor.setTimeZone(tz); - setFirstDisplayedDay(anchor.getTime()); - updateTodayFromCurrentTime(); - updateDatesAfterTimeZoneChange(old); - firePropertyChange("timeZone", old, getTimeZone()); - } - - /** - * All dates are "cleaned" relative to the timezone they had been set. - * After changing the timezone, they need to be updated to the new. - * - * Here: clear everything. - * - * @param oldTimeZone the timezone before the change - */ - protected void updateDatesAfterTimeZoneChange(TimeZone oldTimeZone) { - SortedSet flagged = getFlaggedDates(); - flaggedDates.setTimeZone(getTimeZone()); - firePropertyChange("flaggedDates", flagged, getFlaggedDates()); - } - - /** - * Call back from listening to model firstDayOfWeek change. - */ - private void updateFirstDayOfWeek() { - int oldFirstDayOfWeek = this.firstDayOfWeek; - - firstDayOfWeek = getSelectionModel().getFirstDayOfWeek(); - cal.setFirstDayOfWeek(firstDayOfWeek); - anchor.setFirstDayOfWeek(firstDayOfWeek); - firePropertyChange("firstDayOfWeek", oldFirstDayOfWeek, firstDayOfWeek); - } - - /** - * Call back from listening to model minimalDaysOfFirstWeek change. - *

                - * NOTE: this is not a property as we have no public api to change - * it on JXMonthView. - */ - private void updateMinimalDaysOfFirstWeek() { - cal.setMinimalDaysInFirstWeek(model.getMinimalDaysInFirstWeek()); - anchor.setMinimalDaysInFirstWeek(model.getMinimalDaysInFirstWeek()); - } - - - -//-------------------- scrolling - /** - * Returns the last date able to be displayed. For example, if the last - * visible month was April the time returned would be April 30, 23:59:59. - * - * @return long The last displayed date. - */ - public Date getLastDisplayedDay() { - return getUI().getLastDisplayedDay(); - } - - - /** - * Returns the first displayed date. - * - * @return long The first displayed date. - */ - public Date getFirstDisplayedDay() { - return firstDisplayedDay; - } - - - /** - * Set the first displayed date. We only use the month and year of - * this date. The Calendar.DAY_OF_MONTH field is reset to - * 1 and all other fields, with exception of the year and month, - * are reset to 0. - * - * @param date The first displayed date. - */ - public void setFirstDisplayedDay(Date date) { - anchor.setTime(date); - Date oldDate = getFirstDisplayedDay(); - - cal.setTime(anchor.getTime()); - CalendarUtils.startOfMonth(cal); - firstDisplayedDay = cal.getTime(); - firePropertyChange("firstDisplayedDay", oldDate, getFirstDisplayedDay() ); - } - - /** - * Moves the date into the visible region of the calendar. If - * the date is greater than the last visible date it will become the last - * visible date. While if it is less than the first visible date it will - * become the first visible date.

                - * - * NOTE: this is the recommended method to scroll to a particular date, the - * functionally equivalent method taking a long as parameter will most - * probably be deprecated. - * - * @param date Date to make visible, must not be null. - * @see #ensureDateVisible(Date) - */ - public void ensureDateVisible(Date date) { - if (date.before(firstDisplayedDay)) { - setFirstDisplayedDay(date); - } else { - Date lastDisplayedDate = getLastDisplayedDay(); - if (date.after(lastDisplayedDate)) { - // extract to CalendarUtils! - cal.setTime(date); - int month = cal.get(Calendar.MONTH); - int year = cal.get(Calendar.YEAR); - - cal.setTime(lastDisplayedDate); - int lastMonth = cal.get(Calendar.MONTH); - int lastYear = cal.get(Calendar.YEAR); - - int diffMonths = month - lastMonth - + ((year - lastYear) * MONTHS_IN_YEAR); - - cal.setTime(firstDisplayedDay); - cal.add(Calendar.MONTH, diffMonths); - setFirstDisplayedDay(cal.getTime()); - } - } - } - - - /** - * Returns the Date at the given location. May be null if the - * coordinates don't map to a day in the month which contains the - * coordinates. Specifically: hitting leading/trailing dates returns null. - * - * Mapping pixel to calendar day. - * - * @param x the x position of the location in pixel - * @param y the y position of the location in pixel - * @return the day at the given location or null if the location - * doesn't map to a day in the month which contains the coordinates. - */ - public Date getDayAtLocation(int x, int y) { - return getUI().getDayAtLocation(x, y); - } - -//------------------ today - - /** - * Returns the current Date (whateverthatmeans). Internally always invoked when - * the current default is needed. Introduced mainly for testing, don't override! - * - * This implementation returns a Date instantiated with System.currentTimeInMillis. - * - * @return the date deemed as current. - */ - Date getCurrentDate() { - return new Date(System.currentTimeMillis()); - } - - - /** - * Sets today from the current system time. - * - * temporary widened access for testing. - */ - protected void updateTodayFromCurrentTime() { - setToday(getCurrentDate()); - } - - /** - * Increments today. This is used by the timer. - * - * PENDING: is it safe? doesn't check if we are really tomorrow? - * temporary widened access for testing. - */ - protected void incrementToday() { - cal.setTime(getToday()); - cal.add(Calendar.DAY_OF_MONTH, 1); - setToday(cal.getTime()); - } - - /** - * Sets the date which represents today. Internally - * modified to the start of the day which contains the - * given date in this monthView's calendar coordinates. - * - * temporary widened access for testing. - * - * @param date the date which should be used as today. - */ - protected void setToday(Date date) { - Date oldToday = getToday(); - // PENDING JW: do we really want the start of today? - this.today = startOfDay(date); - firePropertyChange("today", oldToday, getToday()); - } - - /** - * Returns the start of today in this monthviews calendar coordinates. - * - * @return the start of today as Date. - */ - public Date getToday() { - // null only happens in the very first time ... - return today != null ? (Date) today.clone() : null; - } - - - -//---- internal date manipulation ("cleanup" == start of day in monthView's calendar) - - - /** - * Returns the start of the day as Date. - * - * @param date the Date. - * @return start of the given day as Date, relative to this - * monthView's calendar. - * - */ - private Date startOfDay(Date date) { - return CalendarUtils.startOfDay(cal, date); - } - -//------------------- ui delegate - /** - * @inheritDoc - */ - public MonthViewUI getUI() { - return (MonthViewUI)ui; - } - - /** - * Sets the L&F object that renders this component. - * - * @param ui UI to use for this {@code JXMonthView} - */ - public void setUI(MonthViewUI ui) { - super.setUI(ui); - } - - /** - * Resets the UI property with the value from the current look and feel. - * - * @see UIManager#getUI(JComponent) - */ - @Override - public void updateUI() { - setUI((MonthViewUI)LookAndFeelAddons.getUI(this, MonthViewUI.class)); - invalidate(); - } - - /** - * @inheritDoc - */ - @Override - public String getUIClassID() { - return uiClassID; - } - - -//---------------- DateSelectionModel - - /** - * Returns the date selection model which drives this - * JXMonthView. - * - * @return the date selection model - */ - public DateSelectionModel getSelectionModel() { - return model; - } - - /** - * Sets the date selection model to drive this monthView. - * - * @param model the selection model to use, must not be null. - * @throws NullPointerException if model is null - */ - public void setSelectionModel(DateSelectionModel model) { - Contract.asNotNull(model, "date selection model must not be null"); - DateSelectionModel oldModel = getSelectionModel(); - model.removeDateSelectionListener(getDateSelectionListener()); - this.model = model; - installCalendar(); - if (!model.getLocale().equals(getLocale())) { - super.setLocale(model.getLocale()); - } - model.addDateSelectionListener(getDateSelectionListener()); - firePropertyChange(SELECTION_MODEL, oldModel, getSelectionModel()); - } - -//-------------------- delegates to model - - /** - * Clear any selection from the selection model - */ - public void clearSelection() { - getSelectionModel().clearSelection(); - } - - /** - * Return true if the selection is empty, false otherwise - * - * @return true if the selection is empty, false otherwise - */ - public boolean isSelectionEmpty() { - return getSelectionModel().isSelectionEmpty(); - } - - /** - * Get the current selection - * - * @return sorted set of selected dates - */ - public SortedSet getSelection() { - return getSelectionModel().getSelection(); - } - - /** - * Adds the selection interval to the selection model. - * - * @param startDate Start of date range to add to the selection - * @param endDate End of date range to add to the selection - */ - public void addSelectionInterval(Date startDate, Date endDate) { - getSelectionModel().addSelectionInterval(startDate, endDate); - } - - /** - * Sets the selection interval to the selection model. - * - * @param startDate Start of date range to set the selection to - * @param endDate End of date range to set the selection to - */ - public void setSelectionInterval(final Date startDate, final Date endDate) { - getSelectionModel().setSelectionInterval(startDate, endDate); - } - - /** - * Removes the selection interval from the selection model. - * - * @param startDate Start of the date range to remove from the selection - * @param endDate End of the date range to remove from the selection - */ - public void removeSelectionInterval(final Date startDate, final Date endDate) { - getSelectionModel().removeSelectionInterval(startDate, endDate); - } - - /** - * Returns the current selection mode for this JXMonthView. - * - * @return int Selection mode. - */ - public SelectionMode getSelectionMode() { - return getSelectionModel().getSelectionMode(); - } - - /** - * Set the selection mode for this JXMonthView. - - * @param selectionMode The selection mode to use for this {@code JXMonthView} - */ - public void setSelectionMode(final SelectionMode selectionMode) { - getSelectionModel().setSelectionMode(selectionMode); - } - - /** - * Returns the earliest selected date. - * - * - * @return the first Date in the selection or null if empty. - */ - public Date getFirstSelectionDate() { - return getSelectionModel().getFirstSelectionDate(); - } - - - /** - * Returns the earliest selected date. - * - * @return the first Date in the selection or null if empty. - */ - public Date getLastSelectionDate() { - return getSelectionModel().getLastSelectionDate(); - } - - /** - * Returns the earliest selected date. - * - * PENDING JW: keep this? it was introduced before the first/last - * in model. When delegating everything, we duplicate here. - * - * @return the first Date in the selection or null if empty. - */ - public Date getSelectionDate() { - return getFirstSelectionDate(); - } - - /** - * Sets the model's selection to the given date or clears the selection if - * null. - * - * @param newDate the selection date to set - */ - public void setSelectionDate(Date newDate) { - if (newDate == null) { - clearSelection(); - } else { - setSelectionInterval(newDate, newDate); - } - } - - /** - * Returns true if the specified date falls within the _startSelectedDate - * and _endSelectedDate range. - * - * @param date The date to check - * @return true if the date is selected, false otherwise - */ - public boolean isSelected(Date date) { - return getSelectionModel().isSelected(date); - } - - - /** - * Set the lower bound date that is allowed to be selected.

                - * - * - * @param lowerBound the lower bound, null means none. - */ - public void setLowerBound(Date lowerBound) { - getSelectionModel().setLowerBound(lowerBound); - } - - /** - * Set the upper bound date that is allowed to be selected.

                - * - * @param upperBound the upper bound, null means none. - */ - public void setUpperBound(Date upperBound) { - getSelectionModel().setUpperBound(upperBound); - } - - - /** - * Return the lower bound date that is allowed to be selected for this - * model. - * - * @return lower bound date or null if not set - */ - public Date getLowerBound() { - return getSelectionModel().getLowerBound(); - } - - /** - * Return the upper bound date that is allowed to be selected for this - * model. - * - * @return upper bound date or null if not set - */ - public Date getUpperBound() { - return getSelectionModel().getUpperBound(); - } - - /** - * Identifies whether or not the date passed is an unselectable date. - *

                - * - * @param date date which to test for unselectable status - * @return true if the date is unselectable, false otherwise - */ - public boolean isUnselectableDate(Date date) { - return getSelectionModel().isUnselectableDate(date); - } - - /** - * Sets the dates that should be unselectable. This will replace the model's - * current set of unselectable dates. The implication is that calling with - * zero dates will remove all unselectable dates. - *

                - * - * NOTE: neither the given array nor any of its elements must be null. - * - * @param unselectableDates zero or more not-null dates that should be - * unselectable. - * @throws NullPointerException if either the array or any of the elements - * are null - */ - public void setUnselectableDates(Date... unselectableDates) { - Contract.asNotNull(unselectableDates, - "unselectable dates must not be null"); - SortedSet unselectableSet = new TreeSet(); - for (Date unselectableDate : unselectableDates) { - unselectableSet.add(unselectableDate); - } - getSelectionModel().setUnselectableDates(unselectableSet); - // PENDING JW: check that ui does the repaint! - repaint(); - } - - // --------------------- flagged dates - /** - * Identifies whether or not the date passed is a flagged date. - * - * @param date date which to test for flagged status - * @return true if the date is flagged, false otherwise - */ - public boolean isFlaggedDate(Date date) { - if (date == null) - return false; - return flaggedDates.isSelected(date); - } - - /** - * Replace all flags with the given dates.

                - * - * NOTE: neither the given array nor any of its elements should be null. - * Currently, a null array will be tolerated to ease migration. A null - * has the same effect as clearFlaggedDates. - * - * - * @param flagged the dates to be flagged - */ - public void setFlaggedDates(Date... flagged) { -// Contract.asNotNull(flagged, "must not be null"); - SortedSet oldFlagged = getFlaggedDates(); - flaggedDates.clearSelection(); - if (flagged != null) { - for (Date date : flagged) { - flaggedDates.addSelectionInterval(date, date); - } - } - firePropertyChange("flaggedDates", oldFlagged, getFlaggedDates()); - } - /** - * Adds the dates to the flags. - * - * NOTE: neither the given array nor any of its elements should be null. - * Currently, a null array will be tolerated to ease migration. A null - * does nothing. - * - * @param flagged the dates to be flagged - */ - public void addFlaggedDates(Date... flagged) { -// Contract.asNotNull(flagged, "must not be null"); - SortedSet oldFlagged = flaggedDates.getSelection(); - if (flagged != null) { - for (Date date : flagged) { - flaggedDates.addSelectionInterval(date, date); - } - } - firePropertyChange("flaggedDates", oldFlagged, flaggedDates.getSelection()); - } - - /** - * Unflags the given dates. - * - * NOTE: neither the given array nor any of its elements should be null. - * Currently, a null array will be tolerated to ease migration. - * - * @param flagged the dates to be unflagged - */ - public void removeFlaggedDates(Date... flagged) { -// Contract.asNotNull(flagged, "must not be null"); - SortedSet oldFlagged = flaggedDates.getSelection(); - if (flagged != null) { - for (Date date : flagged) { - flaggedDates.removeSelectionInterval(date, date); - } - } - firePropertyChange("flaggedDates", oldFlagged, flaggedDates.getSelection()); - } - /** - * Clears all flagged dates. - * - */ - public void clearFlaggedDates() { - SortedSet oldFlagged = flaggedDates.getSelection(); - flaggedDates.clearSelection(); - firePropertyChange("flaggedDates", oldFlagged, flaggedDates.getSelection()); - } - - /** - * Returns a sorted set of flagged Dates. The returned set is guaranteed to - * be not null, but may be empty. - * - * @return a sorted set of flagged dates. - */ - public SortedSet getFlaggedDates() { - return flaggedDates.getSelection(); - } - - /** - * Returns a boolean indicating if this monthView has flagged dates. - * - * @return a boolean indicating if this monthView has flagged dates. - */ - public boolean hasFlaggedDates() { - return !flaggedDates.isSelectionEmpty(); - } - - -//------------------- visual properties - /** - * Sets a boolean property indicating whether or not to show leading dates - * for a months displayed by this component.

                - * - * The default value is false. - * - * @param value true if leading dates should be displayed, false otherwise. - */ - public void setShowingLeadingDays(boolean value) { - boolean old = isShowingLeadingDays(); - leadingDays = value; - firePropertyChange("showingLeadingDays", old, isShowingLeadingDays()); - } - - /** - * Returns a boolean indicating whether or not we're showing leading dates. - * - * @return true if leading dates are shown, false otherwise. - */ - public boolean isShowingLeadingDays() { - return leadingDays; - } - - /** - * Sets a boolean property indicating whether or not to show - * trailing dates for the months displayed by this component.

                - * - * The default value is false. - * - * @param value true if trailing dates should be displayed, false otherwise. - */ - public void setShowingTrailingDays(boolean value) { - boolean old = isShowingTrailingDays(); - trailingDays = value; - firePropertyChange("showingTrailingDays", old, isShowingTrailingDays()); - } - - /** - * Returns a boolean indicating whether or not we're showing trailing dates. - * - * @return true if trailing dates are shown, false otherwise. - */ - public boolean isShowingTrailingDays() { - return trailingDays; - } - - /** - * Returns whether or not the month view supports traversing months. - * If zoomable is enabled, traversable is enabled as well. Otherwise - * returns the traversable property as set by client code. - * - * @return true if month traversing is enabled. - * @see #setZoomable(boolean) - */ - public boolean isTraversable() { - if (isZoomable()) return true; - return traversable; - } - - /** - * Set whether or not the month view will display buttons to allow the user - * to traverse to previous or next months.

                - * - * The default value is false.

                - * - * PENDING JW: fire the "real" property or the compound with zoomable? - * - * @param traversable set to true to enable month traversing, false - * otherwise. - * @see #isTraversable() - * @see #setZoomable(boolean) - */ - public void setTraversable(boolean traversable) { - boolean old = isTraversable(); - this.traversable = traversable; - firePropertyChange(TRAVERSABLE, old, isTraversable()); - } - - /** - * Returns true if zoomable (through date ranges). - * - * @return true if zoomable is enabled. - * @see #setZoomable(boolean) - */ - public boolean isZoomable() { - return zoomable; - } - - /** - * Sets the zoomable property. If true, the calendar's date range can - * be zoomed. This state implies that the calendar is traversable and - * showing exactly one calendar box, effectively ignoring the properties.

                - * - * Note: The actual zoomable behaviour is not yet implemented. - * - * @param zoomable a boolean indicating whether or not zooming date - * ranges is enabled. - * - * @see #setTraversable(boolean) - */ - public void setZoomable(boolean zoomable) { - boolean old = isZoomable(); - this.zoomable = zoomable; - firePropertyChange("zoomable", old, isZoomable()); - } - - /** - * Returns whether or not this JXMonthView should display - * week number. - * - * @return true if week numbers should be displayed - */ - public boolean isShowingWeekNumber() { - return showWeekNumber; - } - - /** - * Set whether or not this JXMonthView will display week - * numbers or not. - * - * @param showWeekNumber true if week numbers should be displayed, - * false otherwise - */ - public void setShowingWeekNumber(boolean showWeekNumber) { - boolean old = isShowingWeekNumber(); - this.showWeekNumber = showWeekNumber; - firePropertyChange("showingWeekNumber", old, isShowingWeekNumber()); - } - - /** - * Sets the String representation for each day of the week as used - * in the header of the day's grid. For - * this method the first days of the week days[0] is assumed to be - * Calendar.SUNDAY. If null, the representation provided - * by the MonthViewUI is used. - * - * The default value is the representation as - * returned from the MonthViewUI. - * - * @param days Array of characters that represents each day - * @throws IllegalArgumentException if not null and days.length != - * DAYS_IN_WEEK - */ - public void setDaysOfTheWeek(String[] days) { - if ((days != null) && (days.length != DAYS_IN_WEEK)) { - throw new IllegalArgumentException( - "Array of days is not of length " + DAYS_IN_WEEK - + " as expected."); - } - - String[] oldValue = getDaysOfTheWeek(); - _daysOfTheWeek = days; - firePropertyChange(DAYS_OF_THE_WEEK, oldValue, days); - } - - /** - * Returns the String representation for each day of the - * week. - * - * @return String representation for the days of the week, guaranteed to - * never be null. - * - * @see #setDaysOfTheWeek(String[]) - * @see MonthViewUI - */ - public String[] getDaysOfTheWeek() { - if (_daysOfTheWeek != null) { - String[] days = new String[DAYS_IN_WEEK]; - System.arraycopy(_daysOfTheWeek, 0, days, 0, DAYS_IN_WEEK); - return days; - } - return getUI().getDaysOfTheWeek(); - } - - /** - * - * @param dayOfWeek - * @return String representation of day of week. - */ - public String getDayOfTheWeek(int dayOfWeek) { - return getDaysOfTheWeek()[dayOfWeek - 1]; - } - - - /** - * Returns the padding used between days in the calendar. - * - * @return Padding used between days in the calendar - */ - public int getBoxPaddingX() { - return boxPaddingX; - } - - /** - * Sets the number of pixels used to pad the left and right side of a day. - * The padding is applied to both sides of the days. Therefore, if you - * used the padding value of 3, the number of pixels between any two days - * would be 6. - * - * @param boxPaddingX Number of pixels applied to both sides of a day - */ - public void setBoxPaddingX(int boxPaddingX) { - int oldBoxPadding = getBoxPaddingX(); - this.boxPaddingX = boxPaddingX; - firePropertyChange(BOX_PADDING_X, oldBoxPadding, getBoxPaddingX()); - } - - /** - * Returns the padding used above and below days in the calendar. - * - * @return Padding used between dats in the calendar - */ - public int getBoxPaddingY() { - return boxPaddingY; - } - - /** - * Sets the number of pixels used to pad the top and bottom of a day. - * The padding is applied to both the top and bottom of a day. Therefore, - * if you used the padding value of 3, the number of pixels between any - * two days would be 6. - * - * @param boxPaddingY Number of pixels applied to top and bottom of a day - */ - public void setBoxPaddingY(int boxPaddingY) { - int oldBoxPadding = getBoxPaddingY(); - this.boxPaddingY = boxPaddingY; - firePropertyChange(BOX_PADDING_Y, oldBoxPadding, getBoxPaddingY()); - } - - - /** - * Returns the selected background color. - * - * @return the selected background color. - */ - public Color getSelectionBackground() { - return selectedBackground; - } - - /** - * Sets the selected background color to c. The default color - * is installed by the ui. - * - * @param c Selected background. - */ - public void setSelectionBackground(Color c) { - Color old = getSelectionBackground(); - selectedBackground = c; - firePropertyChange("selectionBackground", old, getSelectionBackground()); - } - - /** - * Returns the selected foreground color. - * - * @return the selected foreground color. - */ - public Color getSelectionForeground() { - return selectedForeground; - } - - /** - * Sets the selected foreground color to c. The default color - * is installed by the ui. - * - * @param c Selected foreground. - */ - public void setSelectionForeground(Color c) { - Color old = getSelectionForeground(); - selectedForeground = c; - firePropertyChange("selectionForeground", old, getSelectionForeground()); - } - - - /** - * Returns the color used when painting the today background. - * - * @return Color Color - */ - public Color getTodayBackground() { - return todayBackgroundColor; - } - - /** - * Sets the color used to draw the bounding box around today. The default - * is the background of the JXMonthView component. - * - * @param c color to set - */ - public void setTodayBackground(Color c) { - Color oldValue = getTodayBackground(); - todayBackgroundColor = c; - firePropertyChange("todayBackground", oldValue, getTodayBackground()); - // PENDING JW: remove repaint, ui must take care of it - repaint(); - } - - /** - * Returns the color used to paint the month string background. - * - * @return Color Color. - */ - public Color getMonthStringBackground() { - return monthStringBackground; - } - - /** - * Sets the color used to draw the background of the month string. The - * default is 138, 173, 209 (Blue-ish). - * - * @param c color to set - */ - public void setMonthStringBackground(Color c) { - Color old = getMonthStringBackground(); - monthStringBackground = c; - firePropertyChange("monthStringBackground", old, getMonthStringBackground()); - // PENDING JW: remove repaint, ui must take care of it - repaint(); - } - - /** - * Returns the color used to paint the month string foreground. - * - * @return Color Color. - */ - public Color getMonthStringForeground() { - return monthStringForeground; - } - - /** - * Sets the color used to draw the foreground of the month string. The - * default is Color.WHITE. - * - * @param c color to set - */ - public void setMonthStringForeground(Color c) { - Color old = getMonthStringForeground(); - monthStringForeground = c; - firePropertyChange("monthStringForeground", old, getMonthStringForeground()); - // PENDING JW: remove repaint, ui must take care of it - repaint(); - } - - /** - * Sets the color used to draw the foreground of each day of the week. These - * are the titles - * - * @param c color to set - */ - public void setDaysOfTheWeekForeground(Color c) { - Color old = getDaysOfTheWeekForeground(); - daysOfTheWeekForeground = c; - firePropertyChange("daysOfTheWeekForeground", old, getDaysOfTheWeekForeground()); - } - - /** - * @return Color Color - */ - public Color getDaysOfTheWeekForeground() { - return daysOfTheWeekForeground; - } - - /** - * Set the color to be used for painting the specified day of the week. - * Acceptable values are Calendar.SUNDAY - Calendar.SATURDAY.

                - * - * PENDING JW: this is not a property - should it be and - * fire a change notification? If so, how? - * - * - * @param dayOfWeek constant value defining the day of the week. - * @param c The color to be used for painting the numeric day of the week. - */ - public void setDayForeground(int dayOfWeek, Color c) { - if ((dayOfWeek < Calendar.SUNDAY) || (dayOfWeek > Calendar.SATURDAY)) { - throw new IllegalArgumentException("dayOfWeek must be in [Calendar.SUNDAY ... " + - "Calendar.SATURDAY] but was " + dayOfWeek); - } - dayToColorTable.put(dayOfWeek, c); - repaint(); - } - - /** - * Return the color that should be used for painting the numerical day of the week. - * - * @param dayOfWeek The day of week to get the color for. - * @return The color to be used for painting the numeric day of the week. - * If this was no color has yet been defined the component foreground color - * will be returned. - */ - public Color getDayForeground(int dayOfWeek) { - Color c; - c = dayToColorTable.get(dayOfWeek); - if (c == null) { - c = getForeground(); - } - return c; - } - - /** - * Return the color that should be used for painting the numerical day of the week. - * - * @param dayOfWeek The day of week to get the color for. - * @return The color to be used for painting the numeric day of the week or null - * If no color has yet been defined. - */ - public Color getPerDayOfWeekForeground(int dayOfWeek) { - return dayToColorTable.get(dayOfWeek); - } - /** - * Set the color to be used for painting the foreground of a flagged day. - * - * @param c The color to be used for painting. - */ - public void setFlaggedDayForeground(Color c) { - Color old = getFlaggedDayForeground(); - flaggedDayForeground = c; - firePropertyChange("flaggedDayForeground", old, getFlaggedDayForeground()); - } - - /** - * Return the color that should be used for painting the foreground of the flagged day. - * - * @return The color to be used for painting - */ - public Color getFlaggedDayForeground() { - return flaggedDayForeground; - } - - /** - * Returns a copy of the insets used to paint the month string background. - * - * @return Insets Month string insets. - */ - public Insets getMonthStringInsets() { - return (Insets) _monthStringInsets.clone(); - } - - /** - * Insets used to modify the width/height when painting the background - * of the month string area. - * - * @param insets Insets - */ - public void setMonthStringInsets(Insets insets) { - Insets old = getMonthStringInsets(); - if (insets == null) { - _monthStringInsets.top = 0; - _monthStringInsets.left = 0; - _monthStringInsets.bottom = 0; - _monthStringInsets.right = 0; - } else { - _monthStringInsets.top = insets.top; - _monthStringInsets.left = insets.left; - _monthStringInsets.bottom = insets.bottom; - _monthStringInsets.right = insets.right; - } - firePropertyChange("monthStringInsets", old, getMonthStringInsets()); - // PENDING JW: remove repaint, ui must take care of it - repaint(); - } - - /** - * Returns the preferred number of columns to paint calendars in. - *

                - * @return int preferred number of columns of calendars. - * - * @see #setPreferredColumnCount(int) - */ - public int getPreferredColumnCount() { - return minCalCols; - } - - /** - * Sets the preferred number of columns of calendars. Does nothing if cols - * <= 0. The default value is 1. - *

                - * @param cols The number of columns of calendars. - * - * @see #getPreferredColumnCount() - */ - public void setPreferredColumnCount(int cols) { - if (cols <= 0) { - return; - } - int old = getPreferredColumnCount(); - minCalCols = cols; - firePropertyChange("preferredColumnCount", old, getPreferredColumnCount()); - // PENDING JW: remove revalidate/repaint, ui must take care of it - revalidate(); - repaint(); - } - - - /** - * Returns the preferred number of rows to paint calendars in. - *

                - * @return int Rows of calendars. - * - * @see #setPreferredRowCount(int) - */ - public int getPreferredRowCount() { - return minCalRows; - } - - /** - * Sets the preferred number of rows to paint calendars.Does nothing if rows - * <= 0. The default value is 1. - *

                - * - * @param rows The number of rows of calendars. - * - * @see #getPreferredRowCount() - */ - public void setPreferredRowCount(int rows) { - if (rows <= 0) { - return; - } - int old = getPreferredRowCount(); - minCalRows = rows; - firePropertyChange("preferredRowCount", old, getPreferredRowCount()); - // PENDING JW: remove revalidate/repaint, ui must take care of it - revalidate(); - repaint(); - } - - - /** - * {@inheritDoc} - */ - @Override - public void removeNotify() { - if (todayTimer != null) { - todayTimer.stop(); - } - super.removeNotify(); - } - - /** - * {@inheritDoc} - */ - @Override - public void addNotify() { - super.addNotify(); - // partial fix for #1125: today updated in addNotify - // partial, because still not in synch if not shown - updateTodayFromCurrentTime(); - // Setup timer to update the value of today. - int secondsTillTomorrow = 86400; - - if (todayTimer == null) { - todayTimer = new Timer(secondsTillTomorrow * 1000, - new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - incrementToday(); - } - }); - } - - // Modify the initial delay by the current time. -// cal.setTimeInMillis(System.currentTimeMillis()); - cal.setTime(getCurrentDate()); - secondsTillTomorrow = secondsTillTomorrow - - (cal.get(Calendar.HOUR_OF_DAY) * 3600) - - (cal.get(Calendar.MINUTE) * 60) - - cal.get(Calendar.SECOND); - todayTimer.setInitialDelay(secondsTillTomorrow * 1000); - todayTimer.start(); - } - -//-------------------- action and listener - - - /** - * Commits the current selection.

                - * - * Resets the model's adjusting property to false - * and fires an ActionEvent - * with the COMMIT_KEY action command. - * - * - * @see #cancelSelection() - * @see DateSelectionModel#setAdjusting(boolean) - */ - public void commitSelection() { - getSelectionModel().setAdjusting(false); - fireActionPerformed(COMMIT_KEY); - } - - /** - * Cancels the selection.

                - * - * Resets the model's adjusting property to - * false and fires an ActionEvent with the CANCEL_KEY action command. - * - * @see #commitSelection - * @see DateSelectionModel#setAdjusting(boolean) - */ - public void cancelSelection() { - getSelectionModel().setAdjusting(false); - fireActionPerformed(CANCEL_KEY); - } - - /** - * Sets the component input map enablement property.

                - * - * If enabled, the keybinding for WHEN_IN_FOCUSED_WINDOW are - * installed, otherwise not. Changing this property will - * install/clear the corresponding key bindings. Typically, clients - * which want to use the monthview in a popup, should enable these.

                - * - * The default value is false. - * - * @param enabled boolean to indicate whether the component - * input map should be enabled. - * @see #isComponentInputMapEnabled() - */ - public void setComponentInputMapEnabled(boolean enabled) { - boolean old = isComponentInputMapEnabled(); - this.componentInputMapEnabled = enabled; - firePropertyChange("componentInputMapEnabled", old, isComponentInputMapEnabled()); - } - - /** - * Returns the componentInputMapEnabled property. - * - * @return a boolean indicating whether the component input map is - * enabled. - * @see #setComponentInputMapEnabled(boolean) - * - */ - public boolean isComponentInputMapEnabled() { - return componentInputMapEnabled; - } - - /** - * Adds an ActionListener. - *

                - * The ActionListener will receive an ActionEvent with its actionCommand - * set to COMMIT_KEY or CANCEL_KEY after the selection has been committed - * or canceled, respectively. - *

                - * - * Note that actionEvents are typically fired after a dedicated user gesture - * to end an ongoing selectin (like ENTER, ESCAPE) or after explicit programmatic - * commits/cancels. It is usually not fired after each change to the selection state. - * Client code which wants to be notified about all selection changes should - * register a DateSelectionListener to the DateSelectionModel. - * - * @param l The ActionListener that is to be notified - * - * @see #commitSelection() - * @see #cancelSelection() - * @see #getSelectionModel() - */ - public void addActionListener(ActionListener l) { - listenerMap.add(ActionListener.class, l); - } - - /** - * Removes an ActionListener. - * - * @param l The ActionListener to remove. - */ - public void removeActionListener(ActionListener l) { - listenerMap.remove(ActionListener.class, l); - } - - @Override - @SuppressWarnings("unchecked") - public T[] getListeners(Class listenerType) { - java.util.List listeners = listenerMap.getListeners(listenerType); - T[] result; - if (!listeners.isEmpty()) { - //noinspection unchecked - result = (T[]) java.lang.reflect.Array.newInstance(listenerType, listeners.size()); - result = listeners.toArray(result); - } else { - result = super.getListeners(listenerType); - } - return result; - } - - /** - * Creates and fires an ActionEvent with the given action - * command to all listeners. - * - * @param actionCommand the command for the created. - */ - protected void fireActionPerformed(String actionCommand) { - ActionListener[] listeners = getListeners(ActionListener.class); - ActionEvent e = null; - - for (ActionListener listener : listeners) { - if (e == null) { - e = new ActionEvent(JXMonthView.this, - ActionEvent.ACTION_PERFORMED, - actionCommand); - } - listener.actionPerformed(e); - } - } - - -//--- deprecated code - NOTE: these methods will be removed soon! - - /** - * @deprecated pre-0.9.5 - this is kept as a reminder only, don't - * use! we can make this private or comment it out after - * next version - */ - @Deprecated - protected void cleanupWeekSelectionDates(Date startDate, Date endDate) { - int count = 1; - cal.setTime(startDate); - while (cal.getTimeInMillis() < endDate.getTime()) { - cal.add(Calendar.DAY_OF_MONTH, 1); - count++; - } - - if (count > JXMonthView.DAYS_IN_WEEK) { - // Move the start date to the first day of the week. - cal.setTime(startDate); - int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK); - int firstDayOfWeek = getFirstDayOfWeek(); - int daysFromStart = dayOfWeek - firstDayOfWeek; - if (daysFromStart < 0) { - daysFromStart += JXMonthView.DAYS_IN_WEEK; - } - cal.add(Calendar.DAY_OF_MONTH, -daysFromStart); - - modifiedStartDate = cal.getTime(); - - // Move the end date to the last day of the week. - cal.setTime(endDate); - dayOfWeek = cal.get(Calendar.DAY_OF_WEEK); - int lastDayOfWeek = firstDayOfWeek - 1; - if (lastDayOfWeek == 0) { - lastDayOfWeek = Calendar.SATURDAY; - } - int daysTillEnd = lastDayOfWeek - dayOfWeek; - if (daysTillEnd < 0) { - daysTillEnd += JXMonthView.DAYS_IN_WEEK; - } - cal.add(Calendar.DAY_OF_MONTH, daysTillEnd); - modifiedEndDate = cal.getTime(); - } - } - - - - - -} diff --git a/src/main/java/org/jdesktop/swingx/JXMultiSplitPane.java b/src/main/java/org/jdesktop/swingx/JXMultiSplitPane.java deleted file mode 100644 index cf07cb11f4..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXMultiSplitPane.java +++ /dev/null @@ -1,590 +0,0 @@ -/* - * $Id: JXMultiSplitPane.java 4147 2012-02-01 17:13:24Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.MultiSplitLayout.Divider; -import org.jdesktop.swingx.MultiSplitLayout.Node; -import org.jdesktop.swingx.painter.AbstractPainter; -import org.jdesktop.swingx.painter.Painter; - -import javax.accessibility.AccessibleContext; -import javax.accessibility.AccessibleRole; -import javax.swing.*; -import javax.swing.event.MouseInputAdapter; -import java.awt.*; -import java.awt.event.KeyEvent; -import java.awt.event.KeyListener; -import java.awt.event.MouseEvent; -import java.io.Serializable; - -/** - * - *

                - * All properties in this class are bound: when a properties value - * is changed, all PropertyChangeListeners are fired. - * - * @author Hans Muller - * @author Luan O'Carroll - */ -@JavaBean -public class JXMultiSplitPane extends JPanel implements BackgroundPaintable { - private AccessibleContext accessibleContext = null; - private boolean continuousLayout = true; - private DividerPainter dividerPainter = new DefaultDividerPainter(); - private Painter backgroundPainter; - private boolean paintBorderInsets; - - /** - * Creates a MultiSplitPane with it's LayoutManager set to - * to an empty MultiSplitLayout. - */ - public JXMultiSplitPane() { - this(new MultiSplitLayout()); - } - - /** - * Creates a MultiSplitPane. - * @param layout the new split pane's layout - */ - public JXMultiSplitPane( MultiSplitLayout layout ) { - super(layout); - InputHandler inputHandler = new InputHandler(); - addMouseListener(inputHandler); - addMouseMotionListener(inputHandler); - addKeyListener(inputHandler); - setFocusable(true); - } - - /** - * A convenience method that returns the layout manager cast - * to MutliSplitLayout. - * - * @return this MultiSplitPane's layout manager - * @see java.awt.Container#getLayout - * @see #setModel - */ - public final MultiSplitLayout getMultiSplitLayout() { - return (MultiSplitLayout)getLayout(); - } - - /** - * A convenience method that sets the MultiSplitLayout model. - * Equivalent to getMultiSplitLayout.setModel(model) - * - * @param model the root of the MultiSplitLayout model - * @see #getMultiSplitLayout - * @see MultiSplitLayout#setModel - */ - public final void setModel(Node model) { - getMultiSplitLayout().setModel(model); - } - - /** - * A convenience method that sets the MultiSplitLayout dividerSize - * property. Equivalent to - * getMultiSplitLayout().setDividerSize(newDividerSize). - * - * @param dividerSize the value of the dividerSize property - * @see #getMultiSplitLayout - * @see MultiSplitLayout#setDividerSize - */ - public final void setDividerSize(int dividerSize) { - getMultiSplitLayout().setDividerSize(dividerSize); - } - - /** - * A convenience method that returns the MultiSplitLayout dividerSize - * property. Equivalent to - * getMultiSplitLayout().getDividerSize(). - * - * @see #getMultiSplitLayout - * @see MultiSplitLayout#getDividerSize - */ - public final int getDividerSize() { - return getMultiSplitLayout().getDividerSize(); - } - - /** - * Sets the value of the continuousLayout property. - * If true, then the layout is revalidated continuously while - * a divider is being moved. The default value of this property - * is true. - * - * @param continuousLayout value of the continuousLayout property - * @see #isContinuousLayout - */ - public void setContinuousLayout(boolean continuousLayout) { - boolean oldContinuousLayout = isContinuousLayout(); - this.continuousLayout = continuousLayout; - firePropertyChange("continuousLayout", oldContinuousLayout, isContinuousLayout()); - } - - /** - * Returns true if dragging a divider only updates - * the layout when the drag gesture ends (typically, when the - * mouse button is released). - * - * @return the value of the continuousLayout property - * @see #setContinuousLayout - */ - public boolean isContinuousLayout() { - return continuousLayout; - } - - /** - * Returns the Divider that's currently being moved, typically - * because the user is dragging it, or null. - * - * @return the Divider that's being moved or null. - */ - public Divider activeDivider() { - return dragDivider; - } - - /** - * Draws a single Divider. Typically used to specialize the - * way the active Divider is painted. - * - * @see #getDividerPainter - * @see #setDividerPainter - */ - public static abstract class DividerPainter extends AbstractPainter { - } - - private class DefaultDividerPainter extends DividerPainter implements Serializable { - @Override - protected void doPaint(Graphics2D g, Divider divider, int width, int height) { - if ((divider == activeDivider()) && !isContinuousLayout()) { - g.setColor(Color.black); - g.fillRect(0, 0, width, height); - } - } - } - - /** - * The DividerPainter that's used to paint Dividers on this MultiSplitPane. - * This property may be null. - * - * @return the value of the dividerPainter Property - * @see #setDividerPainter - */ - public DividerPainter getDividerPainter() { - return dividerPainter; - } - - /** - * Sets the DividerPainter that's used to paint Dividers on this - * MultiSplitPane. The default DividerPainter only draws - * the activeDivider (if there is one) and then, only if - * continuousLayout is false. The value of this property is - * used by the paintChildren method: Dividers are painted after - * the MultiSplitPane's children have been rendered so that - * the activeDivider can appear "on top of" the children. - * - * @param dividerPainter the value of the dividerPainter property, can be null - * @see #paintChildren - * @see #activeDivider - */ - public void setDividerPainter(DividerPainter dividerPainter) { - DividerPainter old = getDividerPainter(); - this.dividerPainter = dividerPainter; - firePropertyChange("dividerPainter", old, getDividerPainter()); - } - - /** - * Calls the UI delegate's paint method, if the UI delegate - * is non-null. We pass the delegate a copy of the - * Graphics object to protect the rest of the - * paint code from irrevocable changes - * (for example, Graphics.translate). - *

                - * If you override this in a subclass you should not make permanent - * changes to the passed in Graphics. For example, you - * should not alter the clip Rectangle or modify the - * transform. If you need to do these operations you may find it - * easier to create a new Graphics from the passed in - * Graphics and manipulate it. Further, if you do not - * invoker super's implementation you must honor the opaque property, - * that is - * if this component is opaque, you must completely fill in the background - * in a non-opaque color. If you do not honor the opaque property you - * will likely see visual artifacts. - *

                - * The passed in Graphics object might - * have a transform other than the identify transform - * installed on it. In this case, you might get - * unexpected results if you cumulatively apply - * another transform. - * - * @param g the Graphics object to protect - * @see #paint(Graphics) - * @see javax.swing.plaf.ComponentUI - */ - @Override - protected void paintComponent(Graphics g) - { - if (backgroundPainter == null) { - super.paintComponent(g); - } else { - if (isOpaque()) { - super.paintComponent(g); - } - - Graphics2D g2 = (Graphics2D) g.create(); - - try { - SwingXUtilities.paintBackground(this, g2); - } finally { - g2.dispose(); - } - - getUI().paint(g, this); - } - } - - /** - * Specifies a Painter to use to paint the background of this JXPanel. - * If p is not null, then setOpaque(false) will be called - * as a side effect. A component should not be opaque if painters are - * being used, because Painters may paint transparent pixels or not - * paint certain pixels, such as around the border insets. - */ - @Override - public void setBackgroundPainter(Painter p) - { - Painter old = getBackgroundPainter(); - this.backgroundPainter = p; - - if (p != null) { - setOpaque(false); - } - - firePropertyChange("backgroundPainter", old, getBackgroundPainter()); - repaint(); - } - - @Override - public Painter getBackgroundPainter() { - return backgroundPainter; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isPaintBorderInsets() { - return paintBorderInsets; - } - - /** - * {@inheritDoc} - */ - @Override - public void setPaintBorderInsets(boolean paintBorderInsets) { - boolean oldValue = isPaintBorderInsets(); - this.paintBorderInsets = paintBorderInsets; - firePropertyChange("paintBorderInsets", oldValue, isPaintBorderInsets()); - } - - /** - * Uses the DividerPainter (if any) to paint each Divider that - * overlaps the clip Rectangle. This is done after the call to - * super.paintChildren() so that Dividers can be - * rendered "on top of" the children. - *

                - * {@inheritDoc} - */ - @Override - protected void paintChildren(Graphics g) { - super.paintChildren(g); - DividerPainter dp = getDividerPainter(); - Rectangle clipR = g.getClipBounds(); - if ((dp != null) && (clipR != null)) { - MultiSplitLayout msl = getMultiSplitLayout(); - if ( msl.hasModel()) { - for(Divider divider : msl.dividersThatOverlap(clipR)) { - Rectangle bounds = divider.getBounds(); - Graphics cg = g.create( bounds.x, bounds.y, bounds.width, bounds.height ); - try { - dp.paint((Graphics2D)cg, divider, bounds.width, bounds.height ); - } finally { - cg.dispose(); - } - } - } - } - } - - private boolean dragUnderway = false; - private Divider dragDivider = null; - private Rectangle initialDividerBounds = null; - private boolean oldFloatingDividers = true; - private int dragOffsetX = 0; - private int dragOffsetY = 0; - private int dragMin = -1; - private int dragMax = -1; - - private void startDrag(int mx, int my) { - requestFocusInWindow(); - MultiSplitLayout msl = getMultiSplitLayout(); - Divider divider = msl.dividerAt(mx, my); - if (divider != null) { - Node prevNode = divider.previousSibling(); - Node nextNode = divider.nextSibling(); - if ((prevNode == null) || (nextNode == null)) { - dragUnderway = false; - } - else { - initialDividerBounds = divider.getBounds(); - dragOffsetX = mx - initialDividerBounds.x; - dragOffsetY = my - initialDividerBounds.y; - dragDivider = divider; - - Rectangle prevNodeBounds = prevNode.getBounds(); - Rectangle nextNodeBounds = nextNode.getBounds(); - if (dragDivider.isVertical()) { - dragMin = prevNodeBounds.x; - dragMax = nextNodeBounds.x + nextNodeBounds.width; - dragMax -= dragDivider.getBounds().width; - if ( msl.getLayoutMode() == MultiSplitLayout.USER_MIN_SIZE_LAYOUT ) - dragMax -= msl.getUserMinSize(); - } - else { - dragMin = prevNodeBounds.y; - dragMax = nextNodeBounds.y + nextNodeBounds.height; - dragMax -= dragDivider.getBounds().height; - if ( msl.getLayoutMode() == MultiSplitLayout.USER_MIN_SIZE_LAYOUT ) - dragMax -= msl.getUserMinSize(); - } - - if ( msl.getLayoutMode() == MultiSplitLayout.USER_MIN_SIZE_LAYOUT ) { - dragMin = dragMin + msl.getUserMinSize(); - } - else { - if (dragDivider.isVertical()) { - dragMin = Math.max( dragMin, dragMin + getMinNodeSize(msl,prevNode).width ); - dragMax = Math.min( dragMax, dragMax - getMinNodeSize(msl,nextNode).width ); - - Dimension maxDim = getMaxNodeSize(msl,prevNode); - if ( maxDim != null ) - dragMax = Math.min( dragMax, prevNodeBounds.x + maxDim.width ); - } - else { - dragMin = Math.max( dragMin, dragMin + getMinNodeSize(msl,prevNode).height ); - dragMax = Math.min( dragMax, dragMax - getMinNodeSize(msl,nextNode).height ); - - Dimension maxDim = getMaxNodeSize(msl,prevNode); - if ( maxDim != null ) - dragMax = Math.min( dragMax, prevNodeBounds.y + maxDim.height ); - } - } - - oldFloatingDividers = getMultiSplitLayout().getFloatingDividers(); - getMultiSplitLayout().setFloatingDividers(false); - dragUnderway = true; - } - } - else { - dragUnderway = false; - } - } - - /** - * Set the maximum node size. This method can be overridden to limit the - * size of a node during a drag operation on a divider. When implementing - * this method in a subclass the node instance should be checked, for - * example: - * - * class MyMultiSplitPane extends JXMultiSplitPane - * { - * protected Dimension getMaxNodeSize( MultiSplitLayout msl, Node n ) - * { - * if (( n instanceof Leaf ) && ((Leaf)n).getName().equals( "top" )) - * return msl.maximumNodeSize( n ); - * return null; - * } - * } - * - * @param msl the MultiSplitLayout used by this pane - * @param n the node being resized - * @return the maximum size or null (by default) to ignore the maximum size. - */ - protected Dimension getMaxNodeSize( MultiSplitLayout msl, Node n ) { - return null; - } - - /** - * Set the minimum node size. This method can be overridden to limit the - * size of a node during a drag operation on a divider. - * @param msl the MultiSplitLayout used by this pane - * @param n the node being resized - * @return the maximum size or null (by default) to ignore the maximum size. - */ - protected Dimension getMinNodeSize( MultiSplitLayout msl, Node n ) { - return msl.minimumNodeSize(n); - } - - private void repaintDragLimits() { - Rectangle damageR = dragDivider.getBounds(); - if (dragDivider.isVertical()) { - damageR.x = dragMin; - damageR.width = dragMax - dragMin; - } - else { - damageR.y = dragMin; - damageR.height = dragMax - dragMin; - } - repaint(damageR); - } - - private void updateDrag(int mx, int my) { - if (!dragUnderway) { - return; - } - Rectangle oldBounds = dragDivider.getBounds(); - Rectangle bounds = new Rectangle(oldBounds); - if (dragDivider.isVertical()) { - bounds.x = mx - dragOffsetX; - bounds.x = Math.max(bounds.x, dragMin ); - bounds.x = Math.min(bounds.x, dragMax); - } - else { - bounds.y = my - dragOffsetY; - bounds.y = Math.max(bounds.y, dragMin ); - bounds.y = Math.min(bounds.y, dragMax); - } - dragDivider.setBounds(bounds); - if (isContinuousLayout()) { - revalidate(); - repaintDragLimits(); - } - else { - repaint(oldBounds.union(bounds)); - } - } - - private void clearDragState() { - dragDivider = null; - initialDividerBounds = null; - oldFloatingDividers = true; - dragOffsetX = dragOffsetY = 0; - dragMin = dragMax = -1; - dragUnderway = false; - } - - private void finishDrag(int x, int y) { - if (dragUnderway) { - clearDragState(); - if (!isContinuousLayout()) { - revalidate(); - repaint(); - } - } - setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); - } - - private void cancelDrag() { - if (dragUnderway) { - dragDivider.setBounds(initialDividerBounds); - getMultiSplitLayout().setFloatingDividers(oldFloatingDividers); - setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); - repaint(); - revalidate(); - clearDragState(); - } - } - - private void updateCursor(int x, int y, boolean show) { - if (dragUnderway) { - return; - } - int cursorID = Cursor.DEFAULT_CURSOR; - if (show) { - Divider divider = getMultiSplitLayout().dividerAt(x, y); - if (divider != null) { - cursorID = (divider.isVertical()) ? - Cursor.E_RESIZE_CURSOR : - Cursor.N_RESIZE_CURSOR; - } - } - setCursor(Cursor.getPredefinedCursor(cursorID)); - } - - - private class InputHandler extends MouseInputAdapter implements KeyListener { - - @Override - public void mouseEntered(MouseEvent e) { - updateCursor(e.getX(), e.getY(), true); - } - - @Override - public void mouseMoved(MouseEvent e) { - updateCursor(e.getX(), e.getY(), true); - } - - @Override - public void mouseExited(MouseEvent e) { - updateCursor(e.getX(), e.getY(), false); - } - - @Override - public void mousePressed(MouseEvent e) { - startDrag(e.getX(), e.getY()); - } - @Override - public void mouseReleased(MouseEvent e) { - finishDrag(e.getX(), e.getY()); - } - @Override - public void mouseDragged(MouseEvent e) { - updateDrag(e.getX(), e.getY()); - } - @Override - public void keyPressed(KeyEvent e) { - if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { - cancelDrag(); - } - } - @Override - public void keyReleased(KeyEvent e) { } - - @Override - public void keyTyped(KeyEvent e) { } - } - - @Override - public AccessibleContext getAccessibleContext() { - if( accessibleContext == null ) { - accessibleContext = new AccessibleMultiSplitPane(); - } - return accessibleContext; - } - - protected class AccessibleMultiSplitPane extends AccessibleJPanel { - @Override - public AccessibleRole getAccessibleRole() { - return AccessibleRole.SPLIT_PANE; - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/JXMultiThumbSlider.java b/src/main/java/org/jdesktop/swingx/JXMultiThumbSlider.java deleted file mode 100644 index 4146dc2205..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXMultiThumbSlider.java +++ /dev/null @@ -1,404 +0,0 @@ -/* - * $Id: JXMultiThumbSlider.java 4147 2012-02-01 17:13:24Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.multislider.*; -import org.jdesktop.swingx.plaf.LookAndFeelAddons; -import org.jdesktop.swingx.plaf.MultiThumbSliderAddon; -import org.jdesktop.swingx.plaf.MultiThumbSliderUI; - -import javax.swing.*; -import javax.swing.event.MouseInputAdapter; -import java.awt.*; -import java.awt.event.MouseEvent; -import java.util.ArrayList; -import java.util.List; - -/** - *

                A slider which can have multiple control points or Thumbs

                - *

                The thumbs each represent a value between the minimum and maximum values - * of the slider. Thumbs can pass each other when being dragged. Thumbs have - * no default visual representation. To customize the look of the thumbs and the - * track behind the thumbs you must provide a ThumbRenderer and a TrackRenderer - * implementation. To listen for changes to the thumbs you must provide an - * implementation of ThumbDataListener. - * - * TODOs: - * add min/maxvalue convenience methods to jxmultithumbslider - * add plafs for windows, mac, and basic (if necessary) - * make way to properly control the height. - * hide the inner thumb component - * - * @author joshy - */ -@JavaBean -public class JXMultiThumbSlider extends JComponent { - public static final String uiClassID = "MultiThumbSliderUI"; - - private ThumbDataListener tdl; - - private List thumbs; - - private ThumbRenderer thumbRenderer; - - private TrackRenderer trackRenderer; - - private MultiThumbModel model; - - private List listeners = new ArrayList(); - - private ThumbComp selected; - - static { - LookAndFeelAddons.contribute(new MultiThumbSliderAddon()); - } - - /** Creates a new instance of JMultiThumbSlider */ - public JXMultiThumbSlider() { - thumbs = new ArrayList(); - setLayout(null); - - tdl = new ThumbHandler(); - - setModel(new DefaultMultiThumbModel()); - MultiThumbMouseListener mia = new MultiThumbMouseListener(); - addMouseListener(mia); - addMouseMotionListener(mia); - - Dimension dim = new Dimension(60,16); - setPreferredSize(dim); - setSize(dim); - setMinimumSize(new Dimension(30,16)); - updateUI(); - } - - /** - * {@inheritDoc} - */ - @Override - public String getUIClassID() { - return uiClassID; - } - - public MultiThumbSliderUI getUI() { - return (MultiThumbSliderUI)ui; - } - - public void setUI(MultiThumbSliderUI ui) { - super.setUI(ui); - } - - @Override - public void updateUI() { - setUI((MultiThumbSliderUI)LookAndFeelAddons.getUI(this, MultiThumbSliderUI.class)); - invalidate(); - } - - @Override - protected void paintComponent(Graphics g) { - if(isVisible()) { - if(trackRenderer != null) { - JComponent comp = trackRenderer.getRendererComponent(this); - add(comp); - comp.paint(g); - remove(comp); - } else { - paintRange((Graphics2D)g); - } - } - } - - private void paintRange(Graphics2D g) { - g.setColor(Color.blue); - g.fillRect(0,0,getWidth(),getHeight()); - } - - private float getThumbValue(int thumbIndex) { - return getModel().getThumbAt(thumbIndex).getPosition(); - } - - private float getThumbValue(ThumbComp thumb) { - return getThumbValue(thumbs.indexOf(thumb)); - } - - private int getThumbIndex(ThumbComp thumb) { - return thumbs.indexOf(thumb); - } - - private void clipThumbPosition(ThumbComp thumb) { - if(getThumbValue(thumb) < getModel().getMinimumValue()) { - getModel().getThumbAt(getThumbIndex(thumb)).setPosition( - getModel().getMinimumValue()); - } - if(getThumbValue(thumb) > getModel().getMaximumValue()) { - getModel().getThumbAt(getThumbIndex(thumb)).setPosition( - getModel().getMaximumValue()); - } - } - - public ThumbRenderer getThumbRenderer() { - return thumbRenderer; - } - - public void setThumbRenderer(ThumbRenderer thumbRenderer) { - this.thumbRenderer = thumbRenderer; - } - - public TrackRenderer getTrackRenderer() { - return trackRenderer; - } - - public void setTrackRenderer(TrackRenderer trackRenderer) { - this.trackRenderer = trackRenderer; - } - - public float getMinimumValue() { - return getModel().getMinimumValue(); - } - - public void setMinimumValue(float minimumValue) { - getModel().setMinimumValue(minimumValue); - } - - public float getMaximumValue() { - return getModel().getMaximumValue(); - } - - public void setMaximumValue(float maximumValue) { - getModel().setMaximumValue(maximumValue); - } - - private void setThumbPositionByX(ThumbComp selected) { - float range = getModel().getMaximumValue()-getModel().getMinimumValue(); - int x = selected.getX(); - // adjust to the center of the thumb - x += selected.getWidth()/2; - // adjust for the leading space on the slider - x -= selected.getWidth()/2; - - int w = getWidth(); - // adjust for the leading and trailing space on the slider - w -= selected.getWidth(); - float delta = ((float)x)/((float)w); - int thumb_index = getThumbIndex(selected); - float value = delta*range; - getModel().getThumbAt(thumb_index).setPosition(value); - //getModel().setPositionAt(thumb_index,value); - clipThumbPosition(selected); - } - - private void setThumbXByPosition(ThumbComp thumb, float pos) { - float lp = getWidth()-thumb.getWidth(); - float lu = getModel().getMaximumValue()-getModel().getMinimumValue(); - float tp = (pos*lp)/lu; - thumb.setLocation((int)tp-thumb.getWidth()/2 + thumb.getWidth()/2, thumb.getY()); - } - - private void recalc() { - for(ThumbComp th : thumbs) { - setThumbXByPosition(th,getModel().getThumbAt(getThumbIndex(th)).getPosition()); - //getPositionAt(getThumbIndex(th))); - } - } - - @Override - public void setBounds(int x, int y, int w, int h) { - super.setBounds(x,y,w,h); - recalc(); - } - - public JComponent getSelectedThumb() { - return selected; - } - - public int getSelectedIndex() { - return getThumbIndex(selected); - } - - public MultiThumbModel getModel() { - return model; - } - - public void setModel(MultiThumbModel model) { - if(this.model != null) { - this.model.removeThumbDataListener(tdl); - } - this.model = model; - this.model.addThumbDataListener(tdl); - } - - public void addMultiThumbListener(ThumbListener listener) { - listeners.add(listener); - } - - private class MultiThumbMouseListener extends MouseInputAdapter { - @Override - public void mousePressed(MouseEvent evt) { - ThumbComp handle = findHandle(evt); - if(handle != null) { - selected = handle; - selected.setSelected(true); - int thumb_index = getThumbIndex(selected); - for(ThumbListener tl : listeners) { - tl.thumbSelected(thumb_index); - } - repaint(); - } else { - selected = null; - for(ThumbListener tl : listeners) { - tl.thumbSelected(-1); - } - repaint(); - } - for(ThumbListener tl : listeners) { - tl.mousePressed(evt); - } - } - - @Override - public void mouseReleased(MouseEvent evt) { - if(selected != null) { - selected.setSelected(false); - } - } - - @Override - public void mouseDragged(MouseEvent evt) { - if(selected != null) { - int nx = (int)evt.getPoint().getX()- selected.getWidth()/2; - if(nx < 0) { - nx = 0; - } - if(nx > getWidth()-selected.getWidth()) { - nx = getWidth()-selected.getWidth(); - } - selected.setLocation(nx,(int)selected.getLocation().getY()); - setThumbPositionByX(selected); - int thumb_index = getThumbIndex(selected); - //log.fine("still dragging: " + thumb_index); - for(ThumbListener mtl : listeners) { - mtl.thumbMoved(thumb_index,getModel().getThumbAt(thumb_index).getPosition()); - //getPositionAt(thumb_index)); - } - repaint(); - } - } - - - private ThumbComp findHandle(MouseEvent evt) { - for(ThumbComp hand : thumbs) { - Point p2 = new Point(); - p2.setLocation(evt.getPoint().getX() - hand.getX(), - evt.getPoint().getY() - hand.getY()); - if(hand.contains(p2)) { - return hand; - } - } - return null; - } - } - - private static class ThumbComp extends JComponent { - - private JXMultiThumbSlider slider; - - public ThumbComp(JXMultiThumbSlider slider) { - this.slider = slider; - Dimension dim = new Dimension(10,10);//slider.getHeight()); - /*if(slider.getThumbRenderer() != null) { - JComponent comp = getRenderer(); - dim = comp.getPreferredSize(); - }*/ - setSize(dim); - setMinimumSize(dim); - setPreferredSize(dim); - setMaximumSize(dim); - setBackground(Color.white); - } - - @Override - public void paintComponent(Graphics g) { - if(slider.getThumbRenderer() != null) { - JComponent comp = getRenderer(); - comp.setSize(this.getSize()); - comp.paint(g); - } else { - g.setColor(getBackground()); - g.fillRect(0,0,getWidth(),getHeight()); - if(isSelected()) { - g.setColor(Color.black); - g.drawRect(0,0,getWidth()-1,getHeight()-1); - } - } - } - - private JComponent getRenderer() { - return slider.getThumbRenderer(). - getThumbRendererComponent(slider,slider.getThumbIndex(this),isSelected()); - } - - private boolean selected; - - public boolean isSelected() { - return selected; - } - - public void setSelected(boolean selected) { - this.selected = selected; - } - } - - private class ThumbHandler implements ThumbDataListener { - @Override - public void positionChanged(ThumbDataEvent e) { - ThumbComp comp = thumbs.get(e.getIndex()); - clipThumbPosition(comp); - setThumbXByPosition(comp, e.getThumb().getPosition()); - repaint(); - } - - @Override - public void thumbAdded(ThumbDataEvent evt) { - ThumbComp thumb = new ThumbComp(JXMultiThumbSlider.this); - thumb.setLocation(0, 0); - add(thumb); - thumbs.add(evt.getIndex(), thumb); - clipThumbPosition(thumb); - setThumbXByPosition(thumb, evt.getThumb().getPosition()); - repaint(); - } - - @Override - public void thumbRemoved(ThumbDataEvent evt) { - ThumbComp thumb = thumbs.get(evt.getIndex()); - remove(thumb); - thumbs.remove(thumb); - repaint(); - } - - @Override - public void valueChanged(ThumbDataEvent e) { - repaint(); - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/JXPanel.java b/src/main/java/org/jdesktop/swingx/JXPanel.java deleted file mode 100644 index 8225483337..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXPanel.java +++ /dev/null @@ -1,693 +0,0 @@ -/* - * $Id: JXPanel.java 4280 2013-02-21 17:35:03Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.painter.AbstractPainter; -import org.jdesktop.swingx.painter.Painter; -import org.jdesktop.swingx.util.Contract; - -import javax.swing.*; -import javax.swing.plaf.synth.SynthLookAndFeel; -import java.awt.*; -import java.awt.image.BufferedImage; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.logging.Logger; - -import static org.jdesktop.swingx.util.GraphicsUtilities.createCompatibleTranslucentImage; - -/** - *

                - * An extended {@code JPanel} that provides additional features. - *

                - *

                Scrollable

                - *

                - * {@code JXPanel} is {@link Scrollable} by default. It provides reasonable implementations of all - * of the interface methods. In addition, it supports the setting of common scrolling approaches - * defined in {@link ScrollableSizeHint}. - *

                - *

                Alpha Support

                - *

                - * {@code JXPanel} has full alpha-channel support. This means that the {@code JXPanel} can be made - * fully or partially transparent. This means that the JXPanel and all of its children will behave - * as a single paint at the specified alpha value. Cauton: best practice is to use - * either alpha support or opacity support, but not both. See the documentation on the methods for - * further information. - *

                - *

                - * A transparency example, this following code will show the black background of the parent: - * - *

                - * JXPanel panel = new JXPanel();
                - * panel.add(new JButton("Push Me"));
                - * panel.setAlpha(.5f);
                - * 
                - * container.setBackground(Color.BLACK);
                - * container.add(panel);
                - * 
                - * - *

                - *

                Painter Support

                - *

                - * {@code JXPanel} has support for {@linkplain Painter}s. - *

                - *

                - * A painter example, this following code will show how to add a simple painter: - * - *

                - * JXPanel panel = new JXPanel();
                - * panel.setBackgroundPainter(new PinstripePainter());
                - * 
                - * - *

                - * - * @author rbair - * @see Scrollable - * @see Painter - */ -@JavaBean -@SuppressWarnings("nls") -public class JXPanel extends JPanel implements AlphaPaintable, BackgroundPaintable, Scrollable { -// private boolean scrollableTracksViewportHeight = true; -// private boolean scrollableTracksViewportWidth = true; - - private ScrollableSizeHint scrollableWidthHint = ScrollableSizeHint.FIT; - private ScrollableSizeHint scrollableHeightHint = ScrollableSizeHint.FIT; - - /** - * The alpha level for this component. - */ - private volatile float alpha = 1.0f; - /** - * If the old alpha value was 1.0, I keep track of the opaque setting because - * a translucent component is not opaque, but I want to be able to restore - * opacity to its default setting if the alpha is 1.0. Honestly, I don't know - * if this is necessary or not, but it sounded good on paper :) - *

                TODO: Check whether this variable is necessary or not

                - */ - private boolean oldOpaque; - - private float oldAlpha = 1f; - - /** - * Indicates whether this component should inherit its parent alpha value - */ - private boolean inheritAlpha = true; - /** - * Specifies the Painter to use for painting the background of this panel. - * If no painter is specified, the normal painting routine for JPanel - * is called. Old behavior is also honored for the time being if no - * backgroundPainter is specified - */ - @SuppressWarnings("rawtypes") - private Painter backgroundPainter; - - private boolean paintBorderInsets = true; - - /** - * The listener installed on the current backgroundPainter, if any. - */ - private PropertyChangeListener painterChangeListener; - - /** - * Creates a new JXPanel with a double buffer - * and a flow layout. - */ - public JXPanel() { - } - - /** - * Creates a new JXPanel with FlowLayout - * and the specified buffering strategy. - * If isDoubleBuffered is true, the JXPanel - * will use a double buffer. - * - * @param isDoubleBuffered a boolean, true for double-buffering, which - * uses additional memory space to achieve fast, flicker-free - * updates - */ - public JXPanel(boolean isDoubleBuffered) { - super(isDoubleBuffered); - } - - /** - * Create a new buffered JXPanel with the specified layout manager - * - * @param layout the LayoutManager to use - */ - public JXPanel(LayoutManager layout) { - super(layout); - } - - /** - * Creates a new JXPanel with the specified layout manager and buffering - * strategy. - * - * @param layout the LayoutManager to use - * @param isDoubleBuffered a boolean, true for double-buffering, which - * uses additional memory space to achieve fast, flicker-free - * updates - */ - public JXPanel(LayoutManager layout, boolean isDoubleBuffered) { - super(layout, isDoubleBuffered); - } - - /** - * {@inheritDoc} - *

                - * Setting the component to be opaque will reset the alpha setting to {@code 1f} (full - * opaqueness). Setting the component to be non-opaque will restore the previous alpha - * transparency. If the component is non-opaque with a fully-opaque alpha value ({@code 1f}), - * the behavior should be the same as as a {@code JPanel} that is non-opaque. - */ - @Override - public void setOpaque(boolean opaque) { - if (isPatch()) { - setOpaquePatch(opaque); - return; - } - if (opaque) { - oldAlpha = getAlpha(); - - if (oldAlpha < 1f) { - setAlpha(1f); - } else { - super.setOpaque(true); - repaint(); - } - } else if (getAlpha() == 1f) { - if (oldAlpha == 1f) { - super.setOpaque(false); - repaint(); - } else { - setAlpha(oldAlpha); - } - } - } - - @Override - public boolean isOpaque() { - if (isPatch()) { - return isOpaquePatch(); - } - return super.isOpaque(); - } - - /** - * {@inheritDoc} - */ - @Override - public float getAlpha() { - return alpha; - } - - /** - * {@inheritDoc} - */ - @Override - public void setAlpha(float alpha) { - if (isPatch()) { - setAlphaPatch(alpha); - return; - } - if (alpha < 0f || alpha > 1f) { - throw new IllegalArgumentException("invalid alpha value " + alpha); - } - - float oldValue = getAlpha(); - this.alpha = alpha; - - if (getAlpha() < 1f) { - if (oldValue == 1) { - //it used to be 1, but now is not. Save the oldOpaque - oldOpaque = isOpaque(); - super.setOpaque(false); - } - } else { - //restore the oldOpaque if it was true (since opaque is false now) - if (oldOpaque) { - super.setOpaque(true); - } - } - - firePropertyChange("alpha", oldValue, getAlpha()); - repaint(); - } - - /** - * experimental version: doesn't tweak opaque - * - * called if isPatch - * @param alpha - */ - private void setAlphaPatch(float alpha) { - if (alpha < 0f || alpha > 1f) { - throw new IllegalArgumentException("invalid alpha value " + alpha); - } - - float oldValue = getAlpha(); - this.alpha = alpha; - - if (getAlpha() < 1f) { - if (oldValue == 1) { - //it used to be 1, but now is not. Save the oldOpaque - oldOpaque = isOpaque(); -// super.setOpaque(false); - } - - } else { - //restore the oldOpaque if it was true (since opaque is false now) - if (oldOpaque) { -// super.setOpaque(true); - } - } - - firePropertyChange("alpha", oldValue, getAlpha()); - repaint(); - } - - /** - * {@inheritDoc} - */ - @Override - public float getEffectiveAlpha() { - float a = getAlpha(); - - if (isInheritAlpha()) { - for (Component c = getParent(); c != null; c = c.getParent()) { - if (c instanceof AlphaPaintable) { - a = Math.min(((AlphaPaintable) c).getEffectiveAlpha(), a); - break; - } - } - } - - return a; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isInheritAlpha() { - return inheritAlpha; - } - - /** - * {@inheritDoc} - */ - @Override - public void setInheritAlpha(boolean val) { - boolean oldValue = isInheritAlpha(); - inheritAlpha = val; - firePropertyChange("inheritAlpha", oldValue, isInheritAlpha()); - } - - /** - * Sets the horizontal sizing hint. The hint is used by the Scrollable implementation - * to service the getScrollableTracksWidth. - * - * @param hint the horizontal sizing hint, must not be null - * and must be vertical. - * - * @throws NullPointerException if null - * - * @see #setScrollableHeightHint(ScrollableSizeHint) - * @see ScrollableSizeHint - */ - public final void setScrollableWidthHint(ScrollableSizeHint hint) { - Contract.asNotNull(hint, "hint cannot be null"); - ScrollableSizeHint oldValue = getScrollableWidthHint(); - if (oldValue == hint) return; - this.scrollableWidthHint = hint; - revalidate(); - firePropertyChange("scrollableWidthHint", oldValue, getScrollableWidthHint()); - } - - - /** - * Sets the vertical sizing hint. The hint is used by the Scrollable implementation - * to service the getScrollableTracksHeight. - * - * @param hint the vertical sizing hint, must not be null - * and must be vertical. - * - * @throws NullPointerException if null - * - * @see #setScrollableWidthHint(ScrollableSizeHint) - * @see ScrollableSizeHint - */ - public final void setScrollableHeightHint(ScrollableSizeHint hint) { - Contract.asNotNull(hint, "hint cannot be null"); - ScrollableSizeHint oldValue = getScrollableHeightHint(); - if (oldValue == hint) return; - this.scrollableHeightHint = hint; - revalidate(); - firePropertyChange("scrollableHeightHint", oldValue, getScrollableHeightHint()); - } - - protected ScrollableSizeHint getScrollableWidthHint() { - return scrollableWidthHint; - } - - protected ScrollableSizeHint getScrollableHeightHint() { - return scrollableHeightHint; - - } - - /** - * {@inheritDoc} - */ - @Override - public boolean getScrollableTracksViewportHeight() { - return scrollableHeightHint.getTracksParentSize(this, SwingConstants.VERTICAL); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean getScrollableTracksViewportWidth() { - return scrollableWidthHint.getTracksParentSize(this, SwingConstants.HORIZONTAL); - } - - /** - * {@inheritDoc} - */ - @Override - public Dimension getPreferredScrollableViewportSize() { - return getPreferredSize(); - } - - /** - * {@inheritDoc} - */ - @Override - public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { - if (orientation == SwingConstants.VERTICAL) { - return visibleRect.height; - } else if (orientation == SwingConstants.HORIZONTAL) { - return visibleRect.width; - } else { - throw new IllegalArgumentException("invalid orientation"); //$NON-NLS-1$ - } - } - - /** - * {@inheritDoc} - */ - @Override - public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { - return getScrollableBlockIncrement(visibleRect, orientation, direction) / 10; - } - - /** - * - * Sets the vertical size tracking to either ScrollableSizeTrack.FIT or NONE, if the - * boolean parameter is true or false, respectively.

                - * - * NOTE: this method is kept for backward compatibility only, for full - * control use setScrollableHeightHint. - * - * @param scrollableTracksViewportHeight The scrollableTracksViewportHeight to set. - * - * @see #setScrollableHeightHint(ScrollableSizeHint) - */ - public void setScrollableTracksViewportHeight(boolean scrollableTracksViewportHeight) { - setScrollableHeightHint(scrollableTracksViewportHeight ? - ScrollableSizeHint.FIT : ScrollableSizeHint.NONE); - } - /** - * Sets the horizontal size tracking to either ScrollableSizeTrack.FIT or NONE, if the - * boolean parameter is true or false, respectively.

                - * - * NOTE: this method is kept for backward compatibility only, for full - * control use setScrollableWidthHint. - * - * - * @param scrollableTracksViewportWidth The scrollableTracksViewportWidth to set. - * - * @see #setScrollableWidthHint(ScrollableSizeHint) - */ - public void setScrollableTracksViewportWidth(boolean scrollableTracksViewportWidth) { - setScrollableWidthHint(scrollableTracksViewportWidth ? - ScrollableSizeHint.FIT : ScrollableSizeHint.NONE); - } - - /** - * Sets a Painter to use to paint the background of this JXPanel. - * - * @param p the new painter - * @see #getBackgroundPainter() - */ - @Override - public void setBackgroundPainter(Painter p) { - Painter old = getBackgroundPainter(); - if (old instanceof AbstractPainter) { - ((AbstractPainter) old).removePropertyChangeListener(painterChangeListener); - } - backgroundPainter = p; - if (backgroundPainter instanceof AbstractPainter) { - ((AbstractPainter) backgroundPainter).addPropertyChangeListener(getPainterChangeListener()); - } - firePropertyChange("backgroundPainter", old, getBackgroundPainter()); - repaint(); - } - - /** - * @return a listener for painter change events - */ - protected PropertyChangeListener getPainterChangeListener() { - if (painterChangeListener == null) { - painterChangeListener = new PropertyChangeListener() { - - @Override - public void propertyChange(PropertyChangeEvent evt) { - repaint(); - } - }; - } - return painterChangeListener; - } - - /** - * Returns the current background painter. The default value of this property - * is a painter which draws the normal JPanel background according to the current look and feel. - * @return the current painter - * @see #setBackgroundPainter(Painter) - * @see #isPaintBorderInsets() - */ - @Override - public Painter getBackgroundPainter() { - return backgroundPainter; - } - - /** - * Returns true if the background painter should paint where the border is - * or false if it should only paint inside the border. This property is - * true by default. This property affects the width, height, - * and initial transform passed to the background painter. - */ - @Override - public boolean isPaintBorderInsets() { - return paintBorderInsets; - } - - /** - * Sets the paintBorderInsets property. - * Set to true if the background painter should paint where the border is - * or false if it should only paint inside the border. This property is true by default. - * This property affects the width, height, - * and initial transform passed to the background painter. - * - * This is a bound property. - */ - @Override - public void setPaintBorderInsets(boolean paintBorderInsets) { - boolean old = this.isPaintBorderInsets(); - this.paintBorderInsets = paintBorderInsets; - firePropertyChange("paintBorderInsets", old, isPaintBorderInsets()); - } - - //support for Java 7 painting improvements - protected boolean isPaintingOrigin() { - return getAlpha() < 1f; - } - - /** - * Overridden paint method to take into account the alpha setting. - * - * @param g - * the Graphics context in which to paint - */ - @Override - public void paint(Graphics g) { - //short circuit painting if no transparency - if (getAlpha() == 1f) { - super.paint(g); - } else { - //the component is translucent, so we need to render to - //an intermediate image before painting - // TODO should we cache this image? repaint to same image unless size changes? - BufferedImage img = createCompatibleTranslucentImage(getWidth(), getHeight()); - Graphics2D gfx = img.createGraphics(); - - try { - super.paint(gfx); - } finally { - gfx.dispose(); - } - - Graphics2D g2d = (Graphics2D) g; - Composite oldComp = g2d.getComposite(); - - try { - Composite alphaComp = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, getEffectiveAlpha()); - g2d.setComposite(alphaComp); - //TODO should we cache the image? - g2d.drawImage(img, null, 0, 0); - } finally { - g2d.setComposite(oldComp); - } - } - } - - /** - * Overridden to provide Painter support. It will call backgroundPainter.paint() - * if it is not null, else it will call super.paintComponent(). - * - * @param g - * the Graphics context in which to paint - */ - @Override - @SuppressWarnings("unchecked") - protected void paintComponent(Graphics g) { - if (isPatch()) { - paintComponentPatch(g); - return; - } - Graphics2D g2 = (Graphics2D) g.create(); - - try { - // we should be painting the background behind the painter if we have one - // this prevents issues with buffer reuse where visual artifacts sneak in - if (isOpaque() || UIManager.getLookAndFeel() instanceof SynthLookAndFeel) { - //this will paint the foreground if a JXPanel subclass is - //unfortunate enough to have one - super.paintComponent(g2); - } else if (getAlpha() < 1f) { - g.setColor(getBackground()); - g.fillRect(0, 0, getWidth(), getHeight()); - } - - if (getBackgroundPainter() != null) { - if (isPaintBorderInsets()) { - getBackgroundPainter().paint(g2, this, getWidth(), getHeight()); - } else { - Insets insets = getInsets(); - g.translate(insets.left, insets.top); - getBackgroundPainter().paint(g2, this, getWidth() - insets.left - insets.right, - getHeight() - insets.top - insets.bottom); - g.translate(-insets.left, -insets.top); - } - } - - //force the foreground to paint again...workaround for folks that - //incorrectly extend JXPanel instead of JComponent - getUI().paint(g2, this); - } finally { - g2.dispose(); - } - } - -//--------------------- experimental patch - - protected boolean isPatch() { - return Boolean.TRUE.equals(UIManager.get("JXPanel.patch")); - } - - boolean fakeTransparent; - - protected void paintComponentPatch(Graphics g) { - Graphics2D g2 = (Graphics2D) g.create(); - - try { - if (isPaintingBackground()) { - g2.setColor(getBackground()); - g2.fillRect(0, 0, getWidth(), getHeight()); - } - if (getBackgroundPainter() != null) { - getBackgroundPainter().paint(g2, this, getWidth(), getHeight()); - } - fakeTransparent = true; - getUI().update(g2, this); - } finally { - g2.dispose(); - fakeTransparent = false; - } - - } - - protected boolean isOpaquePatch() { - if (fakeTransparent) return false; - if (isPaintingBackground()) { - return !isTransparentBackground() && !isAlpha(); - } - return false; - } - - protected void setOpaquePatch(boolean opaque) { - super.setOpaque(opaque); - } - /** - * Returns whether or not the container hierarchy below is - * transparent. - * - * @return - */ - protected boolean isAlpha() { - // PENDING JW: use effective alpha? - return getAlpha() < 1.0f; - } - - /** - * Returns whether or not the background is transparent. - * - * @return - */ - protected boolean isTransparentBackground() { - return getBackground().getAlpha() < 255; - } - - /** - * Returns whether or not the background should be painted. - * - * @return - */ - protected boolean isPaintingBackground() { - return super.isOpaque(); - } - - @SuppressWarnings("unused") - private static final Logger LOG = Logger.getLogger(JXPanel.class.getName()); -} diff --git a/src/main/java/org/jdesktop/swingx/JXRadioGroup.java b/src/main/java/org/jdesktop/swingx/JXRadioGroup.java deleted file mode 100644 index 9e5a27b0cb..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXRadioGroup.java +++ /dev/null @@ -1,338 +0,0 @@ -/* - * $Id: JXRadioGroup.java 4158 2012-02-03 18:29:40Z kschaefe $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; - -import javax.swing.*; -import javax.swing.event.EventListenerList; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.ItemEvent; -import java.awt.event.ItemListener; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.List; - -/** - *

                - * {@code JXRadioGroup} is a group of radio buttons that functions as a unit. It - * is similar in concept to a {@link JComboBox} in functionality, but can offer - * a better presentation for a small number of choices. {@code JXRadioGroup} - * should be used in preference to {@code JComboBox} when the number of choices - * is small (less than six) or the choices are verbose. - *

                - *

                - * Notes: - *

                  - *
                1. Enabling and disabling the JXRadioGroup will enable/disable all of the - * child buttons inside the JXRadioGroup.
                2. - *
                3. - * If the generic type parameter of JXRadioGroup is a subclass of - * {@link AbstractButton}, then the buttons will be added "as is" to the - * container. If the generic type is anything else, buttons will be created as - * {@link JRadioButton} objects, and the button text will be set by calling - * toString() on the value object.
                4. - *
                5. - * Alternatively, if you want to configure the buttons individually, construct - * the JXRadioGroup normally, and then call {@link #getChildButton(int)} or - * {@link #getChildButton(Object)} and configure the buttons.
                6. - *
                - *

                - *

                - * TODO back with a model (possibly reuse of extend {@link ComboBoxModel} - *

                - * - * @author Amy Fowler - * @author Noel Grandin - * @version 1.0 - */ -@JavaBean -public class JXRadioGroup extends JPanel { - - private static final long serialVersionUID = 3257285842266567986L; - - private ButtonGroup buttonGroup; - - private final List values = new ArrayList(); - - private ActionSelectionListener actionHandler; - - /** - * Create a default JXRadioGroup with a default layout axis of {@link BoxLayout#X_AXIS}. - */ - public JXRadioGroup() { - setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); - buttonGroup = new ButtonGroup(); - } - - /** - * Create a default JXRadioGroup with a default layout axis of {@link BoxLayout#X_AXIS}. - * - * @param radioValues the list of values used to create the group. - */ - public JXRadioGroup(T[] radioValues) { - this(); - for (int i = 0; i < radioValues.length; i++) { - add(radioValues[i]); - } - } - - /** - * Convenience factory method. - * Reduces code clutter when dealing with generics. - * - * @param radioValues the list of values used to create the group. - */ - public static JXRadioGroup create(T[] radioValues) - { - return new JXRadioGroup(radioValues); - } - - /** - * Set the layout axis of the radio group. - * - * @param axis values from {@link BoxLayout}. - */ - public void setLayoutAxis(int axis) - { - setLayout(new BoxLayout(this, axis)); - } - - /** - * Sets the values backing this group. This replaces the current set of - * values with the new set. - * - * @param radioValues - * the new backing values for this group - */ - public void setValues(T[] radioValues) { - clearAll(); - for (int i = 0; i < radioValues.length; i++) { - add(radioValues[i]); - } - } - - private void clearAll() { - values.clear(); - buttonGroup = new ButtonGroup(); - // remove all the child components - removeAll(); - } - - /** - * You can use this method to manually add your own AbstractButton objects, provided you declared - * the class as JXRadioGroup<JRadioButton>. - */ - public void add(T radioValue) { - if (values.contains(radioValue)) - { - throw new IllegalArgumentException("cannot add the same value twice " + radioValue); - } - if (radioValue instanceof AbstractButton) { - values.add(radioValue); - addButton((AbstractButton) radioValue); - } else { - values.add(radioValue); - // Note: the "quote + object" trick here allows null values - addButton(new JRadioButton(""+radioValue)); - } - } - - private void addButton(AbstractButton button) { - buttonGroup.add(button); - super.add(button); - if (actionHandler == null) { - actionHandler = new ActionSelectionListener(); - } - button.addActionListener(actionHandler); - button.addItemListener(actionHandler); - } - - private class ActionSelectionListener implements ActionListener, ItemListener - { - @Override - public void actionPerformed(ActionEvent e) { - fireActionEvent(e); - } - - @Override - public void itemStateChanged(ItemEvent e) { - fireActionEvent(null); - } - } - - /** - * Gets the currently selected button. - * - * @return the currently selected button - * @see #getSelectedValue() - */ - public AbstractButton getSelectedButton() { - final ButtonModel selectedModel = buttonGroup.getSelection(); - final AbstractButton children[] = getButtonComponents(); - for (int i = 0; i < children.length; i++) { - AbstractButton button = children[i]; - if (button.getModel() == selectedModel) { - return button; - } - } - return null; - } - - private AbstractButton[] getButtonComponents() { - final Component[] children = getComponents(); - final List buttons = new ArrayList(); - for (int i = 0; i < children.length; i++) { - if (children[i] instanceof AbstractButton) { - buttons.add((AbstractButton) children[i]); - } - } - return buttons.toArray(new AbstractButton[buttons.size()]); - } - - private int getSelectedIndex() { - final ButtonModel selectedModel = buttonGroup.getSelection(); - final Component children[] = getButtonComponents(); - for (int i = 0; i < children.length; i++) { - AbstractButton button = (AbstractButton) children[i]; - if (button.getModel() == selectedModel) { - return i; - } - } - return -1; - } - - /** - * The currently selected value. - * - * @return the current value - */ - public T getSelectedValue() { - final int index = getSelectedIndex(); - return (index < 0 || index >= values.size()) ? null : values.get(index); - } - - /** - * Selects the supplied value. - * - * @param value - * the value to select - */ - public void setSelectedValue(T value) { - final int index = values.indexOf(value); - AbstractButton button = getButtonComponents()[index]; - button.setSelected(true); - } - - /** - * Retrieve the child button by index. - */ - public AbstractButton getChildButton(int index) { - return getButtonComponents()[index]; - } - - /** - * Retrieve the child button that represents this value. - */ - public AbstractButton getChildButton(T value) { - final int index = values.indexOf(value); - return getButtonComponents()[index]; - } - - /** - * Get the number of child buttons. - */ - public int getChildButtonCount() { - return getButtonComponents().length; - } - - /** - * Adds an ActionListener. - *

                - * The ActionListener will receive an ActionEvent - * when a selection has been made. - * - * @param l the ActionListener that is to be notified - * @see #setSelectedValue(Object) - */ - public void addActionListener(ActionListener l) { - listenerList.add(ActionListener.class, l); - } - - /** - * Removes an ActionListener. - * - * @param l - * the ActionListener to remove - */ - public void removeActionListener(ActionListener l) { - listenerList.remove(ActionListener.class, l); - } - - /** - * Returns an array of all the ActionListeners added - * to this JRadioGroup with addActionListener(). - * - * @return all of the ActionListeners added or an empty - * array if no listeners have been added - */ - public ActionListener[] getActionListeners() { - return listenerList.getListeners(ActionListener.class); - } - - /** - * Notifies all listeners that have registered interest for notification on - * this event type. - * - * @param e - * the event to pass to the listeners - * @see EventListenerList - */ - protected void fireActionEvent(ActionEvent e) { - for (ActionListener l : getActionListeners()) { - l.actionPerformed(e); - } - } - - /** - * Enable/disable all of the child buttons - * - * @see JComponent#setEnabled(boolean) - */ - @Override - public void setEnabled(boolean enabled) { - super.setEnabled(enabled); - for (Enumeration en = buttonGroup.getElements(); en.hasMoreElements();) { - final AbstractButton button = en.nextElement(); - /* We don't want to enable a button where the action does not - * permit it. */ - if (enabled && button.getAction() != null - && !button.getAction().isEnabled()) { - // do nothing - } else { - button.setEnabled(enabled); - } - } - } - -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/JXRootPane.java b/src/main/java/org/jdesktop/swingx/JXRootPane.java deleted file mode 100644 index f7bd932e4c..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXRootPane.java +++ /dev/null @@ -1,458 +0,0 @@ -/* - * $Id: JXRootPane.java 4147 2012-02-01 17:13:24Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.KeyEvent; - -/** - * Extends the JRootPane by supporting specific placements for a toolbar and a - * status bar. If a status bar exists, then toolbars, menus will be registered - * with the status bar. - * - * @see JXStatusBar - * @author Mark Davidson - */ -@JavaBean -public class JXRootPane extends JRootPane { - /** - * An extended {@code RootLayout} offering support for managing the status - * bar. - * - * @author Karl George Schaefer - * @author Jeanette Winzenberg - */ - protected class XRootLayout extends RootLayout { - - LayoutManager2 delegate; - - /** - * The layout manager backing this manager. The delegate is used to - * calculate the size when the UI handles the window decorations. - * - * @param delegate - * the backing manager - */ - public void setLayoutManager(LayoutManager2 delegate) { - this.delegate = delegate; - } - - private Dimension delegatePreferredLayoutSize(Container parent) { - if (delegate == null) - return super.preferredLayoutSize(parent); - return delegate.preferredLayoutSize(parent); - } - - /** - * {@inheritDoc} - */ - @Override - public Dimension preferredLayoutSize(Container parent) { - Dimension pref = delegatePreferredLayoutSize(parent); - if (statusBar != null && statusBar.isVisible()) { - Dimension statusPref = statusBar.getPreferredSize(); - pref.width = Math.max(pref.width, statusPref.width); - pref.height += statusPref.height; - } - return pref; - } - - private Dimension delegateMinimumLayoutSize(Container parent) { - if (delegate == null) - return super.minimumLayoutSize(parent); - return delegate.minimumLayoutSize(parent); - } - - /** - * {@inheritDoc} - */ - @Override - public Dimension minimumLayoutSize(Container parent) { - Dimension pref = delegateMinimumLayoutSize(parent); - if (statusBar != null && statusBar.isVisible()) { - Dimension statusPref = statusBar.getMinimumSize(); - pref.width = Math.max(pref.width, statusPref.width); - pref.height += statusPref.height; - } - return pref; - - } - - private Dimension delegateMaximumLayoutSize(Container parent) { - if (delegate == null) - - return super.maximumLayoutSize(parent); - return delegate.maximumLayoutSize(parent); - } - - /** - * {@inheritDoc} - */ - @Override - public Dimension maximumLayoutSize(Container target) { - Dimension pref = delegateMaximumLayoutSize(target); - if (statusBar != null && statusBar.isVisible()) { - Dimension statusPref = statusBar.getMaximumSize(); - pref.width = Math.max(pref.width, statusPref.width); - // PENDING JW: overflow? - pref.height += statusPref.height; - } - return pref; - } - - private void delegateLayoutContainer(Container parent) { - if (delegate == null) { - super.layoutContainer(parent); - } else { - delegate.layoutContainer(parent); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void layoutContainer(Container parent) { - delegateLayoutContainer(parent); - if (statusBar == null || !statusBar.isVisible()) - return; - Rectangle b = parent.getBounds(); - Insets i = getInsets(); - int w = b.width - i.right - i.left; -// int h = b.height - i.top - i.bottom; - Dimension statusPref = statusBar.getPreferredSize(); - statusBar.setBounds(i.right, b.height - i.bottom - - statusPref.height, w, statusPref.height); - if (contentPane != null) { - Rectangle bounds = contentPane.getBounds(); - contentPane.setBounds(bounds.x, bounds.y, bounds.width, - bounds.height - statusPref.height); - } - - } - } - - /** - * The current status bar for this root pane. - */ - protected JXStatusBar statusBar; - - private JToolBar toolBar; - - /** - * The button that gets activated when the pane has the focus and - * a UI-specific action like pressing the ESC key occurs. - */ - private JButton cancelButton; - - /** - * Creates an extended root pane. - */ - public JXRootPane() { - installKeyboardActions(); - } - - /** - * {@inheritDoc} - */ - @Override - protected Container createContentPane() { - JComponent c = new JXPanel() { - /** - * {@inheritDoc} - */ - @Override - protected void addImpl(Component comp, Object constraints, int index) { - synchronized (getTreeLock()) { - super.addImpl(comp, constraints, index); - registerStatusBar(comp); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void remove(int index) { - synchronized (getTreeLock()) { - unregisterStatusBar(getComponent(index)); - super.remove(index); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void removeAll() { - synchronized (getTreeLock()) { - for (Component c : getComponents()) { - unregisterStatusBar(c); - } - - super.removeAll(); - } - } - }; - c.setName(this.getName()+".contentPane"); - c.setLayout(new BorderLayout() { - /* This BorderLayout subclass maps a null constraint to CENTER. - * Although the reference BorderLayout also does this, some VMs - * throw an IllegalArgumentException. - */ - @Override - public void addLayoutComponent(Component comp, Object constraints) { - if (constraints == null) { - constraints = BorderLayout.CENTER; - } - super.addLayoutComponent(comp, constraints); - } - }); - return c; - } - - - /** - * {@inheritDoc} - */ - @Override - public void setLayout(LayoutManager layout) { - if (layout instanceof XRootLayout) { - // happens if decoration is uninstalled by ui - if ((layout != null) && (layout == getLayout())) { - ((XRootLayout) layout).setLayoutManager(null); - } - super.setLayout(layout); - } else { - if (layout instanceof LayoutManager2) { - ((XRootLayout) getLayout()).setLayoutManager((LayoutManager2) layout); - if (!isValid()) { - invalidate(); - } - } - } - } - - /** - * {@inheritDoc} - */ - @Override - protected LayoutManager createRootLayout() { - return new XRootLayout(); - } - - /** - * PENDING: move to UI - * - */ - private void installKeyboardActions() { - Action escAction = new AbstractAction() { - @Override - public void actionPerformed(ActionEvent evt) { - JButton cancelButton = getCancelButton(); - if (cancelButton != null) { - cancelButton.doClick(20); - } - } - - /** - * Overridden to hack around #566-swing: - * JXRootPane eats escape keystrokes from datepicker popup. - * Disable action if there is no cancel button.

                - * - * That's basically what RootPaneUI does - only not in - * the parameterless isEnabled, but in the one that passes - * in the sender (available in UIAction only). We can't test - * nor compare against core behaviour, UIAction has - * sun package scope.

                - * - * Cont'd (Issue #1358-swingx: popup menus not closed) - * The extended hack is inspired by Rob Camick's - * Blog - * and consists in checking if the the rootpane has a popup's actionMap "inserted". - * NOTE: this does not work if the popup or any of its children is focusOwner. - */ - @Override - public boolean isEnabled() { - Component component = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); - if (component instanceof JComponent) { - Action cancelPopup = ((JComponent)component).getActionMap().get("cancel"); - if (cancelPopup != null) return false; - } - return (cancelButton != null) && (cancelButton.isEnabled()); - } - }; - getActionMap().put("esc-action", escAction); - InputMap im = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); - KeyStroke key = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); - im.put(key, "esc-action"); - } - - private void registerStatusBar(Component comp) { - if (statusBar == null || comp == null) { - return; - } - if (comp instanceof Container) { - Component[] comps = ((Container) comp).getComponents(); - for (int i = 0; i < comps.length; i++) { - registerStatusBar(comps[i]); - } - } - } - - private void unregisterStatusBar(Component comp) { - if (statusBar == null || comp == null) { - return; - } - if (comp instanceof Container) { - Component[] comps = ((Container) comp).getComponents(); - for (int i = 0; i < comps.length; i++) { - unregisterStatusBar(comps[i]); - } - } - } - - /** - * Set the status bar for this root pane. Any components held by this root - * pane will be registered. If this is replacing an existing status bar then - * the existing component will be unregistered from the old status bar. - * - * @param statusBar - * the status bar to use - */ - public void setStatusBar(JXStatusBar statusBar) { - JXStatusBar oldStatusBar = this.statusBar; - this.statusBar = statusBar; - - Component[] comps = getContentPane().getComponents(); - for (int i = 0; i < comps.length; i++) { - // Unregister the old status bar. - unregisterStatusBar(comps[i]); - - // register the new status bar. - registerStatusBar(comps[i]); - } - if (oldStatusBar != null) { - remove(oldStatusBar); - } - if (statusBar != null) { - add(statusBar); - } - firePropertyChange("statusBar", oldStatusBar, getStatusBar()); - } - - /** - * Gets the currently installed status bar. - * - * @return the current status bar - */ - public JXStatusBar getStatusBar() { - return statusBar; - } - - /** - * Set the toolbar bar for this root pane. If a tool bar is currently registered with this - * {@code JXRootPane}, then it is removed prior to setting the new tool - * bar. If an implementation needs to handle more than one tool bar, a - * subclass will need to override the singleton logic used here or manually - * add toolbars with {@code getContentPane().add}. - * - * @param toolBar - * the toolbar to register - */ - public void setToolBar(JToolBar toolBar) { - JToolBar oldToolBar = getToolBar(); - this.toolBar = toolBar; - - if (oldToolBar != null) { - getContentPane().remove(oldToolBar); - } - - getContentPane().add(BorderLayout.NORTH, this.toolBar); - - //ensure the new toolbar is correctly sized and displayed - getContentPane().validate(); - - firePropertyChange("toolBar", oldToolBar, getToolBar()); - } - - /** - * The currently installed tool bar. - * - * @return the current tool bar - */ - public JToolBar getToolBar() { - return toolBar; - } - - - /** - * Sets the cancelButton property, - * which determines the current default cancel button for this JRootPane. - * The cancel button is the button which will be activated - * when a UI-defined activation event (typically the ESC key) - * occurs in the root pane regardless of whether or not the button - * has keyboard focus (unless there is another component within - * the root pane which consumes the activation event, - * such as a JTextPane). - * For default activation to work, the button must be an enabled - * descendant of the root pane when activation occurs. - * To remove a cancel button from this root pane, set this - * property to null. - * - * @param cancelButton the JButton which is to be the cancel button - * @see #getCancelButton() - * - * @beaninfo - * description: The button activated by default for cancel actions in this root pane - */ - public void setCancelButton(JButton cancelButton) { - JButton old = this.cancelButton; - - if (old != cancelButton) { - this.cancelButton = cancelButton; - - if (old != null) { - old.repaint(); - } - if (cancelButton != null) { - cancelButton.repaint(); - } - } - - firePropertyChange("cancelButton", old, cancelButton); - } - - /** - * Returns the value of the cancelButton property. - * @return the JButton which is currently the default cancel button - * @see #setCancelButton - */ - public JButton getCancelButton() { - return cancelButton; - } - -} diff --git a/src/main/java/org/jdesktop/swingx/JXSearchField.java b/src/main/java/org/jdesktop/swingx/JXSearchField.java deleted file mode 100644 index 64b9d50243..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXSearchField.java +++ /dev/null @@ -1,841 +0,0 @@ -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.plaf.LookAndFeelAddons; -import org.jdesktop.swingx.plaf.SearchFieldAddon; -import org.jdesktop.swingx.plaf.TextUIWrapper; -import org.jdesktop.swingx.plaf.UIManagerExt; -import org.jdesktop.swingx.prompt.BuddyButton; -import org.jdesktop.swingx.search.NativeSearchFieldSupport; -import org.jdesktop.swingx.search.RecentSearches; - -import javax.swing.*; -import javax.swing.text.Document; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; - -/** - * A text field with a find icon in which the user enters text that identifies - * items to search for. - * - * JXSearchField almost looks and behaves like a native Windows Vista search - * box, a Mac OS X search field, or a search field like the one used in Mozilla - * Thunderbird 2.0 - depending on the current look and feel. - * - * JXSearchField is a text field that contains a find button and a cancel - * button. The find button normally displays a lens icon appropriate for the - * current look and feel. The cancel button is used to clear the text and - * therefore only visible when text is present. It normally displays a 'x' like - * icon. Text can also be cleared, using the 'Esc' key. - * - * The position of the find and cancel buttons can be customized by either - * changing the search fields (text) margin or button margin, or by changing the - * {@link LayoutStyle}. - * - * JXSearchField supports two different search modes: {@link SearchMode#INSTANT} - * and {@link SearchMode#REGULAR}. - * - * A search can be performed by registering an {@link ActionListener}. The - * {@link ActionEvent}s command property contains the text to search for. The - * search should be cancelled, when the command text is empty or null. - * - * @see RecentSearches - * @author Peter Weishapl - * - */ -@JavaBean -public class JXSearchField extends JXTextField { - /** - * The default instant search delay. - */ - private static final int DEFAULT_INSTANT_SEARCH_DELAY = 180; - /** - * The key used to invoke the cancel action. - */ - private static final KeyStroke CANCEL_KEY = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); - - /** - * Defines, how the find and cancel button are layouted. - */ - public enum LayoutStyle { - /** - *

                - * In VISTA layout style, the find button is placed on the right side of - * the search field. If text is entered, the find button is replaced by - * the cancel button when the actual search mode is - * {@link SearchMode#INSTANT}. When the search mode is - * {@link SearchMode#REGULAR} the find button will always stay visible - * and the cancel button will never be shown. However, 'Escape' can - * still be pressed to clear the text. - *

                - */ - VISTA, - /** - *

                - * In MAC layout style, the find button is placed on the left side of - * the search field and the cancel button on the right side. The cancel - * button is only visible when text is present. - *

                - */ - MAC - }; - - /** - * Defines when action events are posted. - */ - public enum SearchMode { - /** - *

                - * In REGULAR search mode, an action event is fired, when the user - * presses enter or clicks the find button. - *

                - *

                - * However, if a find popup menu is set and layout style is - * {@link LayoutStyle#MAC}, no action will be fired, when the find - * button is clicked, because instead the popup menu is shown. A search - * can therefore only be triggered, by pressing the enter key. - *

                - *

                - * The find button can have a rollover and a pressed icon, defined by - * the "SearchField.rolloverIcon" and "SearchField.pressedIcon" UI - * properties. When a find popup menu is set, - * "SearchField.popupRolloverIcon" and "SearchField.popupPressedIcon" - * are used. - *

                - * - */ - REGULAR, - /** - * In INSTANT search mode, an action event is fired, when the user - * presses enter or changes the search text. - * - * The action event is delayed about the number of milliseconds - * specified by {@link JXSearchField#getInstantSearchDelay()}. - * - * No rollover and pressed icon is used for the find button. - */ - INSTANT - } - - // ensure at least the default ui is registered - static { - LookAndFeelAddons.contribute(new SearchFieldAddon()); - } - - private JButton findButton; - - private JButton cancelButton; - - private JButton popupButton; - - private LayoutStyle layoutStyle; - - private SearchMode searchMode = SearchMode.INSTANT; - - private boolean useSeperatePopupButton; - - private boolean useSeperatePopupButtonSet; - - private boolean layoutStyleSet; - - private int instantSearchDelay = DEFAULT_INSTANT_SEARCH_DELAY; - - private boolean promptFontStyleSet; - - private Timer instantSearchTimer; - - private String recentSearchesSaveKey; - - private RecentSearches recentSearches; - - /** - * Creates a new search field with a default prompt. - */ - public JXSearchField() { - this(UIManagerExt.getString("SearchField.prompt")); - } - - /** - * Creates a new search field with the given prompt and - * {@link SearchMode#INSTANT}. - * - * @param prompt - */ - public JXSearchField(String prompt) { - super(prompt); - // use the native search field if possible. - setUseNativeSearchFieldIfPossible(true); - // install default actions - setCancelAction(new ClearAction()); - setFindAction(new FindAction()); - - // We cannot register the ClearAction through the Input- and - // ActionMap because ToolTipManager registers the escape key with an - // action that hides the tooltip every time the tooltip is changed and - // then the ClearAction will never be called. - addKeyListener(new KeyAdapter() { - @Override - public void keyPressed(KeyEvent e) { - if (CANCEL_KEY.equals(KeyStroke.getKeyStroke(e.getKeyCode(), e.getModifiers()))) { - getCancelAction().actionPerformed( - new ActionEvent(JXSearchField.this, e.getID(), KeyEvent.getKeyText(e.getKeyCode()))); - } - } - }); - - // Map specific native properties to general JXSearchField properties. - addPropertyChangeListener(NativeSearchFieldSupport.FIND_POPUP_PROPERTY, new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - JPopupMenu oldPopup = (JPopupMenu) evt.getOldValue(); - firePropertyChange("findPopupMenu", oldPopup, evt.getNewValue()); - } - }); - addPropertyChangeListener(NativeSearchFieldSupport.CANCEL_ACTION_PROPERTY, new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - ActionListener oldAction = (ActionListener) evt.getOldValue(); - firePropertyChange("cancelAction", oldAction, evt.getNewValue()); - } - }); - addPropertyChangeListener(NativeSearchFieldSupport.FIND_ACTION_PROPERTY, new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - ActionListener oldAction = (ActionListener) evt.getOldValue(); - firePropertyChange("findAction", oldAction, evt.getNewValue()); - } - }); - } - - /** - * Returns the current {@link SearchMode}. - * - * @return the current {@link SearchMode}. - */ - public SearchMode getSearchMode() { - return searchMode; - } - - /** - * Returns true if the current {@link SearchMode} is - * {@link SearchMode#INSTANT}. - * - * @return true if the current {@link SearchMode} is - * {@link SearchMode#INSTANT} - */ - public boolean isInstantSearchMode() { - return SearchMode.INSTANT.equals(getSearchMode()); - } - - /** - * Returns true if the current {@link SearchMode} is - * {@link SearchMode#REGULAR}. - * - * @return true if the current {@link SearchMode} is - * {@link SearchMode#REGULAR} - */ - public boolean isRegularSearchMode() { - return SearchMode.REGULAR.equals(getSearchMode()); - } - - /** - * Sets the current search mode. See {@link SearchMode} for a description of - * the different search modes. - * - * @param searchMode - * {@link SearchMode#INSTANT} or {@link SearchMode#REGULAR} - */ - public void setSearchMode(SearchMode searchMode) { - firePropertyChange("searchMode", this.searchMode, this.searchMode = searchMode); - } - - /** - * Get the instant search delay in milliseconds. The default delay is 50 - * Milliseconds. - * - * @see {@link #setInstantSearchDelay(int)} - * @return the instant search delay in milliseconds - */ - public int getInstantSearchDelay() { - return instantSearchDelay; - } - - /** - * Set the instant search delay in milliseconds. In - * {@link SearchMode#INSTANT}, when the user changes the text, an action - * event will be fired after the specified instant search delay. - * - * It is recommended to use a instant search delay to avoid the firing of - * unnecessary events. For example when the user replaces the whole text - * with a different text the search fields underlying {@link Document} - * typically fires 2 document events. The first one, because the old text is - * removed and the second one because the new text is inserted. If the - * instant search delay is 0, this would result in 2 action events being - * fired. When a instant search delay is used, the first document event - * typically is ignored, because the second one is fired before the delay is - * over, which results in a correct behavior because only the last and only - * relevant event will be delivered. - * - * @param instantSearchDelay - */ - public void setInstantSearchDelay(int instantSearchDelay) { - firePropertyChange("instantSearchDelay", this.instantSearchDelay, this.instantSearchDelay = instantSearchDelay); - } - - /** - * Get the current {@link LayoutStyle}. - * - * @return - */ - public LayoutStyle getLayoutStyle() { - return layoutStyle; - } - - /** - * Returns true if the current {@link LayoutStyle} is - * {@link LayoutStyle#VISTA}. - * - * @return - */ - public boolean isVistaLayoutStyle() { - return LayoutStyle.VISTA.equals(getLayoutStyle()); - } - - /** - * Returns true if the current {@link LayoutStyle} is - * {@link LayoutStyle#MAC}. - * - * @return - */ - public boolean isMacLayoutStyle() { - return LayoutStyle.MAC.equals(getLayoutStyle()); - } - - /** - * Set the current {@link LayoutStyle}. See {@link LayoutStyle} for a - * description of how this affects layout and behavior of the search field. - * - * @param layoutStyle - * {@link LayoutStyle#MAC} or {@link LayoutStyle#VISTA} - */ - public void setLayoutStyle(LayoutStyle layoutStyle) { - layoutStyleSet = true; - firePropertyChange("layoutStyle", this.layoutStyle, this.layoutStyle = layoutStyle); - } - - /** - * Set the margin space around the search field's text. - * - * @see javax.swing.text.JTextComponent#setMargin(Insets) - */ - @Override - public void setMargin(Insets m) { - super.setMargin(m); - } - - /** - * Returns the cancel action, or an instance of {@link ClearAction}, if - * none has been set. - * - * @return the cancel action - */ - public final ActionListener getCancelAction() { - ActionListener a = NativeSearchFieldSupport.getCancelAction(this); - if (a == null) { - a = new ClearAction(); - } - return a; - } - - /** - * Sets the action that is invoked, when the user presses the 'Esc' key or - * clicks the cancel button. - * - * @param cancelAction - */ - public final void setCancelAction(ActionListener cancelAction) { - NativeSearchFieldSupport.setCancelAction(this, cancelAction); - } - - /** - * Returns the cancel button. - * - * Calls {@link #createCancelButton()} to create the cancel button and - * registers an {@link ActionListener} that delegates actions to the - * {@link ActionListener} returned by {@link #getCancelAction()}, if - * needed. - * - * @return the cancel button - */ - public final JButton getCancelButton() { - if (cancelButton == null) { - cancelButton = createCancelButton(); - cancelButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - getCancelAction().actionPerformed(e); - } - }); - } - return cancelButton; - } - - /** - * Creates and returns the cancel button. - * - * Override to use a custom cancel button. - * - * @see #getCancelButton() - * @return the cancel button - */ - protected JButton createCancelButton() { - BuddyButton btn = new BuddyButton(); - - return btn; - } - - /** - * Returns the action that is invoked when the enter key is pressed or the - * find button is clicked. If no action has been set, a new instance of - * {@link FindAction} will be returned. - * - * @return the find action - */ - public final ActionListener getFindAction() { - ActionListener a = NativeSearchFieldSupport.getFindAction(this); - if (a == null) { - a = new FindAction(); - } - return a; - } - - /** - * Sets the action that is invoked when the enter key is pressed or the find - * button is clicked. - * - * @return the find action - */ - public final void setFindAction(ActionListener findAction) { - NativeSearchFieldSupport.setFindAction(this, findAction); - } - - /** - * Returns the find button. - * - * Calls {@link #createFindButton()} to create the find button and registers - * an {@link ActionListener} that delegates actions to the - * {@link ActionListener} returned by {@link #getFindAction()}, if needed. - * - * @return the find button - */ - public final JButton getFindButton() { - if (findButton == null) { - findButton = createFindButton(); - findButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - getFindAction().actionPerformed(e); - } - }); - } - return findButton; - } - - /** - * Creates and returns the find button. The buttons action is set to the - * action returned by {@link #getSearchAction()}. - * - * Override to use a custom find button. - * - * @see #getFindButton() - * @return the find button - */ - protected JButton createFindButton() { - BuddyButton btn = new BuddyButton(); - - return btn; - } - - /** - * Returns the popup button. If a find popup menu is set, it will be - * displayed when this button is clicked. - * - * This button will only be visible, if {@link #isUseSeperatePopupButton()} - * returns true. Otherwise the popup menu will be displayed - * when the find button is clicked. - * - * @return the popup button - */ - public final JButton getPopupButton() { - if (popupButton == null) { - popupButton = createPopupButton(); - } - return popupButton; - } - - /** - * Creates and returns the popup button. Override to use a custom popup - * button. - * - * @see #getPopupButton() - * @return the popup button - */ - protected JButton createPopupButton() { - return new BuddyButton(); - } - - /** - * Returns true if the popup button should be visible and - * used for displaying the find popup menu. Otherwise, the find popup menu - * will be displayed when the find button is clicked. - * - * @return true if the popup button should be used - */ - public boolean isUseSeperatePopupButton() { - return useSeperatePopupButton; - } - - /** - * Set if the popup button should be used for displaying the find popup - * menu. - * - * @param useSeperatePopupButton - */ - public void setUseSeperatePopupButton(boolean useSeperatePopupButton) { - useSeperatePopupButtonSet = true; - firePropertyChange("useSeperatePopupButton", this.useSeperatePopupButton, - this.useSeperatePopupButton = useSeperatePopupButton); - } - - public boolean isUseNativeSearchFieldIfPossible() { - return NativeSearchFieldSupport.isSearchField(this); - } - - public void setUseNativeSearchFieldIfPossible(boolean useNativeSearchFieldIfPossible) { - TextUIWrapper.getDefaultWrapper().uninstall(this); - NativeSearchFieldSupport.setSearchField(this, useNativeSearchFieldIfPossible); - TextUIWrapper.getDefaultWrapper().install(this, true); - updateUI(); - } - - /** - * Updates the cancel, find and popup buttons enabled state in addition to - * setting the search fields editable state. - * - * @see #updateButtonState() - * @see javax.swing.text.JTextComponent#setEditable(boolean) - */ - @Override - public void setEditable(boolean b) { - super.setEditable(b); - updateButtonState(); - } - - /** - * Updates the cancel, find and popup buttons enabled state in addition to - * setting the search fields enabled state. - * - * @see #updateButtonState() - * @see javax.swing.text.JTextComponent#setEnabled(boolean) - */ - @Override - public void setEnabled(boolean enabled) { - super.setEnabled(enabled); - updateButtonState(); - } - - /** - * Enables the cancel action if this search field is editable and enabled, - * otherwise it will be disabled. Enabled the search action and popup button - * if this search field is enabled, otherwise it will be disabled. - */ - protected void updateButtonState() { - getCancelButton().setEnabled(isEditable() & isEnabled()); - getFindButton().setEnabled(isEnabled()); - getPopupButton().setEnabled(isEnabled()); - } - - /** - * Sets the popup menu that will be displayed when the popup button is - * clicked. If a find popup menu is set and - * {@link #isUseSeperatePopupButton()} returns false, the - * popup button will be displayed instead of the find button. Otherwise the - * popup button will be displayed in addition to the find button. - * - * The find popup menu is managed using {@link NativeSearchFieldSupport} to - * achieve compatibility with the native search field support provided by - * the Mac Look And Feel since Mac OS 10.5. - * - * If a recent searches save key has been set and therefore a recent - * searches popup menu is installed, this method does nothing. You must - * first remove the recent searches save key, by calling - * {@link #setRecentSearchesSaveKey(String)} with a null - * parameter. - * - * @see #setRecentSearchesSaveKey(String) - * @see RecentSearches - * @param findPopupMenu - * the popup menu, which will be displayed when the popup button - * is clicked - */ - public void setFindPopupMenu(JPopupMenu findPopupMenu) { - if (isManagingRecentSearches()) { - return; - } - - NativeSearchFieldSupport.setFindPopupMenu(this, findPopupMenu); - } - - /** - * Returns the find popup menu. - * - * @see #setFindPopupMenu(JPopupMenu) - * @return the find popup menu - */ - public JPopupMenu getFindPopupMenu() { - return NativeSearchFieldSupport.getFindPopupMenu(this); - } - - /** - * TODO - * - * @return - */ - public final boolean isManagingRecentSearches() { - return recentSearches != null; - } - - private boolean isValidRecentSearchesKey(String key) { - return key != null && key.length() > 0; - } - - /** - * Returns the key used to persist recent searches. - * - * @see #setRecentSearchesSaveKey(String) - * @return - */ - public String getRecentSearchesSaveKey() { - return recentSearchesSaveKey; - } - - /** - * Installs and manages a recent searches popup menu as the find popup menu, - * if recentSearchesSaveKey is not null. Otherwise, removes - * the popup menu and stops managing recent searches. - * - * @see #setFindAction(ActionListener) - * @see #isManagingRecentSearches() - * @see RecentSearches - * - * @param recentSearchesSaveKey - * this key is used to persist the recent searches. - */ - public void setRecentSearchesSaveKey(String recentSearchesSaveKey) { - String oldName = getRecentSearchesSaveKey(); - this.recentSearchesSaveKey = recentSearchesSaveKey; - - if (recentSearches != null) { - // set null before uninstalling. otherwise the popup menu is not - // allowed to be changed. - RecentSearches rs = recentSearches; - recentSearches = null; - rs.uninstall(this); - } - - if (isValidRecentSearchesKey(recentSearchesSaveKey)) { - recentSearches = new RecentSearches(recentSearchesSaveKey); - recentSearches.install(this); - } - - firePropertyChange("recentSearchesSaveKey", oldName, this.recentSearchesSaveKey); - } - - /** - * TODO - * - * @return - */ - public RecentSearches getRecentSearches() { - return recentSearches; - } - - /** - * Returns the {@link Timer} used to delay the firing of action events in - * instant search mode when the user enters text. - * - * This timer calls {@link #postActionEvent()}. - * - * @return the {@link Timer} used to delay the firing of action events - */ - public Timer getInstantSearchTimer() { - if (instantSearchTimer == null) { - instantSearchTimer = new Timer(0, new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - postActionEvent(); - } - }); - instantSearchTimer.setRepeats(false); - } - return instantSearchTimer; - } - - /** - * Returns true if this search field is the focus owner or - * the find popup menu is visible. - * - * This is a hack to make the search field paint the focus indicator in Mac - * OS X Aqua when the find popup menu is visible. - * - * @return true if this search field is the focus owner or - * the find popup menu is visible - */ - @Override - public boolean hasFocus() { - if (getFindPopupMenu() != null && getFindPopupMenu().isVisible()) { - return true; - } - return super.hasFocus(); - } - - /** - * Overriden to also update the find popup menu if set. - */ - @Override - public void updateUI() { - super.updateUI(); - if (getFindPopupMenu() != null) { - SwingUtilities.updateComponentTreeUI(getFindPopupMenu()); - } - } - - /** - * Hack to enable the UI delegate to set default values depending on the - * current Look and Feel, without overriding custom values. - */ - @Override - public void setPromptFontStyle(Integer fontStyle) { - super.setPromptFontStyle(fontStyle); - promptFontStyleSet = true; - } - - /** - * Hack to enable the UI delegate to set default values depending on the - * current Look and Feel, without overriding custom values. - * - * @param propertyName - * the name of the property to change - * @param value - * the new value of the property - */ - public void customSetUIProperty(String propertyName, Object value) { - customSetUIProperty(propertyName, value, false); - } - - /** - * Hack to enable the UI delegate to set default values depending on the - * current Look and Feel, without overriding custom values. - * - * @param propertyName - * the name of the property to change - * @param value - * the new value of the property - * @param override - * override custom values - */ - public void customSetUIProperty(String propertyName, Object value, boolean override) { - if (propertyName == "useSeperatePopupButton") { - if (!useSeperatePopupButtonSet || override) { - setUseSeperatePopupButton(((Boolean) value).booleanValue()); - useSeperatePopupButtonSet = false; - } - } else if (propertyName == "layoutStyle") { - if (!layoutStyleSet || override) { - setLayoutStyle(LayoutStyle.valueOf(value.toString())); - layoutStyleSet = false; - } - } else if (propertyName == "promptFontStyle") { - if (!promptFontStyleSet || override) { - setPromptFontStyle((Integer) value); - promptFontStyleSet = false; - } - } else { - throw new IllegalArgumentException(); - } - } - - /** - * Overriden to prevent any delayed {@link ActionEvent}s from being sent - * after posting this action. - * - * For example, if the current {@link SearchMode} is - * {@link SearchMode#INSTANT} and the instant search delay is greater 0. The - * user enters some text and presses enter. This method will be invoked - * immediately because the users presses enter. However, this method would - * be invoked after the instant search delay, if we would not prevent it - * here. - */ - @Override - public void postActionEvent() { - getInstantSearchTimer().stop(); - super.postActionEvent(); - } - - /** - * Invoked when the the cancel button or the 'Esc' key is pressed. Sets the - * text in the search field to null. - * - */ - class ClearAction extends AbstractAction { - public ClearAction() { - putValue(SHORT_DESCRIPTION, "Clear Search Text"); - } - - /** - * Calls {@link #clear()}. - */ - @Override - public void actionPerformed(ActionEvent e) { - clear(); - } - - /** - * Sets the search field's text to null and requests the - * focus for the search field. - */ - public void clear() { - setText(null); - requestFocusInWindow(); - } - } - - /** - * Invoked when the find button is pressed. - */ - public class FindAction extends AbstractAction { - public FindAction() { - } - - /** - * In regular search mode posts an action event if the search field is - * the focus owner. - * - * Also requests the focus for the search field and selects the whole - * text. - */ - @Override - public void actionPerformed(ActionEvent e) { - if (isFocusOwner() && isRegularSearchMode()) { - postActionEvent(); - } - requestFocusInWindow(); - selectAll(); - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/JXSearchPanel.java b/src/main/java/org/jdesktop/swingx/JXSearchPanel.java deleted file mode 100644 index 49e92d8b3d..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXSearchPanel.java +++ /dev/null @@ -1,275 +0,0 @@ -/* - * $Id: JXSearchPanel.java 4158 2012-02-03 18:29:40Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.decorator.Highlighter; -import org.jdesktop.swingx.renderer.DefaultListRenderer; -import org.jdesktop.swingx.renderer.LocalizableStringValue; -import org.jdesktop.swingx.renderer.StringValue; -import org.jdesktop.swingx.search.PatternMatcher; -import org.jdesktop.swingx.search.PatternModel; - -import javax.swing.*; -import java.util.*; -import java.util.regex.Pattern; -/** - *

                - * {@code JXSearchPanel} provides complex searching features. Users are able to - * specify searching rules, enter searching text (including regular - * expressions), and toggle case-sensitivity. - *

                - *

                - * One of the main features that {@code JXSearchPanel} provides is the ability - * to update {@link PatternMatcher}s. To highlight text with a - * {@link Highlighter}, you need to update the highlighter via a pattern - * matcher. - *

                - *
                - * public class PatternHandler implements PatternMatcher {
                - * 
                - *     private Highlighter highlighter;
                - * 
                - *     private Pattern pattern;
                - * 
                - *     public void setPattern(Pattern pattern) {
                - *         this.pattern = pattern;
                - *         highlighter.setHighlightPredicate(new PatternPredicate(pattern));
                - *     }
                - * 
                - * }
                - * 
                - *

                - * TODO: allow custom PatternModel and/or access to configuration of bound - * PatternModel. - *

                - *

                - * TODO: fully support control of multiple PatternMatchers. - *

                - * - * @author Ramesh Gupta - * @author Jeanette Winzenburg - */ -@JavaBean -public class JXSearchPanel extends AbstractPatternPanel { - /** - * The action command key. - */ - public static final String MATCH_RULE_ACTION_COMMAND = "selectMatchRule"; - - private JXComboBox searchCriteria; - - private List patternMatchers; - - - /** - * Creates a search panel. - */ - public JXSearchPanel() { - initComponents(); - build(); - initActions(); - bind(); - getPatternModel().setIncremental(true); - } - -//----------------- accessing public properties - - /** - * Adds a pattern matcher. - * - * @param matcher - * the matcher to add. - */ - public void addPatternMatcher(PatternMatcher matcher) { - getPatternMatchers().add(matcher); - updateFieldName(matcher); - } - - /** - * sets the PatternFilter control. - * - * PENDING: change to do a addPatternMatcher to enable multiple control. - * - */ -// public void setPatternFilter(PatternFilter filter) { -// getPatternMatchers().add(filter); -// updateFieldName(filter); -// } - - /** - * set the label of the search combo. - * - * @param name - * the label - */ - public void setFieldName(String name) { - String old = searchLabel.getText(); - searchLabel.setText(name); - firePropertyChange("fieldName", old, searchLabel.getText()); - } - - /** - * returns the label of the search combo. - * - */ - public String getFieldName() { - return searchLabel.getText(); - } - - /** - * returns the current compiled Pattern. - * - * @return the current compiled Pattern - */ - public Pattern getPattern() { - return patternModel.getPattern(); - } - - /** - * @param matcher - */ - protected void updateFieldName(PatternMatcher matcher) { - -// if (matcher instanceof PatternFilter) { -// PatternFilter filter = (PatternFilter) matcher; -// searchLabel.setText(filter.getColumnName()); -// } else { - if (searchLabel.getText().length() == 0) { // ugly hack - searchLabel.setText("Field"); - /** TODO: Remove this hack!!! */ -// } - } - } - - // ---------------- action callbacks - - /** - * Updates the pattern matchers. - */ - @Override - public void match() { - for (Iterator iter = getPatternMatchers().iterator(); iter.hasNext();) { - iter.next().setPattern(getPattern()); - - } - } - - /** - * set's the PatternModel's MatchRule to the selected in combo. - * - * NOTE: this - * is public as an implementation side-effect! - * No need to ever call directly. - */ - public void updateMatchRule() { - getPatternModel().setMatchRule( - (String) searchCriteria.getSelectedItem()); - } - - private List getPatternMatchers() { - if (patternMatchers == null) { - patternMatchers = new ArrayList(); - } - return patternMatchers; - } - - //---------------- init actions and model - - @Override - protected void initExecutables() { - super.initExecutables(); - getActionMap().put(MATCH_RULE_ACTION_COMMAND, - createBoundAction(MATCH_RULE_ACTION_COMMAND, "updateMatchRule")); - } - - - //--------------------- binding support - - - - /** - * bind the components to the patternModel/actions. - */ - @Override - protected void bind() { - super.bind(); - List matchRules = getPatternModel().getMatchRules(); - // PENDING: map rules to localized strings - ComboBoxModel model = new DefaultComboBoxModel(matchRules.toArray()); - model.setSelectedItem(getPatternModel().getMatchRule()); - searchCriteria.setModel(model); - searchCriteria.setAction(getAction(MATCH_RULE_ACTION_COMMAND)); - searchCriteria.setRenderer(new DefaultListRenderer(createStringValue(getLocale()))); - - } - - - private StringValue createStringValue(Locale locale) { - // TODO Auto-generated method stub - Map keys = new HashMap(); - keys.put(PatternModel.MATCH_RULE_CONTAINS, - PatternModel.MATCH_RULE_CONTAINS); - keys.put(PatternModel.MATCH_RULE_ENDSWITH, - PatternModel.MATCH_RULE_ENDSWITH); - keys.put(PatternModel.MATCH_RULE_EQUALS, - PatternModel.MATCH_RULE_EQUALS); - keys.put(PatternModel.MATCH_RULE_STARTSWITH, - PatternModel.MATCH_RULE_STARTSWITH); - return new LocalizableStringValue(keys, PatternModel.SEARCH_PREFIX, locale); - } - - /** - * {@inheritDoc} - */ - @Override - protected void updateLocaleState(Locale locale) { - // TODO Auto-generated method stub - super.updateLocaleState(locale); - searchCriteria.setRenderer(new DefaultListRenderer(createStringValue(locale))); - } - - //------------------------ init ui - /** - * build container by adding all components. - * PRE: all components created. - */ - private void build() { - add(searchLabel); - add(searchCriteria); - add(searchField); - add(matchCheck); - } - - /** - * create contained components. - * - * - */ - @Override - protected void initComponents() { - super.initComponents(); - searchCriteria = new JXComboBox(); - } - - -} diff --git a/src/main/java/org/jdesktop/swingx/JXStatusBar.java b/src/main/java/org/jdesktop/swingx/JXStatusBar.java deleted file mode 100644 index 3ff00f0f0b..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXStatusBar.java +++ /dev/null @@ -1,318 +0,0 @@ -/* - * $Id: JXStatusBar.java 4147 2012-02-01 17:13:24Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx; - - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.plaf.LookAndFeelAddons; -import org.jdesktop.swingx.plaf.StatusBarAddon; -import org.jdesktop.swingx.plaf.StatusBarUI; - -import javax.swing.*; -import java.awt.*; - -/** - *

                A container for JComponents that is typically placed at - * the bottom of a form and runs the entire width of the form. There are 3 - * important functions that JXStatusBar provides. - * First, JXStatusBar provides a hook for a pluggable look. - * There is a definite look associated with status bars on windows, for instance. - * By implementing a subclass of {@link JComponent}, we provide a way for the - * pluggable look and feel system to modify the look of the status bar.

                - * - *

                Second, JXStatusBar comes with its own layout manager. Each item is added to - * the JXStatusBar with a JXStatusBar.Constraint - * as the constraint argument. The JXStatusBar.Constraint contains - * an Insets object, as well as a ResizeBehavior, - * which can be FIXED or FILL. The resize behaviour applies to the width of - * components. All components added will maintain there preferred height, and the - * height of the JXStatusBar will be the height of the highest - * component plus insets.

                - * - *

                A constraint with JXStatusBar.Constraint.ResizeBehavior.FIXED - * will cause the component to occupy a fixed area on the JXStatusBar. - * The size of the area remains constant when the JXStatusBar is resized. - * A constraint with this behavior may also take a width value, see - * {@link Constraint#setFixedWidth(int)}. The width is a preferred - * minimum width. If the component preferred width is greater than the constraint - * width, the component width will apply.

                - * - *

                All components with constraint JXStatusBar.Constraint.ResizeBehavior.FILL - * will share equally any spare space in the JXStatusBar. Spare space - * is that left over after allowing for all FIXED component and the preferred - * width of FILL components, plus insets - * - *

                Constructing a JXStatusBar is very straightforward: - *

                
                - *      JXStatusBar bar = new JXStatusBar();
                - *      JLabel statusLabel = new JLabel("Ready");
                - *      JXStatusBar.Constraint c1 = new JXStatusBar.Constraint() 
                - *      c1.setFixedWidth(100);
                - *      bar.add(statusLabel, c1);     // Fixed width of 100 with no inserts
                - *      JXStatusBar.Constraint c2 = new JXStatusBarConstraint(
                - *              JXStatusBar.Constraint.ResizeBehavior.FILL) // Fill with no inserts
                - *      JProgressBar pbar = new JProgressBar();
                - *      bar.add(pbar, c2);            // Fill with no inserts - will use remaining space
                - * 

                - * - *

                Two common use cases for status bars include tracking application status and - * progress. JXStatusBar does not manage these tasks, but instead special components - * exist or can be created that do manage these tasks. For example, if your application - * has a TaskManager or some other repository of currently running jobs, you could - * easily create a TaskManagerProgressBar that tracks those jobs. This component - * could then be added to the JXStatusBar like any other component.

                - * - *

                Client Properties

                - *

                The BasicStatusBarUI.AUTO_ADD_SEPARATOR client property can be specified, which - * will disable the auto-adding of separators. In this case, you must add your own - * JSeparator components. To use: - *

                
                - *      JXStatusBar sbar = new JXStatusBar();
                - *      sbar.putClientProperty(BasicStatusBarUI.AUTO_ADD_SEPARATOR, false);
                - *      sbar.add(comp1);
                - *      sbar.add(new JSeparator(JSeparator.VERTICAL));
                - *      sbar.add(comp2);
                - *      sbar.add(comp3);
                - *  

                - * - * @status REVIEWED - * - * @author pdoubleya - * @author rbair - * @author Karl George Schaefer - */ -@JavaBean -public class JXStatusBar extends JComponent { - /** - * @see #getUIClassID - * @see #readObject - */ - public static final String uiClassID = "StatusBarUI"; - - //TODO how to handle UI delegate setting of primitive? - private boolean resizeHandleEnabled; - - /** - * Initialization that would ideally be moved into various look and feel - * classes. - */ - static { - LookAndFeelAddons.contribute(new StatusBarAddon()); - } - - /** - * Creates a new JXStatusBar - */ - public JXStatusBar() { - super(); - updateUI(); - } - - /** - * @param resizeHandleEnabled the resizeHandleEnabled to set - */ - public void setResizeHandleEnabled(boolean resizeHandleEnabled) { - boolean oldValue = isResizeHandleEnabled(); - this.resizeHandleEnabled = resizeHandleEnabled; - firePropertyChange("resizeHandleEnabled", oldValue, isResizeHandleEnabled()); - } - - /** - * @return the resizeHandleEnabled - */ - public boolean isResizeHandleEnabled() { - return resizeHandleEnabled; - } - - /** - * Returns the look and feel (L&F) object that renders this component. - * - * @return the StatusBarUI object that renders this component - */ - public StatusBarUI getUI() { - return (StatusBarUI) ui; - } - - /** - * Sets the look and feel (L&F) object that renders this component. - * - * @param ui - * the StatusBarUI L&F object - * @see javax.swing.UIDefaults#getUI - * @beaninfo - * bound: true - * hidden: true - * attribute: visualUpdate true - * description: The component's look and feel delegate. - */ - public void setUI(StatusBarUI ui) { - super.setUI(ui); - } - - /** - * Returns a string that specifies the name of the L&F class that renders - * this component. - * - * @return "StatusBarUI" - * @see JComponent#getUIClassID - * @see javax.swing.UIDefaults#getUI - * @beaninfo expert: true description: A string that specifies the name of - * the L&F class. - */ - @Override - public String getUIClassID() { - return uiClassID; - } - - /** - * Notification from the UIManager that the L&F has changed. - * Replaces the current UI object with the latest version from the - * UIManager. - * - * @see JComponent#updateUI - */ - @Override - public void updateUI() { - setUI((StatusBarUI) LookAndFeelAddons - .getUI(this, StatusBarUI.class)); - } - - /** - * The constraint object to be used with the JXStatusBar. It takes - * a ResizeBehaviour, Insets and a Width. Width is only applicable for - * ResizeBehavior.FIXED. @see JXStatusBar class documentation. - */ - public static class Constraint { - public enum ResizeBehavior {FILL, FIXED} - - private final Insets insets; - private final ResizeBehavior resizeBehavior; - private int fixedWidth = 0; - - /** - * Creates a new Constraint with default FIXED behaviour and no insets. - */ - public Constraint() { - this(ResizeBehavior.FIXED, null); - } - - /** - * Creates a new Constraint with default FIXED behaviour and the given insets - * - * @param insets may be null. If null, an Insets with 0 values will be used. - */ - public Constraint(Insets insets) { - this(ResizeBehavior.FIXED, insets); - } - - /** - * Creates a new Constraint with default FIXED behaviour and the given fixed - * width. - * - * @param fixedWidth must be >= 0 - */ - public Constraint(int fixedWidth) { - this(fixedWidth, null); - } - - /** - * Creates a new Constraint with default FIXED behaviour and the given fixed - * width, and using the given Insets. - * - * @param fixedWidth must be >= 0 - * @param insets may be null. If null, an Insets with 0 values will be used. - */ - public Constraint(int fixedWidth, Insets insets) { - if (fixedWidth < 0) { - throw new IllegalArgumentException("fixedWidth must be >= 0"); - } - this.fixedWidth = fixedWidth; - this.insets = insets == null ? new Insets(0, 0, 0, 0) : (Insets)insets.clone(); - this.resizeBehavior = ResizeBehavior.FIXED; - } - - /** - * Creates a new Constraint with the specified resize behaviour and no insets - * - * @param resizeBehavior - either JXStatusBar.Constraint.ResizeBehavior.FIXED - * or JXStatusBar.Constraint.ResizeBehavior.FILL. - */ - public Constraint(ResizeBehavior resizeBehavior) { - this(resizeBehavior, null); - } - - /** - * Creates a new Constraint with the specified resize behavior and insets. - * - * @param resizeBehavior - either JXStatusBar.Constraint.ResizeBehavior.FIXED - * or JXStatusBar.Constraints.ResizeBehavior.FILL. - * @param insets may be null. If null, an Insets with 0 values will be used. - */ - public Constraint(ResizeBehavior resizeBehavior, Insets insets) { - this.resizeBehavior = resizeBehavior; - this.insets = insets == null ? new Insets(0, 0, 0, 0) : (Insets)insets.clone(); - } - - /** - * Set the fixed width the component added with this - * constraint will occupy on the JXStatusBar. Only applies - * to ResizeBehavior.FIXED. Will be ignored for ResizeBehavior.FILL. - * - * @param width - minimum width component will occupy. If 0, the preferred - * width of the component will be used. - * The width specified must be >= 0 - */ - public void setFixedWidth(int width) { - if (width < 0) { - throw new IllegalArgumentException("width must be >= 0"); - } - fixedWidth = resizeBehavior == ResizeBehavior.FIXED ? width : 0; - } - - /** - * Returns the ResizeBehavior. - * - * @return ResizeBehavior - */ - public ResizeBehavior getResizeBehavior() { - return resizeBehavior; - } - - /** - * Returns the insets. - * - * @return insets - */ - public Insets getInsets() { - return (Insets)insets.clone(); - } - - /** - * Get fixed width. Width is zero for resize behavior FILLED - * @return the width of this constraint - */ - public int getFixedWidth() { - return fixedWidth; - } - } - -} diff --git a/src/main/java/org/jdesktop/swingx/JXTable.java b/src/main/java/org/jdesktop/swingx/JXTable.java deleted file mode 100644 index 419ffb5bff..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXTable.java +++ /dev/null @@ -1,4399 +0,0 @@ -/* - * $Id: JXTable.java 4266 2012-12-05 16:34:37Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.action.AbstractActionExt; -import org.jdesktop.swingx.action.BoundAction; -import org.jdesktop.swingx.decorator.ComponentAdapter; -import org.jdesktop.swingx.decorator.CompoundHighlighter; -import org.jdesktop.swingx.decorator.Highlighter; -import org.jdesktop.swingx.decorator.ResetDTCRColorHighlighter; -import org.jdesktop.swingx.event.TableColumnModelExtListener; -import org.jdesktop.swingx.hyperlink.HyperlinkAction; -import org.jdesktop.swingx.plaf.*; -import org.jdesktop.swingx.renderer.*; -import org.jdesktop.swingx.rollover.RolloverProducer; -import org.jdesktop.swingx.rollover.TableRolloverController; -import org.jdesktop.swingx.rollover.TableRolloverProducer; -import org.jdesktop.swingx.search.AbstractSearchable; -import org.jdesktop.swingx.search.SearchFactory; -import org.jdesktop.swingx.search.Searchable; -import org.jdesktop.swingx.search.TableSearchable; -import org.jdesktop.swingx.sort.*; -import org.jdesktop.swingx.table.*; - -import javax.swing.*; -import javax.swing.RowSorter.SortKey; -import javax.swing.border.LineBorder; -import javax.swing.event.*; -import javax.swing.table.*; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.print.PrinterException; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.net.URI; -import java.util.List; -import java.util.*; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Enhanced Table component with support for general SwingX sorting/filtering, - * rendering, highlighting, rollover and search functionality. Table specific - * enhancements include runtime configuration options like toggle column - * visibility, column sizing, PENDING JW ... - * - *

                Sorting and Filtering

                - * - * JXTable supports sorting and filtering of rows (switched to core sorting). - * - * Additionally, it provides api to apply - * a specific sort order, to toggle the sort order of columns identified - * by view index or column identifier and to reset all sorts. F.i: - * - *
                
                - * table.setSortOrder("PERSON_ID", SortOrder.DESCENDING);
                - * table.toggleSortOder(4);
                - * table.resetSortOrder();
                - * 
                - * - * Sorting sequence can be configured per column by setting the TableColumnExt's - * comparator property. Sorting can be disabled per column - setting the TableColumnExt's - * sortable or per table by {@link #setSortable(boolean)}. - * The table takes responsibility to propagate these - * properties to the current sorter, if available

                - * - * Note that the enhanced sorting controls are effective only if the RowSorter is - * of type SortController, which it is by default. Different from core JTable, the - * autoCreateRowSorter property is enabled by default. If on, the JXTable creates and - * uses a default row sorter as returned by the createDefaultRowSorter method. - * - *

                - * Typically, a JXTable is sortable by left clicking on column headers. By default, each - * subsequent click on a header reverses the order of the sort, and a sort arrow - * icon is automatically drawn on the header. - * - *

                - * - *

                Rendering and Highlighting

                - * - * As all SwingX collection views, a JXTable is a HighlighterClient (PENDING JW: - * formally define and implement, like in AbstractTestHighlighter), that is it - * provides consistent api to add and remove Highlighters which can visually - * decorate the rendering component. - * - *

                - * An example multiple highlighting (default striping as appropriate for the - * current LookAndFeel, cell foreground on matching pattern, and shading a - * column): - * - *

                
                - * 
                - * Highlighter simpleStriping = HighlighterFactory.createSimpleStriping();
                - * PatternPredicate patternPredicate = new PatternPredicate("ˆM", 1);
                - * ColorHighlighter magenta = new ColorHighlighter(patternPredicate, null,
                - *       Color.MAGENTA, null, Color.MAGENTA);
                - * Highlighter shading = new ShadingColorHighlighter(
                - *       new HighlightPredicate.ColumnHighlightPredicate(1));
                - * 
                - * table.setHighlighters(simpleStriping,
                - *        magenta,
                - *        shading);
                - * 
                - * - *

                - * To fully support, JXTable registers SwingX default table renderers instead of - * core defaults (see {@link DefaultTableRenderer}) The recommended approach for - * customizing rendered content it to intall a DefaultTableRenderer configured - * with a custom String- and/or IconValue. F.i. assuming the cell value is a - * File and should be rendered by showing its name followed and date of last - * change: - * - *

                
                - * StringValue sv = new StringValue() {
                - *      public String getString(Object value) {
                - *        if (!(value instanceof File)) return StringValues.TO_STRING.getString(value);
                - *        return StringValues.FILE_NAME.getString(value) + ", " 
                - *           + StringValues.DATE_TO_STRING.getString(((File) value).lastModified());
                - * }};
                - * table.setCellRenderer(File.class, new DefaultTableRenderer(sv));
                - * 
                - * - * In addition to super default per-class registration, JXTable registers a default - * renderer for URIs which opens the default application to view the related - * document as supported by Desktop. Note: this action is triggered only if - * rolloverEnabled is true (default value) and the cell is not editable. - * - *

                - * Note: DefaultTableCellRenderer and subclasses require a hack to play - * nicely with Highlighters because it has an internal "color memory" in - * setForeground/setBackground. The hack is applied by default which might lead - * to unexpected side-effects in custom renderers subclassing DTCR. See - * {@link #resetDefaultTableCellRendererHighlighter} for details. - *

                - * - * Note: by default JXTable disables the alternate row striping provided - * by Nimbus, instead it does use the color provided by Nimbus to configure the - * UIColorHighlighter. Like in any other LAF without striping support, - * client code has to explicitly turn on striping by - * setting a Highlighter like: - * - *

                
                - * table.addHighlighter(HighlighterFactory.createSimpleStriping());
                - * 
                - * - * Alternatively, if client code wants to rely on the LAF provided striping - * support, it can set a property in the UIManager ("early" in the application - * lifetime to prevent JXTable to disable Nimbus handling it. In this case it is - * recommended to not any of the ui-dependent Highlighters provided by the - * HighlighterFactory. - * - *
                
                - * UIManager.put("Nimbus.keepAlternateRowColor", Boolean.TRUE);
                - * 
                - * - *

                Rollover

                - * - * As all SwingX collection views, a JXTable supports per-cell rollover which is - * enabled by default. If enabled, the component fires rollover events on - * enter/exit of a cell which by default is promoted to the renderer if it - * implements RolloverRenderer, that is simulates live behaviour. The rollover - * events can be used by client code as well, f.i. to decorate the rollover row - * using a Highlighter. - * - *
                
                - * JXTable table = new JXTable();
                - * table.addHighlighter(new ColorHighlighter(HighlightPredicate.ROLLOVER_ROW, 
                - *      null, Color.RED);      
                - * 
                - * - *

                Search

                - * - * As all SwingX collection views, a JXTable is searchable. A search action is - * registered in its ActionMap under the key "find". The default behaviour is to - * ask the SearchFactory to open a search component on this component. The - * default keybinding is retrieved from the SearchFactory, typically ctrl-f (or - * cmd-f for Mac). Client code can register custom actions and/or bindings as - * appropriate. - *

                - * - * JXTable provides api to vend a renderer-controlled String representation of - * cell content. This allows the Searchable and Highlighters to use WYSIWYM - * (What-You-See-Is-What-You-Match), that is pattern matching against the actual - * string as seen by the user. - * - *

                Column Configuration

                - * - * JXTable's default column model - * is of type TableColumnModelExt which allows management of hidden columns. - * Furthermore, it guarantees to delegate creation and configuration of table columns - * to its ColumnFactory. The factory is meant as the central place to - * customize column configuration. - * - *

                - * Columns can be hidden or shown by setting the visible property on the - * TableColumnExt using {@link TableColumnExt#setVisible(boolean)}. Columns can - * also be shown or hidden from the column control popup. - * - *

                - * The column control popup is triggered by an icon drawn to the far right of - * the column headers, above the table's scrollbar (when installed in a - * JScrollPane). The popup allows the user to select which columns should be - * shown or hidden, as well as to pack columns and turn on horizontal scrolling. - * To show or hide the column control, use the - * {@link #setColumnControlVisible(boolean show)}method. - * - *

                - * You can resize all columns, selected columns, or a single column using the - * methods like {@link #packAll()}. Packing combines several other aspects of a - * JXTable. If horizontal scrolling is enabled using - * {@link #setHorizontalScrollEnabled(boolean)}, then the scrollpane will allow - * the table to scroll right-left, and columns will be sized to their preferred - * size. To control the preferred sizing of a column, you can provide a - * prototype value for the column in the TableColumnExt using - * {@link TableColumnExt#setPrototypeValue(Object)}. The prototype is used as an - * indicator of the preferred size of the column. This can be useful if some - * data in a given column is very long, but where the resize algorithm would - * normally not pick this up. - * - *

                - * - * - *

                - * Keys/Actions registered with this component: - * - *

                  - *
                • "find" - open an appropriate search widget for searching cell content. - * The default action registeres itself with the SearchFactory as search target. - *
                • "print" - print the table - *
                • {@link JXTable#HORIZONTALSCROLL_ACTION_COMMAND} - toggle the horizontal - * scrollbar - *
                • {@link JXTable#PACKSELECTED_ACTION_COMMAND} - resize the selected column - * to fit the widest cell content - *
                • {@link JXTable#PACKALL_ACTION_COMMAND} - resize all columns to fit the - * widest cell content in each column - * - *
                - * - *

                - * Key bindings. - * - *

                  - *
                • "control F" - bound to actionKey "find". - *
                - * - *

                - * Client Properties. - * - *

                  - *
                • {@link JXTable#MATCH_HIGHLIGHTER} - set to Boolean.TRUE to use a - * SearchHighlighter to mark a cell as matching. - *
                - * - * @author Ramesh Gupta - * @author Amy Fowler - * @author Mark Davidson - * @author Jeanette Winzenburg - * - */ -@JavaBean -public class JXTable extends JTable implements TableColumnModelExtListener { - - /** - * - */ - public static final String FOCUS_PREVIOUS_COMPONENT = "focusPreviousComponent"; - - /** - * - */ - public static final String FOCUS_NEXT_COMPONENT = "focusNextComponent"; - - private static final Logger LOG = Logger.getLogger(JXTable.class.getName()); - - /** - * Identifier of show horizontal scroll action, used in JXTable's - * ActionMap. - * - */ - public static final String HORIZONTALSCROLL_ACTION_COMMAND = ColumnControlButton.COLUMN_CONTROL_MARKER - + "horizontalScroll"; - - /** - * Identifier of pack table action, used in JXTable's ActionMap - * . - */ - public static final String PACKALL_ACTION_COMMAND = ColumnControlButton.COLUMN_CONTROL_MARKER - + "packAll"; - - /** - * Identifier of pack selected column action, used in JXTable's - * ActionMap. - */ - public static final String PACKSELECTED_ACTION_COMMAND = ColumnControlButton.COLUMN_CONTROL_MARKER - + "packSelected"; - - /** - * The prefix marker to find table related properties in the - * ResourceBundle. - */ - public static final String UIPREFIX = "JXTable."; - - /** key for client property to use SearchHighlighter as match marker. */ - public static final String MATCH_HIGHLIGHTER = AbstractSearchable.MATCH_HIGHLIGHTER; - - static { - // Hack: make sure the resource bundle is loaded - LookAndFeelAddons.getAddon(); - LookAndFeelAddons.contribute(new TableAddon()); - } - - /** The CompoundHighlighter for the table. */ - protected CompoundHighlighter compoundHighlighter; - - /** - * The key for the client property deciding about whether the color memory - * hack for DefaultTableCellRenderer should be used. - * - * @see #resetDefaultTableCellRendererHighlighter - */ - public static final String USE_DTCR_COLORMEMORY_HACK = "useDTCRColorMemoryHack"; - - /** - * The Highlighter used to hack around DefaultTableCellRenderer's color - * memory. - */ - protected Highlighter resetDefaultTableCellRendererHighlighter; - - /** The ComponentAdapter for model data access. */ - protected ComponentAdapter dataAdapter; - - - - /** Listens for changes from the highlighters. */ - private ChangeListener highlighterChangeListener; - - /** the factory to use for column creation and configuration. */ - private ColumnFactory columnFactory; - - /** The default number of visible rows (in a ScrollPane). */ - private int visibleRowCount = 20; - - /** The default number of visible columns (in a ScrollPane). */ - private int visibleColumnCount = -1; - - - /** - * Flag to indicate if the column control is visible. - */ - private boolean columnControlVisible; - - /** - * ScrollPane's original vertical scroll policy. If the column control is - * visible the policy is set to ALWAYS. - */ - private int verticalScrollPolicy; - - /** - * The component used a column control in the upper trailing corner of an - * enclosing JScrollPane. - */ - private JComponent columnControlButton; - - /** - * Mouse/Motion/Listener keeping track of mouse moved in cell coordinates. - */ - private transient RolloverProducer rolloverProducer; - - /** - * RolloverController: listens to cell over events and repaints - * entered/exited rows. - */ - private transient TableRolloverController linkController; - - /** - * field to store the autoResizeMode while interactively setting horizontal - * scrollbar to visible. - */ - private int oldAutoResizeMode; - - /** - * flag to indicate enhanced auto-resize-off behaviour is on. This is - * set/reset in setHorizontalScrollEnabled. - */ - private boolean intelliMode; - - /** - * internal flag indicating that we are in super.doLayout(). (used in - * columnMarginChanged to not update the resizingCol's prefWidth). - */ - private boolean inLayout; - - /** - * Flag to distinguish internal settings of row height from client code - * settings. The rowHeight will be internally adjusted to font size on - * instantiation and in updateUI if the height has not been set explicitly - * by the application. - * - * @see #adminSetRowHeight(int) - * @see #setRowHeight(int) - */ - protected boolean isXTableRowHeightSet; - - /** property to control search behaviour. */ - protected Searchable searchable; - - /** property to control table's editability as a whole. */ - private boolean editable; - - private Dimension calculatedPrefScrollableViewportSize; - - /** flag to indicate whether the rowSorter is auto-created. */ - private boolean autoCreateRowSorter; - /** flag to indicate if table is interactively sortable. */ - private boolean sortable; - /** flag to indicate whether model update events should trigger resorts. */ - private boolean sortsOnUpdates; - /** flag to indicate that it's unsafe to update sortable-related sorter properties. */ - private boolean ignoreAddColumn; - /** Registry of per-cell string representation. */ - private transient StringValueRegistry stringValueRegistry; - - private SortOrder[] sortOrderCycle; - - - /** Instantiates a JXTable with a default table model, no data. */ - public JXTable() { - init(); - } - - /** - * Instantiates a JXTable with a specific table model. - * - * @param dm The model to use. - */ - public JXTable(TableModel dm) { - super(dm); - init(); - } - - /** - * Instantiates a JXTable with a specific table model. - * - * @param dm The model to use. - */ - public JXTable(TableModel dm, TableColumnModel cm) { - super(dm, cm); - init(); - } - - /** - * Instantiates a JXTable with a specific table model, column model, and - * selection model. - * - * @param dm The table model to use. - * @param cm The column model to use. - * @param sm The list selection model to use. - */ - public JXTable(TableModel dm, TableColumnModel cm, ListSelectionModel sm) { - super(dm, cm, sm); - init(); - } - - /** - * Instantiates a JXTable for a given number of columns and rows. - * - * @param numRows Count of rows to accommodate. - * @param numColumns Count of columns to accommodate. - */ - public JXTable(int numRows, int numColumns) { - super(numRows, numColumns); - init(); - } - - /** - * Instantiates a JXTable with data in a vector or rows and column names. - * - * @param rowData Row data, as a Vector of Objects. - * @param columnNames Column names, as a Vector of Strings. - */ - public JXTable(Vector rowData, Vector columnNames) { - super(rowData, columnNames); - init(); - } - - /** - * Instantiates a JXTable with data in a array or rows and column names. - * - * @param rowData Row data, as a two-dimensional Array of Objects (by row, - * for column). - * @param columnNames Column names, as a Array of Strings. - */ - public JXTable(Object[][] rowData, Object[] columnNames) { - super(rowData, columnNames); - init(); - } - - /** - * Initializes the table for use. - * - */ - private void init() { - putClientProperty(USE_DTCR_COLORMEMORY_HACK, Boolean.TRUE); - initDefaultStringValues(); - sortOrderCycle = DefaultSortController.getDefaultSortOrderCycle(); - setSortsOnUpdates(true); - setSortable(true); - setAutoCreateRowSorter(true); - setRolloverEnabled(true); - setEditable(true); - setTerminateEditOnFocusLost(true); - initActionsAndBindings(); - initFocusBindings(); - // instantiate row height depending ui setting or font size. - updateRowHeightUI(false); - // set to null - don't want hard-coded pixel sizes. - setPreferredScrollableViewportSize(null); - // PENDING: need to duplicate here.. - // why doesn't the call in tableChanged work? - initializeColumnWidths(); - setFillsViewportHeight(true); - updateLocaleState(getLocale()); - } - -//--------------- Rollover support - /** - * Sets the property to enable/disable rollover support. If enabled, this component - * fires property changes on per-cell mouse rollover state, i.e. - * when the mouse enters/leaves a list cell.

                - * - * This can be enabled to show "live" rollover behaviour, f.i. the cursor over a cell - * rendered by a JXHyperlink.

                - * - * The default value is true. - * - * @param rolloverEnabled a boolean indicating whether or not the rollover - * functionality should be enabled. - * - * @see #isRolloverEnabled() - * @see #getLinkController() - * @see #createRolloverProducer() - * @see org.jdesktop.swingx.rollover.RolloverRenderer - */ - public void setRolloverEnabled(boolean rolloverEnabled) { - boolean old = isRolloverEnabled(); - if (rolloverEnabled == old) - return; - if (rolloverEnabled) { - rolloverProducer = createRolloverProducer(); - rolloverProducer.install(this); - getLinkController().install(this); - - } else { - rolloverProducer.release(this); - rolloverProducer = null; - getLinkController().release(); - } - firePropertyChange("rolloverEnabled", old, isRolloverEnabled()); - } - - /** - * Returns a boolean indicating whether or not rollover support is enabled. - * - * @return a boolean indicating whether or not rollover support is enabled. - * - * @see #setRolloverEnabled(boolean) - */ - public boolean isRolloverEnabled() { - return rolloverProducer != null; - } - - /** - * Returns the RolloverController for this component. Lazyly creates the - * controller if necessary, that is the return value is guaranteed to be - * not null.

                - * - * PENDING JW: rename to getRolloverController - * - * @return the RolloverController for this tree, guaranteed to be not null. - * - * @see #setRolloverEnabled(boolean) - * @see #createLinkController() - * @see org.jdesktop.swingx.rollover.RolloverController - */ - protected TableRolloverController getLinkController() { - if (linkController == null) { - linkController = createLinkController(); - } - return linkController; - } - - /** - * Creates and returns a RolloverController appropriate for this component. - * - * @return a RolloverController appropriate for this component. - * - * @see #getLinkController() - * @see org.jdesktop.swingx.rollover.RolloverController - */ - protected TableRolloverController createLinkController() { - return new TableRolloverController(); - } - - /** - * Creates and returns the RolloverProducer to use with this component. - *

                - * - * @return RolloverProducer to use with this component - * - * @see #setRolloverEnabled(boolean) - */ - protected RolloverProducer createRolloverProducer() { - return new TableRolloverProducer(); - } - - - /** - * Returns the column control visible property. - *

                - * - * @return boolean to indicate whether the column control is visible. - * @see #setColumnControlVisible(boolean) - * @see #setColumnControl(JComponent) - */ - public boolean isColumnControlVisible() { - return columnControlVisible; - } - - /** - * Sets the column control visible property. If true and - * JXTable is contained in a JScrollPane, the - * table adds the column control to the trailing corner of the scroll pane. - *

                - * - * Note: if the table is not inside a JScrollPane the column - * control is not shown even if this returns true. In this case it's the - * responsibility of the client code to actually show it. - *

                - * - * The default value is false. - * - * @param visible boolean to indicate if the column control should be shown - * @see #isColumnControlVisible() - * @see #setColumnControl(JComponent) - * - */ - public void setColumnControlVisible(boolean visible) { - if (isColumnControlVisible() == visible) - return; - boolean old = isColumnControlVisible(); - if (old) { - unconfigureColumnControl(); - } - this.columnControlVisible = visible; - if (isColumnControlVisible()) { - configureColumnControl(); - } - firePropertyChange("columnControlVisible", old, !old); - - } - - /** - * Returns the component used as column control. Lazily creates the control - * to the default if it is null. - * - * @return component for column control, guaranteed to be != null. - * @see #setColumnControl(JComponent) - * @see #createDefaultColumnControl() - */ - public JComponent getColumnControl() { - if (columnControlButton == null) { - columnControlButton = createDefaultColumnControl(); - } - return columnControlButton; - } - - /** - * Sets the component used as column control. Updates the enclosing - * JScrollPane if appropriate. Passing a null - * parameter restores the column control to the default. - *

                - * The component is automatically visible only if the - * columnControlVisible property is true and the - * table is contained in a JScrollPane. - * - *

                - * NOTE: from the table's perspective, the column control is simply a - * JComponent to add to and keep in the trailing corner of the - * scrollpane. (if any). It's up the concrete control to configure itself - * from and keep synchronized to the columns' states. - *

                - * - * @param columnControl the JComponent to use as columnControl. - * @see #getColumnControl() - * @see #createDefaultColumnControl() - * @see #setColumnControlVisible(boolean) - * - */ - public void setColumnControl(JComponent columnControl) { - // PENDING JW: release old column control? who's responsible? - // Could implement CCB.autoRelease()? - JComponent old = columnControlButton; - this.columnControlButton = columnControl; - configureColumnControl(); - firePropertyChange("columnControl", old, getColumnControl()); - } - - /** - * Creates the default column control used by this table. This - * implementation returns a ColumnControlButton configured with - * default ColumnControlIcon. - * - * @return the default component used as column control. - * @see #setColumnControl(JComponent) - * @see ColumnControlButton - * @see org.jdesktop.swingx.icon.ColumnControlIcon - */ - protected JComponent createDefaultColumnControl() { - return new ColumnControlButton(this); - } - - /** - * Sets the language-sensitive orientation that is to be used to order the - * elements or text within this component. - *

                - * - * Overridden to work around a core bug: JScrollPane can't cope - * with corners when changing component orientation at runtime. This method - * explicitly re-configures the column control. - *

                - * - * @param o the ComponentOrientation for this table. - * @see Component#setComponentOrientation(ComponentOrientation) - */ - @Override - public void setComponentOrientation(ComponentOrientation o) { - removeColumnControlFromCorners(); - super.setComponentOrientation(o); - configureColumnControl(); - } - - /** - * Sets upper corners in JScrollPane to null if same as getColumnControl(). - * This is a hack around core not coping correctly with component orientation. - * - * @see #setComponentOrientation(ComponentOrientation) - */ - protected void removeColumnControlFromCorners() { - JScrollPane scrollPane = getEnclosingScrollPane(); - if ((scrollPane == null) || !isColumnControlVisible()) return; - removeColumnControlFromCorners(scrollPane, - JScrollPane.UPPER_LEFT_CORNER, JScrollPane.UPPER_RIGHT_CORNER); - } - - private void removeColumnControlFromCorners(JScrollPane scrollPane, String... corners) { - for (String corner : corners) { - if (scrollPane.getCorner(corner) == getColumnControl()) { - scrollPane.setCorner(corner, null); - } - } - } - - /** - * Configures the enclosing JScrollPane. - *

                - * - * Overridden to addionally configure the upper trailing corner with the - * column control. - * - * @see #configureColumnControl() - * - */ - @Override - protected void configureEnclosingScrollPane() { - super.configureEnclosingScrollPane(); - configureColumnControl(); - } - - /** - * Unconfigures the enclosing JScrollPane. - *

                - * - * Overridden to addionally unconfigure the upper trailing corner with the - * column control. - * - * @see #unconfigureColumnControl() - * - */ - @Override - protected void unconfigureEnclosingScrollPane() { - unconfigureColumnControl(); - super.unconfigureEnclosingScrollPane(); - } - - /** - * /** Unconfigures the upper trailing corner of an enclosing - * JScrollPane. - * - * Here: removes the upper trailing corner and resets. - * - * @see #setColumnControlVisible(boolean) - * @see #setColumnControl(JComponent) - */ - protected void unconfigureColumnControl() { - JScrollPane scrollPane = getEnclosingScrollPane(); - if (scrollPane == null) return; - if (verticalScrollPolicy != 0) { - // Fix #155-swingx: reset only if we had forced always before - // PENDING: JW - doesn't cope with dynamically changing the - // policy - // shouldn't be much of a problem because doesn't happen too - // often?? - scrollPane.setVerticalScrollBarPolicy(verticalScrollPolicy); - verticalScrollPolicy = 0; - } - if (isColumnControlVisible()) { - scrollPane.setCorner(JScrollPane.UPPER_TRAILING_CORNER, null); - } - } - - /** - * Configures the upper trailing corner of an enclosing - * JScrollPane. - * - * Adds the ColumnControl if the - * columnControlVisible property is true. - *

                - * - * @see #setColumnControlVisible(boolean) - * @see #setColumnControl(JComponent) - */ - protected void configureColumnControl() { - if (!isColumnControlVisible()) - return; - JScrollPane scrollPane = getEnclosingScrollPane(); - if (scrollPane == null) return; - if (verticalScrollPolicy == 0) { - verticalScrollPolicy = scrollPane.getVerticalScrollBarPolicy(); - } - scrollPane.setCorner(JScrollPane.UPPER_TRAILING_CORNER, - getColumnControl()); - scrollPane - .setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); - } - - /** - * Returns the enclosing JScrollPane of this table, or null if not - * contained in a JScrollPane or not the main view of the scrollPane. - * - * @return the enclosing JScrollPane if this table is the main view or - * null if not. - */ - protected JScrollPane getEnclosingScrollPane() { - Container p = getParent(); - if (p instanceof JViewport) { - Container gp = p.getParent(); - if (gp instanceof JScrollPane) { - JScrollPane scrollPane = (JScrollPane) gp; - // Make certain we are the viewPort's view and not, for - // example, the rowHeaderView of the scrollPane - - // an implementor of fixed columns might do this. - JViewport viewport = scrollPane.getViewport(); - if (viewport == null || viewport.getView() != this) { - return null; - } - return scrollPane; - } - } - return null; - } - - - // --------------------- actions - /** - * Take over ctrl-tab. - * - */ - private void initFocusBindings() { - setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, - new TreeSet()); - setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, - new TreeSet()); - getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put( - KeyStroke.getKeyStroke("ctrl TAB"), FOCUS_NEXT_COMPONENT); - getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put( - KeyStroke.getKeyStroke("shift ctrl TAB"), - FOCUS_PREVIOUS_COMPONENT); - getActionMap().put(FOCUS_NEXT_COMPONENT, - createFocusTransferAction(true)); - getActionMap().put(FOCUS_PREVIOUS_COMPONENT, - createFocusTransferAction(false)); - } - - /** - * Creates and returns an action for forward/backward focus transfer, - * depending on the given flag. - * - * @param forward a boolean indicating the direction of the required focus - * transfer - * @return the action bound to focusTraversal. - */ - private Action createFocusTransferAction(final boolean forward) { - BoundAction action = new BoundAction(null, - forward ? FOCUS_NEXT_COMPONENT : FOCUS_PREVIOUS_COMPONENT); - action.registerCallback(this, forward ? "transferFocus" - : "transferFocusBackward"); - return action; - } - - /** - * A small class which dispatches actions. - *

                - * TODO (?): Is there a way that we can make this static? - *

                - * - * PENDING JW: don't use UIAction ... we are in OO-land! - */ - private class Actions extends UIAction { - Actions(String name) { - super(name); - } - - @Override - public void actionPerformed(ActionEvent evt) { - if ("print".equals(getName())) { - try { - print(); - } catch (PrinterException ex) { - // REMIND(aim): should invoke pluggable application error - // handler - LOG.log(Level.WARNING, "", ex); - } - } else if ("find".equals(getName())) { - doFind(); - } - } - - } - - /** - * Registers additional, per-instance Actions to the this - * table's ActionMap. Binds the search accelerator (as returned by the - * SearchFactory) to the find action. - * - * - */ - private void initActionsAndBindings() { - // Register the actions that this class can handle. - ActionMap map = getActionMap(); - map.put("print", new Actions("print")); - map.put("find", new Actions("find")); - // hack around core bug: cancel editing doesn't fire - // reported against SwingX as of #610-swingx - map.put("cancel", createCancelAction()); - map.put(PACKALL_ACTION_COMMAND, createPackAllAction()); - map.put(PACKSELECTED_ACTION_COMMAND, createPackSelectedAction()); - map - .put(HORIZONTALSCROLL_ACTION_COMMAND, - createHorizontalScrollAction()); - - KeyStroke findStroke = SearchFactory.getInstance() - .getSearchAccelerator(); - getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put( - findStroke, "find"); - } - - /** - * Creates and returns an Action which cancels an ongoing edit correctly. - * Note: the correct thing to do is to call the editor's cancelEditing, the - * wrong thing to do is to call table removeEditor (as core JTable does...). - * So this is a quick hack around a core bug, reported against SwingX in - * #610-swingx. - * - * @return an Action which cancels an edit. - */ - private Action createCancelAction() { - Action action = new AbstractActionExt() { - - @Override - public void actionPerformed(ActionEvent e) { - if (!isEditing()) - return; - getCellEditor().cancelCellEditing(); - } - - @Override - public boolean isEnabled() { - return isEditing(); - } - - }; - return action; - } - - /** - * Creates and returns the default Action for toggling the - * horizontal scrollBar. - */ - private Action createHorizontalScrollAction() { - BoundAction action = new BoundAction(null, - HORIZONTALSCROLL_ACTION_COMMAND); - action.setStateAction(); - action.registerCallback(this, "setHorizontalScrollEnabled"); - action.setSelected(isHorizontalScrollEnabled()); - return action; - } - - /** - * Returns a potentially localized value from the UIManager. The given key - * is prefixed by this table's UIPREFIX before doing the - * lookup. The lookup respects this table's current locale - * property. Returns the key, if no value is found. - * - * @param key the bare key to look up in the UIManager. - * @return the value mapped to UIPREFIX + key or key if no value is found. - */ - protected String getUIString(String key) { - return getUIString(key, getLocale()); - } - - /** - * Returns a potentially localized value from the UIManager for the given - * locale. The given key is prefixed by this table's UIPREFIX - * before doing the lookup. Returns the key, if no value is found. - * - * @param key the bare key to look up in the UIManager. - * @param locale the locale use for lookup - * @return the value mapped to UIPREFIX + key in the given locale, or key if - * no value is found. - */ - protected String getUIString(String key, Locale locale) { - String text = UIManagerExt.getString(UIPREFIX + key, locale); - return text != null ? text : key; - } - - /** - * Creates and returns the default Action for packing the - * selected column. - */ - private Action createPackSelectedAction() { - BoundAction action = new BoundAction(null, PACKSELECTED_ACTION_COMMAND); - action.registerCallback(this, "packSelected"); - action.setEnabled(getSelectedColumnCount() > 0); - return action; - } - - /** - * Creates and returns the default Action for packing all columns. - */ - private Action createPackAllAction() { - BoundAction action = new BoundAction(null, PACKALL_ACTION_COMMAND); - action.registerCallback(this, "packAll"); - return action; - } - - /** - * {@inheritDoc} - *

                - * Overridden to update locale-dependent properties. - * - * @see #updateLocaleState(Locale) - */ - @Override - public void setLocale(Locale locale) { - updateLocaleState(locale); - super.setLocale(locale); - } - - /** - * Updates locale-dependent state to the given Locale. - * - * Here: updates registered column actions' locale-dependent state. - *

                - * - * PENDING: Try better to find all column actions including custom - * additions? Or move to columnControl? - * - * @param locale the Locale to use for value lookup - * @see #setLocale(Locale) - * @see #updateLocaleActionState(String, Locale) - */ - protected void updateLocaleState(Locale locale) { - updateLocaleActionState(HORIZONTALSCROLL_ACTION_COMMAND, locale); - updateLocaleActionState(PACKALL_ACTION_COMMAND, locale); - updateLocaleActionState(PACKSELECTED_ACTION_COMMAND, locale); - } - - /** - * Updates locale-dependent state of action registered with key in - * ActionMap. Does nothing if no action with key is found. - *

                - * - * Here: updates the Action's name property. - * - * @param key the string for lookup in this table's ActionMap - * @see #updateLocaleState(Locale) - */ - protected void updateLocaleActionState(String key, Locale locale) { - Action action = getActionMap().get(key); - if (action == null) - return; - action.putValue(Action.NAME, getUIString(key, locale)); - } - - // ------------------ bound action callback methods - - /** - * Resizes all columns to fit their content. - *

                - * - * By default this method is bound to the pack all columns - * Action and registered in the table's ActionMap. - * - */ - public void packAll() { - packTable(-1); - } - - /** - * Resizes the lead column to fit its content. - *

                - * - * By default this method is bound to the pack selected column - * Action and registered in the table's ActionMap. - */ - public void packSelected() { - int selected = getColumnModel().getSelectionModel() - .getLeadSelectionIndex(); - if (selected >= 0) { - packColumn(selected, -1); - } - } - - /** - * {@inheritDoc} - *

                - * - * Overridden to update the enabled state of the pack selected column - * Action. - */ - @Override - public void columnSelectionChanged(ListSelectionEvent e) { - super.columnSelectionChanged(e); - if (e.getValueIsAdjusting()) - return; - Action packSelected = getActionMap().get(PACKSELECTED_ACTION_COMMAND); - if ((packSelected != null)) { - packSelected.setEnabled(!((ListSelectionModel) e.getSource()) - .isSelectionEmpty()); - } - } - - // ----------------------- scrollable control - - /** - * Sets the enablement of enhanced horizontal scrolling. If enabled, it - * toggles an auto-resize mode which always fills the JViewport - * horizontally and shows the horizontal scrollbar if necessary. - *

                - * - * The default value is false. - *

                - * - * Note: this is not a bound property, though it follows - * bean naming conventions. - * - * PENDING: Probably should be... If so, could be taken by a listening - * Action as in the app-framework. - *

                - * PENDING JW: the name is mis-leading? - * - * @param enabled a boolean indicating whether enhanced auto-resize mode is - * enabled. - * @see #isHorizontalScrollEnabled() - */ - public void setHorizontalScrollEnabled(boolean enabled) { - /* - * PENDING JW: add a "real" mode? Problematic because there are several - * places in core which check for #AUTO_RESIZE_OFF, can't use different - * value without unwanted side-effects. The current solution with - * tagging the #AUTO_RESIZE_OFF by a boolean flag #intelliMode is - * brittle - need to be very careful to turn off again ... Another - * problem is to keep the horizontalScrollEnabled toggling action in - * synch with this property. Yet another problem is the change - * notification: currently this is _not_ a bound property. - */ - if (enabled == (isHorizontalScrollEnabled())) { - return; - } - boolean old = isHorizontalScrollEnabled(); - if (enabled) { - // remember the resizeOn mode if any - if (getAutoResizeMode() != AUTO_RESIZE_OFF) { - oldAutoResizeMode = getAutoResizeMode(); - } - setAutoResizeMode(AUTO_RESIZE_OFF); - // setAutoResizeModel always disables the intelliMode - // must set after calling and update the action again - intelliMode = true; - updateHorizontalAction(); - } else { - setAutoResizeMode(oldAutoResizeMode); - } - firePropertyChange("horizontalScrollEnabled", old, - isHorizontalScrollEnabled()); - } - - /** - * Returns the current setting for horizontal scrolling. - * - * @return the enablement of enhanced horizontal scrolling. - * @see #setHorizontalScrollEnabled(boolean) - */ - public boolean isHorizontalScrollEnabled() { - return intelliMode && getAutoResizeMode() == AUTO_RESIZE_OFF; - } - - /** - * {@inheritDoc} - *

                - * - * Overridden for internal bookkeeping related to the enhanced auto-resize - * behaviour. - *

                - * - * Note: to enable/disable the enhanced auto-resize mode use exclusively - * setHorizontalScrollEnabled, this method can't cope with it. - * - * @see #setHorizontalScrollEnabled(boolean) - * - */ - @Override - public void setAutoResizeMode(int mode) { - if (mode != AUTO_RESIZE_OFF) { - oldAutoResizeMode = mode; - } - intelliMode = false; - super.setAutoResizeMode(mode); - updateHorizontalAction(); - } - - /** - * Synchs selected state of horizontal scrolling Action to - * enablement of enhanced auto-resize behaviour. - */ - protected void updateHorizontalAction() { - Action showHorizontal = getActionMap().get( - HORIZONTALSCROLL_ACTION_COMMAND); - if (showHorizontal instanceof BoundAction) { - ((BoundAction) showHorizontal) - .setSelected(isHorizontalScrollEnabled()); - } - } - - /** - *{@inheritDoc} - *

                - * - * Overridden to support enhanced auto-resize behaviour enabled and - * necessary. - * - * @see #setHorizontalScrollEnabled(boolean) - */ - @Override - public boolean getScrollableTracksViewportWidth() { - boolean shouldTrack = super.getScrollableTracksViewportWidth(); - if (isHorizontalScrollEnabled()) { - return hasExcessWidth(); - } - return shouldTrack; - } - - /** - * Layouts column width. The exact behaviour depends on the - * autoResizeMode property. - *

                - * Overridden to support enhanced auto-resize behaviour enabled and - * necessary. - * - * @see #setAutoResizeMode(int) - * @see #setHorizontalScrollEnabled(boolean) - */ - @Override - public void doLayout() { - int resizeMode = getAutoResizeMode(); - // fool super... - if (isHorizontalScrollEnabled() && hasRealizedParent() - && hasExcessWidth()) { - autoResizeMode = oldAutoResizeMode; - } - inLayout = true; - super.doLayout(); - inLayout = false; - autoResizeMode = resizeMode; - } - - /** - * - * @return boolean to indicate whether the table has a realized parent. - */ - private boolean hasRealizedParent() { - return (getWidth() > 0) && (getParent() != null) - && (getParent().getWidth() > 0); - } - - /** - * PRE: hasRealizedParent() - * - * @return boolean to indicate whether the table has widths excessing - * parent's width - */ - private boolean hasExcessWidth() { - return getPreferredSize().width < getParent().getWidth(); - } - - /** - * {@inheritDoc} - *

                - * - * Overridden to support enhanced auto-resize behaviour enabled and - * necessary. - * - * @see #setHorizontalScrollEnabled(boolean) - */ - @Override - public void columnMarginChanged(ChangeEvent e) { - if (isEditing()) { - removeEditor(); - } - TableColumn resizingColumn = getResizingColumn(); - // Need to do this here, before the parent's - // layout manager calls getPreferredSize(). - if (resizingColumn != null && autoResizeMode == AUTO_RESIZE_OFF - && !inLayout) { - resizingColumn.setPreferredWidth(resizingColumn.getWidth()); - } - resizeAndRepaint(); - } - - /** - * Returns the column which is interactively resized. The return value is - * null if the header is null or has no resizing column. - * - * @return the resizing column. - */ - private TableColumn getResizingColumn() { - return (tableHeader == null) ? null : tableHeader.getResizingColumn(); - } - - /** - * {@inheritDoc}

                - * - * Overridden for documentation reasons only: same behaviour but different default value. - *

                - * - * The default value is true. - *

                - */ - @Override - public void setFillsViewportHeight(boolean fillsViewportHeight) { - if (fillsViewportHeight == getFillsViewportHeight()) - return; - super.setFillsViewportHeight(fillsViewportHeight); - } - - // ------------------------ override super because of filter-awareness - - - /** - * {@inheritDoc}

                - * Overridden to respect the cell's editability, that is it has no effect if - * !isCellEditable(row, column). - * - * - * @see #isCellEditable(int, int) - */ - @Override - public void setValueAt(Object aValue, int row, int column) { - if (!isCellEditable(row, column)) - return; - super.setValueAt(aValue, row, column); - } - - /** - * Returns true if the cell at row and column is - * editable. Otherwise, invoking setValueAt on the cell will - * have no effect. - *

                - * Overridden to account for row index mapping and to support a layered - * editability control: - *

                  - *
                • per-table: JXTable.isEditable() - *
                • per-column: TableColumnExt.isEditable() - *
                • per-cell: controlled by the model - * TableModel.isCellEditable() - *
                - * The view cell is considered editable only if all three layers are - * enabled. - * - * @param row the row index in view coordinates - * @param column the column index in view coordinates - * @return true if the cell is editable - * - * @see #setValueAt(Object, int, int) - * @see #isEditable() - * @see TableColumnExt#isEditable - * @see TableModel#isCellEditable - */ - @Override - public boolean isCellEditable(int row, int column) { - if (!isEditable()) - return false; - boolean editable = super.isCellEditable(row, column); - if (editable) { - TableColumnExt tableColumn = getColumnExt(column); - if (tableColumn != null) { - editable = tableColumn.isEditable(); - } - } - return editable; - } - - - - /** - * {@inheritDoc} - *

                - * - * Overridden for documentation clarification. The property has the same - * meaning as super, that is if true to re-create all table columns on - * either setting a new TableModel or receiving a structureChanged from the - * existing. The most obvious visual effect is that custom column properties - * appear to be "lost". - *

                - * - * JXTable does support additonal custom configuration (via a custom - * ColumnFactory) which can (and incorrectly was) called independently from - * the creation. Setting this property to false guarantees that no column - * configuration is applied. - * - * @see #tableChanged(TableModelEvent) - * @see ColumnFactory - * - */ - @Override - public boolean getAutoCreateColumnsFromModel() { - return super.getAutoCreateColumnsFromModel(); - } - - /** - * {@inheritDoc} - *

                - * - * Overridden to update internal state related to enhanced functionality and - * hack around core bugs. - *

                  - *
                • re-calculate intialize column width and preferred - * scrollable size after a structureChanged if autocreateColumnsFromModel is - * true. - *
                • update string representation control after structureChanged - *
                • core bug #6791934 logic to force revalidate if appropriate - *
                - *

                - * - */ - @Override - public void tableChanged(TableModelEvent e) { - preprocessModelChange(e); - super.tableChanged(e); - if (isStructureChanged(e) && getAutoCreateColumnsFromModel()) { - initializeColumnWidths(); - resetCalculatedScrollableSize(true); - } - if ((isStructureChanged(e))) { - updateStringValueRegistryColumnClasses(); - } - postprocessModelChange(e); - } - -//----> start hack around core issue 6791934: -// table not updated correctly after updating model -// while having a sorter with filter. - - /** - * Overridden to hack around core bug - * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6791934 - * - */ - @Override - public void sorterChanged(RowSorterEvent e) { - super.sorterChanged(e); - postprocessSorterChanged(e); - } - - - /** flag to indicate if forced revalidate is needed. */ - protected boolean forceRevalidate; - /** flag to indicate if a sortOrderChanged has happened between pre- and postProcessModelChange. */ - protected boolean filteredRowCountChanged; - - /** - * Hack around core issue 6791934: sets flags to force revalidate if appropriate. - * Called before processing the event. - * @param e the TableModelEvent received from the model - */ - protected void preprocessModelChange(TableModelEvent e) { - forceRevalidate = getSortsOnUpdates() && getRowFilter() != null && isUpdate(e) ; - } - - /** - * Hack around core issue 6791934: forces a revalidate if appropriate and resets - * internal flags. - * Called after processing the event. - * @param e the TableModelEvent received from the model - */ - protected void postprocessModelChange(TableModelEvent e) { - if (forceRevalidate && filteredRowCountChanged) { - resizeAndRepaint(); - } - filteredRowCountChanged = false; - forceRevalidate = false; - } - - /** - * Hack around core issue 6791934: sets the sorter changed flag if appropriate. - * Called after processing the event. - * @param e the sorter event received from the sorter - */ - protected void postprocessSorterChanged(RowSorterEvent e) { - filteredRowCountChanged = false; - if (forceRevalidate && e.getType() == RowSorterEvent.Type.SORTED) { - filteredRowCountChanged = e.getPreviousRowCount() != getRowCount(); - } - } - -//----> end hack around core issue 6791934: - - /** - * {@inheritDoc}

                - * - * Overridden to prevent super from creating RowSorter. - */ - @Override - public void setModel(TableModel dataModel) { - boolean old = getAutoCreateRowSorter(); - try { - this.autoCreateRowSorter = false; - this.ignoreAddColumn = true; - super.setModel(dataModel); - } finally { - this.autoCreateRowSorter = old; - this.ignoreAddColumn = false; - } - if (getAutoCreateRowSorter()) { - setRowSorter(createDefaultRowSorter()); - } - - } - - /** - * {@inheritDoc}

                - * - * Overridden to synch sorter state from columns. - */ - @Override - public void setColumnModel(TableColumnModel columnModel) { - super.setColumnModel(columnModel); - configureSorterProperties(); - initPerColumnStringValues(); - } - - /** - * {@inheritDoc}

                - * - * Overridden to - *

                  - *
                • fix core bug: replaces sorter even if flag doesn't change. - *
                • use xflag (need because super's RowSorter creation is hard-coded. - *
                - */ - @Override - public void setAutoCreateRowSorter(boolean autoCreateRowSorter) { - if (getAutoCreateRowSorter() == autoCreateRowSorter) return; - boolean oldValue = getAutoCreateRowSorter(); - this.autoCreateRowSorter = autoCreateRowSorter; - if (autoCreateRowSorter) { - setRowSorter(createDefaultRowSorter()); - } - firePropertyChange("autoCreateRowSorter", oldValue, - getAutoCreateRowSorter()); - } - - /** - * {@inheritDoc}

                - * - * Overridden to return xflag - */ - @Override - public boolean getAutoCreateRowSorter() { - return autoCreateRowSorter; - } - - /** - * {@inheritDoc}

                - * - * Overridden propagate sort-related properties to the sorter after calling super, - * if the given RowSorter is of type SortController. Does nothing additional otherwise. - */ - @Override - public void setRowSorter(RowSorter sorter) { - super.setRowSorter(sorter); - configureSorterProperties(); - } - - /** - * Propagates sort-related properties from table/columns to the sorter if it - * is of type SortController, does nothing otherwise. - * - */ - protected void configureSorterProperties() { - // need to hack: if a structureChange is the result of a setModel - // the rowsorter is not yet updated - if (ignoreAddColumn || (!getControlsSorterProperties())) return; - getSortController().setStringValueProvider(getStringValueRegistry()); - // configure from table properties - getSortController().setSortable(sortable); - getSortController().setSortsOnUpdates(sortsOnUpdates); - getSortController().setSortOrderCycle(getSortOrderCycle()); - // configure from column properties - List columns = getColumns(true); - for (TableColumn tableColumn : columns) { - int modelIndex = tableColumn.getModelIndex(); - getSortController().setSortable(modelIndex, - tableColumn instanceof TableColumnExt ? - ((TableColumnExt) tableColumn).isSortable() : true); - getSortController().setComparator(modelIndex, - tableColumn instanceof TableColumnExt ? - ((TableColumnExt) tableColumn).getComparator() : null); - } - } - - /** - * Creates and returns the default RowSorter. Note that this is already - * configured to the current TableModel - no api in the base class to set - * the model?

                - * - * PENDING JW: review method signature - better expose the need for the - * model by adding a parameter? - * - * @return the default RowSorter. - */ - protected RowSorter createDefaultRowSorter() { -// return new TableRowSorter(getModel()); - return new TableSortController(getModel()); - } - - - - /** - * Convenience method to detect dataChanged table event type. - * - * @param e the event to examine. - * @return true if the event is of type dataChanged, false else. - */ - protected boolean isDataChanged(TableModelEvent e) { - if (e == null) - return false; - return e.getType() == TableModelEvent.UPDATE && e.getFirstRow() == 0 - && e.getLastRow() == Integer.MAX_VALUE; - } - - /** - * Convenience method to detect update table event type. - * - * @param e the event to examine. - * @return true if the event is of type update and not dataChanged, false - * else. - */ - protected boolean isUpdate(TableModelEvent e) { - if (isStructureChanged(e)) - return false; - return e.getType() == TableModelEvent.UPDATE - && e.getLastRow() < Integer.MAX_VALUE; - } - - /** - * Convenience method to detect a structureChanged table event type. - * - * @param e the event to examine. - * @return true if the event is of type structureChanged or null, false - * else. - */ - protected boolean isStructureChanged(TableModelEvent e) { - return e == null || e.getFirstRow() == TableModelEvent.HEADER_ROW; - } - - - // -------------------------------- sorting: configure sorter - - - /** - * Sets "sortable" property indicating whether or not this table - * supports sortable columns. If sortable is true - * then sorting will be enabled on all columns whose sortable - * property is true. If sortable is - * false then sorting will be disabled for all columns, - * regardless of each column's individual sorting property. The - * default is true.

                - * - * Note: as of post-1.0 this property is propagated to the SortController - * if controlsSorterProperties is true. - * Whether or not a change triggers a re-sort is up to either the concrete controller - * implementation (the default doesn't) or client code. This behaviour is - * different from old SwingX style sorting. - * - * @param sortable boolean indicating whether or not this table supports - * sortable columns - * @see #getControlsSorterProperties() - */ - public void setSortable(boolean sortable) { - boolean old = isSortable(); - this.sortable = sortable; - if (getControlsSorterProperties()) { - getSortController().setSortable(sortable); - } - firePropertyChange("sortable", old, isSortable()); - } - - /** - * Returns the table's sortable property.

                - * - * @return true if the table is sortable. - * @see #setSortable(boolean) - */ - public boolean isSortable() { - return sortable; - } - - /** - * If true, specifies that a sort should happen when the underlying - * model is updated (rowsUpdated is invoked). For - * example, if this is true and the user edits an entry the - * location of that item in the view may change. - * This property is propagated to the SortController - * if controlsSorterProperties is true. - *

                - * - * The default value is true.

                - * - * @param sortsOnUpdates whether or not to sort on update events - * @see #getSortsOnUpdates() - * @see #getControlsSorterProperties() - * - */ - public void setSortsOnUpdates(boolean sortsOnUpdates) { - boolean old = getSortsOnUpdates(); - this.sortsOnUpdates = sortsOnUpdates; - if (getControlsSorterProperties()) { - getSortController().setSortsOnUpdates(sortsOnUpdates); - } - firePropertyChange("sortsOnUpdates", old, getSortsOnUpdates()); - } - - /** - * Returns true if a sort should happen when the underlying - * model is updated; otherwise, returns false. - * - * @return whether or not to sort when the model is updated - */ - public boolean getSortsOnUpdates() { - return sortsOnUpdates; - } - - /** - * Sets the sortorder cycle used when toggle sorting this table's columns. - * This property is propagated to the SortController - * if controlsSorterProperties is true. - * - * @param cycle the sequence of zero or more not-null SortOrders to cycle through. - * @throws NullPointerException if the array or any of its elements are null - * - */ - public void setSortOrderCycle(SortOrder... cycle) { - SortOrder[] old = getSortOrderCycle(); - if (getControlsSorterProperties()) { - getSortController().setSortOrderCycle(cycle); - } - this.sortOrderCycle = Arrays.copyOf(cycle, cycle.length); - firePropertyChange("sortOrderCycle", old, getSortOrderCycle()); - } - - /** - * Returns the sortOrder cycle used when toggle sorting this table's columns, guaranteed - * to be not null. - * - * @return the sort order cycle used in toggle sort, not null - */ - public SortOrder[] getSortOrderCycle() { - return Arrays.copyOf(sortOrderCycle, sortOrderCycle.length); - } - - -//------------------------- sorting: sort/filter - - /** - * Sets the filter to the sorter, if available and of type SortController. - * Does nothing otherwise. - *

                - * - * @param filter the filter used to determine what entries should be - * included - */ - @SuppressWarnings("unchecked") - public void setRowFilter(RowFilter filter) { - if (hasSortController()) { - // all fine, because R is a TableModel (R extends TableModel) - SortController controller = (SortController) getSortController(); - controller.setRowFilter(filter); - } - } - - /** - * Returns the filter of the sorter, if available and of type SortController. - * Returns null otherwise.

                - * - * PENDING JW: generics? had to remove return type from getSortController to - * make this compilable, so probably wrong. - * - * @return the filter used in the sorter. - */ - @SuppressWarnings("unchecked") - public RowFilter getRowFilter() { - return hasSortController() ? getSortController().getRowFilter() : null; - } - - /** - * Resets sorting of all columns. - * Delegates to the SortController if available, or does nothing if not.

                - * - * PENDING JW: method name - consistent in SortController and here. - * - */ - public void resetSortOrder() { - if (!hasSortController()) return; - getSortController().resetSortOrders(); - // JW PENDING: think about notification instead of manual repaint. - if (getTableHeader() != null) { - getTableHeader().repaint(); - } - } - - /** - * - * Toggles the sort order of the column at columnIndex. - * Delegates to the SortController if available, or does nothing if not.

                - * - *

                - * The exact behaviour is defined by the SortController's toggleSortOrder - * implementation. Typically a unsorted column is sorted in ascending order, - * a sorted column's order is reversed. - *

                - * - * PRE: 0 <= columnIndex < getColumnCount() - * - * @param columnIndex the columnIndex in view coordinates. - * - */ - public void toggleSortOrder(int columnIndex) { - if (hasSortController()){ - getSortController().toggleSortOrder(convertColumnIndexToModel(columnIndex)); - } - } - - /** - * Sorts the table by the given column using SortOrder. - * Delegates to the SortController if available, or does nothing if not.

                - * - * PRE: 0 <= columnIndex < getColumnCount() - *

                - * - * - * @param columnIndex the column index in view coordinates. - * @param sortOrder the sort order to use. - * - */ - public void setSortOrder(int columnIndex, SortOrder sortOrder) { - if (hasSortController()) { - getSortController().setSortOrder( - convertColumnIndexToModel(columnIndex), sortOrder); - } - } - - /** - * Returns the SortOrder of the given column. - * Delegates to the SortController if available, or returns SortOrder.UNSORTED if not.

                - * - * @param columnIndex the column index in view coordinates. - * @return the interactive sorter's SortOrder if matches the column or - * SortOrder.UNSORTED - */ - public SortOrder getSortOrder(int columnIndex) { - if (hasSortController()) { - return getSortController().getSortOrder(convertColumnIndexToModel(columnIndex)); - } - return SortOrder.UNSORTED; - } - - /** - * - * Toggles the sort order of the column with identifier. - * Delegates to the SortController if available, or does nothing if not.

                - * - * The exact behaviour of a toggle is defined by the SortController's toggleSortOrder - * implementation. Typically a unsorted column is sorted in ascending order, - * a sorted column's order is reversed. - *

                - * - * PENDING: JW - define the behaviour if the identifier is not found. This - * can happen if either there's no column at all with the identifier or if - * there's no column of type TableColumnExt. Currently does nothing, that is - * does not change sort state. - * - * @param identifier the column identifier. - * - */ - public void toggleSortOrder(Object identifier) { - if (!hasSortController()) - return; - TableColumn columnExt = getColumnByIdentifier(identifier); - if (columnExt == null) - return; - getSortController().toggleSortOrder(columnExt.getModelIndex()); - } - - /** - * Sorts the table by the given column using the SortOrder. - * Delegates to the SortController, if available or does nothing if not. - *

                - * - * PENDING: JW - define the behaviour if the identifier is not found. This - * can happen if either there's no column at all with the identifier or if - * there's no column of type TableColumnExt. Currently does nothing, that is - * does not change sort state. - * - * @param identifier the column's identifier. - * @param sortOrder the sort order to use. If null or SortOrder.UNSORTED, - * this method has the same effect as resetSortOrder(); - * - */ - public void setSortOrder(Object identifier, SortOrder sortOrder) { - if (!hasSortController()) - return; - TableColumn columnExt = getColumnByIdentifier(identifier); - if (columnExt == null) - return; - getSortController().setSortOrder(columnExt.getModelIndex(), sortOrder); - } - - /** - * Returns the SortOrder of the given column. - * Delegates to the SortController if available, or returns SortOrder.UNSORTED if not.

                - * - * PENDING: JW - define the behaviour if the identifier is not found. This - * can happen if either there's no column at all with the identifier or if - * there's no column of type TableColumnExt. Currently returns - * SortOrder.UNSORTED. - * - * @param identifier the column's identifier. - * @return the interactive sorter's SortOrder if matches the column or - * SortOrder.UNSORTED - */ - public SortOrder getSortOrder(Object identifier) { - if (!hasSortController()) - return SortOrder.UNSORTED; - TableColumn columnExt = getColumnByIdentifier(identifier); - if (columnExt == null) - return SortOrder.UNSORTED; - int modelIndex = columnExt.getModelIndex(); - return getSortController().getSortOrder(modelIndex); - } - - /** - * Returns a contained TableColumn with the given identifier. - * - * Note that this is a hack around weird columnModel.getColumn(Object) contract in - * core TableColumnModel (throws exception if not found). - * - * @param identifier the column identifier - * @return a TableColumn with the identifier if found, or null if not found. - */ - private TableColumn getColumnByIdentifier(Object identifier) { - TableColumn columnExt; - try { - columnExt = getColumn(identifier); - } catch (IllegalArgumentException e) { - // hacking around weird getColumn(Object) behaviour - - // PENDING JW: revisit and override - columnExt = getColumnExt(identifier); - } - return columnExt; - } - - /** - * Returns the currently active SortController. May be null, if the current RowSorter - * is not an instance of SortController.

                - * - * PENDING JW: generics - can't get the - * RowFilter getter signature correct with having controller typed here.

                - * - * PENDING JW: swaying about hiding or not - currently the only way to - * make the view not configure a RowSorter of type SortController is to - * let this return null. - * - * @return the currently active SortController may be null - */ - @SuppressWarnings("unchecked") - protected SortController getSortController() { - if (hasSortController()) { - // JW: the RowSorter is always of type - // so the unchecked cast is safe - return (SortController) getRowSorter(); - } - return null; - } - - /** - * Returns a boolean indicating whether the table has a SortController. - * If true, the call to getSortController is guaranteed to return a not-null - * value. - * - * @return a boolean indicating whether the table has a SortController. - * - * @see #getSortController() - */ - protected boolean hasSortController() { - return getRowSorter() instanceof SortController; - } - - /** - * Returns a boolean indicating whether the table configures the sorter's - * properties. If true, guaranteed that table's and the columns' sort related - * properties are propagated to the sorter. If false, guaranteed to not - * touch the sorter's configuration.

                - * - * This implementation returns true if the sorter is of type SortController. - * - * Note: the synchronization is unidirection from the table to the sorter. - * Changing the sorter under the table's feet might lead to undefined - * behaviour. - * - * @return a boolean indicating whether the table configurers the sorter's - * properties. - */ - protected boolean getControlsSorterProperties() { - return hasSortController() && getAutoCreateRowSorter(); - } - - /** - * Returns the primary sort column, or null if nothing sorted or no sortKey - * corresponds to a TableColumn currently contained in the TableColumnModel. - * - * @return the currently interactively sorted TableColumn or null if there - * is not sorter active or if the sorted column index does not - * correspond to any column in the TableColumnModel. - */ - public TableColumn getSortedColumn() { - // bloody hack: get primary SortKey and - // check if there's a column with it available - RowSorter controller = getRowSorter(); - if (controller != null) { - // PENDING JW: must use RowSorter? - SortKey sortKey = SortUtils.getFirstSortingKey(controller - .getSortKeys()); - if (sortKey != null) { - int sorterColumn = sortKey.getColumn(); - List columns = getColumns(true); - for (Iterator iter = columns.iterator(); iter - .hasNext();) { - TableColumn column = iter.next(); - if (column.getModelIndex() == sorterColumn) { - return column; - } - } - - } - } - return null; - } - - /** - * Returns the view column index of the primary sort column. - * - * @return the view column index of the primary sort column or -1 if nothing - * sorted or the primary sort column not visible. - */ - public int getSortedColumnIndex() { - RowSorter controller = getRowSorter(); - if (controller != null) { - SortKey sortKey = SortUtils.getFirstSortingKey(controller.getSortKeys()); - if (sortKey != null) { - return convertColumnIndexToView(sortKey.getColumn()); - } - } - return -1; - } - /** - * {@inheritDoc}

                - * - * Overridden to propagate sort-related column properties to the SortController and - * to update string representation of column.

                - * - * PENDING JW: check correct update on visibility change!

                - * PENDING JW: need cleanup of string rep after column removed (if it's a real remove) - */ - @Override - public void columnAdded(TableColumnModelEvent e) { - super.columnAdded(e); - // PENDING JW: check for visibility event? - TableColumn column = getColumn(e.getToIndex()); - updateStringValueForColumn(column, column.getCellRenderer()); - if (ignoreAddColumn) return; - updateSortableAfterColumnChanged(column, column instanceof TableColumnExt ? ((TableColumnExt) column).isSortable() : true); - updateComparatorAfterColumnChanged(column, column instanceof TableColumnExt ? ((TableColumnExt) column).getComparator() : null); - } - - - - // ----------------- enhanced column support: delegation to TableColumnModel - /** - * Returns the TableColumn at view position - * columnIndex. The return value is not null. - * - *

                - * NOTE: This delegate method is added to protect developer's from - * unexpected exceptions in jdk1.5+. Super does not expose the - * TableColumn access by index which may lead to unexpected - * IllegalArgumentException: If client code assumes the - * delegate method is available, autoboxing will convert the given int to an - * Integer which will call the getColumn(Object) method. - * - * - * @param viewColumnIndex index of the column with the object in question - * - * @return the TableColumn object that matches the column index - * @throws ArrayIndexOutOfBoundsException if viewColumnIndex out of allowed - * range. - * - * @see #getColumn(Object) - * @see #getColumnExt(int) - * @see TableColumnModel#getColumn(int) - */ - public TableColumn getColumn(int viewColumnIndex) { - return getColumnModel().getColumn(viewColumnIndex); - } - - /** - * Returns a List of visible TableColumns. - * - * @return a List of visible columns. - * @see #getColumns(boolean) - */ - public List getColumns() { - return Collections.list(getColumnModel().getColumns()); - } - - /** - * Returns the margin between columns. - *

                - * - * Convenience to expose column model properties through - * JXTable api. - * - * @return the margin between columns - * - * @see #setColumnMargin(int) - * @see TableColumnModel#getColumnMargin() - */ - public int getColumnMargin() { - return getColumnModel().getColumnMargin(); - } - - /** - * Sets the margin between columns. - * - * Convenience to expose column model properties through - * JXTable api. - * - * @param value margin between columns; must be greater than or equal to - * zero. - * @see #getColumnMargin() - * @see TableColumnModel#setColumnMargin(int) - */ - public void setColumnMargin(int value) { - getColumnModel().setColumnMargin(value); - } - - // ----------------- enhanced column support: delegation to - // TableColumnModelExt - - /** - * Returns the number of contained columns. The count includes or excludes - * invisible columns, depending on whether the includeHidden is - * true or false, respectively. If false, this method returns the same count - * as getColumnCount(). If the columnModel is not of type - * TableColumnModelExt, the parameter value has no effect. - * - * @param includeHidden a boolean to indicate whether invisible columns - * should be included - * @return the number of contained columns, including or excluding the - * invisible as specified. - * @see #getColumnCount() - * @see TableColumnModelExt#getColumnCount(boolean) - */ - public int getColumnCount(boolean includeHidden) { - if (getColumnModel() instanceof TableColumnModelExt) { - return ((TableColumnModelExt) getColumnModel()) - .getColumnCount(includeHidden); - } - return getColumnCount(); - } - - /** - * Returns a List of contained TableColumns. - * Includes or excludes invisible columns, depending on whether the - * includeHidden is true or false, respectively. If false, an - * Iterator over the List is equivalent to the - * Enumeration returned by getColumns(). If the - * columnModel is not of type TableColumnModelExt, the - * parameter value has no effect. - *

                - * - * NOTE: the order of columns in the List depends on whether or not the - * invisible columns are included, in the former case it's the insertion - * order in the latter it's the current order of the visible columns. - * - * @param includeHidden a boolean to indicate whether invisible columns - * should be included - * @return a List of contained columns. - * - * @see #getColumns() - * @see TableColumnModelExt#getColumns(boolean) - */ - public List getColumns(boolean includeHidden) { - if (getColumnModel() instanceof TableColumnModelExt) { - return ((TableColumnModelExt) getColumnModel()) - .getColumns(includeHidden); - } - return getColumns(); - } - - /** - * Returns the first TableColumnExt with the given - * identifier. The return value is null if there is no - * contained column with identifier or if the column with - * identifier is not of type TableColumnExt. The - * returned column may be visible or hidden. - * - * @param identifier the object used as column identifier - * @return first TableColumnExt with the given identifier or - * null if none is found - * - * @see #getColumnExt(int) - * @see #getColumn(Object) - * @see TableColumnModelExt#getColumnExt(Object) - */ - public TableColumnExt getColumnExt(Object identifier) { - if (getColumnModel() instanceof TableColumnModelExt) { - return ((TableColumnModelExt) getColumnModel()) - .getColumnExt(identifier); - } else { - // PENDING: not tested! - try { - TableColumn column = getColumn(identifier); - if (column instanceof TableColumnExt) { - return (TableColumnExt) column; - } - } catch (Exception e) { - // TODO: handle exception - } - } - return null; - } - - /** - * Returns the TableColumnExt at view position - * columnIndex. The return value is null, if the column at - * position columnIndex is not of type - * TableColumnExt. The returned column is visible. - * - * @param viewColumnIndex the index of the column desired - * @return the TableColumnExt object that matches the column - * index - * @throws ArrayIndexOutOfBoundsException if columnIndex out of allowed - * range, that is if - * (columnIndex < 0) || (columnIndex >= getColumnCount()) - * . - * - * @see #getColumnExt(Object) - * @see #getColumn(int) - * @see TableColumnModelExt#getColumnExt(int) - */ - public TableColumnExt getColumnExt(int viewColumnIndex) { - TableColumn column = getColumn(viewColumnIndex); - if (column instanceof TableColumnExt) { - return (TableColumnExt) column; - } - return null; - } - - // ---------------------- enhanced TableColumn/Model support: convenience - - /** - * Reorders the columns in the sequence given array. Logical names that do - * not correspond to any column in the model will be ignored. Columns with - * logical names not contained are added at the end. - * - * PENDING JW - do we want this? It's used by JNTable. - * - * @param identifiers array of logical column names - * - * @see #getColumns(boolean) - */ - public void setColumnSequence(Object[] identifiers) { - /* - * JW: not properly tested (not in all in fact) ... - */ - List columns = getColumns(true); - Map map = new HashMap(); - for (Iterator iter = columns.iterator(); iter.hasNext();) { - // PENDING: handle duplicate identifiers ... - TableColumn column = iter.next(); - map.put(column.getIdentifier(), column); - getColumnModel().removeColumn(column); - } - for (int i = 0; i < identifiers.length; i++) { - TableColumn column = map.get(identifiers[i]); - if (column != null) { - getColumnModel().addColumn(column); - columns.remove(column); - } - } - for (Iterator iter = columns.iterator(); iter.hasNext();) { - TableColumn column = (TableColumn) iter.next(); - getColumnModel().addColumn(column); - } - } - - // --------------- implement TableColumnModelExtListener - - /** - * {@inheritDoc} - * - * Listens to column property changes. - * - */ - @Override - public void columnPropertyChange(PropertyChangeEvent event) { - if (event.getPropertyName().equals("editable")) { - updateEditingAfterColumnChanged((TableColumn) event.getSource(), - (Boolean) event.getNewValue()); - } else if (event.getPropertyName().equals("sortable")) { - updateSortableAfterColumnChanged((TableColumn) event.getSource(), - (Boolean) event.getNewValue()); - } else if (event.getPropertyName().equals("comparator")) { - updateComparatorAfterColumnChanged((TableColumn) event.getSource(), - (Comparator) event.getNewValue()); - } else if (event.getPropertyName().equals("cellRenderer")) { - updateStringValueForColumn((TableColumn) event.getSource(), - (TableCellRenderer) event.getNewValue()); - } else if (event.getPropertyName().startsWith("highlighter")) { - if (event.getSource() instanceof TableColumnExt - && getRowCount() > 0) { - TableColumnExt column = (TableColumnExt) event.getSource(); - - Rectangle r = getCellRect(0, convertColumnIndexToView(column - .getModelIndex()), true); - r.height = getHeight(); - repaint(r); - } else { - repaint(); - } - } - - } - - - /** - * Adjusts editing state after column's property change. Cancels ongoing - * editing if the sending column is the editingColumn and the column's - * editable changed to false, otherwise does nothing. - * - * @param column the TableColumn which sent the change - * notifcation - * @param editable the new value of the column's editable property - */ - private void updateEditingAfterColumnChanged(TableColumn column, - boolean editable) { - if (!isEditing()) - return; - int viewIndex = convertColumnIndexToView(column.getModelIndex()); - if ((viewIndex < 0) || (viewIndex != getEditingColumn())) - return; - getCellEditor().cancelCellEditing(); - } - - /** - * Synch's the SortController column sortable property to the new value, if - * controlsSorterProperties. Does nothing otherwise. This method is - * called on sortable property change notification from the ext column model.

                - * - * @param column the TableColumn which sent the change - * notifcation - * @param sortable the new value of the column's sortable property - */ - private void updateSortableAfterColumnChanged(TableColumn column, - boolean sortable) { - if (getControlsSorterProperties()) { - getSortController().setSortable(column.getModelIndex(), sortable); - } - } - /** - * Synch's the SortController column comparator property to the new value, if - * controlsSorterProperties. Does nothing otherwise. This method is - * called on comparator property change notification from the ext column model.

                - * - * @param column the TableColumn which sent the change - * notifcation - * @param comparator the new value of the column's sortable property - */ - private void updateComparatorAfterColumnChanged(TableColumn column, - Comparator comparator) { - if (getControlsSorterProperties()) { - getSortController().setComparator(column.getModelIndex(), comparator); - } - } - - // -------------------------- ColumnFactory - - /** - * Creates, configures and adds default TableColumns for - * columns in this table's TableModel. Removes all currently - * contained TableColumns. The exact type and configuration of - * the columns is controlled completely by the ColumnFactory. - * Client code can use {@link #setColumnFactory(ColumnFactory)} to plug-in a - * custom ColumnFactory implementing their own default column creation and - * behaviour. - *

                - * - * Note: this method will probably become final (Issue #961-SwingX) - * so it's strongly recommended to not override now (and replace existing - * overrides by a custom ColumnFactory)! - * - * @see #setColumnFactory(ColumnFactory) - * @see ColumnFactory - * - */ - @Override - public final void createDefaultColumnsFromModel() { - // JW: when could this happen? - if (getModel() == null) - return; - // Remove any current columns - removeColumns(); - createAndAddColumns(); - } - - /** - * Creates and adds TableColumns for each column of the table - * model. - *

                - * - * - */ - private void createAndAddColumns() { - /* - * PENDING: go the whole distance and let the factory decide which model - * columns to map to view columns? That would introduce an collection - * managing operation into the factory, sprawling? Can't (and probably - * don't want to) move all collection related operations over - the - * ColumnFactory relies on TableColumnExt type columns, while the - * JXTable has to cope with all the base types. - */ - for (int i = 0; i < getModel().getColumnCount(); i++) { - // add directly to columnModel - don't go through this.addColumn - // to guarantee full control of ColumnFactory - // addColumn has the side-effect to set the header! - TableColumnExt tableColumn = getColumnFactory() - .createAndConfigureTableColumn(getModel(), i); - if (tableColumn != null) { - getColumnModel().addColumn(tableColumn); - } - } - } - - /** - * Remove all columns, make sure to include hidden. - *

                - */ - private void removeColumns() { - /* - * TODO: promote this method to superclass, and change - * createDefaultColumnsFromModel() to call this method - */ - List columns = getColumns(true); - for (Iterator iter = columns.iterator(); iter.hasNext();) { - getColumnModel().removeColumn(iter.next()); - - } - } - - /** - * Returns the ColumnFactory. - *

                - * - * @return the columnFactory to use for column creation and configuration, - * guaranteed to not be null. - * - * @see #setColumnFactory(ColumnFactory) - * @see ColumnFactory - */ - public ColumnFactory getColumnFactory() { - /* - * TODO JW: think about implications of not/ copying the reference to - * the shared instance into the table's field? Better access the - * getInstance() on each call? We are on single thread anyway... - * Furthermore, we don't expect the instance to change often, typically - * it is configured on startup. So we don't really have to worry about - * changes which would destabilize column state? - */ - if (columnFactory == null) { - return ColumnFactory.getInstance(); - // columnFactory = ColumnFactory.getInstance(); - } - return columnFactory; - } - - /** - * Sets the ColumnFactory to use for column creation and - * configuration. The default value is the shared application ColumnFactory. - *

                - * - * Note: this method has no side-effect, that is existing columns are - * not re-created automatically, client code must trigger it - * manually. - * - * @param columnFactory the factory to use, null indicates to - * use the shared application factory. - * - * @see #getColumnFactory() - * @see ColumnFactory - */ - public void setColumnFactory(ColumnFactory columnFactory) { - /* - * - * TODO auto-configure columns on set? or add public table api to do so? - * Mostly, this is meant to be done once in the lifetime of the table, - * preferably before a model is set ... overshoot? - */ - ColumnFactory old = getColumnFactory(); - this.columnFactory = columnFactory; - firePropertyChange("columnFactory", old, getColumnFactory()); - } - - // -------------------------------- enhanced sizing support - - /** - * Packs all the columns to their optimal size. Works best with auto - * resizing turned off. - * - * @param margin the margin to apply to each column. - * - * @see #packColumn(int, int) - * @see #packColumn(int, int, int) - */ - public void packTable(int margin) { - for (int c = 0; c < getColumnCount(); c++) - packColumn(c, margin, -1); - } - - /** - * Packs an indivudal column in the table. - * - * @param column The Column index to pack in View Coordinates - * @param margin The Margin to apply to the column width. - * - * @see #packColumn(int, int, int) - * @see #packTable(int) - */ - public void packColumn(int column, int margin) { - packColumn(column, margin, -1); - } - - /** - * Packs an indivual column in the table to less than or equal to the - * maximum witdth. If maximum is -1 then the column is made as wide as it - * needs. - * - * @param column the column index to pack in view coordinates - * @param margin the margin to apply to the column - * @param max the maximum width the column can be resized to, -1 means no - * limit - * - * @see #packColumn(int, int) - * @see #packTable(int) - * @see ColumnFactory#packColumn(JXTable, TableColumnExt, int, int) - */ - public void packColumn(int column, int margin, int max) { - getColumnFactory().packColumn(this, getColumnExt(column), margin, max); - } - - /** - * Returns the preferred number of rows to show in a - * JScrollPane. - * - * @return the number of rows to show in a JScrollPane - * @see #setVisibleRowCount(int) - */ - public int getVisibleRowCount() { - return visibleRowCount; - } - - /** - * Sets the preferred number of rows to show in a JScrollPane. - *

                - * - * This is a bound property. The default value is 20. - *

                - * - * PENDING: allow negative for use-all? Analogous to visColumnCount. - * - * @param visibleRowCount number of rows to show in a - * JScrollPane - * @throws IllegalArgumentException if given count is negative. - * - * @see #getVisibleRowCount() - */ - public void setVisibleRowCount(int visibleRowCount) { - if (visibleRowCount < 0) - throw new IllegalArgumentException( - "visible row count must not be negative " + visibleRowCount); - if (getVisibleRowCount() == visibleRowCount) - return; - int old = getVisibleRowCount(); - this.visibleRowCount = visibleRowCount; - resetCalculatedScrollableSize(false); - firePropertyChange("visibleRowCount", old, getVisibleRowCount()); - } - - /** - * Returns the preferred number of columns to show in the - * JScrollPane. - * - * @return the number of columns to show in the scroll pane. - * - * @see #setVisibleColumnCount - */ - public int getVisibleColumnCount() { - return visibleColumnCount; - } - - /** - * Sets the preferred number of Columns to show in a - * JScrollPane. A negative number is interpreted as use-all - * available visible columns. - *

                - * - * This is a bound property. The default value is -1 (effectively the same - * as before the introduction of this property). - * - * @param visibleColumnCount number of rows to show in a - * JScrollPane - * @see #getVisibleColumnCount() - */ - public void setVisibleColumnCount(int visibleColumnCount) { - if (getVisibleColumnCount() == visibleColumnCount) - return; - int old = getVisibleColumnCount(); - this.visibleColumnCount = visibleColumnCount; - resetCalculatedScrollableSize(true); - firePropertyChange("visibleColumnCount", old, getVisibleColumnCount()); - } - - /** - * Resets the calculated scrollable size in one dimension, if appropriate. - * - * @param isColumn flag to denote which dimension to reset, true for width, - * false for height - * - */ - private void resetCalculatedScrollableSize(boolean isColumn) { - if (calculatedPrefScrollableViewportSize != null) { - if (isColumn) { - calculatedPrefScrollableViewportSize.width = -1; - } else { - calculatedPrefScrollableViewportSize.height = -1; - } - } - } - - /** - * {@inheritDoc} - *

                - * - * If the given dimension is null, the auto-calculation of the pref - * scrollable size is enabled, otherwise the behaviour is the same as super. - * - * The default is auto-calc enabled on. - * - * @see #getPreferredScrollableViewportSize() - */ - @Override - public void setPreferredScrollableViewportSize(Dimension size) { - // TODO: figure out why firing the event screws the - // JXTableUnitTest.testPrefScrollableUpdatedOnStructureChanged - // Dimension old = getPreferredScrollableViewportSize(); - super.setPreferredScrollableViewportSize(size); - // firePropertyChange("preferredScrollableViewportSize", old, - // getPreferredScrollableViewportSize()); - } - - /** - * {@inheritDoc} - *

                - * Overridden to support auto-calculation of pref scrollable size, dependent - * on the visible row/column count properties. The auto-calc is on if - * there's no explicit pref scrollable size set. Otherwise the fixed size is - * returned - *

                - * - * The calculation of the preferred scrollable width is delegated to the - * ColumnFactory to allow configuration with custom strategies implemented - * in custom factories. - * - * @see #setPreferredScrollableViewportSize(Dimension) - * @see ColumnFactory#getPreferredScrollableViewportWidth(JXTable) - */ - @Override - public Dimension getPreferredScrollableViewportSize() { - // client code has set this - takes precedence. - Dimension prefSize = super.getPreferredScrollableViewportSize(); - if (prefSize != null) { - return new Dimension(prefSize); - } - if (calculatedPrefScrollableViewportSize == null) { - calculatedPrefScrollableViewportSize = new Dimension(); - // JW: hmm... fishy ... shouldn't be necessary here? - // maybe its the "early init" in super's tableChanged(); - // moved to init which looks okay so far - // initializeColumnPreferredWidths(); - } - // the width is reset to -1 in setVisibleColumnCount - if (calculatedPrefScrollableViewportSize.width <= 0) { - calculatedPrefScrollableViewportSize.width = getColumnFactory() - .getPreferredScrollableViewportWidth(this); - } - // the heigth is reset in setVisualRowCount - if (calculatedPrefScrollableViewportSize.height <= 0) { - calculatedPrefScrollableViewportSize.height = getVisibleRowCount() - * getRowHeight(); - } - return new Dimension(calculatedPrefScrollableViewportSize); - } - - /** - * Initialize the width related properties of all contained TableColumns, - * both visible and hidden. - *

                - *

                  - *
                • PENDING: move into ColumnFactory? - *
                • PENDING: what to do if autoCreateColumn off? - *
                • PENDING: public? to allow manual setting of column properties which - * might effect their default sizing. Needed in testing - but real-world? - * the factory is meant to do the property setting, based on tableModel and - * meta-data (from where?). But leads to funny call sequence for per-table - * factory (new JXTable(), table.setColumnFactory(..), table.setModel(...)) - *
                - * - * @see #initializeColumnPreferredWidth(TableColumn) - */ - protected void initializeColumnWidths() { - for (TableColumn column : getColumns(true)) { - initializeColumnPreferredWidth(column); - } - } - - /** - * Initialize the width related properties of the specified column. The - * details are specified by the current ColumnFactory if the - * column is of type TableColumnExt. Otherwise nothing is - * changed. - *

                - * - * TODO JW - need to cleanup getScrollablePreferred (refactor and inline) - * - * @param column TableColumn object representing view column - * @see ColumnFactory#configureColumnWidths - */ - protected void initializeColumnPreferredWidth(TableColumn column) { - if (column instanceof TableColumnExt) { - getColumnFactory().configureColumnWidths(this, - (TableColumnExt) column); - } - } - - // ----------------- scrolling support - /** - * Scrolls vertically to make the given row visible. This might not have any - * effect if the table isn't contained in a JViewport. - *

                - * - * Note: this method has no precondition as it internally uses - * getCellRect which is lenient to off-range coordinates. - * - * @param row the view row index of the cell - * - * @see #scrollColumnToVisible(int) - * @see #scrollCellToVisible(int, int) - * @see #scrollRectToVisible(Rectangle) - */ - public void scrollRowToVisible(int row) { - Rectangle cellRect = getCellRect(row, 0, false); - Rectangle visibleRect = getVisibleRect(); - cellRect.x = visibleRect.x; - cellRect.width = visibleRect.width; - scrollRectToVisible(cellRect); - } - - /** - * Scrolls horizontally to make the given column visible. This might not - * have any effect if the table isn't contained in a JViewport. - *

                - * - * Note: this method has no precondition as it internally uses - * getCellRect which is lenient to off-range coordinates. - * - * @param column the view column index of the cell - * - * @see #scrollRowToVisible(int) - * @see #scrollCellToVisible(int, int) - * @see #scrollRectToVisible(Rectangle) - */ - public void scrollColumnToVisible(int column) { - Rectangle cellRect = getCellRect(0, column, false); - Rectangle visibleRect = getVisibleRect(); - cellRect.y = visibleRect.y; - cellRect.height = visibleRect.height; - scrollRectToVisible(cellRect); - } - - /** - * Scrolls to make the cell at row and column visible. This might not have - * any effect if the table isn't contained in a JViewport. - *

                - * - * Note: this method has no precondition as it internally uses - * getCellRect which is lenient to off-range coordinates. - * - * @param row the view row index of the cell - * @param column the view column index of the cell - * - * @see #scrollColumnToVisible(int) - * @see #scrollRowToVisible(int) - * @see #scrollRectToVisible(Rectangle) - */ - public void scrollCellToVisible(int row, int column) { - Rectangle cellRect = getCellRect(row, column, false); - scrollRectToVisible(cellRect); - } - - // ----------------------- delegating methods?? from super - /** - * Returns the selection mode used by this table's selection model. - *

                - * PENDING JW - setter? - * - * @return the selection mode used by this table's selection model - * @see ListSelectionModel#getSelectionMode() - */ - public int getSelectionMode() { - return getSelectionModel().getSelectionMode(); - } - - // ----------------------- Search support - - /** - * Starts a search on this List's visible items. This implementation asks the - * SearchFactory to open a find widget on itself. - */ - protected void doFind() { - SearchFactory.getInstance().showFindInput(this, getSearchable()); - } - - /** - * Returns a Searchable for this component, guaranteed to be not null. This - * implementation lazily creates a TableSearchable if necessary. - * - * - * @return a not-null Searchable for this component. - * - * @see #setSearchable(Searchable) - * @see TableSearchable - */ - public Searchable getSearchable() { - if (searchable == null) { - searchable = new TableSearchable(this); - } - return searchable; - } - - /** - * Sets the Searchable for this table. If null, a default - * searchable will be used. - * - * @param searchable the Searchable to use for this table, may be null to indicate - * using the table's default searchable. - */ - public void setSearchable(Searchable searchable) { - this.searchable = searchable; - } - - // ----------------------------------- uniform data model access - /** - * @return the unconfigured ComponentAdapter. - */ - protected ComponentAdapter getComponentAdapter() { - if (dataAdapter == null) { - dataAdapter = new TableAdapter(this); - } - return dataAdapter; - } - - /** - * Convenience to access a configured ComponentAdapter. - * - * @param row the row index in view coordinates. - * @param column the column index in view coordinates. - * @return the configured ComponentAdapter. - */ - protected ComponentAdapter getComponentAdapter(int row, int column) { - ComponentAdapter adapter = getComponentAdapter(); - adapter.row = row; - adapter.column = column; - return adapter; - } - - protected static class TableAdapter extends ComponentAdapter { - private final JXTable table; - - /** - * Constructs a TableDataAdapter for the specified target - * component. - * - * @param component the target component - */ - public TableAdapter(JXTable component) { - super(component); - table = component; - } - - /** - * Typesafe accessor for the target component. - * - * @return the target component as a {@link JTable} - */ - public JXTable getTable() { - return table; - } - -//----------------- column meta data - /** - * {@inheritDoc} - */ - @Override - public String getColumnName(int columnIndex) { - TableColumn column = getColumnByModelIndex(columnIndex); - return column == null ? "" : column.getHeaderValue().toString(); - } - - /** - * Returns the first contained TableColumn with the given model index, or - * null if none is found. - * - * @param modelColumn the column index in model coordinates, must be valid - * @return the first contained TableColumn with the given model index, or - * null if none is found - * @throws IllegalArgumentException if model index invalid - */ - protected TableColumn getColumnByModelIndex(int modelColumn) { - if ((modelColumn < 0) || (modelColumn >= getColumnCount())) { - throw new IllegalArgumentException("invalid column index, must be positive and less than " - + getColumnCount() + " was: " + modelColumn); - } - List columns = table.getColumns(true); - for (Iterator iter = columns.iterator(); iter - .hasNext();) { - TableColumn column = iter.next(); - if (column.getModelIndex() == modelColumn) { - return column; - } - } - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public Object getColumnIdentifierAt(int columnIndex) { - if ((columnIndex < 0) || (columnIndex >= getColumnCount())) { - throw new ArrayIndexOutOfBoundsException( - "invalid column index: " + columnIndex); - } - TableColumn column = getColumnByModelIndex(columnIndex); - Object identifier = column != null ? column.getIdentifier() : null; - return identifier; - } - - /** - * {@inheritDoc} - */ - @Override - public int getColumnIndex(Object identifier) { - TableColumn column = table.getColumnExt(identifier); - return column != null ? column.getModelIndex() : -1; - } - - /** - * @inherited

                - */ - @Override - public Class getColumnClass(int column) { - return table.getModel().getColumnClass(column); - } - - //--------------------- model meta data - /** - * {@inheritDoc} - */ - @Override - public int getColumnCount() { - return table.getModel().getColumnCount(); - } - - /** - * {@inheritDoc} - */ - @Override - public int getRowCount() { - return table.getModel().getRowCount(); - } - - /** - * {@inheritDoc} - */ - @Override - public Object getValueAt(int row, int column) { - return table.getModel().getValueAt(row, column); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isCellEditable(int row, int column) { - return table.getModel().isCellEditable(row, column); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isTestable(int column) { - return getColumnByModelIndex(column) != null; - } - - // -------------------------- accessing view state/values - - /** - * {@inheritDoc} - * - * This is implemented to query the table's StringValueRegistry for an appropriate - * StringValue and use that for getting the string representation. - */ - @Override - public String getStringAt(int row, int column) { - StringValue sv = table.getStringValueRegistry().getStringValue(row, column); - return sv.getString(getValueAt(row, column)); - } - - /** - * {@inheritDoc} - */ - @Override - public Rectangle getCellBounds() { - return table.getCellRect(row, column, false); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isEditable() { - return table.isCellEditable(row, column); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isSelected() { - return table.isCellSelected(row, column); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean hasFocus() { - boolean rowIsLead = (table.getSelectionModel() - .getLeadSelectionIndex() == row); - boolean colIsLead = (table.getColumnModel().getSelectionModel() - .getLeadSelectionIndex() == column); - return table.isFocusOwner() && (rowIsLead && colIsLead); - } - - /** - * {@inheritDoc} - */ - @Override - public int convertColumnIndexToView(int columnIndex) { - return table.convertColumnIndexToView(columnIndex); - } - - /** - * {@inheritDoc} - */ - @Override - public int convertColumnIndexToModel(int columnIndex) { - return table.convertColumnIndexToModel(columnIndex); - } - - /** - * {@inheritDoc} - */ - @Override - public int convertRowIndexToView(int rowModelIndex) { - return table.convertRowIndexToView(rowModelIndex); - } - - /** - * {@inheritDoc} - */ - @Override - public int convertRowIndexToModel(int rowViewIndex) { - return table.convertRowIndexToModel(rowViewIndex); - } - - } - - // --------------------- managing renderers/editors - - /** - * Sets the Highlighters to the table, replacing any old - * settings. None of the given Highlighters must be null. - *

                - * - * This is a bound property. - *

                - * - * Note: as of version #1.257 the null constraint is enforced strictly. To - * remove all highlighters use this method without param. - * - * @param highlighters zero or more not null highlighters to use for - * renderer decoration. - * @throws NullPointerException if array is null or array contains null - * values. - * - * @see #getHighlighters() - * @see #addHighlighter(Highlighter) - * @see #removeHighlighter(Highlighter) - * - */ - public void setHighlighters(Highlighter... highlighters) { - Highlighter[] old = getHighlighters(); - getCompoundHighlighter().setHighlighters(highlighters); - firePropertyChange("highlighters", old, getHighlighters()); - } - - /** - * Returns the Highlighters used by this table. Maybe empty, - * but guarantees to be never null. - * - * @return the Highlighters used by this table, guaranteed to never null. - * @see #setHighlighters(Highlighter[]) - */ - public Highlighter[] getHighlighters() { - return getCompoundHighlighter().getHighlighters(); - } - - /** - * Appends a Highlighter to the end of the list of used - * Highlighters. The argument must not be null. - *

                - * - * @param highlighter the Highlighter to add, must not be null. - * @throws NullPointerException if Highlighter is null. - * - * @see #removeHighlighter(Highlighter) - * @see #setHighlighters(Highlighter[]) - */ - public void addHighlighter(Highlighter highlighter) { - Highlighter[] old = getHighlighters(); - getCompoundHighlighter().addHighlighter(highlighter); - firePropertyChange("highlighters", old, getHighlighters()); - } - - /** - * Removes the given Highlighter. - *

                - * - * Does nothing if the Highlighter is not contained. - * - * @param highlighter the Highlighter to remove. - * @see #addHighlighter(Highlighter) - * @see #setHighlighters(Highlighter...) - */ - public void removeHighlighter(Highlighter highlighter) { - Highlighter[] old = getHighlighters(); - getCompoundHighlighter().removeHighlighter(highlighter); - firePropertyChange("highlighters", old, getHighlighters()); - } - - /** - * Returns the CompoundHighlighter assigned to the table, null if none. - * PENDING: open up for subclasses again?. - * - * @return the CompoundHighlighter assigned to the table. - */ - protected CompoundHighlighter getCompoundHighlighter() { - if (compoundHighlighter == null) { - compoundHighlighter = new CompoundHighlighter(); - compoundHighlighter - .addChangeListener(getHighlighterChangeListener()); - } - return compoundHighlighter; - } - - /** - * Returns the ChangeListener to use with highlighters. Lazily - * creates the listener. - * - * @return the ChangeListener for observing changes of highlighters, - * guaranteed to be not-null - */ - protected ChangeListener getHighlighterChangeListener() { - if (highlighterChangeListener == null) { - highlighterChangeListener = createHighlighterChangeListener(); - } - return highlighterChangeListener; - } - - /** - * Creates and returns the ChangeListener observing Highlighters. - *

                - * Here: repaints the table on receiving a stateChanged. - * - * @return the ChangeListener defining the reaction to changes of - * highlighters. - */ - protected ChangeListener createHighlighterChangeListener() { - return new ChangeListener() { - @Override - public void stateChanged(ChangeEvent e) { - repaint(); - } - }; - } - - /** - * Returns the StringValueRegistry which defines the string representation for - * each cells. This is strictly for internal use by the table, which has the - * responsibility to keep in synch with registered renderers.

                - * - * Currently exposed for testing reasons, client code is recommended to not use nor override. - * - * @return the current string value registry - */ - protected StringValueRegistry getStringValueRegistry() { - if (stringValueRegistry == null) { - stringValueRegistry = createDefaultStringValueRegistry(); - } - return stringValueRegistry; - } - - /** - * Creates and returns the default registry for StringValues.

                - * - * @return the default registry for StringValues. - */ - protected StringValueRegistry createDefaultStringValueRegistry() { - return new StringValueRegistry(); - } - - - /** - * Updates per-column class in StringValueRegistry. This is called after - * structureChanged. - */ - private void updateStringValueRegistryColumnClasses() { - getStringValueRegistry().setColumnClasses(null); - for (int i = 0; i < getModel().getColumnCount(); i++) { - getStringValueRegistry().setColumnClass(getModel().getColumnClass(i), i); - } - } - - /** - * Updates per-column StringValue in StringValueRegistry based on given tableColumn. - * This is called after the column's renderer property changed or after the column - * is added. - * - * @param tableColumn the column to update from - * @param renderer the renderer potentially useful as StringValue. - */ - private void updateStringValueForColumn(TableColumn tableColumn, - TableCellRenderer renderer) { - getStringValueRegistry().setStringValue( - renderer instanceof StringValue ? (StringValue) renderer : null, - tableColumn.getModelIndex()); - } - /** - * Called in init to synch the StringValueProvider with default renderers per class - */ - private void initDefaultStringValues() { - for (Object clazz : defaultRenderersByColumnClass.keySet()) { - Object renderer = defaultRenderersByColumnClass.get(clazz); - if (renderer instanceof StringValue) { - getStringValueRegistry().setStringValue((StringValue) renderer, (Class) clazz); - } - } - } - - /** - * Inits per column string values from TableColumns - */ - private void initPerColumnStringValues() { - getStringValueRegistry().clearColumnStringValues(); - for (TableColumn tableColumn : getColumns(true)) { - updateStringValueForColumn(tableColumn, tableColumn.getCellRenderer()); - } - } - /** - * {@inheritDoc}

                - * - * Overridden to synchronize the string representation. If the renderer is of type - * StringValue a mapping it will be used as converter for the class type. If not, - * the mapping is reset to default. - */ - @Override - public void setDefaultRenderer(Class columnClass, - TableCellRenderer renderer) { - super.setDefaultRenderer(columnClass, renderer); - getStringValueRegistry().setStringValue( - (renderer instanceof StringValue) ? (StringValue) renderer : null, - columnClass); - } - - /** - * Returns the string representation of the cell value at the given - * position. - * - * @param row the row index of the cell in view coordinates - * @param column the column index of the cell in view coordinates. - * @return the string representation of the cell value as it will appear in - * the table. - */ - public String getStringAt(int row, int column) { - // changed implementation to use StringValueRegistry - StringValue stringValue = getStringValueRegistry().getStringValue( - convertRowIndexToModel(row), convertColumnIndexToModel(column)); - return stringValue.getString(getValueAt(row, column)); - } - - /** - * {@inheritDoc} - *

                - * - * Overridden to fix core bug #4614616 (NPE if TableModel's - * Class for the column is an interface). This method - * guarantees to always return a not null value. Returns the - * default renderer for Object if super returns - * null.

                - * - * Note: The lookup strategy for is unchanged compared to super. Subclasses - * which override this with a different lookup strategy are strongly advised to - * implement a custom StringValueRegistry with that lookup strategy to keep - * the WYSIWYM in SwingX. - * - */ - @Override - public TableCellRenderer getCellRenderer(int row, int column) { - TableCellRenderer renderer = super.getCellRenderer(row, column); - if (renderer == null) { - renderer = getDefaultRenderer(Object.class); - } - return renderer; - } - - /** - * Returns the decorated Component used as a stamp to render - * the specified cell. Overrides superclass version to provide support for - * cell decorators. - *

                - * - * Adjusts component orientation (guaranteed to happen before applying - * Highlighters). - *

                - * - * Per-column highlighters contained in - * {@link TableColumnExt#getHighlighters()} are applied to the renderer - * after the table highlighters. - *

                - * - * TODO kgs: interaction of search highlighter and column highlighters - *

                - * - * Note: DefaultTableCellRenderer and subclasses require a hack to play - * nicely with Highlighters because it has an internal "color memory" in - * setForeground/setBackground. The hack is applied in - * resetDefaultTableCellRendererColors which is called after - * super.prepareRenderer and before applying the Highlighters. The method is - * called always and for all renderers. - * - * @param renderer the TableCellRenderer to prepare - * @param row the row of the cell to render, where 0 is the first row - * @param column the column of the cell to render, where 0 is the first - * column - * @return the decorated Component used as a stamp to render - * the specified cell - * @see #resetDefaultTableCellRendererColors(Component, int, int) - * @see Highlighter - */ - @Override - public Component prepareRenderer(TableCellRenderer renderer, int row, - int column) { - Component stamp = super.prepareRenderer(renderer, row, column); - // #145-swingx: default renderers don't respect componentOrientation. - adjustComponentOrientation(stamp); - // #258-swingx: hacking around DefaultTableCellRenderer color memory. - resetDefaultTableCellRendererColors(stamp, row, column); - - ComponentAdapter adapter = getComponentAdapter(row, column); - // a very slight optimization: if this instance never had a highlighter - // added then don't create a compound here. - if (compoundHighlighter != null) { - stamp = compoundHighlighter.highlight(stamp, adapter); - } - - TableColumnExt columnExt = getColumnExt(column); - - if (columnExt != null) { - // JW: fix for #838 - artificial compound installs listener - // PENDING JW: instead of doing the looping ourselves, how - // about adding a method prepareRenderer to the TableColumnExt - for (Highlighter highlighter : columnExt.getHighlighters()) { - stamp = highlighter.highlight(stamp, adapter); - - } - // CompoundHighlighter columnHighlighters - // = new CompoundHighlighter(columnExt.getHighlighters()); - - } - - return stamp; - } - - /** - * Convenience method to get the rendering component for the given cell. - * - * @param row the row of the cell to render, where 0 is the first row - * @param col the column of the cell to render, where 0 is the first - * column - * @return the decorated Component used as a stamp to render - * the specified cell - */ - public Component prepareRenderer(int row, int col) { - return prepareRenderer(getCellRenderer(row, col), row, col); - } - - /** - * - * Method to apply a hack around DefaultTableCellRenderer "color memory" - * (Issue #258-swingx). Applies the hack if the client property - * USE_DTCR_COLORMEMORY_HACK having the value of - * Boolean.TRUE, does nothing otherwise. The property is true - * by default. - *

                - * - * The hack consists of applying a specialized Highlighter to - * force reset the color "memory" of DefaultTableCellRenderer. - * Note that the hack is applied always, that is even if there are no custom - * Highlighters. - *

                - * - * Client code which solves the problem at the core (that is in a - * well-behaved DefaultTableCellRenderer) can disable the hack - * by removing the client property or by subclassing and override this to do - * nothing. - * - * @param renderer the TableCellRenderer to hack - * @param row the row of the cell to render - * @param column the column index of the cell to render - * - * @see #prepareRenderer(TableCellRenderer, int, int) - * @see #USE_DTCR_COLORMEMORY_HACK - * @see ResetDTCRColorHighlighter - */ - protected void resetDefaultTableCellRendererColors(Component renderer, - int row, int column) { - if (!Boolean.TRUE.equals(getClientProperty(USE_DTCR_COLORMEMORY_HACK))) - return; - ComponentAdapter adapter = getComponentAdapter(row, column); - if (resetDefaultTableCellRendererHighlighter == null) { - resetDefaultTableCellRendererHighlighter = new ResetDTCRColorHighlighter(); - } - // hacking around DefaultTableCellRenderer color memory. - resetDefaultTableCellRendererHighlighter.highlight(renderer, adapter); - } - - /** - * {@inheritDoc} - *

                - * - * Overridden to adjust the editor's component orientation. - */ - @Override - public Component prepareEditor(TableCellEditor editor, int row, int column) { - Component comp = super.prepareEditor(editor, row, column); - // JW: might be null if generic editor barks about constructor - // super silently backs out - we do the same here - if (comp != null) { - adjustComponentOrientation(comp); - } - return comp; - } - - /** - * Adjusts the Component's orientation to this - * JXTable's CO if appropriate. The parameter must not be - * null. - *

                - * - * This implementation synchs the CO always. - * - * @param stamp the Component who's CO may need to be synched, - * must not be null. - */ - protected void adjustComponentOrientation(Component stamp) { - if (stamp.getComponentOrientation().equals(getComponentOrientation())) - return; - stamp.applyComponentOrientation(getComponentOrientation()); - } - - /** - * Creates default cell renderers for Objects, - * Numbers, Dates, Booleans, - * Icon/Image/s and URIs. - *

                - * Overridden to replace all super default renderers with SwingX variants and - * additionally register a default for URI types. Note: the latter - * registration will fail silently in headless environments or when the runtime - * context doesn't support Desktop. - * - *

                - * {@inheritDoc} - * - * @see DefaultTableRenderer - * @see org.jdesktop.swingx.renderer.ComponentProvider - */ - @Override - protected void createDefaultRenderers() { - defaultRenderersByColumnClass = new UIDefaults(8, 0.75f); - // configured default table renderer (internally LabelProvider) - setDefaultRenderer(Object.class, new DefaultTableRenderer()); - setDefaultRenderer(Number.class, new DefaultTableRenderer( - StringValues.NUMBER_TO_STRING, JLabel.RIGHT)); - setDefaultRenderer(Date.class, new DefaultTableRenderer( - StringValues.DATE_TO_STRING)); - // use the same center aligned default for Image/Icon - TableCellRenderer renderer = new DefaultTableRenderer(new MappedValue( - StringValues.EMPTY, IconValues.ICON), JLabel.CENTER); - setDefaultRenderer(Icon.class, renderer); - setDefaultRenderer(ImageIcon.class, renderer); - // use a ButtonProvider for booleans - setDefaultRenderer(Boolean.class, new DefaultTableRenderer( - new CheckBoxProvider())); - - try { - setDefaultRenderer(URI.class, new DefaultTableRenderer( - new HyperlinkProvider(new HyperlinkAction()) - )); - } catch (Exception e) { - // nothing to do - either headless or Desktop not supported - } - } - - /** - * Creates default cell editors for objects, numbers, and boolean values. - *

                - * Overridden to hook enhanced editors (f.i. NumberEditorExt - * - * @see DefaultCellEditor - */ - @SuppressWarnings("unchecked") - @Override - protected void createDefaultEditors() { - defaultEditorsByColumnClass = new UIDefaults(3, 0.75f); - defaultEditorsByColumnClass.put(Object.class, new GenericEditor()); - // Numbers - // JW: fix for - // Issue #1183-swingx: NumberEditorExt throws in getCellEditorValue if - // Integer (short, byte..) below/above min/max. - // Issue #1236-swingx: NumberEditorExt cannot be used in columns with Object type - defaultEditorsByColumnClass.put(Number.class, new NumberEditorExt(true)); - // Booleans - defaultEditorsByColumnClass.put(Boolean.class, new BooleanEditor()); - - } - - /** - * Default editor registered for Object. The editor tries to - * create a new instance of the column's class by reflection. It assumes - * that the class has a constructor taking a single String - * parameter. - *

                - * - * The editor can be configured with a custom JTextField. - * - */ - public static class GenericEditor extends DefaultCellEditor { - - Class[] argTypes = new Class[] { String.class }; - - java.lang.reflect.Constructor constructor; - - Object value; - - public GenericEditor() { - this(new JTextField()); - } - - public GenericEditor(JTextField textField) { - super(textField); - getComponent().setName("Table.editor"); - } - - @Override - public boolean stopCellEditing() { - String s = (String) super.getCellEditorValue(); - // JW: changed logic to hack around (core!) Issue #1535 - // don't special case empty, but string contructor: - // if so, by-pass relection altogether - if (constructor.getDeclaringClass() == String.class) { - value = s; - } else { // try instantiating a new Object with the string - try { - value = constructor.newInstance(new Object[] { s }); - } catch (Exception e) { - ((JComponent) getComponent()).setBorder(new LineBorder( - Color.red)); - return false; - } - } - return super.stopCellEditing(); - } - - @Override - public Component getTableCellEditorComponent(JTable table, - Object value, boolean isSelected, int row, int column) { - this.value = null; - ((JComponent) getComponent()) - .setBorder(new LineBorder(Color.black)); - try { - Class type = table.getColumnClass(column); - // Since our obligation is to produce a value which is - // assignable for the required type it is OK to use the - // String constructor for columns which are declared - // to contain Objects. A String is an Object. - if (type == Object.class) { - type = String.class; - } - constructor = type.getConstructor(argTypes); - } catch (Exception e) { - return null; - } - return super.getTableCellEditorComponent(table, value, isSelected, - row, column); - } - - @Override - public Object getCellEditorValue() { - return value; - } - } - - /** - * - * Editor for Numbers. - *

                - * Note: this is no longer registered by default. The current default is - * NumberEditorExt which differs from this in being - * locale-aware. - * - */ - public static class NumberEditor extends GenericEditor { - - public NumberEditor() { - ((JTextField) getComponent()) - .setHorizontalAlignment(JTextField.RIGHT); - } - } - - /** - * The default editor for Boolean types. - */ - public static class BooleanEditor extends DefaultCellEditor { - public BooleanEditor() { - super(new JCheckBox()); - JCheckBox checkBox = (JCheckBox) getComponent(); - checkBox.setHorizontalAlignment(JCheckBox.CENTER); - } - - @Override - public Component getTableCellEditorComponent(JTable table, - Object value, boolean isSelected, int row, int column) { - // fix #1521-swingx: consistent selection behaviour - Component comp = super.getTableCellEditorComponent(table, - value, isSelected || shouldSelectCell(null), row, column); - return comp; - } - - - } - - // ----------------------------- enhanced editing support - - /** - * Returns the editable property of the JXTable as a whole. - * - * @return boolean to indicate if the table is editable. - * @see #setEditable - */ - public boolean isEditable() { - return editable; - } - - /** - * Sets the editable property. This property allows to mark all cells in a - * table as read-only, independent of their per-column editability as - * returned by TableColumnExt.isEditable and their per-cell - * editability as returned by the TableModel.isCellEditable. If - * a cell is read-only in its column or model layer, this property has no - * effect. - *

                - * - * The default value is true. - * - * @param editable the flag to indicate if the table is editable. - * @see #isEditable - * @see #isCellEditable(int, int) - */ - public void setEditable(boolean editable) { - boolean old = isEditable(); - this.editable = editable; - firePropertyChange("editable", old, isEditable()); - } - - /** - * Returns the property which determines the edit termination behaviour on - * focus lost. - * - * @return boolean to indicate whether an ongoing edit should be terminated - * if the focus is moved to somewhere outside of the table. - * @see #setTerminateEditOnFocusLost(boolean) - */ - public boolean isTerminateEditOnFocusLost() { - return Boolean.TRUE - .equals(getClientProperty("terminateEditOnFocusLost")); - } - - /** - * Sets the property to determine whether an ongoing edit should be - * terminated if the focus is moved to somewhere outside of the table. If - * true, terminates the edit, does nothing otherwise. The exact behaviour is - * implemented in JTable.CellEditorRemover: "outside" is - * interpreted to be on a component which is not under the table hierarchy - * but inside the same toplevel window, "terminate" does so in any case, - * first tries to stop the edit, if that's unsuccessful it cancels the edit. - *

                - * The default value is true. - * - * @param terminate the flag to determine whether or not to terminate the - * edit - * @see #isTerminateEditOnFocusLost() - */ - public void setTerminateEditOnFocusLost(boolean terminate) { - // JW: we can leave the propertyChange notification to the - // putClientProperty - the key and method name are the same - putClientProperty("terminateEditOnFocusLost", terminate); - } - - /** - * Returns the autoStartsEdit property. - * - * @return boolean to indicate whether a keyStroke should try to start - * editing. - * @see #setAutoStartEditOnKeyStroke(boolean) - */ - public boolean isAutoStartEditOnKeyStroke() { - return !Boolean.FALSE - .equals(getClientProperty("JTable.autoStartsEdit")); - } - - /** - * Sets the autoStartsEdit property. If true, keystrokes are passed-on to - * the cellEditor of the lead cell to let it decide whether to start an - * edit. - *

                - * The default value is true. - *

                - * - * @param autoStart boolean to determine whether a keyStroke should try to - * start editing. - * @see #isAutoStartEditOnKeyStroke() - */ - public void setAutoStartEditOnKeyStroke(boolean autoStart) { - boolean old = isAutoStartEditOnKeyStroke(); - // JW: we have to take over propertyChange notification - // because the key and method name are different. - // As a consequence, there are two events fired: one for - // the client prop and one for this method. - putClientProperty("JTable.autoStartsEdit", autoStart); - firePropertyChange("autoStartEditOnKeyStroke", old, - isAutoStartEditOnKeyStroke()); - } - - /** - * {@inheritDoc} - *

                - * - * overridden to install a custom editor remover. - */ - @Override - public boolean editCellAt(int row, int column, EventObject e) { - boolean started = super.editCellAt(row, column, e); - if (started) { - hackEditorRemover(); - } - return started; - } - - /** - * Overridden with backport from Mustang fix for #4684090, #4887999. - */ - @Override - public void removeEditor() { - boolean isFocusOwnerInTheTable = isFocusOwnerDescending(); - // let super do its stuff - super.removeEditor(); - if (isFocusOwnerInTheTable) { - requestFocusInWindow(); - } - } - - /** - * Returns a boolean to indicate if the current focus owner is descending - * from this table. Returns false if not editing, otherwise walks the - * focusOwner hierarchy, taking popups into account. - * - * @return a boolean to indicate if the current focus owner is contained. - */ - private boolean isFocusOwnerDescending() { - if (!isEditing()) - return false; - Component focusOwner = KeyboardFocusManager - .getCurrentKeyboardFocusManager().getFocusOwner(); - // PENDING JW: special casing to not fall through ... really wanted? - if (focusOwner == null) - return false; - if (SwingXUtilities.isDescendingFrom(focusOwner, this)) - return true; - // same with permanent focus owner - Component permanent = KeyboardFocusManager - .getCurrentKeyboardFocusManager().getPermanentFocusOwner(); - return SwingXUtilities.isDescendingFrom(permanent, this); - } - - protected transient CellEditorRemover editorRemover; - - /** - * removes the standard editor remover and adds the custom remover. - * - */ - private void hackEditorRemover() { - KeyboardFocusManager manager = KeyboardFocusManager - .getCurrentKeyboardFocusManager(); - PropertyChangeListener[] listeners = manager - .getPropertyChangeListeners("permanentFocusOwner"); - for (int i = listeners.length - 1; i >= 0; i--) { - if (listeners[i].getClass().getName().startsWith( - "javax.swing.JTable")) { - manager.removePropertyChangeListener("permanentFocusOwner", - listeners[i]); - break; - } - } - if (editorRemover == null) { - editorRemover = new CellEditorRemover(); - } - } - - /** - * {@inheritDoc} - *

                - * - * Overridden to uninstall the custom editor remover. - */ - @Override - public void removeNotify() { - if (editorRemover != null) { - editorRemover.uninstall(); - editorRemover = null; - } - super.removeNotify(); - } - - /** - * {@inheritDoc} - *

                - * - * Overridden to prevent spurious focus loss to outside of table while - * removing the editor. This is essentially a hack around core bug #6210779. - * - * PENDING: add link to wiki! - */ - @Override - public boolean isFocusCycleRoot() { - if (isEditingFocusCycleRoot()) { - return true; - } - return super.isFocusCycleRoot(); - } - - /** - * {@inheritDoc} - *

                - * Overridden to try to stop the edit, if appropriate. Calls super if - * succeeded, does not yield otherwise. - * - */ - @Override - public void transferFocus() { - if (isEditingFocusCycleRoot() && !getCellEditor().stopCellEditing()) - return; - super.transferFocus(); - } - - /** - * {@inheritDoc} - *

                - * Overridden to try to stop the edit, if appropiate. Calls super if - * succeeded, does not yield otherwise. - * - */ - @Override - public void transferFocusBackward() { - if (isEditingFocusCycleRoot() && !getCellEditor().stopCellEditing()) - return; - super.transferFocusBackward(); - } - - /** - * - * @return a boolean to indicate whether the table needs to fake being focus - * cycle root. - */ - private boolean isEditingFocusCycleRoot() { - return isEditing() && isTerminateEditOnFocusLost(); - } - - /** - * This class tracks changes in the keyboard focus state. It is used when - * the JTable is editing to determine when to cancel the edit. If focus - * switches to a component outside of the jtable, but in the same window, - * this will cancel editing. - */ - class CellEditorRemover implements PropertyChangeListener { - KeyboardFocusManager focusManager; - - public CellEditorRemover() { - install(); - } - - private void install() { - focusManager = KeyboardFocusManager - .getCurrentKeyboardFocusManager(); - focusManager.addPropertyChangeListener("permanentFocusOwner", this); - focusManager.addPropertyChangeListener("managingFocus", this); - } - - /** - * remove all listener registrations. - * - */ - public void uninstall() { - focusManager.removePropertyChangeListener("permanentFocusOwner", - this); - focusManager.removePropertyChangeListener("managingFocus", this); - focusManager = null; - } - - @Override - public void propertyChange(PropertyChangeEvent ev) { - if (ev == null) - return; - if ("permanentFocusOwner".equals(ev.getPropertyName())) { - permanentFocusOwnerChange(); - } else if ("managingFocus".equals(ev.getPropertyName())) { - // TODO uninstall/install after manager changed. - } - } - - /** - * - */ - private void permanentFocusOwnerChange() { - if (!isEditing() || !isTerminateEditOnFocusLost()) { - return; - } - - Component c = focusManager.getPermanentFocusOwner(); - while (c != null) { - // PENDING JW: logic untested! - if (c instanceof JPopupMenu) { - c = ((JPopupMenu) c).getInvoker(); - } else { - if (c == JXTable.this) { - // focus remains inside the table - return; - } else if (c instanceof JPopupMenu) { - // PENDING JW: left-over? we should never reach this ... - // need to switch the hierarchy to a popups invoker - } else if ((c instanceof Window) - || (SwingXUtilities.isApplet(c) && c.getParent() == null)) { - if (c == SwingUtilities.getRoot(JXTable.this)) { - if (!getCellEditor().stopCellEditing()) { - getCellEditor().cancelCellEditing(); - } - } - break; - } - c = c.getParent(); - } - } - } - } - - // ---------------------------- updateUI support - - /** - * {@inheritDoc} - *

                - * Additionally updates auto-adjusted row height and highlighters. - *

                - * Another of the override motivation is to fix core issue (?? ID): super - * fails to update all renderers/editors. - */ - @Override - public void updateUI() { - super.updateUI(); - updateColumnControlUI(); - for (Enumeration defaultEditors = defaultEditorsByColumnClass - .elements(); defaultEditors.hasMoreElements();) { - updateEditorUI(defaultEditors.nextElement()); - } - - for (Enumeration defaultRenderers = defaultRenderersByColumnClass - .elements(); defaultRenderers.hasMoreElements();) { - updateRendererUI(defaultRenderers.nextElement()); - } - for (TableColumn column : getColumns(true)) { - updateColumnUI(column); - } - updateRowHeightUI(true); - updateHighlighterUI(); - } - - /** - * Updates the ui of the columnControl if appropriate. - */ - protected void updateColumnControlUI() { - if ((columnControlButton != null) - && (columnControlButton.getParent() == null)) { - SwingUtilities.updateComponentTreeUI(columnControlButton); - } - } - - /** - * Tries its best to updateUI of the potential - * TableCellEditor. - * - * @param maybeEditor the potential editor. - */ - private void updateEditorUI(Object maybeEditor) { - // maybe null or proxyValue - if (!(maybeEditor instanceof TableCellEditor)) - return; - // super handled this - if ((maybeEditor instanceof JComponent) - || (maybeEditor instanceof DefaultCellEditor)) - return; - // custom editors might balk about fake rows/columns - try { - Component comp = ((TableCellEditor) maybeEditor) - .getTableCellEditorComponent(this, null, false, -1, -1); - if (comp != null) { - SwingUtilities.updateComponentTreeUI(comp); - } - } catch (Exception e) { - // ignore - can't do anything - } - } - - /** - * Tries its best to updateUI of the potential - * TableCellRenderer. - * - * @param maybeRenderer the potential renderer. - */ - private void updateRendererUI(Object maybeRenderer) { - // maybe null or proxyValue - if (!(maybeRenderer instanceof TableCellRenderer)) - return; - // super handled this - if (maybeRenderer instanceof JComponent) - return; - Component comp = null; - if (maybeRenderer instanceof AbstractRenderer) { - comp = ((AbstractRenderer) maybeRenderer).getComponentProvider() - .getRendererComponent(null); - } else { - try { - // custom editors might balk about fake rows/columns - comp = ((TableCellRenderer) maybeRenderer) - .getTableCellRendererComponent(this, null, false, - false, -1, -1); - - } catch (Exception e) { - // can't do anything - renderer can't cope with off-range cells - } - } - if (comp != null) { - SwingUtilities.updateComponentTreeUI(comp); - } - } - - /** - * Updates TableColumn after updateUI changes. This implementation delegates - * to the column if it is of type UIDependent, takes over to try an update - * of the column's cellEditor, Cell-/HeaderRenderer otherwise. - * - * @param column the tableColumn to update. - */ - protected void updateColumnUI(TableColumn column) { - if (column instanceof UIDependent) { - ((UIDependent) column).updateUI(); - } else { - updateEditorUI(column.getCellEditor()); - updateRendererUI(column.getCellRenderer()); - updateRendererUI(column.getHeaderRenderer()); - } - } - - /** - * Updates highlighter after updateUI changes. - * - * @see UIDependent - */ - protected void updateHighlighterUI() { - if (compoundHighlighter == null) - return; - compoundHighlighter.updateUI(); - } - - /** - * Auto-adjusts rowHeight to something more pleasing then the default. This - * method is called after instantiation and after updating the UI. Does - * nothing if the given parameter is true and the rowHeight had - * been already set by client code. The underlying problem is that raw types - * can't implement UIResource. - *

                - * This implementation asks the UIManager for a default value (stored with - * key "JXTable.rowHeight"). If none is available, calculates a "reasonable" - * height from the table's fontMetrics, assuming that most renderers/editors - * will have a border with top/bottom of 1. - *

                - * - * @param respectRowSetFlag a boolean to indicate whether client-code flag - * should be respected. - * @see #isXTableRowHeightSet - */ - protected void updateRowHeightUI(boolean respectRowSetFlag) { - if (respectRowSetFlag && isXTableRowHeightSet) - return; - int uiHeight = UIManager.getInt(UIPREFIX + "rowHeight"); - if (uiHeight > 0) { - setRowHeight(uiHeight); - } else { - int fontBasedHeight = getFontMetrics(getFont()).getHeight() + 2; - int magicMinimum = 18; - setRowHeight(Math.max(fontBasedHeight, magicMinimum)); - } - isXTableRowHeightSet = false; - } - - /** - * Convenience to set both grid line visibility and default margin for - * horizontal/vertical lines. The margin defaults to 1 or 0 if the grid - * lines are drawn or not drawn. - *

                - * - * @param showHorizontalLines boolean to decide whether to draw horizontal - * grid lines. - * @param showVerticalLines boolean to decide whether to draw vertical grid - * lines. - * @see JTable#setShowGrid(boolean) - * @see JTable#setIntercellSpacing(Dimension) - */ - public void setShowGrid(boolean showHorizontalLines, - boolean showVerticalLines) { - int defaultRowMargin = showHorizontalLines ? 1 : 0; - setRowMargin(defaultRowMargin); - setShowHorizontalLines(showHorizontalLines); - int defaultColumnMargin = showVerticalLines ? 1 : 0; - setColumnMargin(defaultColumnMargin); - setShowVerticalLines(showVerticalLines); - } - - /** - * {@inheritDoc} - *

                - * Behaves exactly like super. - *

                - * It's overridden to warn against a frequent programming error: this method - * toggles only the visibility of the grid lines, it does not - * update the row/column margins - which may lead to visual artefacts, as - * f.i. not showing the lines at all or showing normal table background in - * selected state where the lines should have been. - * - * @see #setShowGrid(boolean, boolean) - */ - @Override - public void setShowGrid(boolean showGrid) { - super.setShowGrid(showGrid); - } - - /** - * {@inheritDoc} - *

                - * Overriden to mark the request as client-code induced. - * - * @see #isXTableRowHeightSet - */ - @Override - public void setRowHeight(int rowHeight) { - super.setRowHeight(rowHeight); - if (rowHeight > 0) { - isXTableRowHeightSet = true; - } - } - - /** - * Sets the rowHeight for all rows to the given value. Keeps the flag - * isXTableRowHeight unchanged. This enables the distinction - * between setting the height for internal reasons from doing so by client - * code. - * - * @param rowHeight new height in pixel. - * @see #setRowHeight(int) - * @see #isXTableRowHeightSet - */ - protected void adminSetRowHeight(int rowHeight) { - boolean heightSet = isXTableRowHeightSet; - setRowHeight(rowHeight); - isXTableRowHeightSet = heightSet; - } - - // ---------------------------- overriding super factory methods and buggy - /** - * {@inheritDoc} - *

                - * Overridden to work around core Bug (ID #6291631): negative y is mapped to - * row 0). - * - */ - @Override - public int rowAtPoint(Point point) { - if (point.y < 0) - return -1; - return super.rowAtPoint(point); - } - - /** - * - * {@inheritDoc} - *

                - * - * Overridden to return a JXTableHeader. - * - * @see JXTableHeader - */ - @Override - protected JTableHeader createDefaultTableHeader() { - return new JXTableHeader(columnModel); - } - - /** - * - * {@inheritDoc} - *

                - * - * Overridden to return a DefaultTableColumnModelExt. - * - * @see DefaultTableColumnModelExt - */ - @Override - protected TableColumnModel createDefaultColumnModel() { - return new DefaultTableColumnModelExt(); - } - - /** - * {@inheritDoc} - *

                - * Overridden because super throws NPE on null param. - */ - @Override - public void setSelectionBackground(Color selectionBackground) { - Color old = getSelectionBackground(); - this.selectionBackground = selectionBackground; - firePropertyChange("selectionBackground", old, getSelectionBackground()); - repaint(); - } - - /** - * {@inheritDoc} - *

                - * Overridden because super throws NPE on null param. - */ - @Override - public void setSelectionForeground(Color selectionForeground) { - Color old = getSelectionForeground(); - this.selectionForeground = selectionForeground; - firePropertyChange("selectionForeground", old, getSelectionForeground()); - repaint(); - } - - /** - * {@inheritDoc} - *

                - * Overridden because super throws NPE on null param. - */ - @Override - public void setGridColor(Color gridColor) { - Color old = getGridColor(); - this.gridColor = gridColor; - firePropertyChange("gridColor", old, getGridColor()); - repaint(); - } - -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/JXTableHeader.java b/src/main/java/org/jdesktop/swingx/JXTableHeader.java deleted file mode 100644 index 6a9586e06b..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXTableHeader.java +++ /dev/null @@ -1,752 +0,0 @@ -/* - * $Id: JXTableHeader.java 3960 2011-03-15 19:36:53Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx; - -import org.jdesktop.swingx.event.TableColumnModelExtListener; -import org.jdesktop.swingx.plaf.LookAndFeelAddons; -import org.jdesktop.swingx.plaf.TableHeaderAddon; -import org.jdesktop.swingx.sort.SortController; -import org.jdesktop.swingx.table.TableColumnExt; - -import javax.swing.*; -import javax.swing.event.MouseInputListener; -import javax.swing.table.JTableHeader; -import javax.swing.table.TableCellRenderer; -import javax.swing.table.TableColumn; -import javax.swing.table.TableColumnModel; -import java.awt.*; -import java.awt.event.MouseEvent; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.io.Serializable; -import java.util.logging.Logger; - -/** - * TableHeader with extended functionality if associated Table is of - * type JXTable.

                - * - *

                Extended user interaction

                - * - *
                  - *
                • Supports column pack (== auto-resize to exactly fit the contents) - * on double-click in resize region. - *
                • Configurable to resort a column on the second click of a mouseClicked event - * (feature request #271-swingx) - *
                • Does its best to not sort if the mouse click happens in the resize region. - *
                • Supports horizontal auto-scroll if a column is dragged outside visible rectangle. - * This feature is enabled if the autoscrolls property is true. The default is false - * (because of Issue #788-swingx which still isn't fixed for jdk1.6). - *
                - * - * Note: extended sort and resize related functionality is fully effective only if the header's - * table is of type JXTable and has control over the row sorter, that is the row sorter - * is of type SortController. - * - *

                Extended functionality

                - * - *
                  - *
                • Listens to TableColumn propertyChanges to update itself accordingly. - *
                • Supports per-column header ToolTips. - *
                • Guarantees reasonable minimal height > 0 for header preferred height. -*
                - * - * - * @author Jeanette Winzenburg - * - * @see JXTable#toggleSortOrder(int) - * @see JXTable#resetSortOrder() - * @see SortGestureRecognizer - */ -public class JXTableHeader extends JTableHeader - implements TableColumnModelExtListener { - - @SuppressWarnings("unused") - private static final Logger LOG = Logger.getLogger(JXTableHeader.class - .getName()); - - static { - LookAndFeelAddons.contribute(new TableHeaderAddon()); - } - - private transient PropertyChangeListener tablePropertyChangeListener; - private boolean resortsOnDoubleClick; - - /** - * Constructs a JTableHeader with a default - * TableColumnModel. - * - * @see #createDefaultColumnModel - */ - public JXTableHeader() { - super(); - } - - /** - * Constructs a JTableHeader which is initialized with - * cm as the column model. If cm is - * null this method will initialize the table header with a - * default TableColumnModel. - * - * @param columnModel the column model for the table - * @see #createDefaultColumnModel - */ - public JXTableHeader(TableColumnModel columnModel) { - super(columnModel); - } - - - /** - * {@inheritDoc}

                - * Sets the associated JTable. Enables enhanced header - * features if table is of type JXTable.

                - * - * PENDING: who is responsible for synching the columnModel? - */ - @Override - public void setTable(JTable table) { - uninstallTable(); - super.setTable(table); - installTable(); -// setColumnModel(table.getColumnModel()); - // the additional listening option makes sense only if the table - // actually is a JXTable - if (getXTable() != null) { - installHeaderListener(); - } else { - uninstallHeaderListener(); - } - } - - /** - * Installs the table.

                - * This implemenation synchs enabled state and installs the PropertyChangeListener. - */ - protected void installTable() { - updateEnabledFromTable(); - if (getTable() == null) return; - getTable().addPropertyChangeListener(getTablePropertyChangeListener()); - } - - /** - * Synchs the header's enabled with the table's enabled property. - */ - protected void updateEnabledFromTable() { - setEnabled(getTable() != null ? getTable().isEnabled() : true); - } - - /** - * Uninstalls the table.

                - * This implementation uninstalls the PropertyChangeListener. - */ - protected void uninstallTable() { - if (getTable() == null) return; - getTable().removePropertyChangeListener(getTablePropertyChangeListener()); - } - - - /** - * Implements TableColumnModelExt to allow internal update after - * column property changes.

                - * - * This implementation triggers a resizeAndRepaint on every propertyChange which - * doesn't already fire a "normal" columnModelEvent. - * - * @param event change notification from a contained TableColumn. - * @see #isColumnEvent(PropertyChangeEvent) - * @see TableColumnModelExtListener - * - * - */ - @Override - public void columnPropertyChange(PropertyChangeEvent event) { - if (isColumnEvent(event)) return; - resizeAndRepaint(); - } - - - /** - * Returns a boolean indicating if a property change event received - * from column changes is expected to be already broadcasted by the - * core TableColumnModel.

                - * - * This implementation returns true for notification of width, preferredWidth - * and visible properties, false otherwise. - * - * @param event the PropertyChangeEvent received as TableColumnModelExtListener. - * @return a boolean to decide whether the same event triggers a - * base columnModelEvent. - */ - protected boolean isColumnEvent(PropertyChangeEvent event) { - return "width".equals(event.getPropertyName()) || - "preferredWidth".equals(event.getPropertyName()) - || "visible".equals(event.getPropertyName()); - } - - /** - * {@inheritDoc}

                - * - * Overridden to respect the column tooltip, if available. - * - * @return the column tooltip of the column at the mouse position - * if not null or super if not available. - */ - @Override - public String getToolTipText(MouseEvent event) { - String columnToolTipText = getColumnToolTipText(event); - return columnToolTipText != null ? columnToolTipText : super.getToolTipText(event); - } - - /** - * Returns the column tooltip of the column at the position - * of the MouseEvent, if a tooltip is available. - * - * @param event the mouseEvent representing the mouse location. - * @return the column tooltip of the column below the mouse location, - * or null if not available. - */ - protected String getColumnToolTipText(MouseEvent event) { - if (getXTable() == null) return null; - int column = columnAtPoint(event.getPoint()); - if (column < 0) return null; - TableColumnExt columnExt = getXTable().getColumnExt(column); - return columnExt != null ? columnExt.getToolTipText() : null; - } - - /** - * Returns the associated table if it is of type JXTable, or null if not. - * - * @return the associated table if of type JXTable or null if not. - */ - public JXTable getXTable() { - if (!(getTable() instanceof JXTable)) - return null; - return (JXTable) getTable(); - } - - /** - * Returns the resortsOnDoubleClick property. - * - * @return a flag indicating whether or not the second click in a mouseClicked - * event should toggle the sort order again. - * - * @see #setResortsOnDoubleClick(boolean) - */ - public boolean getResortsOnDoubleClick() { - return getXTable() != null && resortsOnDoubleClick; - } - - /** - * Sets the resortsOnDoubleClick property. If enabled, the second click - * of a mouseClicked event will toggle the sort order again if the - * column has been unsorted before. This is introduced to support - * feature request #271-swingx. It is effective only if the coupled table - * is of type JXTable and has full control about its RowSorter's properties. - * - * The default value is false. - * - * @param resortsOnDoubleClick a boolean indicating whether or not the - * second click in a mouseClicked event should resort the column. - * - * @see #getResortsOnDoubleClick() - */ - public void setResortsOnDoubleClick(boolean resortsOnDoubleClick) { - boolean old = getResortsOnDoubleClick(); - this.resortsOnDoubleClick = resortsOnDoubleClick; - firePropertyChange("resortsOnDoubleClick", old, getResortsOnDoubleClick()); - } - - /** - * Returns the TableCellRenderer to use for the column with the given index. This - * implementation returns the column's header renderer if available or this header's - * default renderer if not. - * - * @param columnIndex the index in view coordinates of the column - * @return the renderer to use for the column, guaranteed to be not null. - */ - public TableCellRenderer getCellRenderer(int columnIndex) { - TableCellRenderer renderer = getColumnModel().getColumn(columnIndex).getHeaderRenderer(); - return renderer != null ? renderer : getDefaultRenderer(); - } - - /** - * {@inheritDoc}

                - * - * Overridden to adjust for a reasonable minimum height. Done to fix Issue 334-swingx, - * which actually is a core issue misbehaving in returning a zero height - * if the first column has no text. - * - * @see #getPreferredSize(Dimension) - * @see #getMinimumHeight(int). - * - */ - @Override - public Dimension getPreferredSize() { - Dimension pref = super.getPreferredSize(); - pref = getPreferredSize(pref); - pref.height = getMinimumHeight(pref.height); - return pref; - } - - /** - * Returns a preferred size which is adjusted to the maximum of all - * header renderers' height requirement. - * - * @param pref an initial preferred size - * @return the initial preferred size with its height property adjusted - * to the maximum of all renderers preferred height requirement. - * - * @see #getPreferredSize() - * @see #getMinimumHeight(int) - */ - protected Dimension getPreferredSize(Dimension pref) { - int height = pref.height; - for (int i = 0; i < getColumnModel().getColumnCount(); i++) { - TableCellRenderer renderer = getCellRenderer(i); - Component comp = renderer.getTableCellRendererComponent(table, - getColumnModel().getColumn(i).getHeaderValue(), false, false, -1, i); - height = Math.max(height, comp.getPreferredSize().height); - } - pref.height = height; - return pref; - - } - - /** - * Returns a reasonable minimal preferred height for the header. This is - * meant as a last straw if all header values are null, renderers report 0 as - * their preferred height.

                - * - * This implementation returns the default header renderer's preferred height as measured - * with a dummy value if the input height is 0, otherwise returns the height - * unchanged. - * - * @param height the initial height. - * @return a reasonable minimal preferred height. - * - * @see #getPreferredSize() - * @see #getPreferredSize(Dimension) - */ - protected int getMinimumHeight(int height) { - if ((height == 0)) { -// && (getXTable() != null) -// && getXTable().isColumnControlVisible()){ - TableCellRenderer renderer = getDefaultRenderer(); - Component comp = renderer.getTableCellRendererComponent(getTable(), - "dummy", false, false, -1, -1); - height = comp.getPreferredSize().height; - } - return height; - } - - - /** - * @inherited

                - * - * Overridden to fire a propertyChange for draggedColumn. - */ - @Override - public void setDraggedColumn(TableColumn column) { - if (getDraggedColumn() == column) return; - TableColumn old = getDraggedColumn(); - super.setDraggedColumn(column); - firePropertyChange("draggedColumn", old, getDraggedColumn()); - } - - - /** - * @inherited

                - * - * Overridden to fire a propertyChange for resizingColumn. - */ - @Override - public void setResizingColumn(TableColumn aColumn) { - if (getResizingColumn() == aColumn) return; - TableColumn old = getResizingColumn(); - super.setResizingColumn(aColumn); - firePropertyChange("resizingColumn", old, getResizingColumn()); - } - - - - /** - * {@inheritDoc}

                - * - * Overridden to scroll the table to keep the dragged column visible. - * This side-effect is enabled only if the header's autoscroll property is - * true and the associated table is of type JXTable.

                - * - * The autoscrolls is disabled by default. With or without - core - * issue #6503981 has weird effects (for jdk 1.6 - 1.6u3) on a plain - * JTable as well as a JXTable, fixed in 1.6u4. - * - */ - @Override - public void setDraggedDistance(int distance) { - int old = getDraggedDistance(); - super.setDraggedDistance(distance); - // fire because super doesn't - firePropertyChange("draggedDistance", old, getDraggedDistance()); - if (!getAutoscrolls() || (getXTable() == null)) return; - TableColumn column = getDraggedColumn(); - // fix for #788-swingx: don't try to scroll if we have no dragged column - // as doing will confuse the horizontalScrollEnabled on the JXTable. - if (column != null) { - getXTable().scrollColumnToVisible(getViewIndexForColumn(column)); - } - } - - /** - * Returns the the dragged column if and only if, a drag is in process and - * the column is visible, otherwise returns null. - * - * @return the dragged column, if a drag is in process and the column is - * visible, otherwise returns null - * @see #getDraggedDistance - */ - @Override - public TableColumn getDraggedColumn() { - return isVisible(draggedColumn) ? draggedColumn : null; - } - - /** - * Checks and returns the column's visibility. - * - * @param column the TableColumn to check - * @return a boolean indicating if the column is visible - */ - private boolean isVisible(TableColumn column) { - return getViewIndexForColumn(column) >= 0; - } - - /** - * Returns the (visible) view index for the table column - * or -1 if not visible or not contained in this header's - * columnModel. - * - * - * @param aColumn the TableColumn to find the view index for - * @return the view index of the given table column or -1 if not visible - * or not contained in the column model. - */ - private int getViewIndexForColumn(TableColumn aColumn) { - if (aColumn == null) - return -1; - TableColumnModel cm = getColumnModel(); - for (int column = 0; column < cm.getColumnCount(); column++) { - if (cm.getColumn(column) == aColumn) { - return column; - } - } - return -1; - } - - /** - * Returns the PropertyChangeListener to register on the owning table, - * lazily created. - * - * @return the PropertyChangeListener to use on the owning table. - */ - protected PropertyChangeListener getTablePropertyChangeListener() { - if (tablePropertyChangeListener == null) { - tablePropertyChangeListener = createTablePropertyChangeListener(); - } - return tablePropertyChangeListener; - } - - /** - * Creates and returns the PropertyChangeListener to register on the - * owning table.

                - * - * This implementation synchs the header's enabled properties with the - * table's enabled. - * - * @return the PropertyChangeListener to register on the owning table. - */ - protected PropertyChangeListener createTablePropertyChangeListener() { - PropertyChangeListener l = new PropertyChangeListener() { - - @Override - public void propertyChange(PropertyChangeEvent evt) { - if ("enabled".equals(evt.getPropertyName())) { - updateEnabledFromTable(); - } - } - }; - return l; - } - - - /** - * Creates and installs header listeners to service the extended functionality. - * This implementation creates and installs a custom mouse input listener. - */ - protected void installHeaderListener() { - if (headerListener == null) { - headerListener = new HeaderListener(); - addMouseListener(headerListener); - addMouseMotionListener(headerListener); - } - } - - /** - * Uninstalls header listeners to service the extended functionality. - * This implementation uninstalls a custom mouse input listener. - */ - protected void uninstallHeaderListener() { - if (headerListener != null) { - removeMouseListener(headerListener); - removeMouseMotionListener(headerListener); - headerListener = null; - } - } - - private MouseInputListener headerListener; - - /** - * A MouseListener implementation to support enhanced tableHeader functionality. - * - * Supports column "packing" by double click in resize region. Works around - * core issue #6862170 (must not sort column by click into resize region). - *

                - * - * Note that the logic is critical, mostly because it must be independent of - * sequence of listener notification. So we check whether or not a pressed - * happens in the resizing region in both pressed and released, taking the - * header's resizingColumn property as a marker. The inResize flag can only - * be turned on in those. At the end of the released, we check if we are - * in resize and disable core sorting - which happens in clicked - if appropriate. - * In our clicked we hook the pack action (happens only on double click) - * and reset the resizing region flag always. Pressed (and all other methods) - * restore sorting enablement. - *

                - * - * Supports resort on double click if enabled in the JXTableHeader (Issue #271-swingx). - * - * Is fully effective only if JXTable has control over the row sorter, that is - * if the row sorter is of type SortController. - * - */ - private class HeaderListener implements MouseInputListener, Serializable { - private TableColumn cachedResizingColumn; - private SortOrder[] cachedSortOrderCycle; - private int sortColumn = -1; - - /** - * Packs column on double click in resize region. Resorts - * column on double click if enabled and not in resize region. - */ - @Override - public void mouseClicked(MouseEvent e) { - if (shouldIgnore(e)) { - return; - } - doResize(e); - doDoubleSort(e); - uncacheResizingColumn(); - } - - private void doDoubleSort(MouseEvent e) { - if (!hasCachedSortColumn() || e.getClickCount() % 2 == 1) return; - getXTable().toggleSortOrder(sortColumn); - uncacheSortColumn(); - } - - private boolean hasCachedSortColumn() { - return sortColumn >= 0; - } - - /** - * Resets sort enablement always, set resizing marker if available. - */ - @Override - public void mousePressed(MouseEvent e) { - resetToggleSortOrder(e); - if (shouldIgnore(e)) { - return; - } - cacheResizingColumn(e); - } - - /** - * Sets resizing marker if available, disables table sorting if in - * resize region and sort gesture (aka: single click). - */ - @Override - public void mouseReleased(MouseEvent e) { - if (shouldIgnore(e)) { - return; - } - cacheResizingColumn(e); - cacheSortColumn(e); - if (isInResizeRegion(e) && e.getClickCount() % 2 == 1) { - disableToggleSortOrder(e); - } - } - - private void cacheSortColumn(MouseEvent e) { - if (!canCacheSortColumn(e)) uncacheSortColumn(); - if (e.getClickCount() % 2 == 1) { - int column = columnAtPoint(e.getPoint()); - if (column >= 0) { - int primarySortIndex = getXTable().getSortedColumnIndex(); - if (primarySortIndex == column) { - column = -1; - } - } - sortColumn = column; - } - - } - - private void uncacheSortColumn() { - sortColumn = -1; - } - - private boolean canCacheSortColumn(MouseEvent e) { - if (hasSortController() && !isInResizeRegion(e) && getResortsOnDoubleClick()) { - return true; - } - return false; - } - - /** - * Returns a boolean indication if the mouse event should be ignored. - * Here: returns true if table not enabled or not an event from the left mouse - * button. - * - * @param e - * @return - */ - private boolean shouldIgnore(MouseEvent e) { - return !SwingUtilities.isLeftMouseButton(e) - || !table.isEnabled(); - } - - /** - * Packs caches resizing column on double click, if available. Does nothing - * otherwise. - * - * @param e - */ - private void doResize(MouseEvent e) { - if (e.getClickCount() != 2) - return; - int column = getViewIndexForColumn(cachedResizingColumn); - if (column >= 0) { - (getXTable()).packColumn(column, 5); - } - } - - - /** - * - * @param e - */ - private void disableToggleSortOrder(MouseEvent e) { - if (!hasSortController()) return; - SortController controller = (SortController) getXTable().getRowSorter(); - cachedSortOrderCycle = controller.getSortOrderCycle(); - controller.setSortOrderCycle(); - } - - /** - * @return - */ - private boolean hasSortController() { - return (getXTable().getRowSorter() instanceof SortController); - } - - /** - * - */ - private void resetToggleSortOrder(MouseEvent e) { - if (cachedSortOrderCycle == null) return; - ((SortController) getXTable().getRowSorter()).setSortOrderCycle(cachedSortOrderCycle); - cachedSortOrderCycle = null; - } - - - /** - * Caches the resizing column if set. Does nothing if null. - * - * @param e - */ - private void cacheResizingColumn(MouseEvent e) { - TableColumn column = getResizingColumn(); - if (column != null) { - cachedResizingColumn = column; - } - } - - /** - * Sets the cached resizing column to null. - */ - private void uncacheResizingColumn() { - cachedResizingColumn = null; - } - - /** - * Returns true if the mouseEvent happened in the resizing region. - * - * @param e - * @return - */ - private boolean isInResizeRegion(MouseEvent e) { - return cachedResizingColumn != null; // inResize; - } - - @Override - public void mouseEntered(MouseEvent e) { - } - - /** - * Resets all cached state. - */ - @Override - public void mouseExited(MouseEvent e) { - uncacheSortColumn(); - uncacheResizingColumn(); - resetToggleSortOrder(e); - } - - /** - * Resets all cached state. - */ - @Override - public void mouseDragged(MouseEvent e) { - uncacheSortColumn(); - uncacheResizingColumn(); - resetToggleSortOrder(e); - } - - /** - * Resets all cached state. - */ - @Override - public void mouseMoved(MouseEvent e) { - uncacheSortColumn(); - resetToggleSortOrder(e); - } - } - - - -} diff --git a/src/main/java/org/jdesktop/swingx/JXTaskPane.java b/src/main/java/org/jdesktop/swingx/JXTaskPane.java deleted file mode 100644 index 94b4de71bb..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXTaskPane.java +++ /dev/null @@ -1,625 +0,0 @@ -/* - * $Id: JXTaskPane.java 4260 2012-11-14 20:59:08Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.plaf.LookAndFeelAddons; -import org.jdesktop.swingx.plaf.TaskPaneAddon; -import org.jdesktop.swingx.plaf.TaskPaneUI; - -import javax.swing.*; -import java.awt.*; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; - -/** - * JXTaskPane is a container for tasks and other - * arbitrary components. - * - *

                - * Several JXTaskPanes are usually grouped together within a - * {@link JXTaskPaneContainer}. However it is not mandatory - * to use a JXTaskPaneContainer as the parent for JXTaskPane. The JXTaskPane can - * be added to any other container. See - * {@link JXTaskPaneContainer} to understand the benefits of - * using it as the parent container. - * - *

                - * JXTaskPane provides control to expand and - * collapse the content area in order to show or hide the task list. It can have an - * icon, a title and can be marked as - * special. Marking a JXTaskPane as - * special ({@link #setSpecial(boolean)} is only a hint for - * the pluggable UI which will usually paint it differently (by example by - * using another color for the border of the pane). - * - *

                - * When the JXTaskPane is expanded or collapsed, it will be - * animated with a fade effect. The animated can be disabled on a per - * component basis through {@link #setAnimated(boolean)}. - * - * To disable the animation for all newly created JXTaskPane, - * use the UIManager property: - * UIManager.put("TaskPane.animate", Boolean.FALSE);. - * - *

                - * Example: - *

                - * 
                - * JXFrame frame = new JXFrame();
                - * 
                - * // a container to put all JXTaskPane together
                - * JXTaskPaneContainer taskPaneContainer = new JXTaskPaneContainer();
                - * 
                - * // create a first taskPane with common actions
                - * JXTaskPane actionPane = new JXTaskPane();
                - * actionPane.setTitle("Files and Folders");
                - * actionPane.setSpecial(true);
                - * 
                - * // actions can be added, a hyperlink will be created
                - * Action renameSelectedFile = createRenameFileAction();
                - * actionPane.add(renameSelectedFile);
                - * actionPane.add(createDeleteFileAction());
                - * 
                - * // add this taskPane to the taskPaneContainer
                - * taskPaneContainer.add(actionPane);
                - * 
                - * // create another taskPane, it will show details of the selected file
                - * JXTaskPane details = new JXTaskPane();
                - * details.setTitle("Details");
                - *  
                - * // add standard components to the details taskPane
                - * JLabel searchLabel = new JLabel("Search:");
                - * JTextField searchField = new JTextField("");
                - * details.add(searchLabel);
                - * details.add(searchField);
                - * 
                - * taskPaneContainer.add(details);
                - * 
                - * // put the action list on the left 
                - * frame.add(taskPaneContainer, BorderLayout.EAST);
                - * 
                - * // and a file browser in the middle
                - * frame.add(fileBrowser, BorderLayout.CENTER);
                - * 
                - * frame.pack();
                - * frame.setVisible(true);
                - * 
                - * 
                - * - * @see JXTaskPaneContainer - * @see JXCollapsiblePane - * @author Frederic Lavigne - * @author Karl George Schaefer - * - * @javabean.attribute - * name="isContainer" - * value="Boolean.TRUE" - * rtexpr="true" - * - * @javabean.attribute - * name="containerDelegate" - * value="getContentPane" - * - * @javabean.class - * name="JXTaskPane" - * shortDescription="JXTaskPane is a container for tasks and other arbitrary components." - * stopClass="java.awt.Component" - * - * @javabean.icons - * mono16="JXTaskPane16-mono.gif" - * color16="JXTaskPane16.gif" - * mono32="JXTaskPane32-mono.gif" - * color32="JXTaskPane32.gif" - */ -@JavaBean -@SuppressWarnings("nls") -public class JXTaskPane extends JPanel implements - JXCollapsiblePane.CollapsiblePaneContainer, Mnemonicable { - - /** - * JXTaskPane pluggable UI key swingx/TaskPaneUI - */ - public final static String uiClassID = "swingx/TaskPaneUI"; - - // ensure at least the default ui is registered - static { - LookAndFeelAddons.contribute(new TaskPaneAddon()); - } - - /** - * Used when generating PropertyChangeEvents for the "scrollOnExpand" property - */ - public static final String SCROLL_ON_EXPAND_CHANGED_KEY = "scrollOnExpand"; - - /** - * Used when generating PropertyChangeEvents for the "title" property - */ - public static final String TITLE_CHANGED_KEY = "title"; - - /** - * Used when generating PropertyChangeEvents for the "icon" property - */ - public static final String ICON_CHANGED_KEY = "icon"; - - /** - * Used when generating PropertyChangeEvents for the "special" property - */ - public static final String SPECIAL_CHANGED_KEY = "special"; - - /** - * Used when generating PropertyChangeEvents for the "animated" property - */ - public static final String ANIMATED_CHANGED_KEY = "animated"; - - private String title; - private Icon icon; - private boolean special; - private boolean scrollOnExpand; - - private int mnemonic; - private int mnemonicIndex = -1; - - private JXCollapsiblePane collapsePane; - - /** - * Creates a new empty JXTaskPane. - */ - public JXTaskPane() { - this((String) null); - } - - /** - * Creates a new task pane with the specified title. - * - * @param title - * the title to use - */ - public JXTaskPane(String title) { - this(title, null); - } - - /** - * Creates a new task pane with the specified icon. - * - * @param icon - * the icon to use - */ - public JXTaskPane(Icon icon) { - this(null, icon); - } - - /** - * Creates a new task pane with the specified title and icon. - * - * @param title - * the title to use - * @param icon - * the icon to use - */ - public JXTaskPane(String title, Icon icon) { - collapsePane = new JXCollapsiblePane(); - collapsePane.setOpaque(false); - super.setLayout(new BorderLayout(0, 0)); - super.addImpl(collapsePane, BorderLayout.CENTER, -1); - - setTitle(title); - setIcon(icon); - - updateUI(); - setFocusable(true); - - // disable animation if specified in UIManager - setAnimated(!Boolean.FALSE.equals(UIManager.get("TaskPane.animate"))); - - // listen for animation events and forward them to registered listeners - collapsePane.addPropertyChangeListener("collapsed", new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - JXTaskPane.this.firePropertyChange(evt.getPropertyName(), evt.getOldValue(), - evt.getNewValue()); - } - }); - } - - /** - * Returns the contentPane object for this JXTaskPane. - * @return the contentPane property - */ - public Container getContentPane() { - return collapsePane.getContentPane(); - } - - /** - * Notification from the UIManager that the L&F has changed. - * Replaces the current UI object with the latest version from the UIManager. - * - * @see JComponent#updateUI - */ - @Override - public void updateUI() { - // collapsePane is null when updateUI() is called by the "super()" - // constructor - if (collapsePane == null) { - return; - } - setUI((TaskPaneUI)LookAndFeelAddons.getUI(this, TaskPaneUI.class)); - } - - /** - * Sets the L&F object that renders this component. - * - * @param ui the TaskPaneUI L&F object - * @see javax.swing.UIDefaults#getUI - * - * @beaninfo bound: true hidden: true description: The UI object that - * implements the taskpane group's LookAndFeel. - */ - public void setUI(TaskPaneUI ui) { - super.setUI(ui); - } - - /** - * Returns the name of the L&F class that renders this component. - * - * @return the string {@link #uiClassID} - * @see JComponent#getUIClassID - * @see javax.swing.UIDefaults#getUI - */ - @Override - public String getUIClassID() { - return uiClassID; - } - - /** - * Returns the title currently displayed in the border of this pane. - * - * @return the title currently displayed in the border of this pane - */ - public String getTitle() { - return title; - } - - /** - * Sets the title to be displayed in the border of this pane. - * - * @param title the title to be displayed in the border of this pane - * @javabean.property - * bound="true" - * preferred="true" - */ - public void setTitle(String title) { - String old = this.title; - this.title = title; - firePropertyChange(TITLE_CHANGED_KEY, old, title); - } - - /** - * Returns the icon currently displayed in the border of this pane. - * - * @return the icon currently displayed in the border of this pane - */ - public Icon getIcon() { - return icon; - } - - /** - * Sets the icon to be displayed in the border of this pane. Some pluggable - * UIs may impose size constraints for the icon. A size of 16x16 pixels is - * the recommended icon size. - * - * @param icon the icon to be displayed in the border of this pane - * @javabean.property - * bound="true" - * preferred="true" - */ - public void setIcon(Icon icon) { - Icon old = this.icon; - this.icon = icon; - firePropertyChange(ICON_CHANGED_KEY, old, icon); - } - - /** - * Returns true if this pane is "special". - * - * @return true if this pane is "special" - * @see #setSpecial(boolean) - */ - public boolean isSpecial() { - return special; - } - - /** - * Sets this pane to be "special" or not. Marking a JXTaskPane - * as special is only a hint for the pluggable UI which will - * usually paint it differently (by example by using another color for the - * border of the pane). - * - *

                - * Usually the first JXTaskPane in a JXTaskPaneContainer is marked as special - * because it contains the default set of actions which can be executed given - * the current context. - * - * @param special - * true if this pane is "special", false otherwise - * @javabean.property bound="true" preferred="true" - */ - public void setSpecial(boolean special) { - boolean oldValue = isSpecial(); - this.special = special; - firePropertyChange(SPECIAL_CHANGED_KEY, oldValue, isSpecial()); - } - - /** - * Should this group be scrolled to be visible on expand. - * - * @param scrollOnExpand true to scroll this group to be - * visible if this group is expanded. - * - * @see #setCollapsed(boolean) - * - * @javabean.property - * bound="true" - * preferred="true" - */ - public void setScrollOnExpand(boolean scrollOnExpand) { - boolean oldValue = isScrollOnExpand(); - this.scrollOnExpand = scrollOnExpand; - firePropertyChange(SCROLL_ON_EXPAND_CHANGED_KEY, - oldValue, isScrollOnExpand()); - } - - /** - * Should this group scroll to be visible after - * this group was expanded. - * - * @return true if we should scroll false if nothing - * should be done. - */ - public boolean isScrollOnExpand() { - return scrollOnExpand; - } - - /** - * Expands or collapses this group. - *

                - * As of SwingX 1.6.3, the property change event only fires when the - * state is accurate. As such, animated task panes fire once the - * animation is complete. - * - * @param collapsed - * true to collapse the group, false to expand it - * @javabean.property - * bound="true" - * preferred="false" - */ - public void setCollapsed(boolean collapsed) { - collapsePane.setCollapsed(collapsed); - } - - /** - * Returns the collapsed state of this task pane. - * - * @return {@code true} if the task pane is collapsed; {@code false} - * otherwise - */ - public boolean isCollapsed() { - return collapsePane.isCollapsed(); - } - - /** - * Enables or disables animation during expand/collapse transition. - * - * @param animated - * @javabean.property - * bound="true" - * preferred="true" - */ - public void setAnimated(boolean animated) { - boolean oldValue = isAnimated(); - collapsePane.setAnimated(animated); - firePropertyChange(ANIMATED_CHANGED_KEY, oldValue, isAnimated()); - } - - /** - * Returns true if this task pane is animated during expand/collapse - * transition. - * - * @return true if this task pane is animated during expand/collapse - * transition. - */ - public boolean isAnimated() { - return collapsePane.isAnimated(); - } - - /** - * {@inheritDoc} - *

                - * If the character defined by the mnemonic is found within the task pane's - * text string, the first occurrence of it will be underlined to indicate - * the mnemonic to the user. - */ - @Override - public int getMnemonic() { - return mnemonic; - } - - /** - * {@inheritDoc} - */ - @Override - public void setMnemonic(int mnemonic) { - int oldValue = getMnemonic(); - this.mnemonic = mnemonic; - - firePropertyChange("mnemonic", oldValue, getMnemonic()); - - updateDisplayedMnemonicIndex(getTitle(), mnemonic); - revalidate(); - repaint(); - } - - /** - * Update the displayedMnemonicIndex property. This method - * is called when either text or mnemonic changes. The new - * value of the displayedMnemonicIndex property is the index - * of the first occurrence of mnemonic in text. - */ - private void updateDisplayedMnemonicIndex(String text, int mnemonic) { - if (text == null || mnemonic == '\0') { - mnemonicIndex = -1; - - return; - } - - char uc = Character.toUpperCase((char)mnemonic); - char lc = Character.toLowerCase((char)mnemonic); - - int uci = text.indexOf(uc); - int lci = text.indexOf(lc); - - if (uci == -1) { - mnemonicIndex = lci; - } else if(lci == -1) { - mnemonicIndex = uci; - } else { - mnemonicIndex = (lci < uci) ? lci : uci; - } - } - - /** - * {@inheritDoc} - */ - @Override - public int getDisplayedMnemonicIndex() { - return mnemonicIndex; - } - - /** - * {@inheritDoc} - */ - @Override - public void setDisplayedMnemonicIndex(int index) - throws IllegalArgumentException { - int oldValue = mnemonicIndex; - if (index == -1) { - mnemonicIndex = -1; - } else { - String text = getTitle(); - int textLength = (text == null) ? 0 : text.length(); - if (index < -1 || index >= textLength) { // index out of range - throw new IllegalArgumentException("index == " + index); - } - } - mnemonicIndex = index; - firePropertyChange("displayedMnemonicIndex", oldValue, index); - if (index != oldValue) { - revalidate(); - repaint(); - } - } - - /** - * Adds an action to this JXTaskPane. Returns a - * component built from the action. The returned component has been - * added to the JXTaskPane. - * - * @param action - * @return a component built from the action - */ - public Component add(Action action) { - Component c = ((TaskPaneUI)ui).createAction(action); - add(c); - return c; - } - - /** - * @see JXCollapsiblePane.CollapsiblePaneContainer - */ - @Override -public Container getValidatingContainer() { - return getParent(); - } - - /** - * Overridden to redirect call to the content pane. - */ - @Override - protected void addImpl(Component comp, Object constraints, int index) { - getContentPane().add(comp, constraints, index); - //Fixes SwingX #364; adding to internal component we need to revalidate ourself - revalidate(); - } - - /** - * Overridden to redirect call to the content pane. - */ - @Override - public void setLayout(LayoutManager mgr) { - if (collapsePane != null) { - getContentPane().setLayout(mgr); - } - } - - /** - * Overridden to redirect call to the content pane - */ - @Override - public void remove(Component comp) { - getContentPane().remove(comp); - } - - /** - * Overridden to redirect call to the content pane. - */ - @Override - public void remove(int index) { - getContentPane().remove(index); - } - - /** - * Overridden to redirect call to the content pane. - */ - @Override - public void removeAll() { - getContentPane().removeAll(); - } - - /** - * @see JComponent#paramString() - */ - @Override - protected String paramString() { - return super.paramString() - + ",title=" - + getTitle() - + ",icon=" - + getIcon() - + ",collapsed=" - + String.valueOf(isCollapsed()) - + ",special=" - + String.valueOf(isSpecial()) - + ",scrollOnExpand=" - + String.valueOf(isScrollOnExpand()) - + ",ui=" + getUI(); - } - -} diff --git a/src/main/java/org/jdesktop/swingx/JXTaskPaneContainer.java b/src/main/java/org/jdesktop/swingx/JXTaskPaneContainer.java deleted file mode 100644 index 2f9b017b07..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXTaskPaneContainer.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * $Id: JXTaskPaneContainer.java 4226 2012-08-07 16:09:19Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.plaf.LookAndFeelAddons; -import org.jdesktop.swingx.plaf.TaskPaneContainerAddon; -import org.jdesktop.swingx.plaf.TaskPaneContainerUI; - -import java.awt.event.ContainerAdapter; -import java.awt.event.ContainerEvent; - -/** - * JXTaskPaneContainer provides an elegant view - * to display a list of tasks ordered by groups ({@link JXTaskPane}s). - * - *

                - * Although {@link JXTaskPane} can be added to any other - * container, the JXTaskPaneContainer will provide better - * fidelity when it comes to matching the look and feel of the host operating - * system than any other panel. As example, when using on a Windows platform, - * the JXTaskPaneContainer will be painted with a light gradient - * background. Also JXTaskPaneContainer takes care of using the - * right {@link java.awt.LayoutManager} (as required by - * {@link JXCollapsiblePane}) so that - * {@link JXTaskPane} behaves correctly when collapsing and - * expanding its content. - * - *

                - * JXTaskPaneContainer can be added to a JScrollPane. - * - *

                - * Example: - *

                - * 
                - * JXFrame frame = new JXFrame();
                - * 
                - * // a container to put all JXTaskPane together
                - * JXTaskPaneContainer taskPaneContainer = new JXTaskPaneContainer();
                - * 
                - * // add JXTaskPanes to the container
                - * JXTaskPane actionPane = createActionPane();
                - * JXTaskPane miscActionPane = createMiscActionPane();
                - * JXTaskPane detailsPane = createDetailsPane();
                - * taskPaneContainer.add(actionPane);
                - * taskPaneContainer.add(miscActionPane);
                - * taskPaneContainer.add(detailsPane);
                - *
                - * // put the action list on the left in a JScrollPane
                - * // as we have several taskPane and we want to make sure they
                - * // all get visible.   
                - * frame.add(new JScrollPane(taskPaneContainer), BorderLayout.EAST);
                - * 
                - * // and a file browser in the middle
                - * frame.add(fileBrowser, BorderLayout.CENTER);
                - * 
                - * frame.pack().
                - * frame.setVisible(true);
                - * 
                - * 
                - * - * @author Frederic Lavigne - * - * @javabean.attribute - * name="isContainer" - * value="Boolean.TRUE" - * rtexpr="true" - * - * @javabean.class - * name="JXTaskPaneContainer" - * shortDescription="A component that contains JTaskPaneGroups." - * stopClass="java.awt.Component" - * - * @javabean.icons - * mono16="JXTaskPaneContainer16-mono.gif" - * color16="JXTaskPaneContainer16.gif" - * mono32="JXTaskPaneContainer32-mono.gif" - * color32="JXTaskPaneContainer32.gif" - */ -@JavaBean -public class JXTaskPaneContainer extends JXPanel { - - public final static String uiClassID = "swingx/TaskPaneContainerUI"; - - // ensure at least the default ui is registered - static { - LookAndFeelAddons.contribute(new TaskPaneContainerAddon()); - } - - /** - * Creates a new empty task pane. - */ - public JXTaskPaneContainer() { - super(null); - updateUI(); - - addContainerListener(new ContainerAdapter() { - @Override - public void componentRemoved(ContainerEvent e) { - repaint(); - } - }); - setScrollableHeightHint(ScrollableSizeHint.PREFERRED_STRETCH); - } - - /** - * {@inheritDoc} - */ - @Override - public TaskPaneContainerUI getUI() { - return (TaskPaneContainerUI) super.getUI(); - } - - /** - * Notification from the UIManager that the L&F has changed. - * Replaces the current UI object with the latest version from the - * UIManager. - * - * @see javax.swing.JComponent#updateUI - */ - @Override - public void updateUI() { - setUI((TaskPaneContainerUI) LookAndFeelAddons.getUI(this, - TaskPaneContainerUI.class)); - } - - /** - * Sets the L&F object that renders this component. - * - * @param ui the TaskPaneContainerUI L&F object - * @see javax.swing.UIDefaults#getUI - * - * @beaninfo bound: true hidden: true description: The UI object that - * implements the taskpane's LookAndFeel. - */ - public void setUI(TaskPaneContainerUI ui) { - super.setUI(ui); - } - - /** - * Returns the name of the L&F class that renders this component. - * - * @return the string {@link #uiClassID} - * @see javax.swing.JComponent#getUIClassID - * @see javax.swing.UIDefaults#getUI - */ - @Override - public String getUIClassID() { - return uiClassID; - } -} diff --git a/src/main/java/org/jdesktop/swingx/JXTextArea.java b/src/main/java/org/jdesktop/swingx/JXTextArea.java deleted file mode 100644 index ec697b9749..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXTextArea.java +++ /dev/null @@ -1,108 +0,0 @@ -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.prompt.BuddySupport; -import org.jdesktop.swingx.prompt.PromptSupport; -import org.jdesktop.swingx.prompt.PromptSupport.FocusBehavior; - -import javax.swing.*; -import java.awt.*; - -/** - * {@link JTextArea}, with integrated support for prompts. - * - * @see PromptSupport - * @see BuddySupport - * @author Peter Weishapl - * - */ -@JavaBean -public class JXTextArea extends JTextArea { - public JXTextArea() { - this(null); - } - - public JXTextArea(String promptText) { - this(promptText, null); - } - - public JXTextArea(String promptText, Color promptForeground) { - this(promptText, promptForeground, null); - } - - public JXTextArea(String promptText, Color promptForeground, - Color promptBackground) { - PromptSupport.init(promptText, promptForeground, promptBackground, - this); - } - - /** - * @see PromptSupport#getFocusBehavior(javax.swing.text.JTextComponent) - */ - public FocusBehavior getFocusBehavior() { - return PromptSupport.getFocusBehavior(this); - } - - /** - * @see PromptSupport#getPrompt(javax.swing.text.JTextComponent) - */ - public String getPrompt() { - return PromptSupport.getPrompt(this); - } - - /** - * @see PromptSupport#getForeground(javax.swing.text.JTextComponent) - */ - public Color getPromptForeground() { - return PromptSupport.getForeground(this); - } - - /** - * @see PromptSupport#getForeground(javax.swing.text.JTextComponent) - */ - public Color getPromptBackground() { - return PromptSupport.getBackground(this); - } - - /** - * @see PromptSupport#getFontStyle(javax.swing.text.JTextComponent) - */ - public Integer getPromptFontStyle() { - return PromptSupport.getFontStyle(this); - } - - /** - * @see PromptSupport#getFocusBehavior(javax.swing.text.JTextComponent) - */ - public void setFocusBehavior(FocusBehavior focusBehavior) { - PromptSupport.setFocusBehavior(focusBehavior, this); - } - - /** - * @see PromptSupport#setPrompt(String, javax.swing.text.JTextComponent) - */ - public void setPrompt(String labelText) { - PromptSupport.setPrompt(labelText, this); - } - - /** - * @see PromptSupport#setForeground(Color, javax.swing.text.JTextComponent) - */ - public void setPromptForeground(Color promptTextColor) { - PromptSupport.setForeground(promptTextColor, this); - } - - /** - * @see PromptSupport#setBackground(Color, javax.swing.text.JTextComponent) - */ - public void setPromptBackround(Color promptTextColor) { - PromptSupport.setBackground(promptTextColor, this); - } - - /** - * @see PromptSupport#setFontStyle(Integer, javax.swing.text.JTextComponent) - */ - public void setPromptFontStyle(Integer fontStyle) { - PromptSupport.setFontStyle(fontStyle, this); - } -} diff --git a/src/main/java/org/jdesktop/swingx/JXTextField.java b/src/main/java/org/jdesktop/swingx/JXTextField.java deleted file mode 100644 index 09de52cc10..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXTextField.java +++ /dev/null @@ -1,152 +0,0 @@ -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.prompt.BuddySupport; -import org.jdesktop.swingx.prompt.BuddySupport.Position; -import org.jdesktop.swingx.prompt.PromptSupport; -import org.jdesktop.swingx.prompt.PromptSupport.FocusBehavior; - -import javax.swing.*; -import java.awt.*; -import java.util.List; - -/** - * {@link JTextField}, with integrated support for prompts and buddies. - * - * @see PromptSupport - * @see BuddySupport - * @author Peter Weishapl - * - */ -@JavaBean -public class JXTextField extends JTextField { - public JXTextField() { - this(null); - } - - public JXTextField(String promptText) { - this(promptText, null); - } - - public JXTextField(String promptText, Color promptForeground) { - this(promptText, promptForeground, null); - } - - public JXTextField(String promptText, Color promptForeground, - Color promptBackground) { - PromptSupport.init(promptText, promptForeground, promptBackground, - this); - } - - /** - * @see PromptSupport#getFocusBehavior(javax.swing.text.JTextComponent) - */ - public FocusBehavior getFocusBehavior() { - return PromptSupport.getFocusBehavior(this); - } - - /** - * @see PromptSupport#getPrompt(javax.swing.text.JTextComponent) - */ - public String getPrompt() { - return PromptSupport.getPrompt(this); - } - - /** - * @see PromptSupport#getForeground(javax.swing.text.JTextComponent) - */ - public Color getPromptForeground() { - return PromptSupport.getForeground(this); - } - - /** - * @see PromptSupport#getForeground(javax.swing.text.JTextComponent) - */ - public Color getPromptBackground() { - return PromptSupport.getBackground(this); - } - - /** - * @see PromptSupport#getFontStyle(javax.swing.text.JTextComponent) - */ - public Integer getPromptFontStyle() { - return PromptSupport.getFontStyle(this); - } - - /** - * @see PromptSupport#getFocusBehavior(javax.swing.text.JTextComponent) - */ - public void setFocusBehavior(FocusBehavior focusBehavior) { - PromptSupport.setFocusBehavior(focusBehavior, this); - } - - /** - * @see PromptSupport#setPrompt(String, javax.swing.text.JTextComponent) - */ - public void setPrompt(String labelText) { - PromptSupport.setPrompt(labelText, this); - } - - /** - * @see PromptSupport#setForeground(Color, javax.swing.text.JTextComponent) - */ - public void setPromptForeground(Color promptTextColor) { - PromptSupport.setForeground(promptTextColor, this); - } - - /** - * @see PromptSupport#setBackground(Color, javax.swing.text.JTextComponent) - */ - public void setPromptBackround(Color promptTextColor) { - PromptSupport.setBackground(promptTextColor, this); - } - - /** - * @see PromptSupport#setFontStyle(Integer, javax.swing.text.JTextComponent) - */ - public void setPromptFontStyle(Integer fontStyle) { - PromptSupport.setFontStyle(fontStyle, this); - } - - /** - * @see BuddySupport#setOuterMargin(JTextField, Insets) - */ - public void setOuterMargin(Insets margin) { - BuddySupport.setOuterMargin(this, margin); - } - - /** - * @see BuddySupport#getOuterMargin(JTextField) - */ - public Insets getOuterMargin() { - return BuddySupport.getOuterMargin(this); - } - - /** - * @see BuddySupport#add(Component, Position, JTextField) - */ - public void addBuddy(Component buddy, Position pos) { - BuddySupport.add(buddy, pos, this); - } - - /** - * @see BuddySupport#addGap(int, Position, JTextField) - */ - public void addGap(int width, Position pos) { - BuddySupport.addGap(width, pos, this); - } - - /** - * @see BuddySupport#getBuddies(Position, JTextField) - */ - public List getBuddies(Position pos) { - return BuddySupport.getBuddies(pos, this); - } - - /** - * @see BuddySupport#removeAll(JTextField) - */ - public void removeAllBuddies() { - BuddySupport.removeAll(this); - } -} diff --git a/src/main/java/org/jdesktop/swingx/JXTipOfTheDay.java b/src/main/java/org/jdesktop/swingx/JXTipOfTheDay.java deleted file mode 100644 index 4d4ede31de..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXTipOfTheDay.java +++ /dev/null @@ -1,460 +0,0 @@ -/* - * $Id: JXTipOfTheDay.java 4147 2012-02-01 17:13:24Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.plaf.LookAndFeelAddons; -import org.jdesktop.swingx.plaf.TipOfTheDayAddon; -import org.jdesktop.swingx.plaf.TipOfTheDayUI; -import org.jdesktop.swingx.tips.DefaultTipOfTheDayModel; -import org.jdesktop.swingx.tips.TipOfTheDayModel; -import org.jdesktop.swingx.tips.TipOfTheDayModel.Tip; - -import javax.swing.*; -import java.awt.*; -import java.util.prefs.Preferences; - -/** - * Provides the "Tip of The Day" pane and dialog.
                - * - *

                - * Tips are retrieved from the {@link TipOfTheDayModel}. - * In the most common usage, a tip (as returned by - * {@link Tip#getTip()}) is just a - * String. However, the return type of this method is actually - * Object. Its interpretation depends on its type: - *

                - *
                Component - *
                The Component is displayed in the dialog. - *
                Icon - *
                The Icon is wrapped in a JLabel and - * displayed in the dialog. - *
                others - *
                The object is converted to a String by calling its - * toString method. The result is wrapped in a - * JEditorPane or JTextArea and displayed. - *
                - * - *

                - * JXTipOfTheDay finds its tips in its {@link TipOfTheDayModel}. - * Such model can be programmatically built using {@link DefaultTipOfTheDayModel} - * and {@link org.jdesktop.swingx.tips.DefaultTip} but - * the {@link org.jdesktop.swingx.tips.TipLoader} provides a convenient method to - * build a model and its tips from a {@link java.util.Properties} object. - * - *

                - * Example: - *

                - * Let's consider a file tips.properties with the following content: - *

                - * 
                - * tip.1.description=This is the first time! Plain text.
                - * tip.2.description=<html>This is <b>another tip</b>, it uses HTML!
                - * tip.3.description=A third one
                - * 
                - * 
                - * - * To load and display the tips: - * - *
                - * 
                - * Properties tips = new Properties();
                - * tips.load(new FileInputStream("tips.properties"));
                - * 
                - * TipOfTheDayModel model = TipLoader.load(tips);
                - * JXTipOfTheDay totd = new JXTipOfTheDay(model);
                - * 
                - * totd.showDialog(someParentComponent);
                - * 
                - * 
                - * - *

                - * Additionally, JXTipOfTheDay features an option enabling the end-user - * to choose to not display the "Tip Of The Day" dialog. This user choice can be stored - * in the user {@link Preferences} but JXTipOfTheDay also - * supports custom storage through the {@link ShowOnStartupChoice} interface. - * - *

                - * 
                - * Preferences userPreferences = Preferences.userRoot().node("myApp");
                - * totd.showDialog(someParentComponent, userPreferences);
                - * 
                - * 
                - * In this code, the first time showDialog is called, the dialog will be made - * visible and the user will have the choice to not display it again in the future - * (usually this is controlled by a checkbox "Show tips on startup"). If the user - * unchecks the option, subsequent calls to showDialog will not display the dialog. - * As the choice is saved in the user Preferences, it will persist when the application is relaunched. - * - * @see org.jdesktop.swingx.tips.TipLoader - * @see TipOfTheDayModel - * @see Tip - * @see #showDialog(Component, Preferences) - * @see #showDialog(Component, ShowOnStartupChoice) - * - * @author Frederic Lavigne - */ -@JavaBean -public class JXTipOfTheDay extends JXPanel { - - /** - * JXTipOfTheDay pluggable UI key swingx/TipOfTheDayUI - */ - public final static String uiClassID = "swingx/TipOfTheDayUI"; - - // ensure at least the default ui is registered - static { - LookAndFeelAddons.contribute(new TipOfTheDayAddon()); - } - - /** - * Key used to store the status of the "Show tip on startup" checkbox" - */ - public static final String PREFERENCE_KEY = "ShowTipOnStartup"; - - /** - * Used when generating PropertyChangeEvents for the "currentTip" property - */ - public static final String CURRENT_TIP_CHANGED_KEY = "currentTip"; - - private TipOfTheDayModel model; - private int currentTip = 0; - - /** - * Constructs a new JXTipOfTheDay with an empty - * TipOfTheDayModel - */ - public JXTipOfTheDay() { - this(new DefaultTipOfTheDayModel(new Tip[0])); - } - - /** - * Constructs a new JXTipOfTheDay showing tips from the given - * TipOfTheDayModel. - * - * @param model - */ - public JXTipOfTheDay(TipOfTheDayModel model) { - this.model = model; - updateUI(); - } - - /** - * Notification from the UIManager that the L&F has changed. - * Replaces the current UI object with the latest version from the - * UIManager. - * - * @see javax.swing.JComponent#updateUI - */ - @Override -public void updateUI() { - setUI((TipOfTheDayUI)LookAndFeelAddons.getUI(this, TipOfTheDayUI.class)); - } - - /** - * Sets the L&F object that renders this component. - * - * @param ui - * the TipOfTheDayUI L&F object - * @see javax.swing.UIDefaults#getUI - * - * @beaninfo bound: true hidden: true description: The UI object that - * implements the taskpane group's LookAndFeel. - */ - public void setUI(TipOfTheDayUI ui) { - super.setUI(ui); - } - - /** - * Gets the UI object which implements the L&F for this component. - * - * @return the TipOfTheDayUI object that implements the TipOfTheDayUI L&F - */ - @Override - public TipOfTheDayUI getUI() { - return (TipOfTheDayUI)ui; - } - - /** - * Returns the name of the L&F class that renders this component. - * - * @return the string {@link #uiClassID} - * @see javax.swing.JComponent#getUIClassID - * @see javax.swing.UIDefaults#getUI - */ - @Override - public String getUIClassID() { - return uiClassID; - } - - public TipOfTheDayModel getModel() { - return model; - } - - public void setModel(TipOfTheDayModel model) { - if (model == null) { - throw new IllegalArgumentException("model can not be null"); - } - TipOfTheDayModel old = this.model; - this.model = model; - firePropertyChange("model", old, model); - } - - public int getCurrentTip() { - return currentTip; - } - - /** - * Sets the index of the tip to show - * - * @param currentTip - * @throws IllegalArgumentException if currentTip is not within the bounds [0, - * getModel().getTipCount()[. - */ - public void setCurrentTip(int currentTip) { - if (currentTip < 0 || currentTip >= getModel().getTipCount()) { - throw new IllegalArgumentException( - "Current tip must be within the bounds [0, " + getModel().getTipCount() - + "]"); - } - - int oldTip = this.currentTip; - this.currentTip = currentTip; - firePropertyChange(CURRENT_TIP_CHANGED_KEY, oldTip, currentTip); - } - - /** - * Shows the next tip in the list. It cycles the tip list. - */ - public void nextTip() { - int count = getModel().getTipCount(); - if (count == 0) { return; } - - int nextTip = currentTip + 1; - if (nextTip >= count) { - nextTip = 0; - } - setCurrentTip(nextTip); - } - - /** - * Shows the previous tip in the list. It cycles the tip list. - */ - public void previousTip() { - int count = getModel().getTipCount(); - if (count == 0) { return; } - - int previousTip = currentTip - 1; - if (previousTip < 0) { - previousTip = count - 1; - } - setCurrentTip(previousTip); - } - - /** - * Pops up a "Tip of the day" dialog. - * - * @param parentComponent - * @exception HeadlessException - * if GraphicsEnvironment.isHeadless() returns true. - * @see java.awt.GraphicsEnvironment#isHeadless - */ - public void showDialog(Component parentComponent) throws HeadlessException { - showDialog(parentComponent, (ShowOnStartupChoice)null); - } - - /** - * Pops up a "Tip of the day" dialog. Additionally, it saves the state of the - * "Show tips on startup" checkbox in a key named "ShowTipOnStartup" in the - * given Preferences. - * - * @param parentComponent - * @param showOnStartupPref - * @exception HeadlessException - * if GraphicsEnvironment.isHeadless() returns true. - * @throws IllegalArgumentException - * if showOnStartupPref is null - * @see java.awt.GraphicsEnvironment#isHeadless - * @return true if the user chooses to see the tips again, false otherwise. - */ - public boolean showDialog(Component parentComponent, - Preferences showOnStartupPref) throws HeadlessException { - return showDialog(parentComponent, showOnStartupPref, false); - } - - /** - * Pops up a "Tip of the day" dialog. Additionally, it saves the state of the - * "Show tips on startup" checkbox in a key named "ShowTipOnStartup" in the - * given Preferences. - * - * @param parentComponent - * @param showOnStartupPref - * @param force - * if true, the dialog is displayed even if the Preferences is set to - * hide the dialog - * @exception HeadlessException - * if GraphicsEnvironment.isHeadless() returns true. - * @throws IllegalArgumentException - * if showOnStartupPref is null - * @see java.awt.GraphicsEnvironment#isHeadless - * @return true if the user chooses to see the tips again, false - * otherwise. - */ - public boolean showDialog(Component parentComponent, - final Preferences showOnStartupPref, boolean force) throws HeadlessException { - if (showOnStartupPref == null) { throw new IllegalArgumentException( - "Preferences can not be null"); } - - ShowOnStartupChoice store = new ShowOnStartupChoice() { - @Override - public boolean isShowingOnStartup() { - return showOnStartupPref.getBoolean(PREFERENCE_KEY, true); - } - @Override - public void setShowingOnStartup(boolean showOnStartup) { - if (showOnStartup && !showOnStartupPref.getBoolean(PREFERENCE_KEY, true)) { - // if the choice was previously not enable and now we re-enable it, we - // must remove the key - showOnStartupPref.remove(PREFERENCE_KEY); - } else if (!showOnStartup) { - // user does not want to see the tip - showOnStartupPref.putBoolean(PREFERENCE_KEY, showOnStartup); - } - } - }; - return showDialog(parentComponent, store, force); - } - - /** - * Pops up a "Tip of the day" dialog. - * - * If choice is not null, the method first checks if - * {@link ShowOnStartupChoice#isShowingOnStartup()} is true before showing the - * dialog. - * - * Additionally, it saves the state of the "Show tips on startup" checkbox - * using the given {@link ShowOnStartupChoice} object. - * - * @param parentComponent - * @param choice - * @exception HeadlessException - * if GraphicsEnvironment.isHeadless() returns true. - * @see java.awt.GraphicsEnvironment#isHeadless - * @return true if the user chooses to see the tips again, false otherwise. - */ - public boolean showDialog(Component parentComponent, - ShowOnStartupChoice choice) { - return showDialog(parentComponent, choice, false); - } - - /** - * Pops up a "Tip of the day" dialog. - * - * If choice is not null, the method first checks if - * force is true or if - * {@link ShowOnStartupChoice#isShowingOnStartup()} is true before showing the - * dialog. - * - * Additionally, it saves the state of the "Show tips on startup" checkbox - * using the given {@link ShowOnStartupChoice} object. - * - * @param parentComponent - * @param choice - * @param force - * if true, the dialog is displayed even if - * {@link ShowOnStartupChoice#isShowingOnStartup()} is false - * @exception HeadlessException - * if GraphicsEnvironment.isHeadless() returns true. - * @see java.awt.GraphicsEnvironment#isHeadless - * @return true if the user chooses to see the tips again, false otherwise. - */ - public boolean showDialog(Component parentComponent, - ShowOnStartupChoice choice, boolean force) { - if (choice == null) { - JDialog dialog = createDialog(parentComponent, choice); - dialog.setVisible(true); - dialog.dispose(); - return true; - } else if (force || choice.isShowingOnStartup()) { - JDialog dialog = createDialog(parentComponent, choice); - dialog.setVisible(true); - dialog.dispose(); - return choice.isShowingOnStartup(); - } else { - return false; - } - } - - /** - * @param showOnStartupPref - * @return true if the key named "ShowTipOnStartup" is not set to false - */ - public static boolean isShowingOnStartup(Preferences showOnStartupPref) { - return showOnStartupPref.getBoolean(PREFERENCE_KEY, true); - } - - /** - * Removes the value set for "ShowTipOnStartup" in the given Preferences to - * ensure the dialog shown by a later call to - * {@link #showDialog(Component, Preferences)} will be visible to the user. - * - * @param showOnStartupPref - */ - public static void forceShowOnStartup(Preferences showOnStartupPref) { - showOnStartupPref.remove(PREFERENCE_KEY); - } - - /** - * Calls - * {@link TipOfTheDayUI#createDialog(Component, ShowOnStartupChoice)}. - * - * This method can be overriden in order to control things such as the - * placement of the dialog or its title. - * - * @param parentComponent - * @param choice - * @return a JDialog to show this TipOfTheDay pane - */ - protected JDialog createDialog(Component parentComponent, - ShowOnStartupChoice choice) { - return getUI().createDialog(parentComponent, choice); - } - - /** - * Used in conjunction with the - * {@link JXTipOfTheDay#showDialog(Component, ShowOnStartupChoice)} to save the - * "Show tips on startup" choice. - */ - public static interface ShowOnStartupChoice { - - /** - * Persists the user choice - * @param showOnStartup the user choice - */ - void setShowingOnStartup(boolean showOnStartup); - - /** - * @return the previously stored user choice - */ - boolean isShowingOnStartup(); - } - -} diff --git a/src/main/java/org/jdesktop/swingx/JXTitledPanel.java b/src/main/java/org/jdesktop/swingx/JXTitledPanel.java deleted file mode 100644 index 05f850c62f..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXTitledPanel.java +++ /dev/null @@ -1,301 +0,0 @@ -/* - * $Id: JXTitledPanel.java 4260 2012-11-14 20:59:08Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.painter.Painter; -import org.jdesktop.swingx.plaf.LookAndFeelAddons; -import org.jdesktop.swingx.plaf.TitledPanelAddon; -import org.jdesktop.swingx.plaf.TitledPanelUI; - -import javax.swing.*; -import java.awt.*; - -/** - * A special type of Panel that has a Title section and a Content section.
                - * The following properties can be set with the UIManager to change the look - * and feel of the JXTitledPanel: - *
                  - *
                • JXTitledPanel.titleForeground
                • - *
                • JXTitledPanel.titleBackground
                • - *
                • JXTitledPanel.titleFont
                • - *
                • JXTitledPanel.titlePainter
                • - *
                • JXTitledPanel.captionInsets
                • - *
                • JXTitledPanel.rightDecorationInsets
                • - *
                • JXTitledPanel.leftDecorationInsets
                • - *
                - * - * @author Richard Bair - * @author Nicola Ken Barozzi - * @author Jeanette Winzenburg - */ -@JavaBean -public class JXTitledPanel extends JXPanel { - - /** - * @see #getUIClassID // * - * @see #readObject - */ - static public final String uiClassID = "TitledPanelUI"; - - public static final String LEFT_DECORATION = "JXTitledPanel.leftDecoration"; - - public static final String RIGHT_DECORATION = "JXTitledPanel.rightDecoration"; - - /** - * Initialization that would ideally be moved into various look and feel - * classes. - */ - static { - LookAndFeelAddons.contribute(new TitledPanelAddon()); - } - - /** - * The text to use for the title - */ - private String title; - - /** - * The Font to use for the Title - */ - private Font titleFont; - - /** - * The foreground color to use for the Title (particularly for the text) - */ - private Color titleForeground; - - /** - * The ContentPanel. Whatever this container is will be displayed in the - * Content section - */ - private Container contentPanel; - - /** - * The Painter to use for painting the title section of the JXTitledPanel - */ - private Painter titlePainter; - - /** - * Create a new JTitledPanel with an empty string for the title. - */ - public JXTitledPanel() { - this(null); - } - - /** - * Create a new JTitledPanel with the given title as the title for the - * panel. - * - * @param title - */ - public JXTitledPanel(String title) { - this(title, createDefaultContainer()); - } - - /** - * Create a new JTitledPanel with the given String as the title, and the - * given Container as the content panel. - * - * @param title - * @param content - */ - public JXTitledPanel(String title, Container content) { - setTitle(title); - setContentContainer(content); - } - - /** - * Returns the look and feel (L&F) object that renders this component. - * - * @return the TitledPanelUI object that renders this component - */ - @Override - public TitledPanelUI getUI() { - return (TitledPanelUI) ui; - } - - /** - * Sets the look and feel (L&F) object that renders this component. - * - * @param ui - * the TitledPanelUI L&F object - * @see javax.swing.UIDefaults#getUI - * @beaninfo bound: true - * hidden: true attribute: visualUpdate true - * description: The UI object that implements the Component's LookAndFeel. - */ - public void setUI(TitledPanelUI ui) { - super.setUI(ui); - } - - /** - * Returns a string that specifies the name of the L&F class that renders - * this component. - * - * @return "TitledPanelUI" - * @see JComponent#getUIClassID - * @see javax.swing.UIDefaults#getUI - * @beaninfo expert: true - * description: A string that specifies the name of the L&F class. - */ - @Override - public String getUIClassID() { - return uiClassID; - } - - /** - * Notification from the UIManager that the L&F has changed. - * Replaces the current UI object with the latest version from the - * UIManager. - * - * @see JComponent#updateUI - */ - @Override - public void updateUI() { - setUI((TitledPanelUI) LookAndFeelAddons - .getUI(this, TitledPanelUI.class)); - } - - /** - * Gets the title for this titled panel. - * - * @return the currently displayed title - */ - public String getTitle() { - return title; - } - - /** - * Sets the title for this title panel. - * - * @param title - * the title to display - */ - public void setTitle(String title) { - String oldTitle = this.title; - this.title = (title == null ? "" : title); - // JW: fix swingx #9 - missing/incorrect notification - // let standard notification handle - // NOTE - "getting" the new property in the fire method is - // intentional: there's no way of missing any transformations - // on the parameter to set (like above: setting a - // value depending on whether the input is null). - firePropertyChange("title", oldTitle, getTitle()); - } - - public Container getContentContainer() { - if (contentPanel == null) { - contentPanel = new JXPanel(); - ((JXPanel) contentPanel).setBorder(BorderFactory - .createEmptyBorder()); - this.add(contentPanel, BorderLayout.CENTER); - } - return contentPanel; - } - - public void setContentContainer(Container contentPanel) { - if (this.contentPanel != null) { - remove(this.contentPanel); - } - add(contentPanel, BorderLayout.CENTER); - this.contentPanel = contentPanel; - } - - /** - * Adds the given JComponent as a decoration on the right of the title - * - * @param decoration - */ - public void setRightDecoration(JComponent decoration) { - JComponent old = getRightDecoration(); - getUI().setRightDecoration(decoration); - firePropertyChange("rightDecoration", old, getRightDecoration()); - } - - public JComponent getRightDecoration() { - return getUI().getRightDecoration(); - } - - /** - * Adds the given JComponent as a decoration on the left of the title - * - * @param decoration - */ - public void setLeftDecoration(JComponent decoration) { - JComponent old = getLeftDecoration(); - getUI().setLeftDecoration(decoration); - firePropertyChange("leftDecoration", old, getLeftDecoration()); - } - - public JComponent getLeftDecoration() { - return getUI().getLeftDecoration(); - } - - public Font getTitleFont() { - return titleFont; - } - - public void setTitleFont(Font titleFont) { - Font old = getTitleFont(); - this.titleFont = titleFont; - firePropertyChange("titleFont", old, getTitleFont()); - } - - /** - * Set the Painter to use for painting the title section of the JXTitledPanel. - * This value may be null, which will cause the current look and feel to paint - * an appropriate look - * - * @param p The Painter to use. May be null - */ - public void setTitlePainter(Painter p) { - Painter old = getTitlePainter(); - this.titlePainter = p; - firePropertyChange("titlePainter", old, getTitlePainter()); - } - - /** - * @return the Painter to use for painting the background of the title section - */ - public Painter getTitlePainter() { - return titlePainter; - } - - public Color getTitleForeground() { - return titleForeground; - } - - public void setTitleForeground(Color titleForeground) { - Color old = getTitleForeground(); - this.titleForeground = titleForeground; - firePropertyChange("titleForeground", old, getTitleForeground()); - } - - private static Container createDefaultContainer() { - //TODO: All this default container creation stuff should be in the UI - //delegate. Not enough time at the moment for me to do this right. - JXPanel p = new JXPanel(); - return p; - } - -} diff --git a/src/main/java/org/jdesktop/swingx/JXTitledSeparator.java b/src/main/java/org/jdesktop/swingx/JXTitledSeparator.java deleted file mode 100644 index 987c85522c..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXTitledSeparator.java +++ /dev/null @@ -1,410 +0,0 @@ -/* - * $Id: JXTitledSeparator.java 4147 2012-02-01 17:13:24Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; - -import javax.swing.*; -import javax.swing.plaf.ColorUIResource; -import javax.swing.plaf.FontUIResource; -import java.awt.*; - -/** - *

                A simple horizontal separator that contains a title.
                - * - *

                JXTitledSeparator allows you to specify the title via the {@link #setTitle} method. - * The title alignment may be specified by using the {@link #setHorizontalAlignment} - * method, and accepts all the same arguments as the {@link JLabel#setHorizontalAlignment} - * method.

                - * - *

                In addition, you may specify an Icon to use with this separator. The icon - * will appear "leading" the title (on the left in left-to-right languages, - * on the right in right-to-left languages). To change the position of the - * title with respect to the icon, call {@link #setHorizontalTextPosition}.

                - * - *

                The default font and color of the title comes from the LookAndFeel, mimicking - * the font and color of the {@link javax.swing.border.TitledBorder}

                - * - *

                Here are a few example code snippets: - *

                
                - *  //create a plain separator
                - *  JXTitledSeparator sep = new JXTitledSeparator();
                - *  sep.setTitle("Customer Info");
                - *
                - *  //create a separator with an icon
                - *  sep = new JXTitledSeparator();
                - *  sep.setTitle("Customer Info");
                - *  sep.setIcon(new ImageIcon("myimage.png"));
                - *
                - *  //create a separator with an icon to the right of the title,
                - *  //center justified
                - *  sep = new JXTitledSeparator();
                - *  sep.setTitle("Customer Info");
                - *  sep.setIcon(new ImageIcon("myimage.png"));
                - *  sep.setHorizontalAlignment(SwingConstants.CENTER);
                - *  sep.setHorizontalTextPosition(SwingConstants.TRAILING);
                - * 
                - * - * @status REVIEWED - * @author rbair - */ -@JavaBean -public class JXTitledSeparator extends JXPanel { - /** - * Implementation detail: the label used to display the title - */ - private JLabel label; - /** - * Implementation detail: a separator to use on the left of the - * title if alignment is centered or right justified - */ - private JSeparator leftSeparator; - /** - * Implementation detail: a separator to use on the right of the - * title if alignment is centered or left justified - */ - private JSeparator rightSeparator; - - private int iconTextGap = 5; - - /** - * Creates a new instance of JXTitledSeparator. The default title is simply - * an empty string. Default justification is LEADING, and the default - * horizontal text position is TRAILING (title follows icon) - */ - public JXTitledSeparator() { - this("Untitled"); - } - - /** - * Creates a new instance of JXTitledSeparator with the specified - * title. Default horizontal alignment is LEADING, and the default - * horizontal text position is TRAILING (title follows icon) - */ - public JXTitledSeparator(String title) { - this(title, SwingConstants.LEADING, null); - } - - /** - * Creates a new instance of JXTitledSeparator with the specified - * title and horizontal alignment. The default - * horizontal text position is TRAILING (title follows icon) - */ - public JXTitledSeparator(String title, int horizontalAlignment) { - this(title, horizontalAlignment, null); - } - - /** - * Creates a new instance of JXTitledSeparator with the specified - * title, icon, and horizontal alignment. The default - * horizontal text position is TRAILING (title follows icon) - */ - public JXTitledSeparator(String title, int horizontalAlignment, Icon icon) { - setLayout(new GridBagLayout()); - - label = new JLabel(title) { - @Override - public void updateUI(){ - super.updateUI(); - updateTitle(); - } - }; - label.setIcon(icon); - label.setHorizontalAlignment(horizontalAlignment); - leftSeparator = new JSeparator(); - rightSeparator = new JSeparator(); - - layoutSeparator(); - - updateTitle(); - setOpaque(false); - } - - /** - * Implementation detail. Handles updates of title color and font on LAF change. For more - * details see swingx#451. - */ - //TODO remove this method in favor of UI delegate -- kgs - protected void updateTitle() - { - if (label == null) return; - - Color c = label.getForeground(); - if (c == null || c instanceof ColorUIResource) - setForeground(UIManager.getColor("TitledBorder.titleColor")); - - Font f = label.getFont(); - if (f == null || f instanceof FontUIResource) - setFont(UIManager.getFont("TitledBorder.font")); - } - - /** - * Implementation detail. lays out this component, showing/hiding components - * as necessary. Actually changes the containment (removes and adds components). - * JXTitledSeparator is treated as a single component rather than - * a container. - */ - private void layoutSeparator() { - removeAll(); - - //SwingX #304 fix alignment issues - //this is really a hacky fix, but a fix nonetheless - //we need a better layout approach for this class - int alignment = getHorizontalAlignment(); - - if (!getComponentOrientation().isLeftToRight()) { - switch (alignment) { - case SwingConstants.LEFT: - alignment = SwingConstants.RIGHT; - break; - case SwingConstants.RIGHT: - alignment = SwingConstants.LEFT; - break; - case SwingConstants.EAST: - alignment = SwingConstants.WEST; - break; - case SwingConstants.WEST: - alignment = SwingConstants.EAST; - break; - default: - break; - } - } - - switch (alignment) { - case SwingConstants.LEFT: - case SwingConstants.LEADING: - case SwingConstants.WEST: - add(label, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0,0,0,0), 0, 0)); - add(Box.createHorizontalStrut(iconTextGap), new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0,0,0,0), 0, 0)); - add(rightSeparator, new GridBagConstraints(2, 0, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0,0,0,0), 0, 0)); - break; - case SwingConstants.RIGHT: - case SwingConstants.TRAILING: - case SwingConstants.EAST: - add(rightSeparator, new GridBagConstraints(0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0,0,0,0), 0, 0)); - add(Box.createHorizontalStrut(iconTextGap), new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0,0,0,0), 0, 0)); - add(label, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0,0,0,0), 0, 0)); - break; - case SwingConstants.CENTER: - default: - add(leftSeparator, new GridBagConstraints(0, 0, 1, 1, 0.5, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0,0,0,0), 0, 0)); - add(Box.createHorizontalStrut(iconTextGap), new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0,0,0,0), 0, 0)); - add(label, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0,0,0,0), 0, 0)); - add(Box.createHorizontalStrut(iconTextGap), new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0,0,0,0), 0, 0)); - add(rightSeparator, new GridBagConstraints(4, 0, 1, 1, 0.5, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0,0,0,0), 0, 0)); - } - } - - /** - * Sets the title for the separator. This may be simple html, or plain - * text. - * - * @param title the new title. Any string input is acceptable - */ - public void setTitle(String title) { - String old = getTitle(); - label.setText(title); - firePropertyChange("title", old, getTitle()); - } - - /** - * Gets the title. - * - * @return the title being used for this JXTitledSeparator. - * This will be the raw title text, and so may include html tags etc - * if they were so specified in #setTitle. - */ - public String getTitle() { - return label.getText(); - } - - /** - *

                Sets the alignment of the title along the X axis. If leading, then - * the title will lead the separator (in left-to-right languages, - * the title will be to the left and the separator to the right). If centered, - * then a separator will be to the left, followed by the title (centered), - * followed by a separator to the right. Trailing will have the title - * on the right with a separator to its left, in left-to-right languages.

                - * - *

                LEFT and RIGHT always position the text left or right of the separator, - * respectively, regardless of the language orientation.

                - * - * @param alignment One of the following constants - * defined in SwingConstants: - * LEFT, - * CENTER, - * RIGHT, - * LEADING (the default) or - * TRAILING. - * - * @throws IllegalArgumentException if the alignment does not match one of - * the accepted inputs. - * @see SwingConstants - * @see #getHorizontalAlignment - */ - public void setHorizontalAlignment(int alignment) { - int old = getHorizontalAlignment(); - label.setHorizontalAlignment(alignment); - if (old != getHorizontalAlignment()) { - layoutSeparator(); - } - firePropertyChange("horizontalAlignment", old, getHorizontalAlignment()); - } - - /** - * Returns the alignment of the title contents along the X axis. - * - * @return The value of the horizontalAlignment property, one of the - * following constants defined in SwingConstants: - * LEFT, - * CENTER, - * RIGHT, - * LEADING or - * TRAILING. - * - * @see #setHorizontalAlignment - * @see SwingConstants - */ - public int getHorizontalAlignment() { - return label.getHorizontalAlignment(); - } - - /** - * Sets the horizontal position of the title's text, - * relative to the icon. - * - * @param position One of the following constants - * defined in SwingConstants: - * LEFT, - * CENTER, - * RIGHT, - * LEADING, or - * TRAILING (the default). - * @throws IllegalArgumentException if the position does not match one of - * the accepted inputs. - */ - public void setHorizontalTextPosition(int position) { - int old = getHorizontalTextPosition(); - label.setHorizontalTextPosition(position); - firePropertyChange("horizontalTextPosition", old, getHorizontalTextPosition()); - } - - /** - * Returns the horizontal position of the title's text, - * relative to the icon. - * - * @return One of the following constants - * defined in SwingConstants: - * LEFT, - * CENTER, - * RIGHT, - * LEADING or - * TRAILING. - * - * @see SwingConstants - */ - public int getHorizontalTextPosition() { - return label.getHorizontalTextPosition(); - } - - /** - * {@inheritDoc} - */ - @Override - public ComponentOrientation getComponentOrientation() { - return label.getComponentOrientation(); - } - - /** - * {@inheritDoc} - */ - @Override - public void setComponentOrientation(ComponentOrientation o) { - ComponentOrientation old = label.getComponentOrientation(); - label.setComponentOrientation(o); - firePropertyChange("componentOrientation", old, label.getComponentOrientation()); - } - - /** - * Defines the icon this component will display. If - * the value of icon is null, nothing is displayed. - *

                - * The default value of this property is null. - * - * @see #setHorizontalTextPosition - * @see #getIcon - */ - public void setIcon(Icon icon) { - Icon old = getIcon(); - label.setIcon(icon); - firePropertyChange("icon", old, getIcon()); - } - - /** - * Returns the graphic image (glyph, icon) that the - * JXTitledSeparator displays. - * - * @return an Icon - * @see #setIcon - */ - public Icon getIcon() { - return label.getIcon(); - } - - /** - * {@inheritDoc} - */ - @Override - public void setForeground(Color foreground) { - if (label != null) { - label.setForeground(foreground); - } - super.setForeground(foreground); - } - - /** - * {@inheritDoc} - */ - @Override - public void setFont(Font font) { - if (label != null) { - label.setFont(font); - } - super.setFont(font); - } - - public void setIconTextGap(int iconTextGap) { - int oldValue = this.iconTextGap; - this.iconTextGap = iconTextGap; - - if (iconTextGap != oldValue) { - layoutSeparator(); - revalidate(); - repaint(); - } - } - - public int getIconTextGap() { - return iconTextGap; - } -} diff --git a/src/main/java/org/jdesktop/swingx/JXTree.java b/src/main/java/org/jdesktop/swingx/JXTree.java deleted file mode 100644 index ee7a4c956e..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXTree.java +++ /dev/null @@ -1,1651 +0,0 @@ -/* - * $Id: JXTree.java 4166 2012-02-15 15:21:04Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.decorator.ComponentAdapter; -import org.jdesktop.swingx.decorator.CompoundHighlighter; -import org.jdesktop.swingx.decorator.Highlighter; -import org.jdesktop.swingx.plaf.UIAction; -import org.jdesktop.swingx.plaf.UIDependent; -import org.jdesktop.swingx.renderer.StringValue; -import org.jdesktop.swingx.renderer.StringValues; -import org.jdesktop.swingx.rollover.RolloverProducer; -import org.jdesktop.swingx.rollover.RolloverRenderer; -import org.jdesktop.swingx.rollover.TreeRolloverController; -import org.jdesktop.swingx.rollover.TreeRolloverProducer; -import org.jdesktop.swingx.search.SearchFactory; -import org.jdesktop.swingx.search.Searchable; -import org.jdesktop.swingx.search.TreeSearchable; -import org.jdesktop.swingx.tree.DefaultXTreeCellEditor; -import org.jdesktop.swingx.tree.DefaultXTreeCellRenderer; - -import javax.swing.*; -import javax.swing.event.*; -import javax.swing.plaf.basic.BasicTreeUI; -import javax.swing.text.Position.Bias; -import javax.swing.tree.*; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.Hashtable; -import java.util.Vector; -import java.util.logging.Logger; -import java.util.regex.Pattern; - - -/** - * Enhanced Tree component with support for SwingX rendering, highlighting, - * rollover and search functionality. - *

                - * - *

                Rendering and Highlighting

                - * - * As all SwingX collection views, a JXTree is a HighlighterClient (PENDING JW: - * formally define and implement, like in AbstractTestHighlighter), that is it - * provides consistent api to add and remove Highlighters which can visually - * decorate the rendering component. - *

                - * - *

                
                - * 
                - * JXTree tree = new JXTree(new FileSystemModel());
                - * // use system file icons and name to render
                - * tree.setCellRenderer(new DefaultTreeRenderer(IconValues.FILE_ICON, 
                - *      StringValues.FILE_NAME));
                - * // highlight condition: file modified after a date     
                - * HighlightPredicate predicate = new HighlightPredicate() {
                - *    public boolean isHighlighted(Component renderer,
                - *                     ComponentAdapter adapter) {
                - *       File file = getUserObject(adapter.getValue());
                - *       return file != null ? lastWeek < file.lastModified : false;
                - *    }
                - * };
                - * // highlight with foreground color 
                - * tree.addHighlighter(new ColorHighlighter(predicate, null, Color.RED);      
                - * 
                - * 
                - * - * Note: for full functionality, a DefaultTreeRenderer must be installed - * as TreeCellRenderer. This is not done by default, because there are - * unresolved issues when editing. PENDING JW: still? Check! - * - * Note: to support the highlighting this implementation wraps the - * TreeCellRenderer set by client code with a DelegatingRenderer which applies - * the Highlighter after delegating the default configuration to the wrappee. As - * a side-effect, getCellRenderer does return the wrapper instead of the custom - * renderer. To access the latter, client code must call getWrappedCellRenderer. - *

                - *

                Rollover

                - * - * As all SwingX collection views, a JXTree supports per-cell rollover. If - * enabled, the component fires rollover events on enter/exit of a cell which by - * default is promoted to the renderer if it implements RolloverRenderer, that - * is simulates live behaviour. The rollover events can be used by client code - * as well, f.i. to decorate the rollover row using a Highlighter. - * - *
                
                - * 
                - * JXTree tree = new JXTree();
                - * tree.setRolloverEnabled(true);
                - * tree.setCellRenderer(new DefaultTreeRenderer());
                - * tree.addHighlighter(new ColorHighlighter(HighlightPredicate.ROLLOVER_ROW, 
                - *      null, Color.RED);      
                - * 
                - * 
                - * - * - *

                Search

                - * - * As all SwingX collection views, a JXTree is searchable. A search action is - * registered in its ActionMap under the key "find". The default behaviour is to - * ask the SearchFactory to open a search component on this component. The - * default keybinding is retrieved from the SearchFactory, typically ctrl-f (or - * cmd-f for Mac). Client code can register custom actions and/or bindings as - * appropriate. - *

                - * - * JXTree provides api to vend a renderer-controlled String representation of - * cell content. This allows the Searchable and Highlighters to use WYSIWYM - * (What-You-See-Is-What-You-Match), that is pattern matching against the actual - * string as seen by the user. - * - *

                Miscellaneous

                - * - *
                  - *
                • Improved usability for editing: guarantees that the tree is the - * focusOwner if editing terminated by user gesture and guards against data - * corruption if focusLost while editing - *
                • Access methods for selection colors, for consistency with JXTable, - * JXList - *
                • Convenience methods and actions to expand, collapse all nodes - *
                - * - * @author Ramesh Gupta - * @author Jeanette Winzenburg - * - * @see org.jdesktop.swingx.renderer.DefaultTreeRenderer - * @see org.jdesktop.swingx.renderer.ComponentProvider - * @see Highlighter - * @see org.jdesktop.swingx.decorator.HighlightPredicate - * @see SearchFactory - * @see Searchable - * - */ -@JavaBean -public class JXTree extends JTree { - @SuppressWarnings("unused") - private static final Logger LOG = Logger.getLogger(JXTree.class.getName()); - - - /** Empty int array used in getSelectedRows(). */ - private static final int[] EMPTY_INT_ARRAY = new int[0]; - /** Empty TreePath used in getSelectedPath() if selection empty. */ - private static final TreePath[] EMPTY_TREEPATH_ARRAY = new TreePath[0]; - - /** Collection of active Highlighters. */ - protected CompoundHighlighter compoundHighlighter; - /** Listener to changes of Highlighters in collection. */ - private ChangeListener highlighterChangeListener; - - /** Wrapper around the installed renderer, needed to support Highlighters. */ - private DelegatingRenderer delegatingRenderer; - - /** - * The RolloverProducer used if rollover is enabled. - */ - private RolloverProducer rolloverProducer; - - /** - * The RolloverController used if rollover is enabled. - */ - private TreeRolloverController linkController; - - private boolean overwriteIcons; - private Searchable searchable; - - // hacks around core focus issues around editing. - /** - * The propertyChangeListener responsible for terminating - * edits if focus lost. - */ - private CellEditorRemover editorRemover; - /** - * The CellEditorListener responsible to force the - * focus back to the tree after terminating edits. - */ - private CellEditorListener editorListener; - - /** Color of selected foreground. Added for consistent api across collection components. */ - private Color selectionForeground; - /** Color of selected background. Added for consistent api across collection components. */ - private Color selectionBackground; - - - - /** - * Constructs a JXTree with a sample model. The default model - * used by this tree defines a leaf node as any node without children. - */ - public JXTree() { - init(); - } - - /** - * Constructs a JXTree with each element of the specified array - * as the child of a new root node which is not displayed. By default, this - * tree defines a leaf node as any node without children. - * - * This version of the constructor simply invokes the super class version - * with the same arguments. - * - * @param value an array of objects that are children of the root. - */ - public JXTree(Object[] value) { - super(value); - init(); - } - - /** - * Constructs a JXTree with each element of the specified - * Vector as the child of a new root node which is not displayed. - * By default, this tree defines a leaf node as any node without children. - * - * This version of the constructor simply invokes the super class version - * with the same arguments. - * - * @param value an Vector of objects that are children of the root. - */ - public JXTree(Vector value) { - super(value); - init(); - } - - /** - * Constructs a JXTree created from a Hashtable which does not - * display with root. Each value-half of the key/value pairs in the HashTable - * becomes a child of the new root node. By default, the tree defines a leaf - * node as any node without children. - * - * This version of the constructor simply invokes the super class version - * with the same arguments. - * - * @param value a Hashtable containing objects that are children of the root. - */ - public JXTree(Hashtable value) { - super(value); - init(); - } - - /** - * Constructs a JXTree with the specified TreeNode as its root, - * which displays the root node. By default, the tree defines a leaf node as - * any node without children. - * - * This version of the constructor simply invokes the super class version - * with the same arguments. - * - * @param root root node of this tree - */ - public JXTree(TreeNode root) { - super(root, false); - init(); - } - - /** - * Constructs a JXTree with the specified TreeNode as its root, - * which displays the root node and which decides whether a node is a leaf - * node in the specified manner. - * - * This version of the constructor simply invokes the super class version - * with the same arguments. - * - * @param root root node of this tree - * @param asksAllowsChildren if true, only nodes that do not allow children - * are leaf nodes; otherwise, any node without children is a leaf node; - * @see javax.swing.tree.DefaultTreeModel#asksAllowsChildren - */ - public JXTree(TreeNode root, boolean asksAllowsChildren) { - super(root, asksAllowsChildren); - init(); - } - - /** - * Constructs an instance of JXTree which displays the root - * node -- the tree is created using the specified data model. - * - * This version of the constructor simply invokes the super class version - * with the same arguments. - * - * @param newModel - * the TreeModel to use as the data model - */ - public JXTree(TreeModel newModel) { - super(newModel); - init(); - } - - /** - * Instantiates JXTree state which is new compared to super. Installs the - * Delegating renderer and editor, registers actions and keybindings. - * - * This must be called from each constructor. - */ - private void init() { - // Issue #1061-swingx: renderer inconsistencies - // force setting of renderer - setCellRenderer(createDefaultCellRenderer()); - // Issue #233-swingx: default editor not bidi-compliant - // manually install an enhanced TreeCellEditor which - // behaves slightly better in RtoL orientation. - // Issue #231-swingx: icons lost - // Anyway, need to install the editor manually because - // the default install in BasicTreeUI doesn't know about - // the DelegatingRenderer and therefore can't see - // the DefaultTreeCellRenderer type to delegate to. - // As a consequence, the icons are lost in the default - // setup. - // JW PENDING need to mimic ui-delegate default re-set? - // JW PENDING alternatively, cleanup and use DefaultXXTreeCellEditor in incubator - if (getWrappedCellRenderer() instanceof DefaultTreeCellRenderer) { - setCellEditor(new DefaultXTreeCellEditor(this, (DefaultTreeCellRenderer) getWrappedCellRenderer())); - } - // Register the actions that this class can handle. - ActionMap map = getActionMap(); - map.put("expand-all", new Actions("expand-all")); - map.put("collapse-all", new Actions("collapse-all")); - map.put("find", createFindAction()); - - KeyStroke findStroke = SearchFactory.getInstance().getSearchAccelerator(); - getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(findStroke, "find"); - } - - /** - * Listens to the model and updates the {@code expandedState} accordingly - * when nodes are removed, or changed. - *

                - * This class will expand an invisible root when a child has been added to - * it. - * - * @author Karl George Schaefer - */ - protected class XTreeModelHandler extends TreeModelHandler { - /** - * {@inheritDoc} - */ - @Override - public void treeNodesInserted(TreeModelEvent e) { - TreePath path = e.getTreePath(); - - //fixes SwingX bug #612 - if (path.getParentPath() == null && !isRootVisible() && isCollapsed(path)) { - //should this be wrapped in SwingUtilities.invokeLater? - expandPath(path); - } - - super.treeNodesInserted(e); - } - } - - /** - * {@inheritDoc} - */ - @Override - protected TreeModelListener createTreeModelListener() { - return new XTreeModelHandler(); - } - - /** - * A small class which dispatches actions. - * TODO: Is there a way that we can make this static? - */ - private class Actions extends UIAction { - Actions(String name) { - super(name); - } - - @Override - public void actionPerformed(ActionEvent evt) { - if ("expand-all".equals(getName())) { - expandAll(); - } - else if ("collapse-all".equals(getName())) { - collapseAll(); - } - } - } - - -//-------------------- search support - - /** - * Creates and returns the action to invoke on a find request. - * - * @return the action to invoke on a find request. - */ - private Action createFindAction() { - return new UIAction("find") { - @Override - public void actionPerformed(ActionEvent e) { - doFind(); - } - }; - } - - /** - * Starts a search on this Tree's visible nodes. This implementation asks the - * SearchFactory to open a find widget on itself. - */ - protected void doFind() { - SearchFactory.getInstance().showFindInput(this, getSearchable()); - } - - /** - * Returns a Searchable for this component, guaranteed to be not null. This - * implementation lazily creates a TreeSearchable if necessary. - * - * - * @return a not-null Searchable for this component. - * - * @see #setSearchable(Searchable) - * @see TreeSearchable - */ - public Searchable getSearchable() { - if (searchable == null) { - searchable = new TreeSearchable(this); - } - return searchable; - } - - /** - * Sets the Searchable for this component. If null, a default - * Searchable will be created and used. - * - * @param searchable the Searchable to use for this component, may be null to - * indicate using the default. - * - * @see #getSearchable() - */ - public void setSearchable(Searchable searchable) { - this.searchable = searchable; - } - - /** - * Returns the string representation of the cell value at the given position. - * - * @param row the row index of the cell in view coordinates - * @return the string representation of the cell value as it will appear in the - * table. - */ - public String getStringAt(int row) { - return getStringAt(getPathForRow(row)); - } - - /** - * Returns the string representation of the cell value at the given position. - * - * @param path the TreePath representing the node. - * @return the string representation of the cell value as it will appear in the - * table, or null if the path is not visible. - */ - public String getStringAt(TreePath path) { - if (path == null) return null; - TreeCellRenderer renderer = getDelegatingRenderer().getDelegateRenderer(); - if (renderer instanceof StringValue) { - return ((StringValue) renderer).getString(path.getLastPathComponent()); - } - return StringValues.TO_STRING.getString(path.getLastPathComponent()); - } - - - /** - * Overridden to respect the string representation, if any. This takes over - * completely (as compared to super), internally messaging the Searchable. - *

                - * - * PENDING JW: re-visit once we support deep node search. - * - */ - @Override - public TreePath getNextMatch(String prefix, int startingRow, Bias bias) { - Pattern pattern = Pattern.compile("^" + prefix, Pattern.CASE_INSENSITIVE); - int row = getSearchable().search(pattern, startingRow, bias ==Bias.Backward); - return getPathForRow(row); - } - -//--------------------- misc. new api and super overrides - /** - * Collapses all nodes in this tree. - */ - public void collapseAll() { - for (int i = getRowCount() - 1; i >= 0 ; i--) { - collapseRow(i); - } - } - - /** - * Expands all nodes in this tree.

                - * - * Note: it's not recommended to use this method on the EDT for large/deep trees - * because expansion can take a considerable amount of time. - */ - public void expandAll() { - if (getRowCount() == 0) { - expandRoot(); - } - for (int i = 0; i < getRowCount(); i++) { - expandRow(i); - } - } - - /** - * Expands the root path if a TreeModel has been set, does nothing if not. - * - */ - private void expandRoot() { - TreeModel model = getModel(); - if (model != null && model.getRoot() != null) { - expandPath(new TreePath(model.getRoot())); - } - } - - /** - * {@inheritDoc} - *

                - * - * Overridden to always return a not-null array (following SwingX - * convention). - */ - @Override - public int[] getSelectionRows() { - int[] rows = super.getSelectionRows(); - return rows != null ? rows : EMPTY_INT_ARRAY; - } - - /** - * {@inheritDoc} - *

                - * - * Overridden to always return a not-null array (following SwingX - * convention). - */ - @Override - public TreePath[] getSelectionPaths() { - TreePath[] paths = super.getSelectionPaths(); - return paths != null ? paths : EMPTY_TREEPATH_ARRAY; - } - - /** - * Returns the background color for selected cells. - * - * @return the Color used for the background of - * selected list items - * @see #setSelectionBackground - * @see #setSelectionForeground - */ - public Color getSelectionBackground() { - return selectionBackground; - } - - /** - * Returns the selection foreground color. - * - * @return the Color object for the foreground property - * @see #setSelectionForeground - * @see #setSelectionBackground - */ - public Color getSelectionForeground() { - return selectionForeground; - } - - /** - * Sets the foreground color for selected cells. Cell renderers - * can use this color to render text and graphics for selected - * cells. - *

                - * The default value of this property is defined by the look - * and feel implementation. - *

                - * This is a JavaBeans bound property. - * - * @param selectionForeground the Color to use in the foreground - * for selected list items - * @see #getSelectionForeground - * @see #setSelectionBackground - * @see #setForeground - * @see #setBackground - * @see #setFont - * @beaninfo - * bound: true - * attribute: visualUpdate true - * description: The foreground color of selected cells. - */ - public void setSelectionForeground(Color selectionForeground) { - Object oldValue = getSelectionForeground(); - this.selectionForeground = selectionForeground; - firePropertyChange("selectionForeground", oldValue, getSelectionForeground()); - repaint(); - } - - /** - * Sets the background color for selected cells. Cell renderers - * can use this color to the fill selected cells. - *

                - * The default value of this property is defined by the look - * and feel implementation. - *

                - * This is a JavaBeans bound property. - * - * @param selectionBackground the Color to use for the - * background of selected cells - * @see #getSelectionBackground - * @see #setSelectionForeground - * @see #setForeground - * @see #setBackground - * @see #setFont - * @beaninfo - * bound: true - * attribute: visualUpdate true - * description: The background color of selected cells. - */ - public void setSelectionBackground(Color selectionBackground) { - Object oldValue = getSelectionBackground(); - this.selectionBackground = selectionBackground; - firePropertyChange("selectionBackground", oldValue, getSelectionBackground()); - repaint(); - } - - -//------------------------- update ui - - /** - * {@inheritDoc}

                - * - * Overridden to update selection background/foreground. Mimicking behaviour of - * ui-delegates for JTable, JList. - */ - @Override - public void updateUI() { - uninstallSelectionColors(); - super.updateUI(); - installSelectionColors(); - updateHighlighterUI(); - updateRendererEditorUI(); - invalidateCellSizeCache(); - } - - - /** - * Quick fix for #1060-swingx: icons lost on toggling LAF - */ - protected void updateRendererEditorUI() { - if (getCellEditor() instanceof UIDependent) { - ((UIDependent) getCellEditor()).updateUI(); - } - // PENDING JW: here we get the DelegationRenderer which is not (yet) UIDependent - // need to think about how to handle the per-tree icons - // anyway, the "real" renderer usually is updated accidentally - // don't know exactly why, added to the comp hierarchy? -// if (getCellRenderer() instanceof UIDependent) { -// ((UIDependent) getCellRenderer()).updateUI(); -// } - } - - /** - * Installs selection colors from UIManager.

                - * - * Note: this should be done in the UI delegate. - */ - private void installSelectionColors() { - if (SwingXUtilities.isUIInstallable(getSelectionBackground())) { - setSelectionBackground(UIManager.getColor("Tree.selectionBackground")); - } - if (SwingXUtilities.isUIInstallable(getSelectionForeground())) { - setSelectionForeground(UIManager.getColor("Tree.selectionForeground")); - } - - } - - /** - * Uninstalls selection colors.

                - * - * Note: this should be done in the UI delegate. - */ - private void uninstallSelectionColors() { - if (SwingXUtilities.isUIInstallable(getSelectionBackground())) { - setSelectionBackground(null); - } - if (SwingXUtilities.isUIInstallable(getSelectionForeground())) { - setSelectionForeground(null); - } - } - - /** - * Updates highlighter after updateUI changes. - * - * @see UIDependent - */ - protected void updateHighlighterUI() { - if (compoundHighlighter == null) return; - compoundHighlighter.updateUI(); - } - - - -//------------------------ Rollover support - - /** - * Sets the property to enable/disable rollover support. If enabled, the list - * fires property changes on per-cell mouse rollover state, i.e. - * when the mouse enters/leaves a list cell.

                - * - * This can be enabled to show "live" rollover behaviour, f.i. the cursor over a cell - * rendered by a JXHyperlink.

                - * - * The default value is false. - * - * @param rolloverEnabled a boolean indicating whether or not the rollover - * functionality should be enabled. - * - * @see #isRolloverEnabled() - * @see #getLinkController() - * @see #createRolloverProducer() - * @see RolloverRenderer - */ - public void setRolloverEnabled(boolean rolloverEnabled) { - boolean old = isRolloverEnabled(); - if (rolloverEnabled == old) return; - if (rolloverEnabled) { - rolloverProducer = createRolloverProducer(); - rolloverProducer.install(this); - getLinkController().install(this); - } else { - rolloverProducer.release(this); - rolloverProducer = null; - getLinkController().release(); - } - firePropertyChange("rolloverEnabled", old, isRolloverEnabled()); - } - - /** - * Returns a boolean indicating whether or not rollover support is enabled. - * - * @return a boolean indicating whether or not rollover support is enabled. - * - * @see #setRolloverEnabled(boolean) - */ - public boolean isRolloverEnabled() { - return rolloverProducer != null; - } - - /** - * Returns the RolloverController for this component. Lazyly creates the - * controller if necessary, that is the return value is guaranteed to be - * not null.

                - * - * PENDING JW: rename to getRolloverController - * - * @return the RolloverController for this tree, guaranteed to be not null. - * - * @see #setRolloverEnabled(boolean) - * @see #createLinkController() - * @see org.jdesktop.swingx.rollover.RolloverController - */ - protected TreeRolloverController getLinkController() { - if (linkController == null) { - linkController = createLinkController(); - } - return linkController; - } - - /** - * Creates and returns a RolloverController appropriate for this tree. - * - * @return a RolloverController appropriate for this tree. - * - * @see #getLinkController() - * @see org.jdesktop.swingx.rollover.RolloverController - */ - protected TreeRolloverController createLinkController() { - return new TreeRolloverController(); - } - - /** - * Creates and returns the RolloverProducer to use with this tree. - *

                - * - * @return RolloverProducer to use with this tree - * - * @see #setRolloverEnabled(boolean) - */ - protected RolloverProducer createRolloverProducer() { - return new TreeRolloverProducer(); - } - - -//----------------------- Highlighter api - - /** - * Sets the Highlighters to the table, replacing any old settings. - * None of the given Highlighters must be null.

                - * - * This is a bound property.

                - * - * Note: as of version #1.257 the null constraint is enforced strictly. To remove - * all highlighters use this method without param. - * - * @param highlighters zero or more not null highlighters to use for renderer decoration. - * @throws NullPointerException if array is null or array contains null values. - * - * @see #getHighlighters() - * @see #addHighlighter(Highlighter) - * @see #removeHighlighter(Highlighter) - * - */ - public void setHighlighters(Highlighter... highlighters) { - Highlighter[] old = getHighlighters(); - getCompoundHighlighter().setHighlighters(highlighters); - firePropertyChange("highlighters", old, getHighlighters()); - } - - /** - * Returns the Highlighters used by this table. - * Maybe empty, but guarantees to be never null. - * - * @return the Highlighters used by this table, guaranteed to never null. - * @see #setHighlighters(Highlighter[]) - */ - public Highlighter[] getHighlighters() { - return getCompoundHighlighter().getHighlighters(); - } - - /** - * Appends a Highlighter to the end of the list of used - * Highlighters. The argument must not be null. - *

                - * - * @param highlighter the Highlighter to add, must not be null. - * @throws NullPointerException if Highlighter is null. - * - * @see #removeHighlighter(Highlighter) - * @see #setHighlighters(Highlighter[]) - */ - public void addHighlighter(Highlighter highlighter) { - Highlighter[] old = getHighlighters(); - getCompoundHighlighter().addHighlighter(highlighter); - firePropertyChange("highlighters", old, getHighlighters()); - } - - /** - * Removes the given Highlighter.

                - * - * Does nothing if the Highlighter is not contained. - * - * @param highlighter the Highlighter to remove. - * @see #addHighlighter(Highlighter) - * @see #setHighlighters(Highlighter...) - */ - public void removeHighlighter(Highlighter highlighter) { - Highlighter[] old = getHighlighters(); - getCompoundHighlighter().removeHighlighter(highlighter); - firePropertyChange("highlighters", old, getHighlighters()); - } - - /** - * Returns the CompoundHighlighter assigned to the table, null if none. - * PENDING: open up for subclasses again?. - * - * @return the CompoundHighlighter assigned to the table. - */ - protected CompoundHighlighter getCompoundHighlighter() { - if (compoundHighlighter == null) { - compoundHighlighter = new CompoundHighlighter(); - compoundHighlighter.addChangeListener(getHighlighterChangeListener()); - } - return compoundHighlighter; - } - - /** - * Returns the ChangeListener to use with highlighters. Lazily - * creates the listener. - * - * @return the ChangeListener for observing changes of highlighters, - * guaranteed to be not-null - */ - protected ChangeListener getHighlighterChangeListener() { - if (highlighterChangeListener == null) { - highlighterChangeListener = createHighlighterChangeListener(); - } - return highlighterChangeListener; - } - - /** - * Creates and returns the ChangeListener observing Highlighters. - *

                - * Here: repaints the table on receiving a stateChanged. - * - * @return the ChangeListener defining the reaction to changes of - * highlighters. - */ - protected ChangeListener createHighlighterChangeListener() { - return new ChangeListener() { - @Override - public void stateChanged(ChangeEvent e) { - repaint(); - } - }; - } - - /** - * Sets the Icon to use for the handle of an expanded node.

                - * - * Note: this will only succeed if the current ui delegate is - * a BasicTreeUI otherwise it will do nothing.

                - * - * PENDING JW: incomplete api (no getter) and not a bound property. - * - * @param expandedIcon the Icon to use for the handle of an expanded node. - */ - public void setExpandedIcon(Icon expandedIcon) { - if (getUI() instanceof BasicTreeUI) { - ((BasicTreeUI) getUI()).setExpandedIcon(expandedIcon); - } - } - - /** - * Sets the Icon to use for the handle of a collapsed node. - * - * Note: this will only succeed if the current ui delegate is - * a BasicTreeUI otherwise it will do nothing. - * - * PENDING JW: incomplete api (no getter) and not a bound property. - * - * @param collapsedIcon the Icon to use for the handle of a collapsed node. - */ - public void setCollapsedIcon(Icon collapsedIcon) { - if (getUI() instanceof BasicTreeUI) { - ((BasicTreeUI) getUI()).setCollapsedIcon(collapsedIcon); - } - } - - /** - * Sets the Icon to use for a leaf node.

                - * - * Note: this will only succeed if current renderer is a - * DefaultTreeCellRenderer.

                - * - * PENDING JW: this (all setXXIcon) is old api pulled up from the JXTreeTable. - * Need to review if we really want it - problematic if sharing the same - * renderer instance across different trees. - * - * PENDING JW: incomplete api (no getter) and not a bound property.

                - * - * @param leafIcon the Icon to use for a leaf node. - */ - public void setLeafIcon(Icon leafIcon) { - getDelegatingRenderer().setLeafIcon(leafIcon); - } - - /** - * Sets the Icon to use for an open folder node. - * - * Note: this will only succeed if current renderer is a - * DefaultTreeCellRenderer. - * - * PENDING JW: incomplete api (no getter) and not a bound property. - * - * @param openIcon the Icon to use for an open folder node. - */ - public void setOpenIcon(Icon openIcon) { - getDelegatingRenderer().setOpenIcon(openIcon); - } - - /** - * Sets the Icon to use for a closed folder node. - * - * Note: this will only succeed if current renderer is a - * DefaultTreeCellRenderer. - * - * PENDING JW: incomplete api (no getter) and not a bound property. - * - * @param closedIcon the Icon to use for a closed folder node. - */ - public void setClosedIcon(Icon closedIcon) { - getDelegatingRenderer().setClosedIcon(closedIcon); - } - - /** - * Property to control whether per-tree icons should be - * copied to the renderer on setCellRenderer.

                - * - * The default value is false. - * - * PENDING: should update the current renderer's icons when - * setting to true? - * - * @param overwrite a boolean to indicate if the per-tree Icons should - * be copied to the new renderer on setCellRenderer. - * - * @see #isOverwriteRendererIcons() - * @see #setLeafIcon(Icon) - * @see #setOpenIcon(Icon) - * @see #setClosedIcon(Icon) - */ - public void setOverwriteRendererIcons(boolean overwrite) { - if (overwriteIcons == overwrite) return; - boolean old = overwriteIcons; - this.overwriteIcons = overwrite; - firePropertyChange("overwriteRendererIcons", old, overwrite); - } - - /** - * Returns a boolean indicating whether the per-tree icons should be - * copied to the renderer on setCellRenderer. - * - * @return true if a TreeCellRenderer's icons will be overwritten with the - * tree's Icons, false if the renderer's icons will be unchanged. - * - * @see #setOverwriteRendererIcons(boolean) - * @see #setLeafIcon(Icon) - * @see #setOpenIcon(Icon) - * @see #setClosedIcon(Icon) - * - */ - public boolean isOverwriteRendererIcons() { - return overwriteIcons; - } - - private DelegatingRenderer getDelegatingRenderer() { - if (delegatingRenderer == null) { - // only called once... to get hold of the default? - delegatingRenderer = new DelegatingRenderer(); - } - return delegatingRenderer; - } - - /** - * Creates and returns the default cell renderer to use. Subclasses may - * override to use a different type. - *

                - * - * This implementation returns a renderer of type - * DefaultTreeCellRenderer. Note: Will be changed to - * return a renderer of type DefaultTreeRenderer, - * once WrappingProvider is reasonably stable. - * - * @return the default cell renderer to use with this tree. - */ - protected TreeCellRenderer createDefaultCellRenderer() { -// return new DefaultTreeCellRenderer(); - return new DefaultXTreeCellRenderer(); - } - - /** - * {@inheritDoc}

                - * - * Overridden to return the delegating renderer which is wrapped around the - * original to support highlighting. The returned renderer is of type - * DelegatingRenderer and guaranteed to not-null

                - * - * @see #setCellRenderer(TreeCellRenderer) - * @see DelegatingRenderer - */ - @Override - public TreeCellRenderer getCellRenderer() { - // PENDING JW: something wrong here - why exactly can't we return super? - // not even if we force the initial setting in init? -// return super.getCellRenderer(); - return getDelegatingRenderer(); - } - - /** - * Returns the renderer installed by client code or the default if none has - * been set. - * - * @return the wrapped renderer. - * @see #setCellRenderer(TreeCellRenderer) - */ - public TreeCellRenderer getWrappedCellRenderer() { - return getDelegatingRenderer().getDelegateRenderer(); - } - - /** - * {@inheritDoc}

                - * - * Overridden to wrap the given renderer in a DelegatingRenderer to support - * highlighting.

                - * - * Note: the wrapping implies that the renderer returned from the getCellRenderer - * is not the renderer as given here, but the wrapper. To access the original, - * use getWrappedCellRenderer. - * - * @see #getWrappedCellRenderer() - * @see #getCellRenderer() - */ - @Override - public void setCellRenderer(TreeCellRenderer renderer) { - // PENDING: do something against recursive setting - // == multiple delegation... - getDelegatingRenderer().setDelegateRenderer(renderer); - super.setCellRenderer(delegatingRenderer); - // quick hack for #1061: renderer/editor inconsistent - if ((renderer instanceof DefaultTreeCellRenderer) && - (getCellEditor() instanceof DefaultXTreeCellEditor)) { - ((DefaultXTreeCellEditor) getCellEditor()).setRenderer((DefaultTreeCellRenderer) renderer); - } - firePropertyChange("cellRenderer", null, delegatingRenderer); - } - - - /** - * A decorator for the original TreeCellRenderer. Needed to hook highlighters - * after messaging the delegate.

                - * - * PENDING JW: formally implement UIDependent? - * PENDING JW: missing updateUI anyway (got lost when c&p from JXList ;-) - * PENDING JW: missing override of updateUI in xtree ... - */ - public class DelegatingRenderer implements TreeCellRenderer, RolloverRenderer { - private Icon closedIcon = null; - private Icon openIcon = null; - private Icon leafIcon = null; - - private TreeCellRenderer delegate; - - /** - * Instantiates a DelegatingRenderer with tree's default renderer as delegate. - */ - public DelegatingRenderer() { - this(null); - initIcons(new DefaultTreeCellRenderer()); - } - - /** - * Instantiates a DelegatingRenderer with the given delegate. If the - * delegate is null, the default is created via the list's factory method. - * - * @param delegate the delegate to use, if null the tree's default is - * created and used. - */ - public DelegatingRenderer(TreeCellRenderer delegate) { - initIcons((DefaultTreeCellRenderer) (delegate instanceof DefaultTreeCellRenderer ? - delegate : new DefaultTreeCellRenderer())); - setDelegateRenderer(delegate); - } - - /** - * initially sets the icons to the defaults as given - * by a DefaultTreeCellRenderer. - * - * @param renderer - */ - private void initIcons(DefaultTreeCellRenderer renderer) { - closedIcon = renderer.getDefaultClosedIcon(); - openIcon = renderer.getDefaultOpenIcon(); - leafIcon = renderer.getDefaultLeafIcon(); - } - - /** - * Sets the delegate. If the - * delegate is null, the default is created via the list's factory method. - * Updates the folder/leaf icons. - * - * THINK: how to update? always override with this.icons, only - * if renderer's icons are null, update this icons if they are not, - * update all if only one is != null.... ?? - * - * @param delegate the delegate to use, if null the list's default is - * created and used. - */ - public void setDelegateRenderer(TreeCellRenderer delegate) { - if (delegate == null) { - delegate = createDefaultCellRenderer(); - } - this.delegate = delegate; - updateIcons(); - } - - /** - * tries to set the renderers icons. Can succeed only if the - * delegate is a DefaultTreeCellRenderer. - * THINK: how to update? always override with this.icons, only - * if renderer's icons are null, update this icons if they are not, - * update all if only one is != null.... ?? - * - */ - private void updateIcons() { - if (!isOverwriteRendererIcons()) return; - setClosedIcon(closedIcon); - setOpenIcon(openIcon); - setLeafIcon(leafIcon); - } - - public void setClosedIcon(Icon closedIcon) { - if (delegate instanceof DefaultTreeCellRenderer) { - ((DefaultTreeCellRenderer) delegate).setClosedIcon(closedIcon); - } - this.closedIcon = closedIcon; - } - - public void setOpenIcon(Icon openIcon) { - if (delegate instanceof DefaultTreeCellRenderer) { - ((DefaultTreeCellRenderer) delegate).setOpenIcon(openIcon); - } - this.openIcon = openIcon; - } - - public void setLeafIcon(Icon leafIcon) { - if (delegate instanceof DefaultTreeCellRenderer) { - ((DefaultTreeCellRenderer) delegate).setLeafIcon(leafIcon); - } - this.leafIcon = leafIcon; - } - - //--------------- TreeCellRenderer - - /** - * Returns the delegate. - * - * @return the delegate renderer used by this renderer, guaranteed to - * not-null. - */ - public TreeCellRenderer getDelegateRenderer() { - return delegate; - } - - /** - * {@inheritDoc}

                - * - * Overridden to apply the highlighters, if any, after calling the delegate. - * The decorators are not applied if the row is invalid. - */ - @Override - public Component getTreeCellRendererComponent(JTree tree, Object value, - boolean selected, boolean expanded, boolean leaf, int row, - boolean hasFocus) { - Component result = delegate.getTreeCellRendererComponent(tree, - value, selected, expanded, leaf, row, hasFocus); - - if ((compoundHighlighter != null) && (row < getRowCount()) - && (row >= 0)) { - result = compoundHighlighter.highlight(result, - getComponentAdapter(row)); - } - - return result; - } - - // ------------------ RolloverRenderer - - @Override - public boolean isEnabled() { - return (delegate instanceof RolloverRenderer) - && ((RolloverRenderer) delegate).isEnabled(); - } - - @Override - public void doClick() { - if (isEnabled()) { - ((RolloverRenderer) delegate).doClick(); - } - } - - } - - /** - * Invalidates cell size caching in the ui delegate. May do nothing if there's no - * safe (i.e. without reflection) way to message the delegate.

                - * - * This implementation calls BasicTreeUI setLeftChildIndent with the old indent if available. - * Beware: clearing the cache is an undocumented implementation side-effect of the - * method. Revisit if we ever should have a custom ui delegate. - * - * - */ - public void invalidateCellSizeCache() { - if (getUI() instanceof BasicTreeUI) { - BasicTreeUI ui = (BasicTreeUI) getUI(); - ui.setLeftChildIndent(ui.getLeftChildIndent()); - } - } - -//----------------------- edit - - /** - * {@inheritDoc}

                - * Overridden to fix focus issues with editors. - * This method installs and updates the internal CellEditorRemover which - * terminates ongoing edits if appropriate. Additionally, it - * registers a CellEditorListener with the cell editor to grab the - * focus back to tree, if appropriate. - * - * @see #updateEditorRemover() - */ - @Override - public void startEditingAtPath(TreePath path) { - super.startEditingAtPath(path); - if (isEditing()) { - updateEditorListener(); - updateEditorRemover(); - } - } - - - /** - * Hack to grab focus after editing. - */ - private void updateEditorListener() { - if (editorListener == null) { - editorListener = new CellEditorListener() { - - @Override - public void editingCanceled(ChangeEvent e) { - terminated(e); - } - - /** - * @param e - */ - private void terminated(ChangeEvent e) { - analyseFocus(); - ((CellEditor) e.getSource()).removeCellEditorListener(editorListener); - } - - @Override - public void editingStopped(ChangeEvent e) { - terminated(e); - } - - }; - } - getCellEditor().addCellEditorListener(editorListener); - - } - - /** - * This is called from cell editor listener if edit terminated. - * Trying to analyse if we should grab the focus back to the - * tree after. Brittle ... we assume we are the first to - * get the event, so we can analyse the hierarchy before the - * editing component is removed. - */ - protected void analyseFocus() { - if (isFocusOwnerDescending()) { - requestFocusInWindow(); - } - } - - - /** - * Returns a boolean to indicate if the current focus owner - * is descending from this table. - * Returns false if not editing, otherwise walks the focusOwner - * hierarchy, taking popups into account.

                - * - * PENDING: copied from JXTable ... should be somewhere in a utility - * class? - * - * @return a boolean to indicate if the current focus - * owner is contained. - */ - private boolean isFocusOwnerDescending() { - if (!isEditing()) return false; - Component focusOwner = - KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); - // PENDING JW: special casing to not fall through ... really wanted? - if (focusOwner == null) return false; - if (SwingXUtilities.isDescendingFrom(focusOwner, this)) return true; - // same with permanent focus owner - Component permanent = - KeyboardFocusManager.getCurrentKeyboardFocusManager().getPermanentFocusOwner(); - return SwingXUtilities.isDescendingFrom(permanent, this); - } - - - - /** - * Overridden to release the CellEditorRemover, if any. - */ - @Override - public void removeNotify() { - if (editorRemover != null) { - editorRemover.release(); - editorRemover = null; - } - super.removeNotify(); - } - - /** - * Lazily creates and updates the internal CellEditorRemover. - * - * - */ - private void updateEditorRemover() { - if (editorRemover == null) { - editorRemover = new CellEditorRemover(); - } - editorRemover.updateKeyboardFocusManager(); - } - - /** This class tracks changes in the keyboard focus state. It is used - * when the JXTree is editing to determine when to terminate the edit. - * If focus switches to a component outside of the JXTree, but in the - * same window, this will terminate editing. The exact terminate - * behaviour is controlled by the invokeStopEditing property. - * - * @see JTree#setInvokesStopCellEditing(boolean) - * - */ - public class CellEditorRemover implements PropertyChangeListener { - /** the focusManager this is listening to. */ - KeyboardFocusManager focusManager; - - public CellEditorRemover() { - updateKeyboardFocusManager(); - } - - /** - * Updates itself to listen to the current KeyboardFocusManager. - * - */ - public void updateKeyboardFocusManager() { - KeyboardFocusManager current = KeyboardFocusManager.getCurrentKeyboardFocusManager(); - setKeyboardFocusManager(current); - } - - /** - * stops listening. - * - */ - public void release() { - setKeyboardFocusManager(null); - } - - /** - * Sets the focusManager this is listening to. - * Unregisters/registers itself from/to the old/new manager, - * respectively. - * - * @param current the KeyboardFocusManager to listen too. - */ - private void setKeyboardFocusManager(KeyboardFocusManager current) { - if (focusManager == current) - return; - KeyboardFocusManager old = focusManager; - if (old != null) { - old.removePropertyChangeListener("permanentFocusOwner", this); - } - focusManager = current; - if (focusManager != null) { - focusManager.addPropertyChangeListener("permanentFocusOwner", - this); - } - - } - @Override - public void propertyChange(PropertyChangeEvent ev) { - if (!isEditing()) { - return; - } - - Component c = focusManager.getPermanentFocusOwner(); - JXTree tree = JXTree.this; - while (c != null) { - if (c instanceof JPopupMenu) { - c = ((JPopupMenu) c).getInvoker(); - } else { - - if (c == tree) { - // focus remains inside the table - return; - } else if ((c instanceof Window) || - (SwingXUtilities.isApplet(c) && c.getParent() == null)) { - if (c == SwingUtilities.getRoot(tree)) { - if (tree.getInvokesStopCellEditing()) { - tree.stopEditing(); - } - if (tree.isEditing()) { - tree.cancelEditing(); - } - } - break; - } - c = c.getParent(); - } - } - } - } - -// ------------------ oldish String conversion api, no longer recommended - - /** - * {@inheritDoc}

                - * - * Overridden to initialize the String conversion method of the model, if any.

                - * PENDING JW: remove - that is an outdated approach? - */ - @Override - public void setModel(TreeModel newModel) { - super.setModel(newModel); - } - - - -//------------------------------- ComponentAdapter - /** - * @return the unconfigured ComponentAdapter. - */ - protected ComponentAdapter getComponentAdapter() { - if (dataAdapter == null) { - dataAdapter = new TreeAdapter(this); - } - return dataAdapter; - } - - /** - * Convenience to access a configured ComponentAdapter. - * Note: the column index of the configured adapter is always 0. - * - * @param index the row index in view coordinates, must be valid. - * @return the configured ComponentAdapter. - */ - protected ComponentAdapter getComponentAdapter(int index) { - ComponentAdapter adapter = getComponentAdapter(); - adapter.column = 0; - adapter.row = index; - return adapter; - } - - protected ComponentAdapter dataAdapter; - - protected static class TreeAdapter extends ComponentAdapter { - private final JXTree tree; - - /** - * Constructs a TableCellRenderContext for the specified - * target component. - * - * @param component the target component - */ - public TreeAdapter(JXTree component) { - super(component); - tree = component; - } - - public JXTree getTree() { - return tree; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean hasFocus() { - return tree.isFocusOwner() && (tree.getLeadSelectionRow() == row); - } - - /** - * {@inheritDoc} - */ - @Override - public Object getValueAt(int row, int column) { - TreePath path = tree.getPathForRow(row); - return path.getLastPathComponent(); - } - - /** - * {@inheritDoc} - */ - @Override - public String getStringAt(int row, int column) { - return tree.getStringAt(row); - } - - /** - * {@inheritDoc} - */ - @Override - public Rectangle getCellBounds() { - return tree.getRowBounds(row); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isEditable() { - //this is not as robust as JXTable; should it be? -- kgs - return tree.isPathEditable(tree.getPathForRow(row)); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isSelected() { - return tree.isRowSelected(row); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isExpanded() { - return tree.isExpanded(row); - } - - /** - * {@inheritDoc} - */ - @Override - public int getDepth() { - return tree.getPathForRow(row).getPathCount() - 1; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isHierarchical() { - return true; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isLeaf() { - return tree.getModel().isLeaf(getValue()); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isCellEditable(int row, int column) { - return false; /** TODO: */ - } - } - - -} diff --git a/src/main/java/org/jdesktop/swingx/JXTreeTable.java b/src/main/java/org/jdesktop/swingx/JXTreeTable.java deleted file mode 100644 index f79f3024f2..0000000000 --- a/src/main/java/org/jdesktop/swingx/JXTreeTable.java +++ /dev/null @@ -1,3336 +0,0 @@ -/* - * $Id: JXTreeTable.java 4268 2012-12-07 11:55:23Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - - -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.decorator.ComponentAdapter; -import org.jdesktop.swingx.event.TreeExpansionBroadcaster; -import org.jdesktop.swingx.plaf.UIAction; -import org.jdesktop.swingx.renderer.StringValue; -import org.jdesktop.swingx.renderer.StringValues; -import org.jdesktop.swingx.rollover.RolloverProducer; -import org.jdesktop.swingx.rollover.RolloverRenderer; -import org.jdesktop.swingx.tree.DefaultXTreeCellRenderer; -import org.jdesktop.swingx.treetable.DefaultTreeTableModel; -import org.jdesktop.swingx.treetable.TreeTableCellEditor; -import org.jdesktop.swingx.treetable.TreeTableModel; -import org.jdesktop.swingx.treetable.TreeTableModelProvider; -import org.jdesktop.swingx.util.Contract; - -import javax.swing.*; -import javax.swing.border.Border; -import javax.swing.event.*; -import javax.swing.plaf.basic.BasicTreeUI; -import javax.swing.table.*; -import javax.swing.tree.*; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.InputEvent; -import java.awt.event.MouseEvent; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.EventObject; -import java.util.List; -import java.util.logging.Logger; - -/** - *

                JXTreeTable is a specialized {@link JTable table} - * consisting of a single column in which to display hierarchical data, and any - * number of other columns in which to display regular data. The interface for - * the data model used by a JXTreeTable is - * {@link TreeTableModel}. It extends the - * {@link javax.swing.tree.TreeModel} interface to allow access to cell data by - * column indices within each node of the tree hierarchy.

                - * - *

                The most straightforward way create and use a JXTreeTable, is to - * first create a suitable data model for it, and pass that to a - * JXTreeTable constructor, as shown below: - *

                - *  TreeTableModel  treeTableModel = new FileSystemModel(); // any TreeTableModel
                - *  JXTreeTable     treeTable = new JXTreeTable(treeTableModel);
                - *  JScrollPane     scrollpane = new JScrollPane(treeTable);
                - * 
                - * See {@link JTable} for an explanation of why putting the treetable - * inside a scroll pane is necessary.

                - * - *

                A single treetable model instance may be shared among more than one - * JXTreeTable instances. To access the treetable model, always call - * {@link #getTreeTableModel() getTreeTableModel} and - * {@link #setTreeTableModel(TreeTableModel) setTreeTableModel}. - * JXTreeTable wraps the supplied treetable model inside a private - * adapter class to adapt it to a {@link TableModel}. Although - * the model adapter is accessible through the {@link #getModel() getModel} method, you - * should avoid accessing and manipulating it in any way. In particular, each - * model adapter instance is tightly bound to a single table instance, and any - * attempt to share it with another table (for example, by calling - * {@link #setModel(TableModel) setModel}) - * will throw an IllegalArgumentException! - * - * Note:

                - * This implementation is basically as hacky as the very first version - * more than a decaded ago: the renderer of the hierarchical column is a - * JXTree which is trickst into painting a single row at the position of - * the table cell. TreeModel changes must be adapted to TableModel changes - * after the tree received them, that is the TableModel events are asynchronous - * as compared to their base trigger. As a consequence, the adapted TableModel - * doesn't play nicely when shared in other J/X/Tables (f.i. used as rowHeader - - * see http://java.net/jira/browse/SWINGX-1529) - * - * @author Philip Milne - * @author Scott Violet - * @author Ramesh Gupta - * - */ -@JavaBean -public class JXTreeTable extends JXTable { - @SuppressWarnings("unused") - private static final Logger LOG = Logger.getLogger(JXTreeTable.class - .getName()); - /** - * Key for clientProperty to decide whether to apply hack around #168-jdnc. - */ - public static final String DRAG_HACK_FLAG_KEY = "treeTable.dragHackFlag"; - /** - * Key for clientProperty to decide whether to apply hack around #766-swingx. - */ - public static final String DROP_HACK_FLAG_KEY = "treeTable.dropHackFlag"; - /** - * Renderer used to render cells within the - * {@link #isHierarchical(int) hierarchical} column. - * renderer extends JXTree and implements TableCellRenderer - */ - private TreeTableCellRenderer renderer; - - /** - * Editor used to edit cells within the - * {@link #isHierarchical(int) hierarchical} column. - */ - private TreeTableCellEditor hierarchicalEditor; - - private TreeTableHacker treeTableHacker; - private boolean consumedOnPress; - private TreeExpansionBroadcaster treeExpansionBroadcaster; - - /** - * Constructs a JXTreeTable using a - * {@link DefaultTreeTableModel}. - */ - public JXTreeTable() { - this(new DefaultTreeTableModel()); - } - - /** - * Constructs a JXTreeTable using the specified - * {@link TreeTableModel}. - * - * @param treeModel model for the JXTreeTable - */ - public JXTreeTable(TreeTableModel treeModel) { - this(new TreeTableCellRenderer(treeModel)); - } - - /** - * Constructs a JXTreeTable using the specified - * {@link TreeTableCellRenderer}. - * - * @param renderer - * cell renderer for the tree portion of this JXTreeTable - * instance. - */ - private JXTreeTable(TreeTableCellRenderer renderer) { - // To avoid unnecessary object creation, such as the construction of a - // DefaultTableModel, it is better to invoke - // super(TreeTableModelAdapter) directly, instead of first invoking - // super() followed by a call to setTreeTableModel(TreeTableModel). - - // Adapt tree model to table model before invoking super() - super(new TreeTableModelAdapter(renderer)); - - // renderer-related initialization - init(renderer); // private method - initActions(); - // disable sorting - super.setSortable(false); - super.setAutoCreateRowSorter(false); - super.setRowSorter(null); - // no grid - setShowGrid(false, false); - - hierarchicalEditor = new TreeTableCellEditor(renderer); - -// // No grid. -// setShowGrid(false); // superclass default is "true" -// -// // Default intercell spacing -// setIntercellSpacing(spacing); // for both row margin and column margin - - } - - /** - * Initializes this JXTreeTable and permanently binds the specified renderer - * to it. - * - * @param renderer private tree/renderer permanently and exclusively bound - * to this JXTreeTable. - */ - private void init(TreeTableCellRenderer renderer) { - this.renderer = renderer; - assert ((TreeTableModelAdapter) getModel()).tree == this.renderer; - - // Force the JTable and JTree to share their row selection models. - ListToTreeSelectionModelWrapper selectionWrapper = - new ListToTreeSelectionModelWrapper(); - - // JW: when would that happen? - if (renderer != null) { - renderer.bind(this); // IMPORTANT: link back! - renderer.setSelectionModel(selectionWrapper); - } - // adjust the tree's rowHeight to this.rowHeight - adjustTreeRowHeight(getRowHeight()); - adjustTreeBounds(); - setSelectionModel(selectionWrapper.getListSelectionModel()); - - // propagate the lineStyle property to the renderer - PropertyChangeListener l = new PropertyChangeListener() { - - @Override - public void propertyChange(PropertyChangeEvent evt) { - JXTreeTable.this.renderer.putClientProperty(evt.getPropertyName(), evt.getNewValue()); - - } - - }; - addPropertyChangeListener("JTree.lineStyle", l); - - } - - - private void initActions() { - // Register the actions that this class can handle. - ActionMap map = getActionMap(); - map.put("expand-all", new Actions("expand-all")); - map.put("collapse-all", new Actions("collapse-all")); - } - - /** - * A small class which dispatches actions. - * TODO: Is there a way that we can make this static? - */ - private class Actions extends UIAction { - Actions(String name) { - super(name); - } - - @Override - public void actionPerformed(ActionEvent evt) { - if ("expand-all".equals(getName())) { - expandAll(); - } - else if ("collapse-all".equals(getName())) { - collapseAll(); - } - } - } - - - /** - * {@inheritDoc}

                - * Overridden to do nothing. - * - * TreeTable is not sortable because there is no equivalent to - * RowSorter (which is targeted to linear structures) for - * hierarchical data. - * - */ - @Override - public void setSortable(boolean sortable) { - // no-op - } - - /** - * {@inheritDoc}

                - * Overridden to do nothing. - * - * TreeTable is not sortable because there is no equivalent to - * RowSorter (which is targeted to linear structures) for - * hierarchical data. - * - */ - @Override - public void setAutoCreateRowSorter(boolean autoCreateRowSorter) { - } - - /** - * {@inheritDoc}

                - * Overridden to do nothing. - * - * TreeTable is not sortable because there is no equivalent to - * RowSorter (which is targeted to linear structures) for - * hierarchical data. - * - */ - @Override - public void setRowSorter(RowSorter sorter) { - } - - /** - * Hook into super's setAutoCreateRowSorter for use in sub-classes which want to experiment - * with tree table sorting/filtering.

                - * - * NOTE: While subclasses may use this method to allow access to - * super that usage alone will not magically turn sorting/filtering on! They have - * to implement an appropriate RowSorter/SortController - * as well. This is merely a hook to hang themselves, as requested in Issue #479-swingx - * - * - * @param autoCreateRowSorter - */ - protected void superSetAutoCreateRowSorter(boolean autoCreateRowSorter) { - super.setAutoCreateRowSorter(autoCreateRowSorter); - } - - /** - * Hook into super's setSortable for use in sub-classes which want to experiment - * with tree table sorting/filtering.

                - * - * NOTE: While subclasses may use this method to allow access to - * super that usage alone will not magically turn sorting/filtering on! They have - * to implement an appropriate RowSorter/SortController - * as well. This is merely a hook to hang themselves, as requested in Issue #479-swingx - * - * - * @param sortable - */ - protected void superSetSortable(boolean sortable) { - super.setSortable(sortable); - } - - /** - * Hook into super's setRowSorter for use in sub-classes which want to experiment - * with tree table sorting/filtering.

                - * - * NOTE: While subclasses may use this method to allow access to - * super that usage alone will not magically turn sorting/filtering on! They have - * to implement an appropriate RowSorter/SortController - * as well. This is merely a hook to hang themselves, as requested in Issue #479-swingx - * - * - * @param sorter - */ - protected void superSetRowSorter(RowSorter sorter) { - super.setRowSorter(sorter); - } - - /** - * {@inheritDoc}

                - * - * Overridden to keep the tree's enabled in synch. - */ - @Override - public void setEnabled(boolean enabled) { - renderer.setEnabled(enabled); - super.setEnabled(enabled); - } - - /** - * {@inheritDoc}

                - * - * Overridden to keep the tree's selectionBackground in synch. - */ - @Override - public void setSelectionBackground(Color selectionBackground) { - // happens on instantiation, updateUI is called before the renderer is installed - if (renderer != null) - renderer.setSelectionBackground(selectionBackground); - super.setSelectionBackground(selectionBackground); - } - - /** - * {@inheritDoc}

                - * - * Overridden to keep the tree's selectionForeground in synch. - */ - @Override - public void setSelectionForeground(Color selectionForeground) { - // happens on instantiation, updateUI is called before the renderer is installed - if (renderer != null) - renderer.setSelectionForeground(selectionForeground); - super.setSelectionForeground(selectionForeground); - } - - /** - * Overriden to invoke repaint for the particular location if - * the column contains the tree. This is done as the tree editor does - * not fill the bounds of the cell, we need the renderer to paint - * the tree in the background, and then draw the editor over it. - * You should not need to call this method directly.

                - * - * Additionally, there is tricksery involved to expand/collapse - * the nodes. - * - * {@inheritDoc} - */ - @Override - public boolean editCellAt(int row, int column, EventObject e) { - getTreeTableHacker().hitHandleDetectionFromEditCell(column, e); // RG: Fix Issue 49! - boolean canEdit = super.editCellAt(row, column, e); - if (canEdit && isHierarchical(column)) { - repaint(getCellRect(row, column, false)); - } - return canEdit; - } - - /** - * Overridden to enable hit handle detection a mouseEvent which triggered - * a expand/collapse. - */ - @Override - protected void processMouseEvent(MouseEvent e) { - // BasicTableUI selects on released if the pressed had been - // consumed. So we try to fish for the accompanying released - // here and consume it as wll. - if ((e.getID() == MouseEvent.MOUSE_RELEASED) && consumedOnPress) { - consumedOnPress = false; - e.consume(); - return; - } - if (getTreeTableHacker().hitHandleDetectionFromProcessMouse(e)) { - // Issue #332-swing: hacking around selection loss. - // prevent the - // _table_ selection by consuming the mouseEvent - // if it resulted in a expand/collapse - consumedOnPress = true; - e.consume(); - return; - } - consumedOnPress = false; - super.processMouseEvent(e); - } - - - protected TreeTableHacker getTreeTableHacker() { - if (treeTableHacker == null) { - treeTableHacker = createTreeTableHacker(); - } - return treeTableHacker; - } - - /** - * Hacking around various issues. Subclass and let it return - * your favourite. The current default is TreeTableHackerExt5 (latest - * evolution to work around #1230), the old long-standing default was - * TreeTableHackerExt3. If you experience problems with the latest, please - * let us know. - * - * @return - */ - protected TreeTableHacker createTreeTableHacker() { -// return new TreeTableHacker(); -// return new TreeTableHackerExt(); -// return new TreeTableHackerExt2(); -// return new TreeTableHackerExt3(); -// return new TreeTableHackerExt4(); - return new TreeTableHackerExt5(); - } - - private boolean processMouseMotion = true; - - @Override - protected void processMouseMotionEvent(MouseEvent e) { - if (processMouseMotion) - super.processMouseMotionEvent(e); - } - - /** - * This class extends TreeTableHackerExt instead of TreeTableHackerExt3 so - * as to serve as a clue that it is a complete overhaul and looking in - * TreeTableHackerExt2 and TreeTableHackerExt3 for methods to change the - * behavior will do you no good. - *

                - * The methods previously used are abandoned as they would be misnomers to - * the behavior as implemented in this class. - *

                - * Changes: - *

                  - *
                1. - * According to TreeTableHackerExt3, clickCounts > 1 are not sent to the - * JTree so that double clicks will start edits (Issue #474). Well, mouse - * events are only sent to the JTree if they occur within the tree handle - * space - so that is not the behavior desired. Double clicks on the - * text/margin opposite the tree handle already started edits without that - * modification (I checked). The only thing that modification does is - * introduce bugs when one actually double clicks on a tree handle... so - * that idea was abandoned.
                2. - *
                3. - * There is no longer any discrimination between events that cause an - * expansion/collapse. Since the event location is check to see if it is in - * the tree handle margin area, this doesn't seem necessary. Plus it is more - * user friendly: if someone missed the tree handle by 1 pixel, then it - * caused a selection change instead of a node expansion/ collapse.
                4. - *
                5. - * The consumption of events are handled within this class itself because - * the behavior associated with the way that processMouseEvent(MouseEvent) - * consumed events was incompatible with the way this - * class does things. As a consequence, - * hitHandleDetectionFromProcessMouse(MouseEvent) - * always returns false so that processMoueEvent(MouseEvent) will not - * doing anything other than call its super - * method.
                6. - *
                7. - * All events of type MOUSE_PRESSED, MOUSE_RELEASED, and MOUSE_CLICKED, but - * excluding when isPopupTrigger() returns true, are sent to - * the JTree. This has the added benefit of not having to piggy back a mouse - * released event as we can just use the real mouse released event. This - * keeps the look and feel consistent for the user of UI's that - * expand/collapse nodes on the release of the mouse.
                8. - *
                9. - * The previous implementations have a spiel about avoiding events with - * modifiers because the UI might try to change the selection. Well that - * didn't occur in any of the look and feels I tested. Perhaps that was the - * case if events that landed within the content area of a node were sent to - * the JTree. If that behavior is actually necessary, then it can be added - * to the isTreeHandleEventType(MouseEvent) method. This - * implementation sends all events regardless of the modifiers.
                10. - *
                11. - * This implementation toggles the processing of mouse motion events. When - * events are sent to the tree, it is turned off and turned back on when an - * event is not sent to the tree. This fixes selection changes that occur - * when one drags the mouse after pressing on a tree handle.
                12. - *
                - * - * contributed by member aephyr@dev.java.net - */ - public class TreeTableHackerExt4 extends TreeTableHackerExt { - - /** - * Filter to find mouse events that are candidates for node expansion/ - * collapse. MOUSE_PRESSED and MOUSE_RELEASED are used by default UIs. - * MOUSE_CLICKED is included as it may be used by a custom UI. - * - * @param e the currently dispatching mouse event - * @return true if the event is a candidate for sending to the JTree - */ - protected boolean isTreeHandleEventType(MouseEvent e) { - switch (e.getID()) { - case MouseEvent.MOUSE_CLICKED: - case MouseEvent.MOUSE_PRESSED: - case MouseEvent.MOUSE_RELEASED: - return !e.isPopupTrigger(); - } - return false; - } - - /** - * This method checks if the location of the event is in the tree handle - * margin and translates the coordinates for the JTree. - * - * @param e the currently dispatching mouse event - * @return the mouse event to dispatch to the JTree or null if nothing - * should be dispatched - */ - protected MouseEvent getEventForTreeRenderer(MouseEvent e) { - Point pt = e.getPoint(); - int col = columnAtPoint(pt); - if (col >= 0 && isHierarchical(col)) { - int row = rowAtPoint(pt); - if (row >= 0) { - // There will not be a check to see if the y coordinate is in range - // because the use of row = rowAtPoint(pt) will only return - // a row that has the y coordinates in the range of our point. - Rectangle cellBounds = getCellRect(row, col, false); - int x = e.getX() - cellBounds.x; - Rectangle nodeBounds = renderer.getRowBounds(row); - // The renderer's component orientation is checked because that - // is the one that really matters. Though it seems to always be - // in sync with the JXTreeTable's component orientation, maybe - // someone wants them to be different for some reason. - if (renderer.getComponentOrientation().isLeftToRight() ? x < nodeBounds.x - : x > nodeBounds.x + nodeBounds.width) { - return new MouseEvent(renderer, e.getID(), e.getWhen(), - e.getModifiers(), x, e.getY(), - e.getXOnScreen(), e.getYOnScreen(), e - .getClickCount(), false, e.getButton()); - } - } - } - return null; - } - - /** - * - * @return this method always returns false, so that processMouseEvent - * always just simply calls its super method - */ - @Override - public boolean hitHandleDetectionFromProcessMouse(MouseEvent e) { - if (!isHitDetectionFromProcessMouse()) - return false; - if (isTreeHandleEventType(e)) { - MouseEvent newE = getEventForTreeRenderer(e); - if (newE != null) { - renderer.dispatchEvent(newE); - if (shouldDisableMouseMotionOnTable(e)) { - // This fixes the issue of drags on tree handles - // (often unintentional) from selecting all nodes from the - // anchor to the node of said tree handle. - processMouseMotion = false; - // part of 561-swingx: if focus elsewhere and dispatching the - // mouseEvent the focus doesn't move from elsewhere - // still doesn't help in very first click after startup - // probably lead of row selection event not correctly - // updated on synch from treeSelectionModel - requestFocusInWindow(); - } else { - processMouseMotion = true; - } - e.consume(); - // Return false to prevent JXTreeTable.processMouseEvent(MouseEvent) - // from stopping the processing of the event. This allows the - // listeners to see the event even though it is consumed - // (perhaps useful for a user supplied listener). A proper UI - // listener will ignore consumed events. - return false; - // alternatively, you would have to use: return e.getID() == MouseEvent.MOUSE_PRESSED; - // because JXTreeTable.processMouseEvent(MouseEvent) assumes true - // will only be returned for MOUSE_PRESSED events. Also, if true - // were to be returned, then you'd have to piggy back a released - // event as the previous implementation does, because the actual - // released event would never reach this method. - } - } - processMouseMotion = true; - return false; - } - - /** - * Returns a boolean indicating whether mouseMotionEvents to the - * table should be disabled. This is called from hitHandleDetectionFromMouseEvent - * if the event was passed to the rendering tree and consumed. Returning - * true has the side-effect of requesting focus to the table.

                - * - * NOTE JW: this was extracted to from the calling method to fix - * Issue #1527-swingx (no tooltips on JXTreeTable after expand/collapse) - * and at the same time allow subclasses to further hack around ...

                - * - * @param e the mouseEvent that was routed to the renderer. - * @return true if disabling mouseMotionEvents to table, false if enabling them - */ - protected boolean shouldDisableMouseMotionOnTable(MouseEvent e) { - return processMouseMotion && e.getID() == MouseEvent.MOUSE_PRESSED; - } - } - - /* - * Changed to calculate the area of the tree handle and only forward mouse - * events to the tree if the event lands within that area. This keeps the - * selection behavior consistent with TreeTableHackerExt3. - * - * contributed by member aephyr@dev.java.net - */ - public class TreeTableHackerExt5 extends TreeTableHackerExt4 { - - /** - * If a negative number is returned, then all events that occur in the - * leading margin will be forwarded to the tree and consumed. - * - * @return the width of the tree handle if it can be determined, else -1 - */ - protected int getTreeHandleWidth() { - if (renderer.getUI() instanceof BasicTreeUI) { - BasicTreeUI ui = (BasicTreeUI) renderer.getUI(); - return ui.getLeftChildIndent() + ui.getRightChildIndent(); - } else { - return -1; - } - } - - @Override - protected MouseEvent getEventForTreeRenderer(MouseEvent e) { - Point pt = e.getPoint(); - int col = columnAtPoint(pt); - if (col >= 0 && isHierarchical(col)) { - int row = rowAtPoint(pt); - // There will not be a check to see if the y coordinate is in - // range - // because the use of row = rowAtPoint(pt) will only return a - // row - // that has the y coordinates in the range of our point. - if (row >= 0) { - TreePath path = getPathForRow(row); - Object node = path.getLastPathComponent(); - // Check if the node has a tree handle and if so, check - // if the event location falls over the tree handle. - if (!getTreeTableModel().isLeaf(node) - && (getTreeTableModel().getChildCount(node) > 0 || !renderer - .hasBeenExpanded(path))) { - Rectangle cellBounds = getCellRect(row, col, false); - int x = e.getX() - cellBounds.x; - Rectangle nb = renderer.getRowBounds(row); - int thw = getTreeHandleWidth(); - // The renderer's component orientation is checked - // because that - // is the one that really matters. Though it seems to - // always be - // in sync with the JXTreeTable's component orientation, - // maybe - // someone wants them to be different for some reason. - if (renderer.getComponentOrientation().isLeftToRight() ? x < nb.x - && (thw < 0 || x > nb.x - thw) - : x > nb.x + nb.width - && (thw < 0 || x < nb.x + nb.width - + thw)) { - return new MouseEvent(renderer, e.getID(), e - .getWhen(), e.getModifiers(), x, e.getY(), - e.getXOnScreen(), e.getYOnScreen(), e - .getClickCount(), false, e - .getButton()); - } - } - } - } - return null; - } - - } - - - - /** - * Temporary class to have all the hacking at one place. Naturally, it will - * change a lot. The base class has the "stable" behaviour as of around - * jun2006 (before starting the fix for 332-swingx).

                - * - * specifically: - * - *

                  - *
                1. hitHandleDetection triggeredn in editCellAt - *
                - * - */ - public class TreeTableHacker { - - protected boolean expansionChangedFlag; - - /** - * Decision whether the handle hit detection - * should be done in processMouseEvent or editCellAt. - * Here: returns false. - * - * @return true for handle hit detection in processMouse, false - * for editCellAt. - */ - protected boolean isHitDetectionFromProcessMouse() { - return false; - } - - /** - * Entry point for hit handle detection called from editCellAt, - * does nothing if isHitDetectionFromProcessMouse is true; - * - * @see #isHitDetectionFromProcessMouse() - */ - public void hitHandleDetectionFromEditCell(int column, EventObject e) { - if (!isHitDetectionFromProcessMouse()) { - expandOrCollapseNode(column, e); - } - } - - /** - * Entry point for hit handle detection called from processMouse. - * Does nothing if isHitDetectionFromProcessMouse is false. - * - * @return true if the mouseEvent triggered an expand/collapse in - * the renderer, false otherwise. - * - * @see #isHitDetectionFromProcessMouse() - */ - public boolean hitHandleDetectionFromProcessMouse(MouseEvent e) { - if (!isHitDetectionFromProcessMouse()) - return false; - int col = columnAtPoint(e.getPoint()); - return ((col >= 0) && expandOrCollapseNode(columnAtPoint(e - .getPoint()), e)); - } - - /** - * Complete editing if collapsed/expanded. - *

                - * - * Is: first try to stop editing before falling back to cancel. - *

                - * This is part of fix for #730-swingx - editingStopped not always - * called. The other part is to call this from the renderer before - * expansion related state has changed. - *

                - * - * Was: any editing is always cancelled. - *

                - * This is a rude fix to #120-jdnc: data corruption on collapse/expand - * if editing. This is called from the renderer after expansion related - * state has changed. - * - */ - protected void completeEditing() { - // JW: fix for 1126 - ignore complete if not editing hierarchical - // reverted - introduced regression .... for details please see the bug report - if (isEditing()) { // && isHierarchical(getEditingColumn())) { - boolean success = getCellEditor().stopCellEditing(); - if (!success) { - getCellEditor().cancelCellEditing(); - } - } - } - - /** - * Tricksery to make the tree expand/collapse. - *

                - * - * This might be - indirectly - called from one of two places: - *

                  - *
                1. editCellAt: original, stable but buggy (#332, #222) the table's - * own selection had been changed due to the click before even entering - * into editCellAt so all tree selection state is lost. - * - *
                2. processMouseEvent: the idea is to catch the mouseEvent, check - * if it triggered an expanded/collapsed, consume and return if so or - * pass to super if not. - *
                - * - *

                - * widened access for testing ... - * - * - * @param column the column index under the event, if any. - * @param e the event which might trigger a expand/collapse. - * - * @return this methods evaluation as to whether the event triggered a - * expand/collaps - */ - protected boolean expandOrCollapseNode(int column, EventObject e) { - if (!isHierarchical(column)) - return false; - if (!mightBeExpansionTrigger(e)) - return false; - boolean changedExpansion = false; - MouseEvent me = (MouseEvent) e; - if (hackAroundDragEnabled(me)) { - /* - * Hack around #168-jdnc: dirty little hack mentioned in the - * forum discussion about the issue: fake a mousePressed if drag - * enabled. The usability is slightly impaired because the - * expand/collapse is effectively triggered on released only - * (drag system intercepts and consumes all other). - */ - me = new MouseEvent((Component) me.getSource(), - MouseEvent.MOUSE_PRESSED, me.getWhen(), me - .getModifiers(), me.getX(), me.getY(), me - .getClickCount(), me.isPopupTrigger()); - - } - // If the modifiers are not 0 (or the left mouse button), - // tree may try and toggle the selection, and table - // will then try and toggle, resulting in the - // selection remaining the same. To avoid this, we - // only dispatch when the modifiers are 0 (or the left mouse - // button). - if (me.getModifiers() == 0 - || me.getModifiers() == InputEvent.BUTTON1_MASK) { - MouseEvent pressed = new MouseEvent(renderer, me.getID(), me - .getWhen(), me.getModifiers(), me.getX() - - getCellRect(0, column, false).x, me.getY(), me - .getClickCount(), me.isPopupTrigger()); - renderer.dispatchEvent(pressed); - // For Mac OS X, we need to dispatch a MOUSE_RELEASED as well - MouseEvent released = new MouseEvent(renderer, - MouseEvent.MOUSE_RELEASED, pressed - .getWhen(), pressed.getModifiers(), pressed - .getX(), pressed.getY(), pressed - .getClickCount(), pressed.isPopupTrigger()); - renderer.dispatchEvent(released); - if (expansionChangedFlag) { - changedExpansion = true; - } - } - expansionChangedFlag = false; - return changedExpansion; - } - - protected boolean mightBeExpansionTrigger(EventObject e) { - if (!(e instanceof MouseEvent)) return false; - MouseEvent me = (MouseEvent) e; - if (!SwingUtilities.isLeftMouseButton(me)) return false; - return me.getID() == MouseEvent.MOUSE_PRESSED; - } - - /** - * called from the renderer's setExpandedPath after - * all expansion-related updates happend. - * - */ - protected void expansionChanged() { - expansionChangedFlag = true; - } - - } - - /** - * - * Note: currently this class looks a bit funny (only overriding - * the hit decision method). That's because the "experimental" code - * as of the last round moved to stable. But I expect that there's more - * to come, so I leave it here. - * - *

                  - *
                1. hit handle detection in processMouse - *
                - */ - public class TreeTableHackerExt extends TreeTableHacker { - - - /** - * Here: returns true. - * @inheritDoc - */ - @Override - protected boolean isHitDetectionFromProcessMouse() { - return true; - } - - } - - /** - * Patch for #471-swingx: no selection on click in hierarchical column - * if outside of node-text. Mar 2007. - *

                - * - * Note: with 1.6 the expansion control was broken even with the "normal extended" - * TreeTableHackerExt. When fixing that (renderer must have correct width for - * BasicTreeUI since 1.6) took a look into why this didn't work and made it work. - * So, now this is bidi-compliant. - * - * @author tiberiu@dev.java.net - */ - public class TreeTableHackerExt2 extends TreeTableHackerExt { - @Override - protected boolean expandOrCollapseNode(int column, EventObject e) { - if (!isHierarchical(column)) - return false; - if (!mightBeExpansionTrigger(e)) - return false; - boolean changedExpansion = false; - MouseEvent me = (MouseEvent) e; - if (hackAroundDragEnabled(me)) { - /* - * Hack around #168-jdnc: dirty little hack mentioned in the - * forum discussion about the issue: fake a mousePressed if drag - * enabled. The usability is slightly impaired because the - * expand/collapse is effectively triggered on released only - * (drag system intercepts and consumes all other). - */ - me = new MouseEvent((Component) me.getSource(), - MouseEvent.MOUSE_PRESSED, me.getWhen(), me - .getModifiers(), me.getX(), me.getY(), me - .getClickCount(), me.isPopupTrigger()); - } - // If the modifiers are not 0 (or the left mouse button), - // tree may try and toggle the selection, and table - // will then try and toggle, resulting in the - // selection remaining the same. To avoid this, we - // only dispatch when the modifiers are 0 (or the left mouse - // button). - if (me.getModifiers() == 0 - || me.getModifiers() == InputEvent.BUTTON1_MASK) { - // compute where the mouse point is relative to the tree - // as renderer, that the x coordinate translated to be relative - // to the column x-position - Point treeMousePoint = getTreeMousePoint(column, me); - int treeRow = renderer.getRowForLocation(treeMousePoint.x, - treeMousePoint.y); - int row = 0; - // mouse location not inside the node content - if (treeRow < 0) { - // get the row for mouse location - row = renderer.getClosestRowForLocation(treeMousePoint.x, - treeMousePoint.y); - // check against actual bounds of the row - Rectangle bounds = renderer.getRowBounds(row); - if (bounds == null) { - row = -1; - } else { - // check if the mouse location is "leading" - // relative to the content box - // JW: fix issue 1168-swingx: expansion control broken in - if (getComponentOrientation().isLeftToRight()) { - // this is LToR only - if ((bounds.y + bounds.height < treeMousePoint.y) - || bounds.x > treeMousePoint.x) { - row = -1; - } - } else { - if ((bounds.y + bounds.height < treeMousePoint.y) - || bounds.x + bounds.width < treeMousePoint.x) { - row = -1; - } - - } - } - // make sure the expansionChangedFlag is set to false for - // the case that up in the tree nothing happens - expansionChangedFlag = false; - } - - if ((treeRow >= 0) // if in content box - || ((treeRow < 0) && (row < 0))) {// or outside but leading - if (treeRow >= 0) { //Issue 561-swingx: in content box, update column lead to focus - getColumnModel().getSelectionModel().setLeadSelectionIndex(column); - } - // dispatch the translated event to the tree - // which either triggers a tree selection - // or expands/collapses a node - MouseEvent pressed = new MouseEvent(renderer, me.getID(), - me.getWhen(), me.getModifiers(), treeMousePoint.x, - treeMousePoint.y, me.getClickCount(), me - .isPopupTrigger()); - renderer.dispatchEvent(pressed); - // For Mac OS X, we need to dispatch a MOUSE_RELEASED as - // well - MouseEvent released = new MouseEvent(renderer, - MouseEvent.MOUSE_RELEASED, pressed - .getWhen(), pressed.getModifiers(), pressed - .getX(), pressed.getY(), pressed - .getClickCount(), pressed.isPopupTrigger()); - renderer.dispatchEvent(released); - // part of 561-swingx: if focus elsewhere and dispatching the - // mouseEvent the focus doesn't move from elsewhere - // still doesn't help in very first click after startup - // probably lead of row selection event not correctly updated - // on synch from treeSelectionModel - requestFocusInWindow(); - } - if (expansionChangedFlag) { - changedExpansion = true; - } else { - } - } - expansionChangedFlag = false; - return changedExpansion; - } - - /** - * This is a patch provided for Issue #980-swingx which should - * improve the bidi-compliance. Still doesn't work in our - * visual tests...

                - * - * Problem was not in the translation to renderer coordinate system, - * it was in the method itself: the check whether we are "beyond" the - * cell content box is bidi-dependent. Plus (since 1.6), width of - * renderer must be > 0. - * - * - * @param column the column index under the event, if any. - * @param e the event which might trigger a expand/collapse. - * @return the Point adjusted for bidi - */ - protected Point getTreeMousePoint(int column, MouseEvent me) { - // could inline as it wasn't the place to fix for broken RToL - return new Point(me.getX() - - getCellRect(0, column, false).x, me.getY()); - } - } - /** - * A more (or less, depending in pov :-) aggressiv hacker. Compared - * to super, it dispatches less events to address open issues.

                - * - * Issue #474-swingx: double click should start edit (not expand/collapse) - * changed mightBeExpansionTrigger to filter out clickCounts > 1 - *

                - * Issue #875-swingx: cell selection mode - * changed the dispatch to do so only if mouse event outside content - * box and leading - *

                - * Issue #1169-swingx: remove 1.5 dnd hack - * removed the additional dispatch here and - * changed in the implementation of hackAroundDragEnabled - * to no longer look for the system property (it's useless even if set) - * - * @author tiberiu@dev.java.net - */ - public class TreeTableHackerExt3 extends TreeTableHackerExt2 { - @Override - protected boolean expandOrCollapseNode(int column, EventObject e) { - if (!isHierarchical(column)) - return false; - if (!mightBeExpansionTrigger(e)) - return false; - boolean changedExpansion = false; - MouseEvent me = (MouseEvent) e; - // If the modifiers are not 0 (or the left mouse button), - // tree may try and toggle the selection, and table - // will then try and toggle, resulting in the - // selection remaining the same. To avoid this, we - // only dispatch when the modifiers are 0 (or the left mouse - // button). - if (me.getModifiers() == 0 - || me.getModifiers() == InputEvent.BUTTON1_MASK) { - // compute where the mouse point is relative to the tree - // as renderer, that the x coordinate translated to be relative - // to the column x-position - Point treeMousePoint = getTreeMousePoint(column, me); - int treeRow = renderer.getRowForLocation(treeMousePoint.x, - treeMousePoint.y); - int row = 0; - // mouse location not inside the node content - if (treeRow < 0) { - // get the row for mouse location - row = renderer.getClosestRowForLocation(treeMousePoint.x, - treeMousePoint.y); - // check against actual bounds of the row - Rectangle bounds = renderer.getRowBounds(row); - if (bounds == null) { - row = -1; - } else { - // check if the mouse location is "leading" - // relative to the content box - // JW: fix issue 1168-swingx: expansion control broken in - if (getComponentOrientation().isLeftToRight()) { - // this is LToR only - if ((bounds.y + bounds.height < treeMousePoint.y) - || bounds.x > treeMousePoint.x) { - row = -1; - } - } else { - if ((bounds.y + bounds.height < treeMousePoint.y) - || bounds.x + bounds.width < treeMousePoint.x) { - row = -1; - } - - } - } - } - // make sure the expansionChangedFlag is set to false for - // the case that up in the tree nothing happens - expansionChangedFlag = false; - - if ((treeRow < 0) && (row < 0)) {// outside and leading - // dispatch the translated event to the tree - // which either triggers a tree selection - // or expands/collapses a node - MouseEvent pressed = new MouseEvent(renderer, me.getID(), - me.getWhen(), me.getModifiers(), treeMousePoint.x, - treeMousePoint.y, me.getClickCount(), me - .isPopupTrigger()); - renderer.dispatchEvent(pressed); - // For Mac OS X, we need to dispatch a MOUSE_RELEASED as - // well - MouseEvent released = new MouseEvent(renderer, - MouseEvent.MOUSE_RELEASED, pressed - .getWhen(), pressed.getModifiers(), pressed - .getX(), pressed.getY(), pressed - .getClickCount(), pressed.isPopupTrigger()); - renderer.dispatchEvent(released); - // part of 561-swingx: if focus elsewhere and dispatching the - // mouseEvent the focus doesn't move from elsewhere - // still doesn't help in very first click after startup - // probably lead of row selection event not correctly updated - // on synch from treeSelectionModel - requestFocusInWindow(); - } - if (expansionChangedFlag) { - changedExpansion = true; - } else { - } - } - expansionChangedFlag = false; - return changedExpansion; - } - /** - * Overridden to exclude clickcounts > 1. - */ - @Override - protected boolean mightBeExpansionTrigger(EventObject e) { - if (!(e instanceof MouseEvent)) return false; - MouseEvent me = (MouseEvent) e; - if (!SwingUtilities.isLeftMouseButton(me)) return false; - if (me.getClickCount() > 1) return false; - return me.getID() == MouseEvent.MOUSE_PRESSED; - } - - } - - /** - * Decides whether we want to apply the hack for #168-jdnc. here: returns - * true if dragEnabled() and a client property with key DRAG_HACK_FLAG_KEY - * has a value of boolean true.

                - * - * Note: this is updated for 1.6, as the intermediate system property - * for enabled drag support is useless now (it's the default) - * - * @param me the mouseEvent that triggered a editCellAt - * @return true if the hack should be applied. - */ - protected boolean hackAroundDragEnabled(MouseEvent me) { - Boolean dragHackFlag = (Boolean) getClientProperty(DRAG_HACK_FLAG_KEY); - return getDragEnabled() && Boolean.TRUE.equals(dragHackFlag); - } - - /** - * Overridden to provide a workaround for BasicTableUI anomaly. Make sure - * the UI never tries to resize the editor. The UI currently uses different - * techniques to paint the renderers and editors. So, overriding setBounds() - * is not the right thing to do for an editor. Returning -1 for the - * editing row in this case, ensures the editor is never painted. - * - * {@inheritDoc} - */ - @Override - public int getEditingRow() { - if (editingRow == -1) return -1; - return isHierarchical(editingColumn) ? -1 : editingRow; - } - - /** - * Returns the actual row that is editing as getEditingRow - * will always return -1. - */ - private int realEditingRow() { - return editingRow; - } - - /** - * Sets the data model for this JXTreeTable to the specified - * {@link TreeTableModel}. The same data model - * may be shared by any number of JXTreeTable instances. - * - * @param treeModel data model for this JXTreeTable - */ - public void setTreeTableModel(TreeTableModel treeModel) { - TreeTableModel old = getTreeTableModel(); -// boolean rootVisible = isRootVisible(); -// setRootVisible(false); - renderer.setModel(treeModel); -// setRootVisible(rootVisible); - - firePropertyChange("treeTableModel", old, getTreeTableModel()); - } - - /** - * Returns the underlying TreeTableModel for this JXTreeTable. - * - * @return the underlying TreeTableModel for this JXTreeTable - */ - public TreeTableModel getTreeTableModel() { - return (TreeTableModel) renderer.getModel(); - } - - /** - *

                Overrides superclass version to make sure that the specified - * {@link TableModel} is compatible with JXTreeTable before - * invoking the inherited version.

                - * - *

                Because JXTreeTable internally adapts an - * {@link TreeTableModel} to make it a compatible - * TableModel, this method should never be called directly. Use - * {@link #setTreeTableModel(TreeTableModel) setTreeTableModel} instead.

                - * - *

                While it is possible to obtain a reference to this adapted - * version of the TableModel by calling {@link JTable#getModel()}, - * any attempt to call setModel() with that adapter will fail because - * the adapter might have been bound to a different JXTreeTable instance. If - * you want to extract the underlying TreeTableModel, which, by the way, - * can be shared, use {@link #getTreeTableModel() getTreeTableModel} - * instead

                . - * - * @param tableModel must be a TreeTableModelAdapter - * @throws IllegalArgumentException if the specified tableModel is not an - * instance of TreeTableModelAdapter - */ - @Override - public final void setModel(TableModel tableModel) { // note final keyword - if (tableModel instanceof TreeTableModelAdapter) { - if (((TreeTableModelAdapter) tableModel).getTreeTable() == null) { - // Passing the above test ensures that this method is being - // invoked either from JXTreeTable/JTable constructor or from - // setTreeTableModel(TreeTableModel) - super.setModel(tableModel); // invoke superclass version - - ((TreeTableModelAdapter) tableModel).bind(this); // permanently bound - // Once a TreeTableModelAdapter is bound to any JXTreeTable instance, - // invoking JXTreeTable.setModel() with that adapter will throw an - // IllegalArgumentException, because we really want to make sure - // that a TreeTableModelAdapter is NOT shared by another JXTreeTable. - } - else { - throw new IllegalArgumentException("model already bound"); - } - } - else { - throw new IllegalArgumentException("unsupported model type"); - } - } - - - - @Override - public void tableChanged(TableModelEvent e) { - if (isStructureChanged(e) || isUpdate(e)) { - super.tableChanged(e); - } else { - resizeAndRepaint(); - } - } - - /** - * Throws UnsupportedOperationException because variable height rows are - * not supported. - * - * @param row ignored - * @param rowHeight ignored - * @throws UnsupportedOperationException because variable height rows are - * not supported - */ - @Override - public final void setRowHeight(int row, int rowHeight) { - throw new UnsupportedOperationException("variable height rows not supported"); - } - - /** - * Sets the row height for this JXTreeTable and forwards the - * row height to the renderering tree. - * - * @param rowHeight height of a row. - */ - @Override - public void setRowHeight(int rowHeight) { - super.setRowHeight(rowHeight); - adjustTreeRowHeight(getRowHeight()); - } - - /** - * Forwards tableRowHeight to tree. - * - * @param tableRowHeight height of a row. - */ - protected void adjustTreeRowHeight(int tableRowHeight) { - if (renderer != null && renderer.getRowHeight() != tableRowHeight) { - renderer.setRowHeight(tableRowHeight); - } - } - - /** - * Forwards treeRowHeight to table. This is for completeness only: the - * rendering tree is under our total control, so we don't expect - * any external call to tree.setRowHeight. - * - * @param treeRowHeight height of a row. - */ - protected void adjustTableRowHeight(int treeRowHeight) { - if (getRowHeight() != treeRowHeight) { - adminSetRowHeight(treeRowHeight); - } - } - - /** - * {@inheritDoc}

                - * - * Overridden to adjust the renderer's size. - */ - @Override - public void columnMarginChanged(ChangeEvent e) { - super.columnMarginChanged(e); - adjustTreeBounds(); - } - - /** - * Forces the renderer to resize for fitting into hierarchical column. - */ - private void adjustTreeBounds() { - if (renderer != null) { - renderer.setBounds(0, 0, 0, 0); - } - } - - /** - *

                Overridden to ensure that private renderer state is kept in sync with the - * state of the component. Calls the inherited version after performing the - * necessary synchronization. If you override this method, make sure you call - * this version from your version of this method.

                - * - *

                This version maps the selection mode used by the renderer to match the - * selection mode specified for the table. Specifically, the modes are mapped - * as follows: - *

                -     *  ListSelectionModel.SINGLE_INTERVAL_SELECTION: TreeSelectionModel.CONTIGUOUS_TREE_SELECTION;
                -     *  ListSelectionModel.MULTIPLE_INTERVAL_SELECTION: TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION;
                -     *  any other (default): TreeSelectionModel.SINGLE_TREE_SELECTION;
                -     * 
                - * - * {@inheritDoc} - * - * @param mode any of the table selection modes - */ - @Override - public void setSelectionMode(int mode) { - if (renderer != null) { - switch (mode) { - case ListSelectionModel.SINGLE_INTERVAL_SELECTION: { - renderer.getSelectionModel().setSelectionMode( - TreeSelectionModel.CONTIGUOUS_TREE_SELECTION); - break; - } - case ListSelectionModel.MULTIPLE_INTERVAL_SELECTION: { - renderer.getSelectionModel().setSelectionMode( - TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION); - break; - } - default: { - renderer.getSelectionModel().setSelectionMode( - TreeSelectionModel.SINGLE_TREE_SELECTION); - break; - } - } - } - super.setSelectionMode(mode); - } - - /** - * {@inheritDoc}

                - * - * Overridden to decorate the tree's renderer after calling super. - * At that point, it is only the tree itself that has been decorated. - * - * @param renderer the TableCellRenderer to prepare - * @param row the row of the cell to render, where 0 is the first row - * @param column the column of the cell to render, where 0 is the first column - * @return the Component used as a stamp to render the specified cell - * - * @see #applyRenderer(Component, ComponentAdapter) - */ - @Override - public Component prepareRenderer(TableCellRenderer renderer, int row, - int column) { - Component component = super.prepareRenderer(renderer, row, column); - return applyRenderer(component, getComponentAdapter(row, column)); - } - - /** - * Performs configuration of the tree's renderer if the adapter's column is - * the hierarchical column, does nothing otherwise. - *

                - * - * Note: this is legacy glue if the treeCellRenderer is of type - * DefaultTreeCellRenderer. In that case the renderer's - * background/foreground/Non/Selection colors are set to the tree's - * background/foreground depending on the adapter's selection state. Does - * nothing if the treeCellRenderer is backed by a ComponentProvider. - * - * @param component the rendering component - * @param adapter component data adapter - * @throws NullPointerException if the specified component or adapter is - * null - */ - protected Component applyRenderer(Component component, - ComponentAdapter adapter) { - if (component == null) { - throw new IllegalArgumentException("null component"); - } - if (adapter == null) { - throw new IllegalArgumentException("null component data adapter"); - } - - if (isHierarchical(adapter.column)) { - // After all decorators have been applied, make sure that relevant - // attributes of the table cell renderer are applied to the - // tree cell renderer before the hierarchical column is rendered! - TreeCellRenderer tcr = renderer.getCellRenderer(); - if (tcr instanceof JXTree.DelegatingRenderer) { - tcr = ((JXTree.DelegatingRenderer) tcr).getDelegateRenderer(); - - } - if (tcr instanceof DefaultTreeCellRenderer) { - - DefaultTreeCellRenderer dtcr = ((DefaultTreeCellRenderer) tcr); - // this effectively overwrites the dtcr settings - if (adapter.isSelected()) { - dtcr.setTextSelectionColor(component.getForeground()); - dtcr.setBackgroundSelectionColor(component.getBackground()); - } else { - dtcr.setTextNonSelectionColor(component.getForeground()); - dtcr.setBackgroundNonSelectionColor(component - .getBackground()); - } - } - } - return component; - } - - /** - * Sets the specified TreeCellRenderer as the Tree cell renderer. - * - * @param cellRenderer to use for rendering tree cells. - */ - public void setTreeCellRenderer(TreeCellRenderer cellRenderer) { - if (renderer != null) { - renderer.setCellRenderer(cellRenderer); - } - } - - public TreeCellRenderer getTreeCellRenderer() { - return renderer.getCellRenderer(); - } - - /** - * {@inheritDoc}

                - * - * Overridden to special-case the hierarchical column. - */ - @Override - public String getToolTipText(MouseEvent event) { - int column = columnAtPoint(event.getPoint()); - if (column >= 0 && isHierarchical(column)) { - int row = rowAtPoint(event.getPoint()); - return renderer.getToolTipText(event, row, column); - } - return super.getToolTipText(event); - } - - /** - * {@inheritDoc}

                - * - * Overridden to set the fixed tooltip text to the tree that is rendering the - * hierarchical column. - */ - @Override - public void setToolTipText(String text) { - super.setToolTipText(text); - renderer.setToolTipText(text); - } - - /** - * Sets the specified icon as the icon to use for rendering collapsed nodes. - * - * @param icon to use for rendering collapsed nodes - * - * @see JXTree#setCollapsedIcon(Icon) - */ - public void setCollapsedIcon(Icon icon) { - renderer.setCollapsedIcon(icon); - } - - /** - * Sets the specified icon as the icon to use for rendering expanded nodes. - * - * @param icon to use for rendering expanded nodes - * - * @see JXTree#setExpandedIcon(Icon) - */ - public void setExpandedIcon(Icon icon) { - renderer.setExpandedIcon(icon); - } - - /** - * Sets the specified icon as the icon to use for rendering open container nodes. - * - * @param icon to use for rendering open nodes - * - * @see JXTree#setOpenIcon(Icon) - */ - public void setOpenIcon(Icon icon) { - renderer.setOpenIcon(icon); - } - - /** - * Sets the specified icon as the icon to use for rendering closed container nodes. - * - * @param icon to use for rendering closed nodes - * - * @see JXTree#setClosedIcon(Icon) - */ - public void setClosedIcon(Icon icon) { - renderer.setClosedIcon(icon); - } - - /** - * Sets the specified icon as the icon to use for rendering leaf nodes. - * - * @param icon to use for rendering leaf nodes - * - * @see JXTree#setLeafIcon(Icon) - */ - public void setLeafIcon(Icon icon) { - renderer.setLeafIcon(icon); - } - - /** - * Property to control whether per-tree icons should be - * copied to the renderer on setTreeCellRenderer.

                - * - * The default value is false. - * - * @param overwrite a boolean to indicate if the per-tree Icons should - * be copied to the new renderer on setTreeCellRenderer. - * - * @see #isOverwriteRendererIcons() - * @see #setLeafIcon(Icon) - * @see #setOpenIcon(Icon) - * @see #setClosedIcon(Icon) - * @see JXTree#setOverwriteRendererIcons(boolean) - */ - public void setOverwriteRendererIcons(boolean overwrite) { - renderer.setOverwriteRendererIcons(overwrite); - } - - - /** - * Returns a boolean indicating whether the per-tree icons should be - * copied to the renderer on setTreeCellRenderer. - * - * @return true if a TreeCellRenderer's icons will be overwritten with the - * tree's Icons, false if the renderer's icons will be unchanged. - * - * @see #setOverwriteRendererIcons(boolean) - * @see #setLeafIcon(Icon) - * @see #setOpenIcon(Icon) - * @see #setClosedIcon(Icon) - * @see JXTree#isOverwriteRendererIcons() - * - */ - public boolean isOverwriteRendererIcons() { - return renderer.isOverwriteRendererIcons(); - } - - /** - * Overridden to ensure that private renderer state is kept in sync with the - * state of the component. Calls the inherited version after performing the - * necessary synchronization. If you override this method, make sure you call - * this version from your version of this method. - */ - @Override - public void clearSelection() { - if (renderer != null) { - renderer.clearSelection(); - } - super.clearSelection(); - } - - /** - * Collapses all nodes in the treetable. - */ - public void collapseAll() { - renderer.collapseAll(); - } - - /** - * Expands all nodes in the treetable. - */ - public void expandAll() { - renderer.expandAll(); - } - - /** - * Collapses the node at the specified path in the treetable. - * - * @param path path of the node to collapse - */ - public void collapsePath(TreePath path) { - renderer.collapsePath(path); - } - - /** - * Expands the the node at the specified path in the treetable. - * - * @param path path of the node to expand - */ - public void expandPath(TreePath path) { - renderer.expandPath(path); - } - - /** - * Makes sure all the path components in path are expanded (except - * for the last path component) and scrolls so that the - * node identified by the path is displayed. Only works when this - * JTree is contained in a JScrollPane. - * - * (doc copied from JTree) - * - * PENDING: JW - where exactly do we want to scroll? Here: the scroll - * is in vertical direction only. Might need to show the tree column? - * - * @param path the TreePath identifying the node to - * bring into view - */ - public void scrollPathToVisible(TreePath path) { - renderer.scrollPathToVisible(path); -// if (path == null) return; -// renderer.makeVisible(path); -// int row = getRowForPath(path); -// scrollRowToVisible(row); - } - - - /** - * Collapses the row in the treetable. If the specified row index is - * not valid, this method will have no effect. - */ - public void collapseRow(int row) { - renderer.collapseRow(row); - } - - /** - * Expands the specified row in the treetable. If the specified row index is - * not valid, this method will have no effect. - */ - public void expandRow(int row) { - renderer.expandRow(row); - } - - - /** - * Returns true if the value identified by path is currently viewable, which - * means it is either the root or all of its parents are expanded. Otherwise, - * this method returns false. - * - * @return true, if the value identified by path is currently viewable; - * false, otherwise - */ - public boolean isVisible(TreePath path) { - return renderer.isVisible(path); - } - - /** - * Returns true if the node identified by path is currently expanded. - * Otherwise, this method returns false. - * - * @param path path - * @return true, if the value identified by path is currently expanded; - * false, otherwise - */ - public boolean isExpanded(TreePath path) { - return renderer.isExpanded(path); - } - - /** - * Returns true if the node at the specified display row is currently expanded. - * Otherwise, this method returns false. - * - * @param row row - * @return true, if the node at the specified display row is currently expanded. - * false, otherwise - */ - public boolean isExpanded(int row) { - return renderer.isExpanded(row); - } - - /** - * Returns true if the node identified by path is currently collapsed, - * this will return false if any of the values in path are currently not - * being displayed. - * - * @param path path - * @return true, if the value identified by path is currently collapsed; - * false, otherwise - */ - public boolean isCollapsed(TreePath path) { - return renderer.isCollapsed(path); - } - - /** - * Returns true if the node at the specified display row is collapsed. - * - * @param row row - * @return true, if the node at the specified display row is currently collapsed. - * false, otherwise - */ - public boolean isCollapsed(int row) { - return renderer.isCollapsed(row); - } - - - /** - * Returns an Enumeration of the descendants of the - * path parent that - * are currently expanded. If parent is not currently - * expanded, this will return null. - * If you expand/collapse nodes while - * iterating over the returned Enumeration - * this may not return all - * the expanded paths, or may return paths that are no longer expanded. - * - * @param parent the path which is to be examined - * @return an Enumeration of the descendents of - * parent, or null if - * parent is not currently expanded - */ - - public Enumeration getExpandedDescendants(TreePath parent) { - return renderer.getExpandedDescendants(parent); - } - - - /** - * Returns the TreePath for a given x,y location. - * - * @param x x value - * @param y y value - * - * @return the TreePath for the givern location. - */ - public TreePath getPathForLocation(int x, int y) { - int row = rowAtPoint(new Point(x,y)); - if (row == -1) { - return null; - } - return renderer.getPathForRow(row); - } - - /** - * Returns the TreePath for a given row. - * - * @param row - * - * @return the TreePath for the given row. - */ - public TreePath getPathForRow(int row) { - return renderer.getPathForRow(row); - } - - /** - * Returns the row for a given TreePath. - * - * @param path - * @return the row for the given TreePath. - */ - public int getRowForPath(TreePath path) { - return renderer.getRowForPath(path); - } - -//------------------------------ exposed Tree properties - - /** - * Determines whether or not the root node from the TreeModel is visible. - * - * @param visible true, if the root node is visible; false, otherwise - */ - public void setRootVisible(boolean visible) { - renderer.setRootVisible(visible); - // JW: the revalidate forces the root to appear after a - // toggling a visible from an initially invisible root. - // JTree fires a propertyChange on the ROOT_VISIBLE_PROPERTY - // BasicTreeUI reacts by (ultimately) calling JTree.treeDidChange - // which revalidate the tree part. - // Might consider to listen for the propertyChange (fired only if there - // actually was a change) instead of revalidating unconditionally. - revalidate(); - repaint(); - } - - /** - * Returns true if the root node of the tree is displayed. - * - * @return true if the root node of the tree is displayed - */ - public boolean isRootVisible() { - return renderer.isRootVisible(); - } - - - /** - * Sets the value of the scrollsOnExpand property for the tree - * part. This property specifies whether the expanded paths should be scrolled - * into view. In a look and feel in which a tree might not need to scroll - * when expanded, this property may be ignored. - * - * @param scroll true, if expanded paths should be scrolled into view; - * false, otherwise - */ - public void setScrollsOnExpand(boolean scroll) { - renderer.setScrollsOnExpand(scroll); - } - - /** - * Returns the value of the scrollsOnExpand property. - * - * @return the value of the scrollsOnExpand property - */ - public boolean getScrollsOnExpand() { - return renderer.getScrollsOnExpand(); - } - - /** - * Sets the value of the showsRootHandles property for the tree - * part. This property specifies whether the node handles should be displayed. - * If handles are not supported by a particular look and feel, this property - * may be ignored. - * - * @param visible true, if root handles should be shown; false, otherwise - */ - public void setShowsRootHandles(boolean visible) { - renderer.setShowsRootHandles(visible); - repaint(); - } - - /** - * Returns the value of the showsRootHandles property. - * - * @return the value of the showsRootHandles property - */ - public boolean getShowsRootHandles() { - return renderer.getShowsRootHandles(); - } - - /** - * Sets the value of the expandsSelectedPaths property for the tree - * part. This property specifies whether the selected paths should be expanded. - * - * @param expand true, if selected paths should be expanded; false, otherwise - */ - public void setExpandsSelectedPaths(boolean expand) { - renderer.setExpandsSelectedPaths(expand); - } - - /** - * Returns the value of the expandsSelectedPaths property. - * - * @return the value of the expandsSelectedPaths property - */ - public boolean getExpandsSelectedPaths() { - return renderer.getExpandsSelectedPaths(); - } - - - /** - * Returns the number of mouse clicks needed to expand or close a node. - * - * @return number of mouse clicks before node is expanded - */ - public int getToggleClickCount() { - return renderer.getToggleClickCount(); - } - - /** - * Sets the number of mouse clicks before a node will expand or close. - * The default is two. - * - * @param clickCount the number of clicks required to expand/collapse a node. - */ - public void setToggleClickCount(int clickCount) { - renderer.setToggleClickCount(clickCount); - } - - /** - * Returns true if the tree is configured for a large model. - * The default value is false. - * - * @return true if a large model is suggested - * @see #setLargeModel - */ - public boolean isLargeModel() { - return renderer.isLargeModel(); - } - - /** - * Specifies whether the UI should use a large model. - * (Not all UIs will implement this.)

                - * - * NOTE: this method is exposed for completeness - - * currently it's not recommended - * to use a large model because there are some issues - * (not yet fully understood), namely - * issue #25-swingx, and probably #270-swingx. - * - * @param newValue true to suggest a large model to the UI - */ - public void setLargeModel(boolean newValue) { - renderer.setLargeModel(newValue); - // JW: random method calling ... doesn't help -// renderer.treeDidChange(); -// revalidate(); -// repaint(); - } - -//------------------------------ exposed tree listeners - - /** - * Adds a listener for TreeExpansion events. - * - * @param tel a TreeExpansionListener that will be notified - * when a tree node is expanded or collapsed - */ - public void addTreeExpansionListener(TreeExpansionListener tel) { - getTreeExpansionBroadcaster().addTreeExpansionListener(tel); - } - - /** - * @return - */ - private TreeExpansionBroadcaster getTreeExpansionBroadcaster() { - if (treeExpansionBroadcaster == null) { - treeExpansionBroadcaster = new TreeExpansionBroadcaster(this); - renderer.addTreeExpansionListener(treeExpansionBroadcaster); - } - return treeExpansionBroadcaster; - } - - /** - * Removes a listener for TreeExpansion events. - * @param tel the TreeExpansionListener to remove - */ - public void removeTreeExpansionListener(TreeExpansionListener tel) { - if (treeExpansionBroadcaster == null) return; - treeExpansionBroadcaster.removeTreeExpansionListener(tel); - } - - /** - * Adds a listener for TreeSelection events. - * TODO (JW): redirect event source to this. - * - * @param tsl a TreeSelectionListener that will be notified - * when a tree node is selected or deselected - */ - public void addTreeSelectionListener(TreeSelectionListener tsl) { - renderer.addTreeSelectionListener(tsl); - } - - /** - * Removes a listener for TreeSelection events. - * @param tsl the TreeSelectionListener to remove - */ - public void removeTreeSelectionListener(TreeSelectionListener tsl) { - renderer.removeTreeSelectionListener(tsl); - } - - /** - * Adds a listener for TreeWillExpand events. - * TODO (JW): redirect event source to this. - * - * @param tel a TreeWillExpandListener that will be notified - * when a tree node will be expanded or collapsed - */ - public void addTreeWillExpandListener(TreeWillExpandListener tel) { - renderer.addTreeWillExpandListener(tel); - } - - /** - * Removes a listener for TreeWillExpand events. - * @param tel the TreeWillExpandListener to remove - */ - public void removeTreeWillExpandListener(TreeWillExpandListener tel) { - renderer.removeTreeWillExpandListener(tel); - } - - - /** - * Returns the selection model for the tree portion of the this treetable. - * - * @return selection model for the tree portion of the this treetable - */ - public TreeSelectionModel getTreeSelectionModel() { - return renderer.getSelectionModel(); // RG: Fix JDNC issue 41 - } - - /** - * Overriden to invoke supers implementation, and then, - * if the receiver is editing a Tree column, the editors bounds is - * reset. The reason we have to do this is because JTable doesn't - * think the table is being edited, as getEditingRow returns - * -1, and therefore doesn't automaticly resize the editor for us. - */ - @Override - public void sizeColumnsToFit(int resizingColumn) { - /** TODO: Review wrt doLayout() */ - super.sizeColumnsToFit(resizingColumn); - // rg:changed - if (getEditingColumn() != -1 && isHierarchical(editingColumn)) { - Rectangle cellRect = getCellRect(realEditingRow(), - getEditingColumn(), false); - Component component = getEditorComponent(); - component.setBounds(cellRect); - component.validate(); - } - } - - - /** - * Determines if the specified column is defined as the hierarchical column. - * - * @param column - * zero-based index of the column in view coordinates - * @return true if the column is the hierarchical column; false otherwise. - * @throws IllegalArgumentException - * if the column is less than 0 or greater than or equal to the - * column count - */ - public boolean isHierarchical(int column) { - if (column < 0 || column >= getColumnCount()) { - throw new IllegalArgumentException("column must be valid, was" + column); - } - - return (getHierarchicalColumn() == column); - } - - /** - * Returns the index of the hierarchical column. This is the column that is - * displayed as the tree. - * - * @return the index of the hierarchical column, -1 if there is - * no hierarchical column - * - */ - public int getHierarchicalColumn() { - return convertColumnIndexToView(((TreeTableModel) renderer.getModel()).getHierarchicalColumn()); - } - - /** - * {@inheritDoc} - */ - @Override - public TableCellRenderer getCellRenderer(int row, int column) { - if (isHierarchical(column)) { - return renderer; - } - - return super.getCellRenderer(row, column); - } - - /** - * {@inheritDoc} - */ - @Override - public TableCellEditor getCellEditor(int row, int column) { - if (isHierarchical(column)) { - return hierarchicalEditor; - } - - return super.getCellEditor(row, column); - } - - @Override - public void updateUI() { - super.updateUI(); - updateHierarchicalRendererEditor(); - } - - /** - * Updates Ui of renderer/editor for the hierarchical column. Need to do so - * manually, as not accessible by the default lookup. - */ - protected void updateHierarchicalRendererEditor() { - if (renderer != null) { - SwingUtilities.updateComponentTreeUI(renderer); - } - } - - /** - * {@inheritDoc}

                - * - * Overridden to message the tree directly if the column is the view index of - * the hierarchical column.

                - * - * PENDING JW: revisit once we switch to really using a table renderer. As is, it's - * a quick fix for #821-swingx: string rep for hierarchical column incorrect. - */ - @Override - public String getStringAt(int row, int column) { - if (isHierarchical(column)) { - return getHierarchicalStringAt(row); - } - return super.getStringAt(row, column); - } - - /** - * Returns the String representation of the hierarchical column at the given - * row.

                - * - * @param row the row index in view coordinates - * @return the string representation of the hierarchical column at the given row. - * - * @see #getStringAt(int, int) - */ - private String getHierarchicalStringAt(int row) { - return renderer.getStringAt(row); - } - - /** - * ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel - * to listen for changes in the ListSelectionModel it maintains. Once - * a change in the ListSelectionModel happens, the paths are updated - * in the DefaultTreeSelectionModel. - */ - class ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel { - /** Set to true when we are updating the ListSelectionModel. */ - protected boolean updatingListSelectionModel; - - public ListToTreeSelectionModelWrapper() { - super(); - getListSelectionModel().addListSelectionListener - (createListSelectionListener()); - } - - /** - * Returns the list selection model. ListToTreeSelectionModelWrapper - * listens for changes to this model and updates the selected paths - * accordingly. - */ - ListSelectionModel getListSelectionModel() { - return listSelectionModel; - } - - /** - * This is overridden to set updatingListSelectionModel - * and message super. This is the only place DefaultTreeSelectionModel - * alters the ListSelectionModel. - */ - @Override - public void resetRowSelection() { - if (!updatingListSelectionModel) { - updatingListSelectionModel = true; - try { - super.resetRowSelection(); - } - finally { - updatingListSelectionModel = false; - } - } - // Notice how we don't message super if - // updatingListSelectionModel is true. If - // updatingListSelectionModel is true, it implies the - // ListSelectionModel has already been updated and the - // paths are the only thing that needs to be updated. - } - - /** - * Creates and returns an instance of ListSelectionHandler. - */ - protected ListSelectionListener createListSelectionListener() { - return new ListSelectionHandler(); - } - - /** - * If updatingListSelectionModel is false, this will - * reset the selected paths from the selected rows in the list - * selection model. - */ - protected void updateSelectedPathsFromSelectedRows() { - if (!updatingListSelectionModel) { - updatingListSelectionModel = true; - try { - if (listSelectionModel.isSelectionEmpty()) { - clearSelection(); - } else { - // This is way expensive, ListSelectionModel needs an - // enumerator for iterating. - int min = listSelectionModel.getMinSelectionIndex(); - int max = listSelectionModel.getMaxSelectionIndex(); - - List paths = new ArrayList(); - for (int counter = min; counter <= max; counter++) { - if (listSelectionModel.isSelectedIndex(counter)) { - TreePath selPath = renderer.getPathForRow( - counter); - - if (selPath != null) { - paths.add(selPath); - } - } - } - setSelectionPaths(paths.toArray(new TreePath[paths.size()])); - // need to force here: usually the leadRow is adjusted - // in resetRowSelection which is disabled during this method - leadRow = leadIndex; - } - } - finally { - updatingListSelectionModel = false; - } - } - } - - /** - * Class responsible for calling updateSelectedPathsFromSelectedRows - * when the selection of the list changse. - */ - class ListSelectionHandler implements ListSelectionListener { - @Override - public void valueChanged(ListSelectionEvent e) { - if (!e.getValueIsAdjusting()) { - updateSelectedPathsFromSelectedRows(); - } - } - } - } - - /** - * - */ - protected static class TreeTableModelAdapter extends AbstractTableModel - implements TreeTableModelProvider { - private TreeModelListener treeModelListener; - private final JTree tree; // immutable - private JXTreeTable treeTable; // logically immutable - - /** - * Maintains a TreeTableModel and a JTree as purely implementation details. - * Developers can plug in any type of custom TreeTableModel through a - * JXTreeTable constructor or through setTreeTableModel(). - * - * @param tree TreeTableCellRenderer instantiated with the same model as - * the driving JXTreeTable's TreeTableModel. - * @throws IllegalArgumentException if a null tree argument is passed - */ - TreeTableModelAdapter(JTree tree) { - Contract.asNotNull(tree, "tree must not be null"); - - this.tree = tree; // need tree to implement getRowCount() - tree.getModel().addTreeModelListener(getTreeModelListener()); - tree.addTreeExpansionListener(new TreeExpansionListener() { - // Don't use fireTableRowsInserted() here; the selection model - // would get updated twice. - @Override - public void treeExpanded(TreeExpansionEvent event) { - updateAfterExpansionEvent(event); - } - - @Override - public void treeCollapsed(TreeExpansionEvent event) { - updateAfterExpansionEvent(event); - } - }); - tree.addPropertyChangeListener("model", new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - TreeTableModel model = (TreeTableModel) evt.getOldValue(); - model.removeTreeModelListener(getTreeModelListener()); - - model = (TreeTableModel) evt.getNewValue(); - model.addTreeModelListener(getTreeModelListener()); - - fireTableStructureChanged(); - } - }); - } - - /** - * updates the table after having received an TreeExpansionEvent.

                - * - * @param event the TreeExpansionEvent which triggered the method call. - */ - protected void updateAfterExpansionEvent(TreeExpansionEvent event) { - // moved to let the renderer handle directly -// treeTable.getTreeTableHacker().setExpansionChangedFlag(); - // JW: delayed fire leads to a certain sluggishness occasionally? - fireTableDataChanged(); - } - - /** - * Returns the JXTreeTable instance to which this TreeTableModelAdapter is - * permanently and exclusively bound. For use by - * {@link JXTreeTable#setModel(TableModel)}. - * - * @return JXTreeTable to which this TreeTableModelAdapter is permanently bound - */ - protected JXTreeTable getTreeTable() { - return treeTable; - } - - /** - * Immutably binds this TreeTableModelAdapter to the specified JXTreeTable. - * - * @param treeTable the JXTreeTable instance that this adapter is bound to. - */ - protected final void bind(JXTreeTable treeTable) { - // Suppress potentially subversive invocation! - // Prevent clearing out the deck for possible hijack attempt later! - if (treeTable == null) { - throw new IllegalArgumentException("null treeTable"); - } - - if (this.treeTable == null) { - this.treeTable = treeTable; - } - else { - throw new IllegalArgumentException("adapter already bound"); - } - } - - /** - * - * @inherited

                - * - * Implemented to return the the underlying TreeTableModel. - */ - @Override - public TreeTableModel getTreeTableModel() { - return (TreeTableModel) tree.getModel(); - } - - // Wrappers, implementing TableModel interface. - // TableModelListener management provided by AbstractTableModel superclass. - - @Override - public Class getColumnClass(int column) { - return getTreeTableModel().getColumnClass(column); - } - - @Override - public int getColumnCount() { - return getTreeTableModel().getColumnCount(); - } - - @Override - public String getColumnName(int column) { - return getTreeTableModel().getColumnName(column); - } - - @Override - public int getRowCount() { - return tree.getRowCount(); - } - - @Override - public Object getValueAt(int row, int column) { - // Issue #270-swingx: guard against invisible row - Object node = nodeForRow(row); - return node != null ? getTreeTableModel().getValueAt(node, column) : null; - } - - @Override - public boolean isCellEditable(int row, int column) { - // Issue #270-swingx: guard against invisible row - Object node = nodeForRow(row); - return node != null ? getTreeTableModel().isCellEditable(node, column) : false; - } - - @Override - public void setValueAt(Object value, int row, int column) { - // Issue #270-swingx: guard against invisible row - Object node = nodeForRow(row); - if (node != null) { - getTreeTableModel().setValueAt(value, node, column); - } - } - - protected Object nodeForRow(int row) { - // Issue #270-swingx: guard against invisible row - TreePath path = tree.getPathForRow(row); - return path != null ? path.getLastPathComponent() : null; - } - - /** - * @return TreeModelListener - */ - private TreeModelListener getTreeModelListener() { - if (treeModelListener == null) { - treeModelListener = new TreeModelListener() { - - @Override - public void treeNodesChanged(TreeModelEvent e) { -// LOG.info("got tree event: changed " + e); - delayedFireTableDataUpdated(e); - } - - // We use delayedFireTableDataChanged as we can - // not be guaranteed the tree will have finished processing - // the event before us. - @Override - public void treeNodesInserted(TreeModelEvent e) { - delayedFireTableDataChanged(e, 1); - } - - @Override - public void treeNodesRemoved(TreeModelEvent e) { -// LOG.info("got tree event: removed " + e); - delayedFireTableDataChanged(e, 2); - } - - @Override - public void treeStructureChanged(TreeModelEvent e) { - // ?? should be mapped to structureChanged -- JW - if (isTableStructureChanged(e)) { - delayedFireTableStructureChanged(); - } else { - delayedFireTableDataChanged(); - } - } - }; - } - - return treeModelListener; - } - - /** - * Decides if the given treeModel structureChanged should - * trigger a table structureChanged. Returns true if the - * source path is the root or null, false otherwise.

                - * - * PENDING: need to refine? "Marker" in Event-Object? - * - * @param e the TreeModelEvent received in the treeModelListener's - * treeStructureChanged - * @return a boolean indicating whether the given TreeModelEvent - * should trigger a structureChanged. - */ - private boolean isTableStructureChanged(TreeModelEvent e) { - if ((e.getTreePath() == null) || - (e.getTreePath().getParentPath() == null)) return true; - return false; - } - - /** - * Invokes fireTableDataChanged after all the pending events have been - * processed. SwingUtilities.invokeLater is used to handle this. - */ - private void delayedFireTableStructureChanged() { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - fireTableStructureChanged(); - } - }); - } - - /** - * Invokes fireTableDataChanged after all the pending events have been - * processed. SwingUtilities.invokeLater is used to handle this. - */ - private void delayedFireTableDataChanged() { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - fireTableDataChanged(); - } - }); - } - - /** - * Invokes fireTableDataChanged after all the pending events have been - * processed. SwingUtilities.invokeLater is used to handle this. - * Allowed event types: 1 for insert, 2 for delete - */ - private void delayedFireTableDataChanged(final TreeModelEvent tme, final int typeChange) { - if ((typeChange < 1 ) || (typeChange > 2)) - throw new IllegalArgumentException("Event type must be 1 or 2, was " + typeChange); - // expansion state before invoke may be different - // from expansion state in invoke - final boolean expanded = tree.isExpanded(tme.getTreePath()); - // quick test if tree throws for unrelated path. Seems like not. -// tree.getRowForPath(new TreePath("dummy")); - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - int indices[] = tme.getChildIndices(); - TreePath path = tme.getTreePath(); - // quick test to see if bailing out is an option -// if (false) { - if (indices != null) { - if (expanded) { // Dont bother to update if the parent - // node is collapsed - // indices must in ascending order, as per TreeEvent/Listener doc - int min = indices[0]; - int max = indices[indices.length - 1]; - int startingRow = tree.getRowForPath(path) + 1; - min = startingRow + min; - max = startingRow + max; - switch (typeChange) { - case 1: -// LOG.info("rows inserted: path " + path + "/" + min + "/" -// + max); - fireTableRowsInserted(min, max); - break; - case 2: -// LOG.info("rows deleted path " + path + "/" + min + "/" -// + max); - fireTableRowsDeleted(min, max); - break; - } - } else { - // not expanded - but change might effect appearance - // of parent - // Issue #82-swingx - int row = tree.getRowForPath(path); - // fix Issue #247-swingx: prevent accidental - // structureChanged - // for collapsed path - // in this case row == -1, which == - // TableEvent.HEADER_ROW - if (row >= 0) - fireTableRowsUpdated(row, row); - } - } else { // case where the event is fired to identify - // root. - fireTableDataChanged(); - } - } - }); - } - - /** - * This is used for updated only. PENDING: not necessary to delay? - * Updates are never structural changes which are the critical. - * - * @param tme - */ - protected void delayedFireTableDataUpdated(final TreeModelEvent tme) { - final boolean expanded = tree.isExpanded(tme.getTreePath()); - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - int indices[] = tme.getChildIndices(); - TreePath path = tme.getTreePath(); - if (indices != null) { - if (expanded) { // Dont bother to update if the parent - // node is collapsed - Object children[] = tme.getChildren(); - // can we be sure that children.length > 0? - // int min = tree.getRowForPath(path.pathByAddingChild(children[0])); - // int max = tree.getRowForPath(path.pathByAddingChild(children[children.length -1])); - int min = Integer.MAX_VALUE; - int max = Integer.MIN_VALUE; - for (int i = 0; i < indices.length; i++) { - Object child = children[i]; - TreePath childPath = path - .pathByAddingChild(child); - int index = tree.getRowForPath(childPath); - if (index < min) { - min = index; - } - if (index > max) { - max = index; - } - } -// LOG.info("Updated: parentPath/min/max" + path + "/" + min + "/" + max); - // JW: the index is occasionally - 1 - need further digging - fireTableRowsUpdated(Math.max(0, min), Math.max(0, max)); - } else { - // not expanded - but change might effect appearance - // of parent Issue #82-swingx - int row = tree.getRowForPath(path); - // fix Issue #247-swingx: prevent accidental structureChanged - // for collapsed path in this case row == -1, - // which == TableEvent.HEADER_ROW - if (row >= 0) - fireTableRowsUpdated(row, row); - } - } else { // case where the event is fired to identify - // root. - fireTableDataChanged(); - } - } - }); - - } - - } - - static class TreeTableCellRenderer extends JXTree implements - TableCellRenderer - // need to implement RolloverRenderer - // PENDING JW: method name clash rolloverRenderer.isEnabled and - // component.isEnabled .. don't extend, use? And change - // the method name in rolloverRenderer? - // commented - so doesn't show the rollover cursor. - // -// , RolloverRenderer - { - private PropertyChangeListener rolloverListener; - private Border cellBorder; - - // Force user to specify TreeTableModel instead of more general - // TreeModel - public TreeTableCellRenderer(TreeTableModel model) { - super(model); - putClientProperty("JTree.lineStyle", "None"); - setRootVisible(false); // superclass default is "true" - setShowsRootHandles(true); // superclass default is "false" - /** - * TODO: Support truncated text directly in - * DefaultTreeCellRenderer. - */ - // removed as fix for #769-swingx: defaults for treetable should be same as tree -// setOverwriteRendererIcons(true); -// setCellRenderer(new DefaultTreeRenderer()); - setCellRenderer(new ClippedTreeCellRenderer()); - } - - - /** - * {@inheritDoc}

                - * - * Overridden to hack around #766-swingx: cursor flickering in DnD - * when dragging over tree column. This is a core bug (#6700748) related - * to painting the rendering component on a CellRendererPane. A trick - * around is to let this return false.

                - * - * This implementation applies the trick, that is returns false always. - * The hack can be disabled by setting the treeTable's client property - * DROP_HACK_FLAG_KEY to Boolean.FALSE. - * - */ - @Override - public boolean isVisible() { - return shouldApplyDropHack() ? false : super.isVisible(); - } - - - /** - * Returns a boolean indicating whether the drop hack should be applied. - * - * @return a boolean indicating whether the drop hack should be applied. - */ - protected boolean shouldApplyDropHack() { - return !Boolean.FALSE.equals(treeTable.getClientProperty(DROP_HACK_FLAG_KEY)); - } - - - /** - * Hack around #297-swingx: tooltips shown at wrong row. - * - * The problem is that - due to much tricksery when rendering the tree - - * the given coordinates are rather useless. As a consequence, super - * maps to wrong coordinates. This takes over completely. - * - * PENDING: bidi? - * - * @param event the mouseEvent in treetable coordinates - * @param row the view row index - * @param column the view column index - * @return the tooltip as appropriate for the given row - */ - private String getToolTipText(MouseEvent event, int row, int column) { - if (row < 0) return null; - String toolTip = null; - TreeCellRenderer renderer = getCellRenderer(); - TreePath path = getPathForRow(row); - Object lastPath = path.getLastPathComponent(); - Component rComponent = renderer.getTreeCellRendererComponent - (this, lastPath, isRowSelected(row), - isExpanded(row), getModel().isLeaf(lastPath), row, - true); - - if(rComponent instanceof JComponent) { - Rectangle pathBounds = getPathBounds(path); - Rectangle cellRect = treeTable.getCellRect(row, column, false); - // JW: what we are after - // is the offset into the hierarchical column - // then intersect this with the pathbounds - Point mousePoint = event.getPoint(); - // translate to coordinates relative to cell - mousePoint.translate(-cellRect.x, -cellRect.y); - // translate horizontally to - mousePoint.translate(-pathBounds.x, 0); - // show tooltip only if over renderer? -// if (mousePoint.x < 0) return null; -// p.translate(-pathBounds.x, -pathBounds.y); - MouseEvent newEvent = new MouseEvent(rComponent, event.getID(), - event.getWhen(), - event.getModifiers(), - mousePoint.x, - mousePoint.y, -// p.x, p.y, - event.getClickCount(), - event.isPopupTrigger()); - - toolTip = ((JComponent)rComponent).getToolTipText(newEvent); - } - if (toolTip != null) { - return toolTip; - } - return getToolTipText(); - } - - /** - * {@inheritDoc}

                - * - * Overridden to not automatically de/register itself from/to the ToolTipManager. - * As rendering component it is not considered to be active in any way, so the - * manager must not listen. - */ - @Override - public void setToolTipText(String text) { - putClientProperty(TOOL_TIP_TEXT_KEY, text); - } - - /** - * Immutably binds this TreeTableModelAdapter to the specified JXTreeTable. - * For internal use by JXTreeTable only. - * - * @param treeTable the JXTreeTable instance that this renderer is bound to - */ - public final void bind(JXTreeTable treeTable) { - // Suppress potentially subversive invocation! - // Prevent clearing out the deck for possible hijack attempt later! - if (treeTable == null) { - throw new IllegalArgumentException("null treeTable"); - } - - if (this.treeTable == null) { - this.treeTable = treeTable; - // commented because still has issus -// bindRollover(); - } - else { - throw new IllegalArgumentException("renderer already bound"); - } - } - - /** - * Install rollover support. - * Not used - still has issues. - * - not bidi-compliant - * - no coordinate transformation for hierarchical column != 0 - * - method name clash enabled - * - keyboard triggered click unreliable (triggers the treetable) - * ... - */ - @SuppressWarnings("unused") - private void bindRollover() { - setRolloverEnabled(treeTable.isRolloverEnabled()); - treeTable.addPropertyChangeListener(getRolloverListener()); - } - - - /** - * @return - */ - private PropertyChangeListener getRolloverListener() { - if (rolloverListener == null) { - rolloverListener = createRolloverListener(); - } - return rolloverListener; - } - - /** - * Creates and returns a property change listener for - * table's rollover related properties. - * - * This implementation - * - Synchs the tree's rolloverEnabled - * - maps rollover cell from the table to the cell - * (still incomplete: first column only) - * - * @return - */ - protected PropertyChangeListener createRolloverListener() { - PropertyChangeListener l = new PropertyChangeListener() { - - @Override - public void propertyChange(PropertyChangeEvent evt) { - if ((treeTable == null) || (treeTable != evt.getSource())) - return; - if ("rolloverEnabled".equals(evt.getPropertyName())) { - setRolloverEnabled(((Boolean) evt.getNewValue()).booleanValue()); - } - if (RolloverProducer.ROLLOVER_KEY.equals(evt.getPropertyName())){ - rollover(evt); - } - } - - private void rollover(PropertyChangeEvent evt) { - boolean isHierarchical = isHierarchical((Point)evt.getNewValue()); - putClientProperty(evt.getPropertyName(), isHierarchical ? - new Point((Point) evt.getNewValue()) : null); - } - - private boolean isHierarchical(Point point) { - if (point != null) { - int column = point.x; - if (column >= 0) { - return treeTable.isHierarchical(column); - } - } - return false; - } - @SuppressWarnings("unused") - Point rollover = new Point(-1, -1); - }; - return l; - } - - /** - * {@inheritDoc}

                - * - * Overridden to produce clicked client props only. The - * rollover are produced by a propertyChangeListener to - * the table's corresponding prop. - * - */ - @Override - protected RolloverProducer createRolloverProducer() { - return new RolloverProducer() { - - /** - * Overridden to do nothing. - * - * @param e - * @param property - */ - @Override - protected void updateRollover(MouseEvent e, String property, boolean fireAlways) { - if (CLICKED_KEY.equals(property)) { - super.updateRollover(e, property, fireAlways); - } - } - @Override - protected void updateRolloverPoint(JComponent component, - Point mousePoint) { - JXTree tree = (JXTree) component; - int row = tree.getClosestRowForLocation(mousePoint.x, mousePoint.y); - Rectangle bounds = tree.getRowBounds(row); - if (bounds == null) { - row = -1; - } else { - if ((bounds.y + bounds.height < mousePoint.y) || - bounds.x > mousePoint.x) { - row = -1; - } - } - int col = row < 0 ? -1 : 0; - rollover.x = col; - rollover.y = row; - } - - }; - } - - - @Override - public void scrollRectToVisible(Rectangle aRect) { - treeTable.scrollRectToVisible(aRect); - } - - @Override - protected void setExpandedState(TreePath path, boolean state) { - // JW: fix for #1126 - CellEditors are removed immediately after starting an - // edit if they involve a change of selection and the - // expandsOnSelection property is true - // back out if the selection change does not cause a change in - // expansion state - if (isExpanded(path) == state) return; - // on change of expansion state, the editor's row might be changed - // for simplicity, it's stopped always (even if the row is not changed) - treeTable.getTreeTableHacker().completeEditing(); - super.setExpandedState(path, state); - treeTable.getTreeTableHacker().expansionChanged(); - - } - - /** - * updateUI is overridden to set the colors of the Tree's renderer - * to match that of the table. - */ - @Override - public void updateUI() { - super.updateUI(); - // Make the tree's cell renderer use the table's cell selection - // colors. - // TODO JW: need to revisit... - // a) the "real" of a JXTree is always wrapped into a DelegatingRenderer - // consequently the if-block never executes - // b) even if it does it probably (?) should not - // unconditionally overwrite custom selection colors. - // Check for UIResources instead. - TreeCellRenderer tcr = getCellRenderer(); - if (tcr instanceof DefaultTreeCellRenderer) { - DefaultTreeCellRenderer dtcr = ((DefaultTreeCellRenderer) tcr); - // For 1.1 uncomment this, 1.2 has a bug that will cause an - // exception to be thrown if the border selection color is null. - dtcr.setBorderSelectionColor(null); - dtcr.setTextSelectionColor( - UIManager.getColor("Table.selectionForeground")); - dtcr.setBackgroundSelectionColor( - UIManager.getColor("Table.selectionBackground")); - } - } - - /** - * Sets the row height of the tree, and forwards the row height to - * the table. - * - * - */ - @Override - public void setRowHeight(int rowHeight) { - // JW: can't ... updateUI invoked with rowHeight = 0 - // hmmm... looks fishy ... -// if (rowHeight <= 0) throw -// new IllegalArgumentException("the rendering tree must have a fixed rowHeight > 0"); - super.setRowHeight(rowHeight); - if (rowHeight > 0) { - if (treeTable != null) { - treeTable.adjustTableRowHeight(rowHeight); - } - } - } - - - /** - * This is overridden to set the location to (0, 0) and set - * the dimension to exactly fill the bounds of the hierarchical - * column.

                - */ - @Override - public void setBounds(int x, int y, int w, int h) { - // location is relative to the hierarchical column - y = 0; - x = 0; - if (treeTable != null) { - // adjust height to table height - // It is not enough to set the height to treeTable.getHeight() - // JW: why not? - h = treeTable.getRowCount() * this.getRowHeight(); - int hierarchicalC = treeTable.getHierarchicalColumn(); - // JW: re-introduced to fix Issue 1168-swingx - if (hierarchicalC >= 0) { - TableColumn column = treeTable.getColumn(hierarchicalC); - // adjust width to width of hierarchical column - w = column.getWidth(); - } - } - super.setBounds(x, y, w, h); - } - - /** - * Sublcassed to translate the graphics such that the last visible row - * will be drawn at 0,0. - */ - @Override - public void paint(Graphics g) { - Rectangle cellRect = treeTable.getCellRect(visibleRow, 0, false); - g.translate(0, -cellRect.y); - - hierarchicalColumnWidth = getWidth(); - super.paint(g); - - Border border = cellBorder; - if (highlightBorder != null) { - border = highlightBorder; - } - // Draw the Table border if we have focus. - if (border != null) { - // #170: border not drawn correctly - // JW: position the border to be drawn in translated area - // still not satifying in all cases... - // RG: Now it satisfies (at least for the row margins) - // Still need to make similar adjustments for column margins... - border.paintBorder(this, g, 0, cellRect.y, - getWidth(), cellRect.height); - } - } - - /** - * {@inheritDoc}

                - * - * Overridden to fix #swingx-1525: BorderHighlighter fills tree column.

                - * - * Basically, the reason was that the border is set on the tree as a whole - * instead of on the cell level. The fix is to bypass super completely, keep - * a reference to the cell border and manually paint it around the cell - * in the overridden paint.

                - * - * Note: in the paint we need to paint either the focus border or the - * cellBorder, the former taking precedence. - * - */ - @Override - public void setBorder(Border border) { - cellBorder = border; - } - - - public void doClick() { - if ((getCellRenderer() instanceof RolloverRenderer) - && ((RolloverRenderer) getCellRenderer()).isEnabled()) { - ((RolloverRenderer) getCellRenderer()).doClick(); - } - - } - - - @Override - public boolean isRowSelected(int row) { - if ((treeTable == null) || (treeTable.getHierarchicalColumn() <0)) return false; - return treeTable.isCellSelected(row, treeTable.getHierarchicalColumn()); - } - - - @Override - public Component getTableCellRendererComponent(JTable table, - Object value, - boolean isSelected, boolean hasFocus, int row, int column) { - assert table == treeTable; - // JW: quick fix for the tooltip part of #794-swingx: - // visual properties must be reset in each cycle. - // reverted - otherwise tooltip per Highlighter doesn't work - // -// setToolTipText(null); - - if (isSelected) { - setBackground(table.getSelectionBackground()); - setForeground(table.getSelectionForeground()); - } - else { - setBackground(table.getBackground()); - setForeground(table.getForeground()); - } - - highlightBorder = null; - if (treeTable != null) { - if (treeTable.realEditingRow() == row && - treeTable.getEditingColumn() == column) { - } - else if (hasFocus) { - highlightBorder = UIManager.getBorder( - "Table.focusCellHighlightBorder"); - } - } - - visibleRow = row; - - return this; - } - - private class ClippedTreeCellRenderer extends DefaultXTreeCellRenderer - implements StringValue - { - @SuppressWarnings("unused") - private boolean inpainting; - private String shortText; - @Override - public void paint(Graphics g) { - String fullText = super.getText(); - - shortText = SwingUtilities.layoutCompoundLabel( - this, g.getFontMetrics(), fullText, getIcon(), - getVerticalAlignment(), getHorizontalAlignment(), - getVerticalTextPosition(), getHorizontalTextPosition(), - getItemRect(itemRect), iconRect, textRect, - getIconTextGap()); - - /** TODO: setText is more heavyweight than we want in this - * situation. Make JLabel.text protected instead of private. - */ - - try { - inpainting = true; - // TODO JW: don't - override getText to return the short version - // during painting - setText(shortText); // temporarily truncate text - super.paint(g); - } finally { - inpainting = false; - setText(fullText); // restore full text - } - } - - - private Rectangle getItemRect(Rectangle itemRect) { - getBounds(itemRect); -// LOG.info("rect" + itemRect); - itemRect.width = hierarchicalColumnWidth - itemRect.x; - return itemRect; - } - - @Override - public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { - return super.getTreeCellRendererComponent(tree, getHierarchicalTableValue(value), sel, expanded, leaf, - row, hasFocus); - } - - - /** - * - * @param node the node in the treeModel as passed into the TreeCellRenderer - * @return the corresponding value of the hierarchical cell in the TreeTableModel - */ - private Object getHierarchicalTableValue(Object node) { - Object val = node; - - if (treeTable != null) { - int treeColumn = treeTable.getTreeTableModel().getHierarchicalColumn(); - Object o = null; - if (treeColumn >= 0) { - // following is unreliable during a paint cycle - // somehow interferes with BasicTreeUIs painting cache -// o = treeTable.getValueAt(row, treeColumn); - // ask the model - that's always okay - // might blow if the TreeTableModel is strict in - // checking the containment of the value and - // this renderer is called for sizing with a prototype - o = treeTable.getTreeTableModel().getValueAt(node, treeColumn); - } - val = o; - } - return val; - } - - /** - * {@inheritDoc}

                - */ - @Override - public String getString(Object node) { -// int treeColumn = treeTable.getTreeTableModel().getHierarchicalColumn(); -// if (treeColumn >= 0) { -// return StringValues.TO_STRING.getString(treeTable.getTreeTableModel().getValueAt(value, treeColumn)); -// } - return StringValues.TO_STRING.getString(getHierarchicalTableValue(node)); - } - - // Rectangles filled in by SwingUtilities.layoutCompoundLabel(); - private final Rectangle iconRect = new Rectangle(); - private final Rectangle textRect = new Rectangle(); - // Rectangle filled in by this.getItemRect(); - private final Rectangle itemRect = new Rectangle(); - } - - /** Border to draw around the tree, if this is non-null, it will - * be painted. */ - protected Border highlightBorder = null; - protected JXTreeTable treeTable = null; - protected int visibleRow = 0; - - // A JXTreeTable may not have more than one hierarchical column - private int hierarchicalColumnWidth = 0; - - } - - /** - * Returns the adapter that knows how to access the component data model. - * The component data adapter is used by filters, sorters, and highlighters. - * - * @return the adapter that knows how to access the component data model - */ - @Override - protected ComponentAdapter getComponentAdapter() { - if (dataAdapter == null) { - dataAdapter = new TreeTableDataAdapter(this); - } - return dataAdapter; - } - - - protected static class TreeTableDataAdapter extends TableAdapter { - private final JXTreeTable table; - - /** - * Constructs a TreeTableDataAdapter for the specified - * target component. - * - * @param component the target component - */ - public TreeTableDataAdapter(JXTreeTable component) { - super(component); - table = component; - } - - public JXTreeTable getTreeTable() { - return table; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isExpanded() { - return table.isExpanded(row); - } - - /** - * {@inheritDoc} - */ - @Override - public int getDepth() { - return table.getPathForRow(row).getPathCount() - 1; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isLeaf() { - // Issue #270-swingx: guard against invisible row - TreePath path = table.getPathForRow(row); - if (path != null) { - return table.getTreeTableModel().isLeaf(path.getLastPathComponent()); - } - // JW: this is the same as BasicTreeUI.isLeaf. - // Shouldn't happen anyway because must be called for visible rows only. - return true; - } - /** - * - * @return true if the cell identified by this adapter displays hierarchical - * nodes; false otherwise - */ - @Override - public boolean isHierarchical() { - return table.isHierarchical(column); - } - - /** - * {@inheritDoc}

                - * - * Overridden to fix #821-swingx: string rep of hierarchical column incorrect. - * In this case we must delegate to the tree directly (via treetable.getHierarchicalString). - * - * PENDING JW: revisit once we switch to really using a table renderer. - */ - @Override - public String getFilteredStringAt(int row, int column) { - if (table.getTreeTableModel().getHierarchicalColumn() == column) { - if (convertColumnIndexToView(column) < 0) { - // hidden hierarchical column, access directly - // PENDING JW: after introducing and wiring StringValueRegistry, - // had to change to query the hierarchicalString always - // could probably be done more elegantly, but ... - } - return table.getHierarchicalStringAt(row); - } - return super.getFilteredStringAt(row, column); - } - - /** - * {@inheritDoc}

                - * - * Overridden to fix #821-swingx: string rep of hierarchical column incorrect. - * In this case we must delegate to the tree directly (via treetable.getHierarchicalString). - * - * PENDING JW: revisit once we switch to really using a table renderer. - */ - @Override - public String getStringAt(int row, int column) { - if (table.getTreeTableModel().getHierarchicalColumn() == column) { - if (convertColumnIndexToView(column) < 0) { - // hidden hierarchical column, access directly - // PENDING JW: after introducing and wiring StringValueRegistry, - // had to change to query the hierarchicalString always - // could probably be done more elegantly, but ... - } - return table.getHierarchicalStringAt(row); - } - return super.getStringAt(row, column); - } - - } - -} diff --git a/src/main/java/org/jdesktop/swingx/Mnemonicable.java b/src/main/java/org/jdesktop/swingx/Mnemonicable.java deleted file mode 100644 index 1dbf53affd..0000000000 --- a/src/main/java/org/jdesktop/swingx/Mnemonicable.java +++ /dev/null @@ -1,79 +0,0 @@ -package org.jdesktop.swingx; - -/** - * An interface that describes an object that is capable of being accessed/used via a mnemonic - * keystroke. - * - * @author Karl George Schaefer - */ -// TODO this describes the mnemonic feature but not what is used, -// ie. what String returning method is called -interface Mnemonicable { - /** - * Returns the keyboard mnemonic for this component. - * - * @return the keyboard mnemonic - */ - int getMnemonic(); - - /** - * Sets the keyboard mnemonic on this component. The mnemonic is the key - * which when combined with the look and feel's mouseless modifier (usually - * Alt) will activate this component. - *

                - * A mnemonic must correspond to a single key on the keyboard and should be - * specified using one of the VK_XXX keycodes defined in - * java.awt.event.KeyEvent. Mnemonics are case-insensitive, - * therefore a key event with the corresponding keycode would cause the - * button to be activated whether or not the Shift modifier was pressed. - * - * @param mnemonic - * the key code which represents the mnemonic - * @see java.awt.event.KeyEvent - * @see #setDisplayedMnemonicIndex - * - * @beaninfo bound: true attribute: visualUpdate true description: the - * keyboard character mnemonic - */ - void setMnemonic(int mnemonic); - - /** - * Returns the character, as an index, that the look and feel should - * provide decoration for as representing the mnemonic character. - * - * @since 1.4 - * @return index representing mnemonic character - * @see #setDisplayedMnemonicIndex - */ - int getDisplayedMnemonicIndex(); - - /** - * Provides a hint to the look and feel as to which character in the - * text should be decorated to represent the mnemonic. Not all look and - * feels may support this. A value of -1 indicates either there is no - * mnemonic, the mnemonic character is not contained in the string, or - * the developer does not wish the mnemonic to be displayed. - *

                - * The value of this is updated as the properties relating to the - * mnemonic change (such as the mnemonic itself, the text...). - * You should only ever have to call this if - * you do not wish the default character to be underlined. For example, if - * the text was 'Save As', with a mnemonic of 'a', and you wanted the 'A' - * to be decorated, as 'Save As', you would have to invoke - * setDisplayedMnemonicIndex(5) after invoking - * setMnemonic(KeyEvent.VK_A). - * - * @since 1.4 - * @param index Index into the String to underline - * @exception IllegalArgumentException will be thrown if index - * is >= length of the text, or < -1 - * @see #getDisplayedMnemonicIndex - * - * @beaninfo - * bound: true - * attribute: visualUpdate true - * description: the index into the String to draw the keyboard character - * mnemonic at - */ - void setDisplayedMnemonicIndex(int index) throws IllegalArgumentException; -} diff --git a/src/main/java/org/jdesktop/swingx/MultiSplitLayout.java b/src/main/java/org/jdesktop/swingx/MultiSplitLayout.java deleted file mode 100644 index 6ac3618505..0000000000 --- a/src/main/java/org/jdesktop/swingx/MultiSplitLayout.java +++ /dev/null @@ -1,2211 +0,0 @@ -/* - * $Id: MultiSplitLayout.java 4238 2012-08-28 17:56:36Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx; - -import javax.swing.*; -import java.awt.*; -import java.beans.PropertyChangeListener; -import java.beans.PropertyChangeSupport; -import java.io.*; -import java.util.List; -import java.util.*; - - -/** - * The MultiSplitLayout layout manager recursively arranges its - * components in row and column groups called "Splits". Elements of - * the layout are separated by gaps called "Dividers". The overall - * layout is defined with a simple tree model whose nodes are - * instances of MultiSplitLayout.Split, MultiSplitLayout.Divider, - * and MultiSplitLayout.Leaf. Named Leaf nodes represent the space - * allocated to a component that was added with a constraint that - * matches the Leaf's name. Extra space is distributed - * among row/column siblings according to their 0.0 to 1.0 weight. - * If no weights are specified then the last sibling always gets - * all of the extra space, or space reduction. - * - *

                - * Although MultiSplitLayout can be used with any Container, it's - * the default layout manager for MultiSplitPane. MultiSplitPane - * supports interactively dragging the Dividers, accessibility, - * and other features associated with split panes. - * - *

                - * All properties in this class are bound: when a properties value - * is changed, all PropertyChangeListeners are fired. - * - * - * @author Hans Muller - * @author Luan O'Carroll - * @see JXMultiSplitPane - */ - -/* - * Changes by Luan O'Carroll - * 1 Support for visibility added. - */ -public class MultiSplitLayout implements LayoutManager, Serializable -{ - public static final int DEFAULT_LAYOUT = 0; - public static final int NO_MIN_SIZE_LAYOUT = 1; - public static final int USER_MIN_SIZE_LAYOUT = 2; - - private final Map childMap = new HashMap(); - private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); - private Node model; - private int dividerSize; - private boolean floatingDividers = true; - - private boolean removeDividers = true; - private boolean layoutByWeight = false; - - private int layoutMode; - private int userMinSize = 20; - - /** - * Create a MultiSplitLayout with a default model with a single - * Leaf node named "default". - * - * #see setModel - */ - public MultiSplitLayout() - { - this(new Leaf("default")); - } - - /** - * Create a MultiSplitLayout with a default model with a single - * Leaf node named "default". - * - * @param layoutByWeight if true the layout is initialized in proportion to - * the node weights rather than the component preferred sizes. - * #see setModel - */ - public MultiSplitLayout(boolean layoutByWeight) - { - this(new Leaf("default")); - this.layoutByWeight = layoutByWeight; - } - - /** - * Set the size of the child components to match the weights of the children. - * If the components to not all specify a weight then the available layout - * space is divided equally between the components. - */ - public void layoutByWeight( Container parent ) - { - doLayoutByWeight( parent ); - - layoutContainer( parent ); - } - - /** - * Set the size of the child components to match the weights of the children. - * If the components to not all specify a weight then the available layout - * space is divided equally between the components. - */ - private void doLayoutByWeight( Container parent ) - { - Dimension size = parent.getSize(); - Insets insets = parent.getInsets(); - int width = size.width - (insets.left + insets.right); - int height = size.height - (insets.top + insets.bottom); - Rectangle bounds = new Rectangle(insets.left, insets.top, width, height); - - if (model instanceof Leaf) - model.setBounds(bounds); - else if (model instanceof Split) - doLayoutByWeight( model, bounds ); - } - - private void doLayoutByWeight( Node node, Rectangle bounds ) - { - int width = bounds.width; - int height = bounds.height; - Split split = (Split)node; - List splitChildren = split.getChildren(); - double distributableWeight = 1.0; - int unweightedComponents = 0; - int dividerSpace = 0; - for( Node splitChild : splitChildren ) { - if ( !splitChild.isVisible()) - continue; - else if ( splitChild instanceof Divider ) { - dividerSpace += dividerSize; - continue; - } - - double weight = splitChild.getWeight(); - if ( weight > 0.0 ) - distributableWeight -= weight; - else - unweightedComponents++; - } - - if ( split.isRowLayout()) { - width -= dividerSpace; - double distributableWidth = width * distributableWeight; - for( Node splitChild : splitChildren ) { - if ( !splitChild.isVisible() || ( splitChild instanceof Divider )) - continue; - - double weight = splitChild.getWeight(); - Rectangle splitChildBounds = splitChild.getBounds(); - if ( weight >= 0 ) - splitChildBounds = new Rectangle( splitChildBounds.x, splitChildBounds.y, (int)( width * weight ), height ); - else - splitChildBounds = new Rectangle( splitChildBounds.x, splitChildBounds.y, (int)( distributableWidth / unweightedComponents ), height ); - - if ( layoutMode == USER_MIN_SIZE_LAYOUT ) { - splitChildBounds.setSize( Math.max( splitChildBounds.width, userMinSize ), splitChildBounds.height ); - } - - splitChild.setBounds( splitChildBounds ); - - if ( splitChild instanceof Split ) - doLayoutByWeight( splitChild, splitChildBounds ); - else { - Component comp = getComponentForNode( splitChild ); - if ( comp != null ) - comp.setPreferredSize( splitChildBounds.getSize()); - } - } - } - else { - height -= dividerSpace; - double distributableHeight = height * distributableWeight; - for( Node splitChild : splitChildren ) { - if ( !splitChild.isVisible() || ( splitChild instanceof Divider )) - continue; - - double weight = splitChild.getWeight(); - Rectangle splitChildBounds = splitChild.getBounds(); - if ( weight >= 0 ) - splitChildBounds = new Rectangle( splitChildBounds.x, splitChildBounds.y, width, (int)( height * weight )); - else - splitChildBounds = new Rectangle( splitChildBounds.x, splitChildBounds.y, width, (int)( distributableHeight / unweightedComponents )); - - if ( layoutMode == USER_MIN_SIZE_LAYOUT ) { - splitChildBounds.setSize( splitChildBounds.width, Math.max( splitChildBounds.height, userMinSize ) ); - } - - splitChild.setBounds( splitChildBounds ); - - if ( splitChild instanceof Split ) - doLayoutByWeight( splitChild, splitChildBounds ); - else { - Component comp = getComponentForNode( splitChild ); - if ( comp != null ) - comp.setPreferredSize( splitChildBounds.getSize()); - } - } - } - } - - /** - * Get the component associated with a MultiSplitLayout.Node - * @param n the layout node - * @return the component handled by the layout or null if not found - */ - public Component getComponentForNode( Node n ) - { - String name = ((Leaf)n).getName(); - return (name != null) ? (Component)childMap.get(name) : null; - } - - /** - * Get the MultiSplitLayout.Node associated with a component - * @param comp the component being positioned by the layout - * @return the node associated with the component - */ - public Node getNodeForComponent( Component comp ) - { - return getNodeForName( getNameForComponent( comp )); - } - - /** - * Get the MultiSplitLayout.Node associated with a component - * @param name the name used to associate a component with the layout - * @return the node associated with the component - */ - public Node getNodeForName( String name ) - { - if ( model instanceof Split ) { - Split split = ((Split)model); - return getNodeForName( split, name ); - } else if (model instanceof Leaf) { - if (((Leaf) model).getName().equals(name)) { - return model; - } else { - return null; - } - } else { - return null; - } - } - - /** - * Get the name used to map a component - * @param child the component - * @return the name used to map the component or null if no mapping is found - */ - public String getNameForComponent( Component child ) - { - String name = null; - for(Map.Entry kv : childMap.entrySet()) { - if (kv.getValue() == child) { - name = kv.getKey(); - break; - } - } - - return name; - } - - /** - * Get the MultiSplitLayout.Node associated with a component - * @param split the layout split that owns the requested node - * @param comp the component being positioned by the layout - * @return the node associated with the component - */ - public Node getNodeForComponent( Split split, Component comp ) - { - return getNodeForName( split, getNameForComponent( comp )); - } - - /** - * Get the MultiSplitLayout.Node associated with a component - * @param split the layout split that owns the requested node - * @param name the name used to associate a component with the layout - * @return the node associated with the component - */ - public Node getNodeForName( Split split, String name ) - { - for(Node n : split.getChildren()) { - if ( n instanceof Leaf ) { - if ( ((Leaf)n).getName().equals( name )) - return n; - } - else if ( n instanceof Split ) { - Node n1 = getNodeForName( (Split)n, name ); - if ( n1 != null ) - return n1; - } - } - return null; - } - - /** - * Is there a valid model for the layout? - * @return true if there is a model - */ - public boolean hasModel() - { - return model != null; - } - - /** - * Create a MultiSplitLayout with the specified model. - * - * #see setModel - */ - public MultiSplitLayout(Node model) { - this.model = model; - this.dividerSize = UIManager.getInt("SplitPane.dividerSize"); - if (this.dividerSize == 0) { - this.dividerSize = 7; - } - } - - public void addPropertyChangeListener(PropertyChangeListener listener) { - if (listener != null) { - pcs.addPropertyChangeListener(listener); - } - } - public void removePropertyChangeListener(PropertyChangeListener listener) { - if (listener != null) { - pcs.removePropertyChangeListener(listener); - } - } - public PropertyChangeListener[] getPropertyChangeListeners() { - return pcs.getPropertyChangeListeners(); - } - - private void firePCS(String propertyName, Object oldValue, Object newValue) { - if (!(oldValue != null && newValue != null && oldValue.equals(newValue))) { - pcs.firePropertyChange(propertyName, oldValue, newValue); - } - } - - /** - * Return the root of the tree of Split, Leaf, and Divider nodes - * that define this layout. - * - * @return the value of the model property - * @see #setModel - */ - public Node getModel() { return model; } - - /** - * Set the root of the tree of Split, Leaf, and Divider nodes - * that define this layout. The model can be a Split node - * (the typical case) or a Leaf. The default value of this - * property is a Leaf named "default". - * - * @param model the root of the tree of Split, Leaf, and Divider node - * @throws IllegalArgumentException if model is a Divider or null - * @see #getModel - */ - public void setModel(Node model) { - if ((model == null) || (model instanceof Divider)) { - throw new IllegalArgumentException("invalid model"); - } - Node oldModel = getModel(); - this.model = model; - firePCS("model", oldModel, getModel()); - } - - /** - * Returns the width of Dividers in Split rows, and the height of - * Dividers in Split columns. - * - * @return the value of the dividerSize property - * @see #setDividerSize - */ - public int getDividerSize() { return dividerSize; } - - /** - * Sets the width of Dividers in Split rows, and the height of - * Dividers in Split columns. The default value of this property - * is the same as for JSplitPane Dividers. - * - * @param dividerSize the size of dividers (pixels) - * @throws IllegalArgumentException if dividerSize < 0 - * @see #getDividerSize - */ - public void setDividerSize(int dividerSize) { - if (dividerSize < 0) { - throw new IllegalArgumentException("invalid dividerSize"); - } - int oldDividerSize = this.dividerSize; - this.dividerSize = dividerSize; - firePCS("dividerSize", oldDividerSize, dividerSize); - } - - /** - * @return the value of the floatingDividers property - * @see #setFloatingDividers - */ - public boolean getFloatingDividers() { return floatingDividers; } - - - /** - * If true, Leaf node bounds match the corresponding component's - * preferred size and Splits/Dividers are resized accordingly. - * If false then the Dividers define the bounds of the adjacent - * Split and Leaf nodes. Typically this property is set to false - * after the (MultiSplitPane) user has dragged a Divider. - * - * @see #getFloatingDividers - */ - public void setFloatingDividers(boolean floatingDividers) - { - boolean oldFloatingDividers = this.floatingDividers; - this.floatingDividers = floatingDividers; - firePCS("floatingDividers", oldFloatingDividers, floatingDividers); - } - - /** - * @return the value of the removeDividers property - * @see #setRemoveDividers - */ - public boolean getRemoveDividers() { return removeDividers; } - - /** - * If true, the next divider is removed when a component is removed from the - * layout. If false, only the node itself is removed. Normally the next - * divider should be removed from the layout when a component is removed. - * @param removeDividers true to removed the next divider whena component is - * removed from teh layout - */ - public void setRemoveDividers( boolean removeDividers ) - { - boolean oldRemoveDividers = this.removeDividers; - this.removeDividers = removeDividers; - firePCS("removeDividers", oldRemoveDividers, removeDividers); - } - - /** - * Add a component to this MultiSplitLayout. The - * name should match the name property of the Leaf - * node that represents the bounds of child. After - * layoutContainer() recomputes the bounds of all of the nodes in - * the model, it will set this child's bounds to the bounds of the - * Leaf node with name. Note: if a component was already - * added with the same name, this method does not remove it from - * its parent. - * - * @param name identifies the Leaf node that defines the child's bounds - * @param child the component to be added - * @see #removeLayoutComponent - */ - @Override -public void addLayoutComponent(String name, Component child) { - if (name == null) { - throw new IllegalArgumentException("name not specified"); - } - childMap.put(name, child); - } - - /** - * Removes the specified component from the layout. - * - * @param child the component to be removed - * @see #addLayoutComponent - */ - @Override -public void removeLayoutComponent(Component child) { - String name = getNameForComponent( child ); - - if ( name != null ) { - childMap.remove( name ); - } - } - - /** - * Removes the specified node from the layout. - * - * @param name the name of the component to be removed - * @see #addLayoutComponent - */ - public void removeLayoutNode(String name) { - - if ( name != null ) { - Node n; - if ( !( model instanceof Split )) - n = model; - else - n = getNodeForName( name ); - - childMap.remove(name); - - if ( n != null ) { - Split s = n.getParent(); - s.remove( n ); - if (removeDividers) { - while ( s.getChildren().size() < 2 ) { - Split p = s.getParent(); - if ( p == null ) { - if ( s.getChildren().size() > 0 ) - model = s.getChildren().get( 0 ); - else - model = null; - return; - } - if ( s.getChildren().size() == 1 ) { - Node next = s.getChildren().get( 0 ); - p.replace( s, next ); - next.setParent( p ); - } - else - p.remove( s ); - s = p; - } - } - } - else { - childMap.remove( name ); - } - } - } - - /** - * Show/Hide nodes. Any dividers that are no longer required due to one of the - * nodes being made visible/invisible are also shown/hidden. The visibility of - * the component managed by the node is also changed by this method - * @param name the node name - * @param visible the new node visible state - */ - public void displayNode( String name, boolean visible ) - { - Node node = getNodeForName( name ); - if ( node != null ) { - Component comp = getComponentForNode( node ); - comp.setVisible( visible ); - node.setVisible( visible ); - - Split p = node.getParent(); - if ( !visible ) { - p.hide( node ); - if ( !p.isVisible()) - p.getParent().hide( p ); - - p.checkDividers( p ); - // If the split has become invisible then the parent may also have a - // divider that needs to be hidden. - while ( !p.isVisible()) { - p = p.getParent(); - if ( p != null ) - p.checkDividers( p ); - else - break; - } - } - else - p.restoreDividers( p ); - } - setFloatingDividers( false ); - } - - private Component childForNode(Node node) { - if (node instanceof Leaf) { - Leaf leaf = (Leaf)node; - String name = leaf.getName(); - return (name != null) ? childMap.get(name) : null; - } - return null; - } - - - private Dimension preferredComponentSize(Node node) { - if ( layoutMode == NO_MIN_SIZE_LAYOUT ) - return new Dimension(0, 0); - - Component child = childForNode(node); - return ((child != null) && child.isVisible() ) ? child.getPreferredSize() : new Dimension(0, 0); - } - - private Dimension minimumComponentSize(Node node) { - if ( layoutMode == NO_MIN_SIZE_LAYOUT ) - return new Dimension(0, 0); - - Component child = childForNode(node); - return ((child != null) && child.isVisible() ) ? child.getMinimumSize() : new Dimension(0, 0); - } - - private Dimension preferredNodeSize(Node root) { - if (root instanceof Leaf) { - return preferredComponentSize(root); - } - else if (root instanceof Divider) { - if ( !((Divider)root).isVisible()) - return new Dimension(0,0); - int divSize = getDividerSize(); - return new Dimension(divSize, divSize); - } - else { - Split split = (Split)root; - List splitChildren = split.getChildren(); - int width = 0; - int height = 0; - if (split.isRowLayout()) { - for(Node splitChild : splitChildren) { - if ( !splitChild.isVisible()) - continue; - Dimension size = preferredNodeSize(splitChild); - width += size.width; - height = Math.max(height, size.height); - } - } - else { - for(Node splitChild : splitChildren) { - if ( !splitChild.isVisible()) - continue; - Dimension size = preferredNodeSize(splitChild); - width = Math.max(width, size.width); - height += size.height; - } - } - return new Dimension(width, height); - } - } - - /** - * Get the minimum size of this node. Sums the minumum sizes of rows or - * columns to get the overall minimum size for the layout node, including the - * dividers. - * @param root the node whose size is required. - * @return the minimum size. - */ - public Dimension minimumNodeSize(Node root) { - assert( root.isVisible ); - if (root instanceof Leaf) { - if ( layoutMode == NO_MIN_SIZE_LAYOUT ) - return new Dimension(0, 0); - - Component child = childForNode(root); - return ((child != null) && child.isVisible() ) ? child.getMinimumSize() : new Dimension(0, 0); - } - else if (root instanceof Divider) { - if ( !((Divider)root).isVisible() ) - return new Dimension(0,0); - int divSize = getDividerSize(); - return new Dimension(divSize, divSize); - } - else { - Split split = (Split)root; - List splitChildren = split.getChildren(); - int width = 0; - int height = 0; - if (split.isRowLayout()) { - for(Node splitChild : splitChildren) { - if ( !splitChild.isVisible()) - continue; - Dimension size = minimumNodeSize(splitChild); - width += size.width; - height = Math.max(height, size.height); - } - } - else { - for(Node splitChild : splitChildren) { - if ( !splitChild.isVisible()) - continue; - Dimension size = minimumNodeSize(splitChild); - width = Math.max(width, size.width); - height += size.height; - } - } - return new Dimension(width, height); - } - } - - /** - * Get the maximum size of this node. Sums the minumum sizes of rows or - * columns to get the overall maximum size for the layout node, including the - * dividers. - * @param root the node whose size is required. - * @return the minimum size. - */ - public Dimension maximumNodeSize(Node root) { - assert( root.isVisible ); - if (root instanceof Leaf) { - Component child = childForNode(root); - return ((child != null) && child.isVisible() ) ? child.getMaximumSize() : new Dimension(0, 0); - } - else if (root instanceof Divider) { - if ( !((Divider)root).isVisible() ) - return new Dimension(0,0); - int divSize = getDividerSize(); - return new Dimension(divSize, divSize); - } - else { - Split split = (Split)root; - List splitChildren = split.getChildren(); - int width = Integer.MAX_VALUE; - int height = Integer.MAX_VALUE; - if (split.isRowLayout()) { - for(Node splitChild : splitChildren) { - if ( !splitChild.isVisible()) - continue; - Dimension size = maximumNodeSize(splitChild); - width += size.width; - height = Math.min(height, size.height); - } - } - else { - for(Node splitChild : splitChildren) { - if ( !splitChild.isVisible()) - continue; - Dimension size = maximumNodeSize(splitChild); - width = Math.min(width, size.width); - height += size.height; - } - } - return new Dimension(width, height); - } - } - - private Dimension sizeWithInsets(Container parent, Dimension size) { - Insets insets = parent.getInsets(); - int width = size.width + insets.left + insets.right; - int height = size.height + insets.top + insets.bottom; - return new Dimension(width, height); - } - - @Override -public Dimension preferredLayoutSize(Container parent) { - Dimension size = preferredNodeSize(getModel()); - return sizeWithInsets(parent, size); - } - - @Override -public Dimension minimumLayoutSize(Container parent) { - Dimension size = minimumNodeSize(getModel()); - return sizeWithInsets(parent, size); - } - - - private Rectangle boundsWithYandHeight(Rectangle bounds, double y, double height) { - Rectangle r = new Rectangle(); - r.setBounds((int)(bounds.getX()), (int)y, (int)(bounds.getWidth()), (int)height); - return r; - } - - private Rectangle boundsWithXandWidth(Rectangle bounds, double x, double width) { - Rectangle r = new Rectangle(); - r.setBounds((int)x, (int)(bounds.getY()), (int)width, (int)(bounds.getHeight())); - return r; - } - - - private void minimizeSplitBounds(Split split, Rectangle bounds) { - assert ( split.isVisible()); - Rectangle splitBounds = new Rectangle(bounds.x, bounds.y, 0, 0); - List splitChildren = split.getChildren(); - Node lastChild = null; - int lastVisibleChildIdx = splitChildren.size(); - do { - lastVisibleChildIdx--; - lastChild = splitChildren.get( lastVisibleChildIdx ); - } while (( lastVisibleChildIdx > 0 ) && !lastChild.isVisible()); - - if ( !lastChild.isVisible()) - return; - if ( lastVisibleChildIdx >= 0 ) { - Rectangle lastChildBounds = lastChild.getBounds(); - if (split.isRowLayout()) { - int lastChildMaxX = lastChildBounds.x + lastChildBounds.width; - splitBounds.add(lastChildMaxX, bounds.y + bounds.height); - } - else { - int lastChildMaxY = lastChildBounds.y + lastChildBounds.height; - splitBounds.add(bounds.x + bounds.width, lastChildMaxY); - } - } - split.setBounds(splitBounds); - } - - - private void layoutShrink(Split split, Rectangle bounds) { - Rectangle splitBounds = split.getBounds(); - ListIterator splitChildren = split.getChildren().listIterator(); - Node lastWeightedChild = split.lastWeightedChild(); - - if (split.isRowLayout()) { - int totalWidth = 0; // sum of the children's widths - int minWeightedWidth = 0; // sum of the weighted childrens' min widths - int totalWeightedWidth = 0; // sum of the weighted childrens' widths - for(Node splitChild : split.getChildren()) { - if ( !splitChild.isVisible()) - continue; - int nodeWidth = splitChild.getBounds().width; - int nodeMinWidth = 0; - if (( layoutMode == USER_MIN_SIZE_LAYOUT ) && !( splitChild instanceof Divider )) - nodeMinWidth = userMinSize; - else if ( layoutMode == DEFAULT_LAYOUT ) - nodeMinWidth = Math.min(nodeWidth, minimumNodeSize(splitChild).width); - totalWidth += nodeWidth; - if (splitChild.getWeight() > 0.0) { - minWeightedWidth += nodeMinWidth; - totalWeightedWidth += nodeWidth; - } - } - - double x = bounds.getX(); - double extraWidth = splitBounds.getWidth() - bounds.getWidth(); - double availableWidth = extraWidth; - boolean onlyShrinkWeightedComponents = - (totalWeightedWidth - minWeightedWidth) > extraWidth; - - while(splitChildren.hasNext()) { - Node splitChild = splitChildren.next(); - if ( !splitChild.isVisible()) { - if ( splitChildren.hasNext()) - splitChildren.next(); - continue; - } - Rectangle splitChildBounds = splitChild.getBounds(); - double minSplitChildWidth = 0.0; - if (( layoutMode == USER_MIN_SIZE_LAYOUT ) && !( splitChild instanceof Divider )) - minSplitChildWidth = userMinSize; - else if ( layoutMode == DEFAULT_LAYOUT ) - minSplitChildWidth = minimumNodeSize(splitChild).getWidth(); - double splitChildWeight = (onlyShrinkWeightedComponents) - ? splitChild.getWeight() - : (splitChildBounds.getWidth() / totalWidth); - - if (!splitChildren.hasNext()) { - double newWidth = Math.max(minSplitChildWidth, bounds.getMaxX() - x); - Rectangle newSplitChildBounds = boundsWithXandWidth(bounds, x, newWidth); - layout2(splitChild, newSplitChildBounds); - } - if ( splitChild.isVisible()) { - if ((availableWidth > 0.0) && (splitChildWeight > 0.0)) { - double oldWidth = splitChildBounds.getWidth(); - double newWidth; - if ( splitChild instanceof Divider ) { - newWidth = dividerSize; - } - else { - double allocatedWidth = Math.rint(splitChildWeight * extraWidth); - newWidth = Math.max(minSplitChildWidth, oldWidth - allocatedWidth); - } - Rectangle newSplitChildBounds = boundsWithXandWidth(bounds, x, newWidth); - layout2(splitChild, newSplitChildBounds); - availableWidth -= (oldWidth - splitChild.getBounds().getWidth()); - } - else { - double existingWidth = splitChildBounds.getWidth(); - Rectangle newSplitChildBounds = boundsWithXandWidth(bounds, x, existingWidth); - layout2(splitChild, newSplitChildBounds); - } - x = splitChild.getBounds().getMaxX(); - } - } - } - - else { - int totalHeight = 0; // sum of the children's heights - int minWeightedHeight = 0; // sum of the weighted childrens' min heights - int totalWeightedHeight = 0; // sum of the weighted childrens' heights - for(Node splitChild : split.getChildren()) { - if ( !splitChild.isVisible()) - continue; - int nodeHeight = splitChild.getBounds().height; - int nodeMinHeight = 0; - if (( layoutMode == USER_MIN_SIZE_LAYOUT ) && !( splitChild instanceof Divider )) - nodeMinHeight = userMinSize; - else if ( layoutMode == DEFAULT_LAYOUT ) - nodeMinHeight = Math.min(nodeHeight, minimumNodeSize(splitChild).height); - totalHeight += nodeHeight; - if (splitChild.getWeight() > 0.0) { - minWeightedHeight += nodeMinHeight; - totalWeightedHeight += nodeHeight; - } - } - - double y = bounds.getY(); - double extraHeight = splitBounds.getHeight() - bounds.getHeight(); - double availableHeight = extraHeight; - boolean onlyShrinkWeightedComponents = - (totalWeightedHeight - minWeightedHeight) > extraHeight; - - while(splitChildren.hasNext()) { - Node splitChild = splitChildren.next(); - if ( !splitChild.isVisible()) { - if ( splitChildren.hasNext()) - splitChildren.next(); - continue; - } - Rectangle splitChildBounds = splitChild.getBounds(); - double minSplitChildHeight = 0.0; - if (( layoutMode == USER_MIN_SIZE_LAYOUT ) && !( splitChild instanceof Divider )) - minSplitChildHeight = userMinSize; - else if ( layoutMode == DEFAULT_LAYOUT ) - minSplitChildHeight = minimumNodeSize(splitChild).getHeight(); - double splitChildWeight = (onlyShrinkWeightedComponents) - ? splitChild.getWeight() - : (splitChildBounds.getHeight() / totalHeight); - - // If this split child is the last visible node it should all the - // remaining space - if ( !hasMoreVisibleSiblings( splitChild )) { - double oldHeight = splitChildBounds.getHeight(); - double newHeight; - if ( splitChild instanceof Divider ) { - newHeight = dividerSize; - } - else { - newHeight = Math.max(minSplitChildHeight, bounds.getMaxY() - y); - } - Rectangle newSplitChildBounds = boundsWithYandHeight(bounds, y, newHeight); - layout2(splitChild, newSplitChildBounds); - availableHeight -= (oldHeight - splitChild.getBounds().getHeight()); - } - else /*if ( splitChild.isVisible()) {*/ - if ((availableHeight > 0.0) && (splitChildWeight > 0.0)) { - double newHeight; - double oldHeight = splitChildBounds.getHeight(); - // Prevent the divider from shrinking - if ( splitChild instanceof Divider ) { - newHeight = dividerSize; - } - else { - double allocatedHeight = Math.rint(splitChildWeight * extraHeight); - newHeight = Math.max(minSplitChildHeight, oldHeight - allocatedHeight); - } - Rectangle newSplitChildBounds = boundsWithYandHeight(bounds, y, newHeight); - layout2(splitChild, newSplitChildBounds); - availableHeight -= (oldHeight - splitChild.getBounds().getHeight()); - } - else { - double existingHeight = splitChildBounds.getHeight(); - Rectangle newSplitChildBounds = boundsWithYandHeight(bounds, y, existingHeight); - layout2(splitChild, newSplitChildBounds); - } - y = splitChild.getBounds().getMaxY(); - } - } - - /* The bounds of the Split node root are set to be - * big enough to contain all of its children. Since - * Leaf children can't be reduced below their - * (corresponding java.awt.Component) minimum sizes, - * the size of the Split's bounds maybe be larger than - * the bounds we were asked to fit within. - */ - minimizeSplitBounds(split, bounds); - } - - /** - * Check if the specified node has any following visible siblings - * @param splitChild the node to check - * @param true if there are visible children following - */ - private boolean hasMoreVisibleSiblings( Node splitChild ) { - Node next = splitChild.nextSibling(); - if ( next == null ) - return false; - - do { - if ( next.isVisible()) - return true; - next = next.nextSibling(); - } while ( next != null ); - - return false; - } - - private void layoutGrow(Split split, Rectangle bounds) { - Rectangle splitBounds = split.getBounds(); - ListIterator splitChildren = split.getChildren().listIterator(); - Node lastWeightedChild = split.lastWeightedChild(); - - /* Layout the Split's child Nodes' along the X axis. The bounds - * of each child will have the same y coordinate and height as the - * layoutGrow() bounds argument. Extra width is allocated to the - * to each child with a non-zero weight: - * newWidth = currentWidth + (extraWidth * splitChild.getWeight()) - * Any extraWidth "left over" (that's availableWidth in the loop - * below) is given to the last child. Note that Dividers always - * have a weight of zero, and they're never the last child. - */ - if (split.isRowLayout()) { - double x = bounds.getX(); - double extraWidth = bounds.getWidth() - splitBounds.getWidth(); - double availableWidth = extraWidth; - - while(splitChildren.hasNext()) { - Node splitChild = splitChildren.next(); - if ( !splitChild.isVisible()) { - continue; - } - Rectangle splitChildBounds = splitChild.getBounds(); - double splitChildWeight = splitChild.getWeight(); - - if ( !hasMoreVisibleSiblings( splitChild )) { - double newWidth = bounds.getMaxX() - x; - Rectangle newSplitChildBounds = boundsWithXandWidth(bounds, x, newWidth); - layout2(splitChild, newSplitChildBounds); - } - else if ((availableWidth > 0.0) && (splitChildWeight > 0.0)) { - double allocatedWidth = (splitChild.equals(lastWeightedChild)) - ? availableWidth - : Math.rint(splitChildWeight * extraWidth); - double newWidth = splitChildBounds.getWidth() + allocatedWidth; - Rectangle newSplitChildBounds = boundsWithXandWidth(bounds, x, newWidth); - layout2(splitChild, newSplitChildBounds); - availableWidth -= allocatedWidth; - } - else { - double existingWidth = splitChildBounds.getWidth(); - Rectangle newSplitChildBounds = boundsWithXandWidth(bounds, x, existingWidth); - layout2(splitChild, newSplitChildBounds); - } - x = splitChild.getBounds().getMaxX(); - } - } - - /* Layout the Split's child Nodes' along the Y axis. The bounds - * of each child will have the same x coordinate and width as the - * layoutGrow() bounds argument. Extra height is allocated to the - * to each child with a non-zero weight: - * newHeight = currentHeight + (extraHeight * splitChild.getWeight()) - * Any extraHeight "left over" (that's availableHeight in the loop - * below) is given to the last child. Note that Dividers always - * have a weight of zero, and they're never the last child. - */ - else { - double y = bounds.getY(); - double extraHeight = bounds.getHeight() - splitBounds.getHeight(); - double availableHeight = extraHeight; - - while(splitChildren.hasNext()) { - Node splitChild = splitChildren.next(); - if ( !splitChild.isVisible()) { - continue; - } - Rectangle splitChildBounds = splitChild.getBounds(); - double splitChildWeight = splitChild.getWeight(); - - if (!splitChildren.hasNext()) { - double newHeight = bounds.getMaxY() - y; - Rectangle newSplitChildBounds = boundsWithYandHeight(bounds, y, newHeight); - layout2(splitChild, newSplitChildBounds); - } - else if ((availableHeight > 0.0) && (splitChildWeight > 0.0)) { - double allocatedHeight = (splitChild.equals(lastWeightedChild)) - ? availableHeight - : Math.rint(splitChildWeight * extraHeight); - double newHeight = splitChildBounds.getHeight() + allocatedHeight; - Rectangle newSplitChildBounds = boundsWithYandHeight(bounds, y, newHeight); - layout2(splitChild, newSplitChildBounds); - availableHeight -= allocatedHeight; - } - else { - double existingHeight = splitChildBounds.getHeight(); - Rectangle newSplitChildBounds = boundsWithYandHeight(bounds, y, existingHeight); - layout2(splitChild, newSplitChildBounds); - } - y = splitChild.getBounds().getMaxY(); - } - } - } - - - /* Second pass of the layout algorithm: branch to layoutGrow/Shrink - * as needed. - */ - private void layout2(Node root, Rectangle bounds) { - if (root instanceof Leaf) { - Component child = childForNode(root); - if (child != null) { - child.setBounds(bounds); - } - root.setBounds(bounds); - } - else if (root instanceof Divider) { - root.setBounds(bounds); - } - else if (root instanceof Split) { - Split split = (Split)root; - boolean grow = split.isRowLayout() - ? (split.getBounds().width <= bounds.width) - : (split.getBounds().height <= bounds.height); - if (grow) { - layoutGrow(split, bounds); - root.setBounds(bounds); - } - else { - layoutShrink(split, bounds); - // split.setBounds() called in layoutShrink() - } - } - } - - - /* First pass of the layout algorithm. - * - * If the Dividers are "floating" then set the bounds of each - * node to accomodate the preferred size of all of the - * Leaf's java.awt.Components. Otherwise, just set the bounds - * of each Leaf/Split node so that it's to the left of (for - * Split.isRowLayout() Split children) or directly above - * the Divider that follows. - * - * This pass sets the bounds of each Node in the layout model. It - * does not resize any of the parent Container's - * (java.awt.Component) children. That's done in the second pass, - * see layoutGrow() and layoutShrink(). - */ - private void layout1(Node root, Rectangle bounds) { - if (root instanceof Leaf) { - root.setBounds(bounds); - } - else if (root instanceof Split) { - Split split = (Split)root; - Iterator splitChildren = split.getChildren().iterator(); - Rectangle childBounds = null; - int divSize = getDividerSize(); - boolean initSplit = false; - - - /* Layout the Split's child Nodes' along the X axis. The bounds - * of each child will have the same y coordinate and height as the - * layout1() bounds argument. - * - * Note: the column layout code - that's the "else" clause below - * this if, is identical to the X axis (rowLayout) code below. - */ - if (split.isRowLayout()) { - double x = bounds.getX(); - while(splitChildren.hasNext()) { - Node splitChild = splitChildren.next(); - if ( !splitChild.isVisible()) { - if ( splitChildren.hasNext()) - splitChildren.next(); - continue; - } - Divider dividerChild = - (splitChildren.hasNext()) ? (Divider)(splitChildren.next()) : null; - - double childWidth = 0.0; - if (getFloatingDividers()) { - childWidth = preferredNodeSize(splitChild).getWidth(); - } - else { - if ((dividerChild != null) && dividerChild.isVisible()) { - double cw = dividerChild.getBounds().getX() - x; - if ( cw > 0.0 ) - childWidth = cw; - else { - childWidth = preferredNodeSize(splitChild).getWidth(); - initSplit = true; - } - } - else { - childWidth = split.getBounds().getMaxX() - x; - } - } - childBounds = boundsWithXandWidth(bounds, x, childWidth); - layout1(splitChild, childBounds); - - if (( initSplit || getFloatingDividers()) && (dividerChild != null) && dividerChild.isVisible()) { - double dividerX = childBounds.getMaxX(); - Rectangle dividerBounds; - dividerBounds = boundsWithXandWidth(bounds, dividerX, divSize); - dividerChild.setBounds(dividerBounds); - } - if ((dividerChild != null) && dividerChild.isVisible()) { - x = dividerChild.getBounds().getMaxX(); - } - } - } - - /* Layout the Split's child Nodes' along the Y axis. The bounds - * of each child will have the same x coordinate and width as the - * layout1() bounds argument. The algorithm is identical to what's - * explained above, for the X axis case. - */ - else { - double y = bounds.getY(); - while(splitChildren.hasNext()) { - Node splitChild = splitChildren.next(); - if ( !splitChild.isVisible()) { - if ( splitChildren.hasNext()) - splitChildren.next(); - continue; - } - Divider dividerChild = - (splitChildren.hasNext()) ? (Divider)(splitChildren.next()) : null; - - double childHeight = 0.0; - if (getFloatingDividers()) { - childHeight = preferredNodeSize(splitChild).getHeight(); - } - else { - if ((dividerChild != null) && dividerChild.isVisible()) { - double cy = dividerChild.getBounds().getY() - y; - if ( cy > 0.0 ) - childHeight = cy; - else { - childHeight = preferredNodeSize(splitChild).getHeight(); - initSplit = true; - } - } - else { - childHeight = split.getBounds().getMaxY() - y; - } - } - childBounds = boundsWithYandHeight(bounds, y, childHeight); - layout1(splitChild, childBounds); - - if (( initSplit || getFloatingDividers()) && (dividerChild != null) && dividerChild.isVisible()) { - double dividerY = childBounds.getMaxY(); - Rectangle dividerBounds = boundsWithYandHeight(bounds, dividerY, divSize); - dividerChild.setBounds(dividerBounds); - } - if ((dividerChild != null) && dividerChild.isVisible()) { - y = dividerChild.getBounds().getMaxY(); - } - } - } - /* The bounds of the Split node root are set to be just - * big enough to contain all of its children, but only - * along the axis it's allocating space on. That's - * X for rows, Y for columns. The second pass of the - * layout algorithm - see layoutShrink()/layoutGrow() - * allocates extra space. - */ - minimizeSplitBounds(split, bounds); - } - } - - /** - * Get the layout mode - * @return current layout mode - */ - public int getLayoutMode() - { - return layoutMode; - } - - /** - * Set the layout mode. By default this layout uses the preferred and minimum - * sizes of the child components. To ignore the minimum size set the layout - * mode to MultiSplitLayout.LAYOUT_NO_MIN_SIZE. - * @param layoutMode the layout mode - *

                  - *
                • DEFAULT_LAYOUT - use the preferred and minimum sizes when sizing the children
                • - *
                • LAYOUT_NO_MIN_SIZE - ignore the minimum size when sizing the children
                • - * - */ - public void setLayoutMode( int layoutMode ) - { - this.layoutMode = layoutMode; - } - - /** - * Get the minimum node size - * @return the minimum size - */ - public int getUserMinSize() - { - return userMinSize; - } - - /** - * Set the user defined minimum size support in the USER_MIN_SIZE_LAYOUT - * layout mode. - * @param minSize the new minimum size - */ - public void setUserMinSize( int minSize ) - { - userMinSize = minSize; - } - - /** - * Get the layoutByWeight falg. If the flag is true the layout initializes - * itself using the model weights - * @return the layoutByWeight - */ - public boolean getLayoutByWeight() - { - return layoutByWeight; - } - - /** - * Sset the layoutByWeight falg. If the flag is true the layout initializes - * itself using the model weights - * @param state the new layoutByWeight to set - */ - public void setLayoutByWeight( boolean state ) - { - layoutByWeight = state; - } - - /** - * The specified Node is either the wrong type or was configured - * incorrectly. - */ - public static class InvalidLayoutException extends RuntimeException { - private final Node node; - public InvalidLayoutException(String msg, Node node) { - super(msg); - this.node = node; - } - /** - * @return the invalid Node. - */ - public Node getNode() { return node; } - } - - private void throwInvalidLayout(String msg, Node node) { - throw new InvalidLayoutException(msg, node); - } - - private void checkLayout(Node root) { - if (root instanceof Split) { - Split split = (Split)root; - if (split.getChildren().size() <= 2) { - throwInvalidLayout("Split must have > 2 children", root); - } - Iterator splitChildren = split.getChildren().iterator(); - double weight = 0.0; - while(splitChildren.hasNext()) { - Node splitChild = splitChildren.next(); - if ( !splitChild.isVisible()) { - if ( splitChildren.hasNext()) - splitChildren.next(); - continue; - } - if (splitChild instanceof Divider) { - continue; - //throwInvalidLayout("expected a Split or Leaf Node", splitChild); - } - if (splitChildren.hasNext()) { - Node dividerChild = splitChildren.next(); - if (!(dividerChild instanceof Divider)) { - throwInvalidLayout("expected a Divider Node", dividerChild); - } - } - weight += splitChild.getWeight(); - checkLayout(splitChild); - } - if (weight > 1.0) { - throwInvalidLayout("Split children's total weight > 1.0", root); - } - } - } - - /** - * Compute the bounds of all of the Split/Divider/Leaf Nodes in - * the layout model, and then set the bounds of each child component - * with a matching Leaf Node. - */ - @Override -public void layoutContainer(Container parent) - { - if ( layoutByWeight && floatingDividers ) - doLayoutByWeight( parent ); - - checkLayout(getModel()); - Insets insets = parent.getInsets(); - Dimension size = parent.getSize(); - int width = size.width - (insets.left + insets.right); - int height = size.height - (insets.top + insets.bottom); - Rectangle bounds = new Rectangle( insets.left, insets.top, width, height); - layout1(getModel(), bounds); - layout2(getModel(), bounds); - } - - - private Divider dividerAt(Node root, int x, int y) { - if (root instanceof Divider) { - Divider divider = (Divider)root; - return (divider.getBounds().contains(x, y)) ? divider : null; - } - else if (root instanceof Split) { - Split split = (Split)root; - for(Node child : split.getChildren()) { - if ( !child.isVisible()) - continue; - if (child.getBounds().contains(x, y)) { - return dividerAt(child, x, y); - } - } - } - return null; - } - - /** - * Return the Divider whose bounds contain the specified - * point, or null if there isn't one. - * - * @param x x coordinate - * @param y y coordinate - * @return the Divider at x,y - */ - public Divider dividerAt(int x, int y) { - return dividerAt(getModel(), x, y); - } - - private boolean nodeOverlapsRectangle(Node node, Rectangle r2) { - Rectangle r1 = node.getBounds(); - return - (r1.x <= (r2.x + r2.width)) && ((r1.x + r1.width) >= r2.x) && - (r1.y <= (r2.y + r2.height)) && ((r1.y + r1.height) >= r2.y); - } - - private List dividersThatOverlap(Node root, Rectangle r) { - if (nodeOverlapsRectangle(root, r) && (root instanceof Split)) { - List dividers = new ArrayList(); - for(Node child : ((Split)root).getChildren()) { - if (child instanceof Divider) { - if (nodeOverlapsRectangle(child, r)) { - dividers.add((Divider)child); - } - } - else if (child instanceof Split) { - dividers.addAll(dividersThatOverlap(child, r)); - } - } - return dividers; - } - else { - return Collections.emptyList(); - } - } - - /** - * Return the Dividers whose bounds overlap the specified - * Rectangle. - * - * @param r target Rectangle - * @return the Dividers that overlap r - * @throws IllegalArgumentException if the Rectangle is null - */ - public List dividersThatOverlap(Rectangle r) { - if (r == null) { - throw new IllegalArgumentException("null Rectangle"); - } - return dividersThatOverlap(getModel(), r); - } - - - /** - * Base class for the nodes that model a MultiSplitLayout. - */ - public static abstract class Node implements Serializable { - private Split parent = null; - private Rectangle bounds = new Rectangle(); - private double weight = 0.0; - private boolean isVisible = true; - public void setVisible( boolean b ) { - isVisible = b; - } - - /** - * Determines whether this node should be visible when its - * parent is visible. Nodes are - * initially visible - * @return true if the node is visible, - * false otherwise - */ - public boolean isVisible() { - return isVisible; - } - - /** - * Returns the Split parent of this Node, or null. - * - * @return the value of the parent property. - * @see #setParent - */ - public Split getParent() { return parent; } - - /** - * Set the value of this Node's parent property. The default - * value of this property is null. - * - * @param parent a Split or null - * @see #getParent - */ - public void setParent(Split parent) { - this.parent = parent; - } - - /** - * Returns the bounding Rectangle for this Node. - * - * @return the value of the bounds property. - * @see #setBounds - */ - public Rectangle getBounds() { - return new Rectangle(this.bounds); - } - - /** - * Set the bounding Rectangle for this node. The value of - * bounds may not be null. The default value of bounds - * is equal to new Rectangle(0,0,0,0). - * - * @param bounds the new value of the bounds property - * @throws IllegalArgumentException if bounds is null - * @see #getBounds - */ - public void setBounds(Rectangle bounds) { - if (bounds == null) { - throw new IllegalArgumentException("null bounds"); - } - this.bounds = new Rectangle(bounds); - } - - /** - * Value between 0.0 and 1.0 used to compute how much space - * to add to this sibling when the layout grows or how - * much to reduce when the layout shrinks. - * - * @return the value of the weight property - * @see #setWeight - */ - public double getWeight() { return weight; } - - /** - * The weight property is a between 0.0 and 1.0 used to - * compute how much space to add to this sibling when the - * layout grows or how much to reduce when the layout shrinks. - * If rowLayout is true then this node's width grows - * or shrinks by (extraSpace * weight). If rowLayout is false, - * then the node's height is changed. The default value - * of weight is 0.0. - * - * @param weight a double between 0.0 and 1.0 - * @see #getWeight - * @see MultiSplitLayout#layoutContainer - * @throws IllegalArgumentException if weight is not between 0.0 and 1.0 - */ - public void setWeight(double weight) { - if ((weight < 0.0)|| (weight > 1.0)) { - throw new IllegalArgumentException("invalid weight"); - } - this.weight = weight; - } - - private Node siblingAtOffset(int offset) { - Split p = getParent(); - if (p == null) { return null; } - List siblings = p.getChildren(); - int index = siblings.indexOf(this); - if (index == -1) { return null; } - index += offset; - return ((index > -1) && (index < siblings.size())) ? siblings.get(index) : null; - } - - /** - * Return the Node that comes after this one in the parent's - * list of children, or null. If this node's parent is null, - * or if it's the last child, then return null. - * - * @return the Node that comes after this one in the parent's list of children. - * @see #previousSibling - * @see #getParent - */ - public Node nextSibling() { - return siblingAtOffset(+1); - } - - /** - * Return the Node that comes before this one in the parent's - * list of children, or null. If this node's parent is null, - * or if it's the last child, then return null. - * - * @return the Node that comes before this one in the parent's list of children. - * @see #nextSibling - * @see #getParent - */ - public Node previousSibling() { - return siblingAtOffset(-1); - } - } - - public static class RowSplit extends Split { - public RowSplit() { - } - - public RowSplit(Node... children) { - setChildren(children); - } - - /** - * Returns true if the this Split's children are to be - * laid out in a row: all the same height, left edge - * equal to the previous Node's right edge. If false, - * children are laid on in a column. - * - * @return the value of the rowLayout property. - * @see #setRowLayout - */ - @Override - public final boolean isRowLayout() { return true; } - } - - public static class ColSplit extends Split { - public ColSplit() { - } - - public ColSplit(Node... children) { - setChildren(children); - } - - /** - * Returns true if the this Split's children are to be - * laid out in a row: all the same height, left edge - * equal to the previous Node's right edge. If false, - * children are laid on in a column. - * - * @return the value of the rowLayout property. - * @see #setRowLayout - */ - @Override - public final boolean isRowLayout() { return false; } - } - - /** - * Defines a vertical or horizontal subdivision into two or more - * tiles. - */ - public static class Split extends Node { - private List children = Collections.emptyList(); - private boolean rowLayout = true; - private String name; - - public Split(Node... children) { - setChildren(children); - } - - /** - * Default constructor to support xml (de)serialization and other bean spec dependent ops. - * Resulting instance of Split is invalid until setChildren() is called. - */ - public Split() { - } - - /** - * Determines whether this node should be visible when its - * parent is visible. Nodes are - * initially visible - * @return true if the node is visible, - * false otherwise - */ - @Override - public boolean isVisible() { - for(Node child : children) { - if ( child.isVisible() && !( child instanceof Divider )) - return true; - } - return false; - } - - /** - * Returns true if the this Split's children are to be - * laid out in a row: all the same height, left edge - * equal to the previous Node's right edge. If false, - * children are laid on in a column. - * - * @return the value of the rowLayout property. - * @see #setRowLayout - */ - public boolean isRowLayout() { return rowLayout; } - - /** - * Set the rowLayout property. If true, all of this Split's - * children are to be laid out in a row: all the same height, - * each node's left edge equal to the previous Node's right - * edge. If false, children are laid on in a column. Default - * value is true. - * - * @param rowLayout true for horizontal row layout, false for column - * @see #isRowLayout - */ - public void setRowLayout(boolean rowLayout) { - this.rowLayout = rowLayout; - } - - /** - * Returns this Split node's children. The returned value - * is not a reference to the Split's internal list of children - * - * @return the value of the children property. - * @see #setChildren - */ - public List getChildren() { - return new ArrayList(children); - } - - - /** - * Remove a node from the layout. Any sibling dividers will also be removed - * @param n the node to be removed - */ - public void remove( Node n ) { - if ( n.nextSibling() instanceof Divider ) - children.remove( n.nextSibling() ); - else if ( n.previousSibling() instanceof Divider ) - children.remove( n.previousSibling() ); - children.remove( n ); - } - - /** - * Replace one node with another. This method is used when a child is removed - * from a split and the split is no longer required, in which case the - * remaining node in the child split can replace the split in the parent - * node - * @param target the node being replaced - * @param replacement the replacement node - */ - public void replace( Node target, Node replacement ) { - int idx = children.indexOf( target ); - children.remove( target ); - children.add( idx, replacement ); - - replacement.setParent ( this ); - target.setParent( this ); - } - - /** - * Change a node to being hidden. Any associated divider nodes are also hidden - * @param target the node to hide - */ - public void hide( Node target ){ - Node next = target.nextSibling(); - if ( next instanceof Divider ) - next.setVisible( false ); - else { - Node prev = target.previousSibling(); - if ( prev instanceof Divider ) - prev.setVisible( false ); - } - target.setVisible( false ); - } - - /** - * Check the dividers to ensure that redundant dividers are hidden and do - * not interfere in the layout, for example when all the children of a split - * are hidden (the split is then invisible), so two dividers may otherwise - * appear next to one another. - * @param split the split to check - */ - public void checkDividers( Split split ) { - ListIterator splitChildren = split.getChildren().listIterator(); - while( splitChildren.hasNext()) { - Node splitChild = splitChildren.next(); - if ( !splitChild.isVisible()) { - continue; - } - else if ( splitChildren.hasNext()) { - Node dividerChild = splitChildren.next(); - if ( dividerChild instanceof Divider ) { - if ( splitChildren.hasNext()) { - Node rightChild = splitChildren.next(); - while ( !rightChild.isVisible()) { - rightChild = rightChild.nextSibling(); - if ( rightChild == null ) { - // No visible right sibling found, so hide the divider - dividerChild.setVisible( false ); - break; - } - } - - // A visible child is found but it's a divider and therefore - // we have to visible and adjacent dividers - so we hide one - if (( rightChild != null ) && ( rightChild instanceof Divider )) - dividerChild.setVisible( false ); - } - } - else if (( splitChild instanceof Divider ) && ( dividerChild instanceof Divider )) { - splitChild.setVisible( false ); - } - } - } - } - - /** - * Restore any of the hidden dividers that are required to separate visible nodes - * @param split the node to check - */ - public void restoreDividers( Split split ) { - boolean nextDividerVisible = false; - ListIterator splitChildren = split.getChildren().listIterator(); - while( splitChildren.hasNext()) { - Node splitChild = splitChildren.next(); - if ( splitChild instanceof Divider ) { - Node prev = splitChild.previousSibling(); - if ( prev.isVisible()) { - Node next = splitChild.nextSibling(); - while ( next != null ) { - if ( next.isVisible()) { - splitChild.setVisible( true ); - break; - } - next = next.nextSibling(); - } - } - } - } - if ( split.getParent() != null ) - restoreDividers( split.getParent()); - } - - /** - * Set's the children property of this Split node. The parent - * of each new child is set to this Split node, and the parent - * of each old child (if any) is set to null. This method - * defensively copies the incoming List. Default value is - * an empty List. - * - * @param children List of children - * @see #getChildren - * @throws IllegalArgumentException if children is null - */ - public void setChildren(List children) { - if (children == null) { - throw new IllegalArgumentException("children must be a non-null List"); - } - for(Node child : this.children) { - child.setParent(null); - } - - this.children = new ArrayList(children); - for(Node child : this.children) { - child.setParent(this); - } - } - - /** - * Convenience method for setting the children of this Split node. The parent - * of each new child is set to this Split node, and the parent - * of each old child (if any) is set to null. This method - * defensively copies the incoming array. - * - * @param children array of children - * @see #getChildren - * @throws IllegalArgumentException if children is null - */ - public void setChildren(Node... children) { - setChildren(children == null ? null : Arrays.asList(children)); - } - - /** - * Convenience method that returns the last child whose weight - * is > 0.0. - * - * @return the last child whose weight is > 0.0. - * @see #getChildren - * @see Node#getWeight - */ - public final Node lastWeightedChild() { - List kids = getChildren(); - Node weightedChild = null; - for(Node child : kids) { - if ( !child.isVisible()) - continue; - if (child.getWeight() > 0.0) { - weightedChild = child; - } - } - return weightedChild; - } - - /** - * Return the Leaf's name. - * - * @return the value of the name property. - * @see #setName - */ - public String getName() { return name; } - - /** - * Set the value of the name property. Name may not be null. - * - * @param name value of the name property - * @throws IllegalArgumentException if name is null - */ - public void setName(String name) { - if (name == null) { - throw new IllegalArgumentException("name is null"); - } - this.name = name; - } - - @Override - public String toString() { - int nChildren = getChildren().size(); - StringBuffer sb = new StringBuffer("MultiSplitLayout.Split"); - sb.append(" \""); - sb.append(getName()); - sb.append("\""); - sb.append(isRowLayout() ? " ROW [" : " COLUMN ["); - sb.append(nChildren + ((nChildren == 1) ? " child" : " children")); - sb.append("] "); - sb.append(getBounds()); - return sb.toString(); - } - } - - - /** - * Models a java.awt Component child. - */ - public static class Leaf extends Node { - private String name = ""; - - /** - * Create a Leaf node. The default value of name is "". - */ - public Leaf() { } - - - /** - * Create a Leaf node with the specified name. Name can not - * be null. - * - * @param name value of the Leaf's name property - * @throws IllegalArgumentException if name is null - */ - public Leaf(String name) { - if (name == null) { - throw new IllegalArgumentException("name is null"); - } - this.name = name; - } - - /** - * Return the Leaf's name. - * - * @return the value of the name property. - * @see #setName - */ - public String getName() { return name; } - - /** - * Set the value of the name property. Name may not be null. - * - * @param name value of the name property - * @throws IllegalArgumentException if name is null - */ - public void setName(String name) { - if (name == null) { - throw new IllegalArgumentException("name is null"); - } - this.name = name; - } - - @Override - public String toString() { - StringBuffer sb = new StringBuffer("MultiSplitLayout.Leaf"); - sb.append(" \""); - sb.append(getName()); - sb.append("\""); - sb.append(" weight="); - sb.append(getWeight()); - sb.append(" "); - sb.append(getBounds()); - return sb.toString(); - } - } - - - /** - * Models a single vertical/horiztonal divider. - */ - public static class Divider extends Node { - /** - * Convenience method, returns true if the Divider's parent - * is a Split row (a Split with isRowLayout() true), false - * otherwise. In other words if this Divider's major axis - * is vertical, return true. - * - * @return true if this Divider is part of a Split row. - */ - public final boolean isVertical() { - Split parent = getParent(); - return (parent != null) ? parent.isRowLayout() : false; - } - - /** - * Dividers can't have a weight, they don't grow or shrink. - * @throws UnsupportedOperationException - */ - @Override - public void setWeight(double weight) { - throw new UnsupportedOperationException(); - } - - @Override - public String toString() { - return "MultiSplitLayout.Divider " + getBounds().toString(); - } - } - - - private static void throwParseException(StreamTokenizer st, String msg) throws Exception { - throw new Exception("MultiSplitLayout.parseModel Error: " + msg); - } - - private static void parseAttribute(String name, StreamTokenizer st, Node node) throws Exception { - if ((st.nextToken() != '=')) { - throwParseException(st, "expected '=' after " + name); - } - if (name.equalsIgnoreCase("WEIGHT")) { - if (st.nextToken() == StreamTokenizer.TT_NUMBER) { - node.setWeight(st.nval); - } - else { - throwParseException(st, "invalid weight"); - } - } - else if (name.equalsIgnoreCase("NAME")) { - if (st.nextToken() == StreamTokenizer.TT_WORD) { - if (node instanceof Leaf) { - ((Leaf)node).setName(st.sval); - } - else if (node instanceof Split) { - ((Split)node).setName(st.sval); - } - else { - throwParseException(st, "can't specify name for " + node); - } - } - else { - throwParseException(st, "invalid name"); - } - } - else { - throwParseException(st, "unrecognized attribute \"" + name + "\""); - } - } - - private static void addSplitChild(Split parent, Node child) { - List children = new ArrayList(parent.getChildren()); - if (children.size() == 0) { - children.add(child); - } - else { - children.add(new Divider()); - children.add(child); - } - parent.setChildren(children); - } - - private static void parseLeaf(StreamTokenizer st, Split parent) throws Exception { - Leaf leaf = new Leaf(); - int token; - while ((token = st.nextToken()) != StreamTokenizer.TT_EOF) { - if (token == ')') { - break; - } - if (token == StreamTokenizer.TT_WORD) { - parseAttribute(st.sval, st, leaf); - } - else { - throwParseException(st, "Bad Leaf: " + leaf); - } - } - addSplitChild(parent, leaf); - } - - private static void parseSplit(StreamTokenizer st, Split parent) throws Exception { - int token; - while ((token = st.nextToken()) != StreamTokenizer.TT_EOF) { - if (token == ')') { - break; - } - else if (token == StreamTokenizer.TT_WORD) { - if (st.sval.equalsIgnoreCase("WEIGHT")) { - parseAttribute(st.sval, st, parent); - } - else if (st.sval.equalsIgnoreCase("NAME")) { - parseAttribute(st.sval, st, parent); - } - else { - addSplitChild(parent, new Leaf(st.sval)); - } - } - else if (token == '(') { - if ((token = st.nextToken()) != StreamTokenizer.TT_WORD) { - throwParseException(st, "invalid node type"); - } - String nodeType = st.sval.toUpperCase(); - if (nodeType.equals("LEAF")) { - parseLeaf(st, parent); - } - else if (nodeType.equals("ROW") || nodeType.equals("COLUMN")) { - Split split = new Split(); - split.setRowLayout(nodeType.equals("ROW")); - addSplitChild(parent, split); - parseSplit(st, split); - } - else { - throwParseException(st, "unrecognized node type '" + nodeType + "'"); - } - } - } - } - - private static Node parseModel(Reader r) { - StreamTokenizer st = new StreamTokenizer(r); - try { - Split root = new Split(); - parseSplit(st, root); - return root.getChildren().get(0); - } - catch (Exception e) { - System.err.println(e); - } - finally { - try { r.close(); } catch (IOException ignore) {} - } - return null; - } - - /** - * A convenience method that converts a string to a - * MultiSplitLayout model (a tree of Nodes) using a - * a simple syntax. Nodes are represented by - * parenthetical expressions whose first token - * is one of ROW/COLUMN/LEAF. ROW and COLUMN specify - * horizontal and vertical Split nodes respectively, - * LEAF specifies a Leaf node. A Leaf's name and - * weight can be specified with attributes, - * name=myLeafName weight=myLeafWeight. - * Similarly, a Split's weight can be specified with - * weight=mySplitWeight. - * - *

                  For example, the following expression generates - * a horizontal Split node with three children: - * the Leafs named left and right, and a Divider in - * between: - *

                  -   * (ROW (LEAF name=left) (LEAF name=right weight=1.0))
                  -   * 
                  - * - *

                  Dividers should not be included in the string, - * they're added automatcially as needed. Because - * Leaf nodes often only need to specify a name, one - * can specify a Leaf by just providing the name. - * The previous example can be written like this: - *

                  -   * (ROW left (LEAF name=right weight=1.0))
                  -   * 
                  - * - *

                  Here's a more complex example. One row with - * three elements, the first and last of which are columns - * with two leaves each: - *

                  -   * (ROW (COLUMN weight=0.5 left.top left.bottom)
                  -   *      (LEAF name=middle)
                  -   *      (COLUMN weight=0.5 right.top right.bottom))
                  -   * 
                  - * - * - *

                  This syntax is not intended for archiving or - * configuration files . It's just a convenience for - * examples and tests. - * - * @return the Node root of a tree based on s. - */ - public static Node parseModel(String s) { - return parseModel(new StringReader(s)); - } - - - private static void printModel(String indent, Node root) { - if (root instanceof Split) { - Split split = (Split)root; - System.out.println(indent + split); - for(Node child : split.getChildren()) { - printModel(indent + " ", child); - } - } - else { - System.out.println(indent + root); - } - } - - /** - * Print the tree with enough detail for simple debugging. - */ - public static void printModel(Node root) { - printModel("", root); - } -} diff --git a/src/main/java/org/jdesktop/swingx/RepaintManagerX.java b/src/main/java/org/jdesktop/swingx/RepaintManagerX.java deleted file mode 100644 index 67c81cee0b..0000000000 --- a/src/main/java/org/jdesktop/swingx/RepaintManagerX.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * $Id: RepaintManagerX.java 4249 2012-11-13 18:12:49Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx; - -import javax.swing.*; -import java.awt.*; - -/** - *

                  An implementation of {@link RepaintManager} which adds support for transparency - * in {@link JXPanel}s. JXPanel (which supports translucency) will - * replace the current RepaintManager with an instance of RepaintManagerX - * unless the current RepaintManager is tagged by the {@link TranslucentRepaintManager} - * annotation.

                  - * - * @author zixle - * @author rbair - * @author Karl Schaefer - */ -@TranslucentRepaintManager -public class RepaintManagerX extends ForwardingRepaintManager { - /** - * Creates a new manager that forwards all calls to the delegate. - * - * @param delegate - * the manager backing this {@code RepaintManagerX} - * @throws NullPointerException - * if {@code delegate} is {@code null} - */ - public RepaintManagerX(RepaintManager delegate) { - super(delegate); - } - - /** - * {@inheritDoc} - */ - @Override - public void addDirtyRegion(JComponent c, int x, int y, int w, int h) { - AlphaPaintable alphaPaintable = (AlphaPaintable) SwingUtilities.getAncestorOfClass(AlphaPaintable.class, c); - - if (alphaPaintable != null && alphaPaintable.getAlpha() < 1f) { - Point p = SwingUtilities.convertPoint(c, x, y, (JComponent) alphaPaintable); - addDirtyRegion((JComponent) alphaPaintable, p.x, p.y, w, h); - } else { - super.addDirtyRegion(c, x, y, w, h); - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/ScrollableSizeHint.java b/src/main/java/org/jdesktop/swingx/ScrollableSizeHint.java deleted file mode 100644 index e5ed79b928..0000000000 --- a/src/main/java/org/jdesktop/swingx/ScrollableSizeHint.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * $Id$ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx; - -import org.jdesktop.swingx.util.Contract; - -import javax.swing.*; - -/** - * Sizing hints for layout, useful f.i. in a Scrollable implementation.

                  - * - * Inspired by Rob Camick. - *

                  - * PENDING JW: naming... suggestions?
                  - * KS: I'd go with TrackingHint or ScrollableTrackingHint, since it is used in getScrollableTracksViewportXXX. - * - * - * @author Jeanette Winzenburg - * @author Karl Schaefer - */ -@SuppressWarnings("nls") -public enum ScrollableSizeHint { - /** - * Size should be unchanged. - */ - NONE { - /** - * {@inheritDoc} - */ - @Override - boolean getTracksParentSizeImpl(JComponent component, int orientation) { - return false; - } - }, - - /** - * Size should be adjusted to parent size. - */ - FIT { - /** - * {@inheritDoc} - */ - @Override - boolean getTracksParentSizeImpl(JComponent component, int orientation) { - return true; - } - }, - - /** - * Stretches the component when its parent is larger than its minimum size. - */ - MINIMUM_STRETCH { - /** - * {@inheritDoc} - */ - @Override - boolean getTracksParentSizeImpl(JComponent component, int orientation) { - switch (orientation) { - case SwingConstants.HORIZONTAL: - return component.getParent() instanceof JViewport - && component.getParent().getWidth() > component.getMinimumSize().width - && component.getParent().getWidth() < component.getMaximumSize().width; - case SwingConstants.VERTICAL: - return component.getParent() instanceof JViewport - && component.getParent().getHeight() > component.getMinimumSize().height - && component.getParent().getHeight() < component.getMaximumSize().height; - default: - throw new IllegalArgumentException("invalid orientation"); - } - } - }, - - /** - * Stretches the component when its parent is larger than its preferred size. - */ - PREFERRED_STRETCH { - /** - * {@inheritDoc} - */ - @Override - boolean getTracksParentSizeImpl(JComponent component, int orientation) { - switch (orientation) { - case SwingConstants.HORIZONTAL: - return component.getParent() instanceof JViewport - && component.getParent().getWidth() > component.getPreferredSize().width - && component.getParent().getWidth() < component.getMaximumSize().width; - case SwingConstants.VERTICAL: - return component.getParent() instanceof JViewport - && component.getParent().getHeight() > component.getPreferredSize().height - && component.getParent().getHeight() < component.getMaximumSize().height; - default: - throw new IllegalArgumentException("invalid orientation"); - } - } - }, - ; - - /** - * Returns a boolean indicating whether the component's size should be - * adjusted to parent. - * - * @param component the component resize, must not be null - * @return a boolean indicating whether the component's size should be - * adjusted to parent - * - * @throws NullPointerException if component is null - * @throws IllegalArgumentException if orientation is invalid - */ - public boolean getTracksParentSize(JComponent component, int orientation) { - Contract.asNotNull(component, "component must be not-null"); - - return getTracksParentSizeImpl(component, orientation); - } - - /** - * Determines whether the supplied component is smaller than its parent; used to determine - * whether to track with the parents size. - * - * @param component - * the component to test - * @param orientation - * the orientation to test - * @return {@code true} to track; {@code false} otherwise - */ - abstract boolean getTracksParentSizeImpl(JComponent component, int orientation); -} diff --git a/src/main/java/org/jdesktop/swingx/StackLayout.java b/src/main/java/org/jdesktop/swingx/StackLayout.java deleted file mode 100644 index 8b18227229..0000000000 --- a/src/main/java/org/jdesktop/swingx/StackLayout.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * $Id: StackLayout.java 3793 2010-09-28 05:15:02Z kschaefe $ - * - * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx; - -import java.awt.*; -import java.util.LinkedList; -import java.util.List; - -/** - *

                  StackLayout is a Swing layout aimed to act as the layers - * stack of most popuplar graphics editing tools like The GIMP or - * Photoshop. While similar to CardLayout, this layout - * displays all the components of the container. If you are using non-rectangular - * components (i.e. transparent) you will see them from top to bottom of the - * stack.

                  - *

                  When using this layout, each component can be added in the container - * either on top of the stack or at the bottom:

                  - *
                  - * JPanel panel = new JPanel(new StackLayout());
                  - * panel.add(new JLabel("On top"),    StackLayout.TOP);
                  - * panel.add(new JLabel("At bottom"), StackLayout.BOTTOM);
                  - * 
                  - * If you don't specify the constraint, the component will be added at the top - * of the components stack.

                  - *

                  All the components managed by this layout will be given the same size as - * the container itself. The minimum, maximum and preferred size of the - * container are based upon the largest minimum, maximum and preferred size of - * the children components.

                  - *

                  StackLayout works only with JSE 1.5 and Java SE 6 and - * greater.

                  - * - * @author Romain Guy - */ - -public class StackLayout implements LayoutManager2 { - /** Use this constraint to add a component at the bottom of the stack. */ - public static final String BOTTOM = "bottom"; - /** Use this contrainst to add a component at the top of the stack. */ - public static final String TOP = "top"; - - // removing components does not happen often compared to adding components - // hence we choose a linked list to make insertion at the bottom faster - private List components = new LinkedList(); - - /** - * {@inheritDoc} - */ - @Override - public void addLayoutComponent(final Component comp, - final Object constraints) { - synchronized (comp.getTreeLock()) { - if (BOTTOM.equals(constraints)) { - components.add(0, comp); - } else if (TOP.equals(constraints)) { - components.add(comp); - } else { - components.add(comp); - } - } - } - - /** - * {@inheritDoc} - */ - @Override - public void addLayoutComponent(final String name, final Component comp) { - addLayoutComponent(comp, TOP); - } - - /** - * {@inheritDoc} - */ - @Override - public void removeLayoutComponent(final Component comp) { - synchronized (comp.getTreeLock()) { - components.remove(comp); - } - } - - /** - * {@inheritDoc} - */ - @Override - public float getLayoutAlignmentX(final Container target) { - return 0.5f; - } - - /** - * {@inheritDoc} - */ - @Override - public float getLayoutAlignmentY(final Container target) { - return 0.5f; - } - - /** - * {@inheritDoc} - */ - @Override - public void invalidateLayout(final Container target) { - } - - /** - * {@inheritDoc} - */ - @Override - public Dimension preferredLayoutSize(final Container parent) { - synchronized (parent.getTreeLock()) { - int width = 0; - int height = 0; - - for (Component comp: components) { - Dimension size = comp.getPreferredSize(); - width = Math.max(size.width, width); - height = Math.max(size.height, height); - } - - Insets insets = parent.getInsets(); - width += insets.left + insets.right; - height += insets.top + insets.bottom; - - return new Dimension(width, height); - } - } - - /** - * {@inheritDoc} - */ - @Override - public Dimension minimumLayoutSize(final Container parent) { - synchronized (parent.getTreeLock()) { - int width = 0; - int height = 0; - - for (Component comp: components) { - Dimension size = comp.getMinimumSize(); - width = Math.max(size.width, width); - height = Math.max(size.height, height); - } - - Insets insets = parent.getInsets(); - width += insets.left + insets.right; - height += insets.top + insets.bottom; - - return new Dimension(width, height); - } - } - - /** - * {@inheritDoc} - */ - @Override - public Dimension maximumLayoutSize(final Container target) { - return new Dimension(Integer.MAX_VALUE, - Integer.MAX_VALUE); - } - - /** - * {@inheritDoc} - */ - @Override - public void layoutContainer(final Container parent) { - synchronized (parent.getTreeLock()) { - int width = parent.getWidth(); - int height = parent.getHeight(); - - Rectangle bounds = new Rectangle(0, 0, width, height); - - int componentsCount = components.size(); - - for (int i = 0; i < componentsCount; i++) { - Component comp = components.get(i); - comp.setBounds(bounds); - parent.setComponentZOrder(comp, componentsCount - i - 1); - } - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/SwingXUtilities.java b/src/main/java/org/jdesktop/swingx/SwingXUtilities.java deleted file mode 100644 index fade647a3d..0000000000 --- a/src/main/java/org/jdesktop/swingx/SwingXUtilities.java +++ /dev/null @@ -1,603 +0,0 @@ -/* - * $Id: SwingXUtilities.java 4262 2012-11-19 18:40:10Z kschaefe $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx; - -import org.jdesktop.swingx.painter.Painter; - -import javax.swing.*; -import javax.swing.plaf.ComponentInputMapUIResource; -import javax.swing.plaf.UIResource; -import javax.swing.text.html.HTMLDocument; -import java.awt.*; -import java.awt.event.InputEvent; -import java.awt.event.MouseEvent; -import java.io.IOException; -import java.io.StringReader; -import java.lang.reflect.InvocationTargetException; -import java.util.Locale; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.FutureTask; - -/** - * A collection of utility methods for Swing(X) classes. - * - *
                    - * PENDING JW: think about location of this class and/or its methods, Options: - * - *
                  • move this class to the swingx utils package which already has a bunch of xxUtils - *
                  • move methods between xxUtils classes as appropriate (one window/comp related util) - *
                  • keep here in swingx (consistent with swingutilities in core) - *
                  - * @author Karl George Schaefer - */ -public final class SwingXUtilities { - private SwingXUtilities() { - //does nothing - } - - - /** - * A helper for creating and updating key bindings for components with - * mnemonics. The {@code pressed} action will be invoked when the mnemonic - * is activated. - * - * @param c - * the component bindings to update - * @param pressed - * the name of the action in the action map to invoke when the - * mnemonic is pressed - * @throws NullPointerException - * if the component is {@code null} - */ - public static void updateMnemonicBinding(T c, String pressed) { - updateMnemonicBinding(c, pressed, null); - } - - /** - * A helper for creating and updating key bindings for components with - * mnemonics. The {@code pressed} action will be invoked when the mnemonic - * is activated and the {@code released} action will be invoked when the - * mnemonic is deactivated. - * - * @param c - * the component bindings to update - * @param pressed - * the name of the action in the action map to invoke when the - * mnemonic is pressed - * @param released - * the name of the action in the action map to invoke when the - * mnemonic is released (if the action is a toggle style, then - * this parameter should be {@code null}) - * @throws NullPointerException - * if the component is {@code null} - */ - public static void updateMnemonicBinding(T c, String pressed, String released) { - int m = c.getMnemonic(); - - InputMap map = SwingUtilities.getUIInputMap(c, - JComponent.WHEN_IN_FOCUSED_WINDOW); - - if (m != 0) { - if (map == null) { - map = new ComponentInputMapUIResource(c); - SwingUtilities.replaceUIInputMap(c, - JComponent.WHEN_IN_FOCUSED_WINDOW, map); - } - - map.clear(); - - //TODO is ALT_MASK right for all platforms? - map.put(KeyStroke.getKeyStroke(m, InputEvent.ALT_MASK, false), - pressed); - map.put(KeyStroke.getKeyStroke(m, InputEvent.ALT_MASK, true), - released); - map.put(KeyStroke.getKeyStroke(m, 0, true), released); - } else { - if (map != null) { - map.clear(); - } - } - } - - @SuppressWarnings("unchecked") - static void paintBackground(C comp, Graphics2D g) { - // we should be painting the background behind the painter if we have one - // this prevents issues with buffer reuse where visual artifacts sneak in - if (comp.isOpaque() - || (comp instanceof AlphaPaintable && ((AlphaPaintable) comp).getAlpha() < 1f) - || UIManager.getLookAndFeel().getID().equals("Nimbus")) { - g.setColor(comp.getBackground()); - g.fillRect(0, 0, comp.getWidth(), comp.getHeight()); - } - - Painter painter = comp.getBackgroundPainter(); - - if (painter != null) { - if (comp.isPaintBorderInsets()) { - painter.paint(g, comp, comp.getWidth(), comp.getHeight()); - } else { - Insets insets = comp.getInsets(); - g.translate(insets.left, insets.top); - painter.paint(g, comp, comp.getWidth() - insets.left - insets.right, - comp.getHeight() - insets.top - insets.bottom); - g.translate(-insets.left, -insets.top); - } - } - } - - private static Component[] getChildren(Component c) { - Component[] children = null; - - if (c instanceof MenuElement) { - MenuElement[] elements = ((MenuElement) c).getSubElements(); - children = new Component[elements.length]; - - for (int i = 0; i < elements.length; i++) { - children[i] = elements[i].getComponent(); - } - } else if (c instanceof Container) { - children = ((Container) c).getComponents(); - } - - return children; - } - - /** - * Enables or disables of the components in the tree starting with {@code c}. - * - * @param c - * the starting component - * @param enabled - * {@code true} if the component is to enabled; {@code false} otherwise - */ - public static void setComponentTreeEnabled(Component c, boolean enabled) { - c.setEnabled(enabled); - - Component[] children = getChildren(c); - - if (children != null) { - for(int i = 0; i < children.length; i++) { - setComponentTreeEnabled(children[i], enabled); - } - } - } - - /** - * Sets the locale for an entire component hierarchy to the specified - * locale. - * - * @param c - * the starting component - * @param locale - * the locale to set - */ - public static void setComponentTreeLocale(Component c, Locale locale) { - c.setLocale(locale); - - Component[] children = getChildren(c); - - if (children != null) { - for(int i = 0; i < children.length; i++) { - setComponentTreeLocale(children[i], locale); - } - } - } - - /** - * Sets the background for an entire component hierarchy to the specified - * color. - * - * @param c - * the starting component - * @param color - * the color to set - */ - public static void setComponentTreeBackground(Component c, Color color) { - c.setBackground(color); - - Component[] children = getChildren(c); - - if (children != null) { - for(int i = 0; i < children.length; i++) { - setComponentTreeBackground(children[i], color); - } - } - } - - /** - * Sets the foreground for an entire component hierarchy to the specified - * color. - * - * @param c - * the starting component - * @param color - * the color to set - */ - public static void setComponentTreeForeground(Component c, Color color) { - c.setForeground(color); - - Component[] children = getChildren(c); - - if (children != null) { - for(int i = 0; i < children.length; i++) { - setComponentTreeForeground(children[i], color); - } - } - } - - /** - * Sets the font for an entire component hierarchy to the specified font. - * - * @param c - * the starting component - * @param font - * the font to set - */ - public static void setComponentTreeFont(Component c, Font font) { - c.setFont(font); - - Component[] children = getChildren(c); - - if (children != null) { - for(int i = 0; i < children.length; i++) { - setComponentTreeFont(children[i], font); - } - } - } - - private static String STYLESHEET = - "body { margin-top: 0; margin-bottom: 0; margin-left: 0; margin-right: 0;" - + " font-family: %s; font-size: %dpt; }" - + "a, p, li { margin-top: 0; margin-bottom: 0; margin-left: 0;" - + " margin-right: 0; font-family: %s; font-size: %dpt; }"; - - /** - * Sets the font used for HTML displays to the specified font. Components - * that display HTML do not necessarily honor font properties, since the - * HTML document can override these values. Calling {@code setHtmlFont} - * after the data is set will force the HTML display to use the font - * specified to this method. - * - * @param doc - * the HTML document to update - * @param font - * the font to use - * @throws NullPointerException - * if any parameter is {@code null} - */ - public static void setHtmlFont(HTMLDocument doc, Font font) { - String stylesheet = String.format(STYLESHEET, font.getName(), - font.getSize(), font.getName(), font.getSize()); - - try { - doc.getStyleSheet().loadRules(new StringReader(stylesheet), null); - } catch (IOException e) { - //this should never happen with our sheet - throw new IllegalStateException(e); - } - } - - /** - * Updates the componentTreeUI of all top-level windows of the - * current application. - * - */ - public static void updateAllComponentTreeUIs() { -// for (Frame frame : Frame.getFrames()) { -// updateAllComponentTreeUIs(frame); -// } - // JW: updated to new 1.6 api - returns all windows, owned and ownerless - for (Window window: Window.getWindows()) { - SwingUtilities.updateComponentTreeUI(window); - } - } - - - - /** - * Updates the componentTreeUI of the given window and all its - * owned windows, recursively. - * - * - * @param window the window to update - */ - public static void updateAllComponentTreeUIs(Window window) { - SwingUtilities.updateComponentTreeUI(window); - for (Window owned : window.getOwnedWindows()) { - updateAllComponentTreeUIs(owned); - } - } - - /** - * A version of {@link SwingUtilities#invokeLater(Runnable)} that supports return values. - * - * @param - * the return type of the callable - * @param callable - * the callable to execute - * @return a future task for accessing the return value - * @see Callable - */ - public static FutureTask invokeLater(Callable callable) { - FutureTask task = new FutureTask(callable); - - SwingUtilities.invokeLater(task); - - return task; - } - - /** - * A version of {@link SwingUtilities#invokeAndWait(Runnable)} that supports return values. - * - * @param - * the return type of the callable - * @param callable - * the callable to execute - * @return the value returned by the callable - * @throws InterruptedException - * if we're interrupted while waiting for the event dispatching thread to finish - * executing {@code callable.call()} - * @throws InvocationTargetException - * if an exception is thrown while running {@code callable} - * @see Callable - */ - public static T invokeAndWait(Callable callable) throws InterruptedException, - InvocationTargetException { - try { - //blocks until future returns - return invokeLater(callable).get(); - } catch (ExecutionException e) { - Throwable t = e.getCause(); - - if (t instanceof RuntimeException) { - throw (RuntimeException) t; - } else if (t instanceof InvocationTargetException) { - throw (InvocationTargetException) t; - } else { - throw new InvocationTargetException(t); - } - } - } - - /** - * An improved version of - * {@link SwingUtilities#getAncestorOfClass(Class, Component)}. This method - * traverses {@code JPopupMenu} invoker and uses generics to return an - * appropriately typed object. - * - * @param - * the type of ancestor to find - * @param clazz - * the class instance of the ancestor to find - * @param c - * the component to start the search from - * @return an ancestor of the correct type or {@code null} if no such - * ancestor exists. This method also returns {@code null} if any - * parameter is {@code null}. - */ - @SuppressWarnings("unchecked") - public static T getAncestor(Class clazz, Component c) { - if (clazz == null || c == null) { - return null; - } - - Component parent = c.getParent(); - - while (parent != null && !(clazz.isInstance(parent))) { - parent = parent instanceof JPopupMenu - ? ((JPopupMenu) parent).getInvoker() : parent.getParent(); - } - - return (T) parent; - } - - /** - * Returns whether the component is part of the parent's - * container hierarchy. If a parent in the chain is of type - * JPopupMenu, the parent chain of its invoker is walked. - * - * @param focusOwner - * @param parent - * @return true if the component is contained under the parent's - * hierarchy, coping with JPopupMenus. - */ - public static boolean isDescendingFrom(Component focusOwner, Component parent) { - while (focusOwner != null) { - if (focusOwner instanceof JPopupMenu) { - focusOwner = ((JPopupMenu) focusOwner).getInvoker(); - if (focusOwner == null) { - return false; - } - } - if (focusOwner == parent) { - return true; - } - focusOwner = focusOwner.getParent(); - } - return false; - } - - /** - * Obtains a {@code TranslucentRepaintManager} from the specified manager. - * If the current manager is a {@code TranslucentRepaintManager} or a - * {@code ForwardingRepaintManager} that contains a {@code - * TranslucentRepaintManager}, then the passed in manager is returned. - * Otherwise a new repaint manager is created and returned. - * - * @param delegate - * the current repaint manager - * @return a non-{@code null} {@code TranslucentRepaintManager} - * @throws NullPointerException if {@code delegate} is {@code null} - */ - static RepaintManager getTranslucentRepaintManager(RepaintManager delegate) { - RepaintManager manager = delegate; - - while (manager != null && !manager.getClass().isAnnotationPresent(TranslucentRepaintManager.class)) { - if (manager instanceof ForwardingRepaintManager) { - manager = ((ForwardingRepaintManager) manager).getDelegateManager(); - } else { - manager = null; - } - } - - return manager == null ? new RepaintManagerX(delegate) : delegate; - } - - /** - * Checks and returns whether the given property should be replaced - * by the UI's default value. - * - * @param property the property to check. - * @return true if the given property should be replaced by the UI's - * default value, false otherwise. - */ - public static boolean isUIInstallable(Object property) { - return (property == null) || (property instanceof UIResource); - } - -//---- methods c&p'ed from SwingUtilities2 to reduce dependencies on sun packages - - /** - * Updates lead and anchor selection index without changing the selection. - * - * Note: this is c&p'ed from SwingUtilities2 to not have any direct - * dependency. - * - * @param selectionModel the selection model to change lead/anchor - * @param lead the lead selection index - * @param anchor the anchor selection index - */ - public static void setLeadAnchorWithoutSelection( - ListSelectionModel selectionModel, int lead, int anchor) { - if (anchor == -1) { - anchor = lead; - } - if (lead == -1) { - selectionModel.setAnchorSelectionIndex(-1); - selectionModel.setLeadSelectionIndex(-1); - } else { - if (selectionModel.isSelectedIndex(lead)) - selectionModel.addSelectionInterval(lead, lead); - else { - selectionModel.removeSelectionInterval(lead, lead); - } - selectionModel.setAnchorSelectionIndex(anchor); - } - } - - public static boolean shouldIgnore(MouseEvent mouseEvent, - JComponent component) { - return ((component == null) || (!(component.isEnabled())) - || (!(SwingUtilities.isLeftMouseButton(mouseEvent))) - || (mouseEvent.isConsumed())); - } - - - public static int loc2IndexFileList(JList list, Point point) { - int i = list.locationToIndex(point); - if (i != -1) { - Object localObject = list - .getClientProperty("List.isFileList"); - if ((localObject instanceof Boolean) - && (((Boolean) localObject).booleanValue()) - // PENDING JW: this isn't aware of sorting/filtering - fix! - && (!(pointIsInActualBounds(list, i, point)))) { - i = -1; - } - } - return i; - } - - // PENDING JW: this isn't aware of sorting/filtering - fix! - private static boolean pointIsInActualBounds(JList list, int index, - Point point) { - ListCellRenderer renderer = list.getCellRenderer(); - ListModel model = list.getModel(); - Object element = model.getElementAt(index); - Component comp = renderer.getListCellRendererComponent(list, element, - index, false, false); - - Dimension prefSize = comp.getPreferredSize(); - Rectangle cellBounds = list.getCellBounds(index, index); - if (!(comp.getComponentOrientation().isLeftToRight())) { - cellBounds.x += cellBounds.width - prefSize.width; - } - cellBounds.width = prefSize.width; - - return cellBounds.contains(point); - } - - public static void adjustFocus(JComponent component) { - if ((!(component.hasFocus())) && (component.isRequestFocusEnabled())) - component.requestFocus(); - } - - public static int convertModifiersToDropAction(int modifiers, - int sourcActions) { - // PENDING JW: c'p from a decompiled SunDragSourceContextPeer - // PENDING JW: haha ... completely readable, right ;-) - int i = 0; - - switch (modifiers & 0xC0) { - case 192: - i = 1073741824; - break; - case 128: - i = 1; - break; - case 64: - i = 2; - break; - default: - if ((sourcActions & 0x2) != 0) { - i = 2; - break; - } - if ((sourcActions & 0x1) != 0) { - i = 1; - break; - } - if ((sourcActions & 0x40000000) == 0) - break; - i = 1073741824; - } - - // label88: - return (i & sourcActions); - } - - private static final Class appletClass; - - static { - Class cls; - try { - cls = Class.forName("java.applet.Applet"); - } catch (ClassNotFoundException ex) { - cls = null; - } - appletClass = cls; - } - - static boolean isApplet(Component c) { - return appletClass != null && appletClass.isInstance(c); - } -} diff --git a/src/main/java/org/jdesktop/swingx/TranslucentRepaintManager.java b/src/main/java/org/jdesktop/swingx/TranslucentRepaintManager.java deleted file mode 100644 index 44ad694ba9..0000000000 --- a/src/main/java/org/jdesktop/swingx/TranslucentRepaintManager.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * $Id: TranslucentRepaintManager.java 2476 2007-11-25 15:52:59Z kschaefe $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - *

                  An annotation that can be applied to a {@link javax.swing.RepaintManager} to suggest that - * the RepaintManager supports translucency. If a JXPanel - * is made translucent by setting it's alpha property to a value between 0 and 1, - * then the JXPanel must ensure that a RepaintManager - * capable of handling transparency is installed. This annotation tells the - * JXPanel that the installed RepaintManager does not - * need to be replaced. This is critical for custom RepaintManagers - * which are used in applications along with transparent JXPanels.

                  - * - *

                  A RepaintManager supports translucency if, when a repaint on a - * child component occurs, it begins painting not on the child component, - * but on the child component's JXPanel ancestor if: a) there is such - * an ancestor and b) the ancestor returns an effective alpha of < 1.

                  - * - * @see RepaintManagerX - * @see JXPanel - * @author rbair - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -public @interface TranslucentRepaintManager { -} diff --git a/src/main/java/org/jdesktop/swingx/VerticalLayout.java b/src/main/java/org/jdesktop/swingx/VerticalLayout.java deleted file mode 100644 index 1a88edc608..0000000000 --- a/src/main/java/org/jdesktop/swingx/VerticalLayout.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * $Id: VerticalLayout.java 4147 2012-02-01 17:13:24Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.util.Separator; - -import java.awt.*; -import java.io.Serial; - -/** - * Organizes components in a vertical layout. - * - * @author fred - * @author Karl Schaefer - */ -@JavaBean -public class VerticalLayout extends AbstractLayoutManager { - @Serial - private static final long serialVersionUID = 5342270033773736441L; - - private int gap; - - /** - * Creates a layout without a gap between components. - */ - public VerticalLayout() { - this(0); - } - - /** - * Creates a layout with the specified gap between components. - * - * @param gap - * the gap between components - */ - //TODO should we allow negative gaps? - public VerticalLayout(int gap) { - this.gap = gap; - } - - /** - * The current gap to place between components. - * - * @return the current gap - */ - public int getGap() { - return gap; - } - - /** - * The new gap to place between components. - * - * @param gap - * the new gap - */ - //TODO should we allow negative gaps? - public void setGap(int gap) { - this.gap = gap; - } - - /** - * {@inheritDoc} - */ - @Override - public Dimension preferredLayoutSize(Container parent) { - Dimension pref = new Dimension(0, 0); - Separator sep = new Separator<>(0, gap); - - for (int i = 0, c = parent.getComponentCount(); i < c; i++) { - Component m = parent.getComponent(i); - - if (m.isVisible()) { - Dimension componentPreferredSize = parent.getComponent(i).getPreferredSize(); - pref.height += componentPreferredSize.height + sep.get(); - pref.width = Math.max(pref.width, componentPreferredSize.width); - } - } - - Insets insets = parent.getInsets(); - pref.width += insets.left + insets.right; - pref.height += insets.top + insets.bottom; - - return pref; - } - - /** - * {@inheritDoc} - */ - @Override - public void layoutContainer(Container parent) { - Insets insets = parent.getInsets(); - Dimension size = parent.getSize(); - int width = size.width - insets.left - insets.right; - int height = insets.top; - - for (int i = 0, c = parent.getComponentCount(); i < c; i++) { - Component m = parent.getComponent(i); - if (m.isVisible()) { - m.setBounds(insets.left, height, width, m.getPreferredSize().height); - height += m.getSize().height + gap; - } - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/WrapLayout.java b/src/main/java/org/jdesktop/swingx/WrapLayout.java deleted file mode 100644 index 71659932e0..0000000000 --- a/src/main/java/org/jdesktop/swingx/WrapLayout.java +++ /dev/null @@ -1,176 +0,0 @@ -package org.jdesktop.swingx; - -import javax.swing.*; -import java.awt.*; - -/** - * FlowLayout subclass that fully supports wrapping of components. - */ -public class WrapLayout extends FlowLayout { - private static final long serialVersionUID = 1L; - - /** - * Constructs a new WrapLayout with a left alignment and a default 5-unit - * horizontal and vertical gap. - */ - public WrapLayout() { - super(); - } - - /** - * Constructs a new FlowLayout with the specified alignment and a default 5-unit - * horizontal and vertical gap. The value of the alignment argument must be one of - * WrapLayout, WrapLayout, or WrapLayout. - * - * @param align - * the alignment value - */ - public WrapLayout(int align) { - super(align); - } - - /** - * Creates a new flow layout manager with the indicated alignment and the indicated horizontal - * and vertical gaps. - *

                  - * The value of the alignment argument must be one of WrapLayout, - * WrapLayout, or WrapLayout. - * - * @param align - * the alignment value - * @param hgap - * the horizontal gap between components - * @param vgap - * the vertical gap between components - */ - public WrapLayout(int align, int hgap, int vgap) { - super(align, hgap, vgap); - } - - /** - * Returns the preferred dimensions for this layout given the visible components in the - * specified target container. - * - * @param target - * the component which needs to be laid out - * @return the preferred dimensions to lay out the subcomponents of the specified container - */ - @Override - public Dimension preferredLayoutSize(Container target) { - return layoutSize(target, true); - } - - /** - * Returns the minimum dimensions needed to layout the visible components contained in - * the specified target container. - * - * @param target - * the component which needs to be laid out - * @return the minimum dimensions to lay out the subcomponents of the specified container - */ - @Override - public Dimension minimumLayoutSize(Container target) { - Dimension minimum = layoutSize(target, false); - minimum.width -= (getHgap() + 1); - return minimum; - } - - /** - * Returns the minimum or preferred dimension needed to layout the target container. - * - * @param target - * target to get layout size for - * @param preferred - * should preferred size be calculated - * @return the dimension to layout the target container - */ - private Dimension layoutSize(Container target, boolean preferred) { - synchronized (target.getTreeLock()) { - // Each row must fit with the width allocated to the containter. - // When the container width = 0, the preferred width of the container - // has not yet been calculated so lets ask for the maximum. - - int targetWidth = target.getSize().width; - - if (targetWidth == 0) - targetWidth = Integer.MAX_VALUE; - - int hgap = getHgap(); - int vgap = getVgap(); - Insets insets = target.getInsets(); - int horizontalInsetsAndGap = insets.left + insets.right + (hgap * 2); - int maxWidth = targetWidth - horizontalInsetsAndGap; - - // Fit components into the allowed width - - Dimension dim = new Dimension(0, 0); - int rowWidth = 0; - int rowHeight = 0; - - int nmembers = target.getComponentCount(); - - for (int i = 0; i < nmembers; i++) { - Component m = target.getComponent(i); - - if (m.isVisible()) { - Dimension d = preferred ? m.getPreferredSize() : m.getMinimumSize(); - - // Can't add the component to current row. Start a new row. - - if (rowWidth + d.width > maxWidth) { - addRow(dim, rowWidth, rowHeight); - rowWidth = 0; - rowHeight = 0; - } - - // Add a horizontal gap for all components after the first - - if (rowWidth != 0) { - rowWidth += hgap; - } - - rowWidth += d.width; - rowHeight = Math.max(rowHeight, d.height); - } - } - - addRow(dim, rowWidth, rowHeight); - - dim.width += horizontalInsetsAndGap; - dim.height += insets.top + insets.bottom + vgap * 2; - - // When using a scroll pane or the DecoratedLookAndFeel we need to - // make sure the preferred size is less than the size of the - // target containter so shrinking the container size works - // correctly. Removing the horizontal gap is an easy way to do this. - - Container scrollPane = SwingUtilities.getAncestorOfClass(JScrollPane.class, target); - - if (scrollPane != null) { - dim.width -= (hgap + 1); - } - - return dim; - } - } - - /* - * A new row has been completed. Use the dimensions of this row to update the preferred size for - * the container. - * - * @param dim update the width and height when appropriate - * - * @param rowWidth the width of the row to add - * - * @param rowHeight the height of the row to add - */ - private void addRow(Dimension dim, int rowWidth, int rowHeight) { - dim.width = Math.max(dim.width, rowWidth); - - if (dim.height > 0) { - dim.height += getVgap(); - } - - dim.height += rowHeight; - } -} diff --git a/src/main/java/org/jdesktop/swingx/action/AbstractActionExt.java b/src/main/java/org/jdesktop/swingx/action/AbstractActionExt.java deleted file mode 100644 index bf77e43af8..0000000000 --- a/src/main/java/org/jdesktop/swingx/action/AbstractActionExt.java +++ /dev/null @@ -1,443 +0,0 @@ -/* - * $Id: AbstractActionExt.java 4158 2012-02-03 18:29:40Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.action; - -import javax.swing.*; -import java.awt.event.ItemEvent; -import java.awt.event.ItemListener; -import java.beans.PropertyChangeListener; - -/** - * Extends the concept of the Action to include toggle or group states. - *

                  - * SwingX 1.6.3 updates {@code AbstractActionExt} to use new features of {@link Action} that were - * added in {@code Java 1.6}. The selection is now managed with {@link Action#SELECTED_KEY}, which - * allows the action to correctly configured Swing buttons. The {@link #LARGE_ICON} has also been - * changed to correspond to {@link Action#LARGE_ICON_KEY}. - * - */ -public abstract class AbstractActionExt extends AbstractAction - implements ItemListener { - - /** - * The key for the large icon - *

                  - * As of SwingX 1.6.3 is now has the same value as {@link Action#LARGE_ICON_KEY}, which is new to 1.6. - */ - public static final String LARGE_ICON = Action.LARGE_ICON_KEY; - - /** - * The key for the button group - */ - public static final String GROUP = "__Group__"; - - /** - * The key for the flag which indicates that this is a state type. - */ - public static final String IS_STATE = "__State__"; - - /** - * Default constructor, does nothing. - */ - public AbstractActionExt() { - this((String) null); - } - - /** - * Copy constructor copies the state. - */ - public AbstractActionExt(AbstractActionExt action) { - Object[] keys = action.getKeys(); - for (int i = 0; i < keys.length; i++) { - putValue((String)keys[i], action.getValue((String)keys[i])); - } - this.enabled = action.enabled; - - // Copy change listeners. - PropertyChangeListener[] listeners = action.getPropertyChangeListeners(); - for (int i = 0; i < listeners.length; i++) { - addPropertyChangeListener(listeners[i]); - } - } - - public AbstractActionExt(String name) { - super(name); - } - - public AbstractActionExt(String name, Icon icon) { - super(name, icon); - } - - /** - * Constructs an Action with the label and command - * - * @param name name of the action usually used as a label - * @param command command key of the action - */ - public AbstractActionExt(String name, String command) { - this(name); - setActionCommand(command); - } - - /** - * @param name display name of the action - * @param command the value of the action command key - * @param icon icon to display - */ - public AbstractActionExt(String name, String command, Icon icon) { - super(name, icon); - setActionCommand(command); - } - /** - * Returns a short description of the action. - * - * @return the short description or null - */ - public String getShortDescription() { - return (String)getValue(Action.SHORT_DESCRIPTION); - } - - /** - * Sets the short description of the action. This will also - * set the long description value is it is null. - *

                  - * This is a convenience method for putValue with the - * Action.SHORT_DESCRIPTION key. - * - * @param desc the short description; can be nullw - * @see Action#SHORT_DESCRIPTION - * @see Action#putValue - */ - public void setShortDescription(String desc) { - putValue(Action.SHORT_DESCRIPTION, desc); - if (desc != null && getLongDescription() == null) { - setLongDescription(desc); - } - } - - /** - * Returns a long description of the action. - * - * @return the long description or null - */ - public String getLongDescription() { - return (String)getValue(Action.LONG_DESCRIPTION); - } - - /** - * Sets the long description of the action. This will also set the - * value of the short description if that value is null. - *

                  - * This is a convenience method for putValue with the - * Action.LONG_DESCRIPTION key. - * - * @param desc the long description; can be null - * @see Action#LONG_DESCRIPTION - * @see Action#putValue - */ - public void setLongDescription(String desc) { - putValue(Action.LONG_DESCRIPTION, desc); - if (desc != null && getShortDescription() == null) { - setShortDescription(desc); - } - } - - /** - * Returns a small icon which represents the action. - * - * @return the small icon or null - */ - public Icon getSmallIcon() { - return (Icon)getValue(SMALL_ICON); - } - - /** - * Sets the small icon which represents the action. - *

                  - * This is a convenience method for putValue with the - * Action.SMALL_ICON key. - * - * @param icon the small icon; can be null - * @see Action#SMALL_ICON - * @see Action#putValue - */ - public void setSmallIcon(Icon icon) { - putValue(SMALL_ICON, icon); - } - - /** - * Returns a large icon which represents the action. - * - * @return the large icon or null - */ - public Icon getLargeIcon() { - return (Icon)getValue(LARGE_ICON); - } - - /** - * Sets the large icon which represents the action. - *

                  - * This is a convenience method for putValue with the - * LARGE_ICON key. - * - * @param icon the large icon; can be null - * @see #LARGE_ICON - * @see Action#putValue - */ - public void setLargeIcon(Icon icon) { - putValue(LARGE_ICON, icon); - } - - /** - * Sets the name of the action. - *

                  - * This is a convenience method for putValue with the - * Action.NAME key. - * - * @param name the name of the action; can be null - * @see Action#NAME - * @see Action#putValue - */ - public void setName(String name) { - putValue(Action.NAME, name); - } - - /** - * Returns the name of the action. - * - * @return the name of the action or null - */ - public String getName() { - return (String)getValue(Action.NAME); - } - - public void setMnemonic(String mnemonic) { - if (mnemonic != null && mnemonic.length() > 0) { - putValue(Action.MNEMONIC_KEY, mnemonic.charAt(0)); - } - } - - /** - * Sets the mnemonic key code for the action. - *

                  - * This is a convenience method for putValue with the - * Action.MNEMONIC_KEY key. - *

                  - * This method does not validate the value. Please see - * {@link javax.swing.AbstractButton#setMnemonic(int)} for details - * concerning the value of the mnemonic. - * - * @param mnemonic an int key code mnemonic or 0 - * @see javax.swing.AbstractButton#setMnemonic(int) - * @see Action#MNEMONIC_KEY - * @see Action#putValue - */ - public void setMnemonic(int mnemonic) { - putValue(Action.MNEMONIC_KEY, mnemonic); - } - - /** - * Return the mnemonic key code for the action. - * - * @return the mnemonic or 0 - */ - public int getMnemonic() { - Integer value = (Integer)getValue(Action.MNEMONIC_KEY); - if (value != null) { - return value.intValue(); - } - return '\0'; - } - - /** - * Sets the action command key. The action command key - * is used to identify the action. - *

                  - * This is a convenience method for putValue with the - * Action.ACTION_COMMAND_KEY key. - * - * @param key the action command - * @see Action#ACTION_COMMAND_KEY - * @see Action#putValue - */ - public void setActionCommand(String key) { - putValue(Action.ACTION_COMMAND_KEY, key); - } - - /** - * Returns the action command. - * - * @return the action command or null - */ - public String getActionCommand() { - return (String) getValue(Action.ACTION_COMMAND_KEY); - } - - /** - * Returns the key stroke which represents an accelerator - * for the action. - * - * @return the key stroke or null - */ - public KeyStroke getAccelerator() { - return (KeyStroke)getValue(Action.ACCELERATOR_KEY); - } - - /** - * Sets the key stroke which represents an accelerator - * for the action. - *

                  - * This is a convenience method for putValue with the - * Action.ACCELERATOR_KEY key. - * - * @param key the key stroke; can be null - * @see Action#ACCELERATOR_KEY - * @see Action#putValue - */ - public void setAccelerator(KeyStroke key) { - putValue(Action.ACCELERATOR_KEY, key); - } - - /** - * Sets the group identity of the state action. This is used to - * identify the action as part of a button group. - */ - public void setGroup(Object group) { - putValue(GROUP, group); - } - - public Object getGroup() { - return getValue(GROUP); - } - - /** - * Will perform cleanup on the object. - * Should be called when finished with the Action. This should be used if - * a new action is constructed from the properties of an old action. - * The old action properties should be disposed. - */ - public void dispose() { - PropertyChangeListener[] listeners = getPropertyChangeListeners(); - for (int i = 0; i < listeners.length; i++) { - removePropertyChangeListener(listeners[i]); - } - } - - // Properties etc.... - - /** - * Indicates if this action has states. If this method returns - * true then the this will send ItemEvents to ItemListeners - * when the control constructed with this action in invoked. - * - * @return true if this can handle states - */ - public boolean isStateAction() { - Boolean state = (Boolean)getValue(IS_STATE); - if (state != null) { - return state.booleanValue(); - } - return false; - } - - /** - * Set the state property to true. - */ - public void setStateAction() { - setStateAction(true); - } - - /** - * Set the state property. - * - * @param state if true then this action will fire ItemEvents - */ - public void setStateAction(boolean state) { - putValue(IS_STATE, Boolean.valueOf(state)); - } - - /** - * @return true if the action is in the selected state - */ - public boolean isSelected() { - Boolean selected = (Boolean) getValue(SELECTED_KEY); - - if (selected == null) { - return false; - } - - return selected.booleanValue(); - } - - /** - * Changes the state of the action. This is a convenience method for updating the Action via the - * value map. - * - * @param newValue - * true to set the action as selected of the action. - * @see Action#SELECTED_KEY - */ - public void setSelected(boolean newValue) { - putValue(SELECTED_KEY, newValue); - } - - @Override - public String toString() { - StringBuffer buffer = new StringBuffer("["); - // RG: Fix for J2SE 5.0; Can't cascade append() calls because - // return type in StringBuffer and AbstractStringBuilder are different - buffer.append(this.getClass().toString()); - buffer.append(":"); - try { - Object[] keys = getKeys(); - for (int i = 0; i < keys.length; i++) { - buffer.append(keys[i]); - buffer.append('='); - buffer.append(getValue( (String) keys[i]).toString()); - if (i < keys.length - 1) { - buffer.append(','); - } - } - buffer.append(']'); - } - catch (Exception ex) { // RG: append(char) throws IOException in J2SE 5.0 - /** @todo Log it */ - } - return buffer.toString(); - } - - /** - * Callback method as ItemListener. Updates internal state based - * on the given ItemEvent.

                  - * - * Here: synchs selected property if isStateAction(), does nothing otherwise. - * - * @param e the ItemEvent fired by a ItemSelectable on changing the selected - * state. - */ - @Override - public void itemStateChanged(ItemEvent e) { - if (isStateAction()) { - setSelected(ItemEvent.SELECTED == e.getStateChange()); - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/action/ActionContainerFactory.java b/src/main/java/org/jdesktop/swingx/action/ActionContainerFactory.java deleted file mode 100644 index 8d0b5ba572..0000000000 --- a/src/main/java/org/jdesktop/swingx/action/ActionContainerFactory.java +++ /dev/null @@ -1,586 +0,0 @@ -/* - * $Id: ActionContainerFactory.java 3980 2011-03-28 20:24:46Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.action; - -import javax.swing.*; -import java.awt.*; -import java.beans.PropertyChangeListener; -import java.util.List; -import java.util.*; - -/** - * Creates user interface elements based on action ids and lists of action ids. - * All action ids must represent actions managed by the ActionManager. - *

                  - *

                  Action Lists

                  - * Use the createXXX(List) methods to construct containers of actions like menu - * bars, menus, popups and toolbars from actions represented as action ids in a - * java.util.List. Each element in the action-list can be one of 3 types: - *
                    - *
                  • action id: corresponds to an action managed by the ActionManager - *
                  • null: indicates a separator should be inserted. - *
                  • java.util.List: represents a submenu. See the note below which describes - * the configuration of menus. - *
                  • - * The order of elements in an action-list determines the arrangement of the ui - * components which are constructed from the action-list. - *

                    - * For a menu or submenu, the first element in the action-list represents a menu - * and subsequent elements represent menu items or separators (if null). - *

                    - * This class can be used as a general component factory which will construct - * components from Actions if the create<comp>(Action,...) - * methods are used. - * - * @see ActionManager - */ -public class ActionContainerFactory { - /** - * Standard margin for toolbar buttons to improve their look - */ - private static Insets TOOLBAR_BUTTON_MARGIN = new Insets(1, 1, 1, 1); - - private ActionMap manager; - - // Map between group id + component and the ButtonGroup - private Map groupMap; - - /** - * Constructs an container factory which uses the default - * ActionManager. - * - */ - public ActionContainerFactory() { - } - /** - * Constructs an container factory which uses managed actions. - * - * @param manager use the actions managed with this manager for - * constructing ui componenents. - */ - public ActionContainerFactory(ActionMap manager) { - setActionManager(manager); - } - - /** - * Gets the ActionManager instance. If the ActionManager has not been explicitly - * set then the default ActionManager instance will be used. - * - * @return the ActionManager used by the ActionContainerFactory. - * @see #setActionManager - */ - public ActionMap getActionManager() { - if (manager == null) { - manager = ActionManager.getInstance(); - } - return manager; - } - - /** - * Sets the ActionManager instance that will be used by this - * ActionContainerFactory - */ - public void setActionManager(ActionMap manager) { - this.manager = manager; - } - - /** - * Constructs a toolbar from an action-list id. By convention, - * the identifier of the main toolbar should be "main-toolbar" - * - * @param list a list of action ids used to construct the toolbar. - * @return the toolbar or null - */ - public JToolBar createToolBar(Object[] list) { - return createToolBar(Arrays.asList(list)); - } - - /** - * Constructs a toolbar from an action-list id. By convention, - * the identifier of the main toolbar should be "main-toolbar" - * - * @param list a list of action ids used to construct the toolbar. - * @return the toolbar or null - */ - public JToolBar createToolBar(List list) { - JToolBar toolbar = new JToolBar(); - Iterator iter = list.iterator(); - while(iter.hasNext()) { - Object element = iter.next(); - - if (element == null) { - toolbar.addSeparator(); - } else { - AbstractButton button = createButton(element, toolbar); - // toolbar buttons shouldn't steal focus - button.setFocusable(false); - /* - * TODO - * The next two lines improve the default look of the buttons. - * This code should be changed to retrieve the default look - * from some UIDefaults object. - */ - button.setMargin(TOOLBAR_BUTTON_MARGIN); - button.setBorderPainted(false); - - toolbar.add(button); - } - } - return toolbar; - } - - - /** - * Constructs a popup menu from an array of action ids. - * - * @param list an array of action ids used to construct the popup. - * @return the popup or null - */ - public JPopupMenu createPopup(Object[] list) { - return createPopup(Arrays.asList(list)); - } - - /** - * Constructs a popup menu from a list of action ids. - * - * @param list a list of action ids used to construct the popup. - * @return the popup or null - */ - public JPopupMenu createPopup(List list) { - JPopupMenu popup = new JPopupMenu(); - Iterator iter = list.iterator(); - while(iter.hasNext()) { - Object element = iter.next(); - - if (element == null) { - popup.addSeparator(); - } else if (element instanceof List) { - JMenu newMenu= createMenu((List)element); - if (newMenu!= null) { - popup.add(newMenu); - } - } else { - popup.add(createMenuItem(element, popup)); - } - } - return popup; - } - - /** - * Constructs a menu tree from a list of actions or lists of lists or actions. - * - * @param actionIds an array which represents the root item. - * @return a menu bar which represents the menu bar tree - */ - public JMenuBar createMenuBar(Object[] actionIds) { - return createMenuBar(Arrays.asList(actionIds)); - } - - /** - * Constructs a menu tree from a list of actions or lists of lists or actions. - * - * @param list a list which represents the root item. - * @return a menu bar which represents the menu bar tree - */ - public JMenuBar createMenuBar(List list) { - final JMenuBar menubar = new JMenuBar(); - - for (Object element : list) { - if (element == null) { - continue; - } - - JMenuItem menu; - - if (element instanceof Object[]) { - menu = createMenu((Object[]) element); - } else if (element instanceof List) { - menu = createMenu((List) element); - } else { - menu = createMenuItem(element, menubar); - } - - if (menu != null) { - menubar.add(menu); - } - } - - return menubar; - } - - - /** - * Creates and returns a menu from a List which represents actions, separators - * and sub-menus. The menu - * constructed will have the attributes from the first action in the List. - * Subsequent actions in the list represent menu items. - * - * @param actionIds an array of action ids used to construct the menu and menu items. - * the first element represents the action used for the menu, - * @return the constructed JMenu or null - */ - public JMenu createMenu(Object[] actionIds) { - return createMenu(Arrays.asList(actionIds)); - } - - /** - * Creates and returns a menu from a List which represents actions, separators - * and sub-menus. The menu - * constructed will have the attributes from the first action in the List. - * Subsequent actions in the list represent menu items. - * - * @param list a list of action ids used to construct the menu and menu items. - * the first element represents the action used for the menu, - * @return the constructed JMenu or null - */ - public JMenu createMenu(List list) { - // The first item will be the action for the JMenu - Action action = getAction(list.get(0)); - - if (action == null) { - return null; - } - - JMenu menu = new JMenu(action); - - // The rest of the items represent the menu items. - for (Object element : list.subList(1, list.size())) { - if (element == null) { - menu.addSeparator(); - } else { - JMenuItem newMenu; - - if (element instanceof Object[]) { - newMenu = createMenu((Object[]) element); - } else if (element instanceof List) { - newMenu = createMenu((List) element); - } else { - newMenu = createMenuItem(element, menu); - } - - if (newMenu != null) { - menu.add(newMenu); - } - } - } - - return menu; - } - - /** - * Convenience method to get the action from an ActionManager. - */ - private Action getAction(Object id) { - return getActionManager().get(id); - } - - /** - * Returns the button group corresponding to the groupid - * - * @param groupid the value of the groupid attribute for the action element - * @param container a container which will further identify the ButtonGroup - */ - private ButtonGroup getGroup(String groupid, JComponent container) { - if (groupMap == null) { - groupMap = new HashMap(); - } - int intCode = groupid.hashCode(); - if (container != null) { - intCode ^= container.hashCode(); - } - Integer hashCode = intCode; - - ButtonGroup group = groupMap.get(hashCode); - if (group == null) { - group = new ButtonGroup(); - groupMap.put(hashCode, group); - } - return group; - } - - /** - * Creates a menu item based on the attributes of the action element. - * Will return a JMenuItem, JRadioButtonMenuItem or a JCheckBoxMenuItem - * depending on the context of the Action. - * - * @return a JMenuItem or subclass depending on type. - */ - private JMenuItem createMenuItem(Object id, JComponent container) { - return createMenuItem(getAction(id), container); - } - - - /** - * Creates a menu item based on the attributes of the action element. - * Will return a JMenuItem, JRadioButtonMenuItem or a JCheckBoxMenuItem - * depending on the context of the Action. - * - * @param action a managed Action - * @param container the parent container may be null for non-group actions. - * @return a JMenuItem or subclass depending on type. - */ - private JMenuItem createMenuItem(Action action, JComponent container) { - JMenuItem menuItem = null; - if (action instanceof AbstractActionExt) { - AbstractActionExt ta = (AbstractActionExt)action; - - if (ta.isStateAction()) { - String groupid = (String)ta.getGroup(); - if (groupid != null) { - // If this action has a groupid attribute then it's a - // GroupAction - menuItem = createRadioButtonMenuItem(getGroup(groupid, container), - (AbstractActionExt)action); - } else { - menuItem = createCheckBoxMenuItem((AbstractActionExt)action); - } - } - } - - if (menuItem == null) { - menuItem= new JMenuItem(action); - configureMenuItemFromExtActionProperties(menuItem, action); - } - return menuItem; - } - - /** - * Creates a menu item based on the attributes of the action. - * Will return a JMenuItem, JRadioButtonMenuItem or a JCheckBoxMenuItem - * depending on the context of the Action. - * - * @param action an action used to create the menu item - * @return a JMenuItem or subclass depending on type. - */ - public JMenuItem createMenuItem(Action action) { - return createMenuItem(action, null); - } - - - /** - * Creates, configures and returns an AbstractButton. - * - * The attributes of the action element - * registered with the ActionManger by the given id. - * Will return a JButton or a JToggleButton. - * - * @param id the identifier - * @param container the JComponent which parents the group, if any. - * @return an AbstractButton based on the - */ - public AbstractButton createButton(Object id, JComponent container) { - return createButton(getAction(id), container); - } - - /** - * Creates a button based on the attributes of the action. If the container - * parameter is non-null then it will be used to uniquely identify - * the returned component within a ButtonGroup. If the action doesn't - * represent a grouped component then this value can be null. - * - * @param action an action used to create the button - * @param container the parent container to uniquely identify - * grouped components or null - * @return will return a JButton or a JToggleButton. - */ - public AbstractButton createButton(Action action, JComponent container) { - if (action == null) { - return null; - } - - AbstractButton button = null; - if (action instanceof AbstractActionExt) { - // Check to see if we should create a toggle button - AbstractActionExt ta = (AbstractActionExt)action; - - if (ta.isStateAction()) { - // If this action has a groupid attribute then it's a - // GroupAction - String groupid = (String)ta.getGroup(); - if (groupid == null) { - button = createToggleButton(ta); - } else { - button = createToggleButton(ta, getGroup(groupid, container)); - } - } - } - - if (button == null) { - // Create a regular button - button = new JButton(action); - configureButtonFromExtActionProperties(button, action); - } - return button; - } - - /** - * Creates a button based on the attributes of the action. - * - * @param action an action used to create the button - * @return will return a JButton or a JToggleButton. - */ - public AbstractButton createButton(Action action) { - return createButton(action, null); - } - - /** - * Adds and configures a toggle button. - * @param a an abstraction of a toggle action. - */ - private JToggleButton createToggleButton(AbstractActionExt a) { - return createToggleButton(a, null); - } - - /** - * Adds and configures a toggle button. - * @param a an abstraction of a toggle action. - * @param group the group to add the toggle button or null - */ - private JToggleButton createToggleButton(AbstractActionExt a, ButtonGroup group) { - JToggleButton button = new JToggleButton(); - configureButton(button, a, group); - return button; - } - - /** - * - * @param button - * @param a - * @param group - */ - public void configureButton(JToggleButton button, AbstractActionExt a, ButtonGroup group) { - configureSelectableButton(button, a, group); - configureButtonFromExtActionProperties(button, a); - } - - /** - * method to configure a "selectable" button from the given AbstractActionExt. - * As there is some un-/wiring involved to support synch of the selected property between - * the action and the button, all config and unconfig (== setting a null action!) - * should be passed through this method.

                    - * - * It's up to the client to only pass in button's where selected and/or the - * group property makes sense. - * - * PENDING: the group properties are yet untested. - * PENDING: think about automated unconfig. - * - * @param button where selected makes sense - * @param a - * @param group the button should be added to. - * @throws IllegalArgumentException if the given action doesn't have the state flag set. - * - */ - public void configureSelectableButton(AbstractButton button, AbstractActionExt a, ButtonGroup group){ - if ((a != null) && !a.isStateAction()) throw - new IllegalArgumentException("the Action must be a stateAction"); - // we assume that all button configuration is done exclusively through this method!! - if (button.getAction() == a) return; - - // unconfigure if the old Action is a state AbstractActionExt - // PENDING JW: automate unconfigure via a PCL that is listening to - // the button's action property? Think about memory leak implications! - Action oldAction = button.getAction(); - if (oldAction instanceof AbstractActionExt) { - AbstractActionExt actionExt = (AbstractActionExt) oldAction; - // remove as itemListener - button.removeItemListener(actionExt); - // remove the button related PCL from the old actionExt - PropertyChangeListener[] l = actionExt.getPropertyChangeListeners(); - for (int i = l.length - 1; i >= 0; i--) { - if (l[i] instanceof ToggleActionPropertyChangeListener) { - ToggleActionPropertyChangeListener togglePCL = (ToggleActionPropertyChangeListener) l[i]; - if (togglePCL.isToggling(button)) { - actionExt.removePropertyChangeListener(togglePCL); - } - } - } - } - - button.setAction(a); - if (group != null) { - group.add(button); - } - if (a != null) { - button.addItemListener(a); - // JW: move the initial config into the PCL?? - button.setSelected(a.isSelected()); - new ToggleActionPropertyChangeListener(a, button); -// new ToggleActionPCL(button, a); - } - - } - - /** - * This method will be called after buttons created from an action. Override - * for custom configuration. - * - * @param button the button to be configured - * @param action the action used to construct the menu item. - */ - protected void configureButtonFromExtActionProperties(AbstractButton button, Action action) { - if (action.getValue(Action.SHORT_DESCRIPTION) == null) { - button.setToolTipText((String)action.getValue(Action.NAME)); - } - // Use the large icon for toolbar buttons. - if (action.getValue(AbstractActionExt.LARGE_ICON) != null) { - button.setIcon((Icon)action.getValue(AbstractActionExt.LARGE_ICON)); - } - // Don't show the text under the toolbar buttons if they have an icon - if (button.getIcon() != null) { - button.setText(""); - } - } - - - /** - * This method will be called after menu items are created. - * Override for custom configuration. - * - * @param menuItem the menu item to be configured - * @param action the action used to construct the menu item. - */ - protected void configureMenuItemFromExtActionProperties(JMenuItem menuItem, Action action) { - } - - /** - * Helper method to add a checkbox menu item. - */ - private JCheckBoxMenuItem createCheckBoxMenuItem(AbstractActionExt a) { - JCheckBoxMenuItem mi = new JCheckBoxMenuItem(); - configureSelectableButton(mi, a, null); - configureMenuItemFromExtActionProperties(mi, a); - return mi; - } - - /** - * Helper method to add a radio button menu item. - */ - private JRadioButtonMenuItem createRadioButtonMenuItem(ButtonGroup group, - AbstractActionExt a) { - JRadioButtonMenuItem mi = new JRadioButtonMenuItem(); - configureSelectableButton(mi, a, group); - configureMenuItemFromExtActionProperties(mi, a); - return mi; - } - - -} diff --git a/src/main/java/org/jdesktop/swingx/action/ActionFactory.java b/src/main/java/org/jdesktop/swingx/action/ActionFactory.java deleted file mode 100644 index 8e287d4d85..0000000000 --- a/src/main/java/org/jdesktop/swingx/action/ActionFactory.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * $Id: ActionFactory.java 3972 2011-03-17 20:31:58Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.action; - -import javax.swing.*; - -/** - * A collection of static methods to make it easier to construct - * Actions. Not sure how usefull they are in reality but it saves a - * lot of typing. - * - * @author Mark Davidson - */ -public class ActionFactory { - - /** - * Factory Methods for creating BoundActions - */ - public static BoundAction createBoundAction(String id, String name, - String mnemonic) { - return createBoundAction(id, name, mnemonic, false); - } - - public static BoundAction createBoundAction(String id, String name, - String mnemonic, boolean toggle) { - return createBoundAction(id, name, mnemonic, toggle, null); - } - - - public static BoundAction createBoundAction(String id, String name, - String mnemonic, boolean toggle, - String group) { - return (BoundAction)configureAction(new BoundAction(name, id), - mnemonic, toggle, group); - } - - /** - * Factory Methods for creating CompositeAction - * @see CompositeAction - */ - public static CompositeAction createCompositeAction(String id, String name, - String mnemonic) { - return createCompositeAction(id, name, mnemonic, false); - } - - public static CompositeAction createCompositeAction(String id, String name, - String mnemonic, boolean toggle) { - return createCompositeAction(id, name, mnemonic, toggle, null); - } - - public static CompositeAction createCompositeAction(String id, String name, - String mnemonic, boolean toggle, - String group) { - return (CompositeAction)configureAction(new CompositeAction(name, id), - mnemonic, toggle, group); - } - - - public static ServerAction createServerAction(String id, String name, - String mnemonic) { - ServerAction action = new ServerAction(name, id); - if (mnemonic != null && !mnemonic.equals("")) { - action.putValue(Action.MNEMONIC_KEY, (int) mnemonic.charAt(0)); - } - return action; - } - - - /** - * These methods are usefull for creating targetable actions - */ - public static TargetableAction createTargetableAction(String id, String name) { - return createTargetableAction(id, name, null); - } - - public static TargetableAction createTargetableAction(String id, String name, - String mnemonic) { - return createTargetableAction(id, name, mnemonic, false); - } - - public static TargetableAction createTargetableAction(String id, String name, - String mnemonic, boolean toggle) { - return createTargetableAction(id, name, mnemonic, toggle, null); - } - - public static TargetableAction createTargetableAction(String id, String name, - String mnemonic, boolean toggle, - String group) { - return (TargetableAction)configureAction(new TargetableAction(name, id), - mnemonic, toggle, group); - } - - private static Action configureAction(AbstractActionExt action, - String mnemonic, boolean toggle, - String group) { - action.setMnemonic(mnemonic); - String description = action.getName() + " action with comand " + action.getActionCommand(); - action.setShortDescription(description); - action.setLongDescription(description); - - if (toggle) { - action.setStateAction(); - } - if (group != null) { - action.setGroup(group); - } - return action; - } - - /** - * Add additional attributes to the action. If any of these attributes - * are null then they will still be set on the action. Many of these - * attributes map to the set methods on AbstractActionExt - * - * @see AbstractActionExt - * @param action the action which will all the attributes will be applied - */ - public static void decorateAction(AbstractAction action, - String shortDesc, String longDesc, - Icon smallIcon, Icon largeIcon, - KeyStroke accel) { - if (action instanceof AbstractActionExt) { - AbstractActionExt a = (AbstractActionExt)action; - a.setShortDescription(shortDesc); - a.setLongDescription(longDesc); - a.setSmallIcon(smallIcon); - a.setLargeIcon(largeIcon); - a.setAccelerator(accel); - } - else { - action.putValue(Action.SHORT_DESCRIPTION, shortDesc); - action.putValue(Action.LONG_DESCRIPTION, longDesc); - action.putValue(Action.SMALL_ICON, smallIcon); - action.putValue(AbstractActionExt.LARGE_ICON, largeIcon); - action.putValue(Action.ACCELERATOR_KEY, accel); - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/action/ActionManager.java b/src/main/java/org/jdesktop/swingx/action/ActionManager.java deleted file mode 100644 index bd2dfcf600..0000000000 --- a/src/main/java/org/jdesktop/swingx/action/ActionManager.java +++ /dev/null @@ -1,383 +0,0 @@ -/* - * $Id: ActionManager.java 3972 2011-03-17 20:31:58Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.action; - -import javax.swing.*; -import java.io.PrintStream; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -/** - * The ActionManager manages sets of javax.swing.Actions for an - * application. There are convenience methods for getting and setting the state - * of the action. - * All of these elements have a unique id tag which is used by the ActionManager - * to reference the action. This id maps to the Action.ACTION_COMMAND_KEY - * on the Action. - *

                    - * The ActionManager may be used to conveniently register callback methods - * on BoundActions. - *

                    - * A typical use case of the ActionManager is: - *

                    - *

                    - *   ActionManager manager = ActionManager.getInstance();
                    - *
                    - *   // load Actions
                    - *   manager.addAction(action);
                    - *
                    - *   // Change the state of the action:
                    - *   manager.setEnabled("new-action", newState);
                    - * 
                    - * - * The ActionManager also supports Actions that can have a selected state - * associated with them. These Actions are typically represented by a - * JCheckBox or similar widget. For such actions the registered method is - * invoked with an additional parameter indicating the selected state of - * the widget. For example, for the callback handler: - *

                    - *

                    - *  public class Handler {
                    - *      public void stateChanged(boolean newState);
                    - *   }
                    - * 
                    - * The registration method would look similar: - *
                    - *  manager.registerCallback("select-action", new Handler(), "stateChanged");
                    - * 
                    - *

                    - * The stateChanged method would be invoked as the selected state of - * the widget changed. Additionally if you need to change the selected - * state of the Action use the ActionManager method setSelected. - *

                    - * The ActionContainerFactory uses the managed Actions in a - * ActionManager to create user interface components. It uses the shared - * instance of ActionManager by default. For example, to create a JMenu based on an - * action-list id: - *

                    - * ActionContainerFactory factory = new ActionContainerFactory();
                    - * JMenu file = factory.createMenu(list);
                    - * 
                    - * - * @see ActionContainerFactory - * @see TargetableAction - * @see BoundAction - * @author Mark Davidson - * @author Neil Weber - */ -public class ActionManager extends ActionMap { - - /** - * Shared instance of the singleton ActionManager. - */ - private static ActionManager INSTANCE; - - /** - * Creates the action manager. Use this constuctor if the application should - * support many ActionManagers. Otherwise, using the getInstance method will - * return a singleton. - */ - public ActionManager() { - } - - /** - * Return the instance of the ActionManger. If this has not been explicity - * set then it will be created. - * - * @return the ActionManager instance. - * @see #setInstance - */ - public static ActionManager getInstance() { - if (INSTANCE == null) { - INSTANCE = new ActionManager(); - } - return INSTANCE; - } - - /** - * Sets the ActionManager instance. - */ - public static void setInstance(ActionManager manager) { - INSTANCE = manager; - } - - /** - * Returns the ids for all the managed actions. - *

                    - * An action id is a unique idenitfier which can - * be used to retrieve the corrspondng Action from the ActionManager. - * This identifier can also - * be used to set the properties of the action through the action - * manager like setting the state of the enabled or selected flags. - * - * @return a set which represents all the action ids - */ - public Set getActionIDs() { - Object[] keys = keys(); - if (keys == null) { - return null; - } - - return new HashSet(Arrays.asList(keys)); - } - - public Action addAction(Action action) { - return addAction(action.getValue(Action.ACTION_COMMAND_KEY), action); - } - - /** - * Adds an action to the ActionManager - * @param id value of the action id - which is value of the ACTION_COMMAND_KEY - * @param action Action to be managed - * @return the action that was added - */ - public Action addAction(Object id, Action action) { - put(id, action); - return action; - } - - /** - * Retrieves the action corresponding to an action id. - * - * @param id value of the action id - * @return an Action or null if id - */ - public Action getAction(Object id) { - return get(id); - } - - /** - * Convenience method for returning the TargetableAction - * - * @param id value of the action id - * @return the TargetableAction referenced by the named id or null - */ - public TargetableAction getTargetableAction(Object id) { - Action a = getAction(id); - if (a instanceof TargetableAction) { - return (TargetableAction)a; - } - return null; - } - - /** - * Convenience method for returning the BoundAction - * - * @param id value of the action id - * @return the TargetableAction referenced by the named id or null - */ - public BoundAction getBoundAction(Object id) { - Action a = getAction(id); - if (a instanceof BoundAction) { - return (BoundAction)a; - } - return null; - } - - /** - * Convenience method for returning the ServerAction - * - * @param id value of the action id - * @return the TargetableAction referenced by the named id or null - */ - public ServerAction getServerAction(Object id) { - Action a = getAction(id); - if (a instanceof ServerAction) { - return (ServerAction)a; - } - return null; - } - - /** - * Convenience method for returning the CompositeAction - * - * @param id value of the action id - * @return the TargetableAction referenced by the named id or null - */ - public CompositeAction getCompositeAction(Object id) { - Action a = getAction(id); - if (a instanceof CompositeAction) { - return (CompositeAction)a; - } - return null; - } - - /** - * Convenience method for returning the StateChangeAction - * - * @param id value of the action id - * @return the StateChangeAction referenced by the named id or null - */ - private AbstractActionExt getStateChangeAction(Object id) { - Action a = getAction(id); - if (a != null && a instanceof AbstractActionExt) { - AbstractActionExt aa = (AbstractActionExt)a; - if (aa.isStateAction()) { - return aa; - } - } - return null; - } - - /** - * Enables or disables the state of the Action corresponding to the - * action id. This method should be used - * by application developers to ensure that all components created from an - * action remain in synch with respect to their enabled state. - * - * @param id value of the action id - * @param enabled true if the action is to be enabled; otherwise false - */ - public void setEnabled(Object id, boolean enabled) { - Action action = getAction(id); - if (action != null) { - action.setEnabled(enabled); - } - } - - - /** - * Returns the enabled state of the Action. When enabled, - * any component associated with this object is active and - * able to fire this object's actionPerformed method. - * - * @param id value of the action id - * @return true if this Action is enabled; false if the - * action doesn't exist or disabled. - */ - public boolean isEnabled(Object id) { - Action action = getAction(id); - if (action != null) { - return action.isEnabled(); - } - return false; - } - - /** - * Sets the selected state of a toggle action. If the id doesn't - * correspond to a toggle action then it will fail silently. - * - * @param id the value of the action id - * @param selected true if the action is to be selected; otherwise false. - */ - public void setSelected(Object id, boolean selected) { - AbstractActionExt action = getStateChangeAction(id); - if (action != null) { - action.setSelected(selected); - } - } - - /** - * Gets the selected state of a toggle action. If the id doesn't - * correspond to a toggle action then it will fail silently. - * - * @param id the value of the action id - * @return true if the action is selected; false if the action - * doesn't exist or is disabled. - */ - public boolean isSelected(Object id) { - AbstractActionExt action = getStateChangeAction(id); - if (action != null) { - return action.isSelected(); - } - return false; - } - - /** - * A diagnostic which prints the Attributes of an action - * on the printStream - */ - static void printAction(PrintStream stream, Action action) { - stream.println("Attributes for " + action.getValue(Action.ACTION_COMMAND_KEY)); - - if (action instanceof AbstractAction) { - Object[] keys = ((AbstractAction)action).getKeys(); - - for (int i = 0; i < keys.length; i++) { - stream.println("\tkey: " + keys[i] + "\tvalue: " + - action.getValue((String)keys[i])); - } - } - } - - /** - * Convenience method to register a callback method on a BoundAction - * - * @see BoundAction#registerCallback - * @param id value of the action id - which is the value of the ACTION_COMMAND_KEY - * @param handler the object which will be perform the action - * @param method the name of the method on the handler which will be called. - */ - public void registerCallback(Object id, Object handler, String method) { - BoundAction action = getBoundAction(id); - if (action != null) { - action.registerCallback(handler, method); - } - } - - // - // Convenience methods for determining the type of action. - // - - /** - * Determines if the Action corresponding to the action id is a state changed - * action (toggle, group type action). - * - * @param id value of the action id - * @return true if the action id represents a multi state action; false otherwise - */ - public boolean isStateAction(Object id) { - Action action = getAction(id); - if (action != null && action instanceof AbstractActionExt) { - return ((AbstractActionExt)action).isStateAction(); - } - return false; - } - - /** - * Test to determine if the action is a TargetableAction - */ - public boolean isTargetableAction(Object id) { - return (getTargetableAction(id) != null); - } - - /** - * Test to determine if the action is a BoundAction - */ - public boolean isBoundAction(Object id) { - return (getBoundAction(id) != null); - } - - /** - * Test to determine if the action is a BoundAction - */ - public boolean isCompositeAction(Object id) { - return (getCompositeAction(id) != null); - } - - /** - * Test to determine if the action is a ServerAction - */ - public boolean isServerAction(Object id) { - return (getServerAction(id) != null); - } -} diff --git a/src/main/java/org/jdesktop/swingx/action/BoundAction.java b/src/main/java/org/jdesktop/swingx/action/BoundAction.java deleted file mode 100644 index 216ed3ec43..0000000000 --- a/src/main/java/org/jdesktop/swingx/action/BoundAction.java +++ /dev/null @@ -1,329 +0,0 @@ -/* - * $Id: BoundAction.java 3972 2011-03-17 20:31:58Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.action; - -import javax.swing.*; -import javax.swing.event.EventListenerList; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.ItemEvent; -import java.awt.event.ItemListener; -import java.beans.EventHandler; -import java.beans.Statement; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Proxy; -import java.util.EventListener; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * A class that represents the many type of actions that this framework supports. - *

                    - * The command invocation of this action may be delegated to another action or item state - * listener. If there isn't an explicit binding then the command is forwarded to - * the TargetManager. - * - * @author Mark Davidson - * @author Karl Schaefer (serialization support) - */ -public class BoundAction extends AbstractActionExt { - private static final Logger LOG = Logger.getLogger(BoundAction.class .getName()); - - // Holds the listeners - private transient EventListenerList listeners; - - public BoundAction() { - this("BoundAction"); - } - - public BoundAction(String name) { - super(name); - } - - /** - * @param name display name of the action - * @param command the value of the action command key - */ - public BoundAction(String name, String command) { - super(name, command); - } - - public BoundAction(String name, Icon icon) { - super(name, icon); - } - - /** - * @param name display name of the action - * @param command the value of the action command key - * @param icon icon to display - */ - public BoundAction(String name, String command, Icon icon) { - super(name, command, icon); - } - - /** - * The callback string will be called to register the action callback. - * Note the toggle property must be set if this is a state action before - * this method is called. - * For example, - *

                    -     *     <exec>com.sun.foo.FubarHandler#handleBar</exec>
                    -     * 
                    - * will register - *
                    -     *     registerCallback(com.sun.foo.FubarHandler(), "handleBar");
                    -     * 
                    - */ - public void setCallback(String callback) { - String[] elems = callback.split("#", 2); - if (elems.length == 2) { - try { - Class clz = Class.forName(elems[0]); - - // May throw a security exception in an Applet - // context. - Object obj = clz.newInstance(); - - registerCallback(obj, elems[1]); - } catch (Exception ex) { - LOG.fine("ERROR: setCallback(" + callback - + ") - " + ex.getMessage()); - } - } - } - - /** - * Registers a callback method when the Action corresponding to - * the action id is invoked. When a Component that was constructed from the - * Action identified by the action id invokes actionPerformed then the method - * named will be invoked on the handler Object. - *

                    - * If the Action represented by the action id is a StateChangeAction, then - * the method passed should take an int as an argument. The value of - * getStateChange() on the ItemEvent object will be passed as the parameter. - * - * @param handler the object which will be perform the action - * @param method the name of the method on the handler which will be called. - */ - public void registerCallback(Object handler, String method) { - if (isStateAction()) { - // Create a handler for toggle type actions. - addItemListener(new BooleanInvocationHandler(handler, method)); - } else { - // Create a new ActionListener using the dynamic proxy API. - addActionListener(EventHandler.create(ActionListener.class, - handler, method)); - } - } - - /** - * The callback for the toggle/state changed action that invokes a method - * with a boolean argument on a target. - * - * TODO: should reimplement this class as something that can be persistable. - */ - private class BooleanInvocationHandler implements ItemListener { - - private Statement falseStatement; - private Statement trueStatement; - - public BooleanInvocationHandler(Object target, String methodName) { - // Create the true and false statements. - falseStatement = new Statement(target, methodName, - new Object[] { Boolean.FALSE }); - - trueStatement = new Statement(target, methodName, - new Object[] { Boolean.TRUE }); - } - - @Override - public void itemStateChanged(ItemEvent evt) { - Statement statement = (evt.getStateChange() == ItemEvent.DESELECTED) ? falseStatement - : trueStatement; - - try { - statement.execute(); - } catch (Exception ex) { - LOG.log(Level.FINE, - "Couldn't execute boolean method via Statement " - + statement, ex); - } - } - } - - // Listener registration... - - private void addListener(Class clz, T listener) { - if (listeners == null) { - listeners = new EventListenerList(); - } - listeners.add(clz, listener); - } - - private void removeListener(Class clz, T listener) { - if (listeners != null) { - listeners.remove(clz, listener); - } - } - - private EventListener[] getListeners(Class clz) { - if (listeners == null) { - return null; - } - return listeners.getListeners(clz); - } - - /** - * Add an action listener which will be invoked when this action is invoked. - */ - public void addActionListener(ActionListener listener) { - addListener(ActionListener.class, listener); - } - - public void removeActionListener(ActionListener listener) { - removeListener(ActionListener.class, listener); - } - - public ActionListener[] getActionListeners() { - return (ActionListener[])getListeners(ActionListener.class); - } - - /** - * Add an item listener which will be invoked for toggle actions. - */ - public void addItemListener(ItemListener listener) { - addListener(ItemListener.class, listener); - } - - public void removeItemListener(ItemListener listener) { - removeListener(ItemListener.class, listener); - } - - public ItemListener[] getItemListeners() { - return (ItemListener[])getListeners(ItemListener.class); - } - - // Callbacks... - - /** - * Callback for command actions. - */ - @Override - public void actionPerformed(ActionEvent evt) { - ActionListener[] alist = getActionListeners(); - if (alist != null) { - for (int i = 0 ; i < alist.length; i++) { - alist[i].actionPerformed(evt); - } - } - } - - /** - * Callback for toggle actions. - */ - @Override - public void itemStateChanged(ItemEvent evt) { - // Update all objects that share this item - boolean newValue; - boolean oldValue = isSelected(); - - newValue = evt.getStateChange() == ItemEvent.SELECTED; - - if (oldValue != newValue) { - setSelected(newValue); - - // Forward the event to the delgate for handling. - ItemListener[] ilist = getItemListeners(); - if (ilist != null) { - for (int i = 0; i < ilist.length; i++) { - ilist[i].itemStateChanged(evt); - } - } - } - } - - private void writeObject(ObjectOutputStream s) throws IOException { - s.defaultWriteObject(); - - if (listeners != null) { - Object[] list = listeners.getListenerList(); - - for (int i = 1; i < list.length; i += 2) { - if (Proxy.isProxyClass(list[i].getClass())) { - InvocationHandler h = Proxy.getInvocationHandler(list[i]); - - if (h instanceof EventHandler && ((EventHandler) h).getTarget() instanceof Serializable) { - EventHandler eh = (EventHandler) h; - - s.writeObject("callback"); - s.writeObject(eh.getTarget()); - s.writeObject(eh.getAction()); - } - } else if (list[i] instanceof BooleanInvocationHandler) { - BooleanInvocationHandler bih = (BooleanInvocationHandler) list[i]; - Object target = bih.trueStatement.getTarget(); - - if (target instanceof Serializable) { - s.writeObject(BooleanInvocationHandler.class.getName()); - s.writeObject(target); - s.writeObject(bih.trueStatement.getMethodName()); - } - } else if (list[i] instanceof Serializable) { - s.writeObject(((Class) list[i - 1]).getName()); - s.writeObject(list[i]); - } - } - } - - s.writeObject(null); - } - - @SuppressWarnings("unchecked") - private void readObject(ObjectInputStream s) throws ClassNotFoundException, - IOException { - s.defaultReadObject(); - - Object typeOrNull; - - while (null != (typeOrNull = s.readObject())) { - if ("callback".equals(typeOrNull)) { - Object handler = s.readObject(); - String method = (String) s.readObject(); - - addActionListener(EventHandler.create(ActionListener.class, handler, method)); - } else if (BooleanInvocationHandler.class.getName().equals(typeOrNull)) { - Object handler = s.readObject(); - String method = (String) s.readObject(); - - addItemListener(new BooleanInvocationHandler(handler, method)); - } else { - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - EventListener l = (EventListener) s.readObject(); - addListener((Class)Class.forName((String)typeOrNull, true, cl), l); - } - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/action/CompositeAction.java b/src/main/java/org/jdesktop/swingx/action/CompositeAction.java deleted file mode 100644 index b0debd8e62..0000000000 --- a/src/main/java/org/jdesktop/swingx/action/CompositeAction.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * $Id: CompositeAction.java 3972 2011-03-17 20:31:58Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.action; - -import javax.swing.*; -import java.awt.event.ActionEvent; -import java.awt.event.ItemEvent; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -/** - * A class that represents an action which will fire a sequence of actions. - * The action ids are added to the internal list. When this action is invoked, - * the event will be dispatched to the actions in the internal list. - *

                    - * The action ids are represented by the value of the Action.ACTION_COMMAND_KEY - * and must be managed by the ActionManager. When this action is - * invoked, then the actions are retrieved from the ActionManager in list order - * and invoked. - * - * @see ActionManager - * @author Mark Davidson - */ -public class CompositeAction extends AbstractActionExt { - - /** - * Keys for storing extended action attributes. May make public. - */ - private static final String LIST_IDS = "action-list-ids"; - - public CompositeAction() { - this("CompositeAction"); - } - - public CompositeAction(String name) { - super(name); - } - - /** - * @param name display name of the action - * @param command the value of the action command key - */ - public CompositeAction(String name, String command) { - super(name, command); - } - - public CompositeAction(String name, Icon icon) { - super(name, icon); - } - - /** - * @param name display name of the action - * @param command the value of the action command key - * @param icon icon to display - */ - public CompositeAction(String name, String command, Icon icon) { - super(name, command, icon); - } - - /** - * Add an action id to the action list. This action will be invoked - * when this composite action is invoked. - */ - @SuppressWarnings("unchecked") - public void addAction(String id) { - List list = (List) getValue(LIST_IDS); - if (list == null) { - list = new ArrayList(); - putValue(LIST_IDS, list); - } - list.add(id); - } - - /** - * Returns a list of action ids which indicates that this is a composite - * action. - * @return a valid list of action ids or null - */ - @SuppressWarnings("unchecked") - public List getActionIDs() { - return (List) getValue(LIST_IDS); - } - - /** - * Callback for composite actions. This method will redispatch the - * ActionEvent to all the actions held in the list. - */ - @Override - public void actionPerformed(ActionEvent evt) { - ActionManager manager = ActionManager.getInstance(); - - Iterator iter = getActionIDs().iterator(); - while (iter.hasNext()) { - String id = iter.next(); - Action action = manager.getAction(id); - if (action != null) { - action.actionPerformed(evt); - } - } - } - - /** - * Callback for toggle actions. - */ - @Override - public void itemStateChanged(ItemEvent evt) { - ActionManager manager = ActionManager.getInstance(); - - Iterator iter = getActionIDs().iterator(); - while (iter.hasNext()) { - String id = iter.next(); - Action action = manager.getAction(id); - if (action != null && action instanceof AbstractActionExt) { - ((AbstractActionExt)action).itemStateChanged(evt); - } - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/action/OpenBrowserAction.java b/src/main/java/org/jdesktop/swingx/action/OpenBrowserAction.java deleted file mode 100644 index 739ac7b460..0000000000 --- a/src/main/java/org/jdesktop/swingx/action/OpenBrowserAction.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * $Id: OpenBrowserAction.java 3972 2011-03-17 20:31:58Z kschaefe $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.action; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * An action for opening a {@link URI} in a browser. The URI may be {@code null} and if so this - * action does nothing. - * - * @author Karl Schaefer - * @author joshy (original version) - */ -public class OpenBrowserAction extends AbstractAction { - private static Logger log = Logger.getLogger(OpenBrowserAction.class.getName()); - - private URI uri; - - /** Creates a new instance of OpenBrowserAction */ - public OpenBrowserAction() { - this((URI) null); - } - - /** - * Creates a new action for the specified URI. - * - * @param uri - * the URI - * @throws NullPointerException - * if {@code uri} is {@code null} - * @throws IllegalArgumentException - * if the given string violates RFC 2396 - */ - public OpenBrowserAction(String uri) { - this(URI.create(uri)); - } - - /** - * Creates a new action for the specified URL. - * - * @param url - * the URL - * @throws URISyntaxException - * if the URL cannot be converted to a valid URI - */ - public OpenBrowserAction(URL url) throws URISyntaxException { - this(url.toURI()); - } - - /** - * Creates a new action for the specified URI. - * - * @param uri - * the URI - */ - public OpenBrowserAction(URI uri) { - setURI(uri); - } - - /** - * Gets the current URI. - * - * @return the URI - */ - public URI getURI() { - return uri; - } - - /** - * Sets the current URI. - * - * @param uri - * the new URI - */ - public void setURI(URI uri) { - this.uri = uri; - } - - /** - * {@inheritDoc} - */ - @Override - public void actionPerformed(ActionEvent e) { - if (uri == null || !Desktop.isDesktopSupported()) { - return; - } - - if (Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) { - try { - Desktop.getDesktop().browse(uri); - } catch (IOException ioe) { - log.log(Level.WARNING, "unable to browse: " + uri, ioe); - } - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/action/ServerAction.java b/src/main/java/org/jdesktop/swingx/action/ServerAction.java deleted file mode 100644 index 362ed943bd..0000000000 --- a/src/main/java/org/jdesktop/swingx/action/ServerAction.java +++ /dev/null @@ -1,324 +0,0 @@ -/* - * $Id: ServerAction.java 3972 2011-03-17 20:31:58Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.action; - -import javax.swing.*; -import java.awt.event.ActionEvent; -import java.io.*; -import java.net.*; -import java.security.AccessControlException; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -//import org.jdesktop.swing.Application; - - -/** - * An action which will invoke an http POST operation. - * - * @author Mark Davidson - */ -public class ServerAction extends AbstractAction { - // Server action support - private static final Logger LOG = Logger.getLogger(ServerAction.class - .getName()); - private static final String PARAMS = "action-params"; - private static final String HEADERS = "action-headers"; - private static final String URL = "action-url"; - - private static final String URL_CACHE = "_URL-CACHE__"; - - public ServerAction() { - this("action"); - } - - public ServerAction(String name) { - super(name); - } - - /** - * @param name display name of the action - * @param command the value of the action command key - */ - public ServerAction(String name, String command) { - this(name, command, null); - } - - public ServerAction(String name, Icon icon) { - super(name, icon); - } - - /** - * @param name display name of the action - * @param command the value of the action command key - * @param icon icon to display - */ - public ServerAction(String name, String command, Icon icon) { - super(name, icon); - putValue(Action.ACTION_COMMAND_KEY, command); - } - - /** - * Set the url for the action. - *

                    - * @param url a string representation of the url - */ - public void setURL(String url) { - putValue(URL, url); - putValue(URL_CACHE, null); - } - - public String getURL() { - return (String)getValue(URL); - } - - @SuppressWarnings("unchecked") - private Map getParams() { - return (Map)getValue(PARAMS); - } - - private void setParams(Map params) { - putValue(PARAMS, params); - } - - /** - * Adds a name value pair which represents a url parameter in an http - * POST request. - */ - public void addParam(String name, String value) { - Map params = getParams(); - if (params == null) { - params = new HashMap(); - setParams(params); - } - params.put(name, value); - } - - /** - * Return a parameter value corresponding to name or null if it doesn't exist. - */ - public String getParamValue(String name) { - Map params = getParams(); - return params == null ? null : params.get(name); - } - - /** - * Return a set of parameter names or null if there are no params - */ - public Set getParamNames() { - Map params = getParams(); - return params == null ? null : params.keySet(); - } - - @SuppressWarnings("unchecked") - private Map getHeaders() { - return (Map)getValue(HEADERS); - } - - private void setHeaders(Map headers) { - putValue(HEADERS, headers); - } - - /** - * Adds a name value pair which represents a url connection request property. - * For example, name could be "Content-Type" and the value could be - * "application/x-www-form-urlencoded" - */ - public void addHeader(String name, String value) { - Map map = getHeaders(); - if (map == null) { - map = new HashMap(); - setHeaders(map); - } - map.put(name, value); - } - - /** - * Return a header value corresponding to name or null if it doesn't exist. - */ - public String getHeaderValue(String name) { - Map headers = getHeaders(); - return headers == null ? null : headers.get(name); - } - - /** - * Return a set of parameter names or null if there are no params - */ - public Set getHeaderNames() { - Map headers = getHeaders(); - return headers == null ? null : headers.keySet(); - } - - /** - * Invokes the server operation when the action has been invoked. - */ - @Override - public void actionPerformed(ActionEvent evt) { - URL execURL = (URL)getValue(URL_CACHE); - if (execURL == null && !"".equals(getURL())) { - try { - String url = getURL(); - if (url.startsWith("http")) { - execURL = new URL(url); - } else { - } - if (execURL == null) { - // XXX TODO: send a message - return; - } else { - // Cache this value. - putValue(URL_CACHE, execURL); - } - - } catch (MalformedURLException ex) { - LOG.log(Level.WARNING, "something went wrong...", ex); - } - } - - try { - URLConnection uc = execURL.openConnection(); - - // Get all the header name/value pairs ans set the request headers - Set headerNames = getHeaderNames(); - if (headerNames != null && !headerNames.isEmpty()) { - Iterator iter = headerNames.iterator(); - while (iter.hasNext()) { - String name = (String)iter.next(); - uc.setRequestProperty(name, getHeaderValue(name)); - } - } - uc.setUseCaches(false); - uc.setDoOutput(true); - - ByteArrayOutputStream byteStream = new ByteArrayOutputStream(512); - PrintWriter out = new PrintWriter(byteStream, true); - out.print(getPostData()); - out.flush(); - - // POST requests must have a content-length. - String length = String.valueOf(byteStream.size()); - uc.setRequestProperty("Content-length", length); - - // Write POST data to real output stream. - byteStream.writeTo(uc.getOutputStream()); - - BufferedReader buf = null; - if (uc instanceof HttpURLConnection) { - HttpURLConnection huc = (HttpURLConnection)uc; - int code = huc.getResponseCode(); - String message = huc.getResponseMessage(); - - // Handle the result. - if (code < 400) { - // action succeeded send to status bar - // XXX TODO: setStatusMessage(createMessage(code, message)); - // Format the response - // TODO: This should load asychnonously - buf = new BufferedReader(new InputStreamReader(uc.getInputStream())); - - } else { - // action has failed show dialog - // XXX TODO: setStatusMessage(createMessage(code, message)); - buf = new BufferedReader(new InputStreamReader(huc.getErrorStream())); - } - String line; - - StringBuffer buffer = new StringBuffer(); - while ((line = buf.readLine()) != null) { - // RG: Fix for J2SE 5.0; Can't cascade append() calls because - // return type in StringBuffer and AbstractStringBuilder are different - buffer.append(line); - buffer.append('\n'); - } - // JW: this used the Debug - maybe use finest level? - LOG.finer("returned from connection\n" + buffer.toString()); - } - } catch (UnknownHostException ex) { - LOG.log(Level.WARNING, "UnknownHostException detected. Could it be a proxy issue?", ex); - - } catch (AccessControlException ex) { - LOG.log(Level.WARNING, "AccessControlException detected", ex); - } catch (IOException ex) { - LOG.log(Level.WARNING, "IOException detected", ex); - } - } - - /** - * Retrieves a string which represents the parameter data for a server action. - * @return a string of name value pairs prefixed by a '?' and delimited by an '&' - */ - private String getPostData() { - // Write the data into local buffer - StringBuffer postData = new StringBuffer(); - - // TODO: the action should be configured to retrieve the data. - - // Get all the param name/value pairs and build the data string - Set paramNames = getParamNames(); - if (paramNames != null && !paramNames.isEmpty()) { - Iterator iter = paramNames.iterator(); - try { - while (iter.hasNext()) { - String name = iter.next(); - postData.append('&').append(name).append('='); - postData.append(getParamValue(name)); - } - } - catch (Exception ex) { // RG: append(char) throws IOException in J2SE 5.0 - /** @todo Log it */ - } - // Replace the first & with a ? - postData.setCharAt(0, '?'); - } - - LOG.finer("ServerAction: POST data: " + postData.toString()); - return postData.toString(); - } - - - /** - * Creates a human readable message from the server code and message result. - * @param code an http error code. - * @param msg server message - */ - private String createMessage(int code, String msg) { - StringBuffer buffer = new StringBuffer("The action \""); - buffer.append(getValue(NAME)); - - if (code < 400) { - buffer.append("\" has succeeded "); - } else { - buffer.append("\" has failed\nPlease check the Java console for more details.\n"); - } - // RG: Fix for J2SE 5.0; Can't cascade append() calls because - // return type in StringBuffer and AbstractStringBuilder are different - buffer.append("\nServer response:\nCode: "); - buffer.append(code); - buffer.append(" Message: "); - buffer.append(msg); - - return buffer.toString(); - } -} diff --git a/src/main/java/org/jdesktop/swingx/action/TargetManager.java b/src/main/java/org/jdesktop/swingx/action/TargetManager.java deleted file mode 100644 index ef1798a7d9..0000000000 --- a/src/main/java/org/jdesktop/swingx/action/TargetManager.java +++ /dev/null @@ -1,280 +0,0 @@ -/* - * $Id: TargetManager.java 3972 2011-03-17 20:31:58Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.action; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.beans.PropertyChangeListener; -import java.beans.PropertyChangeSupport; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - - - -/** - * The target manager dispatches commands to {@link Targetable} objects - * that it manages. This design of this class is based on the Chain of - * Responsiblity and Mediator design patterns. The target manager - * acts as a mediator between {@link TargetableAction}s and the intended targets. - * This allows Action based components to invoke commands on components - * without explicitly binding the user Action to the component action. - *

                    - * The target manager maintains a reference to a current - * target and a target list. - * The target list is managed using the addTarget and - * removeTarget methods. The current target is managed using the - * setTarget and getTarget methods. - *

                    - * Commands are dispatched to the Targetable objects in the doCommand - * method in a well defined order. The doCommand method on the Targetable object - * is called and if it returns true then the command has been handled and - * command dispatching will stop. If the Targetable doCommand method returns - * false then the - *

                    - * If none of the Targetable objects can handle the command then the default - * behaviour is to retrieve an Action from the {@link ActionMap} of - * the permanent focus owner with a key that matches the command key. If an - * Action can be found then the actionPerformed - * method is invoked using an ActionEvent that was constructed - * using the command string. - *

                    - * If the Action is not found on the focus order then the ActionMaps of the ancestor - * hierarchy of the focus owner is searched until a matching Action can be found. - * Finally, if none - * of the components can handle the command then it is dispatched to the ActionMap - * of the current Application instance. - *

                    - * The order of command dispatch is as follows: - *

                      - *
                    • Current Targetable object invoking doCommand method - *
                    • List order of Targetable objects invoking doCommand method - *
                    • ActionMap entry of the permanent focus owner invoking actionPerfomed - *
                    • ActionMap entry of the ancestor hierarchy of the permanent focus owner - *
                    • ActionMap entry of the current Application instance - *
                    - * - * @see Targetable - * @see TargetableAction - * @author Mark Davidson - */ -public class TargetManager { - - private static TargetManager INSTANCE; - private List targetList; - private Targetable target; - private PropertyChangeSupport propertySupport; - - /** - * Create a target manager. Use this constructor if the application - * may support many target managers. Otherwise, using the getInstance method - * will return a singleton. - */ - public TargetManager() { - propertySupport = new PropertyChangeSupport(this); - } - - /** - * Return the singleton instance. - */ - public static TargetManager getInstance() { - if (INSTANCE == null) { - INSTANCE = new TargetManager(); - } - return INSTANCE; - } - - /** - * Add a target to the target list. Will be appended - * to the list by default. If the prepend flag is true then - * the target will be added at the head of the list. - *

                    - * Targets added to the head of the list will will be the first - * to handle the command. - * - * @param target the targeted object to add - * @param prepend if true add at the head of the list; false append - */ - public void addTarget(Targetable target, boolean prepend) { - if (targetList == null) { - targetList = new ArrayList(); - } - if (prepend) { - targetList.add(0, target); - } else { - targetList.add(target); - } - // Should add focus listener to the component. - } - - /** - * Appends the target to the target list. - * @param target the targeted object to add - */ - public void addTarget(Targetable target) { - addTarget(target, false); - } - - /** - * Remove the target from the list - */ - public void removeTarget(Targetable target) { - if (targetList != null) { - targetList.remove(target); - } - } - - /** - * Returns an array of managed targets that were added with the - * addTarget methods. - * - * @return all the Targetable added or an empty array if no - * targets have been added - */ - public Targetable[] getTargets() { - Targetable[] targets; - if (targetList == null) { - targets = new Targetable[0]; - } else { - targets = new Targetable[targetList.size()]; - targets = (Targetable[])targetList.toArray(new Targetable[targetList.size()]); - } - return targets; - } - - /** - * Gets the current targetable component. May or may not - * in the target list. If the current target is null then - * the the current targetable component will be the first one - * in the target list which can execute the command. - * - * This is a bound property and will fire a property change event - * if the value changes. - * - * @param newTarget the current targetable component to set or null if - * the TargetManager shouldn't have a current targetable component. - */ - public void setTarget(Targetable newTarget) { - Targetable oldTarget = target; - if (oldTarget != newTarget) { - target = newTarget; - propertySupport.firePropertyChange("target", oldTarget, newTarget); - } - } - - /** - * Return the current targetable component. The curent targetable component - * is the first place where commands will be dispatched. - * - * @return the current targetable component or null - */ - public Targetable getTarget() { - return target; - } - - public void addPropertyChangeListener(PropertyChangeListener listener) { - propertySupport.addPropertyChangeListener(listener); - } - - public void removePropertyChangeListener(PropertyChangeListener listener) { - propertySupport.removePropertyChangeListener(listener); - } - - /** - * Executes the command on the current targetable component. - * If there isn't current targetable component then the list - * of targetable components are searched and the first component - * which can execute the command. If none of the targetable - * components handle the command then the ActionMaps of the - * focused components are searched. - * - * @param command the key of the command - * @param value the value of the command; depends on context - * @return true if the command has been handled otherwise false - */ - public boolean doCommand(Object command, Object value) { - // Try to invoked the explicit target. - if (target != null) { - if (target.hasCommand(command) && target.doCommand(command, value)) { - return true; - } - } - - // The target list has the next chance to handle the command. - if (targetList != null) { - Iterator iter = targetList.iterator(); - while (iter.hasNext()) { - Targetable target = iter.next(); - if (target.hasCommand(command) && - target.doCommand(command, value)) { - return true; - } - } - } - - ActionEvent evt = null; - if (value instanceof ActionEvent) { - evt = (ActionEvent)value; - } - - // Fall back behavior. Get the component which has focus and search the - // ActionMaps in the containment hierarchy for matching action. - Component comp = KeyboardFocusManager.getCurrentKeyboardFocusManager().getPermanentFocusOwner(); - while (comp != null) { - if (comp instanceof JComponent) { - ActionMap map = ((JComponent)comp).getActionMap(); - Action action = map.get(command); - if (action != null) { - if (evt == null) { - evt = new ActionEvent(comp, 0, command.toString()); - } - action.actionPerformed(evt); - - return true; - } - } - comp = comp.getParent(); - } - - return false; - } - - /** - * Resets the TargetManager. - * This method is package private and for testing purposes only. - */ - void reset() { - if (targetList != null) { - targetList.clear(); - targetList = null; - } - target = null; - - PropertyChangeListener[] listeners = propertySupport.getPropertyChangeListeners(); - for (int i = 0; i < listeners.length; i++) { - propertySupport.removePropertyChangeListener(listeners[i]); - } - INSTANCE = null; - } - -} diff --git a/src/main/java/org/jdesktop/swingx/action/Targetable.java b/src/main/java/org/jdesktop/swingx/action/Targetable.java deleted file mode 100644 index 43712ce47a..0000000000 --- a/src/main/java/org/jdesktop/swingx/action/Targetable.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * $Id: Targetable.java 3972 2011-03-17 20:31:58Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.action; - - -/** - * An interface which exposes the allowable actions to a TargetManager. - * The getCommands method will expose the allowable actions to another class - * and the doCommand method is called to invoke an action on the class. - *

                    - * Usually, the command key will be the key value of the Action. For components - * This could be the ActionMap keys. For actions managed with the ActionManager, - * this will be the value of an actions Action.ACTION_COMMAND_KEY - * - * @see TargetManager - * @author Mark Davidson - */ -public interface Targetable { - - /** - * Perform the command using the object value. - * - * @param command is a Action.ACTION_COMMAND_KEY - * @param value an arbitrary value. Usually this will be - * EventObject which trigered the command. - */ - boolean doCommand(Object command, Object value); - - /** - * Return a flag that indicates if a command is supported. - * - * @param command is a Action.ACTION_COMMAND_KEY - * @return true if command is supported; false otherwise - */ - boolean hasCommand(Object command); - - /** - * Returns an array of supported commands. If this Targetable - * doesn't support any commands (which is unlikely) then an - * empty array is returned. - * - * @return array of supported commands - */ - Object[] getCommands(); -} diff --git a/src/main/java/org/jdesktop/swingx/action/TargetableAction.java b/src/main/java/org/jdesktop/swingx/action/TargetableAction.java deleted file mode 100644 index f0b10f4b1f..0000000000 --- a/src/main/java/org/jdesktop/swingx/action/TargetableAction.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * $Id: TargetableAction.java 3972 2011-03-17 20:31:58Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.action; - -import javax.swing.*; -import java.awt.event.ActionEvent; -import java.awt.event.ItemEvent; - -/** - * A class that represents a dynamically targetable action. The invocation of this - * action will be dispatched to the TargetManager instance. - *

                    - * You would create instances of this class to let the TargetManager handle the - * action invocations from the ui components constructed with this action. - * The TargetManager could be configured depending on application state to - * handle these actions. - * - * @see TargetManager - * @author Mark Davidson - */ -public class TargetableAction extends AbstractActionExt { - - private TargetManager targetManager; - - public TargetableAction() { - this("action"); - } - - public TargetableAction(String name) { - super(name); - } - - /** - * @param name display name of the action - * @param command the value of the action command key - */ - public TargetableAction(String name, String command) { - super(name, command); - } - - /** - * @param name display name of the action - * @param command the value of the action command key - * @param icon icon to display - */ - public TargetableAction(String name, String command, Icon icon) { - super(name, command, icon); - } - - public TargetableAction(String name, Icon icon) { - super(name, icon); - } - - /** - * Set target manager which will handle this command. This action - * may be reset to use the singleton instance if set to null. - * - * @param tm the target manager instance to dispatch the actions - */ - public void setTargetManager(TargetManager tm) { - this.targetManager = tm; - } - - /** - * Returns the target manager instance which will be used for action - * dispatch. If the target manager has not previously been set then the - * singleton instance will be returned. - * - * @return a non null target manager - */ - public TargetManager getTargetManager() { - if (targetManager == null) { - targetManager = TargetManager.getInstance(); - } - return targetManager; - } - - // Callbacks... - - /** - * Callback for command actions. This event will be redispatched to - * the target manager along with the value of the Action.ACTION_COMMAND_KEY - * - * @param evt event which will be forwarded to the TargetManager - * @see TargetManager - */ - @Override - public void actionPerformed(ActionEvent evt) { - if (!isStateAction()) { - // Do not process this event if it's a toggle action. - getTargetManager().doCommand(getActionCommand(), evt); - } - } - - /** - * Callback for toggle actions. This event will be redispatched to - * the target manager along with value of the the Action.ACTION_COMMAND_KEY - * - * @param evt event which will be forwarded to the TargetManager - * @see TargetManager - */ - @Override - public void itemStateChanged(ItemEvent evt) { - // Update all objects that share this item - boolean newValue; - boolean oldValue = isSelected(); - - newValue = evt.getStateChange() == ItemEvent.SELECTED; - - if (oldValue != newValue) { - setSelected(newValue); - - getTargetManager().doCommand(getActionCommand(), evt); - } - } - - @Override - public String toString() { - return super.toString(); - } -} diff --git a/src/main/java/org/jdesktop/swingx/action/TargetableSupport.java b/src/main/java/org/jdesktop/swingx/action/TargetableSupport.java deleted file mode 100644 index 093e68dfdd..0000000000 --- a/src/main/java/org/jdesktop/swingx/action/TargetableSupport.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * $Id: TargetableSupport.java 3972 2011-03-17 20:31:58Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.action; - -import javax.swing.*; -import java.awt.event.ActionEvent; - -/** - * - * @author rbair - */ -public class TargetableSupport { - private JComponent component; - - /** Creates a new instance of TargetableSupport */ - public TargetableSupport(JComponent component) { - this.component = component; - } - - public boolean doCommand(Object command, Object value) { - // Look at the internal component first. - ActionMap map = component.getActionMap(); - Action action = map.get(command); - - if (action != null) { - if (value instanceof ActionEvent) { - action.actionPerformed( (ActionEvent) value); - } - else { - // XXX should the value represent the event source? - action.actionPerformed(new ActionEvent(value, 0, - command.toString())); - } - return true; - } - return false; - } - - public Object[] getCommands() { - ActionMap map = component.getActionMap(); - return map.allKeys(); - } - - public boolean hasCommand(Object command) { - Object[] commands = getCommands(); - for (int i = 0; i < commands.length; i++) { - if (commands[i].equals(command)) { - return true; - } - } - return false; - } -} diff --git a/src/main/java/org/jdesktop/swingx/action/ToggleActionPropertyChangeListener.java b/src/main/java/org/jdesktop/swingx/action/ToggleActionPropertyChangeListener.java deleted file mode 100644 index 2a23e6332f..0000000000 --- a/src/main/java/org/jdesktop/swingx/action/ToggleActionPropertyChangeListener.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * $Id: ToggleActionPropertyChangeListener.java 3972 2011-03-17 20:31:58Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.action; - -import javax.swing.*; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.lang.ref.WeakReference; - -/** - * Added to the Toggle type buttons and menu items so that various components - * which have been created from a single StateChangeAction can be in synch. - * - * This listener is responsible for updating the selected property from the - * Action to the AbstractButton.

                    - * - * It guarantees a maximum of 1 instance of - * ToggleActionPCL to be installed per button (PENDING JW: add test to verify). - * It removes all ToggleActionPCLs which are targeted to unreachable buttons - * from the action's listener list. - * - * - */ -class ToggleActionPropertyChangeListener implements PropertyChangeListener { - - - private WeakReference buttonRef; - - public ToggleActionPropertyChangeListener(Action action, AbstractButton button) { - if (shouldAddListener(action, button)) { - this.buttonRef = new WeakReference(button); - action.addPropertyChangeListener(this); - } - } - - - protected synchronized boolean shouldAddListener(Action action, AbstractButton button) { - releasePCLs(action); - // PENDING JW: revisit - we need a configurator to maintain at most a 1:1 from button to - // action anyway: so a true in isToggling must not happen. - // - return !isToggling(action, button); -// return true; - } - - - protected boolean isToggling(Action action, AbstractButton button) { - if (!(action instanceof AbstractAction)) return false; - PropertyChangeListener[] listeners = ((AbstractAction)action).getPropertyChangeListeners(); - for (int i = listeners.length - 1; i >= 0; i--) { - if (listeners[i] instanceof ToggleActionPropertyChangeListener) { - if (((ToggleActionPropertyChangeListener) listeners[i]).isToggling(button)) return true; - } - } - return false; - } - - /** - * Removes all ToggleActionPCLs with unreachable target buttons from the - * Action's PCL-listeners. - * - * @param action to cleanup. - */ - protected void releasePCLs(Action action) { - if (!(action instanceof AbstractAction)) return; - PropertyChangeListener[] listeners = ((AbstractAction)action).getPropertyChangeListeners(); - for (int i = listeners.length - 1; i >= 0; i--) { - if (listeners[i] instanceof ToggleActionPropertyChangeListener) { - ((ToggleActionPropertyChangeListener) listeners[i]).checkReferent(action); - } - } - } - - - @Override - public void propertyChange(PropertyChangeEvent evt) { - AbstractButton button = checkReferent((Action) evt.getSource()); - if (button == null) return; - String propertyName = evt.getPropertyName(); - - if (propertyName.equals("selected")) { - Boolean selected = (Boolean)evt.getNewValue(); - button.setSelected(selected.booleanValue()); - } - } - - /** - * Returns the target button to synchronize from the listener. - * - * Side-effects if the target is no longer reachable: - * - the internal reference to target is nulled. - * - if the given action is != null, this listener removes - * itself from the action's listener list. - * - * @param action The action this is listening to. - * @return the target button if it is strongly reachable or null - * if it is no longer strongly reachable. - */ - protected AbstractButton checkReferent(Action action) { - AbstractButton button = null; - if (buttonRef != null) { - button = buttonRef.get(); - } - if (button == null) { - if (action != null) { - action.removePropertyChangeListener(this); - } - buttonRef = null; - } - return button; - } - - - /** - * Check if this is already synchronizing the given AbstractButton. - * - * This may have the side-effect of releasing the weak reference to - * the target button. - * - * @param button must not be null - * @return true if this target button and the given comp are equal - * false otherwise. - * @throws NullPointerException if the button is null. - */ - public boolean isToggling(AbstractButton button) { - // JW: should check identity instead of equality? - return button.equals(checkReferent(null)); - } - - /** - * Checks if this is synchronizing to the same target button as the - * given listener. - * - * This may have the side-effect of releasing the weak reference to - * the target button. - * - * @param pcl The listener to test, must not be null. - * @return true if the target buttons of the given is equal to this target - * button and the button is strongly reachable, false otherwise. - */ -// public boolean isToggling(ToggleActionPropertyChangeListener pcl) { -// AbstractButton other = pcl.checkReferent(null); -// if (other != null) { -// return isToggling(other); -// } -// return false; -// } - - -} diff --git a/src/main/java/org/jdesktop/swingx/action/package-info.java b/src/main/java/org/jdesktop/swingx/action/package-info.java deleted file mode 100644 index f2182ec7f4..0000000000 --- a/src/main/java/org/jdesktop/swingx/action/package-info.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * $Id: package-info.java 3972 2011-03-17 20:31:58Z kschaefe $ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -/** - * Contains classes related to the JDNC actions architecture. The Actions - * architecture maintains the set of user initiated commands (referred to as - * user actions) in an application. These commands are represented as an - * {@link javax.swing.Action} and have properties like name and icon. The - * user actions - * are represented in the user interface by controls like menu items and - * toolbar buttons. - *

                    - * The other type of actions used by the architecture are the internal - * swing Actions (refered to as behaviour actions) that are embedded - * within the {@link javax.swing.ActionMap} of a {@link javax.swing.JComponent}. - *

                    - * These two types of actions are distinct from each other: user actions - * have a lot of properties but very little semantics by default - * (unless explicity bound). Behavior actions have no properties but have - * semantics. These two types of actions are linked by the action id - * which is the value of the Action.ACTION_COMMAND_KEY - *

                    - * The {@link org.jdesktop.swingx.action.AbstractActionExt} class extends the Swing - * concept of the Action by adding support for toggle or two state actions. - * Toggle type actions may be grouped into a set of mutually exclusive actions. - * This binary actions are represented in the user interface as JToggleButtons, - * JCheckBoxMenuItems or JRadioButtonMenuItems. - *

                    - * There are two types of user actions: A {@link org.jdesktop.swingx.action.BoundAction} - * is an action that will invoke a specific method. It may be bound to an explict - * component, a callback method on an object instance or one or more listeners. - * A {@link org.jdesktop.swingx.action.TargetableAction} is an action that doesn't have an - * explicit binding and the invocation will be sent to an arbitrator - * (the {@link org.jdesktop.swingx.action.TargetManager}) which dispatches the Action - * to the "current component" - represented by a Targetable instance. - * The current component may be explictly set by some programmatic - * policy (for example, changes in state). - *

                    - * By defalt, the current component will be driven by the focus policy as dictated - * by the current FocusManager. If the current component cannot handle the action - * then the action will be dispatched up the containment hierarchy until the action - * is consumed. If the action is not consumed then it will be dispatched to the - * Application instance which manages an application global set of actions. - *

                    - * These are the key classes or the actions architecture: - *

                    - *

                    - *
                    {@link org.jdesktop.swingx.action.ActionManager}
                    - *
                    A repository of all shared actions in the application. - * There will be one instance per application which can be accessed - * via the Application object (was ClientApp) - *
                    - * - *
                    {@link org.jdesktop.swingx.action.ActionContainerFactory}
                    - *
                    Constructs JMenuBars, JMenus, JPopupMenus and - * JToolBars using lists of action ids. This functionality may - * be migrated into ActionManager. - *
                    - * - *
                    {@link org.jdesktop.swingx.action.TargetableAction}
                    - *
                    Represents an unbound Action. The invocation of this action - * will be dispatched to the TargetManager.
                    - * - *
                    {@link org.jdesktop.swingx.action.BoundAction}
                    - *
                    Represents an action which has an exclicit binding.
                    - * - *
                    {@link org.jdesktop.swingx.action.TargetManager}
                    - *
                    Manages the targetable policy for actions which have no - * explicit binding. The policy can be set by changes in application - * state, event based criteria or whatever. If the policy has not been - * set then it will dispatch the action to the current focusable - * component. - *
                    - * - *
                    {@link org.jdesktop.swingx.action.Targetable}
                    - *
                    An interface that contains a few methods which expose actions to - * the TargetManager. Targetable objects don't have to be visual - * components they only have to be able to handle action invocations. - *
                    - *
                    - * - *
                    - *
                    Richard Bair
                    - */ -package org.jdesktop.swingx.action; \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/autocomplete/AbstractAutoCompleteAdaptor.java b/src/main/java/org/jdesktop/swingx/autocomplete/AbstractAutoCompleteAdaptor.java deleted file mode 100644 index 8cd5ad5c5d..0000000000 --- a/src/main/java/org/jdesktop/swingx/autocomplete/AbstractAutoCompleteAdaptor.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * $Id: AbstractAutoCompleteAdaptor.java 4045 2011-07-19 18:39:17Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.autocomplete; - -import javax.swing.text.JTextComponent; - -/** - * This is the interface that binds the mechanism for automatic completion to - * a data model, a selection model (e.g. those used by JList, JComboBox and JTable) - * and the JTextComponent for which the automatic completion should happen. - * It is used to search and select a matching item and to mark the completed text - * inside the JTextComponent. Using this interface the mechanism for automatic - * completion is independent from the underlying data and selection model. - * - * @see ComboBoxAdaptor - * @see ListAdaptor - * - * @author Thomas Bierhance - */ -public abstract class AbstractAutoCompleteAdaptor { - - /** the string representation in use for the currently selected item*/ - private String selectedItemAsString; - - /** - * Returns the currently selected item. - * @return the selected item - */ - public abstract Object getSelectedItem(); - - /** - * Sets the selected item. - * @param item the item that is to be selected - */ - public abstract void setSelectedItem(Object item); - - /** - * Returns the string representation in use for the currently selected item. - * @return the string representation in use for the currently selected item - */ - public String getSelectedItemAsString() { - return this.selectedItemAsString; - } - - /** - * Sets the string representation in use for the currently selected item. - * @param itemAsString the string representation in use for the currently selected item - */ - public void setSelectedItemAsString(String itemAsString) { - this.selectedItemAsString = itemAsString; - } - - /** - * Returns the number of items in the list. - * @return the number of items in the list - */ - public abstract int getItemCount(); - - /** - * Returns the item at a given index. It is supposed that 0<=index<getItemCount(). - * @param index the index of the item that is to be returned - * @return the item at the given index - */ - public abstract Object getItem(int index); - - /** - * Returns true if the list contains the currently selected item. - * @return true if the list contains the currently selected item. - */ - public boolean listContainsSelectedItem() { - Object selectedItem = getSelectedItem(); - for (int i=0,n=getItemCount(); istart. - * @param start index of the first character that should be marked - */ - public void markText(int start) { - getTextComponent().setCaretPosition(getTextComponent().getText().length()); - getTextComponent().moveCaretPosition(start); - } -} diff --git a/src/main/java/org/jdesktop/swingx/autocomplete/AutoComplete.java b/src/main/java/org/jdesktop/swingx/autocomplete/AutoComplete.java deleted file mode 100644 index 9e85fd0207..0000000000 --- a/src/main/java/org/jdesktop/swingx/autocomplete/AutoComplete.java +++ /dev/null @@ -1,225 +0,0 @@ -/* - * $Id: AutoComplete.java 4051 2011-07-19 20:17:05Z kschaefe $ - * - * Copyright 2010 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.autocomplete; - -import javax.swing.*; -import javax.swing.text.JTextComponent; -import java.awt.event.ActionEvent; -import java.awt.event.FocusEvent; -import java.awt.event.KeyEvent; -import java.awt.event.KeyListener; -import java.beans.PropertyChangeEvent; - -import static org.jdesktop.swingx.autocomplete.AutoCompleteDecorator.*; - -/** - * - * @author kschaefer - */ -final class AutoComplete { - static class InputMap extends javax.swing.InputMap { - private static final long serialVersionUID = 1L; - } - - static class FocusAdapter extends java.awt.event.FocusAdapter { - private AbstractAutoCompleteAdaptor adaptor; - - public FocusAdapter(AbstractAutoCompleteAdaptor adaptor) { - this.adaptor = adaptor; - } - - @Override - public void focusGained(FocusEvent e) { - adaptor.markEntireText(); - } - } - - static class KeyAdapter extends java.awt.event.KeyAdapter { - private JComboBox comboBox; - - public KeyAdapter(JComboBox comboBox) { - this.comboBox = comboBox; - } - - @Override - public void keyPressed(KeyEvent keyEvent) { - // don't popup on action keys (cursor movements, etc...) - if (keyEvent.isActionKey()) { - return; - } - - // don't popup if the combobox isn't visible or empty anyway - if (comboBox.isDisplayable() && !comboBox.isPopupVisible() && comboBox.getModel().getSize() != 0) { - int keyCode = keyEvent.getKeyCode(); - // don't popup when the user hits shift,ctrl or alt - if (keyCode==KeyEvent.VK_SHIFT || keyCode==KeyEvent.VK_CONTROL || keyCode==KeyEvent.VK_ALT) return; - // don't popup when the user hits escape (see issue #311) - if (keyCode==KeyEvent.VK_ENTER || keyCode==KeyEvent.VK_ESCAPE) return; - comboBox.setPopupVisible(true); - } - } - } - - static class PropertyChangeListener implements java.beans.PropertyChangeListener { - private JComboBox comboBox; - - public PropertyChangeListener(JComboBox comboBox) { - this.comboBox = comboBox; - } - - /** - * {@inheritDoc} - */ - @Override - @SuppressWarnings("nls") - public void propertyChange(PropertyChangeEvent evt) { - if ("editor".equals(evt.getPropertyName())) { - handleEditor(evt); - } else if ("enabled".equals(evt.getPropertyName())) { - handleEnabled(evt); - } - } - - private void handleEnabled(PropertyChangeEvent evt) { - if (Boolean.TRUE.equals(evt.getNewValue())) { - comboBox.setEditable(true); - } else { - JTextComponent textComponent = (JTextComponent) comboBox.getEditor().getEditorComponent(); - boolean strictMatching = ((AutoCompleteDocument) textComponent.getDocument()).strictMatching; - - comboBox.setEditable(!strictMatching); - } - } - - private void handleEditor(PropertyChangeEvent evt) { - if (evt.getNewValue() instanceof AutoCompleteComboBoxEditor) { - return; - } - - AutoCompleteComboBoxEditor acEditor = (AutoCompleteComboBoxEditor) evt.getOldValue(); - boolean strictMatching = false; - - if (acEditor.getEditorComponent() != null) { - JTextComponent textComponent = (JTextComponent) acEditor.getEditorComponent(); - strictMatching = ((AutoCompleteDocument) textComponent.getDocument()).strictMatching; - - undecorate(textComponent); - - for (KeyListener l : textComponent.getKeyListeners()) { - if (l instanceof KeyAdapter) { - textComponent.removeKeyListener(l); - break; - } - } - } - - JTextComponent editorComponent = (JTextComponent) comboBox.getEditor().getEditorComponent(); - AbstractAutoCompleteAdaptor adaptor = new ComboBoxAdaptor(comboBox); - AutoCompleteDocument document = createAutoCompleteDocument(adaptor, strictMatching, - acEditor.stringConverter, editorComponent.getDocument()); - decorate(editorComponent, document, adaptor); - - editorComponent.addKeyListener(new KeyAdapter(comboBox)); - - //set before adding the listener for the editor - comboBox.setEditor(new AutoCompleteComboBoxEditor(comboBox.getEditor(), document.stringConverter)); - } - } - - static class SelectionAction implements Action { - private Action delegate; - - public SelectionAction(Action delegate) { - this.delegate = delegate; - } - - /** - * {@inheritDoc} - */ - @Override - public void actionPerformed(ActionEvent e) { - JComboBox comboBox = (JComboBox) e.getSource(); - JTextComponent textComponent = (JTextComponent) comboBox.getEditor().getEditorComponent(); - AutoCompleteDocument doc = (AutoCompleteDocument) textComponent.getDocument(); - - // doing this prevents the updating of the selected item to "" during the remove prior - // to the insert in JTextComponent.setText - doc.strictMatching = true; - try { - delegate.actionPerformed(e); - } finally { - doc.strictMatching = false; - } - } - - /** - * {@inheritDoc} - */ - @Override - public void addPropertyChangeListener(java.beans.PropertyChangeListener listener) { - delegate.addPropertyChangeListener(listener); - } - - /** - * {@inheritDoc} - */ - @Override - public void removePropertyChangeListener(java.beans.PropertyChangeListener listener) { - delegate.removePropertyChangeListener(listener); - } - - /** - * {@inheritDoc} - */ - @Override - public Object getValue(String key) { - return delegate.getValue(key); - } - - /** - * {@inheritDoc} - */ - @Override - public void putValue(String key, Object value) { - delegate.putValue(key, value); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isEnabled() { - return delegate.isEnabled(); - } - - /** - * {@inheritDoc} - */ - @Override - public void setEnabled(boolean b) { - delegate.setEnabled(b); - } - } - - private AutoComplete() { - // prevent instantiation - } -} diff --git a/src/main/java/org/jdesktop/swingx/autocomplete/AutoCompleteComboBoxEditor.java b/src/main/java/org/jdesktop/swingx/autocomplete/AutoCompleteComboBoxEditor.java deleted file mode 100644 index 6ea656359f..0000000000 --- a/src/main/java/org/jdesktop/swingx/autocomplete/AutoCompleteComboBoxEditor.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * $Id: AutoCompleteComboBoxEditor.java 4045 2011-07-19 18:39:17Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.autocomplete; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.ActionListener; - -/** - *

                    - * Wrapper around the combobox editor that translates combobox items into - * strings. The methods setItem and getItem are modified - * to account for the string conversion. - *

                    - * This is necessary for those cases where the combobox items have no useful - * toString() method and a custom ObjectToStringConverter is - * used. - *

                    - * If we do not do this, the interaction between ComboBoxEditor and JComboBox - * will result in firing ActionListener events with the string value of - * ComboBoxEditor as the currently selected value. - *

                    - * @author Noel Grandin noelgrandin@gmail.com - * @author Thomas Bierhance - */ -public class AutoCompleteComboBoxEditor implements ComboBoxEditor { - - /** the original combo box editor*/ - final ComboBoxEditor wrapped; - /** the converter used to convert items into their string representation */ - final ObjectToStringConverter stringConverter; - /** last selected item */ - private Object oldItem; - - /** - * Creates a new AutoCompleteComboBoxEditor. - * - * @param wrapped the original ComboBoxEditor to be wrapped - * @param stringConverter the converter to use to convert items into their - * string representation. - */ - public AutoCompleteComboBoxEditor(ComboBoxEditor wrapped, ObjectToStringConverter stringConverter) { - this.wrapped = wrapped; - this.stringConverter = stringConverter; - } - - /* (non-javadoc) - * @see javax.swing.ComboBoxEditor#getEditorComponent() - */ - @Override - public Component getEditorComponent() { - return wrapped.getEditorComponent(); - } - - /* (non-javadoc) - * @see javax.swing.ComboBoxEditor#setItem(java.lang.Object) - */ - @Override - public void setItem(Object anObject) { - this.oldItem = anObject; - wrapped.setItem(stringConverter.getPreferredStringForItem(anObject)); - } - - /* (non-javadoc) - * @see javax.swing.ComboBoxEditor#getItem() - */ - @Override - public Object getItem() { - final Object wrappedItem = wrapped.getItem(); - - String[] oldAsStrings = stringConverter.getPossibleStringsForItem(oldItem); - for (int i=0, n=oldAsStrings.length; iUsage examples:

                    - *

                    
                    - * JComboBox comboBox = [...];
                    - * AutoCompleteDecorator.decorate(comboBox);
                    - * 
                    - * List items = [...];
                    - * JTextField textField = [...];
                    - * AutoCompleteDecorator.decorate(textField, items);
                    - * 
                    - * JList list = [...];
                    - * JTextField textField = [...];
                    - * AutoCompleteDecorator.decorate(list, textField);
                    - * 

                    - * - * @author Thomas Bierhance - * @author Karl Schaefer - */ -@SuppressWarnings({"nls", "serial"}) -public class AutoCompleteDecorator { - //these keys were pulled from BasicComboBoxUI from Sun JDK 1.6.0_20 - private static final List COMBO_BOX_ACTIONS = unmodifiableList(asList("selectNext", - "selectNext2", "selectPrevious", "selectPrevious2", "pageDownPassThrough", - "pageUpPassThrough", "homePassThrough", "endPassThrough")); - /** - * A TextAction that provides an error feedback for the text component that invoked - * the action. The error feedback is most likely a "beep". - */ - private static final Object errorFeedbackAction = new TextAction("provide-error-feedback") { - @Override - public void actionPerformed(ActionEvent e) { - UIManager.getLookAndFeel().provideErrorFeedback(getTextComponent(e)); - } - }; - - private AutoCompleteDecorator() { - //prevents instantiation - } - - private static void installMap(InputMap componentMap, boolean strict) { - InputMap map = new AutoComplete.InputMap(); - - if (strict) { - map.put(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_BACK_SPACE, 0), DefaultEditorKit.selectionBackwardAction); - // ignore VK_DELETE and CTRL+VK_X and beep instead when strict matching - map.put(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_DELETE, 0), errorFeedbackAction); - map.put(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_X, java.awt.event.InputEvent.CTRL_DOWN_MASK), errorFeedbackAction); - } else { - // VK_BACKSPACE will move the selection to the left if the selected item is in the list - // it will delete the previous character otherwise - map.put(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_BACK_SPACE, 0), "nonstrict-backspace"); - // leave VK_DELETE and CTRL+VK_X as is - } - - map.setParent(componentMap.getParent()); - componentMap.setParent(map); - } - - static AutoCompleteDocument createAutoCompleteDocument( - AbstractAutoCompleteAdaptor adaptor, boolean strictMatching, - ObjectToStringConverter stringConverter, Document delegate) { - if (delegate instanceof StyledDocument) { - return new AutoCompleteStyledDocument(adaptor, strictMatching, - stringConverter, (StyledDocument) delegate); - } - - return new AutoCompleteDocument(adaptor, strictMatching, - stringConverter, delegate); - } - - /** - * Enables automatic completion for the given JComboBox. The automatic - * completion will be strict (only items from the combo box can be selected) - * if the combo box is not editable. - * @param comboBox a combo box - * @see #decorate(JComboBox, ObjectToStringConverter) - */ - public static void decorate(JComboBox comboBox) { - decorate(comboBox, null); - } - - /** - * Enables automatic completion for the given JComboBox. The automatic - * completion will be strict (only items from the combo box can be selected) - * if the combo box is not editable. - *

                    - * Note: the {@code AutoCompleteDecorator} will alter the state of - * the {@code JComboBox} to be editable. This can cause side effects with - * layouts and sizing. {@code JComboBox} caches the size, which differs - * depending on the component's editability. Therefore, if the component's - * size is accessed prior to being decorated and then the cached size is - * forced to be recalculated, the size of the component will change. - *

                    - * Because the size of the component can be altered (recalculated), the - * decorator does not attempt to set any sizes on the supplied - * {@code JComboBox}. Users that need to ensure sizes of supplied combos - * should take measures to set the size of the combo. - * - * @param comboBox - * a combo box - * @param stringConverter - * the converter used to transform items to strings - */ - public static void decorate(JComboBox comboBox, ObjectToStringConverter stringConverter) { - undecorate(comboBox); - - boolean strictMatching = !comboBox.isEditable(); - // has to be editable - comboBox.setEditable(true); - // fix the popup location - MacOSXPopupLocationFix.install(comboBox); - - // configure the text component=editor component - JTextComponent editorComponent = (JTextComponent) comboBox.getEditor().getEditorComponent(); - final AbstractAutoCompleteAdaptor adaptor = new ComboBoxAdaptor(comboBox); - final AutoCompleteDocument document = createAutoCompleteDocument(adaptor, strictMatching, - stringConverter, editorComponent.getDocument()); - decorate(editorComponent, document, adaptor); - - editorComponent.addKeyListener(new AutoComplete.KeyAdapter(comboBox)); - - //set before adding the listener for the editor - comboBox.setEditor(new AutoCompleteComboBoxEditor(comboBox.getEditor(), document.stringConverter)); - - // Changing the l&f can change the combobox' editor which in turn - // would not be autocompletion-enabled. The new editor needs to be set-up. - AutoComplete.PropertyChangeListener pcl = new AutoComplete.PropertyChangeListener(comboBox); - comboBox.addPropertyChangeListener("editor", pcl); - comboBox.addPropertyChangeListener("enabled", pcl); - - if (!strictMatching) { - ActionMap map = comboBox.getActionMap(); - - for (String key : COMBO_BOX_ACTIONS) { - Action a = map.get(key); - map.put(key, new AutoComplete.SelectionAction(a)); - } - } - } - - static void undecorate(JComboBox comboBox) { - JTextComponent editorComponent = (JTextComponent) comboBox.getEditor().getEditorComponent(); - - if (editorComponent.getDocument() instanceof AutoCompleteDocument) { - AutoCompleteDocument doc = (AutoCompleteDocument) editorComponent.getDocument(); - - if (doc.strictMatching) { - ActionMap map = comboBox.getActionMap(); - - for (String key : COMBO_BOX_ACTIONS) { - map.put(key, null); - } - } - - //remove old property change listener - for (PropertyChangeListener l : comboBox.getPropertyChangeListeners("editor")) { - if (l instanceof AutoComplete.PropertyChangeListener) { - comboBox.removePropertyChangeListener("editor", l); - } - } - - for (PropertyChangeListener l : comboBox.getPropertyChangeListeners("enabled")) { - if (l instanceof AutoComplete.PropertyChangeListener) { - comboBox.removePropertyChangeListener("enabled", l); - } - } - - AutoCompleteComboBoxEditor editor = (AutoCompleteComboBoxEditor) comboBox.getEditor(); - comboBox.setEditor(editor.wrapped); - - //remove old key listener - for (KeyListener l : editorComponent.getKeyListeners()) { - if (l instanceof AutoComplete.KeyAdapter) { - editorComponent.removeKeyListener(l); - break; - } - } - - undecorate(editorComponent); - - for (ActionListener l : comboBox.getActionListeners()) { - if (l instanceof ComboBoxAdaptor) { - comboBox.removeActionListener(l); - break; - } - } - - //TODO remove aqua fix - - //TODO reset editibility - } - } - - /** - * Enables automatic completion for the given JTextComponent based on the - * items contained in the given JList. The two components will be - * synchronized. The automatic completion will always be strict. - * @param list a JList containing the items for automatic completion - * @param textComponent the text component that will be enabled for automatic - * completion - */ - public static void decorate(JList list, JTextComponent textComponent) { - decorate(list, textComponent, null); - } - - /** - * Enables automatic completion for the given JTextComponent based on the - * items contained in the given JList. The two components will be - * synchronized. The automatic completion will always be strict. - * @param list a JList containing the items for automatic completion - * @param textComponent the text component that will be used for automatic - * completion - * @param stringConverter the converter used to transform items to strings - */ - public static void decorate(JList list, JTextComponent textComponent, ObjectToStringConverter stringConverter) { - undecorate(list); - - AbstractAutoCompleteAdaptor adaptor = new ListAdaptor(list, textComponent, stringConverter); - AutoCompleteDocument document = createAutoCompleteDocument(adaptor, true, stringConverter, textComponent.getDocument()); - decorate(textComponent, document, adaptor); - } - - static void undecorate(JList list) { - for (ListSelectionListener l : list.getListSelectionListeners()) { - if (l instanceof ListAdaptor) { - list.removeListSelectionListener(l); - break; - } - } - } - - /** - * Enables automatic completion for the given JTextComponent based on the - * items contained in the given List. - * @param textComponent the text component that will be used for automatic - * completion. - * @param items contains the items that are used for autocompletion - * @param strictMatching true, if only given items should be allowed to be entered - */ - public static void decorate(JTextComponent textComponent, List items, boolean strictMatching) { - decorate(textComponent, items, strictMatching, null); - } - - /** - * Enables automatic completion for the given JTextComponent based on the - * items contained in the given List. - * @param items contains the items that are used for autocompletion - * @param textComponent the text component that will be used for automatic - * completion. - * @param strictMatching true, if only given items should be allowed to be entered - * @param stringConverter the converter used to transform items to strings - */ - public static void decorate(JTextComponent textComponent, List items, boolean strictMatching, ObjectToStringConverter stringConverter) { - AbstractAutoCompleteAdaptor adaptor = new TextComponentAdaptor(textComponent, items); - AutoCompleteDocument document = createAutoCompleteDocument(adaptor, strictMatching, stringConverter, textComponent.getDocument()); - decorate(textComponent, document, adaptor); - } - - /** - * Decorates a given text component for automatic completion using the - * given AutoCompleteDocument and AbstractAutoCompleteAdaptor. - * - * @param textComponent a text component that should be decorated - * @param document the AutoCompleteDocument to be installed on the text component - * @param adaptor the AbstractAutoCompleteAdaptor to be used - */ - public static void decorate(JTextComponent textComponent, AutoCompleteDocument document, AbstractAutoCompleteAdaptor adaptor) { - undecorate(textComponent); - - // install the document on the text component - textComponent.setDocument(document); - - // mark entire text when the text component gains focus - // otherwise the last mark would have been retained which is quiet confusing - textComponent.addFocusListener(new AutoComplete.FocusAdapter(adaptor)); - - // Tweak some key bindings - InputMap editorInputMap = textComponent.getInputMap(); - - while (editorInputMap != null) { - InputMap parent = editorInputMap.getParent(); - - if (parent instanceof UIResource) { - installMap(editorInputMap, document.isStrictMatching()); - break; - } - - editorInputMap = parent; - } - - ActionMap editorActionMap = textComponent.getActionMap(); - editorActionMap.put("nonstrict-backspace", new NonStrictBackspaceAction( - editorActionMap.get(DefaultEditorKit.deletePrevCharAction), - editorActionMap.get(DefaultEditorKit.selectionBackwardAction), - adaptor)); - } - - static void undecorate(JTextComponent textComponent) { - Document doc = textComponent.getDocument(); - - if (doc instanceof AutoCompleteDocument) { - //remove autocomplete key/action mappings - InputMap map = textComponent.getInputMap(); - - while (map.getParent() != null) { - InputMap parent = map.getParent(); - - if (parent instanceof AutoComplete.InputMap) { - map.setParent(parent.getParent()); - } - - map = parent; - } - - textComponent.getActionMap().put("nonstrict-backspace", null); - - //remove old focus listener - for (FocusListener l : textComponent.getFocusListeners()) { - if (l instanceof AutoComplete.FocusAdapter) { - textComponent.removeFocusListener(l); - break; - } - } - - //reset to original document - textComponent.setDocument(((AutoCompleteDocument) doc).delegate); - } - } - - static class NonStrictBackspaceAction extends TextAction { - Action backspace; - Action selectionBackward; - AbstractAutoCompleteAdaptor adaptor; - - public NonStrictBackspaceAction(Action backspace, Action selectionBackward, AbstractAutoCompleteAdaptor adaptor) { - super("nonstrict-backspace"); - this.backspace = backspace; - this.selectionBackward = selectionBackward; - this.adaptor = adaptor; - } - - @Override - public void actionPerformed(ActionEvent e) { - if (adaptor.listContainsSelectedItem()) { - selectionBackward.actionPerformed(e); - } else { - backspace.actionPerformed(e); - } - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/autocomplete/AutoCompleteDocument.java b/src/main/java/org/jdesktop/swingx/autocomplete/AutoCompleteDocument.java deleted file mode 100644 index e33a7524b7..0000000000 --- a/src/main/java/org/jdesktop/swingx/autocomplete/AutoCompleteDocument.java +++ /dev/null @@ -1,547 +0,0 @@ -/* - * $Id: AutoCompleteDocument.java 4051 2011-07-19 20:17:05Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.autocomplete; - -import org.jdesktop.swingx.util.Contract; - -import javax.swing.*; -import javax.swing.event.*; -import javax.swing.text.*; -import java.util.Comparator; - -import static org.jdesktop.swingx.autocomplete.ObjectToStringConverter.DEFAULT_IMPLEMENTATION; - -/** - * A document that can be plugged into any JTextComponent to enable automatic completion. - * It finds and selects matching items using any implementation of the AbstractAutoCompleteAdaptor. - */ -@SuppressWarnings("nls") -public class AutoCompleteDocument implements Document { - private class Handler implements DocumentListener, UndoableEditListener { - private final EventListenerList listenerList = new EventListenerList(); - - public void addDocumentListener(DocumentListener listener) { - listenerList.add(DocumentListener.class, listener); - } - - public void addUndoableEditListener(UndoableEditListener listener) { - listenerList.add(UndoableEditListener.class, listener); - } - - /** - * {@inheritDoc} - */ - public void removeDocumentListener(DocumentListener listener) { - listenerList.remove(DocumentListener.class, listener); - } - - /** - * {@inheritDoc} - */ - public void removeUndoableEditListener(UndoableEditListener listener) { - listenerList.remove(UndoableEditListener.class, listener); - } - - /** - * {@inheritDoc} - */ - @Override - public void changedUpdate(DocumentEvent e) { - e = new DelegatingDocumentEvent(AutoCompleteDocument.this, e); - - // Guaranteed to return a non-null array - Object[] listeners = listenerList.getListenerList(); - // Process the listeners last to first, notifying - // those that are interested in this event - for (int i = listeners.length-2; i>=0; i-=2) { - if (listeners[i]==DocumentListener.class) { - // Lazily create the event: - // if (e == null) - // e = new ListSelectionEvent(this, firstIndex, lastIndex); - ((DocumentListener)listeners[i+1]).changedUpdate(e); - } - } - } - - /** - * {@inheritDoc} - */ - @Override - public void insertUpdate(DocumentEvent e) { - e = new DelegatingDocumentEvent(AutoCompleteDocument.this, e); - - // Guaranteed to return a non-null array - Object[] listeners = listenerList.getListenerList(); - // Process the listeners last to first, notifying - // those that are interested in this event - for (int i = listeners.length-2; i>=0; i-=2) { - if (listeners[i]==DocumentListener.class) { - // Lazily create the event: - // if (e == null) - // e = new ListSelectionEvent(this, firstIndex, lastIndex); - ((DocumentListener)listeners[i+1]).insertUpdate(e); - } - } - } - - /** - * {@inheritDoc} - */ - @Override - public void removeUpdate(DocumentEvent e) { - e = new DelegatingDocumentEvent(AutoCompleteDocument.this, e); - - // Guaranteed to return a non-null array - Object[] listeners = listenerList.getListenerList(); - // Process the listeners last to first, notifying - // those that are interested in this event - for (int i = listeners.length-2; i>=0; i-=2) { - if (listeners[i]==DocumentListener.class) { - // Lazily create the event: - // if (e == null) - // e = new ListSelectionEvent(this, firstIndex, lastIndex); - ((DocumentListener)listeners[i+1]).removeUpdate(e); - } - } - } - - /** - * {@inheritDoc} - */ - @Override - public void undoableEditHappened(UndoableEditEvent e) { - e = new UndoableEditEvent(AutoCompleteDocument.this, e.getEdit()); - - // Guaranteed to return a non-null array - Object[] listeners = listenerList.getListenerList(); - // Process the listeners last to first, notifying - // those that are interested in this event - for (int i = listeners.length-2; i>=0; i-=2) { - if (listeners[i]==UndoableEditListener.class) { - // Lazily create the event: - // if (e == null) - // e = new ListSelectionEvent(this, firstIndex, lastIndex); - ((UndoableEditListener)listeners[i+1]).undoableEditHappened(e); - } - } - } - } - - /** - * true, if only items from the adaptors's list can be entered - * false, otherwise (selected item might not be in the adaptors's list) - */ - protected boolean strictMatching; - - protected final Document delegate; - - /** Flag to indicate if adaptor.setSelectedItem has been called. - * Subsequent calls to remove/insertString should be ignored - * as they are likely have been caused by the adapted Component that - * is trying to set the text for the selected component.*/ - boolean selecting = false; - - /** - * The adaptor that is used to find and select items. - */ - AbstractAutoCompleteAdaptor adaptor; - - ObjectToStringConverter stringConverter; - - private final Handler handler; - - // Note: these comparators do not impose any ordering - e.g. they do not ensure that sgn(compare(x, y)) == -sgn(compare(y, x)) - private static final Comparator EQUALS_IGNORE_CASE = new Comparator() { - @Override - public int compare(String o1, String o2) { - return o1.equalsIgnoreCase(o2) ? 0 : -1; - } - }; - - private static final Comparator STARTS_WITH_IGNORE_CASE = new Comparator() { - @Override - public int compare(String o1, String o2) { - if (o1.length() < o2.length()) return -1; - return o1.regionMatches(true, 0, o2, 0, o2.length()) ? 0 : -1; - } - }; - - private static final Comparator EQUALS = new Comparator() { - @Override - public int compare(String o1, String o2) { - return o1.equals(o2) ? 0 : -1; - } - }; - - private static final Comparator STARTS_WITH = new Comparator() { - @Override - public int compare(String o1, String o2) { - return o1.startsWith(o2) ? 0 : -1; - } - }; - - /** - * Creates a new AutoCompleteDocument for the given AbstractAutoCompleteAdaptor. - * @param adaptor The adaptor that will be used to find and select matching - * items. - * @param strictMatching true, if only items from the adaptor's list should - * be allowed to be entered - * @param stringConverter the converter used to transform items to strings - * @param delegate the {@code Document} delegate backing this document - */ - public AutoCompleteDocument(AbstractAutoCompleteAdaptor adaptor, boolean strictMatching, - ObjectToStringConverter stringConverter, Document delegate) { - this.adaptor = Contract.asNotNull(adaptor, "adaptor cannot be null"); - this.strictMatching = strictMatching; - this.stringConverter = stringConverter == null ? DEFAULT_IMPLEMENTATION : stringConverter; - this.delegate = delegate == null ? createDefaultDocument() : delegate; - - handler = new Handler(); - this.delegate.addDocumentListener(handler); - - // Handle initially selected object - Object selected = adaptor.getSelectedItem(); - if (selected != null) { - String itemAsString = this.stringConverter.getPreferredStringForItem(selected); - setText(itemAsString); - adaptor.setSelectedItemAsString(itemAsString); - } - this.adaptor.markEntireText(); - } - - - /** - * Creates a new AutoCompleteDocument for the given AbstractAutoCompleteAdaptor. - * @param adaptor The adaptor that will be used to find and select matching - * items. - * @param strictMatching true, if only items from the adaptor's list should - * be allowed to be entered - * @param stringConverter the converter used to transform items to strings - */ - public AutoCompleteDocument(AbstractAutoCompleteAdaptor adaptor, boolean strictMatching, ObjectToStringConverter stringConverter) { - this(adaptor, strictMatching, stringConverter, null); - } - - /** - * Creates a new AutoCompleteDocument for the given AbstractAutoCompleteAdaptor. - * @param strictMatching true, if only items from the adaptor's list should - * be allowed to be entered - * @param adaptor The adaptor that will be used to find and select matching - * items. - */ - public AutoCompleteDocument(AbstractAutoCompleteAdaptor adaptor, boolean strictMatching) { - this(adaptor, strictMatching, null); - } - - /** - * Creates the default backing document when no delegate is passed to this - * document. - * - * @return the default backing document - */ - protected Document createDefaultDocument() { - return new PlainDocument(); - } - - @Override - public void remove(int offs, int len) throws BadLocationException { - // return immediately when selecting an item - if (selecting) return; - delegate.remove(offs, len); - if (!strictMatching) { - setSelectedItem(getText(0, getLength()), getText(0, getLength())); - adaptor.getTextComponent().setCaretPosition(offs); - } - } - - @Override - public void insertString(int offs, String str, AttributeSet a) throws BadLocationException { - // return immediately when selecting an item - if (selecting) return; - // insert the string into the document - delegate.insertString(offs, str, a); - // lookup and select a matching item - LookupResult lookupResult; - String pattern = getText(0, getLength()); - - if(pattern == null || pattern.length() == 0) { - lookupResult = new LookupResult(null, ""); - setSelectedItem(lookupResult.matchingItem, lookupResult.matchingString); - } else { - lookupResult = lookupItem(pattern); - } - - if (lookupResult.matchingItem != null) { - setSelectedItem(lookupResult.matchingItem, lookupResult.matchingString); - } else { - if (strictMatching) { - // keep old item selected if there is no match - lookupResult.matchingItem = adaptor.getSelectedItem(); - lookupResult.matchingString = adaptor.getSelectedItemAsString(); - // imitate no insert (later on offs will be incremented by - // str.length(): selection won't move forward) - offs = str == null ? offs : offs - str.length(); - - if (str != null && !str.isEmpty()) { - // provide feedback to the user that his input has been received but can not be accepted - UIManager.getLookAndFeel().provideErrorFeedback(adaptor.getTextComponent()); - } - } else { - // no item matches => use the current input as selected item - lookupResult.matchingItem=getText(0, getLength()); - lookupResult.matchingString=getText(0, getLength()); - setSelectedItem(lookupResult.matchingItem, lookupResult.matchingString); - } - } - - setText(lookupResult.matchingString); - - // select the completed part - int len = str == null ? 0 : str.length(); - offs = lookupResult.matchingString == null ? 0 : offs + len; - adaptor.markText(offs); - } - - /** - * Sets the text of this AutoCompleteDocument to the given text. - * - * @param text the text that will be set for this document - */ - private void setText(String text) { - try { - // remove all text and insert the completed string - delegate.remove(0, getLength()); - delegate.insertString(0, text, null); - } catch (BadLocationException e) { - throw new RuntimeException(e.toString()); - } - } - - /** - * Selects the given item using the AbstractAutoCompleteAdaptor. - * @param itemAsString string representation of the item to be selected - * @param item the item that is to be selected - */ - private void setSelectedItem(Object item, String itemAsString) { - selecting = true; - adaptor.setSelectedItem(item); - adaptor.setSelectedItemAsString(itemAsString); - selecting = false; - } - - /** - * Searches for an item that matches the given pattern. The AbstractAutoCompleteAdaptor - * is used to access the candidate items. The match is not case-sensitive - * and will only match at the beginning of each item's string representation. - * - * @param pattern the pattern that should be matched - * @return the first item that matches the pattern or null if no item matches - */ - private LookupResult lookupItem(String pattern) { - Object selectedItem = adaptor.getSelectedItem(); - - LookupResult lookupResult; - - // first try: case sensitive - - lookupResult = lookupItem(pattern, EQUALS); - if (lookupResult != null) return lookupResult; - - lookupResult = lookupOneItem(selectedItem, pattern, STARTS_WITH); - if (lookupResult != null) return lookupResult; - - lookupResult = lookupItem(pattern, STARTS_WITH); - if (lookupResult != null) return lookupResult; - - // second try: ignore case - - lookupResult = lookupItem(pattern, EQUALS_IGNORE_CASE); - if (lookupResult != null) return lookupResult; - - lookupResult = lookupOneItem(selectedItem, pattern, STARTS_WITH_IGNORE_CASE); - if (lookupResult != null) return lookupResult; - - lookupResult = lookupItem(pattern, STARTS_WITH_IGNORE_CASE); - if (lookupResult != null) return lookupResult; - - // no item starts with the pattern => return null - return new LookupResult(null, ""); - } - - private LookupResult lookupOneItem(Object item, String pattern, Comparator comparator) { - String[] possibleStrings = stringConverter.getPossibleStringsForItem(item); - if (possibleStrings != null) { - for (int j = 0; j < possibleStrings.length; j++) { - if (comparator.compare(possibleStrings[j], pattern) == 0) { - return new LookupResult(item, possibleStrings[j]); - } - } - } - return null; - } - - private LookupResult lookupItem(String pattern, Comparator comparator) { - // iterate over all items and return first match - for (int i = 0, n = adaptor.getItemCount(); i < n; i++) { - Object currentItem = adaptor.getItem(i); - LookupResult result = lookupOneItem(currentItem, pattern, comparator); - if (result != null) return result; - } - return null; - } - - private static class LookupResult { - Object matchingItem; - String matchingString; - public LookupResult(Object matchingItem, String matchingString) { - this.matchingItem = matchingItem; - this.matchingString = matchingString; - } - } - - /** - * {@inheritDoc} - */ - @Override - public void addDocumentListener(DocumentListener listener) { - handler.addDocumentListener(listener); - } - - /** - * {@inheritDoc} - */ - @Override - public void addUndoableEditListener(UndoableEditListener listener) { - handler.addUndoableEditListener(listener); - } - - /** - * {@inheritDoc} - */ - @Override - public Position createPosition(int offs) throws BadLocationException { - return delegate.createPosition(offs); - } - - /** - * {@inheritDoc} - */ - @Override - public Element getDefaultRootElement() { - return delegate.getDefaultRootElement(); - } - - /** - * {@inheritDoc} - */ - @Override - public Position getEndPosition() { - return delegate.getEndPosition(); - } - - /** - * {@inheritDoc} - */ - @Override - public int getLength() { - return delegate.getLength(); - } - - /** - * {@inheritDoc} - */ - @Override - public Object getProperty(Object key) { - return delegate.getProperty(key); - } - - /** - * {@inheritDoc} - */ - @Override - public Element[] getRootElements() { - return delegate.getRootElements(); - } - - /** - * {@inheritDoc} - */ - @Override - public Position getStartPosition() { - return delegate.getStartPosition(); - } - - /** - * {@inheritDoc} - */ - @Override - public String getText(int offset, int length) throws BadLocationException { - return delegate.getText(offset, length); - } - - /** - * {@inheritDoc} - */ - @Override - public void getText(int offset, int length, Segment txt) throws BadLocationException { - delegate.getText(offset, length, txt); - } - - /** - * {@inheritDoc} - */ - @Override - public void putProperty(Object key, Object value) { - delegate.putProperty(key, value); - } - - /** - * {@inheritDoc} - */ - @Override - public void removeDocumentListener(DocumentListener listener) { - handler.removeDocumentListener(listener); - } - - /** - * {@inheritDoc} - */ - @Override - public void removeUndoableEditListener(UndoableEditListener listener) { - handler.removeUndoableEditListener(listener); - } - - /** - * {@inheritDoc} - */ - @Override - public void render(Runnable r) { - delegate.render(r); - } - - /** - * Returns if only items from the adaptor's list should be allowed to be entered. - * @return if only items from the adaptor's list should be allowed to be entered - */ - public boolean isStrictMatching() { - return strictMatching; - } -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/autocomplete/AutoCompleteStyledDocument.java b/src/main/java/org/jdesktop/swingx/autocomplete/AutoCompleteStyledDocument.java deleted file mode 100644 index d8ea38d593..0000000000 --- a/src/main/java/org/jdesktop/swingx/autocomplete/AutoCompleteStyledDocument.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * $Id: AutoCompleteStyledDocument.java 4045 2011-07-19 18:39:17Z kschaefe $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.autocomplete; - -import javax.swing.text.*; -import java.awt.*; - -/** - * - * @author Karl George Schaefer - */ -public class AutoCompleteStyledDocument extends AutoCompleteDocument implements - StyledDocument { - /** - * @param adaptor - * @param strictMatching - * @param stringConverter - * @param delegate - */ - public AutoCompleteStyledDocument(AbstractAutoCompleteAdaptor adaptor, - boolean strictMatching, ObjectToStringConverter stringConverter, - StyledDocument delegate) { - super(adaptor, strictMatching, stringConverter, delegate); - } - - /** - * @param adaptor - * @param strictMatching - * @param stringConverter - */ - public AutoCompleteStyledDocument(AbstractAutoCompleteAdaptor adaptor, - boolean strictMatching, ObjectToStringConverter stringConverter) { - super(adaptor, strictMatching, stringConverter); - } - - /** - * @param adaptor - * @param strictMatching - */ - public AutoCompleteStyledDocument(AbstractAutoCompleteAdaptor adaptor, - boolean strictMatching) { - super(adaptor, strictMatching); - } - - /** - * {@inheritDoc} - */ - @Override - protected Document createDefaultDocument() { - return new DefaultStyledDocument(); - } - - /** - * {@inheritDoc} - */ - @Override - public Style addStyle(String nm, Style parent) { - return ((StyledDocument) delegate).addStyle(nm, parent); - } - - /** - * {@inheritDoc} - */ - @Override - public Color getBackground(AttributeSet attr) { - return ((StyledDocument) delegate).getBackground(attr); - } - - /** - * {@inheritDoc} - */ - @Override - public Element getCharacterElement(int pos) { - return ((StyledDocument) delegate).getCharacterElement(pos); - } - - /** - * {@inheritDoc} - */ - @Override - public Font getFont(AttributeSet attr) { - return ((StyledDocument) delegate).getFont(attr); - } - - /** - * {@inheritDoc} - */ - @Override - public Color getForeground(AttributeSet attr) { - return ((StyledDocument) delegate).getForeground(attr); - } - - /** - * {@inheritDoc} - */ - @Override - public Style getLogicalStyle(int p) { - return ((StyledDocument) delegate).getLogicalStyle(p); - } - - /** - * {@inheritDoc} - */ - @Override - public Element getParagraphElement(int pos) { - return ((StyledDocument) delegate).getParagraphElement(pos); - } - - /** - * {@inheritDoc} - */ - @Override - public Style getStyle(String nm) { - return ((StyledDocument) delegate).getStyle(nm); - } - - /** - * {@inheritDoc} - */ - @Override - public void removeStyle(String nm) { - ((StyledDocument) delegate).removeStyle(nm); - } - - /** - * {@inheritDoc} - */ - @Override - public void setCharacterAttributes(int offset, int length, - AttributeSet s, boolean replace) { - ((StyledDocument) delegate).setCharacterAttributes(offset, length, s, replace); - } - - /** - * {@inheritDoc} - */ - @Override - public void setLogicalStyle(int pos, Style s) { - ((StyledDocument) delegate).setLogicalStyle(pos, s); - } - - /** - * {@inheritDoc} - */ - @Override - public void setParagraphAttributes(int offset, int length, - AttributeSet s, boolean replace) { - ((StyledDocument) delegate).setParagraphAttributes(offset, length, s, replace); - } -} diff --git a/src/main/java/org/jdesktop/swingx/autocomplete/ComboBoxAdaptor.java b/src/main/java/org/jdesktop/swingx/autocomplete/ComboBoxAdaptor.java deleted file mode 100644 index b285aab238..0000000000 --- a/src/main/java/org/jdesktop/swingx/autocomplete/ComboBoxAdaptor.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * $Id: ComboBoxAdaptor.java 4051 2011-07-19 20:17:05Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.autocomplete; - -import javax.accessibility.Accessible; -import javax.swing.*; -import javax.swing.plaf.basic.ComboPopup; -import javax.swing.text.JTextComponent; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -/** - * An implementation of the AbstractAutoCompleteAdaptor that is suitable for JComboBox. - * - * @author Thomas Bierhance - * @author Karl Schaefer - */ -@SuppressWarnings("nls") -public class ComboBoxAdaptor extends AbstractAutoCompleteAdaptor implements ActionListener { - - /** the combobox being adapted */ - private JComboBox comboBox; - - /** - * Creates a new ComobBoxAdaptor for the given combobox. - * @param comboBox the combobox that should be adapted - */ - public ComboBoxAdaptor(JComboBox comboBox) { - this.comboBox = comboBox; - // mark the entire text when a new item is selected - comboBox.addActionListener(this); - } - - /** - * Implementation side effect - do not invoke. - * @param actionEvent - - */ - // ActionListener (listening to comboBox) - @Override - public void actionPerformed(ActionEvent actionEvent) { - markEntireText(); - } - - @Override - public int getItemCount() { - return comboBox.getItemCount(); - } - - @Override - public Object getItem(int index) { - return comboBox.getItemAt(index); - } - - @Override - public void setSelectedItem(Object item) { - //SwingX 834: avoid moving when already selected - if (item == getSelectedItem()) { - return; - } - - // kgs - back door our way to finding the JList that displays the data. - // then we ask the list to scroll until the last cell is visible. this - // will cause the selected item to appear closest to the top. - // - // it is unknown whether this functionality will work outside of Sun's - // implementation, but the code is safe and will "fail gracefully" on - // other systems - Accessible a = comboBox.getUI().getAccessibleChild(comboBox, 0); - - if (getItemCount() > 0 && a instanceof ComboPopup) { - JList list = ((ComboPopup) a).getList(); - int lastIndex = list.getModel().getSize() - 1; - - Rectangle rect = list.getCellBounds(lastIndex, lastIndex); - - if (rect == null) { - throw new IllegalStateException( - "attempting to access index " + lastIndex + " for " + comboBox); - } - - list.scrollRectToVisible(rect); - } - - //setting the selected item should scroll it into the visible region - comboBox.setSelectedItem(item); - } - - @Override - public Object getSelectedItem() { - return comboBox.getModel().getSelectedItem(); - } - - @Override - public JTextComponent getTextComponent() { - // returning the component of the combobox's editor - return (JTextComponent) comboBox.getEditor().getEditorComponent(); - } -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/autocomplete/ComboBoxCellEditor.java b/src/main/java/org/jdesktop/swingx/autocomplete/ComboBoxCellEditor.java deleted file mode 100644 index 19eb6e508b..0000000000 --- a/src/main/java/org/jdesktop/swingx/autocomplete/ComboBoxCellEditor.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * $Id: ComboBoxCellEditor.java 4051 2011-07-19 20:17:05Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.autocomplete; - -import javax.swing.*; -import javax.swing.text.JTextComponent; -import java.awt.event.ActionEvent; -import java.awt.event.MouseEvent; -import java.util.EventObject; - -/** - *

                    This is a cell editor that can be used when a combo box (that has been set - * up for automatic completion) is to be used in a JTable. The - * {@link DefaultCellEditor DefaultCellEditor} won't work in this - * case, because each time an item gets selected it stops cell editing and hides - * the combo box. - *

                    - *

                    - * Usage example: - *

                    - *

                    - *

                    
                    - * JTable table = ...;
                    - * JComboBox comboBox = ...;
                    - * ...
                    - * TableColumn column = table.getColumnModel().getColumn(0);
                    - * column.setCellEditor(new ComboBoxCellEditor(comboBox));
                    - * 
                    - *

                    - */ -public class ComboBoxCellEditor extends DefaultCellEditor { - - /** - * Creates a new ComboBoxCellEditor. - * @param comboBox the comboBox that should be used as the cell editor. - */ - public ComboBoxCellEditor(final JComboBox comboBox) { - super(comboBox); - - comboBox.removeActionListener(this.delegate); - - this.delegate = new EditorDelegate() { - @Override - public void setValue(Object value) { - comboBox.setSelectedItem(value); - } - - @Override - public Object getCellEditorValue() { - return comboBox.getSelectedItem(); - } - - @Override - public boolean shouldSelectCell(EventObject anEvent) { - if (anEvent instanceof MouseEvent) { - MouseEvent e = (MouseEvent) anEvent; - return e.getID() != MouseEvent.MOUSE_DRAGGED; - } - return true; - } - - @Override - public boolean stopCellEditing() { - if (comboBox.isEditable()) { - // Commit edited value. - comboBox.actionPerformed(new ActionEvent(ComboBoxCellEditor.this, 0, "")); - } - return super.stopCellEditing(); - } - - @Override - public void actionPerformed(ActionEvent e) { - JTextComponent editorComponent = (JTextComponent) comboBox.getEditor() - .getEditorComponent(); - - if (editorComponent.getDocument() instanceof AutoCompleteDocument) { - AutoCompleteDocument document = (AutoCompleteDocument) editorComponent - .getDocument(); - // if auto completion is happening right now, cell editing should not be stopped - if (!document.selecting) { - ComboBoxCellEditor.this.stopCellEditing(); - } - - } else { - ComboBoxCellEditor.this.stopCellEditing(); - } - } - }; - - comboBox.addActionListener(this.delegate); - } -} diff --git a/src/main/java/org/jdesktop/swingx/autocomplete/DelegatingDocumentEvent.java b/src/main/java/org/jdesktop/swingx/autocomplete/DelegatingDocumentEvent.java deleted file mode 100644 index 393fa81ecb..0000000000 --- a/src/main/java/org/jdesktop/swingx/autocomplete/DelegatingDocumentEvent.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * - */ -package org.jdesktop.swingx.autocomplete; - -import javax.swing.event.DocumentEvent; -import javax.swing.text.Document; -import javax.swing.text.Element; - -/** - * @author Karl George Schaefer - * - */ -final class DelegatingDocumentEvent implements DocumentEvent { - private final Document resourcedDocument; - private final DocumentEvent sourceEvent; - - public DelegatingDocumentEvent(Document resourcedDocument, DocumentEvent sourceEvent) { - this.resourcedDocument = resourcedDocument; - this.sourceEvent = sourceEvent; - } - - /** - * {@inheritDoc} - */ - @Override - public ElementChange getChange(Element elem) { - return sourceEvent.getChange(elem); - } - - /** - * {@inheritDoc} - */ - @Override - public Document getDocument() { - return resourcedDocument; - } - - /** - * {@inheritDoc} - */ - @Override - public int getLength() { - return sourceEvent.getLength(); - } - - /** - * {@inheritDoc} - */ - @Override - public int getOffset() { - return sourceEvent.getOffset(); - } - - /** - * {@inheritDoc} - */ - @Override - public EventType getType() { - return sourceEvent.getType(); - } - -} diff --git a/src/main/java/org/jdesktop/swingx/autocomplete/ListAdaptor.java b/src/main/java/org/jdesktop/swingx/autocomplete/ListAdaptor.java deleted file mode 100644 index ea86fde491..0000000000 --- a/src/main/java/org/jdesktop/swingx/autocomplete/ListAdaptor.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * $Id: ListAdaptor.java 4045 2011-07-19 18:39:17Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.autocomplete; - -import javax.swing.*; -import javax.swing.event.ListSelectionListener; -import javax.swing.text.JTextComponent; - -/** - * An implementation of the AbstractAutoCompleteAdaptor that is suitable for a - * JList in conjunction with a JTextComponent. - * - * @author Thomas Bierhance - */ -public class ListAdaptor extends AbstractAutoCompleteAdaptor implements ListSelectionListener { - - /** the list containing the items */ - JList list; - /** the text component that is used for automatic completion*/ - JTextComponent textComponent; - /** the converter used to transform items to strings */ - ObjectToStringConverter stringConverter; - - /** - * Creates a new JListAdaptor for the given list and text component. - * @param list the list that contains the items that are used for automatic - * completion - * @param textComponent the text component that will be used automatic - * completion - */ - public ListAdaptor(JList list, JTextComponent textComponent) { - this(list, textComponent, ObjectToStringConverter.DEFAULT_IMPLEMENTATION); - } - - /** - * Creates a new JListAdaptor for the given list and text component. - * @param list the list that contains the items that are used for automatic - * completion - * @param textComponent the text component that will be used automatic - * completion - * @param stringConverter the converter used to transform items to strings - */ - public ListAdaptor(JList list, JTextComponent textComponent, ObjectToStringConverter stringConverter) { - this.list = list; - this.textComponent = textComponent; - this.stringConverter = stringConverter; - // when a new item is selected set and mark the text - list.addListSelectionListener(this); - } - - /** - * Implementation side effect - do not invoke. - * @param listSelectionEvent - - */ - // ListSelectionListener (listening to list) - @Override - public void valueChanged(javax.swing.event.ListSelectionEvent listSelectionEvent) { - // set the text to the currently selected item - getTextComponent().setText(stringConverter.getPreferredStringForItem(list.getSelectedValue())); - // mark the entire text - markEntireText(); - } - - @Override - public Object getSelectedItem() { - return list.getSelectedValue(); - } - - @Override - public int getItemCount() { - return list.getModel().getSize(); - } - - @Override - public Object getItem(int index) { - return list.getModel().getElementAt(index); - } - - @Override - public void setSelectedItem(Object item) { - list.setSelectedValue(item, true); - } - - @Override - public JTextComponent getTextComponent() { - return textComponent; - } -} diff --git a/src/main/java/org/jdesktop/swingx/autocomplete/ObjectToStringConverter.java b/src/main/java/org/jdesktop/swingx/autocomplete/ObjectToStringConverter.java deleted file mode 100644 index 84b0a28ad1..0000000000 --- a/src/main/java/org/jdesktop/swingx/autocomplete/ObjectToStringConverter.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * $Id: ObjectToStringConverter.java 4045 2011-07-19 18:39:17Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.autocomplete; - -/** - *

                    - * This class is used to provide string representations for objects when - * doing automatic completion. - *

                    - * A class inherited from this class could be used, when the object's - * toString method is not appropriate for automatic completion. - *

                    - * An example for i18n: - *

                    - *

                    - * public class I18NStringConverter extends ObjectToStringConverter {
                    - *   ResourceBundle bundle;
                    - *
                    - *   public I18NStringConverter(ResourceBundle bundle) {
                    - *     this.bundle = bundle;
                    - *   }
                    - *
                    - *   public String getPreferredStringForItem(Object item) {
                    - *     return item==null ? null : bundle.getString(item.toString());
                    - *   }
                    - * }
                    - * 
                    - *

                    - * It's also possible to return more than one string representation. The - * following example shows a converter that will allow a user to choose an - * airport using either the airport's full description (toString()) or - * its ICAO/IATA code: - *

                    - *

                    
                    - * public class AirportConverter extends ObjectToStringConverter {
                    - *
                    - *   public String[] getPossibleStringsForItem(Object item) {
                    - *     if (item==null) return new String[0];
                    - *     if (!(item instanceof Airport)) throw new IllegalArgumentException();
                    - *     Airport airport = (Airport) item;
                    - *     return new String[]{airport.toString(), airport.icaoCode, airport.iataCode};
                    - *   }
                    - *       
                    - *   public String getPreferredStringForItem(Object item) {
                    - *     return item==null?null:getPossibleStringsForItem(item)[0];
                    - *   }
                    - * }
                    - * 
                    - *

                    - * @author Thomas Bierhance - */ -public abstract class ObjectToStringConverter { - - /** - * Returns all possible String representations for a given item. - * The default implementation wraps the method getPreferredStringForItem. - * It returns an empty array, if the wrapped method returns null. Otherwise - * it returns a one dimensional array containing the wrapped method's return value. - * - * @param item the item to convert - * @return possible String representation for the given item. - */ - public String[] getPossibleStringsForItem(Object item) { - String preferred = getPreferredStringForItem(item); - return preferred == null ? new String[0] : new String[] { preferred }; - } - - /** - * Returns the preferred String representations for a given item. - * @param item the item to convert - * @return the preferred String representation for the given item. - */ - public abstract String getPreferredStringForItem(Object item); - - /** - * This field contains the default implementation, that returns item.toString() - * for any item !=null. For any item ==null, it returns null as well. - */ - public static final ObjectToStringConverter DEFAULT_IMPLEMENTATION = new DefaultObjectToStringConverter(); - - private static class DefaultObjectToStringConverter extends ObjectToStringConverter { - @Override - public String getPreferredStringForItem(Object item) { - return item==null ? null : item.toString(); - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/autocomplete/TextComponentAdaptor.java b/src/main/java/org/jdesktop/swingx/autocomplete/TextComponentAdaptor.java deleted file mode 100644 index 704fa73a26..0000000000 --- a/src/main/java/org/jdesktop/swingx/autocomplete/TextComponentAdaptor.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * $Id: TextComponentAdaptor.java 4045 2011-07-19 18:39:17Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.autocomplete; - -import javax.swing.text.JTextComponent; -import java.util.List; - -/** - * An implementation of the AbstractAutoCompleteAdaptor that is suitable for a - * JTextComponent. - * - * @author Thomas Bierhance - */ -public class TextComponentAdaptor extends AbstractAutoCompleteAdaptor { - - /** a List containing the strings to be used for automatic - * completion */ - List items; - /** the text component that is used for automatic completion*/ - JTextComponent textComponent; - /** the item that is currently selected */ - Object selectedItem; - - /** - * Creates a new TextComponentAdaptor for the given list and text - * component. - * - * @param items a List that contains the items that are used for - * automatic completion - * @param textComponent the text component that will be used automatic - * completion - */ - public TextComponentAdaptor(JTextComponent textComponent, List items) { - this.items = items; - this.textComponent = textComponent; - } - - @Override - public Object getSelectedItem() { - return selectedItem; - } - - @Override - public int getItemCount() { - return items.size(); - } - - @Override - public Object getItem(int index) { - return items.get(index); - } - - @Override - public void setSelectedItem(Object item) { - selectedItem = item; - } - - @Override - public JTextComponent getTextComponent() { - return textComponent; - } -} diff --git a/src/main/java/org/jdesktop/swingx/autocomplete/package-info.java b/src/main/java/org/jdesktop/swingx/autocomplete/package-info.java deleted file mode 100644 index 7f99224f86..0000000000 --- a/src/main/java/org/jdesktop/swingx/autocomplete/package-info.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * $Id: package-info.java 4045 2011-07-19 18:39:17Z kschaefe $ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -/** - * Contains classes to enable automatic completion for JComboBox and other - * components. - *

                    - * The automatic completion feature allows the user to enter a few characters - * using the keyboard - meanwhile, the computer "guesses" what the - * user intents to enter. Take a look at the example below to get an idea of the - * resulting user experience. Suppose the user types 'J','O','R' and 'G'... - *

                    - *

                    - * - *

                    - *

                    - * The easiest way to get automatic completion for a component is to use the - * {@link org.jdesktop.swingx.autocomplete.AutoCompleteDecorator AutoCompleteDecorator}. - *

                    - *

                    Enabling automatic completion for e.g. a JComboBox is only one line of - * code:

                    - *

                    - * import org.jdesktop.swingx.autocomplete.AutoCompleteDecorator;
                    - * [...]
                    - * JComboBox comboBox = [...];
                    - * AutoCompleteDecorator.decorate(comboBox); - *

                    - *

                    When the combo box is not editable when calling - * {@link org.jdesktop.swingx.autocomplete.AutoCompleteDecorator#decorate(JComboBox) decorate}, - * the automatic completion will be strict (only allowing items contained in - * the combo box). When the combo box is editable it will also be possible to - * enter items that are not contained in the combo box. - *

                    - *

                    Take care when enabling automatic completion for a JComboBox that is used - * as the cell editor for a JTable. You need to use the special - * {@link org.jdesktop.swingx.autocomplete.ComboBoxCellEditor ComboBoxCellEditor} - * instead of the standard DefaultCellEditor: - *

                    - *

                    - * JTable table = [...];
                    - * JComboBox comboBox = [...];
                    - * [...]
                    - * TableColumn column = table.getColumnModel().getColumn([...]);
                    - * column.setCellEditor(new ComboBoxCellEditor(comboBox)); - *

                    - *

                    - * If you want to enable automatic completion for a component that is not - * supported by the {@link org.jdesktop.swingx.autocomplete.AutoCompleteDecorator AutoCompleteDecorator}, you - * need to implement {@link org.jdesktop.swingx.autocomplete.AbstractAutoCompleteAdaptor AbstractAutoCompleteAdaptor}. For - * an example see {@link org.jdesktop.swingx.autocomplete.ComboBoxAdaptor ComboBoxAdaptor} - * and {@link org.jdesktop.swingx.autocomplete.ListAdaptor ListAdaptor}. - *

                    - *

                    - * The automatic completion works only for subclasses of - * {@link javax.swing.text.JTextComponent JTextComponent}. So you either use a component - * that contains a JTextComponent (e.g. JComboBox) or you connect a - * JTextComponent with another component (e.g. a JTextField and a JList). - * Of course, it's also possible to enable automatic completion for a - * JTextComponent without another visual component. - *

                    - *

                    Once you have a custom implementation of - * {@link org.jdesktop.swingx.autocomplete.AbstractAutoCompleteAdaptor AbstractAutoCompleteAdaptor}, - * you normally would only have to make three more calls: - *

                    - *

                    - * - * AbstractAutoCompleteAdaptor adaptor = new YourAdaptor([...]);
                    - * AutoCompleteDocument document = new AutoCompleteDocument(adaptor, true); // or false if you need non-strict matching
                    - * AutoCompleteDecorator.decorate(yourTextComponent, document, adaptor); - *
                    - *

                    - */ -package org.jdesktop.swingx.autocomplete; diff --git a/src/main/java/org/jdesktop/swingx/autocomplete/workarounds/MacOSXPopupLocationFix.java b/src/main/java/org/jdesktop/swingx/autocomplete/workarounds/MacOSXPopupLocationFix.java deleted file mode 100644 index f3c4bac468..0000000000 --- a/src/main/java/org/jdesktop/swingx/autocomplete/workarounds/MacOSXPopupLocationFix.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * $Id: MacOSXPopupLocationFix.java 4019 2011-05-11 16:52:30Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.autocomplete.workarounds; - -import javax.swing.*; -import javax.swing.event.PopupMenuEvent; -import javax.swing.event.PopupMenuListener; -import java.awt.*; - -/** - * Fix a problem where the JComboBox's popup obscures its editor in the Mac OS X - * Aqua look and feel. - * - *

                    Installing this fix will resolve the problem for Aqua without having - * side-effects for other look-and-feels. It also supports dynamically changed - * look and feels. - * - * @see Glazed Lists bug entry - * @see SwingX bug entry - * - * @author Jesse Wilson - */ -public final class MacOSXPopupLocationFix { - - /** the components being fixed */ - private final JComboBox comboBox; - private final JPopupMenu popupMenu; - - /** the listener provides callbacks as necessary */ - private final Listener listener = new Listener(); - - /** - * Private constructor so users use the more action-oriented - * {@link #install} method. - */ - private MacOSXPopupLocationFix(JComboBox comboBox) { - this.comboBox = comboBox; - this.popupMenu = (JPopupMenu)comboBox.getUI().getAccessibleChild(comboBox, 0); - - popupMenu.addPopupMenuListener(listener); - } - - /** - * Install the fix for the specified combo box. - */ - public static MacOSXPopupLocationFix install(JComboBox comboBox) { - if(comboBox == null) throw new IllegalArgumentException(); - return new MacOSXPopupLocationFix(comboBox); - } - - /** - * Uninstall the fix. Usually this is unnecessary since letting the combo - * box go out of scope is sufficient. - */ - public void uninstall() { - popupMenu.removePopupMenuListener(listener); - } - - /** - * Reposition the popup immediately before it is shown. - */ - private class Listener implements PopupMenuListener { - @Override - public void popupMenuWillBecomeVisible(PopupMenuEvent e) { - final JComponent popupComponent = (JComponent) e.getSource(); - fixPopupLocation(popupComponent); - } - @Override - public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { - // do nothing - } - @Override - public void popupMenuCanceled(PopupMenuEvent e) { - // do nothing - } - } - - /** - * Do the adjustment on the specified popupComponent immediately before - * it is displayed. - */ - private void fixPopupLocation(JComponent popupComponent) { - // we only need to fix Apple's aqua look and feel - if(popupComponent.getClass().getName().indexOf("apple.laf") != 0) { - return; - } - - // put the popup right under the combo box so it looks like a - // normal Aqua combo box - Point comboLocationOnScreen = comboBox.getLocationOnScreen(); - int comboHeight = comboBox.getHeight(); - int popupY = comboLocationOnScreen.y + comboHeight; - - // ...unless the popup overflows the screen, in which case we put it - // above the combobox - Rectangle screenBounds = new ScreenGeometry(comboBox).getScreenBounds(); - int popupHeight = popupComponent.getPreferredSize().height; - if(comboLocationOnScreen.y + comboHeight + popupHeight > screenBounds.x + screenBounds.height) { - popupY = comboLocationOnScreen.y - popupHeight; - } - - popupComponent.setLocation(comboLocationOnScreen.x, popupY); - } - - /** - * Figure out the dimensions of our screen. - * - *

                    This code is inspired by similar in - * JPopupMenu.adjustPopupLocationToFitScreen(). - * - * @author Jesse Wilson - */ - private final static class ScreenGeometry { - - final GraphicsConfiguration graphicsConfiguration; - final boolean aqua; - - public ScreenGeometry(JComponent component) { - this.aqua = UIManager.getLookAndFeel().getName().indexOf("Aqua") != -1; - this.graphicsConfiguration = graphicsConfigurationForComponent(component); - } - - /** - * Get the best graphics configuration for the specified point and component. - */ - private GraphicsConfiguration graphicsConfigurationForComponent(Component component) { - Point point = component.getLocationOnScreen(); - - // try to find the graphics configuration for our point of interest - GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); - GraphicsDevice[] gd = ge.getScreenDevices(); - for(int i = 0; i < gd.length; i++) { - if(gd[i].getType() != GraphicsDevice.TYPE_RASTER_SCREEN) continue; - GraphicsConfiguration defaultGraphicsConfiguration = gd[i].getDefaultConfiguration(); - if(!defaultGraphicsConfiguration.getBounds().contains(point)) continue; - return defaultGraphicsConfiguration; - } - - // we couldn't find a graphics configuration, use the component's - return component.getGraphicsConfiguration(); - } - - /** - * Get the bounds of where we can put a popup. - */ - public Rectangle getScreenBounds() { - Rectangle screenSize = getScreenSize(); - Insets screenInsets = getScreenInsets(); - - return new Rectangle( - screenSize.x + screenInsets.left, - screenSize.y + screenInsets.top, - screenSize.width - screenInsets.left - screenInsets.right, - screenSize.height - screenInsets.top - screenInsets.bottom - ); - } - - /** - * Get the bounds of the screen currently displaying the component. - */ - public Rectangle getScreenSize() { - // get the screen bounds and insets via the graphics configuration - if(graphicsConfiguration != null) { - return graphicsConfiguration.getBounds(); - } - - // just use the toolkit bounds, it's less awesome but sufficient - return new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()); - } - - /** - * Fetch the screen insets, the off limits areas around the screen such - * as menu bar, dock or start bar. - */ - public Insets getScreenInsets() { - Insets screenInsets; - if(graphicsConfiguration != null) { - screenInsets = Toolkit.getDefaultToolkit().getScreenInsets(graphicsConfiguration); - } else { - screenInsets = new Insets(0, 0, 0, 0); - } - - // tweak the insets for aqua, they're reported incorrectly there - if(aqua) { - int aquaBottomInsets = 21; // unreported insets, shown in screenshot, https://glazedlists.dev.java.net/issues/show_bug.cgi?id=332 - int aquaTopInsets = 22; // for Apple menu bar, found via debugger - - screenInsets.bottom = Math.max(screenInsets.bottom, aquaBottomInsets); - screenInsets.top = Math.max(screenInsets.top, aquaTopInsets); - } - - return screenInsets; - } - } -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/autocomplete/workarounds/package-info.java b/src/main/java/org/jdesktop/swingx/autocomplete/workarounds/package-info.java deleted file mode 100644 index ba1fb02945..0000000000 --- a/src/main/java/org/jdesktop/swingx/autocomplete/workarounds/package-info.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -/** - * Contains classes to workaround Look and Feel implemetation problems caused - * when applying the autocomplete decorators. - */ -package org.jdesktop.swingx.autocomplete.workarounds; - diff --git a/src/main/java/org/jdesktop/swingx/border/DropShadowBorder.java b/src/main/java/org/jdesktop/swingx/border/DropShadowBorder.java deleted file mode 100644 index a7d3ca6fa8..0000000000 --- a/src/main/java/org/jdesktop/swingx/border/DropShadowBorder.java +++ /dev/null @@ -1,440 +0,0 @@ -/* - * $Id: DropShadowBorder.java 4147 2012-02-01 17:13:24Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.border; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.util.GraphicsUtilities; - -import javax.swing.border.Border; -import java.awt.*; -import java.awt.geom.RoundRectangle2D; -import java.awt.image.BufferedImage; -import java.awt.image.ConvolveOp; -import java.awt.image.Kernel; -import java.io.Serializable; -import java.util.HashMap; -import java.util.Map; - -/** - * Implements a DropShadow for components. In general, the DropShadowBorder will - * work with any rectangular components that do not have a default border - * installed as part of the look and feel, or otherwise. For example, - * DropShadowBorder works wonderfully with JPanel, but horribly with JComboBox. - *

                    - * Note: {@code DropShadowBorder} should usually be added to non-opaque - * components, otherwise the background is likely to bleed through.

                    - *

                    Note: Since generating drop shadows is relatively expensive operation, - * {@code DropShadowBorder} keeps internal static cache that allows sharing - * same border for multiple re-rendering and between different instances of the - * class. Since this cache is shared at class level and never reset, it might - * bleed your app memory in case you tend to create many different borders - * rapidly.

                    - * @author rbair - */ -@JavaBean -public class DropShadowBorder implements Border, Serializable { - /** - * - */ - private static final long serialVersionUID = 715287754750604058L; - - private static enum Position {TOP, TOP_LEFT, LEFT, BOTTOM_LEFT, - BOTTOM, BOTTOM_RIGHT, RIGHT, TOP_RIGHT} - - private static final Map> CACHE - = new HashMap>(); - - private Color shadowColor; - private int shadowSize; - private float shadowOpacity; - private int cornerSize; - private boolean showTopShadow; - private boolean showLeftShadow; - private boolean showBottomShadow; - private boolean showRightShadow; - - public DropShadowBorder() { - this(Color.BLACK, 5); - } - - public DropShadowBorder(Color shadowColor, int shadowSize) { - this(shadowColor, shadowSize, .5f, 12, false, false, true, true); - } - - public DropShadowBorder(boolean showLeftShadow) { - this(Color.BLACK, 5, .5f, 12, false, showLeftShadow, true, true); - } - - public DropShadowBorder(Color shadowColor, int shadowSize, - float shadowOpacity, int cornerSize, boolean showTopShadow, - boolean showLeftShadow, boolean showBottomShadow, boolean showRightShadow) { - this.shadowColor = shadowColor; - this.shadowSize = shadowSize; - this.shadowOpacity = shadowOpacity; - this.cornerSize = cornerSize; - this.showTopShadow = showTopShadow; - this.showLeftShadow = showLeftShadow; - this.showBottomShadow = showBottomShadow; - this.showRightShadow = showRightShadow; - } - - /** - * {@inheritDoc} - */ - @Override - public void paintBorder(Component c, Graphics graphics, int x, int y, int width, int height) { - /* - * 1) Get images for this border - * 2) Paint the images for each side of the border that should be painted - */ - Map images = getImages((Graphics2D)graphics); - - Graphics2D g2 = (Graphics2D)graphics.create(); - - try { - //The location and size of the shadows depends on which shadows are being - //drawn. For instance, if the left & bottom shadows are being drawn, then - //the left shadow extends all the way down to the corner, a corner is drawn, - //and then the bottom shadow begins at the corner. If, however, only the - //bottom shadow is drawn, then the bottom-left corner is drawn to the - //right of the corner, and the bottom shadow is somewhat shorter than before. - - int shadowOffset = 2; //the distance between the shadow and the edge - - Point topLeftShadowPoint = null; - if (showLeftShadow || showTopShadow) { - topLeftShadowPoint = new Point(); - if (showLeftShadow && !showTopShadow) { - topLeftShadowPoint.setLocation(x, y + shadowOffset); - } else if (showLeftShadow && showTopShadow) { - topLeftShadowPoint.setLocation(x, y); - } else if (!showLeftShadow && showTopShadow) { - topLeftShadowPoint.setLocation(x + shadowSize, y); - } - } - - Point bottomLeftShadowPoint = null; - if (showLeftShadow || showBottomShadow) { - bottomLeftShadowPoint = new Point(); - if (showLeftShadow && !showBottomShadow) { - bottomLeftShadowPoint.setLocation(x, y + height - shadowSize - shadowSize); - } else if (showLeftShadow && showBottomShadow) { - bottomLeftShadowPoint.setLocation(x, y + height - shadowSize); - } else if (!showLeftShadow && showBottomShadow) { - bottomLeftShadowPoint.setLocation(x + shadowSize, y + height - shadowSize); - } - } - - Point bottomRightShadowPoint = null; - if (showRightShadow || showBottomShadow) { - bottomRightShadowPoint = new Point(); - if (showRightShadow && !showBottomShadow) { - bottomRightShadowPoint.setLocation(x + width - shadowSize, y + height - shadowSize - shadowSize); - } else if (showRightShadow && showBottomShadow) { - bottomRightShadowPoint.setLocation(x + width - shadowSize, y + height - shadowSize); - } else if (!showRightShadow && showBottomShadow) { - bottomRightShadowPoint.setLocation(x + width - shadowSize - shadowSize, y + height - shadowSize); - } - } - - Point topRightShadowPoint = null; - if (showRightShadow || showTopShadow) { - topRightShadowPoint = new Point(); - if (showRightShadow && !showTopShadow) { - topRightShadowPoint.setLocation(x + width - shadowSize, y + shadowOffset); - } else if (showRightShadow && showTopShadow) { - topRightShadowPoint.setLocation(x + width - shadowSize, y); - } else if (!showRightShadow && showTopShadow) { - topRightShadowPoint.setLocation(x + width - shadowSize - shadowSize, y); - } - } - - g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, - RenderingHints.VALUE_INTERPOLATION_BILINEAR); - g2.setRenderingHint(RenderingHints.KEY_RENDERING, - RenderingHints.VALUE_RENDER_SPEED); - - if (showLeftShadow) { - Rectangle leftShadowRect = - new Rectangle(x, - topLeftShadowPoint.y + shadowSize, - shadowSize, - bottomLeftShadowPoint.y - topLeftShadowPoint.y - shadowSize); - g2.drawImage(images.get(Position.LEFT), - leftShadowRect.x, leftShadowRect.y, - leftShadowRect.width, leftShadowRect.height, null); - } - - if (showBottomShadow) { - Rectangle bottomShadowRect = - new Rectangle(bottomLeftShadowPoint.x + shadowSize, - y + height - shadowSize, - bottomRightShadowPoint.x - bottomLeftShadowPoint.x - shadowSize, - shadowSize); - g2.drawImage(images.get(Position.BOTTOM), - bottomShadowRect.x, bottomShadowRect.y, - bottomShadowRect.width, bottomShadowRect.height, null); - } - - if (showRightShadow) { - Rectangle rightShadowRect = - new Rectangle(x + width - shadowSize, - topRightShadowPoint.y + shadowSize, - shadowSize, - bottomRightShadowPoint.y - topRightShadowPoint.y - shadowSize); - g2.drawImage(images.get(Position.RIGHT), - rightShadowRect.x, rightShadowRect.y, - rightShadowRect.width, rightShadowRect.height, null); - } - - if (showTopShadow) { - Rectangle topShadowRect = - new Rectangle(topLeftShadowPoint.x + shadowSize, - y, - topRightShadowPoint.x - topLeftShadowPoint.x - shadowSize, - shadowSize); - g2.drawImage(images.get(Position.TOP), - topShadowRect.x, topShadowRect.y, - topShadowRect.width, topShadowRect.height, null); - } - - if (showLeftShadow || showTopShadow) { - g2.drawImage(images.get(Position.TOP_LEFT), - topLeftShadowPoint.x, topLeftShadowPoint.y, null); - } - if (showLeftShadow || showBottomShadow) { - g2.drawImage(images.get(Position.BOTTOM_LEFT), - bottomLeftShadowPoint.x, bottomLeftShadowPoint.y, null); - } - if (showRightShadow || showBottomShadow) { - g2.drawImage(images.get(Position.BOTTOM_RIGHT), - bottomRightShadowPoint.x, bottomRightShadowPoint.y, null); - } - if (showRightShadow || showTopShadow) { - g2.drawImage(images.get(Position.TOP_RIGHT), - topRightShadowPoint.x, topRightShadowPoint.y, null); - } - } finally { - g2.dispose(); - } - } - - private Map getImages(Graphics2D g2) { - //first, check to see if an image for this size has already been rendered - //if so, use the cache. Else, draw and save - Map images = CACHE.get(shadowSize + (shadowColor.hashCode() * .3) + (shadowOpacity * .12));//TODO do a real hash - if (images == null) { - images = new HashMap(); - - /* - * To draw a drop shadow, I have to: - * 1) Create a rounded rectangle - * 2) Create a BufferedImage to draw the rounded rect in - * 3) Translate the graphics for the image, so that the rectangle - * is centered in the drawn space. The border around the rectangle - * needs to be shadowWidth wide, so that there is space for the - * shadow to be drawn. - * 4) Draw the rounded rect as shadowColor, with an opacity of shadowOpacity - * 5) Create the BLUR_KERNEL - * 6) Blur the image - * 7) copy off the corners, sides, etc into images to be used for - * drawing the Border - */ - int rectWidth = cornerSize + 1; - RoundRectangle2D rect = new RoundRectangle2D.Double(0, 0, rectWidth, rectWidth, cornerSize, cornerSize); - int imageWidth = rectWidth + shadowSize * 2; - BufferedImage image = GraphicsUtilities.createCompatibleTranslucentImage(imageWidth, imageWidth); - Graphics2D buffer = (Graphics2D)image.getGraphics(); - - try { - buffer.setPaint(new Color(shadowColor.getRed(), shadowColor.getGreen(), - shadowColor.getBlue(), (int)(shadowOpacity * 255))); -// buffer.setColor(new Color(0.0f, 0.0f, 0.0f, shadowOpacity)); - buffer.translate(shadowSize, shadowSize); - buffer.fill(rect); - } finally { - buffer.dispose(); - } - - float blurry = 1.0f / (shadowSize * shadowSize); - float[] blurKernel = new float[shadowSize * shadowSize]; - for (int i=0; i - * This border is useful when attempting to add {@code Icon}s to pre-existing - * components without requiring specialty painting. - * - * @author Amy Fowler - * @author Karl Schaefer - * - * @version 1.1 - */ -@JavaBean -public class IconBorder implements Border, Serializable { - - /** - * An empty icon. - */ - public static final Icon EMPTY_ICON = new EmptyIcon(); - private int padding; - private Icon icon; - private int iconPosition; - private Rectangle iconBounds = new Rectangle(); - - /** - * Creates an {@code IconBorder} with an empty icon in a trailing position - * with a padding of 4. - * - * @see #EMPTY_ICON - */ - public IconBorder() { - this(null); - } - - /** - * Creates an {@code IconBorder} with the specified icon in a trailing - * position with a padding of 4. - * - * @param validIcon - * the icon to set. This may be {@code null} to represent an - * empty icon. - * @see #EMPTY_ICON - */ - public IconBorder(Icon validIcon) { - this(validIcon, SwingConstants.TRAILING); - } - - /** - * Creates an {@code IconBorder} with the specified constraints and a - * padding of 4. - * - * @param validIcon - * the icon to set. This may be {@code null} to represent an - * empty icon. - * @param iconPosition - * the position to place the icon relative to the component - * contents. This must be one of the following - * {@code SwingConstants}: - *
                      - *
                    • {@code LEADING}
                    • - *
                    • {@code TRAILING}
                    • - *
                    • {@code EAST}
                    • - *
                    • {@code WEST}
                    • - *
                    - * @throws IllegalArgumentException - * if {@code iconPosition} is not a valid position. - * @see #EMPTY_ICON - */ - public IconBorder(Icon validIcon, int iconPosition) { - this(validIcon, iconPosition, 4); - } - - /** - * Creates an {@code IconBorder} with the specified constraints. If - * {@code validIcon} is {@code null}, {@code EMPTY_ICON} is used instead. - * If {@code padding} is negative, then the border does not use padding. - * - * @param validIcon - * the icon to set. This may be {@code null} to represent an - * empty icon. - * @param iconPosition - * the position to place the icon relative to the component - * contents. This must be one of the following - * {@code SwingConstants}: - *
                      - *
                    • {@code LEADING}
                    • - *
                    • {@code TRAILING}
                    • - *
                    • {@code EAST}
                    • - *
                    • {@code WEST}
                    • - *
                    - * @param padding - * the padding to surround the icon with. All non-positive values - * set the padding to 0. - * @throws IllegalArgumentException - * if {@code iconPosition} is not a valid position. - * @see #EMPTY_ICON - */ - public IconBorder(Icon validIcon, int iconPosition, int padding) { - setIcon(validIcon); - setPadding(padding); - setIconPosition(iconPosition); - } - - private boolean isValidPosition(int position) { - boolean result = false; - - switch (position) { - case SwingConstants.LEADING: - case SwingConstants.TRAILING: - case SwingConstants.EAST: - case SwingConstants.WEST: - result = true; - break; - default: - result = false; - } - - return result; - } - - /** - * {@inheritDoc} - */ - public Insets getBorderInsets(Component c) { - int horizontalInset = icon.getIconWidth() + (2 * padding); - int iconPosition = bidiDecodeLeadingTrailing(c.getComponentOrientation(), this.iconPosition); - if (iconPosition == SwingConstants.EAST) { - return new Insets(0, 0, 0, horizontalInset); - } - return new Insets(0, horizontalInset, 0, 0); - } - - /** - * Sets the icon for this border. - * - * @param validIcon - * the icon to set. This may be {@code null} to represent an - * empty icon. - * @see #EMPTY_ICON - */ - public void setIcon(Icon validIcon) { - this.icon = validIcon == null ? EMPTY_ICON : validIcon; - } - - /** - * This border is not opaque. - * - * @return always returns {@code false} - */ - public boolean isBorderOpaque() { - return false; - } - - /** - * {@inheritDoc} - */ - public void paintBorder(Component c, Graphics g, int x, int y, int width, - int height) { - int iconPosition = bidiDecodeLeadingTrailing(c.getComponentOrientation(), this.iconPosition); - if (iconPosition == SwingConstants.NORTH_EAST) { - iconBounds.y = y + padding; - iconBounds.x = x + width - padding - icon.getIconWidth(); - } else if (iconPosition == SwingConstants.EAST) { // EAST - iconBounds.y = y - + ((height - icon.getIconHeight()) / 2); - iconBounds.x = x + width - padding - icon.getIconWidth(); - } else if (iconPosition == SwingConstants.WEST) { - iconBounds.y = y - + ((height - icon.getIconHeight()) / 2); - iconBounds.x = x + padding; - } - iconBounds.width = icon.getIconWidth(); - iconBounds.height = icon.getIconHeight(); - icon.paintIcon(c, g, iconBounds.x, iconBounds.y); - } - - /** - * Returns EAST or WEST depending on the ComponentOrientation and - * the given postion LEADING/TRAILING this method has no effect for other - * position values - */ - private int bidiDecodeLeadingTrailing(ComponentOrientation c, int position) { - if(position == SwingConstants.TRAILING) { - if(!c.isLeftToRight()) { - return SwingConstants.WEST; - } - return SwingConstants.EAST; - } - if(position == SwingConstants.LEADING) { - if(c.isLeftToRight()) { - return SwingConstants.WEST; - } - return SwingConstants.EAST; - } - return position; - } - - /** - * Gets the padding surrounding the icon. - * - * @return the padding for the icon. This value is guaranteed to be - * nonnegative. - */ - public int getPadding() { - return padding; - } - - /** - * Sets the padding around the icon. - * - * @param padding - * the padding to set. If {@code padding < 0}, then - * {@code padding} will be set to {@code 0}. - */ - public void setPadding(int padding) { - this.padding = padding < 0 ? 0 : padding; - } - - /** - * Returns the position to place the icon (relative to the component contents). - * - * @return one of the following {@code SwingConstants}: - *
                      - *
                    • {@code LEADING}
                    • - *
                    • {@code TRAILING}
                    • - *
                    • {@code EAST}
                    • - *
                    • {@code WEST}
                    • - *
                    - */ - public int getIconPosition() { - return iconPosition; - } - - /** - * Sets the position to place the icon (relative to the component contents). - * - * @param iconPosition must be one of the following {@code SwingConstants}: - *
                      - *
                    • {@code LEADING}
                    • - *
                    • {@code TRAILING}
                    • - *
                    • {@code EAST}
                    • - *
                    • {@code WEST}
                    • - *
                    - * @throws IllegalArgumentException - * if {@code iconPosition} is not a valid position. - */ - public void setIconPosition(int iconPosition) { - if (!isValidPosition(iconPosition)) { - throw new IllegalArgumentException("Invalid icon position"); - } - this.iconPosition = iconPosition; - } - -} diff --git a/src/main/java/org/jdesktop/swingx/border/MatteBorderExt.java b/src/main/java/org/jdesktop/swingx/border/MatteBorderExt.java deleted file mode 100644 index 0829c6698e..0000000000 --- a/src/main/java/org/jdesktop/swingx/border/MatteBorderExt.java +++ /dev/null @@ -1,303 +0,0 @@ -/* - * $Id: MatteBorderExt.java 4147 2012-02-01 17:13:24Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.border; - -import org.jdesktop.beans.JavaBean; - -import javax.swing.*; -import javax.swing.border.MatteBorder; -import java.awt.*; - -/** - * Matte border that allows specialized icons for corners and sides. - * - * @author Ramesh Gupta - */ -@JavaBean -public class MatteBorderExt extends MatteBorder { - protected Icon[] tileIcons = null; - private Icon defaultIcon = null; - - /** - * Draws a matte border using specialized icons for corners and sides. If - * tileIcons is null, or if the length of tileIcons array is less than 2, this - * defaults to the {@link MatteBorder superclass} behavior. - * Otherwise, tileIcons must specify icons in clockwise order, starting with - * the top-left icon at index zero, culminating with the left icon at index 7. - * If the length of the tileIcons array is greater than 1, but less than 8, - * then tileIcons[0] is used to paint the corners, and tileIcons[1] is used - * to paint the sides, with icons rotated as necessary. Other icons, if any, - * are ignored. - * - * @param top top inset - * @param left left inset - * @param bottom bottom inset - * @param right right inset - * @param tileIcons array of icons starting with top-left in index 0, - * continuing clockwise through the rest of the indices - */ - public MatteBorderExt(int top, int left, int bottom, int right, - Icon[] tileIcons) { - super(top, left, bottom, right, - (tileIcons == null) || (tileIcons.length == 0) ? null : - tileIcons[0]); - this.tileIcons = tileIcons; - } - - /** - * @see MatteBorder#MatteBorder(int, int, int, int, Color) - */ - public MatteBorderExt(int top, int left, int bottom, int right, - Color matteColor) { - super(top, left, bottom, right, matteColor); - } - - /** - * @see MatteBorder#MatteBorder(Insets, Color) - */ - public MatteBorderExt(Insets borderInsets, Color matteColor) { - super(borderInsets, matteColor); - } - - /** - * @see MatteBorder#MatteBorder(int, int, int, int, Icon) - */ - public MatteBorderExt(int top, int left, int bottom, int right, - Icon tileIcon) { - super(top, left, bottom, right, tileIcon); - } - - /** - * @see MatteBorder#MatteBorder(Insets, Icon) - */ - public MatteBorderExt(Insets borderInsets, Icon tileIcon) { - super(borderInsets, tileIcon); - } - - /** - * @see MatteBorder#MatteBorder(Icon) - */ - public MatteBorderExt(Icon tileIcon) { - super(tileIcon); - } - - /** - * Returns the icons used by this border - * - * @return the icons used by this border - */ - public Icon[] getTileIcons() { - return tileIcons; - } - - /** - * {@inheritDoc} - */ - @Override - public void paintBorder(Component c, Graphics g, int x, int y, - int width, int height) { - if ( (tileIcons == null) || (tileIcons.length < 2)) { - super.paintBorder(c, g, x, y, width, height); - return; - } - - Insets insets = getBorderInsets(c); - int clipWidth, clipHeight; - - clipWidth = Math.min(width, insets.left); // clip to component width or insets - clipHeight = Math.min(height, insets.top); // clip to component height or insets - - if ( (clipWidth <= 0) || (clipHeight <= 0)) { - return; // nothing to paint - } - - // We now know that we have at least two icons! - - Color oldColor = g.getColor(); // restore before exiting - g.translate(x, y); // restore before exiting - - for (int i = 0; i < tileIcons.length; i++) { - // Make sure we have an icon to paint with - if (tileIcons[i] == null) { - tileIcons[i] = getDefaultIcon(); - } - } - - paintTopLeft(c, g, 0, 0, insets.left, insets.top); - paintTop(c, g, insets.left, 0, width - insets.left - insets.right, insets.top); - paintTopRight(c, g, width - insets.right, 0, insets.right, insets.top); - paintRight(c, g, width - insets.right, insets.top, insets.right, height - insets.top - insets.bottom); - paintBottomRight(c, g, width - insets.right, height - insets.bottom, insets.right, insets.bottom); - paintBottom(c, g, insets.left, height - insets.bottom, width - insets.left - insets.right, insets.bottom); - paintBottomLeft(c, g, 0, height - insets.bottom, insets.left, insets.bottom); - paintLeft(c, g, 0, insets.top, insets.left, height - insets.top - insets.bottom); - - g.translate( -x, -y); // restore - g.setColor(oldColor); // restore - - } - - protected void paint(Icon icon, Component c, Graphics g, int x, int y, - int width, int height) { - Graphics cg = g.create(); - - try { - cg.setClip(x, y, width, height); - int tileW = icon.getIconWidth(); - int tileH = icon.getIconHeight(); - int xpos, ypos, startx, starty; - for (ypos = 0; height - ypos > 0; ypos += tileH) { - for (xpos = 0; width - xpos > 0; xpos += tileW) { - icon.paintIcon(c, cg, x + xpos, y + ypos); - } - } - } finally { - cg.dispose(); - } - } - - /** - * Only called by paintBorder() - */ - protected void paintTopLeft(Component c, Graphics g, int x, int y, int width, int height) { - Graphics cg = g.create(); - - try { - cg.setClip(x, y, width, height); - tileIcons[0].paintIcon(c, cg, x, y); - } finally { - cg.dispose(); - } - } - - /** - * Only called by paintBorder() - */ - protected void paintTop(Component c, Graphics g, int x, int y, int width, int height) { - paint(tileIcons[1], c, g, x, y, width, height); - } - - /** - * Only called by paintBorder() - */ - protected void paintTopRight(Component c, Graphics g, int x, int y, int width, int height) { - if (tileIcons.length == 8) { - paint(tileIcons[2], c, g, x, y, width, height); - } - else { - Icon icon = tileIcons[0]; - /** @todo Rotate -90 and paint icon */ - } - } - - /** - * Only called by paintBorder() - */ - protected void paintRight(Component c, Graphics g, int x, int y, int width, int height) { - if (tileIcons.length == 8) { - paint(tileIcons[3], c, g, x, y, width, height); - } - else { - Icon icon = tileIcons[1]; - /** @todo Rotate -90 and paint icon */ - } - } - - /** - * Only called by paintBorder() - */ - protected void paintBottomRight(Component c, Graphics g, int x, int y, int width, int height) { - if (tileIcons.length == 8) { - paint(tileIcons[4], c, g, x, y, width, height); - } - else { - Icon icon = tileIcons[0]; - /** @todo Rotate -180 and paint icon */ - } - } - - /** - * Only called by paintBorder() - */ - protected void paintBottom(Component c, Graphics g, int x, int y, int width, int height) { - if (tileIcons.length == 8) { - paint(tileIcons[5], c, g, x, y, width, height); - } - else { - Icon icon = tileIcons[1]; - /** @todo Rotate -180 and paint icon */ - } - } - - /** - * Only called by paintBorder() - */ - protected void paintBottomLeft(Component c, Graphics g, int x, int y, int width, int height) { - if (tileIcons.length == 8) { - paint(tileIcons[6], c, g, x, y, width, height); - } - else { - Icon icon = tileIcons[0]; - /** @todo Rotate -270 and paint icon */ - } - } - - /** - * Only called by paintBorder() - */ - protected void paintLeft(Component c, Graphics g, int x, int y, int width, int height) { - if (tileIcons.length == 8) { - paint(tileIcons[7], c, g, x, y, width, height); - } - else { - Icon icon = tileIcons[1]; - /** @todo Rotate -270 and paint icon */ - } - } - - /** - * Only called by paintBorder() - */ - protected Icon getDefaultIcon() { - if (defaultIcon == null) { - defaultIcon = new Icon() { - private int width = 3; - private int height = 3; - - public int getIconWidth() { - return width; - } - - public int getIconHeight() { - return height; - } - - public void paintIcon(Component c, Graphics g, int x, int y) { - g.setColor(c.getBackground().darker().darker()); - //g.translate(x, y); - g.fillRect(x, y, width, height); - } - }; - } - return defaultIcon; - } -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/border/package-info.java b/src/main/java/org/jdesktop/swingx/border/package-info.java deleted file mode 100644 index 287491e5d7..0000000000 --- a/src/main/java/org/jdesktop/swingx/border/package-info.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -/** - * Contains extensions to the {@code javax.swingx.border} package. - */ -package org.jdesktop.swingx.border; - diff --git a/src/main/java/org/jdesktop/swingx/calendar/AbstractDateSelectionModel.java b/src/main/java/org/jdesktop/swingx/calendar/AbstractDateSelectionModel.java deleted file mode 100644 index 228988b4e3..0000000000 --- a/src/main/java/org/jdesktop/swingx/calendar/AbstractDateSelectionModel.java +++ /dev/null @@ -1,329 +0,0 @@ -/* - * $Id: AbstractDateSelectionModel.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.calendar; - -import org.jdesktop.swingx.event.DateSelectionEvent; -import org.jdesktop.swingx.event.DateSelectionEvent.EventType; -import org.jdesktop.swingx.event.DateSelectionListener; -import org.jdesktop.swingx.event.EventListenerMap; - -import java.util.*; - -/** - * Abstract base implementation of DateSelectionModel. Implements - * notification, Calendar related properties and lower/upper bounds. - * - * @author Jeanette Winzenburg - */ -public abstract class AbstractDateSelectionModel implements DateSelectionModel { - public static final SortedSet EMPTY_DATES = Collections.unmodifiableSortedSet(new TreeSet()); - - protected EventListenerMap listenerMap; - protected boolean adjusting; - protected Calendar calendar; - protected Date upperBound; - protected Date lowerBound; - - /** - * the locale used by the calendar.

                    - * NOTE: need to keep separately as a Calendar has no getter. - */ - protected Locale locale; - - /** - * Instantiates a DateSelectionModel with default locale. - */ - public AbstractDateSelectionModel() { - this(null); - } - - /** - * Instantiates a DateSelectionModel with the given locale. If the locale is - * null, the Locale's default is used. - * - * PENDING JW: fall back to JComponent.getDefaultLocale instead? We use this - * with components anyway? - * - * @param locale the Locale to use with this model, defaults to Locale.default() - * if null. - */ - public AbstractDateSelectionModel(Locale locale) { - this.listenerMap = new EventListenerMap(); - setLocale(locale); - } - - /** - * {@inheritDoc} - */ - @Override - public Calendar getCalendar() { - return (Calendar) calendar.clone(); - } - - /** - * {@inheritDoc} - */ - @Override - public int getFirstDayOfWeek() { - return calendar.getFirstDayOfWeek(); - } - - /** - * {@inheritDoc} - */ - @Override - public void setFirstDayOfWeek(final int firstDayOfWeek) { - if (firstDayOfWeek == getFirstDayOfWeek()) return; - calendar.setFirstDayOfWeek(firstDayOfWeek); - fireValueChanged(EventType.CALENDAR_CHANGED); - } - - /** - * {@inheritDoc} - */ - @Override - public int getMinimalDaysInFirstWeek() { - return calendar.getMinimalDaysInFirstWeek(); - } - - /** - * {@inheritDoc} - */ - @Override - public void setMinimalDaysInFirstWeek(int minimalDays) { - if (minimalDays == getMinimalDaysInFirstWeek()) return; - calendar.setMinimalDaysInFirstWeek(minimalDays); - fireValueChanged(EventType.CALENDAR_CHANGED); - } - - - /** - * {@inheritDoc} - */ - @Override - public TimeZone getTimeZone() { - return calendar.getTimeZone(); - } - - /** - * {@inheritDoc} - */ - @Override - public void setTimeZone(TimeZone timeZone) { - if (getTimeZone().equals(timeZone)) return; - TimeZone oldTimeZone = getTimeZone(); - calendar.setTimeZone(timeZone); - adjustDatesToTimeZone(oldTimeZone); - fireValueChanged(EventType.CALENDAR_CHANGED); - } - - /** - * Adjusts all stored dates to a new time zone. - * This method is called after the change had been made.

                    - * - * This implementation resets all dates to null, clears everything. - * Subclasses may override to really map to the new time zone. - * - * @param oldTimeZone the old time zone - * - */ - protected void adjustDatesToTimeZone(TimeZone oldTimeZone) { - clearSelection(); - setLowerBound(null); - setUpperBound(null); - setUnselectableDates(EMPTY_DATES); - } - - /** - * {@inheritDoc} - */ - @Override - public Locale getLocale() { - return locale; - } - - /** - * {@inheritDoc} - */ - @Override - public void setLocale(Locale locale) { - if (locale == null) { - locale = Locale.getDefault(); - } - if (locale.equals(getLocale())) return; - this.locale = locale; - if (calendar != null) { - calendar = Calendar.getInstance(calendar.getTimeZone(), locale); - } else { - calendar = Calendar.getInstance(locale); - } - fireValueChanged(EventType.CALENDAR_CHANGED); - } - -//------------------- utility methods - - /** - * Returns the start of the day of the given date in this model's calendar. - * NOTE: the calendar is changed by this operation. - * - * @param date the Date to get the start for. - * @return the Date representing the start of the day of the input date. - */ - protected Date startOfDay(Date date) { - return CalendarUtils.startOfDay(calendar, date); - } - - /** - * Returns the end of the day of the given date in this model's calendar. - * NOTE: the calendar is changed by this operation. - * - * @param date the Date to get the start for. - * @return the Date representing the end of the day of the input date. - */ - protected Date endOfDay(Date date) { - return CalendarUtils.endOfDay(calendar, date); - } - - /** - * Returns a boolean indicating whether the given dates are on the same day in - * the coordinates of the model's calendar. - * - * @param selected one of the dates to check, must not be null. - * @param compare the other of the dates to check, must not be null. - * @return true if both dates represent the same day in this model's calendar. - */ - protected boolean isSameDay(Date selected, Date compare) { - return startOfDay(selected).equals(startOfDay(compare)); - } - -//------------------- bounds - - /** - * {@inheritDoc} - */ - @Override - public Date getUpperBound() { - return upperBound; - } - - /** - * {@inheritDoc} - */ - @Override - public void setUpperBound(Date upperBound) { - if (upperBound != null) { - upperBound = getNormalizedDate(upperBound); - } - if (CalendarUtils.areEqual(upperBound, getUpperBound())) - return; - this.upperBound = upperBound; - if (this.upperBound != null && !isSelectionEmpty()) { - long justAboveUpperBoundMs = this.upperBound.getTime() + 1; - removeSelectionInterval(new Date(justAboveUpperBoundMs), - getLastSelectionDate()); - } - fireValueChanged(EventType.UPPER_BOUND_CHANGED); - } - - /** - * {@inheritDoc} - */ - @Override - public Date getLowerBound() { - return lowerBound; - } - - /** - * {@inheritDoc} - */ - @Override - public void setLowerBound(Date lowerBound) { - if (lowerBound != null) { - lowerBound = getNormalizedDate(lowerBound); - } - if (CalendarUtils.areEqual(lowerBound, getLowerBound())) - return; - this.lowerBound = lowerBound; - if (this.lowerBound != null && !isSelectionEmpty()) { - // Remove anything below the lower bound - long justBelowLowerBoundMs = this.lowerBound.getTime() - 1; - removeSelectionInterval(getFirstSelectionDate(), new Date( - justBelowLowerBoundMs)); - } - fireValueChanged(EventType.LOWER_BOUND_CHANGED); - } - - - /** - * {@inheritDoc} - */ - @Override - public boolean isAdjusting() { - return adjusting; - } - - /** - * {@inheritDoc} - */ - @Override - public void setAdjusting(boolean adjusting) { - if (adjusting == isAdjusting()) return; - this.adjusting = adjusting; - fireValueChanged(adjusting ? EventType.ADJUSTING_STARTED : EventType.ADJUSTING_STOPPED); - - } - -//----------------- notification - /** - * {@inheritDoc} - */ - @Override - public void addDateSelectionListener(DateSelectionListener l) { - listenerMap.add(DateSelectionListener.class, l); - } - - /** - * {@inheritDoc} - */ - @Override - public void removeDateSelectionListener(DateSelectionListener l) { - listenerMap.remove(DateSelectionListener.class, l); - } - - public List getDateSelectionListeners() { - return listenerMap.getListeners(DateSelectionListener.class); - } - - protected void fireValueChanged(EventType eventType) { - List listeners = getDateSelectionListeners(); - DateSelectionEvent e = null; - - for (DateSelectionListener listener : listeners) { - if (e == null) { - e = new DateSelectionEvent(this, eventType, isAdjusting()); - } - listener.valueChanged(e); - } - } - - - -} diff --git a/src/main/java/org/jdesktop/swingx/calendar/CalendarUtils.java b/src/main/java/org/jdesktop/swingx/calendar/CalendarUtils.java deleted file mode 100644 index baf944f3b1..0000000000 --- a/src/main/java/org/jdesktop/swingx/calendar/CalendarUtils.java +++ /dev/null @@ -1,649 +0,0 @@ -/* - * $Id: CalendarUtils.java 3916 2011-01-12 10:21:58Z kleopatra $ - * - * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.calendar; - -import java.util.Calendar; -import java.util.Date; - -/** - * Calendar manipulation. - * - * PENDING: replace by something tested - as is c&p'ed dateUtils - * to work on a calendar instead of using long - * - * @author Jeanette Winzenburg - */ -public class CalendarUtils { - - // Constants used internally; unit is milliseconds - @SuppressWarnings("unused") - public static final int ONE_MINUTE = 60*1000; - @SuppressWarnings("unused") - public static final int ONE_HOUR = 60*ONE_MINUTE; - @SuppressWarnings("unused") - public static final int THREE_HOURS = 3 * ONE_HOUR; - @SuppressWarnings("unused") - public static final int ONE_DAY = 24*ONE_HOUR; - - public static final int DECADE = 5467; - public static final int YEAR_IN_DECADE = DECADE + 1; - - /** - * Increments the calendar field of the given calendar by amount. - * - * @param calendar - * @param field the field to increment, allowed are all fields known to - * Calendar plus DECADE. - * @param amount - * - * @throws IllegalArgumentException - */ - public static void add(Calendar calendar, int field, int amount) { - if (isNativeField(field)) { - calendar.add(field, amount); - } else { - switch (field) { - case DECADE: - calendar.add(Calendar.YEAR, amount * 10); - break; - default: - throw new IllegalArgumentException("unsupported field: " + field); - } - - } - } - - /** - * Gets the calendar field of the given calendar by amount. - * - * @param calendar - * @param field the field to get, allowed are all fields known to - * Calendar plus DECADE. - * - * @throws IllegalArgumentException - */ - public static int get(Calendar calendar, int field) { - if (isNativeField(field)) { - return calendar.get(field); - } - switch (field) { - case DECADE: - return decade(calendar.get(Calendar.YEAR)); - case YEAR_IN_DECADE: - return calendar.get(Calendar.YEAR) % 10; - default: - throw new IllegalArgumentException("unsupported field: " + field); - } - } - - /** - * Sets the calendar field of the given calendar by amount.

                    - * - * NOTE: the custom field implementations are very naive (JSR-310 will do better) - * - for decade: value must be positive, value must be a multiple of 10 and is interpreted as the - * first-year-of-the-decade - * - for year-in-decade: value is added/substracted to/from the start-of-decade of the - * date of the given calendar - * - * @param calendar - * @param field the field to increment, allowed are all fields known to - * Calendar plus DECADE. - * @param value the decade to set, must be a - * - * @throws IllegalArgumentException if the field is unsupported or the value is - * not dividable by 10 or negative. - */ - public static void set(Calendar calendar, int field, int value) { - if (isNativeField(field)) { - calendar.set(field, value); - } else { - switch (field) { - case DECADE: - if(value <= 0 ) { - throw new IllegalArgumentException("value must be a positive but was: " + value); - } - if (value % 10 != 0) { - throw new IllegalArgumentException("value must be a multiple of 10 but was: " + value); - } - int yearInDecade = get(calendar, YEAR_IN_DECADE); - calendar.set(Calendar.YEAR, value + yearInDecade); - break; - case YEAR_IN_DECADE: - int decade = get(calendar, DECADE); - calendar.set(Calendar.YEAR, value + decade); - break; - default: - throw new IllegalArgumentException("unsupported field: " + field); - } - - } - } - - /** - * @param calendarField - * @return - */ - private static boolean isNativeField(int calendarField) { - return calendarField < DECADE; - } - - /** - * Adjusts the Calendar to the end of the day of the last day in DST in the - * current year or unchanged if not using DST. Returns the calendar's date or null, if not - * using DST.

                    - * - * - * @param calendar the calendar to adjust - * @return the end of day of the last day in DST, or null if not using DST. - */ - public static Date getEndOfDST(Calendar calendar) { - if (!calendar.getTimeZone().useDaylightTime()) return null; - long old = calendar.getTimeInMillis(); - calendar.set(Calendar.MONTH, Calendar.DECEMBER); - endOfMonth(calendar); - startOfDay(calendar); - for (int i = 0; i < 366; i++) { - calendar.add(Calendar.DATE, -1); - if (calendar.getTimeZone().inDaylightTime(calendar.getTime())) { - endOfDay(calendar); - return calendar.getTime(); - } - } - calendar.setTimeInMillis(old); - return null; - } - - /** - * Adjusts the Calendar to the end of the day of the first day in DST in the - * current year or unchanged if not using DST. Returns the calendar's date or null, if not - * using DST.

                    - * - * Note: the start of the day of the first day in DST is ill-defined! - * - * @param calendar the calendar to adjust - * @return the start of day of the first day in DST, or null if not using DST. - */ - public static Date getStartOfDST(Calendar calendar) { - if (!calendar.getTimeZone().useDaylightTime()) return null; - long old = calendar.getTimeInMillis(); - calendar.set(Calendar.MONTH, Calendar.JANUARY); - startOfMonth(calendar); - endOfDay(calendar); - for (int i = 0; i < 366; i++) { - calendar.add(Calendar.DATE, 1); - if (calendar.getTimeZone().inDaylightTime(calendar.getTime())) { - endOfDay(calendar); - return calendar.getTime(); - } - } - calendar.setTimeInMillis(old); - return null; - } - - /** - * Returns a boolean indicating if the given calendar represents the - * start of a day (in the calendar's time zone). The calendar is unchanged. - * - * @param calendar the calendar to check. - * - * @return true if the calendar's time is the start of the day, - * false otherwise. - */ - public static boolean isStartOfDay(Calendar calendar) { - Calendar temp = (Calendar) calendar.clone(); - temp.add(Calendar.MILLISECOND, -1); - return temp.get(Calendar.DATE) != calendar.get(Calendar.DATE); - } - - /** - * Returns a boolean indicating if the given calendar represents the - * end of a day (in the calendar's time zone). The calendar is unchanged. - * - * @param calendar the calendar to check. - * - * @return true if the calendar's time is the end of the day, - * false otherwise. - */ - public static boolean isEndOfDay(Calendar calendar) { - Calendar temp = (Calendar) calendar.clone(); - temp.add(Calendar.MILLISECOND, 1); - return temp.get(Calendar.DATE) != calendar.get(Calendar.DATE); - } - - /** - * Returns a boolean indicating if the given calendar represents the - * start of a month (in the calendar's time zone). Returns true, if the time is - * the start of the first day of the month, false otherwise. The calendar is unchanged. - * - * @param calendar the calendar to check. - * - * @return true if the calendar's time is the start of the first day of the month, - * false otherwise. - */ - public static boolean isStartOfMonth(Calendar calendar) { - Calendar temp = (Calendar) calendar.clone(); - temp.add(Calendar.MILLISECOND, -1); - return temp.get(Calendar.MONTH) != calendar.get(Calendar.MONTH); - } - - /** - * Returns a boolean indicating if the given calendar represents the - * end of a month (in the calendar's time zone). Returns true, if the time is - * the end of the last day of the month, false otherwise. The calendar is unchanged. - * - * @param calendar the calendar to check. - * - * @return true if the calendar's time is the end of the last day of the month, - * false otherwise. - */ - public static boolean isEndOfMonth(Calendar calendar) { - Calendar temp = (Calendar) calendar.clone(); - temp.add(Calendar.MILLISECOND, 1); - return temp.get(Calendar.MONTH) != calendar.get(Calendar.MONTH); - } - - /** - * Returns a boolean indicating if the given calendar represents the - * start of a month (in the calendar's time zone). Returns true, if the time is - * the start of the first day of the month, false otherwise. The calendar is unchanged. - * - * @param calendar the calendar to check. - * - * @return true if the calendar's time is the start of the first day of the month, - * false otherwise. - */ - public static boolean isStartOfWeek(Calendar calendar) { - Calendar temp = (Calendar) calendar.clone(); - temp.add(Calendar.MILLISECOND, -1); - return temp.get(Calendar.WEEK_OF_YEAR) != calendar.get(Calendar.WEEK_OF_YEAR); - } - - /** - * Returns a boolean indicating if the given calendar represents the - * end of a week (in the calendar's time zone). Returns true, if the time is - * the end of the last day of the week, false otherwise. The calendar is unchanged. - * - * @param calendar the calendar to check. - * - * @return true if the calendar's time is the end of the last day of the week, - * false otherwise. - */ - public static boolean isEndOfWeek(Calendar calendar) { - Calendar temp = (Calendar) calendar.clone(); - temp.add(Calendar.MILLISECOND, 1); - return temp.get(Calendar.WEEK_OF_YEAR) != calendar.get(Calendar.WEEK_OF_YEAR); - } - - /** - * Adjusts the calendar to the start of the current week. - * That is, first day of the week with all time fields cleared. - * @param calendar the calendar to adjust. - * @return the Date the calendar is set to - */ - public static void startOfWeek(Calendar calendar) { - calendar.set(Calendar.DAY_OF_WEEK, calendar.getFirstDayOfWeek()); - startOfDay(calendar); - } - - /** - * Adjusts the calendar to the end of the current week. - * That is, last day of the week with all time fields at max. - * @param calendar the calendar to adjust. - */ - public static void endOfWeek(Calendar calendar) { - startOfWeek(calendar); - calendar.add(Calendar.DATE, 7); - calendar.add(Calendar.MILLISECOND, -1); - } - - /** - * Adjusts the calendar to the end of the current week. - * That is, last day of the week with all time fields at max. - * The Date of the adjusted Calendar is - * returned. - * - * @param calendar calendar to adjust. - * @param date the Date to use. - * @return the end of the week of the given date - */ - public static Date endOfWeek(Calendar calendar, Date date) { - calendar.setTime(date); - endOfWeek(calendar); - return calendar.getTime(); - } - - /** - * Adjusts the calendar to the start of the current week. - * That is, last day of the week with all time fields at max. - * The Date of the adjusted Calendar is - * returned. - * - * @param calendar calendar to adjust. - * @param date the Date to use. - * @return the start of the week of the given date - */ - public static Date startOfWeek(Calendar calendar, Date date) { - calendar.setTime(date); - startOfWeek(calendar); - return calendar.getTime(); - } - - /** - * Adjusts the given Calendar to the start of the decade. - * - * @param calendar the calendar to adjust. - */ - public static void startOfDecade(Calendar calendar) { - calendar.set(Calendar.YEAR, decade(calendar.get(Calendar.YEAR)) ); - startOfYear(calendar); - } - - /** - * @param year - * @return - */ - private static int decade(int year) { - return (year / 10) * 10; - } - - /** - * Adjusts the given Calendar to the start of the decade as defined by - * the given date. Returns the calendar's Date. - * - * @param calendar calendar to adjust. - * @param date the Date to use. - * @return the start of the decade of the given date - */ - public static Date startOfDecade(Calendar calendar, Date date) { - calendar.setTime(date); - startOfDecade(calendar); - return calendar.getTime(); - } - - /** - * Returns a boolean indicating if the given calendar represents the - * start of a decade (in the calendar's time zone). Returns true, if the time is - * the start of the first day of the decade, false otherwise. The calendar is unchanged. - * - * @param calendar the calendar to check. - * - * @return true if the calendar's time is the start of the first day of the month, - * false otherwise. - */ - public static boolean isStartOfDecade(Calendar calendar) { - Calendar temp = (Calendar) calendar.clone(); - temp.add(Calendar.MILLISECOND, -1); - return decade(temp.get(Calendar.YEAR)) != decade(calendar.get(Calendar.YEAR)); - } - - /** - * Adjusts the given Calendar to the start of the year. - * - * @param calendar the calendar to adjust. - */ - public static void startOfYear(Calendar calendar) { - calendar.set(Calendar.MONTH, Calendar.JANUARY); - startOfMonth(calendar); - } - - /** - * Adjusts the given Calendar to the start of the year as defined by - * the given date. Returns the calendar's Date. - * - * @param calendar calendar to adjust. - * @param date the Date to use. - * @return the start of the year of the given date - */ - public static Date startOfYear(Calendar calendar, Date date) { - calendar.setTime(date); - startOfYear(calendar); - return calendar.getTime(); - } - - /** - * Returns a boolean indicating if the given calendar represents the - * start of a year (in the calendar's time zone). Returns true, if the time is - * the start of the first day of the year, false otherwise. The calendar is unchanged. - * - * @param calendar the calendar to check. - * - * @return true if the calendar's time is the start of the first day of the month, - * false otherwise. - */ - public static boolean isStartOfYear(Calendar calendar) { - Calendar temp = (Calendar) calendar.clone(); - temp.add(Calendar.MILLISECOND, -1); - return temp.get(Calendar.YEAR) != calendar.get(Calendar.YEAR); - } - - /** - * Adjusts the calendar to the start of the current month. - * That is, first day of the month with all time fields cleared. - * - * @param calendar calendar to adjust. - */ - public static void startOfMonth(Calendar calendar) { - calendar.set(Calendar.DAY_OF_MONTH, 1); - startOfDay(calendar); - } - - /** - * Adjusts the calendar to the end of the current month. - * That is the last day of the month with all time-fields - * at max. - * - * @param calendar calendar to adjust. - */ - public static void endOfMonth(Calendar calendar) { - // start of next month - calendar.add(Calendar.MONTH, 1); - startOfMonth(calendar); - // one millisecond back - calendar.add(Calendar.MILLISECOND, -1); - } - - /** - * Adjust the given calendar to the first millisecond of the given date. - * that is all time fields cleared. The Date of the adjusted Calendar is - * returned. - * - * @param calendar calendar to adjust. - * @param date the Date to use. - * @return the start of the day of the given date - */ - public static Date startOfDay(Calendar calendar, Date date) { - calendar.setTime(date); - startOfDay(calendar); - return calendar.getTime(); - } - - /** - * Adjust the given calendar to the last millisecond of the given date. - * that is all time fields cleared. The Date of the adjusted Calendar is - * returned. - * - * @param calendar calendar to adjust. - * @param date the Date to use. - * @return the end of the day of the given date - */ - public static Date endOfDay(Calendar calendar, Date date) { - calendar.setTime(date); - endOfDay(calendar); - return calendar.getTime(); - } - - /** - * Adjust the given calendar to the first millisecond of the current day. - * that is all time fields cleared. - * - * @param calendar calendar to adjust. - */ - public static void startOfDay(Calendar calendar) { - calendar.set(Calendar.HOUR_OF_DAY, 0); - calendar.set(Calendar.MILLISECOND, 0); - calendar.set(Calendar.SECOND, 0); - calendar.set(Calendar.MINUTE, 0); - calendar.getTimeInMillis(); - } - - /** - * Adjust the given calendar to the last millisecond of the specified date. - * - * @param calendar calendar to adjust. - */ - public static void endOfDay(Calendar calendar) { - calendar.add(Calendar.DATE, 1); - startOfDay(calendar); - calendar.add(Calendar.MILLISECOND, -1); - } - - /** - * Adjusts the given calendar to the start of the period as indicated by the - * given field. This delegates to startOfDay, -Week, -Month, -Year as appropriate. - * - * @param calendar - * @param field the period to adjust, allowed are Calendar.DAY_OF_MONTH, -.MONTH, - * -.WEEK and YEAR and CalendarUtils.DECADE. - */ - public static void startOf(Calendar calendar, int field) { - switch (field) { - case Calendar.DAY_OF_MONTH: - startOfDay(calendar); - break; - case Calendar.MONTH: - startOfMonth(calendar); - break; - case Calendar.WEEK_OF_YEAR: - startOfWeek(calendar); - break; - case Calendar.YEAR: - startOfYear(calendar); - break; - case DECADE: - startOfDecade(calendar); - break; - default: - throw new IllegalArgumentException("unsupported field: " + field); - - } - } - - /** - * Returns a boolean indicating if the calendar is set to the start of a - * period as defined by the - * given field. This delegates to startOfDay, -Week, -Month, -Year as appropriate. - * The calendar is unchanged. - * - * @param calendar - * @param field the period to adjust, allowed are Calendar.DAY_OF_MONTH, -.MONTH, - * -.WEEK and YEAR and CalendarUtils.DECADE. - * @throws IllegalArgumentException if the field is not supported. - */ - public static boolean isStartOf(Calendar calendar, int field) { - switch (field) { - case Calendar.DAY_OF_MONTH: - return isStartOfDay(calendar); - case Calendar.MONTH: - return isStartOfMonth(calendar); - case Calendar.WEEK_OF_YEAR: - return isStartOfWeek(calendar); - case Calendar.YEAR: - return isStartOfYear(calendar); - case DECADE: - return isStartOfDecade(calendar); - default: - throw new IllegalArgumentException("unsupported field: " + field); - } - } - - /** - * Checks the given dates for being equal. - * - * @param current one of the dates to compare - * @param date the otherr of the dates to compare - * @return true if the two given dates both are null or both are not null and equal, - * false otherwise. - */ - public static boolean areEqual(Date current, Date date) { - if ((date == null) && (current == null)) { - return true; - } - if (date != null) { - return date.equals(current); - } - return false; - } - - /** - * Returns a boolean indicating whether the given Date is the same day as - * the day in the calendar. Calendar and date are unchanged by the check. - * - * @param today the Calendar representing a date, must not be null. - * @param now the date to compare to, must not be null - * @return true if the calendar and date represent the same day in the - * given calendar. - */ - public static boolean isSameDay(Calendar today, Date now) { - Calendar temp = (Calendar) today.clone(); - startOfDay(temp); - Date start = temp.getTime(); - temp.setTime(now); - startOfDay(temp); - return start.equals(temp.getTime()); - } - - /** - * Returns a boolean indicating whether the given Date is in the same period as - * the Date in the calendar, as defined by the calendar field. - * Calendar and date are unchanged by the check. - * - * @param today the Calendar representing a date, must not be null. - * @param now the date to compare to, must not be null - * @return true if the calendar and date represent the same day in the - * given calendar. - */ - public static boolean isSame(Calendar today, Date now, int field) { - Calendar temp = (Calendar) today.clone(); - startOf(temp, field); - Date start = temp.getTime(); - temp.setTime(now); - startOf(temp, field); - return start.equals(temp.getTime()); - } - - /** - * Returns a boolean to indicate whether the given calendar is flushed.

                    - * - * The only way to guarantee a flushed state is to let client code call - * getTime or getTimeInMillis. See - * - * Despairing - * in Calendar - *

                    - * Note: this if for testing only and not entirely safe! - * - * @param calendar - * @return - */ - public static boolean isFlushed(Calendar calendar) { - return !calendar.toString().contains("time=?"); - } -} diff --git a/src/main/java/org/jdesktop/swingx/calendar/DatePickerFormatter.java b/src/main/java/org/jdesktop/swingx/calendar/DatePickerFormatter.java deleted file mode 100644 index 7991b1e683..0000000000 --- a/src/main/java/org/jdesktop/swingx/calendar/DatePickerFormatter.java +++ /dev/null @@ -1,248 +0,0 @@ -/* - * $Id: DatePickerFormatter.java 3140 2008-12-16 15:09:09Z kleopatra $ - * - * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.calendar; - -import org.jdesktop.swingx.plaf.UIManagerExt; -import org.jdesktop.swingx.util.Contract; - -import javax.swing.*; -import javax.swing.plaf.UIResource; -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.logging.Logger; - -/** - * Default formatter for the JXDatePicker component. - * It can handle a variety of date formats. - * - * @author Joshua Outwater - */ -public class DatePickerFormatter extends - JFormattedTextField.AbstractFormatter { - - private static final Logger LOG = Logger - .getLogger(DatePickerFormatter.class.getName()); - private DateFormat _formats[] = null; - - - /** - * Instantiates a formatter with the localized format patterns defined - * in the swingx.properties. - * - * These formats are localizable and fields may be re-arranged, such as - * swapping the month and day fields. The keys for localizing these fields - * are: - *

                      - *
                    • JXDatePicker.longFormat - *
                    • JXDatePicker.mediumFormat - *
                    • JXDatePicker.shortFormat - *
                    - * - */ - public DatePickerFormatter() { - this(null, null); - } - - /** - * Instantiates a formatter with the given date formats. If the - * array is null, default formats are created from the localized - * patterns in swingx.properties. If empty? - * - * @param formats the array of formats to use. May be null to - * use defaults or empty to do nothing (?), but must not contain - * null formats. - */ - public DatePickerFormatter(DateFormat formats[]) { - this(formats, null); - } - - /** - * Instantiates a formatter with default date formats in the - * given locale. The default formats are created from the localized - * patterns in swingx.properties. - * - * @param locale the Locale the use for the default formats. - */ - public DatePickerFormatter(Locale locale) { - this(null, locale); - } - - /** - * Instantiates a formatter with the given formats and locale. - * - * PENDING JW: makes no sense as a public constructor because the locale is ignored - * if the formats are null. So has same public behaviour as the constructor with - * formats only ... - * - * @param formats - * @param locale - */ - public DatePickerFormatter(DateFormat formats[], Locale locale) { -// super(); - if (locale == null) { - locale = Locale.getDefault(); - } - if (formats == null) { - formats = createDefaultFormats(locale); - } - Contract.asNotNull(formats, "The array of DateFormats must not contain null formats"); - _formats = formats; - } - - /** - * Returns an array of the formats used by this formatter. - * - * @return the formats used by this formatter, guaranteed to be - * not null. - */ - public DateFormat[] getFormats() { - DateFormat[] results = new DateFormat[_formats.length]; - System.arraycopy(_formats, 0, results, 0, results.length); - return results; - } - - /** - * {@inheritDoc} - */ - @Override - public Object stringToValue(String text) throws ParseException { - Object result = null; - ParseException pex = null; - - if (text == null || text.trim().length() == 0) { - return null; - } - - // If the current formatter did not work loop through the other - // formatters and see if any of them can parse the string passed - // in. - for (DateFormat _format : _formats) { - try { - result = (_format).parse(text); - pex = null; - break; - } catch (ParseException ex) { - pex = ex; - } - } - - if (pex != null) { - throw pex; - } - - return result; - } - - /** - * {@inheritDoc} - */ - @Override - public String valueToString(Object value) throws ParseException { - if ((value != null) && (_formats.length > 0)){ - return _formats[0].format(value); - } - return null; - } - - /** - * Creates and returns the localized default formats. First tries to - * add formats created using the patterns stored in the UIManager. If - * there are no patterns, use the DateFormat's instance with style - * DateFormat.SHORT. - * - * @return the localized default formats. - */ - protected DateFormat[] createDefaultFormats(Locale locale) { - List f = new ArrayList(); - addFormat(f, "JXDatePicker.longFormat", locale); - addFormat(f, "JXDatePicker.mediumFormat", locale); - addFormat(f, "JXDatePicker.shortFormat", locale); - if (f.size() == 0) { - addSystemDefaultFormat(f, locale); - } - return f.toArray(new DateFormat[f.size()]); - } - - /** - * Adds the system's default DateFormat. This implementation adds a - * dateInstance of style DateFormat.SHORT. - * - * @param f the List of formats to add to - * @param locale the Locale to use for the formatter. - */ - private void addSystemDefaultFormat(List f, Locale locale) { - f.add(DateFormat.getDateInstance(DateFormat.SHORT, locale)); - } - - /** - * Creates and adds a DateFormat to the given list. Looks up - * a format pattern registered in the UIManager for the given - * key and tries to create a SimpleDateFormat. Does nothing - * if there is no format pattern registered or the pattern is - * invalid. - * - * @param f the list of formats - * @param key the key for getting the pattern from the UI - */ - private void addFormat(List f, String key, Locale locale) { - String pattern = UIManagerExt.getString(key, locale); - if (pattern == null) return; - try { - SimpleDateFormat format = new SimpleDateFormat(pattern, locale); - f.add(format); - } catch (RuntimeException e) { - // format string not available or invalid - LOG.finer("creating date format failed for key/pattern: " + key + "/" + pattern); - } - } - - /** - * - * Same as DatePickerFormatter, but tagged as UIResource. - * - * @author Jeanette Winzenburg - */ - public static class DatePickerFormatterUIResource extends DatePickerFormatter - implements UIResource { - - /** - * @param locale - */ - public DatePickerFormatterUIResource(Locale locale) { - super(locale); - } - - /** - * - */ - public DatePickerFormatterUIResource() { - this(null); - } - - public DatePickerFormatterUIResource(DateFormat[] formats, Locale locale) { - super(formats, locale); - } - } -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/calendar/DateSelectionModel.java b/src/main/java/org/jdesktop/swingx/calendar/DateSelectionModel.java deleted file mode 100644 index 82d122ea60..0000000000 --- a/src/main/java/org/jdesktop/swingx/calendar/DateSelectionModel.java +++ /dev/null @@ -1,374 +0,0 @@ -/* - * $Id: DateSelectionModel.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.calendar; - -import org.jdesktop.swingx.event.DateSelectionListener; - -import java.util.*; - - -/** - * The Model used by calendar components. It controls the Calendar to use and - * keeps selection-related state. - * - * @author Joshua Outwater - */ -public interface DateSelectionModel { - public static enum SelectionMode { - /** - * Mode that allows for selection of a single day. - */ - SINGLE_SELECTION, - /** - * Mode that allows for selecting of multiple consecutive days. - */ - SINGLE_INTERVAL_SELECTION, - /** - * Mode that allows for selecting disjoint days. - */ - MULTIPLE_INTERVAL_SELECTION - } - -//---------------------- mode - /** - * Get the selection mode. - * - * @return return the current selection mode - */ - public SelectionMode getSelectionMode(); - - /** - * Set the selection mode. - * - * @param mode new selection mode - */ - public void setSelectionMode(final SelectionMode mode); - - -//-------------------- calendar - /** - * Returns a clone of the calendar used by this model. It's date is unspecified. - * - * @return a clone of the calendar used by this model. - */ - public Calendar getCalendar(); - - /** - * Gets what the first day of the week is; e.g., - * Calendar.SUNDAY in the U.S., Calendar.MONDAY - * in France. This is needed when the model selection mode is - * WEEK_INTERVAL_SELECTION. - * - * PENDING JW: move week-interval selection from JXMonthView into the model. - * - * @return int The first day of the week. - * @see #setFirstDayOfWeek(int) - */ - public int getFirstDayOfWeek(); - - /** - * Sets what the first day of the week is. E.g., - * Calendar.SUNDAY in US, Calendar.MONDAY - * in France. Fires a DateSelectionEvent of type CALENDAR_CHANGED, if the - * value is different from the old.

                    - * - * The default value depends on the Calendar's default. - * - * PENDING JW: actually, it's a bound property. Use a propertyChangeListener? - * - * @param firstDayOfWeek The first day of the week. - * @see #getFirstDayOfWeek() - * @see Calendar - */ - public void setFirstDayOfWeek(final int firstDayOfWeek); - - - /** - * Gets the minimal number of days in the first week of the year. - * - * @return int the minimal number of days in the first week of the year. - */ - public int getMinimalDaysInFirstWeek(); - - /** - * Sets the minimal number of days in the first week of the year. - * Fires a DateSelectionEvent of type CALENDAR_CHANGED, if the - * value is different from the old. - * - * The default value depends on the Calendar's default. - * - * PENDING JW: actually, it's a bound property. Use a propertyChangeListener? - * - * @param minimalDays the minimal number of days in the first week of the year. - * @see #getMinimalDaysInFirstWeek() - * @see Calendar - */ - public void setMinimalDaysInFirstWeek(final int minimalDays); - - /** - * Returns the TimeZone of this model. - * - * @return the TimeZone of this model. - * @see #setTimeZone(TimeZone) - */ - public TimeZone getTimeZone(); - - - /** - * Sets the TimeZone of this model. Fires a DateSelectionEvent of type - * CALENDAR_CHANGED if the new value is different from the old. - * - * The default value depends on the Calendar's default. - * - * PENDING JW: actually, it's a bound property. Use a propertyChangeListener? - * - * @param timeZone the TimeZone to use in this model, must not be null. - * @see #getTimeZone() - */ - public void setTimeZone(TimeZone timeZone); - - /** - * Returns the Locale of this model's calendar. - * @return the Locale of this model's calendar. - */ - public Locale getLocale(); - - /** - * Sets the Locale of this model's calendar. Fires a DateSelectionEvent of type - * CALENDAR_CHANGED if the new value is different from the old.

                    - * - * The default value is Locale.default().

                    - * - * PENDING JW: fall back to JComponent.getDefaultLocale instead? We use this - * with components anyway?

                    - * PENDING JW: actually, it's a bound property. Use a propertyChangeListener? - * - * @param locale the Locale to use. If null, the default Locale is used. - */ - public void setLocale(Locale locale); - - //-------------------- selection - - /** - * Adds the specified selection interval to the selection model. - * - * @param startDate interval start date, must not be null - * @param endDate interval end date >= start date, must not be null - * @throws NullPointerException if any of the dates is null - */ - public void addSelectionInterval(Date startDate, Date endDate); - - /** - * Sest the specified selection interval to the selection model. - * - * @param startDate interval start date, must not be null - * @param endDate interval end date >= start date, must not be null - * @throws NullPointerException if any of the dates is null - */ - public void setSelectionInterval(Date startDate, Date endDate); - - /** - * Removes the specifed selection interval from the selection model. If - * the selection is changed by this method, it fires a DateSelectionEvent - * of type DATES_REMOVED. - * - * @param startDate interval start date, must not be null - * @param endDate interval end date >= start date, must not be null - * @throws NullPointerException if any of the dates is null - */ - public void removeSelectionInterval(Date startDate, Date endDate); - - /** - * Clears any selection from the selection model. Fires an Event of - * type SELECTION_CLEARED if there had been a selection, does nothing - * otherwise. - */ - public void clearSelection(); - - /** - * Returns the current selection. - * - * @return sorted set of selected dates, guaranteed to be never null. - */ - public SortedSet getSelection(); - - /** - * Returns the earliest date in the selection or null if the selection is empty. - * - * @return the earliest date in the selection, or null if isSelectionEmpty. - * - * @see #getLastSelectionDate() - * @see #getSelection() - * @see #isSelectionEmpty() - */ - public Date getFirstSelectionDate(); - - /** - * Returns the latest date in the selection or null if the selection is empty. - * - * @return the lastest date in the selection, or null if isSelectionEmpty. - * - * @see #getFirstSelectionDate() - * @see #getSelection() - * @see #isSelectionEmpty() - */ - public Date getLastSelectionDate(); - - - /** - * Returns true if the date specified is selected, false otherwise.

                    - * - * Note: it is up to implementations to define the exact notion of selected. - * It does not imply the exact date as given is contained the set returned from - * getSelection(). - * - * @param date date to check for selection, must not be null - * @return true if the date is selected, false otherwise - * @throws NullPointerException if the date is null - */ - public boolean isSelected(final Date date); - - /** - * Returns a normalized Date as used by the implementation, if any. F.i. - * DaySelectionModel returns the start of the day in the model's calendar. - * If no normalization is applied, a clone of the Date itself is returned. - * The given Date is never changed. - *

                    - * - * The overall contract: - * - *

                    
                    -     * if ((date != null) && isSelectable(date)) {
                    -     *     setSelectionInterval(date, date);
                    -     *     assertEquals(getNormalized(date), getFirstSelectionDate();
                    -     * }
                    -     * 
                    - * - * - * @return the date as it would be normalized before used in the model, - * must not be null. - * @throws NullPointerException if given date is null. - */ - public Date getNormalizedDate(Date date); - - /** - * Returns true if the selection is empty, false otherwise. - * - * @return true if the selection is empty, false otherwise - */ - public boolean isSelectionEmpty(); - - - /** - * Returns a SortedSet of Dates that are unselectable. - * - * @return sorted set of dates - */ - public SortedSet getUnselectableDates(); - - /** - * Sets a collection of dates which are not selectable.

                    - * - * Note: it is up to implementations to define the exact notion of unselectableDate. - * It does not imply the only the exact date as given is unselectable, it might - * have a period like "all dates on the same day". - * - * PENDING JW: any collection would do - why insist on a SortedSet? - * - * @param unselectableDates dates that are unselectable, must not be null and - * must not contain null dates. - */ - public void setUnselectableDates(SortedSet unselectableDates); - - /** - * Returns true is the specified date is unselectable. - * - * @param unselectableDate the date to check for unselectability, must not be null. - * @return true is the date is unselectable, false otherwise - */ - public boolean isUnselectableDate(Date unselectableDate); - - /** - * Return the upper bound date that is allowed to be selected for this - * model. - * - * @return upper bound date or null if not set - */ - public Date getUpperBound(); - - /** - * Set the upper bound date that is allowed to be selected for this model. - * - * @param upperBound upper bound - */ - public void setUpperBound(final Date upperBound); - - /** - * Return the lower bound date that is allowed to be selected for this - * model. - * - * @return lower bound date or null if not set - */ - public Date getLowerBound(); - - /** - * Set the lower bound date that is allowed to be selected for this model. - * - * @param lowerBound lower bound date or null if not set - */ - public void setLowerBound(final Date lowerBound); - - /** - * Set the property to mark upcoming selections as intermediate/ - * final. This will fire a event of type adjusting_start/stop. - * - * The default value is false. - * - * Note: Client code marking as intermediate must take care of - * finalizing again. - * - * @param adjusting a flag to turn the adjusting property on/off. - */ - public void setAdjusting(boolean adjusting); - - /** - * Returns the property to decide whether the selection is - * intermediate or final. - * - * @return the adjusting property. - */ - public boolean isAdjusting(); - - /** - * Add the specified listener to this model. - * - * @param listener listener to add to this model - */ - public void addDateSelectionListener(DateSelectionListener listener); - - /** - * Remove the specified listener to this model. - * - * @param listener listener to remove from this model - */ - public void removeDateSelectionListener(DateSelectionListener listener); - -} diff --git a/src/main/java/org/jdesktop/swingx/calendar/DateSpan.java b/src/main/java/org/jdesktop/swingx/calendar/DateSpan.java deleted file mode 100644 index 938afdfa00..0000000000 --- a/src/main/java/org/jdesktop/swingx/calendar/DateSpan.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * $Id: DateSpan.java 542 2005-10-10 18:03:15Z rbair $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.calendar; - -import java.util.Date; - -/** - * An immutable representation of a time range. The time range is - * internally represented as two longs. The methods that take and return - * Dates create the Dates as needed, so that - * if you modify returned Dates you will not effect - * the DateSpan. The end points are inclusive. - * - * @version $Revision: 542 $ - */ -public class DateSpan { - private long _start; - private long _end; - - /** - * Creates a DateSpan between the two end points. - * - * @param start Beginning date - * @param end Ending date - * @throws IllegalArgumentException if start is after - * end - */ - public DateSpan(long start, long end) { - _start = start; - _end = end; - if (_start > _end) { - throw new IllegalArgumentException( - "Start date must be before end date"); - } - } - - /** - * Creates a DateSpan between the two end points. This - * is a conveniance constructor that is equivalent to - * new Date(start.getTime(), end.getTime());. - * - * @param start Beginning date - * @param end Ending date - */ - public DateSpan(Date start, Date end) { - this(start.getTime(), end.getTime()); - } - - /** - * Returns the start of the date span. - * - * @return start of the span. - */ - public long getStart() { - return _start; - } - - /** - * Returns the end of the date span. - * - * @return end of the span. - */ - public long getEnd() { - return _end; - } - - /** - * Returns the start of the date span as a Date. - * - * @return start of the span. - */ - public Date getStartAsDate() { - return new Date(getStart()); - } - - /** - * Returns the end of the date span as a Date. - * - * @return end of the span. - */ - public Date getEndAsDate() { - return new Date(getEnd()); - } - - /** - * Returns true if this DateSpan contains the specified - * DateSpan. - * - * @param span Date to check - * @return true if this DateSpan contains span. - */ - public boolean contains(DateSpan span) { - return (contains(span.getStart()) && contains(span.getEnd())); - } - - /** - * Returns whether or not this DateSpan contains the specified - * time. - * - * @param time time check - * @return true if this DateSpan contains time. - */ - public boolean contains(long time) { - return (time >= getStart() && time <= getEnd()); - } - - /** - * Returns whether or not this DateSpan contains the - * specified date span. - * - * @param start Start of time span - * @param end End of time - * @return true if this DateSpan contains the specified - * date span. - */ - public boolean contains(long start, long end) { - return (start >= getStart() && end <= getEnd()); - } - - /** - * Returns true if the this DateSpan intersects with the - * specified time. - * - * @param start Start time - * @param end End time - * @return true if this DateSpan intersects with the specified - * time. - */ - public boolean intersects(long start, long end) { - return (start <= getEnd() && end >= getStart()); - } - - /** - * Returns true if the this DateSpan intersects with the - * specified DateSpan. - * - * @param span DateSpan to compare to - * @return true if this DateSpan intersects with the specified - * time. - */ - public boolean intersects(DateSpan span) { - return intersects(span.getStart(), span.getEnd()); - } - - /** - * Returns a new DateSpan that is the union of this - * DateSpan and span. - * - * @param span DateSpan to add - * @return union of this DateSpan and span - */ - public DateSpan add(DateSpan span) { - return add(span.getStart(), span.getEnd()); - } - - /** - * Returns a new DateSpan that is the union of this - * DateSpan and the passed in span. - * - * @param start Start of region to add - * @param end End of region to end - * @return union of this DateSpan and start, end - */ - public DateSpan add(long start, long end) { - return new DateSpan(Math.min(start, getStart()), - Math.max(end, getEnd())); - } - - - /** - * {@inheritDoc} - */ - @Override - public boolean equals(Object o) { - if (o == this) { - return true; - } - if (o instanceof DateSpan) { - DateSpan ds = (DateSpan)o; - return (_start == ds.getStart() && _end == ds.getEnd()); - } - return false; - } - - /** - * {@inheritDoc} - */ - @Override - public int hashCode() { - int result = 17; - result = 37 * result + (int)(_start ^ (_start >>> 32)); - result = 37 * result + (int)(_end ^ (_end >>> 32)); - return result; - } - - /** - * {@inheritDoc} - */ - @Override - public String toString() { - return "DateSpan [" + getStartAsDate() + "-" + getEndAsDate() + "]"; - } -} diff --git a/src/main/java/org/jdesktop/swingx/calendar/DateUtils.java b/src/main/java/org/jdesktop/swingx/calendar/DateUtils.java deleted file mode 100644 index 61b36f37e9..0000000000 --- a/src/main/java/org/jdesktop/swingx/calendar/DateUtils.java +++ /dev/null @@ -1,405 +0,0 @@ -/* - * $Id: DateUtils.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.calendar; - -import java.util.Calendar; -import java.util.Date; - -/** - * Utility methods for Date manipulation.

                    - * - * This utility class is replaced by CalendarUtils because day related manipulation - * are meaningfull relative to a Calendar only. Always doing so against the default - * calendar instance isn't enough. - * - * PENDING JW: move the missing ops. Volunteers, please! Once done, this will be deprecated. - * - * @author Scott Violet - * @version $Revision: 3100 $ - * - */ -public class DateUtils { - private static Calendar CALENDAR = Calendar.getInstance(); - - /** - * Returns the last millisecond of the specified date. - * - * @param date Date to calculate end of day from - * @return Last millisecond of date - */ - public static Date endOfDay(Date date) { - Calendar calendar = CALENDAR; - synchronized(calendar) { - calendar.setTime(date); - calendar.set(Calendar.HOUR_OF_DAY, 23); - calendar.set(Calendar.MILLISECOND, 999); - calendar.set(Calendar.SECOND, 59); - calendar.set(Calendar.MINUTE, 59); - return calendar.getTime(); - } - } - - - /** - * Returns a new Date with the hours, milliseconds, seconds and minutes - * set to 0. - * - * @param date Date used in calculating start of day - * @return Start of date - */ - public static Date startOfDay(Date date) { - Calendar calendar = CALENDAR; - synchronized(calendar) { - calendar.setTime(date); - calendar.set(Calendar.HOUR_OF_DAY, 0); - calendar.set(Calendar.MILLISECOND, 0); - calendar.set(Calendar.SECOND, 0); - calendar.set(Calendar.MINUTE, 0); - return calendar.getTime(); - } - } - - /** - * Returns day in millis with the hours, milliseconds, seconds and minutes - * set to 0. - * - * @param date long used in calculating start of day - * @return Start of date - */ - public static long startOfDayInMillis(long date) { - Calendar calendar = CALENDAR; - synchronized(calendar) { - calendar.setTimeInMillis(date); - calendar.set(Calendar.HOUR_OF_DAY, 0); - calendar.set(Calendar.MILLISECOND, 0); - calendar.set(Calendar.SECOND, 0); - calendar.set(Calendar.MINUTE, 0); - return calendar.getTimeInMillis(); - } - } - - /** - * Returns the last millisecond of the specified date. - * - * @param date long to calculate end of day from - * @return Last millisecond of date - */ - public static long endOfDayInMillis(long date) { - Calendar calendar = CALENDAR; - synchronized(calendar) { - calendar.setTimeInMillis(date); - calendar.set(Calendar.HOUR_OF_DAY, 23); - calendar.set(Calendar.MILLISECOND, 999); - calendar.set(Calendar.SECOND, 59); - calendar.set(Calendar.MINUTE, 59); - return calendar.getTimeInMillis(); - } - } - - - /** - * Returns the day after date. - * - * @param date Date used in calculating next day - * @return Day after date. - */ - public static Date nextDay(Date date) { - return new Date(addDays(date.getTime(), 1)); - } - - /** - * Adds amount days to time and returns - * the resulting time. - * - * @param time Base time - * @param amount Amount of increment. - * - * @return the time + amount days - */ - public static long addDays(long time, int amount) { - Calendar calendar = CALENDAR; - synchronized(calendar) { - calendar.setTimeInMillis(time); - calendar.add(Calendar.DAY_OF_MONTH, amount); - return calendar.getTimeInMillis(); - } - } - - /** - * Returns the day after date. - * - * @param date Date used in calculating next day - * @return Day after date. - */ - public static long nextDay(long date) { - return addDays(date, 1); - } - - /** - * Returns the week after date. - * - * @param date Date used in calculating next week - * @return week after date. - */ - public static long nextWeek(long date) { - return addDays(date, 7); - } - - - /** - * Returns the number of days difference between t1 and - * t2. - * - * @param t1 Time 1 - * @param t2 Time 2 - * @param checkOverflow indicates whether to check for overflow - * @return Number of days between start and end - */ - public static int getDaysDiff(long t1, long t2, boolean checkOverflow) { - if (t1 > t2) { - long tmp = t1; - t1 = t2; - t2 = tmp; - } - Calendar calendar = CALENDAR; - synchronized(calendar) { - calendar.setTimeInMillis(t1); - int delta = 0; - while (calendar.getTimeInMillis() < t2) { - calendar.add(Calendar.DAY_OF_MONTH, 1); - delta++; - } - if (checkOverflow && (calendar.getTimeInMillis() > t2)) { - delta--; - } - return delta; - } - } - - /** - * Returns the number of days difference between t1 and - * t2. - * - * @param t1 Time 1 - * @param t2 Time 2 - * @return Number of days between start and end - */ - public static int getDaysDiff(long t1, long t2) { - return getDaysDiff(t1, t2, true); - } - - /** - * Check, whether the date passed in is the first day of the year. - * - * @param date date to check in millis - * @return true if date corresponds to the first - * day of a year - * @see Date#getTime() - */ - public static boolean isFirstOfYear(long date) { - boolean ret = false; - Calendar calendar = CALENDAR; - synchronized(calendar) { - calendar.setTimeInMillis(date); - int currentYear = calendar.get(Calendar.YEAR); - // Check yesterday - calendar.add(Calendar.DATE,-1); - int yesterdayYear = calendar.get(Calendar.YEAR); - ret = (currentYear != yesterdayYear); - } - return ret; - } - - /** - * Check, whether the date passed in is the first day of the month. - * - * @param date date to check in millis - * @return true if date corresponds to the first - * day of a month - * @see Date#getTime() - */ - public static boolean isFirstOfMonth(long date) { - boolean ret = false; - Calendar calendar = CALENDAR; - synchronized(calendar) { - calendar.setTimeInMillis(date); - int currentMonth = calendar.get(Calendar.MONTH); - // Check yesterday - calendar.add(Calendar.DATE,-1); - int yesterdayMonth = calendar.get(Calendar.MONTH); - ret = (currentMonth != yesterdayMonth); - } - return ret; - } - - - /** - * Returns the day before date. - * - * @param date Date used in calculating previous day - * @return Day before date. - */ - public static long previousDay(long date) { - return addDays(date, -1); - } - - /** - * Returns the week before date. - * - * @param date Date used in calculating previous week - * @return week before date. - */ - public static long previousWeek(long date) { - return addDays(date, -7); - } - - - /** - * Returns the first day before date that has the - * day of week matching startOfWeek. For example, if you - * want to find the previous monday relative to date you - * would call getPreviousDay(date, Calendar.MONDAY). - * - * @param date Base date - * @param startOfWeek Calendar constant correspoding to start of week. - * @return start of week, return value will have 0 hours, 0 minutes, - * 0 seconds and 0 ms. - * - */ - public static long getPreviousDay(long date, int startOfWeek) { - return getDay(date, startOfWeek, -1); - } - - /** - * Returns the first day after date that has the - * day of week matching startOfWeek. For example, if you - * want to find the next monday relative to date you - * would call getPreviousDay(date, Calendar.MONDAY). - * - * @param date Base date - * @param startOfWeek Calendar constant correspoding to start of week. - * @return start of week, return value will have 0 hours, 0 minutes, - * 0 seconds and 0 ms. - * - */ - public static long getNextDay(long date, int startOfWeek) { - return getDay(date, startOfWeek, 1); - } - - private static long getDay(long date, int startOfWeek, int increment) { - Calendar calendar = CALENDAR; - synchronized(calendar) { - calendar.setTimeInMillis(date); - int day = calendar.get(Calendar.DAY_OF_WEEK); - // Normalize the view starting date to a week starting day - while (day != startOfWeek) { - calendar.add(Calendar.DATE, increment); - day = calendar.get(Calendar.DAY_OF_WEEK); - } - return startOfDayInMillis(calendar.getTimeInMillis()); - } - } - - /** - * Returns the previous month. - * - * @param date Base date - * @return previous month - */ - public static long getPreviousMonth(long date) { - return incrementMonth(date, -1); - } - - /** - * Returns the next month. - * - * @param date Base date - * @return next month - */ - public static long getNextMonth(long date) { - return incrementMonth(date, 1); - } - - private static long incrementMonth(long date, int increment) { - Calendar calendar = CALENDAR; - synchronized(calendar) { - calendar.setTimeInMillis(date); - calendar.add(Calendar.MONTH, increment); - return calendar.getTimeInMillis(); - } - } - - /** - * Returns the date corresponding to the start of the month. - * - * @param date Base date - * @return Start of month. - */ - public static long getStartOfMonth(long date) { - return getMonth(date, -1); - } - - /** - * Returns the date corresponding to the end of the month. - * - * @param date Base date - * @return End of month. - */ - public static long getEndOfMonth(long date) { - return getMonth(date, 1); - } - - private static long getMonth(long date, int increment) { - long result; - Calendar calendar = CALENDAR; - synchronized(calendar) { - calendar.setTimeInMillis(date); - if (increment == -1) { - calendar.set(Calendar.DAY_OF_MONTH, 1); - result = startOfDayInMillis(calendar.getTimeInMillis()); - } else { - calendar.add(Calendar.MONTH, 1); - calendar.set(Calendar.DAY_OF_MONTH, 1); - calendar.set(Calendar.HOUR_OF_DAY, 0); - calendar.set(Calendar.MILLISECOND, 0); - calendar.set(Calendar.SECOND, 0); - calendar.set(Calendar.MINUTE, 0); - calendar.add(Calendar.MILLISECOND, -1); - result = calendar.getTimeInMillis(); - } - } - return result; - } - - /** - * Returns the day of the week. - * - * @param date date - * @return day of week. - */ - public static int getDayOfWeek(long date) { - Calendar calendar = CALENDAR; - synchronized(calendar) { - calendar.setTimeInMillis(date); - return (calendar.get(Calendar.DAY_OF_WEEK)); - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/calendar/DaySelectionModel.java b/src/main/java/org/jdesktop/swingx/calendar/DaySelectionModel.java deleted file mode 100644 index 82b9b53711..0000000000 --- a/src/main/java/org/jdesktop/swingx/calendar/DaySelectionModel.java +++ /dev/null @@ -1,296 +0,0 @@ -/* - * $Id: DaySelectionModel.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.calendar; - -import org.jdesktop.swingx.event.DateSelectionEvent.EventType; -import org.jdesktop.swingx.event.EventListenerMap; -import org.jdesktop.swingx.util.Contract; - -import java.util.*; - -/** - * - * DaySelectionModel is a (temporary?) implementation of DateSelectionModel - * which normalizes all dates to the start of the day, that is zeroes all - * time fields. Responsibility extracted from JXMonthView (which must - * respect rules of model instead of trying to be clever itself). - * - * @author Joshua Outwater - */ -public class DaySelectionModel extends AbstractDateSelectionModel { - private SelectionMode selectionMode; - private SortedSet selectedDates; - private SortedSet unselectableDates; - - /** - * - */ - public DaySelectionModel() { - this(null); - } - - /** - * - */ - public DaySelectionModel(Locale locale) { - super(locale); - this.listenerMap = new EventListenerMap(); - this.selectionMode = SelectionMode.SINGLE_SELECTION; - this.selectedDates = new TreeSet(); - this.unselectableDates = new TreeSet(); - - } - /** - * - */ - @Override - public SelectionMode getSelectionMode() { - return selectionMode; - } - - /** - * - */ - @Override - public void setSelectionMode(final SelectionMode selectionMode) { - this.selectionMode = selectionMode; - clearSelection(); - } - - //---------------------- selection ops - /** - * {@inheritDoc} - */ - @Override - public void addSelectionInterval(Date startDate, Date endDate) { - if (startDate.after(endDate)) { - return; - } - startDate = startOfDay(startDate); - endDate = startOfDay(endDate); - boolean added = false; - switch (selectionMode) { - case SINGLE_SELECTION: - if (isSelected(startDate)) return; - clearSelectionImpl(); - added = addSelectionImpl(startDate, startDate); - break; - case SINGLE_INTERVAL_SELECTION: - if (isIntervalSelected(startDate, endDate)) return; - clearSelectionImpl(); - added = addSelectionImpl(startDate, endDate); - break; - case MULTIPLE_INTERVAL_SELECTION: - if (isIntervalSelected(startDate, endDate)) return; - added = addSelectionImpl(startDate, endDate); - break; - default: - break; - } - if (added) { - fireValueChanged(EventType.DATES_ADDED); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void setSelectionInterval(Date startDate, Date endDate) { - startDate = startOfDay(startDate); - endDate = startOfDay(endDate); - if (SelectionMode.SINGLE_SELECTION.equals(selectionMode)) { - if (isSelected(startDate)) return; - endDate = startDate; - } else { - if (isIntervalSelected(startDate, endDate)) return; - } - clearSelectionImpl(); - if (addSelectionImpl(startDate, endDate)) { - fireValueChanged(EventType.DATES_SET); - } - } - - /** - * Checks and returns if the single date interval bounded by startDate and endDate - * is selected. This is useful only for SingleInterval mode. - * - * @param startDate the start of the interval - * @param endDate the end of the interval, must be >= startDate - * @return true the interval is selected, false otherwise. - */ - private boolean isIntervalSelected(Date startDate, Date endDate) { - if (isSelectionEmpty()) return false; - startDate = startOfDay(startDate); - endDate = startOfDay(endDate); - return selectedDates.first().equals(startDate) - && selectedDates.last().equals(endDate); - } - - /** - * {@inheritDoc} - */ - @Override - public void removeSelectionInterval(Date startDate, Date endDate) { - if (startDate.after(endDate)) { - return; - } - - startDate = startOfDay(startDate); - endDate = startOfDay(endDate); - long startDateMs = startDate.getTime(); - long endDateMs = endDate.getTime(); - ArrayList datesToRemove = new ArrayList(); - for (Date selectedDate : selectedDates) { - long selectedDateMs = selectedDate.getTime(); - if (selectedDateMs >= startDateMs && selectedDateMs <= endDateMs) { - datesToRemove.add(selectedDate); - } - } - - if (!datesToRemove.isEmpty()) { - selectedDates.removeAll(datesToRemove); - fireValueChanged(EventType.DATES_REMOVED); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void clearSelection() { - if (isSelectionEmpty()) return; - clearSelectionImpl(); - fireValueChanged(EventType.SELECTION_CLEARED); - } - - private void clearSelectionImpl() { - selectedDates.clear(); - } - - /** - * {@inheritDoc} - */ - @Override - public SortedSet getSelection() { - return new TreeSet(selectedDates); - } - - /** - * {@inheritDoc} - */ - @Override - public Date getFirstSelectionDate() { - return isSelectionEmpty() ? null : selectedDates.first(); - } - - /** - * {@inheritDoc} - */ - @Override - public Date getLastSelectionDate() { - return isSelectionEmpty() ? null : selectedDates.last(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isSelected(Date date) { - // JW: don't need Contract ... startOfDay will throw NPE if null - return selectedDates.contains(startOfDay(date)); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isSelectionEmpty() { - return selectedDates.isEmpty(); - } - - /** - * {@inheritDoc} - */ - @Override - public SortedSet getUnselectableDates() { - return new TreeSet(unselectableDates); - } - - /** - * {@inheritDoc} - */ - @Override - public void setUnselectableDates(SortedSet unselectables) { - this.unselectableDates.clear(); - for (Date date : unselectables) { - unselectableDates.add(startOfDay(date)); - } - for (Date unselectableDate : this.unselectableDates) { - removeSelectionInterval(unselectableDate, unselectableDate); - } - fireValueChanged(EventType.UNSELECTED_DATES_CHANGED); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isUnselectableDate(Date date) { - date = startOfDay(date); - return upperBound != null && upperBound.getTime() < date.getTime() || - lowerBound != null && lowerBound.getTime() > date.getTime() || - unselectableDates != null && unselectableDates.contains(date); - } - - - private boolean addSelectionImpl(final Date startDate, final Date endDate) { - boolean hasAdded = false; - calendar.setTime(startDate); - Date date = calendar.getTime(); - while (date.before(endDate) || date.equals(endDate)) { - if (!isUnselectableDate(date)) { - hasAdded = true; - selectedDates.add(date); - } - calendar.add(Calendar.DATE, 1); - date = calendar.getTime(); - } - return hasAdded; - } - - - /** - * {@inheritDoc} - */ - - /** - * {@inheritDoc}

                    - * - * Implemented to return the start of the day which contains the date. - */ - @Override - public Date getNormalizedDate(Date date) { - Contract.asNotNull(date, "date must not be null"); - return startOfDay(date); - } - -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/calendar/DefaultDateSelectionModel.java b/src/main/java/org/jdesktop/swingx/calendar/DefaultDateSelectionModel.java deleted file mode 100644 index ef08080a57..0000000000 --- a/src/main/java/org/jdesktop/swingx/calendar/DefaultDateSelectionModel.java +++ /dev/null @@ -1,273 +0,0 @@ -/* - * $Id: DefaultDateSelectionModel.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.calendar; - -import org.jdesktop.swingx.event.DateSelectionEvent.EventType; -import org.jdesktop.swingx.util.Contract; - -import java.util.*; - -/** - * - * @author Joshua Outwater - */ -public class DefaultDateSelectionModel extends AbstractDateSelectionModel { - private SelectionMode selectionMode; - private SortedSet selectedDates; - private SortedSet unselectableDates; - - /** - * - */ - public DefaultDateSelectionModel() { - this(null); - } - - /** - *

                    - * - * The selection mode defaults to SINGLE_SELECTION. - */ - public DefaultDateSelectionModel(Locale locale) { - super(locale); - this.selectionMode = SelectionMode.SINGLE_SELECTION; - this.selectedDates = new TreeSet(); - this.unselectableDates = new TreeSet(); - } - /** - * {@inheritDoc} - */ - @Override - public SelectionMode getSelectionMode() { - return selectionMode; - } - - /** - * {@inheritDoc} - */ - @Override - public void setSelectionMode(final SelectionMode selectionMode) { - this.selectionMode = selectionMode; - clearSelection(); - } - - -//------------------- selection ops - /** - * {@inheritDoc} - */ - @Override - public void addSelectionInterval(Date startDate, Date endDate) { - if (startDate.after(endDate)) { - return; - } - boolean added = false; - switch (selectionMode) { - case SINGLE_SELECTION: - if (isSelected(startDate)) return; - clearSelectionImpl(); - added = addSelectionImpl(startDate, startDate); - break; - case SINGLE_INTERVAL_SELECTION: - if (isIntervalSelected(startDate, endDate)) return; - clearSelectionImpl(); - added = addSelectionImpl(startDate, endDate); - break; - case MULTIPLE_INTERVAL_SELECTION: - if (isIntervalSelected(startDate, endDate)) return; - added = addSelectionImpl(startDate, endDate); - break; - default: - break; - } - if (added) { - fireValueChanged(EventType.DATES_ADDED); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void setSelectionInterval(final Date startDate, Date endDate) { - if (SelectionMode.SINGLE_SELECTION.equals(selectionMode)) { - if (isSelected(startDate)) return; - endDate = startDate; - } else { - if (isIntervalSelected(startDate, endDate)) return; - } - clearSelectionImpl(); - if (addSelectionImpl(startDate, endDate)) { - fireValueChanged(EventType.DATES_SET); - } - } - - /** - * Checks and returns if the single date interval bounded by startDate and endDate - * is selected. This is useful only for SingleInterval mode. - * - * @param startDate the start of the interval - * @param endDate the end of the interval, must be >= startDate - * @return true the interval is selected, false otherwise. - */ - private boolean isIntervalSelected(Date startDate, Date endDate) { - if (isSelectionEmpty()) return false; - return selectedDates.first().equals(startDate) - && selectedDates.last().equals(endDate); - } - - /** - * {@inheritDoc} - */ - @Override - public void removeSelectionInterval(final Date startDate, final Date endDate) { - if (startDate.after(endDate)) { - return; - } - - long startDateMs = startDate.getTime(); - long endDateMs = endDate.getTime(); - ArrayList datesToRemove = new ArrayList(); - for (Date selectedDate : selectedDates) { - long selectedDateMs = selectedDate.getTime(); - if (selectedDateMs >= startDateMs && selectedDateMs <= endDateMs) { - datesToRemove.add(selectedDate); - } - } - - if (!datesToRemove.isEmpty()) { - selectedDates.removeAll(datesToRemove); - fireValueChanged(EventType.DATES_REMOVED); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void clearSelection() { - if (isSelectionEmpty()) return; - clearSelectionImpl(); - fireValueChanged(EventType.SELECTION_CLEARED); - } - - private void clearSelectionImpl() { - selectedDates.clear(); - } - - /** - * {@inheritDoc} - */ - @Override - public SortedSet getSelection() { - return new TreeSet(selectedDates); - } - - /** - * {@inheritDoc} - */ - @Override - public Date getFirstSelectionDate() { - return isSelectionEmpty() ? null : selectedDates.first(); - } - - /** - * {@inheritDoc} - */ - @Override - public Date getLastSelectionDate() { - return isSelectionEmpty() ? null : selectedDates.last(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isSelected(final Date date) { - Contract.asNotNull(date, "date must not be null"); - return selectedDates.contains(date); - } - - /** - * {@inheritDoc} - */ - @Override - public Date getNormalizedDate(Date date) { - return new Date(date.getTime()); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isSelectionEmpty() { - return selectedDates.isEmpty(); - } - - - /** - * {@inheritDoc} - */ - @Override - public SortedSet getUnselectableDates() { - return new TreeSet(unselectableDates); - } - - /** - * {@inheritDoc} - */ - @Override - public void setUnselectableDates(SortedSet unselectableDates) { - this.unselectableDates = unselectableDates; - for (Date unselectableDate : this.unselectableDates) { - removeSelectionInterval(unselectableDate, unselectableDate); - } - fireValueChanged(EventType.UNSELECTED_DATES_CHANGED); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isUnselectableDate(Date date) { - return upperBound != null && upperBound.getTime() < date.getTime() || - lowerBound != null && lowerBound.getTime() > date.getTime() || - unselectableDates != null && unselectableDates.contains(date); - } - - - private boolean addSelectionImpl(final Date startDate, final Date endDate) { - boolean hasAdded = false; - calendar.setTime(startDate); - Date date = calendar.getTime(); - while (date.before(endDate) || date.equals(endDate)) { - if (!isUnselectableDate(date)) { - hasAdded = true; - selectedDates.add(date); - } - calendar.add(Calendar.DATE, 1); - date = calendar.getTime(); - } - return hasAdded; - } - - -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/calendar/SingleDaySelectionModel.java b/src/main/java/org/jdesktop/swingx/calendar/SingleDaySelectionModel.java deleted file mode 100644 index 50a01b6765..0000000000 --- a/src/main/java/org/jdesktop/swingx/calendar/SingleDaySelectionModel.java +++ /dev/null @@ -1,345 +0,0 @@ -/* - * $Id: SingleDaySelectionModel.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.calendar; - -import org.jdesktop.swingx.event.DateSelectionEvent.EventType; -import org.jdesktop.swingx.util.Contract; - -import java.util.Date; -import java.util.Locale; -import java.util.SortedSet; -import java.util.TreeSet; - -/** - * DateSelectionModel which allows a single selection only.

                    - * - * Temporary quick & dirty class to explore requirements as needed by - * a DatePicker. Need to define the states more exactly. Currently - * - *

                  • takes all Dates as-are (that is the normalized is the same as the given): - * selected, unselectable, lower/upper bounds - *
                  • interprets any Date between the start/end of day of the selected as selected - *
                  • interprets any Date between the start/end of an unselectable date as unselectable - *
                  • interprets the lower/upper bounds as being the start/end of the given - * dates, that is any Date after the start of day of the lower and before the end of - * day of the upper is selectable. - * - * - * @author Jeanette Winzenburg - */ -public class SingleDaySelectionModel extends AbstractDateSelectionModel { - - private SortedSet selectedDates; - private SortedSet unselectableDates; - - /** - * Instantiates a SingleDaySelectionModel with default locale. - */ - public SingleDaySelectionModel() { - this(null); - } - - /** - * Instantiates a SingleSelectionModel with the given locale. If the locale is - * null, the Locale's default is used. - * - * PENDING JW: fall back to JComponent.getDefaultLocale instead? We use this - * with components anyway? - * - * @param locale the Locale to use with this model, defaults to Locale.default() - * if null. - */ - public SingleDaySelectionModel(Locale locale) { - super(locale); - this.selectedDates = new TreeSet(); - this.unselectableDates = new TreeSet(); - } - - /** - * {@inheritDoc} - */ - @Override - public SelectionMode getSelectionMode() { - return SelectionMode.SINGLE_SELECTION; - } - - /** - * {@inheritDoc}

                    - * - * Implemented to do nothing. - * - */ - @Override - public void setSelectionMode(final SelectionMode selectionMode) { - } - - - //---------------------- selection ops - /** - * {@inheritDoc}

                    - * - * Implemented to call setSelectionInterval with startDate for both - * parameters. - */ - @Override - public void addSelectionInterval(Date startDate, Date endDate) { - setSelection(startDate); - } - - /** - * {@inheritDoc}

                    - * - * PENDING JW: define what happens if we have a selection but the interval - * isn't selectable. - */ - @Override - public void setSelectionInterval(Date startDate, Date endDate) { - setSelection(startDate); - } - - /** - * {@inheritDoc} - */ - @Override - public void removeSelectionInterval(Date startDate, Date endDate) { - Contract.asNotNull(startDate, "date must not be null"); - if (isSelectionEmpty()) return; - if (isSelectionInInterval(startDate, endDate)) { - selectedDates.clear(); - fireValueChanged(EventType.DATES_REMOVED); - } - } - - /** - * Checks and returns whether the selected date is contained in the interval - * given by startDate/endDate. The selection must not be empty when - * calling this method.

                    - * - * This implementation interprets the interval between the start of the day - * of startDay to the end of the day of endDate. - * - * @param startDate the start of the interval, must not be null - * @param endDate the end of the interval, must not be null - * @return true if the selected date is contained in the interval - */ - protected boolean isSelectionInInterval(Date startDate, Date endDate) { - if (selectedDates.first().before(startOfDay(startDate)) - || (selectedDates.first().after(endOfDay(endDate)))) return false; - return true; - } - - /** - * Selects the given date if it is selectable and not yet selected. - * Does nothing otherwise. - * If this operation changes the current selection, it will fire a - * DateSelectionEvent of type DATES_SET. - * - * @param date the Date to select, must not be null. - */ - protected void setSelection(Date date) { - Contract.asNotNull(date, "date must not be null"); - if (isSelectedStrict(date)) return; - if (isSelectable(date)) { - selectedDates.clear(); - // PENDING JW: use normalized - selectedDates.add(date); - fireValueChanged(EventType.DATES_SET); - } - } - - /** - * Checks and returns whether the given date is contained in the selection. - * This differs from isSelected in that it tests for the exact (normalized) - * Date instead of for the same day. - * - * @param date the Date to test. - * @return true if the given date is contained in the selection, - * false otherwise - * - */ - private boolean isSelectedStrict(Date date) { - if (!isSelectionEmpty()) { - // PENDING JW: use normalized - return selectedDates.first().equals(date); - } - return false; - } - - /** - * {@inheritDoc} - */ - @Override - public Date getFirstSelectionDate() { - return isSelectionEmpty() ? null : selectedDates.first(); - } - - /** - * {@inheritDoc} - */ - @Override - public Date getLastSelectionDate() { - return isSelectionEmpty() ? null : selectedDates.last(); - } - - /** - * Returns a boolean indicating whether the given date is selectable. - * - * @param date the date to check for selectable, must not be null. - * @return true if the given date is selectable, false if not. - */ - public boolean isSelectable(Date date) { - if (outOfBounds(date)) return false; - return !inUnselectables(date); - } - - /** - * @param date - * @return - */ - private boolean inUnselectables(Date date) { - for (Date unselectable : unselectableDates) { - if (isSameDay(unselectable, date)) return true; - } - return false; - } - - /** - * Returns a boolean indication whether the given date is below - * the lower or above the upper bound. - * - * @param date - * @return - */ - private boolean outOfBounds(Date date) { - if (belowLowerBound(date)) return true; - if (aboveUpperBound(date)) return true; - return false; - } - - /** - * @param date - * @return - */ - private boolean aboveUpperBound(Date date) { - if (upperBound != null) { - return endOfDay(upperBound).before(date); - } - return false; - } - - /** - * @param date - * @return - */ - private boolean belowLowerBound(Date date) { - if (lowerBound != null) { - return startOfDay(lowerBound).after(date); - } - return false; - } - - - /** - * {@inheritDoc} - */ - @Override - public void clearSelection() { - if (isSelectionEmpty()) return; - selectedDates.clear(); - fireValueChanged(EventType.SELECTION_CLEARED); - } - - - /** - * {@inheritDoc} - */ - @Override - public SortedSet getSelection() { - return new TreeSet(selectedDates); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isSelected(Date date) { - Contract.asNotNull(date, "date must not be null"); - if (isSelectionEmpty()) return false; - return isSameDay(selectedDates.first(), date); - } - - - - /** - * {@inheritDoc}

                    - * - * Implemented to return the date itself. - */ - @Override - public Date getNormalizedDate(Date date) { - return new Date(date.getTime()); - } - - - /** - * {@inheritDoc} - */ - @Override - public boolean isSelectionEmpty() { - return selectedDates.isEmpty(); - } - - - /** - * {@inheritDoc} - */ - @Override - public SortedSet getUnselectableDates() { - return new TreeSet(unselectableDates); - } - - /** - * {@inheritDoc} - */ - @Override - public void setUnselectableDates(SortedSet unselectables) { - Contract.asNotNull(unselectables, "unselectable dates must not be null"); - this.unselectableDates.clear(); - for (Date unselectableDate : unselectables) { - removeSelectionInterval(unselectableDate, unselectableDate); - unselectableDates.add(unselectableDate); - } - fireValueChanged(EventType.UNSELECTED_DATES_CHANGED); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isUnselectableDate(Date date) { - return !isSelectable(date); - } - - - - - -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/calendar/package-info.java b/src/main/java/org/jdesktop/swingx/calendar/package-info.java deleted file mode 100644 index 4e4738c59e..0000000000 --- a/src/main/java/org/jdesktop/swingx/calendar/package-info.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -/** - * Contains classes and interfaces used by the {@code JXDatePicker} and - * {@code JXMonthView} components. - */ -package org.jdesktop.swingx.calendar; - diff --git a/src/main/java/org/jdesktop/swingx/color/EyeDropperColorChooserPanel.form b/src/main/java/org/jdesktop/swingx/color/EyeDropperColorChooserPanel.form deleted file mode 100644 index 929940c4d7..0000000000 --- a/src/main/java/org/jdesktop/swingx/color/EyeDropperColorChooserPanel.form +++ /dev/null @@ -1,125 +0,0 @@ - - -

                    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/java/org/jdesktop/swingx/color/EyeDropperColorChooserPanel.java b/src/main/java/org/jdesktop/swingx/color/EyeDropperColorChooserPanel.java deleted file mode 100644 index 3937fc46e0..0000000000 --- a/src/main/java/org/jdesktop/swingx/color/EyeDropperColorChooserPanel.java +++ /dev/null @@ -1,289 +0,0 @@ -/* - * $Id: EyeDropperColorChooserPanel.java 4082 2011-11-15 18:39:43Z kschaefe $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.color; - -import org.jdesktop.swingx.JXColorSelectionButton; -import org.jdesktop.swingx.util.PaintUtils; - -import javax.swing.*; -import javax.swing.colorchooser.AbstractColorChooserPanel; -import javax.swing.event.MouseInputAdapter; -import java.awt.*; -import java.awt.event.MouseEvent; -import java.awt.geom.Point2D; -import java.awt.image.BufferedImage; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; - -/** - *

                    EyeDropperColorChooserPanel is a pluggable panel for the - * {@link JColorChooser} which allows the user to grab any - * color from the screen using a magnifying glass.

                    - * - *

                    Example usage:

                    - *
                    
                    - *    public static void main(String ... args) {
                    -        SwingUtilities.invokeLater(new Runnable() {
                    -            public void run() {
                    -                JColorChooser chooser = new JColorChooser();
                    -                chooser.addChooserPanel(new EyeDropperColorChooserPanel());
                    -                JFrame frame = new JFrame();
                    -                frame.add(chooser);
                    -                frame.pack();
                    -                frame.setVisible(true);
                    -            }
                    -        });
                    -    }
                    - * 
                    - * - * @author joshua@marinacci.org - */ -public class EyeDropperColorChooserPanel extends AbstractColorChooserPanel { - - /** - * Example usage - */ - public static void main(String ... args) { - SwingUtilities.invokeLater(new Runnable() { - public void run() { - JColorChooser chooser = new JColorChooser(); - chooser.addChooserPanel(new EyeDropperColorChooserPanel()); - JFrame frame = new JFrame(); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - frame.add(chooser); - frame.pack(); - frame.setVisible(true); - } - }); - } - - /** - * Creates new EyeDropperColorChooserPanel - */ - public EyeDropperColorChooserPanel() { - initComponents(); - MouseInputAdapter mia = new MouseInputAdapter() { - @Override - public void mousePressed(MouseEvent evt) { - } - @Override - public void mouseDragged(MouseEvent evt) { - Point pt = evt.getPoint(); - SwingUtilities.convertPointToScreen(pt,evt.getComponent()); - ((MagnifyingPanel)magPanel).setMagPoint(pt); - } - @Override - public void mouseReleased(MouseEvent evt) { - Color newColor = new Color(((MagnifyingPanel)magPanel).activeColor); - getColorSelectionModel().setSelectedColor(newColor); - } - }; - eyeDropper.addMouseListener(mia); - eyeDropper.addMouseMotionListener(mia); - try { - eyeDropper.setIcon(new ImageIcon( - EyeDropperColorChooserPanel.class.getResource("mag.png"))); - eyeDropper.setText(""); - } catch (Exception ex) { - ex.printStackTrace(); - } - - magPanel.addPropertyChangeListener(new PropertyChangeListener() { - public void propertyChange(PropertyChangeEvent evt) { - Color color = new Color(((MagnifyingPanel)magPanel).activeColor); - activeColor.setBackground(color); - hexColor.setText(PaintUtils.toHexString(color).substring(1)); - rgbColor.setText(color.getRed() +"," + color.getGreen() + "," + color.getBlue()); - } - }); - } - - private class MagnifyingPanel extends JPanel { - private Point2D point; - private int activeColor; - public void setMagPoint(Point2D point) { - this.point = point; - repaint(); - } - @Override - public void paintComponent(Graphics g) { - if(point != null) { - Rectangle rect = new Rectangle((int)point.getX()-10,(int)point.getY()-10,20,20); - try { - BufferedImage img =new Robot().createScreenCapture(rect); - g.drawImage(img,0,0,getWidth(),getHeight(),null); - int oldColor = activeColor; - activeColor = img.getRGB(img.getWidth()/2,img.getHeight()/2); - firePropertyChange("activeColor", oldColor, activeColor); - } catch (AWTException ex) { - ex.printStackTrace(); - } - } - g.setColor(Color.black); - g.drawRect(getWidth()/2 - 5, getHeight()/2 -5, 10,10); - } - } - - - /** This method is called from within the constructor to - * initialize the form. - * WARNING: Do NOT modify this code. The content of this method is - * always regenerated by the Form Editor. - */ - // //GEN-BEGIN:initComponents - private void initComponents() { - java.awt.GridBagConstraints gridBagConstraints; - - eyeDropper = new javax.swing.JButton(); - magPanel = new MagnifyingPanel(); - activeColor = new JXColorSelectionButton(); - hexColor = new javax.swing.JTextField(); - JTextArea jTextArea1 = new JTextArea(); - jLabel1 = new JLabel(); - rgbColor = new javax.swing.JTextField(); - JLabel jLabel2 = new JLabel(); - - setLayout(new java.awt.GridBagLayout()); - - eyeDropper.setText("eye"); - add(eyeDropper, new java.awt.GridBagConstraints()); - - magPanel.setLayout(new java.awt.BorderLayout()); - - magPanel.setBorder(javax.swing.BorderFactory.createLineBorder(new Color(0, 0, 0))); - magPanel.setMinimumSize(new java.awt.Dimension(100, 100)); - magPanel.setPreferredSize(new java.awt.Dimension(100, 100)); - gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 1; - gridBagConstraints.gridheight = 3; - gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; - gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 12); - add(magPanel, gridBagConstraints); - - activeColor.setEnabled(false); - activeColor.setPreferredSize(new java.awt.Dimension(40, 40)); - gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.gridx = 2; - gridBagConstraints.gridy = 3; - gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; - gridBagConstraints.insets = new java.awt.Insets(2, 0, 2, 0); - add(activeColor, gridBagConstraints); - - hexColor.setEditable(false); - gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.gridx = 2; - gridBagConstraints.gridy = 1; - gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; - gridBagConstraints.insets = new java.awt.Insets(2, 0, 2, 0); - add(hexColor, gridBagConstraints); - - jTextArea1.setColumns(20); - jTextArea1.setEditable(false); - jTextArea1.setLineWrap(true); - jTextArea1.setRows(5); - jTextArea1.setText("Drag the magnifying glass to select a color from the screen."); - jTextArea1.setWrapStyleWord(true); - jTextArea1.setOpaque(false); - gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.gridwidth = 2; - gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH; - gridBagConstraints.weightx = 10.0; - gridBagConstraints.weighty = 10.0; - gridBagConstraints.insets = new java.awt.Insets(0, 0, 7, 0); - add(jTextArea1, gridBagConstraints); - - jLabel1.setText("#"); - gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 1; - gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST; - gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 4); - add(jLabel1, gridBagConstraints); - - rgbColor.setEditable(false); - rgbColor.setText("255,255,255"); - gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.gridx = 2; - gridBagConstraints.gridy = 2; - gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; - gridBagConstraints.insets = new java.awt.Insets(2, 0, 2, 0); - add(rgbColor, gridBagConstraints); - - jLabel2.setText("RGB"); - gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 2; - gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST; - gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 4); - add(jLabel2, gridBagConstraints); - - }// //GEN-END:initComponents - - - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JButton activeColor; - private javax.swing.JButton eyeDropper; - private javax.swing.JTextField hexColor; - private JLabel jLabel1; - private JPanel magPanel; - private javax.swing.JTextField rgbColor; - // End of variables declaration//GEN-END:variables - - /** - * {@inheritDoc} - */ - @Override - public void updateChooser() { - } - - /** - * {@inheritDoc} - */ - @Override - protected void buildChooser() { - } - - /** - * {@inheritDoc} - */ - @Override - public String getDisplayName() { - return "Grab from Screen"; - } - - /** - * {@inheritDoc} - */ - @Override - public Icon getSmallDisplayIcon() { - return new ImageIcon(); - } - - /** - * {@inheritDoc} - */ - @Override - public Icon getLargeDisplayIcon() { - return new ImageIcon(); - } -} diff --git a/src/main/java/org/jdesktop/swingx/color/GradientPreviewPanel.java b/src/main/java/org/jdesktop/swingx/color/GradientPreviewPanel.java deleted file mode 100644 index 02c4c624ae..0000000000 --- a/src/main/java/org/jdesktop/swingx/color/GradientPreviewPanel.java +++ /dev/null @@ -1,285 +0,0 @@ -/* - * $Id: GradientPreviewPanel.java 4082 2011-11-15 18:39:43Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.color; - -import org.jdesktop.swingx.JXGradientChooser; -import org.jdesktop.swingx.JXPanel; -import org.jdesktop.swingx.multislider.MultiThumbModel; -import org.jdesktop.swingx.multislider.Thumb; -import org.jdesktop.swingx.util.PaintUtils; - -import javax.swing.event.MouseInputAdapter; -import java.awt.*; -import java.awt.event.MouseEvent; -import java.awt.geom.Point2D; -import java.util.List; -import java.util.logging.Logger; - -/** - *

                    Dependency: Because this class relies on LinearGradientPaint and - * RadialGradientPaint, it requires the optional MultipleGradientPaint.jar

                    - * - * @author joshy - */ -public class GradientPreviewPanel extends JXPanel { - private Paint checker_texture = null; - private Point2D start, end; - public JXGradientChooser picker; - boolean moving_start = false; - boolean moving_end = false; - private boolean radial = false; - private boolean reversed = false; - private boolean reflected = false; - private boolean repeated = false; - private MultipleGradientPaint gradient; - - public GradientPreviewPanel() { - start = new Point2D.Float(10,10); - end = new Point2D.Float(80,10); - checker_texture = PaintUtils.getCheckerPaint(); - MouseInputAdapter ma = new GradientMouseHandler(); - this.addMouseListener(ma); - this.addMouseMotionListener(ma); - } - - public void setGradient() { - repaint(); - } - - public void setGradient(MultipleGradientPaint grad) { - MultipleGradientPaint old = getGradient(); - if(grad instanceof LinearGradientPaint) { - LinearGradientPaint paint = (LinearGradientPaint)grad; - this.start = paint.getStartPoint(); - this.end = paint.getEndPoint(); - } else { - RadialGradientPaint paint = (RadialGradientPaint)grad; - this.start = paint.getCenterPoint(); - this.end = new Point2D.Double(start.getX(),start.getY()+paint.getRadius()); - } - this.gradient = grad; - firePropertyChange("gradient", old, getGradient()); - repaint(); - } - - public MultipleGradientPaint getGradient() { - return this.gradient; - } - - public MultipleGradientPaint calculateGradient() { - List> stops = getStops(); - int len = stops.size(); - - // set up the data for the gradient - float[] fractions = new float[len]; - Color[] colors = new Color[len]; - int i = 0; - for (Thumb thumb : stops) { - colors[i] = (Color)thumb.getObject(); - fractions[i] = thumb.getPosition(); - i++; - } - - // get the final gradient - this.setGradient(calculateGradient(fractions, colors)); - return getGradient(); - } - - private MultiThumbModel model; - private Logger log = Logger.getLogger(GradientPreviewPanel.class.getName()); - - private List> getStops() { - // calculate the color stops - return model == null ? null : model.getSortedThumbs(); - } - - public void setMultiThumbModel(MultiThumbModel model) { - MultiThumbModel old = getMultiThumbModel(); - this.model = model; - firePropertyChange("multiThumbModel", old, getMultiThumbModel()); - } - - public MultiThumbModel getMultiThumbModel() { - return this.model; - } - - @Override - protected void paintComponent(Graphics g) { - try { - Graphics2D g2 = (Graphics2D)g; - - // fill the background with checker first - g2.setPaint(checker_texture); - g.fillRect(0,0,getWidth(),getHeight()); - - - Paint paint = getGradient(); - // fill the area - if(paint != null) { - g2.setPaint(paint); - } else { - g2.setPaint(Color.black); - } - - g.fillRect(0,0,getWidth(),getHeight()); - - drawHandles(g2); - } catch (Exception ex) { - log .severe("ex: " + ex); - } - } - - private MultipleGradientPaint calculateGradient(final float[] fractions, final Color[] colors) { - // set up the end points - Point2D start = this.start; - Point2D end = this.end; - if(isReversed()) { - //if(picker.reversedCheck.isSelected()) { - start = this.end; - end = this.start; - } - - // set up the cycle type - MultipleGradientPaint.CycleMethod cycle = MultipleGradientPaint.CycleMethod.NO_CYCLE; - if(isRepeated()) { - //if(picker.repeatedRadio.isSelected()) { - cycle = MultipleGradientPaint.CycleMethod.REPEAT; - } - if(isReflected()) { - //if(picker.reflectedRadio.isSelected()) { - cycle = MultipleGradientPaint.CycleMethod.REFLECT; - } - - // create the underlying gradient paint - MultipleGradientPaint paint = null; - if(isRadial()) { //picker.styleCombo.getSelectedItem().toString().equals("Radial")) { - paint = new RadialGradientPaint( - start, (float)start.distance(end),start, - fractions, colors, cycle, MultipleGradientPaint.ColorSpaceType.SRGB, null - ); - } else { - paint = new LinearGradientPaint( - (float)start.getX(), - (float)start.getY(), - (float)end.getX(), - (float)end.getY(), - fractions,colors,cycle); - } - return paint; - } - - private void drawHandles(final Graphics2D g2) { - g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_ON); - // draw the points and gradient line - g2.setColor(Color.black); - g2.drawOval((int)start.getX()-5,(int)start.getY()-5,10,10); - g2.setColor(Color.white); - g2.drawOval((int)start.getX()-4,(int)start.getY()-4,8,8); - - g2.setColor(Color.black); - g2.drawOval((int)end.getX()-5,(int)end.getY()-5,10,10); - g2.setColor(Color.white); - g2.drawOval((int)end.getX()-4,(int)end.getY()-4,8,8); - - g2.setColor(Color.darkGray); - g2.drawLine((int)start.getX(),(int)start.getY(), - (int)end.getX(),(int)end.getY()); - g2.setColor(Color.gray); - g2.drawLine((int)start.getX()-1,(int)start.getY()-1, - (int)end.getX()-1,(int)end.getY()-1); - } - - private class GradientMouseHandler extends MouseInputAdapter { - - @Override - public void mousePressed(MouseEvent evt) { - moving_start = false; - moving_end = false; - if (evt.getPoint().distance(start) < 5) { - moving_start = true; - start = evt.getPoint(); - return; - } - - if (evt.getPoint().distance(end) < 5) { - moving_end = true; - end = evt.getPoint(); - return; - } - - start = evt.getPoint(); - } - - @Override - public void mouseDragged(MouseEvent evt) { - if (moving_start) { - start = evt.getPoint(); - } else { - end = evt.getPoint(); - } - calculateGradient(); - repaint(); - } - } - - public boolean isRadial() { - return radial; - } - - public void setRadial(boolean radial) { - boolean old = isRadial(); - this.radial = radial; - firePropertyChange("radial", old, isRadial()); - } - - public boolean isReversed() { - return reversed; - } - - public void setReversed(boolean reversed) { - boolean old = isReversed(); - this.reversed = reversed; - firePropertyChange("reversed", old, isReversed()); - } - - public boolean isReflected() { - return reflected; - } - - public void setReflected(boolean reflected) { - boolean old = isReflected(); - this.reflected = reflected; - firePropertyChange("reflected", old, isReflected()); - } - - public boolean isRepeated() { - return repeated; - } - - public void setRepeated(boolean repeated) { - boolean old = isRepeated(); - this.repeated = repeated; - firePropertyChange("repeated", old, isRepeated()); - } -} - diff --git a/src/main/java/org/jdesktop/swingx/color/GradientThumbRenderer.java b/src/main/java/org/jdesktop/swingx/color/GradientThumbRenderer.java deleted file mode 100644 index eaf4b3c060..0000000000 --- a/src/main/java/org/jdesktop/swingx/color/GradientThumbRenderer.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * $Id: GradientThumbRenderer.java 4082 2011-11-15 18:39:43Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.color; - -import org.jdesktop.swingx.JXMultiThumbSlider; -import org.jdesktop.swingx.multislider.ThumbRenderer; -import org.jdesktop.swingx.util.PaintUtils; - -import javax.imageio.ImageIO; -import javax.swing.*; -import java.awt.*; - -public class GradientThumbRenderer extends JComponent implements ThumbRenderer { - private Image thumb_black; - private Image thumb_gray; - - public GradientThumbRenderer() { - super(); - - try { - thumb_black = ImageIO.read(GradientThumbRenderer.class.getResourceAsStream("/icons/thumb_black.png")); - thumb_gray = ImageIO.read(GradientThumbRenderer.class.getResourceAsStream("/icons/thumb_gray.png")); - } catch (Exception ex) { -// ex.printStackTrace(); - } - } - - private boolean selected; - @Override - protected void paintComponent(Graphics g) { - JComponent thumb = this; - int w = thumb.getWidth(); - g.setColor(getForeground()); - g.fillRect(0, 0, w - 1, w - 1); - if (selected) { - g.drawImage(thumb_black, 0, 0, null); - } else { - g.drawImage(thumb_gray, 0, 0, null); - } - } - - public JComponent getThumbRendererComponent(JXMultiThumbSlider slider, int index, boolean selected) { - Color c = (Color)slider.getModel().getThumbAt(index).getObject(); - c = PaintUtils.removeAlpha(c); - this.setForeground(c); - this.selected = selected; - return this; - } -} diff --git a/src/main/java/org/jdesktop/swingx/color/GradientTrackRenderer.java b/src/main/java/org/jdesktop/swingx/color/GradientTrackRenderer.java deleted file mode 100644 index 3e6db85748..0000000000 --- a/src/main/java/org/jdesktop/swingx/color/GradientTrackRenderer.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * $Id: GradientTrackRenderer.java 4082 2011-11-15 18:39:43Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.color; - -import org.jdesktop.swingx.JXMultiThumbSlider; -import org.jdesktop.swingx.multislider.Thumb; -import org.jdesktop.swingx.multislider.TrackRenderer; -import org.jdesktop.swingx.util.PaintUtils; - -import javax.swing.*; -import java.awt.*; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; -import java.util.List; - -/** - *

                    Dependency: Because this class relies on LinearGradientPaint and - * RadialGradientPaint, it requires the optional MultipleGradientPaint.jar

                    - */ -public class GradientTrackRenderer extends JComponent implements TrackRenderer { - private Paint checker_paint; - - public GradientTrackRenderer() { - checker_paint = PaintUtils.getCheckerPaint(); - } - - private JXMultiThumbSlider slider; - - @Override - public void paint(Graphics g) { - super.paint(g); - paintComponent(g); - } - - @Override - protected void paintComponent(Graphics gfx) { - Graphics2D g = (Graphics2D)gfx; - - // get the list of colors - List> stops = slider.getModel().getSortedThumbs(); - int len = stops.size(); - - // set up the data for the gradient - float[] fractions = new float[len]; - Color[] colors = new Color[len]; - int i = 0; - for(Thumb thumb : stops) { - colors[i] = (Color)thumb.getObject(); - fractions[i] = thumb.getPosition(); - i++; - } - - // calculate the track area - int thumb_width = 12; - int track_width = slider.getWidth() - thumb_width; - g.translate(thumb_width / 2, 12); - Rectangle2D rect = new Rectangle(0, 0, track_width, 20); - - // fill in the checker - g.setPaint(checker_paint); - g.fill(rect); - - // fill in the gradient - Point2D start = new Point2D.Float(0,0); - Point2D end = new Point2D.Float(track_width,0); - MultipleGradientPaint paint = new LinearGradientPaint( - (float)start.getX(), - (float)start.getY(), - (float)end.getX(), - (float)end.getY(), - fractions,colors); - g.setPaint(paint); - g.fill(rect); - - // draw a border - g.setColor(Color.black); - g.draw(rect); - g.translate(-thumb_width / 2, -12); - } - - public JComponent getRendererComponent(JXMultiThumbSlider slider) { - this.slider = slider; - return this; - } -} diff --git a/src/main/java/org/jdesktop/swingx/color/colorwell.png b/src/main/java/org/jdesktop/swingx/color/colorwell.png deleted file mode 100644 index edf111fc8fbc46631f08ae6494a39b25a309a410..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 725 zcmV;`0xJE9P)z@;j(q!3lK=n!AY({UO#lFTB>(_`g8%^e{{R4h=>PzA zFaQARU;qF*m;eA5Z<1fdMgRZ;OG!jQRCwC#TCuU)FbsWwApP1+?vg4CWQt7VH9UfM z@EqA91Eh$XxUzL;>61hX=x9oo?%&y!KFp3FA&E}__yCf2yB!u95dl!uQlhF8xe3>< zD*(3J?PjydQ?q8NUkb}tmTjfFm&;|p-wS~A`OM5ViHNf1{c|&@`H9xe!qw@fl-jl( zEaG%JbzN6gmBC^9DJ1|R5<*Z_8%-(MXpFI#oMOaEb^E@*UatcXbzO@{(==68g%GN$ zQq`0aGuw0R1w>>LBC6{;Lmq;@yi8qU2W>r;H$;`~HT?`iC)+5T4VhENcKlKKL`2`WlWWcXQSjX}chd@jz zw!>hV*E_O^Nma+sW-&vt{cWFA*6bLsmMy|FkC<@{e?W{Pg?An?-8E*<2%NxS*{1hk zIbxQc36n3^X{WYxFTUi4|U8k<9#u#B|pE1U^ zZ9@o?Ka($GErDvI_aC$(-LC8Uz8`=%91agKklF5fb^i|lry!$8r!90^V+LuOXuOOXFfI%w5kq23CL~0}1sAtOcdks( zuwdZ=FDP!bkchZ2LX9yXF*c|RG?5{qK&cE3T3TprnVuQuaxN~4#Kbs3>bv?k-;?iq zzxVqEf778uNArde?`$E8LMFZ-K`TKLQA(oZz{ZVFzrk%v{Pozs|I@9J7JE{z?WfXd z9LGXR2|yTz)PsO(rBZ6t*tUK9hSF_;_wGHo)kw+V?p!aP=VF=~r4#^B7-5(ON-3&A zmC5qO`RjAx!#j37f2kc<@7_I}4`s9`m+Qm#Gk9qS+cuF>5=JrA>J7qHh*FBK?oI?N z{7U8LVPI`Lu#BSWOMW)vyRL)lI%G0wIx-%BPTPvhY@|J0! zwL}UK2$WJ(sx>Uz!ZHowBt}Y!5P~F0=*{&~uh-gktPNA4ltKu>^-7hXT4&a_P)d;T z(iln+X?+U}5XUi%#*MbA&Gg`)&QxA00Z1;KpBQbmj03AyJyvQo8on>D z4iDB2=T{dz=E~!oz2fZO{K1uNELHD4mz%rx!z)Sc?CZ(Q$B!o`U-pxIn{vbdu;RaJ z?SMC3o4s7TcW4bVb2pxxMOG#YgWcKuS7X7*9fQ9-7nBAZIdN_LO!2{WPa-pS;j0;R zWwOwh?#zE%4o1|S+HvC7pwutr#Prxt#Yfk#LwbF5nX?$dWT8W6^J*zxpS&EDvLO@G zV`qx1pLq)D=17aHd~>Q^P)l88=}NWK6)nQ=U-Ja-?E4W`Zb0xqX-i@Gu;a+B`o7JS cr|L)k1aI$M*{)DsFaQ7m07*qoM6N<$g7Po#qyPW_ diff --git a/src/main/java/org/jdesktop/swingx/color/package-info.java b/src/main/java/org/jdesktop/swingx/color/package-info.java deleted file mode 100644 index 3c9ced2f2a..0000000000 --- a/src/main/java/org/jdesktop/swingx/color/package-info.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -/** - * Contains classes and interfaces used by the {@code JXGradientChooser} - * component. - */ -package org.jdesktop.swingx.color; diff --git a/src/main/java/org/jdesktop/swingx/combobox/EnumComboBoxModel.java b/src/main/java/org/jdesktop/swingx/combobox/EnumComboBoxModel.java deleted file mode 100644 index 8788bf0380..0000000000 --- a/src/main/java/org/jdesktop/swingx/combobox/EnumComboBoxModel.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * $Id: EnumComboBoxModel.java 3475 2009-08-28 08:30:47Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.combobox; - -import java.util.*; - -/** - *

                    - * A ComboBoxModel implementation that safely wraps an Enum. It allows the - * developer to directly use an enum as their model for a combobox without any - * extra work, though the display can can be further customized. - *

                    - * - *

                    Simple Usage

                    - * - *

                    - * The simplest usage is to wrap an enum inside the - * EnumComboBoxModel and then set it as the model on the combo - * box. The combo box will then appear on screen with each value in the - * enum as a value in the combobox. - *

                    - *

                    - * ex: - *

                    - * - *
                    
                    - *  enum MyEnum { GoodStuff, BadStuff };
                    - *  ...
                    - *  JComboBox combo = new JComboBox();
                    - *  combo.setModel(new EnumComboBoxModel(MyEnum.class));
                    - * 
                    - * - *

                    Type safe access

                    - *

                    - * By using generics and co-variant types you can make accessing elements from - * the model be completely typesafe. ex: - *

                    - * - *
                    
                    - * EnumComboBoxModel<MyEnum> enumModel = new EnumComboBoxModel<MyEnum1>(
                    - *         MyEnum1.class);
                    - * 
                    - * MyEnum first = enumModel.getElement(0);
                    - * 
                    - * MyEnum selected = enumModel.getSelectedItem();
                    - * 
                    - * - *

                    Advanced Usage

                    - *

                    - * Since the exact toString() value of each enum constant may not - * be exactly what you want on screen (the values won't have spaces, for - * example) you can override to toString() method on the values when you declare - * your enum. Thus the display value is localized to the enum and not in your - * GUI code. ex: - * - *

                    
                    - *    private enum MyEnum {GoodStuff, BadStuff;
                    - *        public String toString() {
                    - *           switch(this) {
                    - *               case GoodStuff: return "Some Good Stuff";
                    - *               case BadStuff: return "Some Bad Stuff";
                    - *           }
                    - *           return "ERROR";
                    - *        }
                    - *    };
                    - * 
                    - * - * Note: if more than one enum constant returns the same {@code String} via - * {@code toString()}, this model will throw an exception on creation. - * - * @author joshy - * @author Karl Schaefer - */ -public class EnumComboBoxModel> extends ListComboBoxModel { - private static final long serialVersionUID = 2176566393195371004L; - - private final Map valueMap; - private final Class enumClass; - - /** - * Creates an {@code EnumComboBoxModel} for the enum represent by the - * {@code Class} {@code en}. - * - * @param en - * the enum class type - * @throws IllegalArgumentException - * if the {@code Enum.toString} returns the same value for more - * than one constant - */ - public EnumComboBoxModel(Class en) { - super(new ArrayList(EnumSet.allOf(en))); - - //we could size these, probably not worth it; enums are usually small - valueMap = new HashMap(); - enumClass = en; - - Iterator iter = data.iterator(); - - while (iter.hasNext()) { - E element = iter.next(); - String s = element.toString(); - - if (valueMap.containsKey(s)) { - throw new IllegalArgumentException( - "multiple constants map to one string value"); - } - - valueMap.put(s, element); - } - } - - /** - * {@inheritDoc} - */ - @Override - @SuppressWarnings("unchecked") - public void setSelectedItem(Object anItem) { - E input = null; - - if (enumClass.isInstance(anItem)) { - input = (E) anItem; - } else { - input = valueMap.get(anItem); - } - - if (input != null || anItem == null) { - selected = input; - } - - this.fireContentsChanged(this, 0, getSize()); - } - - /* - public static void main(String[] args) { - JFrame frame = new JFrame(); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - frame.setLayout(new BoxLayout(frame.getContentPane(),BoxLayout.Y_AXIS)); - - - JComboBox combo1 = new JComboBox(); - combo1.setModel(new EnumComboBoxModel(MyEnum1.class)); - frame.add(combo1); - - JComboBox combo2 = new JComboBox(); - combo2.setModel(new EnumComboBoxModel(MyEnum2.class)); - frame.add(combo2); - - EnumComboBoxModel enumModel = new EnumComboBoxModel(MyEnum1.class); - JComboBox combo3 = new JComboBox(); - combo3.setModel(enumModel); - frame.add(combo3); - - MyEnum1 selected = enumModel.getSelectedItem(); - - //uncomment to see the ClassCastException -// enumModel.setSelectedItem("Die clown"); - - frame.pack(); - frame.setVisible(true); - } - - private enum MyEnum1 {GoodStuff, BadStuff}; - private enum MyEnum2 {GoodStuff, BadStuff; - public String toString() { - switch(this) { - case GoodStuff: return "Some Good Stuff"; - case BadStuff: return "Some Bad Stuff"; - } - return "ERROR"; - } - }; - */ - -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/combobox/JXTextFieldComboBoxEditor.java b/src/main/java/org/jdesktop/swingx/combobox/JXTextFieldComboBoxEditor.java deleted file mode 100644 index 028ca4fe11..0000000000 --- a/src/main/java/org/jdesktop/swingx/combobox/JXTextFieldComboBoxEditor.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.jdesktop.swingx.combobox; - -import org.jdesktop.swingx.JXTextField; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.ActionListener; - -public class JXTextFieldComboBoxEditor implements ComboBoxEditor { - private JXTextField textField; - - public JXTextFieldComboBoxEditor() { - this(null); - } - - public JXTextFieldComboBoxEditor(String promptText) { - this(promptText, null); - } - - public JXTextFieldComboBoxEditor(String promptText, Color promptForeground) { - this(promptText, promptForeground, null); - } - - public JXTextFieldComboBoxEditor(String promptText, Color promptForeground, Color promptBackground) { - textField = new JXTextField(promptText, promptForeground, promptBackground); - textField.setBorder(BorderFactory.createEmptyBorder()); - textField.setOuterMargin(new Insets(0, 2, 0, 2)); - } - - @Override - public JXTextField getEditorComponent() { - return textField; - } - - @Override - public void setItem(Object anObject) { - textField.setText(anObject != null ? anObject.toString() : ""); - } - - @Override - public Object getItem() { - return textField.getText(); - } - - @Override - public void selectAll() { - textField.selectAll(); - } - - @Override - public void addActionListener(ActionListener l) { - textField.addActionListener(l); - } - - @Override - public void removeActionListener(ActionListener l) { - textField.removeActionListener(l); - } - -} diff --git a/src/main/java/org/jdesktop/swingx/combobox/ListComboBoxModel.java b/src/main/java/org/jdesktop/swingx/combobox/ListComboBoxModel.java deleted file mode 100644 index ca9366fbf8..0000000000 --- a/src/main/java/org/jdesktop/swingx/combobox/ListComboBoxModel.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * $Id: ListComboBoxModel.java 3935 2011-03-02 19:06:41Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.combobox; - -import javax.swing.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.List; - -/** - * A {@code ComboBoxModel} for {@code List}s. - * - * @param the type of elements maintained by the list backing this model - * - * @author jm158417 - * @author Karl George Schaefer - */ -public class ListComboBoxModel extends AbstractListModel implements ComboBoxModel, ActionListener { - /** - * A key used to notify the model that the backing {@code List} has changed. - */ - public static final String UPDATE = "update"; - - /** - * A reference to the list backing this model. - *

                    - * This model does not make a copy of the list, so any changes in - * the list without synchronizing the model may have drastic effects. - */ - protected final List data; - - /** - * The currently selected item. - */ - protected E selected; - - /** - * Creates a {@code ListComboBoxModel} backed by the supplied {@code list}. - * - * @param list - * the list backing this model - * @throws NullPointerException - * if {@code list} is {@code null} - */ - public ListComboBoxModel(List list) { - this.data = list; - - if(list.size() > 0) { - selected = list.get(0); - } - } - - /** - * Set the selected item. The implementation of this method should notify - * all registered {@code ListDataListener}s that the contents have changed. - * - * @param item - * the list object to select or {@code null} to clear the - * selection - * @throws ClassCastException - * if {@code item} is not of type {@code E} - */ - @Override - @SuppressWarnings("unchecked") - public void setSelectedItem(Object item) { - if ((selected != null && !selected.equals(item)) - || selected == null && item != null) { - selected = (E) item; - fireContentsChanged(this, -1, -1); - } - } - - /** - * {@inheritDoc} - */ - @Override - public E getSelectedItem() { - return this.selected; - } - - /** - * {@inheritDoc} - */ - @Override - public E getElementAt(int index) { - return data.get(index); - } - - /** - * {@inheritDoc} - */ - @Override - public int getSize() { - return data.size(); - } - - /** - * {@inheritDoc} - */ - @Override - public void actionPerformed(ActionEvent evt) { - if(evt.getActionCommand().equals(UPDATE)) { - this.fireContentsChanged(this, 0, getSize() - 1); - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/combobox/ListModelComboBoxWrapper.java b/src/main/java/org/jdesktop/swingx/combobox/ListModelComboBoxWrapper.java deleted file mode 100644 index 29af9b4e80..0000000000 --- a/src/main/java/org/jdesktop/swingx/combobox/ListModelComboBoxWrapper.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.jdesktop.swingx.combobox; - -import javax.swing.*; -import javax.swing.event.ListDataListener; - -public class ListModelComboBoxWrapper extends AbstractListModel implements ComboBoxModel { - private ListModel delegate; - - private Object selectedItem; - - public ListModelComboBoxWrapper(ListModel delegate) { - this.delegate = delegate; - } - - @Override - public int getSize() { - return delegate.getSize(); - } - - @Override - public Object getElementAt(int index) { - return delegate.getElementAt(index); - } - - @Override - public void addListDataListener(ListDataListener l) { - super.addListDataListener(l); - delegate.addListDataListener(l); - } - - @Override - public void removeListDataListener(ListDataListener l) { - delegate.removeListDataListener(l); - super.removeListDataListener(l); - } - - @Override - public void setSelectedItem(Object anItem) { - if ((selectedItem != null && !selectedItem.equals(anItem)) - || selectedItem == null && anItem != null) { - selectedItem = anItem; - - fireContentsChanged(this, -1, -1); - } - } - - @Override - public Object getSelectedItem() { - return selectedItem; - } -} diff --git a/src/main/java/org/jdesktop/swingx/combobox/MapComboBoxModel.java b/src/main/java/org/jdesktop/swingx/combobox/MapComboBoxModel.java deleted file mode 100644 index 7ee5daa0cc..0000000000 --- a/src/main/java/org/jdesktop/swingx/combobox/MapComboBoxModel.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * $Id: MapComboBoxModel.java 3751 2010-08-08 04:07:44Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.combobox; - -import java.awt.event.ActionEvent; -import java.util.*; - -/** - * A {@code ComboBoxModel} for {@code Map}s. The model will always present a {@code Map} - * consistently, once it is instantiated. However, unless the {@code Map} is ordered, as a - * {@code java.util.TreeMap} is, the model is not guaranteed to present the maps in a consistent - * order between instantiations. - * - * @author jm158417 - * @author Karl George Schaefer - * - * @param - * the type of keys maintained by the map backing this model - * @param - * the type of mapped values - */ -public class MapComboBoxModel extends ListComboBoxModel { - - /** - * The map backing this model. - */ - protected Map map_data; - - /** - * Creates an empty model. - */ - public MapComboBoxModel() { - this(new LinkedHashMap()); - } - - /** - * Creates a model backed by the specified map. - * - * @param map - * the map backing this model - */ - public MapComboBoxModel(Map map) { - super(buildIndex(map)); - - this.map_data = map; - } - - /** - * Builds an index for this model. This method ensures that the map is always presented in a - * consistent order. - *

                    - * This method is called by the constructor and the {@code List} is passed to {@code super}. - * - * @param - * the type of keys for the map - * @param map - * the map to build an index for - * @return a list containing the map keys - */ - private static List buildIndex(Map map) { - return new ArrayList(map.keySet()); - } - - /** - * {@inheritDoc} - */ - @Override - public int getSize() { - return map_data.size(); - } - - /** - * {@inheritDoc} - */ - @Override - public void actionPerformed(ActionEvent evt) { - //kgs - this code might not be performant with large maps - if(evt.getActionCommand().equals(UPDATE)) { - //add new keys - Set keys = map_data.keySet(); - keys.removeAll(data); - data.addAll(keys); - - //remove dead keys - List copy = new ArrayList(data); - keys = map_data.keySet(); - copy.removeAll(keys); - data.removeAll(copy); - - fireContentsChanged(this, 0, getSize() - 1); - } - } - - /** - * Selects an item from the model and returns that map value. - * - * @param selectedItem - * the item to select - * @return the value for the selected item - */ - public V getValue(Object selectedItem) { - return map_data.get(selectedItem); - } - - /** - * Selects an item from the model and returns that map value. - * - * @param selectedItem - * selects the item at the specified index in this model - * @return the value for the item at the selected index - */ - public V getValue(int selectedItem) { - return getValue(getElementAt(selectedItem)); - } -} diff --git a/src/main/java/org/jdesktop/swingx/combobox/package-info.java b/src/main/java/org/jdesktop/swingx/combobox/package-info.java deleted file mode 100644 index c7a09d8d58..0000000000 --- a/src/main/java/org/jdesktop/swingx/combobox/package-info.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -/** - * Contains classes and interfaces used by the {@code JComboBox} component. - */ -package org.jdesktop.swingx.combobox; - diff --git a/src/main/java/org/jdesktop/swingx/decorator/AbstractHighlighter.java b/src/main/java/org/jdesktop/swingx/decorator/AbstractHighlighter.java deleted file mode 100644 index 9b55bb257e..0000000000 --- a/src/main/java/org/jdesktop/swingx/decorator/AbstractHighlighter.java +++ /dev/null @@ -1,285 +0,0 @@ -/* - * $Id: AbstractHighlighter.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.decorator; - -import org.jdesktop.swingx.event.WeakEventListenerList; - -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import java.awt.*; - -/** - * Abstract Highlighter implementation which manages change - * notification and supports conditional highlighting. - * Subclasses are required to fire ChangeEvents on internal changes which might - * effect the highlight. The HighlightPredicate controls whether or not - * a highlight should be applied for the given ComponentAdapter, - * subclasses must guarantee to respect its decision. - *

                    - * - * Concrete custom implementations should focus on a single (or few) visual - * attribute to highlight. This allows easy re-use by composition. F.i. a custom - * FontHighlighter: - * - *

                    
                    - * public static class FontHighlighter extends AbstractHighlighter {
                    - * 
                    - *     private Font font;
                    - * 
                    - *     public FontHighlighter(HighlightPredicate predicate, Font font) {
                    - *         super(predicate);
                    - *         setFont(font);
                    - *     }
                    - * 
                    - *     @Override
                    - *     protected Component doHighlight(Component component,
                    - *             ComponentAdapter adapter) {
                    - *         component.setFont(font);
                    - *         return component;
                    - *     }
                    - *     
                    - *     public final void setFont(Font font) {
                    - *        if (equals(font, this.font)) return;
                    - *        this.font = font;
                    - *        fireStateChanged();
                    - *     }
                    - *     
                    - * 
                    - * }
                    - * 
                    - * 
                    - * - * Client code can combine the effect with a f.i. Color decoration, and use a - * shared HighlightPredicate to apply both for the same condition. - * - *
                    
                    - * HighlightPredicate predicate = new HighlightPredicate() {
                    - *     public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
                    - *         Object value = adapter.getFilteredValueAt(adapter.row, adapter.column);
                    - *         return (value instanceof Number) && ((Number) value).intValue() < 0;
                    - *     }
                    - * };
                    - * table.setHighlighters(
                    - *         new ColorHighlighter(predicate, Color.RED, null),
                    - *         new FontHighlighter(predicate, myBoldFont));
                    - * 
                    - * - * @author Jeanette Winzenburg - * - * @see HighlightPredicate - * @see org.jdesktop.swingx.renderer.ComponentProvider - */ -public abstract class AbstractHighlighter implements Highlighter { - - /** - * Only one ChangeEvent is needed per model instance since the - * event's only (read-only) state is the source property. The source - * of events generated here is always "this". - */ - private transient ChangeEvent changeEvent; - - /** The listeners waiting for model changes. */ - protected WeakEventListenerList listenerList = new WeakEventListenerList(); - - /** the HighlightPredicate to use. */ - private HighlightPredicate predicate; - - /** - * Instantiates a Highlighter with default HighlightPredicate. - * - * @see #setHighlightPredicate(HighlightPredicate) - */ - public AbstractHighlighter() { - this(null); - } - - /** - * Instantiates a Highlighter with the given - * HighlightPredicate.

                    - * - * @param predicate the HighlightPredicate to use. - * - * @see #setHighlightPredicate(HighlightPredicate) - */ - public AbstractHighlighter(HighlightPredicate predicate) { - setHighlightPredicate(predicate); - } - - /** - * Set the HighlightPredicate used to decide whether a cell should - * be highlighted. If null, sets the predicate to HighlightPredicate.ALWAYS. - * - * The default value is HighlightPredicate.ALWAYS. - * - * @param predicate the HighlightPredicate to use. - */ - public void setHighlightPredicate(HighlightPredicate predicate) { - if (predicate == null) { - predicate = HighlightPredicate.ALWAYS; - } - if (areEqual(predicate, getHighlightPredicate())) return; - this.predicate = predicate; - fireStateChanged(); - } - - /** - * Returns the HighlightPredicate used to decide whether a cell - * should be highlighted. Guaranteed to be never null. - * - * @return the HighlightPredicate to use, never null. - */ - public HighlightPredicate getHighlightPredicate() { - return predicate; - } - - //----------------------- implement predicate respecting highlight - - /** - * {@inheritDoc} - * - * This calls doHighlight to apply the decoration if both HighlightPredicate - * isHighlighted and canHighlight return true. Returns the undecorated component otherwise. - * - * @param component the cell renderer component that is to be decorated - * @param adapter the ComponentAdapter for this decorate operation - * - * @see #canHighlight(Component, ComponentAdapter) - * @see #doHighlight(Component, ComponentAdapter) - * @see #getHighlightPredicate() - */ - @Override - public Component highlight(Component component, ComponentAdapter adapter) { - if (canHighlight(component, adapter) && - getHighlightPredicate().isHighlighted(component, adapter)) { - component = doHighlight(component, adapter); - } - return component; - } - - /** - * Subclasses may override to further limit the highlighting based - * on Highlighter state, f.i. a PainterHighlighter can only be applied - * to PainterAware components.

                    - * - * This implementation returns true always. - * - * @param component - * @param adapter - * @return a boolean indication if the adapter can be highlighted based - * general state. This implementation returns true always. - */ - protected boolean canHighlight(Component component, ComponentAdapter adapter) { - return true; - } - - - /** - * Apply the highlights. - * - * @param component the cell renderer component that is to be decorated - * @param adapter the ComponentAdapter for this decorate operation - * - * @see #highlight(Component, ComponentAdapter) - */ - protected abstract Component doHighlight(Component component, - ComponentAdapter adapter); - - - /** - * Returns true if the to objects are either both null or equal - * each other. - * - * @param oneItem one item - * @param anotherItem another item - * @return true if both are null or equal other, false otherwise. - */ - protected boolean areEqual(Object oneItem, Object anotherItem) { - if ((oneItem == null) && (anotherItem == null)) return true; - if (anotherItem != null) { - return anotherItem.equals(oneItem); - } - return false; - } - - //------------------------ implement Highlighter change notification - - /** - * Adds a ChangeListener. ChangeListeners are - * notified after changes of any attribute. - * - * @param l the ChangeListener to add - * @see #removeChangeListener - */ - @Override - public final void addChangeListener(ChangeListener l) { - listenerList.add(ChangeListener.class, l); - } - - /** - * Removes a ChangeListenere. - * - * @param l the ChangeListener to remove - * @see #addChangeListener - */ - @Override - public final void removeChangeListener(ChangeListener l) { - listenerList.remove(ChangeListener.class, l); - } - - /** - * Returns an array of all the change listeners - * registered on this Highlighter. - * - * @return all of this model's ChangeListeners - * or an empty - * array if no change listeners are currently registered - * - * @see #addChangeListener - * @see #removeChangeListener - * - * @since 1.4 - */ - @Override - public final ChangeListener[] getChangeListeners() { - return listenerList.getListeners(ChangeListener.class); - } - - /** - * Notifies registered ChangeListeners about - * state changes.

                    - * - * Note: subclasses should be polite and implement any property - * setters to fire only if the property is really changed. - * - */ - protected final void fireStateChanged() { - Object[] listeners = listenerList.getListenerList(); - for (int i = listeners.length - 2; i >= 0; i -= 2) { - if (listeners[i] == ChangeListener.class) { - if (changeEvent == null) { - changeEvent = new ChangeEvent(this); - } - ((ChangeListener) listeners[i + 1]).stateChanged(changeEvent); - } - } - } - -} diff --git a/src/main/java/org/jdesktop/swingx/decorator/AlignmentHighlighter.java b/src/main/java/org/jdesktop/swingx/decorator/AlignmentHighlighter.java deleted file mode 100644 index 84499f8798..0000000000 --- a/src/main/java/org/jdesktop/swingx/decorator/AlignmentHighlighter.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * $Id$ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.decorator; - -import javax.swing.*; -import java.awt.*; - -/** - * A Highlighter which sets the horizontal alignment. - * - * @author Jeanette Winzenburg (slight cleanup) - * @author original contributed by swingx member martinm1000 - */ -public class AlignmentHighlighter extends AbstractHighlighter { - private static final int defaultAlignment = SwingConstants.LEADING; - private int alignment; - - - /** - * Instantiates a AlignmentHighlighter with default alignment LEADING. The Highlighter is - * applied always. - */ - public AlignmentHighlighter() { - this(defaultAlignment); - } - - - /** - * Instantiates a AlignmentHighlighter with the specified alignment. The Highlighter is - * applied always. - * - * @param alignment the horizontal alignment to use. - * @throws IllegalArgumentException if not one of the constants allowed as horizontal alignment, - * that is one of LEADING, LEFT, CENTER, RIGHT, TRAILING - */ - public AlignmentHighlighter(int alignment) { - this(null, alignment); - } - - - /** - * Instantiates a FontHighlighter with the given HighlightPredicate and default - * horizontal alignement. - * - * @param predicate the HighlightPredicate to use, may be null to default to ALWAYS. - */ - public AlignmentHighlighter(HighlightPredicate predicate) { - this(predicate, defaultAlignment); - } - - - /** - * Instantiates a FontHighlighter with the given HighlightPredicate and null Font. - * - * @param predicate the HighlightPredicate to use, may be null to default to ALWAYS. - * @param alignment the horizontal alignment to use. - * @throws IllegalArgumentException if not one of the constants allowed as horizontal alignment, - * that is one of LEADING, LEFT, CENTER, RIGHT, TRAILING - */ - public AlignmentHighlighter(HighlightPredicate predicate, int alignment) { - super(predicate); - this.alignment = checkHorizontalAlignment(alignment); - } - - /** - * Returns the alignment which is applied. - * @return the alignment - */ - public int getHorizontalAlignment() { - return alignment; - } - - - /** - * Sets the horizontal alignment to apply. - * - * @param alignment the horizontal alignment to set - * @throws IllegalArgumentException if not one of the constants allowed as horizontal alignment, - * that is one of LEADING, LEFT, CENTER, RIGHT, TRAILING - */ - public void setHorizontalAlignment(int alignment) { - if (getHorizontalAlignment() == alignment) return; - this.alignment = checkHorizontalAlignment(alignment); - fireStateChanged(); - } - - - /** - * Checks if the horizontal alignment is valid. - * - * @param alignment the horizontal alignment to check - * @throws IllegalArgumentException if not one of the constants allowed as horizontal alignment, - * that is one of LEADING, LEFT, CENTER, RIGHT, TRAILING - */ - private int checkHorizontalAlignment(int alignment) { - if ((alignment == SwingConstants.LEFT) || - (alignment == SwingConstants.CENTER) || - (alignment == SwingConstants.RIGHT) || - (alignment == SwingConstants.LEADING) || - (alignment == SwingConstants.TRAILING)) { - return alignment; - } - else { - throw new IllegalArgumentException("invalid horizontal alignment, expected one of " - + SwingConstants.LEFT + " / " + SwingConstants.CENTER + - " / " + SwingConstants.RIGHT + " / " + SwingConstants.LEADING + - " / " + SwingConstants.TRAILING + " but was: " + alignment); - } - } - - /** - * {@inheritDoc}

                    - * - * Implemented to set the horizontal alignement of the rendering component. - */ - @Override - protected Component doHighlight(Component renderer, ComponentAdapter adapter) { - if (renderer instanceof JLabel) { - ((JLabel) renderer).setHorizontalAlignment(getHorizontalAlignment()); - } else if (renderer instanceof AbstractButton ) { - ((AbstractButton) renderer).setHorizontalAlignment(getHorizontalAlignment()); - } else { - ((JTextField) renderer).setHorizontalAlignment(getHorizontalAlignment()); - } - return renderer; - } - - /** - * {@inheritDoc}

                    - * - * Implemented to return true for components of type JLabel, AbstractButton or JTextField, - * false otherwise. - */ - @Override - protected boolean canHighlight(Component component, ComponentAdapter adapter) { - return (component instanceof JLabel) - || (component instanceof AbstractButton) - || (component instanceof JTextField) - ; - } -} diff --git a/src/main/java/org/jdesktop/swingx/decorator/BorderHighlighter.java b/src/main/java/org/jdesktop/swingx/decorator/BorderHighlighter.java deleted file mode 100644 index 032bc39575..0000000000 --- a/src/main/java/org/jdesktop/swingx/decorator/BorderHighlighter.java +++ /dev/null @@ -1,255 +0,0 @@ -/* - * $Id: BorderHighlighter.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.decorator; - -import javax.swing.*; -import javax.swing.border.Border; -import java.awt.*; - -/** - * A Highlighter that applies a border the the renderer component. - * - * The resulting border can be configured to - * - ignore the component's border, set this highlighter's border - * - compound of this highlighter's border and component border, with - * this highlighter's border either inner or outer. - * - * The default setting is compound outer. - * - */ -public class BorderHighlighter extends AbstractHighlighter { - - private Border paddingBorder; - private boolean inner; - private boolean compound; - - /** - * - * Instantiates a BorderHighlighter with no padding. The - * Highlighter is applied unconditionally. - * - */ - public BorderHighlighter() { - this((HighlightPredicate) null, null); - } - - /** - * - * Instantiates a BorderHighlighter with no padding, using the - * given predicate. - * - * @param predicate the HighlightPredicate to use - * - */ - public BorderHighlighter(HighlightPredicate predicate) { - this(predicate, null); - } - - - /** - * - * Instantiates a BorderHighlighter with the given padding. The - * Highlighter is applied unconditionally. - * - * @param paddingBorder the border to apply as visual decoration. - * - */ - public BorderHighlighter(Border paddingBorder) { - this(null, paddingBorder); - } - - /** - * - * Instantiates a BorderHighlighter with the given padding, - * HighlightPredicate and default compound property. - * If the predicate is null, the highlighter - * will be applied unconditionally. - * - * @param predicate the HighlightPredicate to use - * @param paddingBorder the border to apply as visual decoration. - * - */ - public BorderHighlighter(HighlightPredicate predicate, Border paddingBorder) { - this(predicate, paddingBorder, true); - } - - /** - * - * Instantiates a BorderHighlighter with the given padding, - * HighlightPredicate, compound property and default inner property. - * If the predicate is null, the highlighter - * will be applied unconditionally. - * - * @param predicate the HighlightPredicate to use - * @param paddingBorder the border to apply as visual decoration. - * @param compound the compound property. - * - */ - public BorderHighlighter(HighlightPredicate predicate, - Border paddingBorder, boolean compound) { - this(predicate, paddingBorder, compound, false); - } - - /** - * - * Instantiates a BorderHighlighter with the given padding, - * HighlightPredicate and compound property. If the predicate is null, the highlighter - * will be applied unconditionally. - * - * @param predicate the HighlightPredicate to use - * @param paddingBorder the border to apply as visual decoration. - * @param compound the compound property - * @param inner the inner property - */ - public BorderHighlighter(HighlightPredicate predicate, - Border paddingBorder, boolean compound, boolean inner) { - super(predicate); - this.paddingBorder = paddingBorder; - this.compound = compound; - this.inner = inner; - } - - - - - /** - * {@inheritDoc} - */ - @Override - protected Component doHighlight(Component renderer, ComponentAdapter adapter) { - ((JComponent) renderer).setBorder(compoundBorder( - ((JComponent) renderer).getBorder())); - return renderer; - } - - /** - * {@inheritDoc}

                    - * - * Overridden to prevent highlighting if there's no padding available or - * the renderer is not of type JComponent. - * - * @param component the cell renderer component that is to be decorated - * @param adapter the ComponentAdapter for this decorate operation - * @return true padding is available and the - * renderer is a JComponent. False otherwise. - */ - @Override - protected boolean canHighlight(Component component, ComponentAdapter adapter) { - return (getBorder() != null) && (component instanceof JComponent); - } - - /** - * Sets the compound property. If true, the highlight border will be compounded - * with the renderer's border, if any. Otherwise, the highlight border will - * replace the renderer's border.

                    - * - * The default value is true; - * - * @param compound a boolean indicating whether the highlight border should be - * compounded with the component's border. - */ - public void setCompound(boolean compound) { - if (isCompound() == compound) return; - this.compound = compound; - fireStateChanged(); - } - - /** - * - * @return the compound property. - * @see #setCompound(boolean) - */ - public boolean isCompound() { - return compound; - } - - /** - * Sets the inner property. If true/false and compounded is enabled - * the highlight border will be the inner/outer border of the compound. - * - * The default value is false; - * - * @param inner a boolean indicating whether the highlight border should be - * compounded as inner or outer border. - * - * @see #isInner() - */ - public void setInner(boolean inner) { - if (isInner() == inner) return; - this.inner = inner; - fireStateChanged(); - } - - /** - * Returns the inner property. - * - * @return the inner property. - * @see #setInner(boolean) - */ - public boolean isInner() { - return inner; - } - - /** - * Sets the Border used for highlighting.

                    - * - * The default value is null. - * - * @param padding the Border to use - */ - public void setBorder(Border padding) { - if (areEqual(padding, getBorder())) return; - this.paddingBorder = padding; - fireStateChanged(); - } - - - /** - * Returns the border used for highlighing.

                    - * - * @return the border used to highlight. - */ - public Border getBorder() { - return paddingBorder; - } - - - /** - * PRE: paddingBorder != null. - * @param border - * @return - */ - private Border compoundBorder(Border border) { - if (compound) { - if (border != null) { - if (inner) { - return BorderFactory.createCompoundBorder(border, - paddingBorder); - } - return BorderFactory.createCompoundBorder(paddingBorder, - border); - } - } - return paddingBorder; - } - -} - diff --git a/src/main/java/org/jdesktop/swingx/decorator/ColorHighlighter.java b/src/main/java/org/jdesktop/swingx/decorator/ColorHighlighter.java deleted file mode 100644 index 898bd92dde..0000000000 --- a/src/main/java/org/jdesktop/swingx/decorator/ColorHighlighter.java +++ /dev/null @@ -1,267 +0,0 @@ -/* - * $Id: ColorHighlighter.java 4082 2011-11-15 18:39:43Z kschaefe $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.decorator; - - -import java.awt.*; - -import static org.jdesktop.swingx.util.PaintUtils.blend; - -/** - * A Highlighter to modify component colors. - *

                    - * As of SwingX 1.6.1, {@code ColorHighlighter} now blends non-opaque colors. - * This will have little effect on previous users, who were likely to be - * using fully-opaque colors. If you are, however, supplying a non-opaque color - * and need it to be considered opaque, use {@link org.jdesktop.swingx.util.PaintUtils#removeAlpha(Color)}. - * - * @author Jeanette Winzenburg - * @author Karl Schaefer - */ -public class ColorHighlighter extends AbstractHighlighter { - - private Color background; - private Color foreground; - private Color selectedBackground; - private Color selectedForeground; - - /** - * Instantiates a ColorHighlighter with null colors and default - * HighlightPredicate. - */ - public ColorHighlighter() { - this(null); - } - - /** - * Instantiates a ColorHighlighter with null colors and uses the - * specified HighlightPredicate. - * - * @param predicate the HighlightPredicate to use. - */ - public ColorHighlighter(HighlightPredicate predicate) { - this(predicate, null, null); - } - - /** - * Constructs a ColorHighlighter with the specified - * background and foreground colors and null section colors. Uses - * the default predicate. - * - * @param cellBackground background color for unselected cell state - * @param cellForeground foreground color for unselected cell state - */ - public ColorHighlighter(Color cellBackground, Color cellForeground) { - this(null, cellBackground, cellForeground); - } - - /** - * Constructs a ColorHighlighter with the specified - * unselected colors and HighlightPredicate. - * Initializes selected colors to null. - * - * @param predicate the HighlightPredicate to use. - * @param cellBackground background color for unselected cell state - * @param cellForeground foreground color for unselected cell state - */ - public ColorHighlighter(HighlightPredicate predicate, Color cellBackground, - Color cellForeground) { - this(predicate, cellBackground, cellForeground, null, null); - } - - /** - * Constructs a ColorHighlighter with the specified - * background and foreground colors for unselected and selected cells. - * Uses the default HighlightPredicate. - * - * @param cellBackground background color for unselected cell state - * @param cellForeground foreground color for unselected cell state - * @param selectedBackground background color for selected cell state - * @param selectedForeground foreground color for selected cell state - */ - public ColorHighlighter(Color cellBackground, Color cellForeground, - Color selectedBackground, Color selectedForeground) { - this(null, cellBackground, cellForeground, selectedBackground, selectedForeground); - } - - - /** - * Constructs a ColorHighlighter with the specified colors - * and HighlightPredicate. - * - * @param predicate the HighlightPredicate to use. - * @param cellBackground background color for unselected cell state - * @param cellForeground foreground color for unselected cell state - * @param selectedBackground background color for selected cell state - * @param selectedForeground foreground color for selected cell state - */ - public ColorHighlighter(HighlightPredicate predicate, Color cellBackground, - Color cellForeground, Color selectedBackground, - Color selectedForeground) { - super(predicate); - this.background = cellBackground; - this.foreground = cellForeground; - this.selectedBackground = selectedBackground; - this.selectedForeground = selectedForeground; - } - - - - /** - * {@inheritDoc} - */ - @Override - protected Component doHighlight(Component renderer, ComponentAdapter adapter) { - applyBackground(renderer, adapter); - applyForeground(renderer, adapter); - return renderer; - } - - - /** - * Applies a suitable background for the renderer component within the - * specified adapter.

                    - * - * This implementation applies its background or selectedBackground color - * (depending on the adapter's selected state) if != null. - * Otherwise it does nothing. - * - * @param renderer the cell renderer component that is to be decorated - * @param adapter the ComponentAdapter for this decorate operation - */ - protected void applyBackground(Component renderer, ComponentAdapter adapter) { - Color color = adapter.isSelected() ? getSelectedBackground() : getBackground(); - - renderer.setBackground(blend(renderer.getBackground(), color)); - } - - /** - * Applies a suitable foreground for the renderer component within the - * specified adapter.

                    - * - * This implementation applies its foreground or selectedfForeground color - * (depending on the adapter's selected state) if != null. - * Otherwise it does nothing. - * - * @param renderer the cell renderer component that is to be decorated - * @param adapter the ComponentAdapter for this decorate operation - */ - protected void applyForeground(Component renderer, ComponentAdapter adapter) { - Color color = adapter.isSelected() ? getSelectedForeground() : getForeground(); - - renderer.setForeground(blend(renderer.getForeground(), color)); - } - - -//---------------------- state - - /** - * Returns the background color of this ColorHighlighter. - * - * @return the background color of this ColorHighlighter, - * or null, if no background color has been set - */ - public Color getBackground() { - return background; - } - - /** - * Sets the background color of this ColorHighlighter and - * notifies registered ChangeListeners. - * - * @param color the background color of this Highlighter, - * or null, to clear any existing background color - */ - public void setBackground(Color color) { - if (areEqual(color, getBackground())) return; - background = color; - fireStateChanged(); - } - - /** - * Returns the foreground color of this ColorHighlighter. - * - * @return the foreground color of this ColorHighlighter, - * or null, if no foreground color has been set - */ - public Color getForeground() { - return foreground; - } - - /** - * Sets the foreground color of this ColorHighlighter and notifies - * registered ChangeListeners. - * - * @param color the foreground color of this ColorHighlighter, - * or null, to clear any existing foreground color - */ - public void setForeground(Color color) { - if (areEqual(color, getForeground())) return; - foreground = color; - fireStateChanged(); - } - - /** - * Returns the selected background color of this ColorHighlighter. - * - * @return the selected background color of this ColorHighlighter, - * or null, if no selected background color has been set - */ - public Color getSelectedBackground() { - return selectedBackground; - } - - /** - * Sets the selected background color of this ColorHighlighter - * and notifies registered ChangeListeners. - * - * @param color the selected background color of this ColorHighlighter, - * or null, to clear any existing selected background color - */ - public void setSelectedBackground(Color color) { - if (areEqual(color, getSelectedBackground()))return; - selectedBackground = color; - fireStateChanged(); - } - - /** - * Returns the selected foreground color of this ColorHighlighter. - * - * @return the selected foreground color of this ColorHighlighter, - * or null, if no selected foreground color has been set - */ - public Color getSelectedForeground() { - return selectedForeground; - } - - /** - * Sets the selected foreground color of this ColorHighlighter and - * notifies registered ChangeListeners. - * - * @param color the selected foreground color of this ColorHighlighter, - * or null, to clear any existing selected foreground color - */ - public void setSelectedForeground(Color color) { - if (areEqual(color, getSelectedForeground())) return; - selectedForeground = color; - fireStateChanged(); - } -} diff --git a/src/main/java/org/jdesktop/swingx/decorator/ComponentAdapter.java b/src/main/java/org/jdesktop/swingx/decorator/ComponentAdapter.java deleted file mode 100644 index 3bc595133c..0000000000 --- a/src/main/java/org/jdesktop/swingx/decorator/ComponentAdapter.java +++ /dev/null @@ -1,567 +0,0 @@ -/* - * $Id: ComponentAdapter.java 4158 2012-02-03 18:29:40Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.decorator; - -import org.jdesktop.swingx.renderer.StringValues; - -import javax.swing.*; -import java.awt.*; - -/** - * Abstract base class for all component data adapter classes. A - * ComponentAdapter allows the decoration collaborators like f.i. - * {@link Highlighter} to interact with a {@link #target} component through a - * common API.

                    - * - * It has two aspects: - *

                      - *
                    • interact with the view state for the "current" cell. The row/column - * fields and the parameterless methods service this aspect. The coordinates are - * in view coordinate system. - *
                    • interact with the data of the component. The methods for this are those - * taking row/column indices as parameters. The coordinates are in model - * coordinate system. - *
                    - * - * Typically, application code is interested in the first aspect. An example is - * highlighting the background of a row in a JXTable based on the value of a - * cell in a specific column. The solution is to implement a custom - * HighlightPredicate which decides if a given cell should be highlighted and - * configure a ColorHighlighter with the predicate and an appropriate background - * color. - * - *
                    
                    - * HighlightPredicate feverWarning = new HighlightPredicate() {
                    - *     int temperatureColumn = 10;
                    - * 
                    - *     public boolean isHighlighted(Component component, ComponentAdapter adapter) {
                    - *         return hasFever(adapter.getValue(temperatureColumn));
                    - *     }
                    - * 
                    - *     private boolean hasFever(Object value) {
                    - *         if (!value instanceof Number)
                    - *             return false;
                    - *         return ((Number) value).intValue() > 37;
                    - *     }
                    - * };
                    - * 
                    - * Highlighter hl = new ColorHighlighter(feverWarning, Color.RED, null);
                    - * 
                    - * - * The adapter is responsible for mapping column and row coordinates. - * - * All input column indices are in model coordinates with exactly two - * exceptions: - *
                      - *
                    • {@link #column} in column view coordinates - *
                    • the mapping method {@link #convertColumnIndexToModel(int)} in view coordinates - *
                    - * - * All input row indices are in model coordinates with exactly four exceptions: - *
                      - *
                    • {@link #row} in row view coordinates - *
                    • the mapping method {@link #convertRowIndexToModel(int)} in view coordinates - *
                    • the getter for the filtered value {@link #getFilteredValueAt(int, int)} - * takes the row in view coordinates. - *
                    • the getter for the filtered string representation {@link #getFilteredStringAt(int, int)} - * takes the row in view coordinates. -*
                    - * - * - * PENDING JW: anything to gain by generics here?

                    - * PENDING JW: formally document that row/column coordinates must be valid in all methods taking - * model coordinates, that is 0<= row < getRowCount(). - * - * @author Ramesh Gupta - * @author Karl Schaefer - * @author Jeanette Winzenburg - * - * @see HighlightPredicate - * @see Highlighter - */ -public abstract class ComponentAdapter { - public static final Object DEFAULT_COLUMN_IDENTIFIER = "Column0"; - /** current row in view coordinates. */ - public int row = 0; - /** current column in view coordinates. */ - public int column = 0; - protected final JComponent target; - - /** - * Constructs a ComponentAdapter, setting the specified component as the - * target component. - * - * @param component target component for this adapter - */ - public ComponentAdapter(JComponent component) { - target = component; - } - - /** - * Returns the component which is this adapter's target. - * - * @return the component which is this adapter's target. - */ - public JComponent getComponent() { - return target; - } - -//---------------------------- accessing the target's model: column meta data - - /** - * Returns the column's display name (= headerValue) of the column - * at columnIndex in model coordinates. - * - * Used f.i. in SearchPanel to fill the field with the - * column name.

                    - * - * Note: it's up to the implementation to decide for which - * columns it returns a name - most will do so for the - * subset with isTestable = true. - * - * This implementation delegates to getColumnIdentifierAt and returns it's - * toString or null. - * - * @param columnIndex in model coordinates - * @return column name or null if not found - */ - public String getColumnName(int columnIndex) { - Object identifier = getColumnIdentifierAt(columnIndex); - return identifier != null ? identifier.toString() : null; - } - - - /** - * Returns logical identifier of the column at - * columnIndex in model coordinates. - * - * Note: it's up to the implementation to decide for which - * columns it returns an identifier - most will do so for the - * subset with isTestable = true.

                    - * - * This implementation returns DEFAULT_COLUMN_IDENTIFIER. - * - * PENDING JW: This method replaces the old getColumnIdentifier(int) - * which returned a String which is overly restrictive. - * The only way to gently replace this method was - * to add this with a different name - which makes this name suboptimal. - * Probably should rename again once the old has died out ;-) - * - * @param columnIndex in model coordinates, must be valid. - * @return the identifier of the column at columnIndex or null if it has none. - * @throws ArrayIndexOutOfBoundsException if columnIndex < 0 or columnIndex >= getColumnCount(). - * - * - * @see #getColumnIndex(Object) - */ - public Object getColumnIdentifierAt(int columnIndex) { - if ((columnIndex < 0) || (columnIndex >= getColumnCount())) { - throw new ArrayIndexOutOfBoundsException("invalid column index: " + columnIndex); - } - return DEFAULT_COLUMN_IDENTIFIER; - } - - /** - * Returns the column index in model coordinates for the logical identifier. - *

                    - * - * This implementation returns 0 if the identifier is the same as the one - * known identifier returned from getColumnIdentifierAt(0), or -1 otherwise. - * So subclasses with one column and a customizable identifier need not - * override. Subclasses which support multiple columns must override this as - * well to keep the contract as in (assuming that the lookup succeeded): - * - *

                    
                    -     *  Object id = getColumnIdentifierAt(index);
                    -     *  assertEquals(index, getColumnIndex(index);
                    -     *  // and the reverse 
                    -     *  int column = getColumnIndex(identifier);
                    -     *  assertEquals(identifier, getColumnIdentifierAt(column));
                    -     * 
                    - * - * - * @param identifier the column's identifier, must not be null - * @return the index of the column identified by identifier in model - * coordinates or -1 if no column with the given identifier is - * found. - * @throws NullPointerException if identifier is null. - * @see #getColumnIdentifierAt(int) - */ - public int getColumnIndex(Object identifier) { - if (identifier.equals(getColumnIdentifierAt(0))) { - return 0; - } - return -1; - } - - /** - * Returns true if the column should be included in testing.

                    - * - * Here: returns true if visible (that is modelToView gives a valid - * view column coordinate). - * - * @param column the column index in model coordinates - * @return true if the column should be included in testing - */ - public boolean isTestable(int column) { - return convertColumnIndexToView(column) >= 0; - } - - /** - * Returns the common class of all data column identified by the given - * column index in model coordinates.

                    - * - * This implementation returns Object.class. Subclasses should - * implement as appropriate. - * - * @return the common class of all data given column in model coordinates. - * - * @see #getColumnClass() - */ - public Class getColumnClass(int column) { - return Object.class; - } - - - /** - * Returns the common class of all data in the current column.

                    - * - * This implementation delegates to getColumnClass(int) with the current - * column converted to model coordinates. - * - * @return the common class of all data in the current column. - * @see #getColumnClass(int) - */ - public Class getColumnClass() { - return getColumnClass(convertColumnIndexToModel(column)); - } - - - -//---------------------------- accessing the target's model: meta data - /** - * Returns the number of columns in the target's data model. - * - * @return the number of columns in the target's data model. - */ - public int getColumnCount() { - return 1; // default for combo-boxes, lists, and trees - } - - /** - * Returns the number of rows in the target's data model. - * - * @return the number of rows in the target's data model. - */ - public int getRowCount() { - return 0; - } - -//---------------------------- accessing the target's model: data - /** - * Returns the value of the target component's cell identified by the - * specified row and column in model coordinates. - * - * @param row in model coordinates - * @param column in model coordinates - * @return the value of the target component's cell identified by the - * specified row and column - */ - public abstract Object getValueAt(int row, int column); - - /** - * Determines whether this cell is editable. - * - * @param row the row to query in model coordinates - * @param column the column to query in model coordinates - * @return true if the cell is editable, false - * otherwise - */ - public abstract boolean isCellEditable(int row, int column); - - - /** - * Returns the String representation of the value of the cell identified by this adapter. That is, - * for the at position (adapter.row, adapter.column) in view coordinates.

                    - * - * NOTE: this implementation assumes that view coordinates == model - * coordinates, that is simply calls getValueAt(this.row, this.column). It is - * up to subclasses to override appropriately is they support model/view - * coordinate transformation.

                    - * - * This implementation messages the StringValue.TO_STRING with the getValue, - * subclasses should re-implement and use the API appropriate for the target component type. - * - * @return the String representation of value of the cell identified by this adapter - * @see #getValueAt(int, int) - * @see #getFilteredValueAt(int, int) - * @see #getValue(int) - */ - public String getString() { - return getString(convertColumnIndexToModel(column)); - } - - /** - * Returns the String representation of the value of the cell identified by the current - * adapter row and the given column index in model coordinates.

                    - * - * @param modelColumnIndex the column index in model coordinates - * @return the String representation of the value of the cell identified by this adapter - * - * @see #getFilteredStringAt(int, int) - * @see #getString() - */ - public String getString(int modelColumnIndex) { - return getFilteredStringAt(row, modelColumnIndex); - } - - /** - * Returns the String representation of the filtered value of the cell identified by the row - * in view coordinate and the column in model coordinates.

                    - * - * Note: the asymetry of the coordinates is intentional - clients like - * Highlighters are interested in view values but might need to access - * non-visible columns for testing. While it is possible to access - * row coordinates different from the current (that is this.row) it is not - * safe to do so for row > this.row because the adapter doesn't allow to - * query the count of visible rows.

                    - * - * This implementation messages the StringValue.TO_STRING with the filteredValue, - * subclasses should re-implement and use the API appropriate for the target component type.

                    - * - * PENDING JW: what about null cell values? StringValue has a contract to return a - * empty string then, would that be okay here as well? - * - * @param row the row of the cell in view coordinates - * @param column the column of the cell in model coordinates. - * @return the String representation of the filtered value of the cell identified by the row - * in view coordinate and the column in model coordinates - */ - public String getFilteredStringAt(int row, int column) { - return getStringAt(convertRowIndexToModel(row), column); - } - - /** - * Returns the String representation of the value of the cell identified by the row - * specified row and column in model coordinates.

                    - * - * This implementation messages the StringValue.TO_STRING with the valueAt, - * subclasses should re-implement and use the api appropriate for the target component type.

                    - * - * @param row in model coordinates - * @param column in model coordinates - * @return the value of the target component's cell identified by the - * specified row and column - */ - public String getStringAt(int row, int column) { - return StringValues.TO_STRING.getString(getValueAt(row, column)); - } - - /** - * Returns the value of the cell identified by this adapter. That is, - * for the at position (adapter.row, adapter.column) in view coordinates.

                    - * - * NOTE: this implementation assumes that view coordinates == model - * coordinates, that is simply calls getValueAt(this.row, this.column). It is - * up to subclasses to override appropriately is they support model/view - * coordinate transformation. - * - * @return the value of the cell identified by this adapter - * @see #getValueAt(int, int) - * @see #getFilteredValueAt(int, int) - * @see #getValue(int) - */ - public Object getValue() { - return getValue(convertColumnIndexToModel(column)); - } - - - /** - * Returns the value of the cell identified by the current - * adapter row and the given column index in model coordinates.

                    - * - * @param modelColumnIndex the column index in model coordinates - * @return the value of the cell identified by this adapter - * @see #getValueAt(int, int) - * @see #getFilteredValueAt(int, int) - * @see #getValue(int) - */ - public Object getValue(int modelColumnIndex) { - return getFilteredValueAt(row, modelColumnIndex); - } - - /** - * Returns the filtered value of the cell identified by the row - * in view coordinate and the column in model coordinates. - * - * Note: the asymmetry of the coordinates is intentional - clients like - * Highlighters are interested in view values but might need to access - * non-visible columns for testing. While it is possible to access - * row coordinates different from the current (that is this.row) it is not - * safe to do so for row > this.row because the adapter doesn't allow to - * query the count of visible rows. - * - * @param row the row of the cell in view coordinates - * @param column the column of the cell in model coordinates. - * @return the filtered value of the cell identified by the row - * in view coordinate and the column in model coordinates - */ - public Object getFilteredValueAt(int row, int column) { - return getValueAt(convertRowIndexToModel(row), column); - } - - //----------------------- accessing the target's view state - - /** - * Returns the bounds of the cell identified by this adapter.

                    - * - * @return the bounds of the cell identified by this adapter - */ - public Rectangle getCellBounds() { - return target.getBounds(); - } - - /** - * Returns true if the cell identified by this adapter currently has focus. - * Otherwise, it returns false. - * - * @return true if the cell identified by this adapter currently has focus; - * Otherwise, return false - */ - public abstract boolean hasFocus(); - - /** - * Returns true if the cell identified by this adapter is currently selected. - * Otherwise, it returns false. - * - * @return true if the cell identified by this adapter is currently selected; - * Otherwise, return false - */ - public abstract boolean isSelected(); - - /** - * Returns {@code true} if the cell identified by this adapter is editable, - * {@code false} otherwise. - * - * @return {@code true} if the cell is editable, {@code false} otherwise - */ - public abstract boolean isEditable(); - - /** - * Returns true if the cell identified by this adapter is currently expanded. - * Otherwise, it returns false. For components that do not support - * hierarchical data, this method always returns true because the cells in - * such components can never be collapsed. - * - * @return true if the cell identified by this adapter is currently expanded; - * Otherwise, return false - */ - public boolean isExpanded() { - return true; // sensible default for JList and JTable - } - - /** - * Returns true if the cell identified by this adapter is a leaf node. - * Otherwise, it returns false. For components that do not support - * hierarchical data, this method always returns true because the cells in - * such components can never have children. - * - * @return true if the cell identified by this adapter is a leaf node; - * Otherwise, return false - */ - public boolean isLeaf() { - return true; // sensible default for JList and JTable - } - - /** - * Returns true if the cell identified by this adapter displays the hierarchical node. - * Otherwise, it returns false. For components that do not support - * hierarchical data, this method always returns false because the cells in - * such components can never have children. - * - * @return true if the cell identified by this adapter displays the hierarchical node; - * Otherwise, return false - */ - public boolean isHierarchical() { - return false; // sensible default for JList and JTable - } - - /** - * Returns the depth of this row in the hierarchy where the root is 0. For - * components that do not contain hierarchical data, this method returns 1. - * - * @return the depth for this adapter - */ - public int getDepth() { - return 1; // sensible default for JList and JTable - } - -//-------------------- cell coordinate transformations - - /** - * For target components that support multiple columns in their model, - * along with column reordering in the view, this method transforms the - * specified columnIndex from model coordinates to view coordinates. For all - * other types of target components, this method returns the columnIndex - * unchanged. - * - * @param columnModelIndex index of a column in model coordinates - * @return index of the specified column in view coordinates - */ - public int convertColumnIndexToView(int columnModelIndex) { - return columnModelIndex; // sensible default for JList and JTree - } - - /** - * For target components that support multiple columns in their model, along - * with column reordering in the view, this method transforms the specified - * columnIndex from view coordinates to model coordinates. For all other - * types of target components, this method returns the columnIndex - * unchanged. - * - * @param columnViewIndex index of a column in view coordinates - * @return index of the specified column in model coordinates - */ - public int convertColumnIndexToModel(int columnViewIndex) { - return columnViewIndex; // sensible default for JList and JTree - } - - /** - * Converts a row index in model coordinates to an index in view coordinates. - * - * @param rowModelIndex index of a row in model coordinates - * @return index of the specified row in view coordinates - */ - public int convertRowIndexToView(int rowModelIndex) { - return rowModelIndex; // sensible default for JTree - } - - /** - * Converts a row index in view coordinates to an index in model coordinates. - * - * @param rowViewIndex index of a row in view coordinates - * @return index of the specified row in model coordinates - */ - public int convertRowIndexToModel(int rowViewIndex) { - return rowViewIndex; // sensible default for JTree - } -} diff --git a/src/main/java/org/jdesktop/swingx/decorator/ComponentOrientationHighlighter.java b/src/main/java/org/jdesktop/swingx/decorator/ComponentOrientationHighlighter.java deleted file mode 100644 index d041898231..0000000000 --- a/src/main/java/org/jdesktop/swingx/decorator/ComponentOrientationHighlighter.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Created on 31.03.2011 - * - */ -package org.jdesktop.swingx.decorator; - -import java.awt.*; - -/** - * A Highlighter which applies the ComponentOrientation to the component. - * - * @author Jeanette Winzenburg, Berlin - */ -public class ComponentOrientationHighlighter extends AbstractHighlighter { - - private ComponentOrientation co; - - /** - * Instantiates a ComponentOrientationHighlighter with ComponentOrientation.LEFT_TO_RIGHT. - * The Highlighter is applied always. - */ - public ComponentOrientationHighlighter() { - this((HighlightPredicate) null); - } - - /** - * Instantiates a ComponentOrientationHighlighter with the given HighlightPredicate - * and ComponentOrientation.LEFT_TO_RIGHT. - * - * @param predicate the HighlightPredicate to use, may be null to default to ALWAYS. - */ - public ComponentOrientationHighlighter(HighlightPredicate predicate) { - this(predicate, null); - } - - /** - * Instantiates a ComponentOrientationHighlighter with the given ComponentOrientation. - * The Highlighter is applied always. - * - * @param co the ComponentOrientation to apply - */ - public ComponentOrientationHighlighter(ComponentOrientation co) { - this(null, co); - } - - /** - * Instantiates a ComponentOrientationHighlighter with the given ComponentOrientation and HighlightPredicate. - * - * @param predicate the HighlightPredicate to use, may be null to default to ALWAYS. - * @param co the ComponentOrientation to apply, may be null - */ - public ComponentOrientationHighlighter(HighlightPredicate predicate, - ComponentOrientation co) { - super(predicate); - setComponentOrientation(co); - } - - /** - * Returns the ComponentOrientation to apply. - * - * @return the ComponentOrientation to apply, guaranteed to be not null. - */ - public ComponentOrientation getComponentOrientation() { - return co; - } - - /** - * Sets the ComponentOrientation to apply. - * - * @param co the co to set, may be null to denote fallback to LEFT_TO_RIGHT - */ - public void setComponentOrientation(ComponentOrientation co) { - if (co == null) { - co = ComponentOrientation.LEFT_TO_RIGHT; - } - if (areEqual(this.co, co)) return; - this.co = co; - fireStateChanged(); - } - - /** - * @inherited

                    - * Implementated to decorate the given component with the ComponentOrientation. - */ - @Override - protected Component doHighlight(Component component, - ComponentAdapter adapter) { - component.applyComponentOrientation(getComponentOrientation()); - return component; - } - - -} diff --git a/src/main/java/org/jdesktop/swingx/decorator/CompoundHighlighter.java b/src/main/java/org/jdesktop/swingx/decorator/CompoundHighlighter.java deleted file mode 100644 index 82d8a89545..0000000000 --- a/src/main/java/org/jdesktop/swingx/decorator/CompoundHighlighter.java +++ /dev/null @@ -1,243 +0,0 @@ -/* - * $Id: CompoundHighlighter.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.decorator; - -import org.jdesktop.swingx.plaf.UIDependent; -import org.jdesktop.swingx.util.Contract; - -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import java.awt.*; -import java.util.ArrayList; -import java.util.List; - -/** - * A class which manages the lists of Highlighters. - * - * @see Highlighter - * - * @author Ramesh Gupta - * @author Jeanette Winzenburg - * - */ -public class CompoundHighlighter extends AbstractHighlighter - implements UIDependent { - public static final Highlighter[] EMPTY_HIGHLIGHTERS = new Highlighter[0]; - - protected List highlighters; - - /** the listener for changes in contained Highlighters. */ - private ChangeListener highlighterChangeListener; - - - /** - * Instantiates a CompoundHighlighter containing the given - * Highlighters. - * - * @param inList zero or more not-null Highlighters to manage by this - * CompoundHighlighter. - * @throws NullPointerException if array is null or array contains null values. - */ - public CompoundHighlighter(Highlighter... inList) { - this(null, inList); - } - - /** - * Instantiates a CompoundHighlighter with the given predicate containing the given - * Highlighters. - * - * @param predicate the highlightPredicate to use - * @param inList zero or more not-null Highlighters to manage by this - * CompoundHighlighter. - * @throws NullPointerException if array is null or array contains null values. - */ - public CompoundHighlighter(HighlightPredicate predicate, Highlighter... inList) { - super(predicate); - highlighters = new ArrayList(); - setHighlighters(inList); - } - - /** - * Sets the given - * Highlighters. - * - * @param inList zero or more not-null Highlighters to manage by this - * CompoundHighlighter. - * @throws NullPointerException if array is null or array contains null values. - */ - public void setHighlighters(Highlighter... inList) { - Contract.asNotNull(inList, "Highlighter must not be null"); - if (highlighters.isEmpty() && (inList.length == 0)) return; - removeAllHighlightersSilently(); - for (Highlighter highlighter : inList) { - addHighlighterSilently(highlighter, false); - } - fireStateChanged(); - } - - /** - * Removes all contained highlighters without firing an event. - * Deregisters the listener from all. - */ - private void removeAllHighlightersSilently() { - for (Highlighter highlighter : highlighters) { - highlighter.removeChangeListener(getHighlighterChangeListener()); - } - highlighters.clear(); - } - - /** - * Appends a highlighter to the pipeline. - * - * @param highlighter highlighter to add - * @throws NullPointerException if highlighter is null. - */ - public void addHighlighter(Highlighter highlighter) { - addHighlighter(highlighter, false); - } - - /** - * Adds a highlighter to the pipeline. - * - * PENDING: Duplicate inserts? - * - * @param highlighter highlighter to add - * @param prepend prepend the highlighter if true; false will append - * @throws NullPointerException if highlighter is null. - */ - public void addHighlighter(Highlighter highlighter, boolean prepend) { - addHighlighterSilently(highlighter, prepend); - fireStateChanged(); - } - - private void addHighlighterSilently(Highlighter highlighter, boolean prepend) { - Contract.asNotNull(highlighter, "Highlighter must not be null"); - if (prepend) { - highlighters.add(0, highlighter); - } else { - highlighters.add(highlighters.size(), highlighter); - } - updateUI(highlighter); - highlighter.addChangeListener(getHighlighterChangeListener()); - } - - /** - * Removes a highlighter from the pipeline. - * - * - * @param hl highlighter to remove - */ - public void removeHighlighter(Highlighter hl) { - boolean success = highlighters.remove(hl); - if (success) { - // PENDING: duplicates? - hl.removeChangeListener(getHighlighterChangeListener()); - fireStateChanged(); - } - // should log if this didn't succeed. Maybe - } - - /** - * Returns an array of contained Highlighters. - * - * @return the contained Highlighters, might be empty but never null. - */ - public Highlighter[] getHighlighters() { - if (highlighters.isEmpty()) return EMPTY_HIGHLIGHTERS; - return highlighters.toArray(new Highlighter[highlighters.size()]); - } - -//--------------------- implement UIDependent - - /** - * {@inheritDoc}

                    - * - * Implemented to call updateUI on contained Highlighters. - */ - @Override - public void updateUI() { - for (Highlighter highlighter : highlighters) { - updateUI(highlighter); - } - } - - /** - * Returns the ChangeListner to contained - * Highlighters. The listener is lazily created. - * - * @return the listener for contained highlighters, guaranteed - * to be not null. - */ - protected ChangeListener getHighlighterChangeListener() { - if (highlighterChangeListener == null) { - highlighterChangeListener = createHighlighterChangeListener(); - } - return highlighterChangeListener; - } - - /** - * Creates and returns the ChangeListener registered to - * contained Highlighters. Here: fires a - * stateChanged on each notification. - * - * @return the listener for contained Highlighters. - * - */ - protected ChangeListener createHighlighterChangeListener() { - return highlighterChangeListener = new ChangeListener() { - - @Override - public void stateChanged(ChangeEvent e) { - fireStateChanged(); - } - - }; - } - - /** - * Updates the ui-dependent state of the given Highlighter. - * - * @param hl the highlighter to update. - */ - private void updateUI(Highlighter hl) { - if (hl instanceof UIDependent) { - ((UIDependent) hl).updateUI(); - } - } - - -//------------------- implement Highlighter - - /** - * {@inheritDoc} - */ - @Override - protected Component doHighlight(Component stamp, ComponentAdapter adapter) { - for (Highlighter highlighter : highlighters) { - stamp = highlighter.highlight(stamp, adapter); - } - return stamp; - } - - - -} diff --git a/src/main/java/org/jdesktop/swingx/decorator/EnabledHighlighter.java b/src/main/java/org/jdesktop/swingx/decorator/EnabledHighlighter.java deleted file mode 100644 index fad173128b..0000000000 --- a/src/main/java/org/jdesktop/swingx/decorator/EnabledHighlighter.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * $Id$ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.decorator; - -import java.awt.*; - -/** - * A Highlighter which sets the enabled property.

                    - * - * Note: the enabled is a mutable property of this Highlighter which defaults to false - * because we assume that's the most common use case to make a rendering component - * look disabled when the parent is enabled. It's mutable for symmetry reasons, though - * the other way round - enabled looking rendering component on a disabled parent - - * most probably will confuse users. - * - * @author Jeanette Winzenburg (slight cleanup) - * @author original contributed by swingx member martinm1000 - */ -public class EnabledHighlighter extends AbstractHighlighter { - - private boolean enabled; - - /** - * Instantiates a EnabledHighlighter with default enabled property (== false). - * The Highlighter is applied always. - */ - public EnabledHighlighter() { - this(null); - } - - /** - * Instantiates a EnabledHighlighter with the specified enabled property. - * The Highlighter is applied always. - * - * @param enabled the enabled property - */ - public EnabledHighlighter(boolean enabled) { - this(null, enabled); - } - - /** - * Instantiates a EnabledHighlighter with the specified HighlightPredicate and - * default enabled property (== false). - * @param predicate the HighlightPredicate to use, may be null to default to ALWAYS. - */ - public EnabledHighlighter(HighlightPredicate predicate) { - this(predicate, false); - } - - /** - * Instantiates a EnabledHighlighter with the specified HighlightPredicate and - * default enabled property. - * @param predicate the HighlightPredicate to use, may be null to default to ALWAYS. - * @param enabled the enabled property - */ - public EnabledHighlighter(HighlightPredicate predicate, boolean enabled) { - super(predicate); - this.enabled = enabled; - } - - - /** - * Returns the enabled property. - * @return the enabled - */ - public boolean isEnabled() { - return enabled; - } - - /** - * Sets the enabled property. The default value is false. - * - * @param enabled the enabled to set - */ - public void setEnabled(boolean enabled) { - if (isEnabled() == enabled) return; - this.enabled = enabled; - fireStateChanged(); - } - - /** - * {@inheritDoc}

                    - * - * Implemented to set the rendering component's enabled property. - */ - @Override - protected Component doHighlight(Component renderer, ComponentAdapter adapter) { - renderer.setEnabled(enabled); - return renderer; - } - - -} diff --git a/src/main/java/org/jdesktop/swingx/decorator/FontHighlighter.java b/src/main/java/org/jdesktop/swingx/decorator/FontHighlighter.java deleted file mode 100644 index 36ea9d849e..0000000000 --- a/src/main/java/org/jdesktop/swingx/decorator/FontHighlighter.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * $Id: FontHighlighter.java 1164 2009-11-03 04:22:00Z kschaefe $ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.decorator; - -import java.awt.*; - -/** - * A Highlighter which sets the Font of the component.

                    - * - * @author Karl George Schaefer - * - */ -public class FontHighlighter extends AbstractHighlighter { - private Font font; - - /** - * Instantiates a FontHighlighter with null Font. The Highlighter is - * applied always. - */ - public FontHighlighter() { - this((HighlightPredicate) null); - } - - /** - * Instantiates a FontHighlighter with the given Font. The Highlighter is - * applied always. - * - * @param font the Font to apply - */ - public FontHighlighter(Font font) { - this(null, font); - } - - /** - * Instantiates a FontHighlighter with the given HighlightPredicate and null Font. - * - * @param predicate the HighlightPredicate to use, may be null to default to ALWAYS. - */ - public FontHighlighter(HighlightPredicate predicate) { - this(predicate, null); - } - - /** - * Instantiates a FontHighlighter with the given Font and HighlightPredicate. - * - * @param predicate the HighlightPredicate to use, may be null to default to ALWAYS. - * @param font the Font to apply, may be null - */ - public FontHighlighter(HighlightPredicate predicate, Font font) { - super(predicate); - this.font = font; - } - - /** - * Returns the Font used for decoration. - * - * @return the Font used for decoration - * - * @see #setFont(Font) - */ - public Font getFont() { - return font; - } - - /** - * Sets the Font used for decoration. May be null to not decorate. - * - * @param font the Font used for decoration, may be null to not decorate. - * - * @see #getFont() - */ - public void setFont(Font font) { - if (areEqual(font, getFont())) return; - this.font = font; - fireStateChanged(); - } - - /** - * {@inheritDoc}

                    - * - * Implemented to return false if the font property is null. - */ - @Override - protected boolean canHighlight(Component component, ComponentAdapter adapter) { - return font != null; - } - - /** - * {@inheritDoc}

                    - * - * Implemented to set the component's Font. - */ - @Override - protected Component doHighlight(Component component, ComponentAdapter adapter) { - component.setFont(font); - return component; - } -} diff --git a/src/main/java/org/jdesktop/swingx/decorator/HighlightPredicate.java b/src/main/java/org/jdesktop/swingx/decorator/HighlightPredicate.java deleted file mode 100644 index 1f8e90e9ee..0000000000 --- a/src/main/java/org/jdesktop/swingx/decorator/HighlightPredicate.java +++ /dev/null @@ -1,825 +0,0 @@ -/* - * $Id: HighlightPredicate.java 3935 2011-03-02 19:06:41Z kschaefe $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.decorator; - -import org.jdesktop.swingx.rollover.RolloverProducer; -import org.jdesktop.swingx.util.Contract; - -import javax.swing.*; -import java.awt.*; -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -/** - * A controller which decides whether or not a visual decoration should - * be applied to the given Component in the given ComponentAdapter state. - * This is a on/off decision only, the actual decoration is - * left to the AbstractHighlighter which typically respects this predicate.

                    - * - * Note: implementations should be immutable because Highlighters - * guarantee to notify listeners on any state change which might effect the highlight. - * They can't comply to that contract if predicate internal state changes under their - * feet. If dynamic predicate state is required, the safe alternative is to create - * and set a new predicate.

                    - * - * - * @author Jeanette Winzenburg - * - * @see AbstractHighlighter - */ -public interface HighlightPredicate { - - /** - * Returns a boolean to indicate whether the component should be - * highlighted.

                    - * - * Note: both parameters should be considered strictly read-only! - * - * @param renderer the cell renderer component that is to be decorated, - * must not be null - * @param adapter the ComponentAdapter for this decorate operation, - * most not be null - * @return a boolean to indicate whether the component should be highlighted. - */ - boolean isHighlighted(Component renderer, ComponentAdapter adapter); - - -//--------------------- implemented Constants - /** - * Unconditional true. - */ - public static final HighlightPredicate ALWAYS = new HighlightPredicate() { - - /** - * {@inheritDoc}

                    - * - * Implemented to return true always. - */ - @Override - public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { - return true; - } - - }; - - /** - * Unconditional false. - */ - public static final HighlightPredicate NEVER = new HighlightPredicate() { - - /** - * {@inheritDoc}

                    - * - * Implemented to return false always. - */ - @Override - public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { - return false; - } - - }; - - /** - * Rollover Row. - */ - public static final HighlightPredicate ROLLOVER_ROW = new HighlightPredicate() { - - /** - * @inheritDoc - * Implemented to return true if the adapter's component is enabled and - * the row of its rollover property equals the adapter's row, returns - * false otherwise. - * - * @see RolloverProducer - */ - @Override - public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { - if (!adapter.getComponent().isEnabled()) return false; - Point p = (Point) adapter.getComponent().getClientProperty( - RolloverProducer.ROLLOVER_KEY); - return p != null && p.y == adapter.row; - } - - }; - - /** - * Rollover Column. - */ - public static final HighlightPredicate ROLLOVER_COLUMN = new HighlightPredicate() { - - /** - * @inheritDoc - * Implemented to return true if the adapter's component is enabled and - * the column of its rollover property equals the adapter's columns, returns - * false otherwise. - * - * @see RolloverProducer - */ - @Override - public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { - if (!adapter.getComponent().isEnabled()) return false; - Point p = (Point) adapter.getComponent().getClientProperty( - RolloverProducer.ROLLOVER_KEY); - return p != null && p.x == adapter.column; - } - - }; - /** - * Rollover Cell. - */ - public static final HighlightPredicate ROLLOVER_CELL = new HighlightPredicate() { - - /** - * @inheritDoc - * Implemented to return true if the adapter's component is enabled and - * the column of its rollover property equals the adapter's columns, returns - * false otherwise. - * - * @see RolloverProducer - */ - @Override - public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { - if (!adapter.getComponent().isEnabled()) return false; - Point p = (Point) adapter.getComponent().getClientProperty( - RolloverProducer.ROLLOVER_KEY); - return p != null && p.y == adapter.row && p.x == adapter.column; - } - - }; - - /** - * Is editable. - */ - public static final HighlightPredicate EDITABLE = new HighlightPredicate() { - /** - * {@inheritDoc}

                    - * - * Implemented to return true is the given adapter isEditable, false otherwise. - */ - @Override - public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { - return adapter.isEditable(); - } - }; - - /** - * Convenience for read-only (same as !editable). - */ - public static final HighlightPredicate READ_ONLY = new HighlightPredicate() { - /** - * {@inheritDoc}

                    - * - * Implemented to return false is the given adapter isEditable, true otherwise. - */ - @Override - public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { - return !adapter.isEditable(); - } - }; - - /** - * Leaf predicate. - */ - public static final HighlightPredicate IS_LEAF = new HighlightPredicate() { - /** - * {@inheritDoc}

                    - * - * Implemented to return true if the given adapter isLeaf, false otherwise. - */ - @Override - public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { - return adapter.isLeaf(); - } - }; - - /** - * Folder predicate - convenience: same as !IS_LEAF. - */ - public static final HighlightPredicate IS_FOLDER = new HighlightPredicate() { - /** - * {@inheritDoc}

                    - * - * Implemented to return false if the given adapter isLeaf, true otherwise. - */ - @Override - public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { - return !adapter.isLeaf(); - } - }; - - /** - * Selected predicate. - */ - public static final HighlightPredicate IS_SELECTED = new HighlightPredicate() { - - @Override - public boolean isHighlighted(Component renderer, - ComponentAdapter adapter) { - return adapter.isSelected(); - } - - }; - - /** - * Determines if the displayed text is truncated. - * - * @author Karl Schaefer - */ - public static final HighlightPredicate IS_TEXT_TRUNCATED = new HighlightPredicate() { - @Override - public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { - JComponent c = renderer instanceof JComponent ? (JComponent) renderer : null; - String text = adapter.getString(); - Icon icon = null; - //defaults from JLabel - int verticalAlignment = SwingConstants.CENTER; - int horizontalAlignment = SwingConstants.LEADING; - int verticalTextPosition = SwingConstants.CENTER; - int horizontalTextPosition = SwingConstants.TRAILING; - int gap = 0; - - if (renderer instanceof JLabel) { - icon = ((JLabel) renderer).getIcon(); - gap = ((JLabel) renderer).getIconTextGap(); - } else if (renderer instanceof AbstractButton) { - icon = ((AbstractButton) renderer).getIcon(); - gap = ((AbstractButton) renderer).getIconTextGap(); - } - - Rectangle cellBounds = adapter.getCellBounds(); - if (c != null && c.getBorder() != null) { - Insets insets = c.getBorder().getBorderInsets(c); - cellBounds.width -= insets.left + insets.right; - cellBounds.height -= insets.top + insets.bottom; - } - - String result = SwingUtilities.layoutCompoundLabel(c, renderer - .getFontMetrics(renderer.getFont()), text, icon, verticalAlignment, - horizontalAlignment, verticalTextPosition, horizontalTextPosition, cellBounds, - new Rectangle(), new Rectangle(), gap); - - return !text.equals(result); - } - }; - - /** - * Focus predicate. - */ - public static final HighlightPredicate HAS_FOCUS = new HighlightPredicate() { - /** - * {@inheritDoc}

                    - * - * Implemented to return truw if the given adapter hasFocus, false otherwise. - */ - @Override - public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { - return adapter.hasFocus(); - } - }; - /** - * Even rows. - * - * PENDING: this is zero based (that is "really" even 0, 2, 4 ..), differing - * from the old AlternateRowHighlighter. - * - */ - public static final HighlightPredicate EVEN = new HighlightPredicate() { - - @Override - public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { - return adapter.row % 2 == 0; - } - - }; - - /** - * Odd rows. - * - * PENDING: this is zero based (that is 1, 3, 4 ..), differs from - * the old implementation which was one based? - * - */ - public static final HighlightPredicate ODD = new HighlightPredicate() { - - @Override - public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { - return !EVEN.isHighlighted(renderer, adapter); - } - - }; - - /** - * Negative BigDecimals. - */ - public static final HighlightPredicate BIG_DECIMAL_NEGATIVE = new HighlightPredicate() { - - @Override - public boolean isHighlighted(Component renderer, - ComponentAdapter adapter) { - return (adapter.getValue() instanceof BigDecimal) - && ((BigDecimal) adapter.getValue()).compareTo(BigDecimal.ZERO) < 0; - } - - }; - - /** - * Negative Number. - */ - public static final HighlightPredicate INTEGER_NEGATIVE = new HighlightPredicate() { - - @Override - public boolean isHighlighted(Component renderer, - ComponentAdapter adapter) { - return (adapter.getValue() instanceof Number) - && ((Number) adapter.getValue()).intValue() < 0; - } - - }; - - // PENDING: these general type empty arrays don't really belong here? - public static final HighlightPredicate[] EMPTY_PREDICATE_ARRAY = new HighlightPredicate[0]; - public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0]; - public static final Integer[] EMPTY_INTEGER_ARRAY = new Integer[0]; - -//----------------- logical implementations amongst HighlightPredicates - - /** - * Negation of a HighlightPredicate. - */ - public static class NotHighlightPredicate implements HighlightPredicate { - - private HighlightPredicate predicate; - - /** - * Instantiates a not against the given predicate. - * @param predicate the predicate to negate, must not be null. - * @throws NullPointerException if the predicate is null - */ - public NotHighlightPredicate(HighlightPredicate predicate) { - if (predicate == null) - throw new NullPointerException("predicate must not be null"); - this.predicate = predicate; - } - - /** - * {@inheritDoc} - * Implemented to return the negation of the given predicate. - */ - @Override - public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { - return !predicate.isHighlighted(renderer, adapter); - } - - /** - * @return the contained HighlightPredicate. - */ - public HighlightPredicate getHighlightPredicate() { - return predicate; - } - - } - - /** - * Ands a list of predicates. - */ - public static class AndHighlightPredicate implements HighlightPredicate { - - private List predicate; - - /** - * Instantiates a predicate which ands all given predicates. - * @param predicate zero or more not null predicates to and - * @throws NullPointerException if the predicate is null - */ - public AndHighlightPredicate(HighlightPredicate... predicate) { - this.predicate = Arrays.asList(Contract.asNotNull(predicate, "predicate must not be null")); - } - - /** - * Instantiates a predicate which ANDs all contained predicates. - * @param list a collection with zero or more not null predicates to AND - * @throws NullPointerException if the collection is null - */ - public AndHighlightPredicate(Collection list) { - this.predicate = new ArrayList(Contract.asNotNull(list, "predicate list must not be null")); - } - - /** - * {@inheritDoc} - * Implemented to return false if any of the contained predicates is - * false or if there are no predicates. - */ - @Override - public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { - for (HighlightPredicate hp : predicate) { - if (!hp.isHighlighted(renderer, adapter)) return false; - } - return !predicate.isEmpty(); - } - - /** - * @return the contained HighlightPredicates. - */ - public HighlightPredicate[] getHighlightPredicates() { - if (predicate.isEmpty()) return EMPTY_PREDICATE_ARRAY; - return predicate.toArray(new HighlightPredicate[predicate.size()]); - } - - - } - - /** - * Or's a list of predicates. - */ - public static class OrHighlightPredicate implements HighlightPredicate { - - private List predicate; - - /** - * Instantiates a predicate which ORs all given predicates. - * @param predicate zero or more not null predicates to OR - * @throws NullPointerException if the predicate is null - */ - public OrHighlightPredicate(HighlightPredicate... predicate) { - this.predicate = Arrays.asList(Contract.asNotNull(predicate, "predicate must not be null")); - } - - /** - * Instantiates a predicate which ORs all contained predicates. - * @param list a collection with zero or more not null predicates to OR - * @throws NullPointerException if the collection is null - */ - public OrHighlightPredicate(Collection list) { - this.predicate = new ArrayList(Contract.asNotNull(list, "predicate list must not be null")); - } - - /** - * {@inheritDoc} - * Implemented to return true if any of the contained predicates is - * true. - */ - @Override - public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { - for (HighlightPredicate hp : predicate) { - if (hp.isHighlighted(renderer, adapter)) return true; - } - return false; - } - /** - * @return all registered predicates - */ - public HighlightPredicate[] getHighlightPredicates() { - if (predicate.isEmpty()) return EMPTY_PREDICATE_ARRAY; - return predicate.toArray(new HighlightPredicate[predicate.size()]); - } - - } - -//------------------------ coordinates - - public static class RowGroupHighlightPredicate implements HighlightPredicate { - - private int linesPerGroup; - - /** - * Instantiates a predicate with the given grouping. - * - * @param linesPerGroup number of lines constituting a group, must - * be > 0 - * @throws IllegalArgumentException if linesPerGroup < 1 - */ - public RowGroupHighlightPredicate(int linesPerGroup) { - if (linesPerGroup < 1) - throw new IllegalArgumentException("a group contain at least 1 row, was: " + linesPerGroup); - this.linesPerGroup = linesPerGroup; - } - - /** - * {@inheritDoc} - * Implemented to return true if the adapter's row falls into a - * odd group number. - */ - @Override - public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { - // JW: oddness check is okay - adapter.row must be a valid view coordinate - return (adapter.row / linesPerGroup) % 2 == 1; - } - - /** - * - * @return the number of lines per group. - */ - public int getLinesPerGroup() { - return linesPerGroup; - } - - } - - /** - * A HighlightPredicate based on column index. - * - */ - public static class ColumnHighlightPredicate implements HighlightPredicate { - List columnList; - - /** - * Instantiates a predicate which returns true for the - * given columns in model coordinates. - * - * @param columns the columns to highlight in model coordinates. - */ - public ColumnHighlightPredicate(int... columns) { - columnList = new ArrayList(); - for (int i = 0; i < columns.length; i++) { - columnList.add(columns[i]); - } - } - - /** - * {@inheritDoc} - * - * This implementation returns true if the adapter's column - * is contained in this predicates list. - * - */ - @Override - public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { - int modelIndex = adapter.convertColumnIndexToModel(adapter.column); - return columnList.contains(modelIndex); - } - - /** - * PENDING JW: get array of int instead of Integer? - * - * @return the columns indices in model coordinates to highlight - */ - public Integer[] getColumns() { - if (columnList.isEmpty()) return EMPTY_INTEGER_ARRAY; - return columnList.toArray(new Integer[columnList.size()]); - } - - } - - - /** - * A HighlightPredicate based on column identifier. - * - */ - public static class IdentifierHighlightPredicate implements HighlightPredicate { - List columnList; - - /** - * Instantiates a predicate which returns true for the - * given column identifiers. - * - * @param columns the identitiers of the columns to highlight. - */ - public IdentifierHighlightPredicate(Object... columns) { - columnList = new ArrayList(); - for (int i = 0; i < columns.length; i++) { - columnList.add(columns[i]); - } - } - - /** - * {@inheritDoc} - * - * This implementation returns true if the adapter's column - * is contained in this predicates list. - * - */ - @Override - public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { - int modelIndex = adapter.convertColumnIndexToModel(adapter.column); - Object identifier = adapter.getColumnIdentifierAt(modelIndex); - return identifier != null ? columnList.contains(identifier) : false; - } - - /** - * @return the identifiers - */ - public Object[] getIdentifiers() { - if (columnList.isEmpty()) return EMPTY_OBJECT_ARRAY; - return columnList.toArray(new Object[0]); - } - - } - - - - /** - * A {@code HighlightPredicate} based on adapter depth. - * - * @author Karl Schaefer - */ - public static class DepthHighlightPredicate implements HighlightPredicate { - private List depthList; - - /** - * Instantiates a predicate which returns true for the - * given depths. - * - * @param depths the depths to highlight - */ - public DepthHighlightPredicate(int... depths) { - depthList = new ArrayList(); - for (int i = 0; i < depths.length; i++) { - depthList.add(depths[i]); - } - } - - /** - * {@inheritDoc} - * - * This implementation returns true if the adapter's depth is contained - * in this predicates list. - * - */ - @Override - public boolean isHighlighted(Component renderer, - ComponentAdapter adapter) { - int depth = adapter.getDepth(); - return depthList.contains(depth); - } - - /** - * @return array of numbers representing different depths - */ - public Integer[] getDepths() { - if (depthList.isEmpty()) return EMPTY_INTEGER_ARRAY; - return depthList.toArray(new Integer[depthList.size()]); - } - - } - - //--------------------- value testing - - /** - * Predicate testing the componentAdapter value against a fixed - * Object. - */ - public static class EqualsHighlightPredicate implements HighlightPredicate { - - private Object compareValue; - - /** - * Instantitates a predicate with null compare value. - * - */ - public EqualsHighlightPredicate() { - this(null); - } - /** - * Instantiates a predicate with the given compare value. - * PENDING JW: support array? - * @param compareValue the fixed value to compare the - * adapter against. - */ - public EqualsHighlightPredicate(Object compareValue) { - this.compareValue = compareValue; - } - - /** - * {@inheritDoc} - * - * Implemented to return true if the adapter value equals the - * this predicate's compare value. - */ - @Override - public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { - if (compareValue == null) return adapter.getValue() == null; - return compareValue.equals(adapter.getValue()); - } - - /** - * @return the value this predicate checks against. - */ - public Object getCompareValue() { - return compareValue; - } - - } - - /** - * Predicate testing the componentAdapter value type against a given - * Class. - */ - public static class TypeHighlightPredicate implements HighlightPredicate { - - private Class clazz; - - /** - * Instantiates a predicate with Object.clazz. This is essentially the - * same as testing the adapter's value against null. - * - */ - public TypeHighlightPredicate() { - this(Object.class); - } - - /** - * Instantiates a predicate with the given compare class.

                    - * - * PENDING JW: support array? - * - * @param compareValue the fixed class to compare the - * adapter value against, must not be null - * - * @throws NullPointerException if the class is null. - */ - public TypeHighlightPredicate(Class compareValue) { - this.clazz = Contract.asNotNull(compareValue, "compare class must not be null"); - } - - /** - * {@inheritDoc} - * - * Implemented to return true if the adapter value is an instance - * of this predicate's class type. - */ - @Override - public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { - return adapter.getValue() != null ? - clazz.isAssignableFrom(adapter.getValue().getClass()) : false; - } - - /** - * @return type of predicate compare class - */ - public Class getType() { - return clazz; - } - - } - - /** - * Predicate testing the componentAdapter column type against a given - * Class. - */ - public static class ColumnTypeHighlightPredicate implements HighlightPredicate { - - private Class clazz; - - /** - * Instantitates a predicate with Object.class.

                    - * - * PENDING JW: this constructor is not very useful ... concrete implementations of - * ComponentAdapter are required to return a not-null from their - * getColumnClass() methods). - * - */ - public ColumnTypeHighlightPredicate() { - this(Object.class); - } - - /** - * Instantitates a predicate with the given compare class. - * - * @param compareValue the fixed class to compare the - * adapter's column class against, must not be null - * - * @throws NullPointerException if the class is null. - * - */ - public ColumnTypeHighlightPredicate(Class compareValue) { - this.clazz = Contract.asNotNull(compareValue, "compare class must not be null"); - } - - /** - * @inheritDoc - * - * Implemented to return true if the adapter value is an instance - * of this predicate's class type. - */ - @Override - public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { - return clazz.isAssignableFrom(adapter.getColumnClass()); - } - - public Class getType() { - return clazz; - } - - } -} diff --git a/src/main/java/org/jdesktop/swingx/decorator/Highlighter.java b/src/main/java/org/jdesktop/swingx/decorator/Highlighter.java deleted file mode 100644 index f7a19cae4c..0000000000 --- a/src/main/java/org/jdesktop/swingx/decorator/Highlighter.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * $Id: Highlighter.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.decorator; - -import javax.swing.event.ChangeListener; -import java.awt.*; - -/** - * Highlighter provide a mechanism to modify visual attributes of - * cell rendering components. The mechanism is uniform across both rendered and - * rendering component types: it is the same for SwingX collection views - * (JXTable, JXList, JXTree/Table) and independent of the concrete component - * type used for rendering the cell. The view cell state is factored into a - * ComponentAdapter. - *

                    - * - * For example, in data visualization components that support multiple columns - * with potentially different types of data, a ColorHighlighter - * imparts the same background color consistently across all columns - * of the rendered component regardless of the actual cell renderer registered - * for any specific column. - *

                    - * - * The highlightable properties are basically defined by the renderer in use: - * only attributes the renderer guarantees to reset on every call are safe to - * alter. For SwingX renderering support these are listed in - * ComponentProvider. - * - * - * Implementations supporting mutable internal state which effects the - * decoration must notify its listeners about the change. Typically, the - * rendered component installs a listener to its Highlighters - * and triggeres a repaint on notification. - * - * @see ComponentAdapter - * @see org.jdesktop.swingx.renderer.ComponentProvider - * - * @author Ramesh Gupta - * @author Jeanette Winzenburg - */ -public interface Highlighter { - - /** - * Decorates the specified component for the given component - * adapter. - * - * @param renderer the cell rendering component that is to be decorated - * @param adapter the ComponentAdapter for this decorate operation - * @return the decorated cell rendering component - */ - Component highlight(Component renderer, ComponentAdapter adapter); - - /** - * Adds a ChangeListener which are - * notified after changes of any attribute. - * - * @param l the ChangeListener to add - * @see #removeChangeListener - */ - void addChangeListener(ChangeListener l); - - /** - * Removes a ChangeListener. - * - * @param l the ChangeListener to remove - * @see #addChangeListener - */ - void removeChangeListener(ChangeListener l); - - /** - * Returns an array of all the change listeners - * registered on this LegacyHighlighter. - * - * @return all of this model's ChangeListeners - * or an empty - * array if no change listeners are currently registered - * - * @see #addChangeListener - * @see #removeChangeListener - * - * @since 1.4 - */ - ChangeListener[] getChangeListeners(); - -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/decorator/HighlighterFactory.java b/src/main/java/org/jdesktop/swingx/decorator/HighlighterFactory.java deleted file mode 100644 index a1b0eb0ee5..0000000000 --- a/src/main/java/org/jdesktop/swingx/decorator/HighlighterFactory.java +++ /dev/null @@ -1,292 +0,0 @@ -/* - * $Id: HighlighterFactory.java 4082 2011-11-15 18:39:43Z kschaefe $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.decorator; - -import org.jdesktop.swingx.decorator.HighlightPredicate.NotHighlightPredicate; -import org.jdesktop.swingx.decorator.HighlightPredicate.RowGroupHighlightPredicate; -import org.jdesktop.swingx.plaf.LookAndFeelAddons; -import org.jdesktop.swingx.plaf.UIColorHighlighterAddon; -import org.jdesktop.swingx.plaf.UIDependent; -import org.jdesktop.swingx.util.PaintUtils; - -import javax.swing.*; -import java.awt.*; - -/** - * A Factory which creates common Highlighters.

                    - * - * PENDING JW: really need the alternate striping? That's how the - * old AlternateRowHighlighter did it, but feels a bit wrong to - * have one stripe hardcoded to WHITE. Would prefer to remove. - * - * @author Jeanette Winzenburg - */ -public final class HighlighterFactory { - private static Highlighter COMPUTED_FOREGROUND_HIGHLIGHTER = new AbstractHighlighter() { - @Override - protected Component doHighlight(Component component, ComponentAdapter adapter) { - component.setForeground(PaintUtils.computeForeground(component.getBackground())); - - return component; - } - }; - - /** - * Creates a highlighter that sets the foreground color to WHITE or BLACK by computing the best - * match based on the current background color. It is recommended that no background changing - * highlighters be added after this highlighter, lest the computation be incorrect. - * - * @return a highlighter that computes the appropriate foreground color - */ - public static Highlighter createComputedForegroundHighlighter() { - return COMPUTED_FOREGROUND_HIGHLIGHTER; - } - - /** - * Creates and returns a Highlighter which highlights every second row - * background with a color depending on the LookAndFeel. The rows between - * are not highlighted, that is typically, they will show the container's - * background. - * - * @return a Highlighter striping every second row background. - */ - public static Highlighter createSimpleStriping() { - ColorHighlighter hl = new UIColorHighlighter(HighlightPredicate.ODD); - return hl; - } - - /** - * Creates and returns a Highlighter which highlights every second row group - * background with a color depending on LF. The row groups between are not - * highlighted, that is typically, they will show the container's - * background. - * - * @param rowsPerGroup the number of rows in a group - * @return a Highlighter striping every second row group background. - */ - public static Highlighter createSimpleStriping(int rowsPerGroup) { - return new UIColorHighlighter(new RowGroupHighlightPredicate( - rowsPerGroup)); - } - - /** - * Creates and returns a Highlighter which highlights every second row - * background with the given color. The rows between are not highlighted - * that is typically, they will show the container's background. - * - * @param stripeBackground the background color for the striping. - * @return a Highlighter striping every second row background. - */ - public static Highlighter createSimpleStriping(Color stripeBackground) { - ColorHighlighter hl = new ColorHighlighter(HighlightPredicate.ODD, stripeBackground, null); - return hl; - } - - /** - * Creates and returns a Highlighter which highlights every second row group - * background with the given color. The row groups between are not - * highlighted, that is they typically will show the container's background. - * - * @param stripeBackground the background color for the striping. - * @param rowsPerGroup the number of rows in a group - * @return a Highlighter striping every second row group background. - */ - public static Highlighter createSimpleStriping(Color stripeBackground, - int rowsPerGroup) { - HighlightPredicate predicate = new RowGroupHighlightPredicate( - rowsPerGroup); - ColorHighlighter hl = new ColorHighlighter(predicate, stripeBackground, - null); - return hl; - } - - /** - * Creates and returns a Highlighter which highlights - * with alternate background. The first is Color.WHITE, the second - * with the color depending on LF. - * - * @return a Highlighter striping every second row background. - */ - public static Highlighter createAlternateStriping() { - ColorHighlighter first = new ColorHighlighter(HighlightPredicate.EVEN, Color.WHITE, null); - ColorHighlighter hl = new UIColorHighlighter(HighlightPredicate.ODD); - return new CompoundHighlighter(first, hl); - } - - /** - * Creates and returns a Highlighter which highlights - * with alternate background. the first Color.WHITE, the second - * with the color depending on LF. - * - * @param rowsPerGroup the number of rows in a group - * @return a Highlighter striping every second row group background. - */ - public static Highlighter createAlternateStriping(int rowsPerGroup) { - HighlightPredicate predicate = new RowGroupHighlightPredicate(rowsPerGroup); - ColorHighlighter first = new ColorHighlighter(new NotHighlightPredicate(predicate), Color.WHITE, null); - ColorHighlighter hl = new UIColorHighlighter(predicate); - return new CompoundHighlighter(first, hl); - } - - /** - * Creates and returns a Highlighter which highlights with - * alternating background, starting with the base. - * - * @param baseBackground the background color for the even rows. - * @param alternateBackground background color for odd rows. - * @return a Highlighter striping alternating background. - */ - public static Highlighter createAlternateStriping(Color baseBackground, Color alternateBackground) { - ColorHighlighter base = new ColorHighlighter(HighlightPredicate.EVEN, baseBackground, null); - ColorHighlighter alternate = new ColorHighlighter(HighlightPredicate.ODD, alternateBackground, null); - return new CompoundHighlighter(base, alternate); - } - - /** - * Creates and returns a Highlighter which highlights with - * alternating background, starting with the base. - * - * @param baseBackground the background color for the even rows. - * @param alternateBackground background color for odd rows. - * @param linesPerStripe the number of rows in a group - * @return a Highlighter striping every second row group background. - */ - public static Highlighter createAlternateStriping(Color baseBackground, Color alternateBackground, int linesPerStripe) { - HighlightPredicate predicate = new RowGroupHighlightPredicate(linesPerStripe); - ColorHighlighter base = new ColorHighlighter(new NotHighlightPredicate(predicate), baseBackground, null); - ColorHighlighter alternate = new ColorHighlighter(predicate, alternateBackground, null); - - return new CompoundHighlighter(base, alternate); - } - -//--------------------------- UI dependent - - /** - * A ColorHighlighter with UI-dependent background. - * - * PENDING JW: internally install a AND predicate to check for LFs - * which provide striping on the UI-Delegate level? - * - */ - public static class UIColorHighlighter extends ColorHighlighter - implements UIDependent { - - static { - LookAndFeelAddons.contribute(new UIColorHighlighterAddon()); - } - - - /** - * Instantiates a ColorHighlighter with LF provided unselected - * background and default predicate. All other colors are null. - * - */ - public UIColorHighlighter() { - this(null); - } - - - /** - * Instantiates a ColorHighlighter with LF provided unselected - * background and the given predicate. All other colors are null. - * @param odd the predicate to use - */ - public UIColorHighlighter(HighlightPredicate odd) { - super(odd, null, null); - updateUI(); - } - - - /** - * @inheritDoc - */ - @Override - public void updateUI() { - setBackground(getUIColor()); - } - - /** - * Looks up and returns the LF specific color to use for striping - * background highlighting. - * - * Lookup strategy: - *

                      - *
                    1. in UIManager for key = "UIColorHighlighter.stripingBackground", if null - *
                    2. use hard-coded HighlighterFactory.GENERIC_GREY - *
                    - * - * PENDING: fallback or not? - * - * @return the LF specific color for background striping. - */ - private Color getUIColor() { - Color color = null; - // JW: can't do - Nimbus stripes even rows (somewhere deep down the ui?) - //, SwingX stripes odd rows - // --> combined == no striping -// color = UIManager.getColor("Table.alternateRowColor"); - if (color == null) { - color = UIManager.getColor("UIColorHighlighter.stripingBackground"); - } - if (color == null) { - color = HighlighterFactory.GENERIC_GRAY; - } - return color; - } -// /** -// * this is a hack until we can think about something better! -// * we map all known selection colors to highlighter colors. -// * -// */ -// private void initColorMap() { -// colorMap = new HashMap(); -// // Ocean -// colorMap.put(new Color(184, 207, 229), new Color(230, 238, 246)); -// // xp blue -// colorMap.put(new Color(49, 106, 197), new Color(224, 233, 246)); -// // xp silver -// colorMap.put(new Color(178, 180, 191), new Color(235, 235, 236)); -// // xp olive -// colorMap.put(new Color(147, 160, 112), new Color(228, 231, 219)); -// // win classic -// colorMap.put(new Color(10, 36, 106), new Color(218, 222, 233)); -// // win 2k? -// colorMap.put(new Color(0, 0, 128), new Color(218, 222, 233)); -// // default metal -// colorMap.put(new Color(205, 205, 255), new Color(235, 235, 255)); -// // mac OS X -// colorMap.put(new Color(56, 117, 215), new Color(237, 243, 254)); -// -// } - - } - - /** predefined colors - from old alternateRow. */ - public final static Color BEIGE = new Color(245, 245, 220); - public final static Color LINE_PRINTER = new Color(0xCC, 0xCC, 0xFF); - public final static Color CLASSIC_LINE_PRINTER = new Color(0xCC, 0xFF, 0xCC); - public final static Color FLORAL_WHITE = new Color(255, 250, 240); - public final static Color QUICKSILVER = new Color(0xF0, 0xF0, 0xE0); - public final static Color GENERIC_GRAY = new Color(229, 229, 229); - public final static Color LEDGER = new Color(0xF5, 0xFF, 0xF5); - public final static Color NOTEPAD = new Color(0xFF, 0xFF, 0xCC); - -} diff --git a/src/main/java/org/jdesktop/swingx/decorator/IconHighlighter.java b/src/main/java/org/jdesktop/swingx/decorator/IconHighlighter.java deleted file mode 100644 index 1192d8554e..0000000000 --- a/src/main/java/org/jdesktop/swingx/decorator/IconHighlighter.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * $Id: IconHighlighter.java 4192 2012-06-27 19:20:56Z kschaefe $ - * - * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.decorator; - -import org.jdesktop.swingx.renderer.IconAware; - -import javax.swing.*; -import java.awt.*; - -/** - * Highlighter which decorates by setting the icon property of a JLabel.

                    - * - * Note: The limitation to JLabel icons (vs. covering AbstractButton as well) - * is intentional. Highlighters are allowed to touch only those properties of the - * rendering component which are guaranteed to be reset by the corresponding - * ComponentProvider, this implementation is safe enough - LabelProvider guarantees - * to reset both text and icon. On the other hand, CheckBoxProvider doesn't touch - * the icon (which is LAF depend), consequently this Highlighter must not touch - * it as well. Custom subclasses trying to cover AbstractButton - * must take care that their custom providers reset the icon property. - * - * @author Jeanette Winzenburg - * - * @see org.jdesktop.swingx.renderer.ComponentProvider - * @see org.jdesktop.swingx.renderer.LabelProvider - * @see org.jdesktop.swingx.renderer.CheckBoxProvider - */ -public class IconHighlighter extends AbstractHighlighter { - - private Icon icon; - - /** - * Instantiates a IconHighlighter with null Icon and default - * HighlightPredicate. - */ - public IconHighlighter() { - this((HighlightPredicate) null); - } - - /** - * Instantiates a IconHighlighter with null Icon the given predicate. - * - * @param predicate the HighlightPredicate to use. - */ - public IconHighlighter(HighlightPredicate predicate) { - this(predicate, null); - } - - - /** - * Instantiates a IconHighlighter with the specified Icon and default - * HighlightPredicate. - * - * @param icon the icon to use for decoration. - */ - public IconHighlighter(Icon icon) { - this(null, icon); - } - - - /** - * Instantiates a IconHighlighter with the specified Icon and - * HighlightPredicate. - * - * @param predicate the HighlightPredicate to use. - * @param icon the Icon to use for decoration. - */ - public IconHighlighter(HighlightPredicate predicate, Icon icon) { - super(predicate); - setIcon(icon); - } - - /** - * Sets the icon to use for decoration. A null icon indicates - * to not decorate.

                    - * - * The default value is null. - * - * @param icon the Icon to use for decoration, might be null. - */ - public void setIcon(Icon icon) { - if (areEqual(icon, getIcon())) return; - this.icon = icon; - fireStateChanged(); - } - - /** - * Returns the Icon used for decoration. - * - * @return icon the Icon used for decoration. - * @see #setIcon(Icon) - */ - public Icon getIcon() { - return icon; - } - - /** - * {@inheritDoc} - * - * Implemented to set the component's Icon property, if possible and - * this Highlighter's icon is not null. Does nothing if the decorating icon is null. - * @see #canHighlight(Component, ComponentAdapter) - * @see #setIcon(Icon) - */ - @Override - protected Component doHighlight(Component component, - ComponentAdapter adapter) { - if (getIcon() != null) { - if (component instanceof IconAware) { - ((IconAware) component).setIcon(getIcon()); - } else if (component instanceof JLabel) { - ((JLabel) component).setIcon(getIcon()); - } - } - return component; - } - - /** - * {@inheritDoc}

                    - * - * Overridden to return true if the component is of type IconAware or - * of type JLabel, false otherwise.

                    - * - * Note: special casing JLabel is for backward compatibility - application - * highlighting code which doesn't use the Swingx renderers would stop working - * otherwise. - */ - @Override - protected boolean canHighlight(Component component, ComponentAdapter adapter) { - return component instanceof IconAware || component instanceof JLabel; - } - - -} diff --git a/src/main/java/org/jdesktop/swingx/decorator/PainterHighlighter.java b/src/main/java/org/jdesktop/swingx/decorator/PainterHighlighter.java deleted file mode 100644 index 210e38d3f2..0000000000 --- a/src/main/java/org/jdesktop/swingx/decorator/PainterHighlighter.java +++ /dev/null @@ -1,253 +0,0 @@ -/* - * $Id: PainterHighlighter.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.decorator; - -import org.jdesktop.swingx.painter.AbstractPainter; -import org.jdesktop.swingx.painter.Painter; -import org.jdesktop.swingx.renderer.PainterAware; - -import java.awt.*; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; - -/** - * Highlighter implementation which uses a Painter to decorate the component. - *

                    - * - * As Painter implementations can be mutable and Highlighters have the - * responsibility to notify their own listeners about any changes which might - * effect the visuals, this class provides api to install/uninstall a listener - * to the painter, as appropriate. It takes care of Painters of type - * AbstractHighlighter by registering a PropertyChangeListener. Subclasses might - * override to correctly handle different types as well. - *

                    - * - * Subclasses might be implemented to change the Painter during the decoration - * process, which must not passed-on to the Highlighter's listeners. The default - * routing is controlled by a flag isAdjusting. This is set/reset in this - * implementation's highlight method to ease subclass' burden (and to keep - * backward compatibility with implementations preceding the introduction of the - * painter listener). That is, subclasses are free to change painter properties - * during the decoration. - *

                    - * - * As an example, a ValueBasedPainterHighlighter might safely change any painter - * property to decorate a component depending on content. - * - *

                    
                    - * @Override
                    - * protected Component doHighlight(Component renderer, ComponentAdapter adapter) {
                    - *      float end = getEndOfGradient((Number) adapter.getValue());
                    - *      RelativePainter painter = (RelativePainter) getPainter();
                    - *      painter.setXFraction(end);
                    - *      ((PainterAware) renderer).setPainter(painter);
                    - *      return renderer;
                    - * }
                    - * 
                    - * @Override
                    - * protected boolean canHighlight(Component renderer, ComponentAdapter adapter) {
                    - *     return super.canHighlight(renderer, adapter) &&
                    - *        (adapter.getValue() instanceof Number);
                    - * }
                    - * 
                    - * - * NOTE: this will change once the Painter api is stable. - * - * @author Jeanette Winzenburg - */ -public class PainterHighlighter extends AbstractHighlighter { - - /** The painter to use for decoration. */ - private Painter painter; - /** The listener registered with the Painter. */ - private PropertyChangeListener painterListener; - /** - * A flag indicating whether or not changes in the Painter - * should be passed-on to the Highlighter's ChangeListeners. - */ - private boolean isAdjusting; - - /** - * Instantiates a PainterHighlighter with null painter and - * default predicate. - */ - public PainterHighlighter() { - this(null, null); - } - /** - * Instantiates a PainterHighlighter with null painter which - * uses the given predicate. - * - * @param predicate the HighlightPredicate which controls the highlight - * application. - */ - public PainterHighlighter(HighlightPredicate predicate) { - this(predicate, null); - } - - /** - * Instantiates a PainterHighlighter with the given Painter and - * default predicate. - * - * @param painter the painter to use - */ - public PainterHighlighter(Painter painter) { - this(null, painter); - } - - /** - * Instantiates a PainterHighlighter with the given painter and - * predicate. - * @param predicate - * @param painter - */ - public PainterHighlighter(HighlightPredicate predicate, Painter painter) { - super(predicate); - setPainter(painter); - } - - - - /** - * Returns to Painter used in this Highlighter. - * - * @return the Painter used in this Highlighter, may be null. - */ - public Painter getPainter() { - return painter; - } - - /** - * Sets the Painter to use in this Highlighter, may be null. - * Un/installs the listener to changes painter's properties. - * - * @param painter the Painter to uses for decoration. - */ - public void setPainter(Painter painter) { - if (areEqual(painter, getPainter())) return; - uninstallPainterListener(); - this.painter = painter; - installPainterListener(); - fireStateChanged(); - } - - /** - * Installs a listener to the painter if appropriate. - * This implementation registers its painterListener if - * the Painter is of type AbstractPainter. - */ - protected void installPainterListener() { - if (getPainter() instanceof AbstractPainter) { - ((AbstractPainter) getPainter()).addPropertyChangeListener(getPainterListener()); - } - } - - /** - * Uninstalls a listener from the painter if appropriate. - * This implementation removes its painterListener if - * the Painter is of type AbstractPainter. - */ - protected void uninstallPainterListener() { - if (getPainter() instanceof AbstractPainter) { - ((AbstractPainter) getPainter()).removePropertyChangeListener(painterListener); - } - } - - - /** - * Lazyly creates and returns the property change listener used - * to listen to changes of the painter. - * - * @return the property change listener used to listen to changes - * of the painter. - */ - protected final PropertyChangeListener getPainterListener() { - if (painterListener == null) { - painterListener = createPainterListener(); - } - return painterListener; - } - - /** - * Creates and returns the property change listener used - * to listen to changes of the painter.

                    - * - * This implementation fires a stateChanged on receiving - * any propertyChange, if the isAdjusting flag is false. - * Otherwise does nothing. - * - * @return the property change listener used to listen to changes - * of the painter. - */ - protected PropertyChangeListener createPainterListener() { - PropertyChangeListener l = new PropertyChangeListener() { - - @Override - public void propertyChange(PropertyChangeEvent evt) { - if (isAdjusting) return; - fireStateChanged(); - } - - }; - return l; - } - - /** - * {@inheritDoc}

                    - * - * Overridden to set/reset the flag indicating whether or not - * painter's property changes should be passed on to the - * Highlighter's listener. - */ - @Override - public Component highlight(Component component, ComponentAdapter adapter) { - isAdjusting = true; - Component stamp = super.highlight(component, adapter); - isAdjusting = false; - return stamp; - } - - /** - * {@inheritDoc} - *

                    - * This implementation sets the painter if it is not null. Does nothing - * otherwise. - */ - @Override - protected Component doHighlight(Component component, - ComponentAdapter adapter) { - ((PainterAware) component).setPainter(painter); - return component; - } - - /** - * {@inheritDoc}

                    - * - * Overridden to return false if the Painter is null or the component is not - * of type PainterAware. - */ - @Override - protected boolean canHighlight(Component component, ComponentAdapter adapter) { - return getPainter() != null && (component instanceof PainterAware); - } - - -} diff --git a/src/main/java/org/jdesktop/swingx/decorator/PatternPredicate.java b/src/main/java/org/jdesktop/swingx/decorator/PatternPredicate.java deleted file mode 100644 index c06783834a..0000000000 --- a/src/main/java/org/jdesktop/swingx/decorator/PatternPredicate.java +++ /dev/null @@ -1,223 +0,0 @@ -/* - * $Id: PatternPredicate.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.decorator; - -import java.awt.*; -import java.util.regex.Pattern; - -/** - * Pattern based HighlightPredicate.

                    - * - * Turns on the highlight of a single or all columns of the current row if - * a match of the String representation of cell content against the given Pattern - * is found.

                    - * - * The match logic can be configured to either test - * one specific column in the current row or all columns. In the latter case - * the logic is the same as in RowFilters.GeneralFilter: the row is included - * if any of the cell contents in the row are matches.

                    - * - * - * @author Jeanette Winzenburg - */ -public class PatternPredicate implements HighlightPredicate { - public static final int ALL = -1; - - private int highlightColumn; - private int testColumn; - private Pattern pattern; - - /** - * Instantiates a Predicate with the given Pattern and testColumn index - * (in model coordinates) highlighting all columns. - * A column index of -1 is interpreted - * as "all". - * - * @param pattern the Pattern to test the cell value against - * @param testColumn the column index in model coordinates - * of the cell which contains the value to test against the pattern - */ - public PatternPredicate(Pattern pattern, int testColumn) { - this(pattern, testColumn, ALL); - } - - /** - * Instantiates a Predicate with the given Pattern testing against - * all columns and highlighting all columns. - * - * @param pattern the Pattern to test the cell value against - */ - public PatternPredicate(Pattern pattern) { - this(pattern, ALL, ALL); - } - - /** - * Instantiates a Predicate with the given Pattern and test-/decorate - * column index in model coordinates. A column index of -1 is interpreted - * as "all". - * - * - * @param pattern the Pattern to test the cell value against - * @param testColumn the column index in model coordinates - * of the cell which contains the value - * to test against the pattern - * @param decorateColumn the column index in model coordinates - * of the cell which should be - * decorated if the test against the value succeeds. - */ - public PatternPredicate(Pattern pattern, int testColumn, int decorateColumn) { - this.pattern = pattern; - this.testColumn = testColumn; - this.highlightColumn = decorateColumn; - } - - /** - * Instantiates a Predicate with the given Pattern testing against - * all columns and highlighting all columns. - * - * @param pattern the Pattern to test the cell value against - */ - public PatternPredicate(String pattern) { - this(pattern, ALL, ALL); - } - - /** - * Instantiates a Predicate with the given regex and test - * column index in model coordinates. The pattern string is compiled to a - * Pattern with flags 0. A column index of -1 is interpreted - * as "all". - * - * @param regex the regex string to test the cell value against - * @param testColumn the column index in model coordinates - * of the cell which contains the value - * to test against the pattern - */ - public PatternPredicate(String regex, int testColumn) { - this(regex, testColumn, ALL); - } - - - /** - * Instantiates a Predicate with the given regex and test-/decorate - * column index in model coordinates. The pattern string is compiled to a - * Pattern with flags 0. A column index of -1 is interpreted - * as "all". - * - * @param regex the regex string to test the cell value against - * @param testColumn the column index in model coordinates - * of the cell which contains the value - * to test against the pattern - * @param decorateColumn the column index in model coordinates - * of the cell which should be - * decorated if the test against the value succeeds. - */ - public PatternPredicate(String regex, int testColumn, int decorateColumn) { - this(Pattern.compile(regex), testColumn, decorateColumn); - } - - /** - * - * @inherited

                    - * - * Implemented to return true if the match of cell content's String representation - * against the Pattern if found and the adapter's view column maps to the - * decorateColumn/s. Otherwise returns false. - * - */ - @Override - public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { - if (isHighlightCandidate(adapter)) { - return test(adapter); - } - return false; - } - - /** - * Test the value. This is called only if the - * pre-check returned true, because accessing the - * value might be potentially costly - * @param adapter - * @return - */ - private boolean test(ComponentAdapter adapter) { - // single test column - if (testColumn >= 0) return testColumn(adapter, testColumn); - // test all - for (int column = 0; column < adapter.getColumnCount(); column++) { - boolean result = testColumn(adapter, column); - if (result) return true; - } - return false; - } - - /** - * @param adapter - * @param testColumn - * @return - */ - private boolean testColumn(ComponentAdapter adapter, int testColumn) { - if (!adapter.isTestable(testColumn)) - return false; - String value = adapter.getString(testColumn); - - if ((value == null) || (value.length() == 0)) { - return false; - } - return pattern.matcher(value).find(); - } - - /** - * A quick pre-check. - * @param adapter - * - * @return - */ - private boolean isHighlightCandidate(ComponentAdapter adapter) { - return (pattern != null) && - ((highlightColumn < 0) || - (highlightColumn == adapter.convertColumnIndexToModel(adapter.column))); - } - - /** - * - * @return returns the column index to decorate (in model coordinates) - */ - public int getHighlightColumn() { - return highlightColumn; - } - - /** - * - * @return returns the Pattern to test the cell value against - */ - public Pattern getPattern() { - return pattern; - } - - /** - * - * @return the column to use for testing (in model coordinates) - */ - public int getTestColumn() { - return testColumn; - } - -} diff --git a/src/main/java/org/jdesktop/swingx/decorator/ResetDTCRColorHighlighter.java b/src/main/java/org/jdesktop/swingx/decorator/ResetDTCRColorHighlighter.java deleted file mode 100644 index 5820101351..0000000000 --- a/src/main/java/org/jdesktop/swingx/decorator/ResetDTCRColorHighlighter.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * $Id: ResetDTCRColorHighlighter.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.decorator; - -import javax.swing.*; -import javax.swing.table.DefaultTableCellRenderer; -import java.awt.*; - - -/** - * This is a hack around DefaultTableCellRenderer color "memory", - * see Issue #258-swingx.

                    - * - * The issue is that the default has internal color management - * which is different from other types of renderers. The - * consequence of the internal color handling is that there's - * a color memory which must be reset somehow. The "old" hack around - * reset the xxColors of all types of renderers to the adapter's - * target XXColors, introducing #178-swingx (Highlighgters must not - * change any colors except those for which their color properties are - * explicitly set).

                    - * - * This hack limits the interference to renderers of type - * DefaultTableCellRenderer, applying a hacking highlighter which - * resets the renderers XXColors to a previously "memorized" - * color. Note that setting the color to null didn't have the desired - * effect.

                    - * - * PENDING: extend ColorHighlighter - */ - -public class ResetDTCRColorHighlighter extends ColorHighlighter { - - public ResetDTCRColorHighlighter() { - super(null, null); - } - - /** - * applies the memory hack for renderers of type DefaultTableCellRenderer, - * does nothing for other types. - * @param renderer the component to highlight - * @param adapter the renderee's component state. - */ - @Override - public Component highlight(Component renderer, ComponentAdapter adapter) { - //JW - // table renderers have different state memory as list/tree renderers - // without the null they don't unstamp! - // but... null has adversory effect on JXList f.i. - selection - // color is changed. This is related to #178-swingx: - // highlighter background computation is weird. - // - if (renderer instanceof DefaultTableCellRenderer) { - return super.highlight(renderer, adapter); - } - return renderer; - } - - @Override - protected void applyBackground(Component renderer, ComponentAdapter adapter) { - if (!adapter.isSelected()) { - Object colorMemory = ((JComponent) renderer).getClientProperty("rendererColorMemory.background"); - if (colorMemory instanceof ColorMemory) { - renderer.setBackground(((ColorMemory) colorMemory).color); - } else { - ((JComponent) renderer).putClientProperty("rendererColorMemory.background", new ColorMemory(renderer.getBackground())); - } - } - } - - @Override - protected void applyForeground(Component renderer, ComponentAdapter adapter) { - if (!adapter.isSelected()) { - Object colorMemory = ((JComponent) renderer).getClientProperty("rendererColorMemory.foreground"); - if (colorMemory instanceof ColorMemory) { - renderer.setForeground(((ColorMemory) colorMemory).color); - } else { - ((JComponent) renderer).putClientProperty("rendererColorMemory.foreground", new ColorMemory(renderer.getForeground())); - } - } - } - - private static class ColorMemory { - public ColorMemory(Color color) { - this.color = color; - } - - Color color; - } - - - -} diff --git a/src/main/java/org/jdesktop/swingx/decorator/SearchPredicate.java b/src/main/java/org/jdesktop/swingx/decorator/SearchPredicate.java deleted file mode 100644 index 4508c7d3ed..0000000000 --- a/src/main/java/org/jdesktop/swingx/decorator/SearchPredicate.java +++ /dev/null @@ -1,223 +0,0 @@ -/* - * $Id: SearchPredicate.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.decorator; - -import java.awt.*; -import java.util.regex.Pattern; - -/** - * Pattern based HighlightPredicate for searching. Highlights - * the current adapter cell if the value matches the pattern. - * The highlight scope can be limited to a certain column and - * row.

                    - * - * Note: this differs from PatternPredicate in that it is focused - * on the current cell (highlight coordinates == test coordinates) - * while the PatternPredicate can have separate test and highlight - * coordinates.

                    - * - * - * @author Jeanette Winzenburg - */ -public class SearchPredicate implements HighlightPredicate { - public static final int ALL = -1; - public static final String MATCH_ALL = ".*"; - private int highlightColumn; - private int highlightRow; // in view coordinates? - private Pattern pattern; - - /** - * Instantiates a Predicate with the given Pattern. - * All matching cells are highlighted. - * - * - * @param pattern the Pattern to test the cell value against - */ - public SearchPredicate(Pattern pattern) { - this(pattern, ALL, ALL); - } - - /** - * Instantiates a Predicate with the given Pattern. Highlighting - * is limited to matching cells in the given column. - * - * @param pattern the Pattern to test the cell value against - * @param column the column to limit the highlight to - */ - public SearchPredicate(Pattern pattern, int column) { - this(pattern, ALL, column); - } - - /** - * Instantiates a Predicate with the given Pattern. Highlighting - * is limited to matching cells in the given column and row. A - * value of -1 indicates all rows/columns.

                    - * - * Note: the coordinates are asymmetric - rows are in view- and - * column in model-coordinates - due to corresponding methods in - * ComponentAdapter. Hmm... no need to? This happens on the - * current adapter state which is view always, so could use view - * only? - * - * @param pattern the Pattern to test the cell value against - * @param row the row index in view coordinates to limit the - * highlight. - * @param column the column in model coordinates - * to limit the highlight to - */ - public SearchPredicate(Pattern pattern, int row, int column) { - this.pattern = pattern; - this.highlightColumn = column; - this.highlightRow = row; - } - - /** - * Instantiates a Predicate with a Pattern compiled from the given - * regular expression. - * All matching cells are highlighted. - * - * @param regex the regular expression to test the cell value against - */ - public SearchPredicate(String regex) { - this(regex, ALL, ALL); - - } - - /** - * Instantiates a Predicate with a Pattern compiled from the given - * regular expression. Highlighting - * is applied to matching cells in all rows, but only in the given column. A - * value of ALL indicates all columns.

                    - * - * @param regex the regular expression to test the cell value against - * @param column the column index in model coordinates to limit the highlight to - */ - public SearchPredicate(String regex, int column) { - this(regex, ALL, column); - } - - /** - * Instantiates a Predicate with a Pattern compiled from the given - * regular expression. Highlighting - * is limited to matching cells in the given column and row. A - * value of ALL indicates all rows/columns.

                    - * - * Note: the coordinates are asymmetric - rows are in view- and - * column in model-coordinates - due to corresponding methods in - * ComponentAdapter. Hmm... no need to? This happens on the - * current adapter state which is view always, so could use view - * only? - * - * @param regex the Pattern to test the cell value against - * @param row the row index in view coordinates to limit the - * highlight. - * @param column the column in model coordinates - * to limit the highlight to - */ - public SearchPredicate(String regex, int row, int column) { - // test against empty string - this((regex != null) && (regex.length() > 0) ? - Pattern.compile(regex) : null, row, column); - } - - /** - * - * @return returns the column index to decorate (in model coordinates) - */ - public int getHighlightColumn() { - return highlightColumn; - } - - /** - * - * @return returns the column index to decorate (in model coordinates) - */ - public int getHighlightRow() { - return highlightRow; - } - - /** - * - * @return returns the Pattern to test the cell value against - */ - public Pattern getPattern() { - return pattern; - } - - - /** - * {@inheritDoc} - */ - @Override - public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { - if (isHighlightCandidate(renderer, adapter)) { - return test(renderer, adapter); - } - return false; - } - - /** - * Test the value. This is called only if the - * pre-check returned true, because accessing the - * value might be potentially costly - * @param renderer - * @param adapter - * @return - */ - private boolean test(Component renderer, ComponentAdapter adapter) { - // PENDING JW: why convert here? we are focused on the adapter's cell - // looks like an oversight as of ol' days ;-) - int columnToTest = adapter.convertColumnIndexToModel(adapter.column); - String value = adapter.getString(columnToTest); - - if ((value == null) || (value.length() == 0)) { - return false; - } - return pattern.matcher(value).find(); - } - - /** - * A quick pre-check. - * - * @param renderer - * @param adapter - * @return - */ - private boolean isHighlightCandidate(Component renderer, ComponentAdapter adapter) { - if (!isEnabled()) return false; - if (highlightRow >= 0 && (adapter.row != highlightRow)) { - return false; - } - return - ((highlightColumn < 0) || - (highlightColumn == adapter.convertColumnIndexToModel(adapter.column))); - } - - private boolean isEnabled() { - Pattern pattern = getPattern(); - if (pattern == null || MATCH_ALL.equals(pattern.pattern())) { - return false; - } - return true; - } - - -} diff --git a/src/main/java/org/jdesktop/swingx/decorator/ShadingColorHighlighter.java b/src/main/java/org/jdesktop/swingx/decorator/ShadingColorHighlighter.java deleted file mode 100644 index 0666580b3a..0000000000 --- a/src/main/java/org/jdesktop/swingx/decorator/ShadingColorHighlighter.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * $Id: ShadingColorHighlighter.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.decorator; - -import java.awt.*; - -/** - * Experimental replacement of HierarchicalColumnHighligher. - * Darkens the component's background. - * - * @author Jeanette Winzenburg - */ -public class ShadingColorHighlighter extends ColorHighlighter { - - /** - * Instantiates a Highlighter with null colors using the default - * HighlightPredicate. - * - */ - public ShadingColorHighlighter() { - this(null); - } - - /** - * Instantiates a Highlighter with null colors using the specified - * HighlightPredicate. - * - * @param predicate the HighlightPredicate to use. - */ - public ShadingColorHighlighter(HighlightPredicate predicate) { - super(predicate, null, null); - } - - /** - * Applies a suitable background for the renderer component within the - * specified adapter.

                    - * - * This implementation applies its a darkened background to an unselected - * adapter. Does nothing for selected cells. - * - * @param renderer the cell renderer component that is to be decorated - * @param adapter the ComponentAdapter for this decorate operation - */ - @Override - protected void applyBackground(Component renderer, ComponentAdapter adapter) { - if (adapter.isSelected()) - return; - // PENDING JW: really? That would be applying a absolute color, instead - // of shading whatever the renderer has. - Color background = getBackground(); - if (background == null) { - background = renderer.getBackground(); - } - // Change to the following -// Color background = renderer.getBackground(); - if (background != null) { - renderer.setBackground(computeBackgroundSeed(background)); - } - } - - protected Color computeBackgroundSeed(Color seed) { - return new Color(Math.max((int) (seed.getRed() * 0.95), 0), Math.max( - (int) (seed.getGreen() * 0.95), 0), Math.max((int) (seed - .getBlue() * 0.95), 0)); - } - -} diff --git a/src/main/java/org/jdesktop/swingx/decorator/ToolTipHighlighter.java b/src/main/java/org/jdesktop/swingx/decorator/ToolTipHighlighter.java deleted file mode 100644 index 456664f262..0000000000 --- a/src/main/java/org/jdesktop/swingx/decorator/ToolTipHighlighter.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * $Id: ToolTipHighlighter.java 3676 2010-04-26 15:42:26Z kschaefe $ - * - * Copyright 2010 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.decorator; - -import org.jdesktop.swingx.renderer.StringValue; - -import javax.swing.*; -import java.awt.*; - -/** - * A highlighter for setting a tool tip on the component. - * - * @author kschaefer - */ -public class ToolTipHighlighter extends AbstractHighlighter { - private StringValue toolTipValue; - - /** - * Instantiates a ToolTipHighlighter with null StringValue. The Highlighter is - * applied always. - */ - public ToolTipHighlighter() { - this((HighlightPredicate) null); - } - - /** - * Instantiates a ToolTipHighlighter with the specified StringValue. The Highlighter is applied - * always. - * - * @param toolTipValue - * the StringValue used to create the tool tip - */ - public ToolTipHighlighter(StringValue toolTipValue) { - this(null, toolTipValue); - } - - /** - * Instantiates a ToolTipHighlighter with the specified HighlightPredicate and a null - * StringValue. - * - * @param predicate - * the HighlightPredicate to use, may be null to default to ALWAYS. - */ - public ToolTipHighlighter(HighlightPredicate predicate) { - this(predicate, null); - } - - /** - * Instantiates a ToolTipHighlighter with the specified HighlightPredicate and StringValue. - * - * @param predicate - * the HighlightPredicate to use, may be null to default to ALWAYS. - * @param toolTipValue - * the StringValue used to create the tool tip - */ - public ToolTipHighlighter(HighlightPredicate predicate, StringValue toolTipValue) { - super(predicate); - - this.toolTipValue = toolTipValue; - } - - /** - * Returns the StringValue used for decoration. - * - * @return the StringValue used for decoration - * - * @see #setToolTipValue(Font) - */ - public StringValue getToolTipValue() { - return toolTipValue; - } - - /** - * Sets the StringValue used for decoration. May be null to use default decoration. - * - * @param font the Font used for decoration, may be null to use default decoration. - * - * @see #getToolTipValue() - */ - public void setToolTipValue(StringValue toolTipValue) { - if (areEqual(toolTipValue, getToolTipValue())) return; - this.toolTipValue = toolTipValue; - fireStateChanged(); - } - - /** - * {@inheritDoc}

                    - * - * Implemented to return false if the component is not a JComponent. - */ - @Override - protected boolean canHighlight(Component component, ComponentAdapter adapter) { - return component instanceof JComponent; - } - - /** - * {@inheritDoc} - */ - @Override - protected Component doHighlight(Component component, ComponentAdapter adapter) { - String toolTipText = null; - - if (toolTipValue == null) { - toolTipText = adapter.getString(); - } else { - toolTipText = toolTipValue.getString(adapter.getValue()); - } - - ((JComponent) component).setToolTipText(toolTipText); - - return component; - } -} diff --git a/src/main/java/org/jdesktop/swingx/decorator/package-info.java b/src/main/java/org/jdesktop/swingx/decorator/package-info.java deleted file mode 100644 index b110c4506b..0000000000 --- a/src/main/java/org/jdesktop/swingx/decorator/package-info.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -/** - * Contains API used to implement coordinated sorting, filtering - * and highlighting of the extended Swing cell-rendering component - * classes JXTable, JXTreeTable, JXTree, and JXList.

                    - * - * For details, see - * - * SwingX Highlighter Basics (in the wiki) .

                    - * - * PENDING JW: describe here instead of linking ;-) - */ -package org.jdesktop.swingx.decorator; - diff --git a/src/main/java/org/jdesktop/swingx/error/ErrorEvent.java b/src/main/java/org/jdesktop/swingx/error/ErrorEvent.java deleted file mode 100644 index aef5927a02..0000000000 --- a/src/main/java/org/jdesktop/swingx/error/ErrorEvent.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * $Id: ErrorEvent.java 2979 2008-07-08 01:32:06Z kschaefe $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.error; - -import java.util.EventObject; - -/** - * Defines an event which encapsulates an error which occurred in a JX Swing component - * which supports ErrorListeners. - * - * @author Joshua Marinacci joshua.marinacci@sun.com - * @see ErrorListener - * @see ErrorSupport - */ -public class ErrorEvent extends EventObject { - private Throwable throwable; - - /** - * Creates a new instance of ErrorEvent - * @param throwable The Error or Exception which occurred. - * @param source The object which threw the Error or Exception - */ - public ErrorEvent(Throwable throwable, Object source) { - super(source); - this.throwable = throwable; - } - - /** - * Gets the Error or Exception which occurred. - * @return The Error or Exception which occurred. - */ - public Throwable getThrowable() { - return throwable; - } - -} diff --git a/src/main/java/org/jdesktop/swingx/error/ErrorInfo.java b/src/main/java/org/jdesktop/swingx/error/ErrorInfo.java deleted file mode 100644 index 43260500c3..0000000000 --- a/src/main/java/org/jdesktop/swingx/error/ErrorInfo.java +++ /dev/null @@ -1,259 +0,0 @@ -/* - * $Id: ErrorInfo.java 4170 2012-02-21 14:27:15Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.error; - -import javax.swing.*; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; -import java.util.logging.Level; - -/** - *

                    A simple class that encapsulates all the information needed - * to report a problem using the automated report/processing system.

                    - * - *

                    All HTML referred to in this API refers to version 3.2 of the HTML - * markup specification.

                    - * - * @status REVIEWED - * @author Alexander Zuev - * @author rbair - */ -public class ErrorInfo { - /** - * Short string that will be used as a error title - */ - private String title; - /** - * Basic message that describes incident - */ - private String basicErrorMessage; - /** - * Message that will fully describe the incident with all the - * available details - */ - private String detailedErrorMessage; - /** - * A category name, indicating where in the application this incident - * occurred. It is recommended that this be the same value as you - * would use when logging. - */ - private String category; - /** - * Optional Throwable that will be used as a possible source for - * additional information - */ - private Throwable errorException; - /** - * Used to specify how bad this error was. - */ - private Level errorLevel; - /** - * A Map which captures the state of the application - * at the time of an exception. This state is then available for error - * reports. - */ - private Map state; - - /** - * Creates a new ErrorInfo based on the provided data. - * - * @param title used as a quick reference for the - * error (for example, it might be used as the - * title of an error dialog or as the subject of - * an email message). May be null. - * - * @param basicErrorMessage short description of the problem. May be null. - * - * @param detailedErrorMessage full description of the problem. It is recommended, - * though not required, that this String contain HTML - * to improve the look and layout of the detailed - * error message. May be null. - * - * @param category A category name, indicating where in the application - * this incident occurred. It is recommended that - * this be the same value as you would use when logging. - * May be null. - * - * @param errorException Throwable that can be used as a - * source for additional information such as call - * stack, thread name, etc. May be null. - * - * @param errorLevel any Level (Level.SEVERE, Level.WARNING, etc). - * If null, then the level will be set to SEVERE. - * - * @param state the state of the application at the time the incident occured. - * The standard System properties are automatically added to this - * state, and thus do not need to be included. This value may be null. - * If null, the resulting map will contain only the System properties. - * If there is a value in the map with a key that also occurs in the - * System properties (for example: sun.java2d.noddraw), then the - * developer supplied value will be used. In other words, defined - * parameters override standard ones. In addition, the keys - * "System.currentTimeMillis" and "isOnEDT" are both defined - * automatically. - */ - public ErrorInfo(String title, String basicErrorMessage, String detailedErrorMessage, - String category, Throwable errorException, Level errorLevel, Map state) { - this.title = title; - this.basicErrorMessage = basicErrorMessage; - this.detailedErrorMessage = detailedErrorMessage; - this.category = category; - this.errorException = errorException; - this.errorLevel = errorLevel == null ? Level.SEVERE : errorLevel; - this.state = new HashMap(); - - //first add all the System properties - try { - //NOTE: This is not thread safe because System.getProperties() does not appear - //to create a copy of the map. Thus, another thread could be modifying the System - //properties and the "state" at the time of this exception may not be - //accurate! - Properties props = System.getProperties(); - for (Map.Entry entry : props.entrySet()) { - String key = entry.getKey() == null ? null : entry.getKey().toString(); - String val = entry.getKey() == null ? null : entry.getValue().toString(); - if (key != null) { - this.state.put(key, val); - } - } - } catch (SecurityException e) { - //probably running in a sandbox, don't worry about this - } - - //add the automatically supported properties - this.state.put("System.currentTimeMillis", "" + System.currentTimeMillis()); - this.state.put("isOnEDT", "" + SwingUtilities.isEventDispatchThread()); - - //now add all the data in the param "state". Thus, if somebody specified a key in the - //state map, it overrides whatever was in the System map - if (state != null) { - for (Map.Entry entry : state.entrySet()) { - this.state.put(entry.getKey(), entry.getValue()); - } - } - } - - /** - * Gets the string to use for a dialog title or other quick reference. Used - * as a quick reference for the incident. For example, it might be used as the - * title of an error dialog or as the subject of an email message. - * - * @return quick reference String. May be null. - */ - public String getTitle() { - return title; - } - - /** - *

                    Gets the basic error message. This message should be clear and user oriented. - * This String may have HTML formatting, but any such formatting should be used - * sparingly. Generally, such formatting makes sense for making certain words bold, - * but should not be used for page layout or other such things.

                    - * - *

                    For example, the following are perfectly acceptable basic error messages: - *

                    -     *      "Your camera cannot be located. Please make sure that it is powered on
                    -     *       and that it is connected to this computer. Consult the instructions
                    -     *       provided with your camera to make sure you are using the appropriate
                    -     *       cable for attaching the camera to this computer"
                    -     *
                    -     *      "<html>You are running on <b>reserver</b> battery
                    -     *       power. Please plug into a power source immediately, or your work may
                    -     *       be lost!</html>"
                    -     * 

                    - * - * @return basic error message or null - */ - public String getBasicErrorMessage() { - return basicErrorMessage; - } - - /** - *

                    Gets the detailed error message. Unlike {@link #getBasicErrorMessage}, - * this method may return a more technical message to the user. However, it - * should still be user oriented. This String should be formatted using basic - * HTML to improve readability as necessary.

                    - * - *

                    This method may return null.

                    - * - * @return detailed error message or null - */ - public String getDetailedErrorMessage() { - return detailedErrorMessage; - } - - /** - * Gets the category name. This value indicates where in the application - * this incident occurred. It is recommended that this be the same value as - * you would use when logging. This may be null. - * - * @return the category. May be null. - */ - public String getCategory() { - return category; - } - - /** - * Gets the actual exception that generated the error. If this returns a - * non null value, then {@link #getBasicErrorMessage} may return a null value. - * If this returns a non null value and {@link #getDetailedErrorMessage} returns - * a null value, then this returned Throwable may be used as the - * basis for the detailed error message (generally by showing the stack trace). - * - * @return exception or null - */ - public Throwable getErrorException() { - return errorException; - } - - /** - * Gets the severity of the error. The default level is Level.SEVERE, - * but any {@link Level} may be specified when constructing an - * ErrorInfo. - * - * @return the error level. This will never be null - */ - public Level getErrorLevel() { - return errorLevel; - } - - /** - *

                    Gets a copy of the application state at the time that the incident occured. - * This map will never be null. If running with appropriate permissions the - * map will contain all the System properties. In addition, it contains two - * keys, "System.currentTimeMillis" and "isOnEDT".

                    - * - *

                    Warning: The System.properties may not contain the exact set - * of System properties at the time the exception occured. This is due to the - * nature of System.getProperties() and the Properties collection. While they - * are property synchronized, it is possible that while iterating the set of - * properties in the ErrorInfo constructor that some other code can change - * the properties on another thread. This is unlikely to occur, but in some - * applications may occur.

                    - * - * @return a copy of the application state. This will never be null. - */ - public Map getState() { - return new HashMap(state); - } -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/error/ErrorLevel.java b/src/main/java/org/jdesktop/swingx/error/ErrorLevel.java deleted file mode 100644 index 26676c81ff..0000000000 --- a/src/main/java/org/jdesktop/swingx/error/ErrorLevel.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * $Id: ErrorLevel.java 1557 2006-11-10 17:02:53Z rbair $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.error; - -import java.util.logging.Level; - -/** - *

                    Extends {@link Level} adding the FATAL error level. - * Fatal errors are those unrecoverable errors that must result in the termination - * of the application.

                    - * - * @status REVIEWED - * @author rbair - */ -public class ErrorLevel extends Level { - /** - * FATAL is a message level indicating a catastrophic failure that should - * result in the immediate termination of the application. - *

                    - * In general FATAL messages should describe events that are - * of considerable critical and which will prevent - * program execution. They should be reasonably intelligible - * to end users and to system administrators. - * This level is initialized to 1100. - */ - public static final ErrorLevel FATAL = new ErrorLevel("FATAL", 1100); - - /** Creates a new instance of ErrorLevel */ - protected ErrorLevel(String name, int value) { - super(name, value); - } -} diff --git a/src/main/java/org/jdesktop/swingx/error/ErrorListener.java b/src/main/java/org/jdesktop/swingx/error/ErrorListener.java deleted file mode 100644 index b4b1d57df2..0000000000 --- a/src/main/java/org/jdesktop/swingx/error/ErrorListener.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * $Id: ErrorListener.java 2979 2008-07-08 01:32:06Z kschaefe $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.error; - -import java.util.EventListener; - -/** - * ErrorListener defines the interface for an object which listens to errors generated - * by a JX Swing component. ErrorEvents are only generated for internal un-recoverable errors - * that cannot be thrown. An example would be an internal Action implementation that cannot - * throw an Exception directly because the ActionListener interface forbids it. Exceptions - * which can be throw directly (say from the constructor of the JX component) should not use - * the ErrorListener mechanism. - * - * @see ErrorEvent - * @see ErrorSupport - * @author Joshua Marinacci joshua.marinacci@sun.com - */ -public interface ErrorListener extends EventListener { - - /** - * Tells listeners that an error has occured within the watched component. - * @param event - */ - public void errorOccured(ErrorEvent event); -} diff --git a/src/main/java/org/jdesktop/swingx/error/ErrorReporter.java b/src/main/java/org/jdesktop/swingx/error/ErrorReporter.java deleted file mode 100644 index d9969013ff..0000000000 --- a/src/main/java/org/jdesktop/swingx/error/ErrorReporter.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * $Id: ErrorReporter.java 3166 2009-01-02 13:27:18Z rah003 $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.error; - -/** - *

                    ErrorReporter is used by {@link org.jdesktop.swingx.JXErrorPane} to - * implement a pluggable error reporting API. For example, a - * JXErrorPane may use an EmailErrorReporter, or a - * {@code LogErrorReporter}, or perhaps even an - * RSSErrorReporter.

                    - * - * @status REVIEWED - * @author Alexander Zuev - * @author rbair - */ -public interface ErrorReporter { - /** - *

                    Reports an error based on the given {@link ErrorInfo}. This - * method may be a long running method, and so should not block the EDT in - * any way. If an error occurs while reporting the error, it must not - * throw an exception from this method. If an error dialog causes another error, - * it should be silently swallowed. If proper heuristics can be used, an attempt - * can be made some time later to re-report failed error reports, but such attempts - * should be transparent to the user.

                    - * - * @param info encapsulates all information to report using this facility. Must not be null. - * @exception thrown if the info param is null - */ - public void reportError(ErrorInfo info) throws NullPointerException; -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/error/ErrorSupport.java b/src/main/java/org/jdesktop/swingx/error/ErrorSupport.java deleted file mode 100644 index 0fb4bd9ffa..0000000000 --- a/src/main/java/org/jdesktop/swingx/error/ErrorSupport.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * $Id: ErrorSupport.java 3840 2010-10-09 03:25:17Z kschaefe $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.error; - -import javax.swing.*; -import java.util.ArrayList; -import java.util.List; - -/** - * ErrorSupport provides support for managing error listeners. - * @author Joshua Marinacci joshua.marinacci@sun.com - * @see ErrorListener - * @see ErrorEvent - */ -public class ErrorSupport { - private List listeners; - private Object source; - - /** - * Creates a new instance of ErrorSupport - * @param source The object which will fire the ErrorEvents - */ - public ErrorSupport(Object source) { - this.source = source; - listeners = new ArrayList(); - } - - /** - * Add an ErrorListener - * @param listener the listener to add - */ - public void addErrorListener(ErrorListener listener) { - listeners.add(listener); - } - - /** - * Remove an error listener - * @param listener the listener to remove - */ - public void removeErrorListener(ErrorListener listener) { - listeners.remove(listener); - } - - /** - * Returns an array of all the listeners which were added to the - * ErrorSupport object with addErrorListener(). - * @return all of the ErrorListeners added or an empty array if no listeners have been - * added. - */ - public ErrorListener[] getErrorListeners() { - return listeners.toArray(new ErrorListener[0]); - } - - /** - * Report that an error has occurred - * @param throwable The {@link Error} or {@link Exception} which occured. - */ - public void fireErrorEvent(final Throwable throwable) { - final ErrorEvent evt = new ErrorEvent(throwable, source); - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - for(ErrorListener el : listeners) { - el.errorOccured(evt); - } - } - }); - } - -} diff --git a/src/main/java/org/jdesktop/swingx/error/package-info.java b/src/main/java/org/jdesktop/swingx/error/package-info.java deleted file mode 100644 index 83d9035161..0000000000 --- a/src/main/java/org/jdesktop/swingx/error/package-info.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -/** - * Contains classes and interfaces used by the {@code JErrorPane} component. - */ -package org.jdesktop.swingx.error; - diff --git a/src/main/java/org/jdesktop/swingx/event/CompoundFocusListener.java b/src/main/java/org/jdesktop/swingx/event/CompoundFocusListener.java deleted file mode 100644 index 7db84c38d3..0000000000 --- a/src/main/java/org/jdesktop/swingx/event/CompoundFocusListener.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Created on 23.04.2009 - * - */ -package org.jdesktop.swingx.event; - -import org.jdesktop.beans.AbstractBean; -import org.jdesktop.swingx.SwingXUtilities; -import org.jdesktop.swingx.util.Contract; - -import javax.swing.*; -import java.awt.*; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; - -/** - * An convenience class which maps focusEvents received - * from a container hierarchy to a bound read-only property. Registered - * PropertyChangeListeners are notified if the focus is transfered into/out of - * the hierarchy of a given root. - *

                    - * - * F.i, client code which wants to get notified if focus enters/exits the hierarchy below - * panel would install the compound focus listener like: - * - *

                    - * 
                    - *         // add some components inside
                    - *         panel.add(new JTextField("something to .... focus"));
                    - *         panel.add(new JXDatePicker(new Date()));
                    - *         JComboBox combo = new JComboBox(new Object[] {"dooooooooo", 1, 2, 3, 4 });
                    - *         combo.setEditable(true);
                    - *         panel.add(new JButton("something else to ... focus"));
                    - *         panel.add(combo);
                    - *         panel.setBorder(new TitledBorder("has focus dispatcher"));
                    - *         // register the compound dispatcher
                    - *         CompoundFocusListener report = new CompoundFocusListener(panel);
                    - *         PropertyChangeListener l = new PropertyChangeListener() {
                    - * 
                    - *             public void propertyChange(PropertyChangeEvent evt) {
                    - *                 // do something useful here
                    - *                 
                    - *             }};
                    - *         report.addPropertyChangeListener(l);    
                    - *         
                    - * 
                    - * 
                    - * - * PENDING JW: change of current instance of KeyboardFocusManager? - * - */ -public class CompoundFocusListener extends AbstractBean { - - /** the root of the component hierarchy. - * PENDING JW: weak reference and auto-release listener? - */ - private JComponent root; - /** PropertyChangeListener registered with the current keyboardFocusManager. */ - private PropertyChangeListener managerListener; - private boolean focused; - - /** - * Instantiates a CompoundFocusListener on the component hierarchy below the given - * component. - * - * @param root the root of a component hierarchy - * @throws NullPointerException if the root is null - */ - public CompoundFocusListener(JComponent root) { - this.root = Contract.asNotNull(root, "root must not be null"); - KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); - addManagerListener(manager); - permanentFocusOwnerChanged(manager.getPermanentFocusOwner()); - } - - - /** - * Return true if the root or any of its descendants is focused. This is a - * read-only bound property, that is property change event is fired if focus - * is transfered into/out of root's hierarchy. - * - * @return a boolean indicating whether or not any component in the - * container hierarchy below root is permanent focus owner. - */ - public boolean isFocused() { - return focused; - } - - /** - * Releases all listeners and internal references.

                    - * - * Note: this instance must not be used after calling this method. - * - */ - public void release() { - removeManagerListener(KeyboardFocusManager.getCurrentKeyboardFocusManager()); - removeAllListeners(); - this.root = null; - } - - /** - * Removes all property change listeners which are registered with this instance. - */ - private void removeAllListeners() { - for (PropertyChangeListener l : getPropertyChangeListeners()) { - removePropertyChangeListener(l); - } - } - - /** - * Updates focused property depending on whether or not the given component - * is below the root's hierarchy.

                    - * - * Note: Does nothing if the component is null. This might not be entirely correct, - * but property change events from the focus manager come in pairs, with only - * one of the new/old value not-null. - * - * @param focusOwner the component with is the current focusOwner. - */ - protected void permanentFocusOwnerChanged(Component focusOwner) { - if (focusOwner == null) return; - setFocused(SwingXUtilities.isDescendingFrom(focusOwner, root)); - } - - private void setFocused(boolean focused) { - boolean old = isFocused(); - this.focused = focused; - firePropertyChange("focused", old, isFocused()); - } - - - /** - * Adds all listeners to the given KeyboardFocusManager.

                    - * - * @param manager the KeyboardFocusManager to add internal listeners to. - * @see #removeManagerListener(KeyboardFocusManager) - */ - private void addManagerListener(KeyboardFocusManager manager) { - manager.addPropertyChangeListener("permanentFocusOwner", getManagerListener()); - } - - /** - * Removes all listeners this instance has installed from the given KeyboardFocusManager.

                    - * - * @param manager the KeyboardFocusManager to remove internal listeners from. - * @see #addManagerListener(KeyboardFocusManager) - */ - private void removeManagerListener(KeyboardFocusManager manager) { - manager.removePropertyChangeListener("permanentFocusOwner", getManagerListener()); - } - - /** - * Lazily creates and returns a property change listener to be registered on the - * KeyboardFocusManager. - * - * @return a property change listener to be registered on the KeyboardFocusManager. - */ - private PropertyChangeListener getManagerListener() { - if (managerListener == null) { - managerListener = new PropertyChangeListener() { - - @Override - public void propertyChange(PropertyChangeEvent evt) { - if ("permanentFocusOwner".equals(evt.getPropertyName())) { - permanentFocusOwnerChanged((Component) evt.getNewValue()); - } - - }}; - } - return managerListener; - } - - -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/event/DateSelectionEvent.java b/src/main/java/org/jdesktop/swingx/event/DateSelectionEvent.java deleted file mode 100644 index ecaecb81fb..0000000000 --- a/src/main/java/org/jdesktop/swingx/event/DateSelectionEvent.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * $Id: DateSelectionEvent.java 3272 2009-02-25 11:06:37Z kleopatra $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.event; - -import org.jdesktop.swingx.calendar.DateSelectionModel; - -import java.util.Date; -import java.util.EventObject; -import java.util.SortedSet; - -/** - * @author Joshua Outwater - */ -public class DateSelectionEvent extends EventObject { - public static enum EventType { - DATES_ADDED, - DATES_REMOVED, - DATES_SET, - SELECTION_CLEARED, - SELECTABLE_DATES_CHANGED, - SELECTABLE_RANGE_CHANGED, - UNSELECTED_DATES_CHANGED, - LOWER_BOUND_CHANGED, - UPPER_BOUND_CHANGED, - ADJUSTING_STARTED, ADJUSTING_STOPPED, - CALENDAR_CHANGED, - } - - private EventType eventType; - private boolean adjusting; - - /** - * Constructs a prototypical Event. - * - * @param source The object on which the Event initially occurred. - * @param eventType the type of the event - * @param adjusting the adjusting property of the source - * @throws IllegalArgumentException if source is null. - */ - public DateSelectionEvent(Object source, EventType eventType, boolean adjusting) { - super(source); - this.eventType = eventType; - this.adjusting = adjusting; - } - - /** - * Returns the selection of the source dateSelectionModel.

                    - * - * PENDING JW: that's the "live" selection, that is the source is re-queried on every call - * to this method. Bug or feature? - * - * @return the selection of the source. - */ - public SortedSet getSelection() { - return ((DateSelectionModel)source).getSelection(); - } - - /** - * Returns the type of this event. - * - * @return the type of event. - */ - public final EventType getEventType() { - return eventType; - } - - /** - * Returns a boolean indicating whether the event source is in adjusting state. - * - * @return true if the event is fired while the model is in adjusting state. - */ - public boolean isAdjusting() { - return adjusting; - } - - @Override - public String toString() { - return "[" + String.valueOf(getSource()) + " type: " + getEventType() + " isAdjusting: " + isAdjusting(); - } - - -} diff --git a/src/main/java/org/jdesktop/swingx/event/DateSelectionListener.java b/src/main/java/org/jdesktop/swingx/event/DateSelectionListener.java deleted file mode 100644 index 0d9426a1f1..0000000000 --- a/src/main/java/org/jdesktop/swingx/event/DateSelectionListener.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * $Id: DateSelectionListener.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.event; - - -import java.util.EventListener; - -/** - * @author Joshua Outwater - */ -public interface DateSelectionListener extends EventListener { - public void valueChanged(DateSelectionEvent ev); -} diff --git a/src/main/java/org/jdesktop/swingx/event/EventListenerMap.java b/src/main/java/org/jdesktop/swingx/event/EventListenerMap.java deleted file mode 100644 index 4d52ad884e..0000000000 --- a/src/main/java/org/jdesktop/swingx/event/EventListenerMap.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * $Id: EventListenerMap.java 3189 2009-01-20 17:46:04Z kschaefe $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.event; - -import java.util.*; - -/** - * Intended to be a replacement for {@link javax.swing.event.EventListenerList}. - * - * @author Joshua Outwater - * @author Karl Schaefer - * @see javax.swing.event.EventListenerList - */ -public class EventListenerMap { - private final Map, List> listenerList = - new HashMap, List>(); - - /** - * Returns a list containing all of the listeners managed by this {@code EventListenerMap}. - * - * @return all managed listeners - */ - public List getListeners() { - List listeners = new ArrayList(); - - for (List list : listenerList.values()) { - listeners.addAll(list); - } - - return listeners; - } - - /** - * Return a list of all the listeners of the given type. - * - * @return all of the listeners of the specified type. - */ - @SuppressWarnings("unchecked") - public List getListeners(Class clazz) { - List list = (List) listenerList.get(clazz); - if (list == null) { - list = new ArrayList(); - } - return list; - } - - /** - * Returns the total number of listeners of the supplied type - * for this listener list. - */ - public int getListenerCount() { - int count = 0; - - for (List list : listenerList.values()) { - count += list.size(); - } - - return count; - } - - /** - * Returns the total number of listeners for this listener type. - */ - @SuppressWarnings("unchecked") - public int getListenerCount(Class clazz) { - List list = (List) listenerList.get(clazz); - if (list != null) { - return list.size(); - } - return 0; - } - - /** - * Adds the listener as a listener of the specified type. - * - * @param - * the type of the listener to be added - * @param clazz - * the class type to add - * @param l - * the listener to be added - */ - @SuppressWarnings("unchecked") - public synchronized void add(Class clazz, T listener) { - if (listener == null) { - return; - } - - List list = (List) listenerList.get(clazz); - if (list == null) { - list = new ArrayList(); - listenerList.put(clazz, list); - } - list.add(listener); - } - - /** - * Removes the listener as a listener of the specified type. - * - * @param - * the type of the listener to remove - * @param clazz - * the class type to remove - * @param l - * the listener to remove - */ - @SuppressWarnings("unchecked") - public synchronized void remove(Class clazz, T listener) { - if (listener == null) { - return; - } - - List list = (List) listenerList.get(clazz); - if (list != null) { - list.remove(listener); - } - } -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/event/TableColumnModelExtListener.java b/src/main/java/org/jdesktop/swingx/event/TableColumnModelExtListener.java deleted file mode 100644 index 2df544ffd5..0000000000 --- a/src/main/java/org/jdesktop/swingx/event/TableColumnModelExtListener.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * $Id: TableColumnModelExtListener.java 1395 2006-09-14 14:40:12Z kleopatra $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.event; - -import javax.swing.event.TableColumnModelListener; -import java.beans.PropertyChangeEvent; - -/** - * Extended TableColumnModelListener which is interested - * in property changes of contained TableColumns.

                    - * - * Enhanced TableColumnModelExt guarantees to notify - * these extended column listeners. An example of a client which - * adjusts itself based on headerValue property of visible columns: - *

                    
                    - * TableColumnModelExtListener l = new TableColumnModelExtListener() {
                    - * 
                    - *     public void columnPropertyChange(PropertyChangeEvent event) {
                    - *         if ("headerValue".equals(event.getPropertyName())) {
                    - *             TableColumn column = (TableColumn) event.getSource();
                    - *             if ((column instanceof TableColumnExt)
                    - *                     && !((TableColumnExt) column).isVisible()) {
                    - *                 return;
                    - *             }
                    - *             resizeAndRepaint();
                    - *         }
                    - *     }
                    - * 
                    - *     public void columnAdded(TableColumnModelEvent e) {
                    - *     }
                    - * 
                    - *     public void columnMarginChanged(ChangeEvent e) {
                    - *     }
                    - * 
                    - *     public void columnMoved(TableColumnModelEvent e) {
                    - *     }
                    - * 
                    - *     public void columnRemoved(TableColumnModelEvent e) {
                    - *     }
                    - * 
                    - *     public void columnSelectionChanged(ListSelectionEvent e) {
                    - *     }
                    - * 
                    - * };
                    - * columnModel.addColumnModelListener(l);
                    - * 
                    - * - * @author Jeanette Winzenburg - * @see org.jdesktop.swingx.table.TableColumnModelExt - */ -public interface TableColumnModelExtListener extends TableColumnModelListener { - - /** - * Notifies listeners about property changes of contained columns. - * The event is the original as fired from the TableColumn. - * @param event a PropertyChangeEvent fired by a TableColumn - * contained in a TableColumnModel - */ - void columnPropertyChange(PropertyChangeEvent event); -} diff --git a/src/main/java/org/jdesktop/swingx/event/TreeExpansionBroadcaster.java b/src/main/java/org/jdesktop/swingx/event/TreeExpansionBroadcaster.java deleted file mode 100644 index 4c3e5ee282..0000000000 --- a/src/main/java/org/jdesktop/swingx/event/TreeExpansionBroadcaster.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * $Id$ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.event; - -import javax.swing.event.EventListenerList; -import javax.swing.event.TreeExpansionEvent; -import javax.swing.event.TreeExpansionListener; - -/** - * Helper to listen to TreeExpansion events and notify with a remapped source. - * - * @author Jeanette Winzenburg - */ -public class TreeExpansionBroadcaster implements TreeExpansionListener { - - private Object source; - - private EventListenerList listeners; - - public TreeExpansionBroadcaster(Object source) { - this.source = source; - } - - public void addTreeExpansionListener(TreeExpansionListener l) { - getEventListenerList().add(TreeExpansionListener.class, l); - } - - public void removeTreeExpansionListener(TreeExpansionListener l) { - if (!hasListeners()) - return; - listeners.remove(TreeExpansionListener.class, l); - } - - /** - * @return - */ - private EventListenerList getEventListenerList() { - if (listeners == null) { - listeners = new EventListenerList(); - } - return listeners; - } - - // -------------------- TreeExpansionListener - @Override - public void treeExpanded(TreeExpansionEvent event) { - if (!hasListeners()) - return; - fireTreeExpanded(retarget(event)); - } - - @Override - public void treeCollapsed(TreeExpansionEvent event) { - if (!hasListeners()) - return; - fireTreeCollapsed(retarget(event)); - } - - /** - * @param event - */ - private void fireTreeExpanded(TreeExpansionEvent event) { - TreeExpansionListener[] ls = listeners - .getListeners(TreeExpansionListener.class); - for (int i = ls.length - 1; i >= 0; i--) { - ls[i].treeExpanded(event); - } - } - - /** - * @param event - */ - private void fireTreeCollapsed(TreeExpansionEvent event) { - TreeExpansionListener[] ls = listeners - .getListeners(TreeExpansionListener.class); - for (int i = ls.length - 1; i >= 0; i--) { - ls[i].treeCollapsed(event); - } - } - - /** - * @param event - * @return - */ - private TreeExpansionEvent retarget(TreeExpansionEvent event) { - return new TreeExpansionEvent(source, event.getPath()); - } - - /** - * @return - */ - private boolean hasListeners() { - return listeners != null && listeners.getListenerCount() > 0; - } - -} diff --git a/src/main/java/org/jdesktop/swingx/event/WeakEventListenerList.java b/src/main/java/org/jdesktop/swingx/event/WeakEventListenerList.java deleted file mode 100644 index b27187bcb7..0000000000 --- a/src/main/java/org/jdesktop/swingx/event/WeakEventListenerList.java +++ /dev/null @@ -1,275 +0,0 @@ -/* - * $Id: WeakEventListenerList.java 3190 2009-01-20 17:47:52Z kschaefe $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.event; - -import java.io.Serializable; -import java.lang.ref.WeakReference; -import java.lang.reflect.Array; -import java.util.ArrayList; -import java.util.EventListener; -import java.util.List; - -/** - * A class that holds a list of EventListeners. A single instance - * can be used to hold all listeners (of all types) for the instance - * using the list. It is the responsibility of the class using the - * EventListenerList to provide type-safe API (preferably conforming - * to the JavaBeans spec) and methods which dispatch event notification - * methods to appropriate Event Listeners on the list. - * - * The main benefit that this class provides is that it releases - * garbage collected listeners (internally uses weak references).

                    - * - * PENDING: serialization support - * - * - * Usage example: - * Say one is defining a class that sends out FooEvents, and one wants - * to allow users of the class to register FooListeners and receive - * notification when FooEvents occur. The following should be added - * to the class definition: - *

                    - * EventListenerList listenerList = new EventListenerList();
                    - * FooEvent fooEvent = null;
                    - *
                    - * public void addFooListener(FooListener l) {
                    - *     listenerList.add(FooListener.class, l);
                    - * }
                    - *
                    - * public void removeFooListener(FooListener l) {
                    - *     listenerList.remove(FooListener.class, l);
                    - * }
                    - *
                    - *
                    - * // Notify all listeners that have registered interest for
                    - * // notification on this event type.  The event instance 
                    - * // is lazily created using the parameters passed into 
                    - * // the fire method.
                    - *
                    - * 
                    - * protected void fireFooXXX() {
                    - *     // Guaranteed to return a non-null array
                    - *     FooListener[] listeners = listenerList.getListeners(FooListener.class);
                    - *     // Process the listeners last to first, notifying
                    - *     // those that are interested in this event
                    - *     for (FooListener listener: listeners) {
                    - *             // Lazily create the event:
                    - *             if (fooEvent == null)
                    - *                 fooEvent = new FooEvent(this);
                    - *             listener.fooXXX(fooEvent);
                    - *         }
                    - *     }
                    - * }
                    - * 
                    - * foo should be changed to the appropriate name, and fireFooXxx to the - * appropriate method name. One fire method should exist for each - * notification method in the FooListener interface. - *

                    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is - * appropriate for short term storage or RMI between applications running - * the same version of Swing. As of 1.4, support for long term storage - * of all JavaBeansTM - * has been added to the java.beans package. - * Please see {@link java.beans.XMLEncoder}. - * - * @version 1.37 11/17/05 - * @author Georges Saab - * @author Hans Muller - * @author James Gosling - */ -public class WeakEventListenerList implements Serializable { - - protected transient List> weakReferences; - protected transient List> classes; - - /** - * Passes back the event listener list as an array - * of ListenerType-listener pairs. - * As a side-effect, cleans out any - * garbage collected listeners before building the array. - * - * @return a array of listenerType-listener pairs. - */ - public Object[] getListenerList() { - List listeners = cleanReferences(); - Object[] result = new Object[listeners.size() * 2]; - for (int i = 0; i < listeners.size(); i++) { - result[2*i + 1] = listeners.get(i); - result[2*i] = getClasses().get(i); - } - return result; - } - - /** - * Returns a list of strongly referenced EventListeners. Removes - * internal weak references to garbage collected listeners. - * - * @return - */ - @SuppressWarnings("unchecked") - private synchronized List cleanReferences() { - List listeners = new ArrayList(); - for (int i = getReferences().size() - 1; i >= 0; i--) { - - Object listener = getReferences().get(i).get(); - if (listener == null) { - getReferences().remove(i); - getClasses().remove(i); - } else { - listeners.add(0, (T) listener); - } - } - return listeners; - } - - private List> getReferences() { - if (weakReferences == null) { - weakReferences = new ArrayList>(); - } - return weakReferences; - } - - private List> getClasses() { - if (classes == null) { - classes = new ArrayList>(); - - } - return classes; - } - /** - * Return an array of all the listeners of the given type. - * As a side-effect, cleans out any - * garbage collected listeners before building the array. - * @return all of the listeners of the specified type. - * @exception ClassCastException if the supplied class - * is not assignable to EventListener - * - * @since 1.3 - */ - @SuppressWarnings("unchecked") - public T[] getListeners(Class t) { - List liveListeners = cleanReferences(); - List listeners = new ArrayList(); - for (int i = 0; i < liveListeners.size(); i++) { - if (getClasses().get(i) == t) { - listeners.add(liveListeners.get(i)); - } - } - T[] result = (T[])Array.newInstance(t, listeners.size()); - return listeners.toArray(result); - } - - /** - * Adds the listener as a listener of the specified type. - * As a side-effect, cleans out any garbage collected - * listeners before adding. - * @param t the type of the listener to be added - * @param l the listener to be added - */ - public synchronized void add(Class t, T l) { - if (l==null) { - // In an ideal world, we would do an assertion here - // to help developers know they are probably doing - // something wrong - return; - } - if (!t.isInstance(l)) { - throw new IllegalArgumentException("Listener " + l + - " is not of type " + t); - } - cleanReferences(); - getReferences().add(new WeakReference(l)); - getClasses().add(t); - } - - /** - * Removes the listener as a listener of the specified type. - * @param t the type of the listener to be removed - * @param l the listener to be removed - */ - public synchronized void remove(Class t, T l) { - if (l ==null) { - // In an ideal world, we would do an assertion here - // to help developers know they are probably doing - // something wrong - return; - } - if (!t.isInstance(l)) { - throw new IllegalArgumentException("Listener " + l + - " is not of type " + t); - } - for (int i = 0; i < getReferences().size(); i++) { - if (l.equals(getReferences().get(i).get()) && - (t == getClasses().get(i))) { - getReferences().remove(i); - getClasses().remove(i); - break; - } - } - } - -// // Serialization support. -// private void writeObject(ObjectOutputStream s) throws IOException { -// Object[] lList = listenerList; -// s.defaultWriteObject(); -// -// // Save the non-null event listeners: -// for (int i = 0; i < lList.length; i+=2) { -// Class t = (Class)lList[i]; -// EventListener l = (EventListener)lList[i+1]; -// if ((l!=null) && (l instanceof Serializable)) { -// s.writeObject(t.getName()); -// s.writeObject(l); -// } -// } -// -// s.writeObject(null); -// } -// -// private void readObject(ObjectInputStream s) -// throws IOException, ClassNotFoundException { -// listenerList = NULL_ARRAY; -// s.defaultReadObject(); -// Object listenerTypeOrNull; -// -// while (null != (listenerTypeOrNull = s.readObject())) { -// ClassLoader cl = Thread.currentThread().getContextClassLoader(); -// EventListener l = (EventListener)s.readObject(); -// add((Class)Class.forName((String)listenerTypeOrNull, true, cl), l); -// } -// } - -// /** -// * Returns a string representation of the EventListenerList. -// */ -// public String toString() { -// Object[] lList = listenerList; -// String s = "EventListenerList: "; -// s += lList.length/2 + " listeners: "; -// for (int i = 0 ; i <= lList.length-2 ; i+=2) { -// s += " type " + ((Class)lList[i]).getName(); -// s += " listener " + lList[i+1]; -// } -// return s; -// } -} diff --git a/src/main/java/org/jdesktop/swingx/event/package-info.java b/src/main/java/org/jdesktop/swingx/event/package-info.java deleted file mode 100644 index babe7c2879..0000000000 --- a/src/main/java/org/jdesktop/swingx/event/package-info.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * $Id: package-info.java 3165 2009-01-02 13:26:07Z rah003 $ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -/**Contains API for events added as part of JDNC's Swing extensions, -such as message and progress events. - -

                    Package Specification

                    - - - -

                    Related Documentation

                    - - - -*/ -package org.jdesktop.swingx.event; \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/geom/Morphing2D.java b/src/main/java/org/jdesktop/swingx/geom/Morphing2D.java deleted file mode 100644 index d77f926ac3..0000000000 --- a/src/main/java/org/jdesktop/swingx/geom/Morphing2D.java +++ /dev/null @@ -1,705 +0,0 @@ -/* - * $Id: Morphing2D.java 3863 2010-10-26 02:53:32Z kschaefe $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.geom; - -import java.awt.*; -import java.awt.geom.*; - -/** - *

                    A morphing shape is a shape which geometry is constructed from two - * other shapes: a start shape and an end shape.

                    - *

                    The morphing property of a morphing shape defines the amount of - * transformation applied to the start shape to turn it into the end shape.

                    - *

                    Both shapes must have the same winding rule.

                    - * - * @author Jim Graham - * @author Romain Guy (Maintainer) - */ -public class Morphing2D implements Shape { - private double morph; - private Geometry startGeometry; - private Geometry endGeometry; - - /** - *

                    Creates a new morphing shape. A morphing shape can be used to turn - * one shape into another one. The transformation can be controlled by the - * morph property.

                    - * - * @param startShape the shape to morph from - * @param endShape the shape to morph to - * - * @throws IllegalPathStateException if the shapes do not have the same - * winding rule - * @see #getMorphing() - * @see #setMorphing(double) - */ - public Morphing2D(Shape startShape, Shape endShape) { - startGeometry = new Geometry(startShape); - endGeometry = new Geometry(endShape); - if (startGeometry.getWindingRule() != endGeometry.getWindingRule()) { - throw new IllegalPathStateException("shapes must use same " + - "winding rule"); - } - double tvals0[] = startGeometry.getTvals(); - double tvals1[] = endGeometry.getTvals(); - double masterTvals[] = mergeTvals(tvals0, tvals1); - startGeometry.setTvals(masterTvals); - endGeometry.setTvals(masterTvals); - } - - /** - *

                    Returns the morphing value between the two shapes.

                    - * - * @return the morphing value between the two shapes - * - * @see #setMorphing(double) - */ - public double getMorphing() { - return morph; - } - - /** - *

                    Sets the morphing value between the two shapes. This value controls - * the transformation from the start shape to the end shape. A value of 0.0 - * is the start shape. A value of 1.0 is the end shape. A value of 0.5 is a - * new shape, morphed half way from the start shape to the end shape.

                    - *

                    The specified value should be between 0.0 and 1.0. If not, the value - * is clamped in the appropriate range.

                    - * - * @param morph the morphing value between the two shapes - * - * @see #getMorphing() - */ - public void setMorphing(double morph) { - if (morph > 1) { - morph = 1; - } else if (morph >= 0) { - // morphing is finite, not NaN, and in range - } else { - // morph is < 0 or NaN - morph = 0; - } - this.morph = morph; - } - - private static double interp(double v0, double v1, double t) { - return (v0 + ((v1 - v0) * t)); - } - - private static double[] mergeTvals(double tvals0[], double tvals1[]) { - int i0 = 0; - int i1 = 0; - int numtvals = 0; - while (i0 < tvals0.length && i1 < tvals1.length) { - double t0 = tvals0[i0]; - double t1 = tvals1[i1]; - if (t0 <= t1) { - i0++; - } - if (t1 <= t0) { - i1++; - } - numtvals++; - } - double newtvals[] = new double[numtvals]; - i0 = 0; - i1 = 0; - numtvals = 0; - while (i0 < tvals0.length && i1 < tvals1.length) { - double t0 = tvals0[i0]; - double t1 = tvals1[i1]; - if (t0 <= t1) { - newtvals[numtvals] = t0; - i0++; - } - if (t1 <= t0) { - newtvals[numtvals] = t1; - i1++; - } - numtvals++; - } - return newtvals; - } - - /** - * {@inheritDoc} - */ - @Override - public Rectangle getBounds() { - return getBounds2D().getBounds(); - } - - /** - * {@inheritDoc} - */ - @Override - public Rectangle2D getBounds2D() { - int n = startGeometry.getNumCoords(); - double xmin, ymin, xmax, ymax; - xmin = xmax = interp(startGeometry.getCoord(0), endGeometry.getCoord(0), - morph); - ymin = ymax = interp(startGeometry.getCoord(1), endGeometry.getCoord(1), - morph); - for (int i = 2; i < n; i += 2) { - double x = interp(startGeometry.getCoord(i), - endGeometry.getCoord(i), morph); - double y = interp(startGeometry.getCoord(i + 1), - endGeometry.getCoord(i + 1), morph); - if (xmin > x) { - xmin = x; - } - if (ymin > y) { - ymin = y; - } - if (xmax < x) { - xmax = x; - } - if (ymax < y) { - ymax = y; - } - } - return new Rectangle2D.Double(xmin, ymin, xmax - xmin, ymax - ymin); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean contains(double x, double y) { - throw new InternalError("unimplemented"); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean contains(Point2D p) { - return contains(p.getX(), p.getY()); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean intersects(double x, double y, double w, double h) { - throw new InternalError("unimplemented"); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean intersects(Rectangle2D r) { - return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean contains(double x, double y, double w, double h) { - throw new InternalError("unimplemented"); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean contains(Rectangle2D r) { - return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); - } - - /** - * {@inheritDoc} - */ - @Override - public PathIterator getPathIterator(AffineTransform at) { - return new Iterator(at, startGeometry, endGeometry, morph); - } - - /** - * {@inheritDoc} - */ - @Override - public PathIterator getPathIterator(AffineTransform at, double flatness) { - return new FlatteningPathIterator(getPathIterator(at), flatness); - } - - private static class Geometry { - static final double THIRD = (1.0 / 3.0); - static final double MIN_LEN = 0.001; - double bezierCoords[]; - int numCoords; - int windingrule; - double myTvals[]; - - public Geometry(Shape s) { - // Multiple of 6 plus 2 more for initial moveto - bezierCoords = new double[20]; - PathIterator pi = s.getPathIterator(null); - windingrule = pi.getWindingRule(); - if (pi.isDone()) { - // We will have 1 segment and it will be all zeros - // It will have 8 coordinates (2 for moveto, 6 for cubic) - numCoords = 8; - } - double coords[] = new double[6]; - int type = pi.currentSegment(coords); - pi.next(); - if (type != PathIterator.SEG_MOVETO) { - throw new IllegalPathStateException("missing initial moveto"); - } - double curx = bezierCoords[0] = coords[0]; - double cury = bezierCoords[1] = coords[1]; - double newx, newy; - numCoords = 2; - while (!pi.isDone()) { - if (numCoords + 6 > bezierCoords.length) { - // Keep array size to a multiple of 6 plus 2 - int newsize = (numCoords - 2) * 2 + 2; - double newCoords[] = new double[newsize]; - System.arraycopy(bezierCoords, 0, newCoords, 0, numCoords); - bezierCoords = newCoords; - } - switch (pi.currentSegment(coords)) { - case PathIterator.SEG_MOVETO: - throw new InternalError( - "Cannot handle multiple subpaths"); - case PathIterator.SEG_CLOSE: - if (curx == bezierCoords[0] && cury == bezierCoords[1]) - { - break; - } - coords[0] = bezierCoords[0]; - coords[1] = bezierCoords[1]; - /* NO BREAK */ - case PathIterator.SEG_LINETO: - newx = coords[0]; - newy = coords[1]; - // A third of the way from curxy to newxy: - bezierCoords[numCoords++] = interp(curx, newx, THIRD); - bezierCoords[numCoords++] = interp(cury, newy, THIRD); - // A third of the way from newxy back to curxy: - bezierCoords[numCoords++] = interp(newx, curx, THIRD); - bezierCoords[numCoords++] = interp(newy, cury, THIRD); - bezierCoords[numCoords++] = curx = newx; - bezierCoords[numCoords++] = cury = newy; - break; - case PathIterator.SEG_QUADTO: - double ctrlx = coords[0]; - double ctrly = coords[1]; - newx = coords[2]; - newy = coords[3]; - // A third of the way from ctrlxy back to curxy: - bezierCoords[numCoords++] = interp(ctrlx, curx, THIRD); - bezierCoords[numCoords++] = interp(ctrly, cury, THIRD); - // A third of the way from ctrlxy to newxy: - bezierCoords[numCoords++] = interp(ctrlx, newx, THIRD); - bezierCoords[numCoords++] = interp(ctrly, newy, THIRD); - bezierCoords[numCoords++] = curx = newx; - bezierCoords[numCoords++] = cury = newy; - break; - case PathIterator.SEG_CUBICTO: - bezierCoords[numCoords++] = coords[0]; - bezierCoords[numCoords++] = coords[1]; - bezierCoords[numCoords++] = coords[2]; - bezierCoords[numCoords++] = coords[3]; - bezierCoords[numCoords++] = curx = coords[4]; - bezierCoords[numCoords++] = cury = coords[5]; - break; - } - pi.next(); - } - // Add closing segment if either: - // - we only have initial moveto - expand it to an empty cubic - // - or we are not back to the starting point - if ((numCoords < 8) || - curx != bezierCoords[0] || - cury != bezierCoords[1]) { - newx = bezierCoords[0]; - newy = bezierCoords[1]; - // A third of the way from curxy to newxy: - bezierCoords[numCoords++] = interp(curx, newx, THIRD); - bezierCoords[numCoords++] = interp(cury, newy, THIRD); - // A third of the way from newxy back to curxy: - bezierCoords[numCoords++] = interp(newx, curx, THIRD); - bezierCoords[numCoords++] = interp(newy, cury, THIRD); - bezierCoords[numCoords++] = newx; - bezierCoords[numCoords++] = newy; - } - // Now find the segment endpoint with the smallest Y coordinate - int minPt = 0; - double minX = bezierCoords[0]; - double minY = bezierCoords[1]; - for (int ci = 6; ci < numCoords; ci += 6) { - double x = bezierCoords[ci]; - double y = bezierCoords[ci + 1]; - if (y < minY || (y == minY && x < minX)) { - minPt = ci; - minX = x; - minY = y; - } - } - // If the smallest Y coordinate is not the first coordinate, - // rotate the points so that it is... - if (minPt > 0) { - // Keep in mind that first 2 coords == last 2 coords - double newCoords[] = new double[numCoords]; - // Copy all coordinates from minPt to the end of the - // array to the beginning of the new array - System.arraycopy(bezierCoords, minPt, - newCoords, 0, - numCoords - minPt); - // Now we do not want to copy 0,1 as they are duplicates - // of the last 2 coordinates which we just copied. So - // we start the source copy at index 2, but we still - // copy a full minPt coordinates which copies the two - // coordinates that were at minPt to the last two elements - // of the array, thus ensuring that thew new array starts - // and ends with the same pair of coordinates... - System.arraycopy(bezierCoords, 2, - newCoords, numCoords - minPt, - minPt); - bezierCoords = newCoords; - } - /* Clockwise enforcement: - * - This technique is based on the formula for calculating - * the area of a Polygon. The standard formula is: - * Area(Poly) = 1/2 * sum(x[i]*y[i+1] - x[i+1]y[i]) - * - The returned area is negative if the polygon is - * "mostly clockwise" and positive if the polygon is - * "mostly counter-clockwise". - * - One failure mode of the Area calculation is if the - * Polygon is self-intersecting. This is due to the - * fact that the areas on each side of the self-intersection - * are bounded by segments which have opposite winding - * direction. Thus, those areas will have opposite signs - * on the accumulation of their area summations and end - * up canceling each other out partially. - * - This failure mode of the algorithm in determining the - * exact magnitude of the area is not actually a big problem - * for our needs here since we are only using the sign of - * the resulting area to figure out the overall winding - * direction of the path. If self-intersections cause - * different parts of the path to disagree as to the - * local winding direction, that is no matter as we just - * wait for the final answer to tell us which winding - * direction had greater representation. If the final - * result is zero then the path was equal parts clockwise - * and counter-clockwise and we do not care about which - * way we order it as either way will require half of the - * path to unwind and re-wind itself. - */ - double area = 0; - // Note that first and last points are the same so we - // do not need to process coords[0,1] against coords[n-2,n-1] - curx = bezierCoords[0]; - cury = bezierCoords[1]; - for (int i = 2; i < numCoords; i += 2) { - newx = bezierCoords[i]; - newy = bezierCoords[i + 1]; - area += curx * newy - newx * cury; - curx = newx; - cury = newy; - } - if (area < 0) { - /* The area is negative so the shape was clockwise - * in a Euclidean sense. But, our screen coordinate - * systems have the origin in the upper left so they - * are flipped. Thus, this path "looks" ccw on the - * screen so we are flipping it to "look" clockwise. - * Note that the first and last points are the same - * so we do not need to swap them. - * (Not that it matters whether the paths end up cw - * or ccw in the end as long as all of them are the - * same, but above we called this section "Clockwise - * Enforcement", so we do not want to be liars. ;-) - */ - // Note that [0,1] do not need to be swapped with [n-2,n-1] - // So first pair to swap is [2,3] and [n-4,n-3] - int i = 2; - int j = numCoords - 4; - while (i < j) { - curx = bezierCoords[i]; - cury = bezierCoords[i + 1]; - bezierCoords[i] = bezierCoords[j]; - bezierCoords[i + 1] = bezierCoords[j + 1]; - bezierCoords[j] = curx; - bezierCoords[j + 1] = cury; - i += 2; - j -= 2; - } - } - } - - public int getWindingRule() { - return windingrule; - } - - public int getNumCoords() { - return numCoords; - } - - public double getCoord(int i) { - return bezierCoords[i]; - } - - public double[] getTvals() { - if (myTvals != null) { - return myTvals; - } - - // assert(numCoords >= 8); - // assert(((numCoords - 2) % 6) == 0); - double tvals[] = new double[(numCoords - 2) / 6 + 1]; - - // First calculate total "length" of path - // Length of each segment is averaged between - // the length between the endpoints (a lower bound for a cubic) - // and the length of the control polygon (an upper bound) - double segx = bezierCoords[0]; - double segy = bezierCoords[1]; - double tlen = 0; - int ci = 2; - int ti = 0; - while (ci < numCoords) { - double prevx, prevy, newx, newy; - prevx = segx; - prevy = segy; - newx = bezierCoords[ci++]; - newy = bezierCoords[ci++]; - prevx -= newx; - prevy -= newy; - double len = Math.sqrt(prevx * prevx + prevy * prevy); - prevx = newx; - prevy = newy; - newx = bezierCoords[ci++]; - newy = bezierCoords[ci++]; - prevx -= newx; - prevy -= newy; - len += Math.sqrt(prevx * prevx + prevy * prevy); - prevx = newx; - prevy = newy; - newx = bezierCoords[ci++]; - newy = bezierCoords[ci++]; - prevx -= newx; - prevy -= newy; - len += Math.sqrt(prevx * prevx + prevy * prevy); - // len is now the total length of the control polygon - segx -= newx; - segy -= newy; - len += Math.sqrt(segx * segx + segy * segy); - // len is now sum of linear length and control polygon length - len /= 2; - // len is now average of the two lengths - - /* If the result is zero length then we will have problems - * below trying to do the math and bookkeeping to split - * the segment or pair it against the segments in the - * other shape. Since these lengths are just estimates - * to map the segments of the two shapes onto corresponding - * segments of "approximately the same length", we will - * simply fudge the length of this segment to be at least - * a minimum value and it will simply grow from zero or - * near zero length to a non-trivial size as it morphs. - */ - if (len < MIN_LEN) { - len = MIN_LEN; - } - tlen += len; - tvals[ti++] = tlen; - segx = newx; - segy = newy; - } - - // Now set tvals for each segment to its proportional - // part of the length - double prevt = tvals[0]; - tvals[0] = 0; - for (ti = 1; ti < tvals.length - 1; ti++) { - double nextt = tvals[ti]; - tvals[ti] = prevt / tlen; - prevt = nextt; - } - tvals[ti] = 1; - return (myTvals = tvals); - } - - public void setTvals(double newTvals[]) { - double oldCoords[] = bezierCoords; - double newCoords[] = new double[2 + (newTvals.length - 1) * 6]; - double oldTvals[] = getTvals(); - int oldci = 0; - double x0, xc0, xc1, x1; - double y0, yc0, yc1, y1; - x0 = xc0 = xc1 = x1 = oldCoords[oldci++]; - y0 = yc0 = yc1 = y1 = oldCoords[oldci++]; - int newci = 0; - newCoords[newci++] = x0; - newCoords[newci++] = y0; - double t0 = 0; - double t1 = 0; - int oldti = 1; - int newti = 1; - while (newti < newTvals.length) { - if (t0 >= t1) { - x0 = x1; - y0 = y1; - xc0 = oldCoords[oldci++]; - yc0 = oldCoords[oldci++]; - xc1 = oldCoords[oldci++]; - yc1 = oldCoords[oldci++]; - x1 = oldCoords[oldci++]; - y1 = oldCoords[oldci++]; - t1 = oldTvals[oldti++]; - } - double nt = newTvals[newti++]; - // assert(nt > t0); - if (nt < t1) { - // Make nt proportional to [t0 => t1] range - double relt = (nt - t0) / (t1 - t0); - newCoords[newci++] = x0 = interp(x0, xc0, relt); - newCoords[newci++] = y0 = interp(y0, yc0, relt); - xc0 = interp(xc0, xc1, relt); - yc0 = interp(yc0, yc1, relt); - xc1 = interp(xc1, x1, relt); - yc1 = interp(yc1, y1, relt); - newCoords[newci++] = x0 = interp(x0, xc0, relt); - newCoords[newci++] = y0 = interp(y0, yc0, relt); - xc0 = interp(xc0, xc1, relt); - yc0 = interp(yc0, yc1, relt); - newCoords[newci++] = x0 = interp(x0, xc0, relt); - newCoords[newci++] = y0 = interp(y0, yc0, relt); - } else { - newCoords[newci++] = xc0; - newCoords[newci++] = yc0; - newCoords[newci++] = xc1; - newCoords[newci++] = yc1; - newCoords[newci++] = x1; - newCoords[newci++] = y1; - } - t0 = nt; - } - bezierCoords = newCoords; - numCoords = newCoords.length; - myTvals = newTvals; - } - } - - private static class Iterator implements PathIterator { - AffineTransform at; - Geometry g0; - Geometry g1; - double t; - int cindex; - - public Iterator(AffineTransform at, - Geometry g0, Geometry g1, - double t) { - this.at = at; - this.g0 = g0; - this.g1 = g1; - this.t = t; - } - - /** - * {@inheritDoc} - */ - @Override - public int getWindingRule() { - return g0.getWindingRule(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isDone() { - return (cindex > g0.getNumCoords()); - } - - /** - * {@inheritDoc} - */ - @Override - public void next() { - if (cindex == 0) { - cindex = 2; - } else { - cindex += 6; - } - } - - double dcoords[]; - - /** - * {@inheritDoc} - */ - @Override - public int currentSegment(float[] coords) { - if (dcoords == null) { - dcoords = new double[6]; - } - int type = currentSegment(dcoords); - if (type != SEG_CLOSE) { - coords[0] = (float) dcoords[0]; - coords[1] = (float) dcoords[1]; - if (type != SEG_MOVETO) { - coords[2] = (float) dcoords[2]; - coords[3] = (float) dcoords[3]; - coords[4] = (float) dcoords[4]; - coords[5] = (float) dcoords[5]; - } - } - return type; - } - - /** - * {@inheritDoc} - */ - @Override - public int currentSegment(double[] coords) { - int type; - int n; - if (cindex == 0) { - type = SEG_MOVETO; - n = 2; - } else if (cindex >= g0.getNumCoords()) { - type = SEG_CLOSE; - n = 0; - } else { - type = SEG_CUBICTO; - n = 6; - } - if (n > 0) { - for (int i = 0; i < n; i++) { - coords[i] = interp(g0.getCoord(cindex + i), - g1.getCoord(cindex + i), - t); - } - if (at != null) { - at.transform(coords, 0, coords, 0, n / 2); - } - } - return type; - } - } -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/geom/Star2D.java b/src/main/java/org/jdesktop/swingx/geom/Star2D.java deleted file mode 100644 index c59ec7b4aa..0000000000 --- a/src/main/java/org/jdesktop/swingx/geom/Star2D.java +++ /dev/null @@ -1,340 +0,0 @@ -/* - * $Id: Star2D.java 3863 2010-10-26 02:53:32Z kschaefe $ - * - * Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy). - * - * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * Copyright (c) 2006 Romain Guy - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.jdesktop.swingx.geom; - -import java.awt.*; -import java.awt.geom.*; - -/** - *

                    This class provides a star shape. A star is defined by two radii and a - * number of branches. Each branch spans between the two radii. The inner - * radius is the distance between the center of the star and the origin of the - * branches. The outer radius is the distance between the center of the star - * and the tips of the branches.

                    - * - * @author Romain Guy - */ - -public class Star2D implements Shape { - private Shape starShape; - private double x; - private double y; - private double innerRadius; - private double outerRadius; - private int branchesCount; - - /** - *

                    Creates a new star whose center is located at the specified - * x and y coordinates. The number of branches - * and their length can be specified.

                    - * - * @param x the location of the star center - * @param y the location of the star center - * @param innerRadius the distance between the center of the star and the - * origin of the branches - * @param outerRadius the distance between the center of the star and the - * tip of the branches - * @param branchesCount the number of branches in this star; must be >= 3 - * @throws IllegalArgumentException if branchesCount is < 3 or - * if innerRadius is >= outerRadius - */ - public Star2D(double x, double y, - double innerRadius, double outerRadius, - int branchesCount) { - if (branchesCount < 3) { - throw new IllegalArgumentException("The number of branches must" + - " be >= 3."); - } else if (innerRadius >= outerRadius) { - throw new IllegalArgumentException("The inner radius must be < " + - "outer radius."); - } - - this.x = x; - this.y = y; - this.innerRadius = innerRadius; - this.outerRadius = outerRadius; - this.branchesCount = branchesCount; - - starShape = generateStar(x, y, innerRadius, outerRadius, branchesCount); - } - - private static Shape generateStar(double x, double y, - double innerRadius, double outerRadius, - int branchesCount) { - GeneralPath path = new GeneralPath(); - - double outerAngleIncrement = 2 * Math.PI / branchesCount; - - double outerAngle = branchesCount % 2 == 0 ? 0.0 : -(Math.PI / 2.0); - double innerAngle = (outerAngleIncrement / 2.0) + outerAngle; - - float x1 = (float) (Math.cos(outerAngle) * outerRadius + x); - float y1 = (float) (Math.sin(outerAngle) * outerRadius + y); - - float x2 = (float) (Math.cos(innerAngle) * innerRadius + x); - float y2 = (float) (Math.sin(innerAngle) * innerRadius + y); - - path.moveTo(x1, y1); - path.lineTo(x2, y2); - - outerAngle += outerAngleIncrement; - innerAngle += outerAngleIncrement; - - for (int i = 1; i < branchesCount; i++) { - x1 = (float) (Math.cos(outerAngle) * outerRadius + x); - y1 = (float) (Math.sin(outerAngle) * outerRadius + y); - - path.lineTo(x1, y1); - - x2 = (float) (Math.cos(innerAngle) * innerRadius + x); - y2 = (float) (Math.sin(innerAngle) * innerRadius + y); - - path.lineTo(x2, y2); - - outerAngle += outerAngleIncrement; - innerAngle += outerAngleIncrement; - } - - path.closePath(); - return path; - } - - /** - *

                    Sets the inner radius of the star, that is the distance between its - * center and the origin of the branches. The inner radius must always be - * lower than the outer radius.

                    - * - * @param innerRadius the distance between the center of the star and the - * origin of the branches - * @throws IllegalArgumentException if the inner radius is >= outer radius - */ - public void setInnerRadius(double innerRadius) { - if (innerRadius >= outerRadius) { - throw new IllegalArgumentException("The inner radius must be <" + - " outer radius."); - } - - this.innerRadius = innerRadius; - starShape = generateStar(getX(), getY(), innerRadius, getOuterRadius(), - getBranchesCount()); - } - - /** - *

                    Sets location of the center of the star.

                    - * - * @param x the x location of the center of the star - */ - public void setX(double x) { - this.x = x; - starShape = generateStar(x, getY(), getInnerRadius(), getOuterRadius(), - getBranchesCount()); - } - - /** - *

                    Sets the location of the center of the star.

                    - * - * @param y the x location of the center of the star - */ - public void setY(double y) { - this.y = y; - starShape = generateStar(getX(), y, getInnerRadius(), getOuterRadius(), - getBranchesCount()); - } - - /** - *

                    Sets the outer radius of the star, that is the distance between its - * center and the tips of the branches. The outer radius must always be - * greater than the inner radius.

                    - * - * @param outerRadius the distance between the center of the star and the - * tips of the branches - * @throws IllegalArgumentException if the inner radius is >= outer radius - */ - public void setOuterRadius(double outerRadius) { - if (innerRadius >= outerRadius) { - throw new IllegalArgumentException("The outer radius must be > " + - "inner radius."); - } - - this.outerRadius = outerRadius; - starShape = generateStar(getX(), getY(), getInnerRadius(), outerRadius, - getBranchesCount()); - } - - /** - *

                    Sets the number branches of the star. A star must always have at least - * 3 branches.

                    - * - * @param branchesCount the number of branches - * @throws IllegalArgumentException if branchesCount is <=2 - */ - public void setBranchesCount(int branchesCount) { - if (branchesCount <= 2) { - throw new IllegalArgumentException("The number of branches must" + - " be >= 3."); - } - - this.branchesCount = branchesCount; - starShape = generateStar(getX(), getY(), getInnerRadius(), - getOuterRadius(), branchesCount); - } - - /** - *

                    Returns the location of the center of star.

                    - * - * @return the x coordinate of the center of the star - */ - public double getX() { - return x; - } - - /** - *

                    Returns the location of the center of star.

                    - * - * @return the y coordinate of the center of the star - */ - public double getY() { - return y; - } - - /** - *

                    Returns the distance between the center of the star and the origin - * of the branches.

                    - * - * @return the inner radius of the star - */ - public double getInnerRadius() { - return innerRadius; - } - - /** - *

                    Returns the distance between the center of the star and the tips - * of the branches.

                    - * - * @return the outer radius of the star - */ - public double getOuterRadius() { - return outerRadius; - } - - /** - *

                    Returns the number of branches of the star.

                    - * - * @return the number of branches, always >= 3 - */ - public int getBranchesCount() { - return branchesCount; - } - - /** - * {@inheritDoc} - */ - @Override - public Rectangle getBounds() { - return starShape.getBounds(); - } - - /** - * {@inheritDoc} - */ - @Override - public Rectangle2D getBounds2D() { - return starShape.getBounds2D(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean contains(double x, double y) { - return starShape.contains(x, y); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean contains(Point2D p) { - return starShape.contains(p); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean intersects(double x, double y, double w, double h) { - return starShape.intersects(x, y, w, h); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean intersects(Rectangle2D r) { - return starShape.intersects(r); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean contains(double x, double y, double w, double h) { - return starShape.contains(x, y, w, h); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean contains(Rectangle2D r) { - return starShape.contains(r); - } - - /** - * {@inheritDoc} - */ - @Override - public PathIterator getPathIterator(AffineTransform at) { - return starShape.getPathIterator(at); - } - - /** - * {@inheritDoc} - */ - @Override - public PathIterator getPathIterator(AffineTransform at, double flatness) { - return starShape.getPathIterator(at, flatness); - } -} diff --git a/src/main/java/org/jdesktop/swingx/geom/package-info.java b/src/main/java/org/jdesktop/swingx/geom/package-info.java deleted file mode 100644 index 894798fa94..0000000000 --- a/src/main/java/org/jdesktop/swingx/geom/package-info.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -/** - * Contains custom shapes for 2D rendering. - */ -package org.jdesktop.swingx.geom; - diff --git a/src/main/java/org/jdesktop/swingx/graphics/BlendComposite.java b/src/main/java/org/jdesktop/swingx/graphics/BlendComposite.java deleted file mode 100644 index 6d97cae699..0000000000 --- a/src/main/java/org/jdesktop/swingx/graphics/BlendComposite.java +++ /dev/null @@ -1,976 +0,0 @@ -/* - * $Id: BlendComposite.java 4011 2011-05-05 16:19:34Z kschaefe $ - * - * Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy). - * - * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * Copyright (c) 2006 Romain Guy - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.jdesktop.swingx.graphics; - -import java.awt.*; -import java.awt.image.*; - -/** - *

                    A blend composite defines the rule according to which a drawing primitive - * (known as the source) is mixed with existing graphics (know as the - * destination.)

                    - *

                    BlendComposite is an implementation of the - * {@link Composite} interface and must therefore be set as a state on - * a {@link java.awt.Graphics2D} surface.

                    - *

                    Please refer to {@link java.awt.Graphics2D#setComposite(Composite)} - * for more information on how to use this class with a graphics surface.

                    - *

                    Blending Modes

                    - *

                    This class offers a certain number of blending modes, or compositing - * rules. These rules are inspired from graphics editing software packages, - * like Adobe Photoshop or The GIMP.

                    - *

                    Given the wide variety of implemented blending modes and the difficulty - * to describe them with words, please refer to those tools to visually see - * the result of these blending modes.

                    - *

                    Opacity

                    - *

                    Each blending mode has an associated opacity, defined as a float value - * between 0.0 and 1.0. Changing the opacity controls the force with which the - * compositing operation is applied. For instance, a composite with an opacity - * of 0.0 will not draw the source onto the destination. With an opacity of - * 1.0, the source will be fully drawn onto the destination, according to the - * selected blending mode rule.

                    - *

                    The opacity, or alpha value, is used by the composite instance to mutiply - * the alpha value of each pixel of the source when being composited over the - * destination.

                    - *

                    Creating a Blend Composite

                    - *

                    Blend composites can be created in various manners:

                    - *
                      - *
                    • Use one of the pre-defined instance. Example: - * BlendComposite.Average.
                    • - *
                    • Derive one of the pre-defined instances by calling - * {@link #derive(float)} or {@link #derive(BlendingMode)}. Deriving allows - * you to change either the opacity or the blending mode. Example: - * BlendComposite.Average.derive(0.5f).
                    • - *
                    • Use a factory method: {@link #getInstance(BlendingMode)} or - * {@link #getInstance(BlendingMode, float)}.
                    • - *
                    - *

                    Functionality Change in SwingX 1.6.3

                    - *

                    Due to incorrect implementations of various blending modes incompatible changes have occurred. - * The following will help users alleviate problems during migration: - *

                      - *
                    • {@link BlendingMode#BLUE} and {@link BlendingMode#GREEN} have been swapped.
                    • - *
                    - *

                    - * - * @see BlendingMode - * @see java.awt.Graphics2D - * @see Composite - * @see java.awt.AlphaComposite - * @author Romain Guy - * @author Karl Schaefer (support and additional modes) - */ -public final class BlendComposite implements Composite { - /** - * A blending mode defines the compositing rule of a - * {@link BlendComposite}. - * - * @author Romain Guy - * @author Karl Schaefer (support and additional modes) - */ - public enum BlendingMode { - /** - * The {@code Average} blending mode produces an average of the source and blend colors. The - * image will push colors toward the middle, reducing the extremes. - */ - AVERAGE { - @Override - void blend(int[] src, int[] dst, int[] result) { - result[0] = (src[0] + dst[0]) >> 1; - result[1] = (src[1] + dst[1]) >> 1; - result[2] = (src[2] + dst[2]) >> 1; - result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); - } - }, - - /** - * Similar to {@link #AVERAGE}, but more severely lightens or darkens the edge colors. - */ - STAMP { - @Override - void blend(int[] src, int[] dst, int[] result) { - result[0] = Math.max(0, Math.min(255, dst[0] + 2 * src[0] - 256)); - result[1] = Math.max(0, Math.min(255, dst[1] + 2 * src[1] - 256)); - result[2] = Math.max(0, Math.min(255, dst[2] + 2 * src[2] - 256)); - result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); - } - }, - - /** - * The {@code Darken} blend mode compares the color information for each pixel of the base - * and the blend color and applies the darker color as the result. Any pixels in the base - * image that are lighter than the blend color are replaced, and pixels that are darker are - * left unchanged. No part of the image will become lighter. - */ - DARKEN { - @Override - void blend(int[] src, int[] dst, int[] result) { - result[0] = Math.min(src[0], dst[0]); - result[1] = Math.min(src[1], dst[1]); - result[2] = Math.min(src[2], dst[2]); - result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); - } - }, - - /** - * The {@code Multiply} blend mode multiplies the base color with the blend color. The - * resulting color will always be darker, unless the blend color is white, which will result - * in no change. 100% opaque black multiplied with any color will result in black. As you - * overlay strokes of color with the Multiply blending mode, each stroke will result in - * darker and darker color. - */ - MULTIPLY { - @Override - void blend(int[] src, int[] dst, int[] result) { - result[0] = (src[0] * dst[0] + 2) >> 8; - result[1] = (src[1] * dst[1] + 2) >> 8; - result[2] = (src[2] * dst[2] + 2) >> 8; - result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); - } - }, - - /** - * The {@code Color Burn} blending mode increases the contrast to darken the base color - * while reflecting the blend color. The darker the blend color, the more intensely the - * color will be applied in the base image. White as the blend color produces no change. - */ - COLOR_BURN { - @Override - void blend(int[] src, int[] dst, int[] result) { - result[0] = src[0] == 0 ? 0 : Math.max(0, 255 - (((255 - dst[0]) << 8) / src[0])); - result[1] = src[1] == 0 ? 0 : Math.max(0, 255 - (((255 - dst[1]) << 8) / src[1])); - result[2] = src[2] == 0 ? 0 : Math.max(0, 255 - (((255 - dst[2]) << 8) / src[2])); - result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); - } - }, - - /** - * {@code Inverse Color Burn} is the same as {@link #COLOR_BURN Color Burn} with the source - * and destination swapped. - */ - INVERSE_COLOR_BURN { - @Override - void blend(int[] src, int[] dst, int[] result) { - result[0] = dst[0] == 0 ? 0 : Math.max(0, 255 - (((255 - src[0]) << 8) / dst[0])); - result[1] = dst[1] == 0 ? 0 : Math.max(0, 255 - (((255 - src[1]) << 8) / dst[1])); - result[2] = dst[2] == 0 ? 0 : Math.max(0, 255 - (((255 - src[2]) << 8) / dst[2])); - result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); - } - }, - - SOFT_BURN { - @Override - void blend(int[] src, int[] dst, int[] result) { - result[0] = dst[0] + src[0] < 256 - ? (dst[0] == 255 ? 255 : Math.min(255, (src[0] << 7) / (255 - dst[0]))) - : Math.max(0, 255 - (((255 - dst[0]) << 7) / src[0])); - result[1] = dst[1] + src[1] < 256 - ? (dst[1] == 255 ? 255 : Math.min(255, (src[1] << 7) / (255 - dst[1]))) - : Math.max(0, 255 - (((255 - dst[1]) << 7) / src[1])); - result[2] = dst[2] + src[2] < 256 - ? (dst[2] == 255 ? 255 : Math.min(255, (src[2] << 7) / (255 - dst[2]))) - : Math.max(0, 255 - (((255 - dst[2]) << 7) / src[2])); - result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); - } - }, - - /** - * The {@code Subtract} blend mode is similar to {@link #COLOR_BURN Color Burn} but instead of increasing - * contrast, it decreases brightness to darken the base color and reflect the blend color. - * It is also similar to the Multiply blend mode, but produces a much more intense result. - * White as the blend color produces no change. - *

                    - * This mode is also known as {@code Linear Burn}. - */ - SUBTRACT { - @Override - void blend(int[] src, int[] dst, int[] result) { - result[0] = Math.max(0, src[0] + dst[0] - 256); - result[1] = Math.max(0, src[1] + dst[1] - 256); - result[2] = Math.max(0, src[2] + dst[2] - 256); - result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); - } - }, - - /** - * The {@code Lighten} blending mode compares the color information for each pixel of the - * base and the blend color and applies the lighter color as the result. Any pixels in the - * base image that are darker than the blend color are replaced, and pixels that are lighter - * are left unchanged. No part of the image will become darker. - */ - LIGHTEN { - @Override - void blend(int[] src, int[] dst, int[] result) { - result[0] = Math.max(src[0], dst[0]); - result[1] = Math.max(src[1], dst[1]); - result[2] = Math.max(src[2], dst[2]); - result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); - } - }, - - /** - * The {@code Screen} blending mode is the opposite of the {@link #MULTIPLY Multiply} mode - * in that it multiples the inverse of the base color with the blend color. What this means - * is that your image will get lighter overall. In areas where the blend color is black, the - * base image will be unchanged, and in areas where the blend or base color is white, the - * result will be no change. Dark areas in the base image will become significantly lighter, - * and bright areas will become only slightly lighter. - */ - SCREEN { - @Override - void blend(int[] src, int[] dst, int[] result) { - result[0] = 255 - ((255 - src[0]) * (255 - dst[0]) >> 8); - result[1] = 255 - ((255 - src[1]) * (255 - dst[1]) >> 8); - result[2] = 255 - ((255 - src[2]) * (255 - dst[2]) >> 8); - result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); - } - }, - - /** - * The {@code Color Dodge} blending mode is essentially the opposite of {@link #COLOR_BURN - * Color Burn}. The {@code Color Dodge} blending mode decreases the contrast to brighten the - * base color while reflecting the blend color. The lighter the blend color, the more - * significant the color dodge effect will be making the result brighter, with less - * contrast, and tinted toward the blend color. Black as the blend color produces no change. - */ - COLOR_DODGE { - @Override - void blend(int[] src, int[] dst, int[] result) { - result[0] = src[0] == 255 ? 255 : Math.min((dst[0] << 8) / (255 - src[0]), 255); - result[1] = src[1] == 255 ? 255 : Math.min((dst[1] << 8) / (255 - src[1]), 255); - result[2] = src[2] == 255 ? 255 : Math.min((dst[2] << 8) / (255 - src[2]), 255); - result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); - } - }, - - /** - * {@code Inverse Color Dodge} is the same as {@link #COLOR_DODGE Color Dodge} with the - * source and destination swapped. - */ - INVERSE_COLOR_DODGE { - @Override - void blend(int[] src, int[] dst, int[] result) { - result[0] = dst[0] == 255 ? 255 : Math.min((src[0] << 8) / (255 - dst[0]), 255); - result[1] = dst[1] == 255 ? 255 : Math.min((src[1] << 8) / (255 - dst[1]), 255); - result[2] = dst[2] == 255 ? 255 : Math.min((src[2] << 8) / (255 - dst[2]), 255); - result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); - } - }, - - SOFT_DODGE { - @Override - void blend(int[] src, int[] dst, int[] result) { - result[0] = dst[0] + src[0] < 256 - ? (src[0] == 255 ? 255 : Math.min(255, (dst[0] << 7) / (255 - src[0]))) - : Math.max(0, 255 - (((255 - src[0]) << 7) / dst[0])); - result[1] = dst[1] + src[1] < 256 - ? (src[1] == 255 ? 255 : Math.min(255, (dst[1] << 7) / (255 - src[1]))) - : Math.max(0, 255 - (((255 - src[1]) << 7) / dst[1])); - result[2] = dst[2] + src[2] < 256 - ? (src[2] == 255 ? 255 : Math.min(255, (dst[2] << 7) / (255 - src[2]))) - : Math.max(0, 255 - (((255 - src[2]) << 7) / dst[2])); - result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); - } - }, - - /** - * {@code Add} is the opposite of {@link #SUBTRACT Subtract}. It increases brightness to - * lighten the base color and reflect the blend color. It is also similar to the - * {@link #SCREEN Screen} blend mode, but produces a more intense result. Black as the blend - * color produces no change. - *

                    - * This mode is also known as {@code Linear Dodge}. - */ - ADD { - @Override - void blend(int[] src, int[] dst, int[] result) { - result[0] = Math.min(255, src[0] + dst[0]); - result[1] = Math.min(255, src[1] + dst[1]); - result[2] = Math.min(255, src[2] + dst[2]); - result[3] = Math.min(255, src[3] + dst[3]); - } - }, - - /** - * The {@code Overlay} blending mode preserves the highlights and shadows of the base color - * while mixing the base color and the blend color. It is a combination of the - * {@link #MULTIPLY Multiply} and {@link #SCREEN Screen} blending modes--multiplying the - * dark areas, and screening the light areas. A blend color of 50% gray has no effect on the - * base image. - */ - OVERLAY { - @Override - void blend(int[] src, int[] dst, int[] result) { - result[0] = dst[0] < 128 ? dst[0] * src[0] >> 7 - : 255 - ((255 - dst[0]) * (255 - src[0]) >> 7); - result[1] = dst[1] < 128 ? dst[1] * src[1] >> 7 - : 255 - ((255 - dst[1]) * (255 - src[1]) >> 7); - result[2] = dst[2] < 128 ? dst[2] * src[2] >> 7 - : 255 - ((255 - dst[2]) * (255 - src[2]) >> 7); - result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); - } - }, - - /** - * The {@code Soft Light} blend mode creates a subtle lighter or darker result depending on - * the brightness of the blend color. Blend colors that are more than 50% brightness will - * lighten the base image and colors that are less than 50% brightness will darken the base - * image. Pure black will create a slightly darker result; pure white will create a slightly - * lighter result, and 50% gray will have no effect on the base image. - */ - SOFT_LIGHT { - @Override - void blend(int[] src, int[] dst, int[] result) { - int mRed = src[0] * dst[0] / 255; - int mGreen = src[1] * dst[1] / 255; - int mBlue = src[2] * dst[2] / 255; - result[0] = mRed + dst[0] * (255 - ((255 - dst[0]) * (255 - src[0]) / 255) - mRed) / 255; - result[1] = mGreen + dst[1] * (255 - ((255 - dst[1]) * (255 - src[1]) / 255) - mGreen) / 255; - result[2] = mBlue + dst[2] * (255 - ((255 - dst[2]) * (255 - src[2]) / 255) - mBlue) / 255; - result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); - } - }, - - /** - * {@code Hard Light} drastically lightens or darkens the base image depending on the - * brightness of the blend color. The effect is more intense than {@link #SOFT_LIGHT Soft - * Light} because the contrast is also increased. Blend colors that are more than 50% - * brightness will lighten the base image in the same way as the screen blending mode. - * Colors that are less than 50% brightness will darken the base image in the same way as - * the multiply blending mode. Pure black will result in black; pure white will create a - * white result, and 50% gray will have no effect on the base image. - */ - HARD_LIGHT { - @Override - void blend(int[] src, int[] dst, int[] result) { - result[0] = src[0] < 128 ? dst[0] * src[0] >> 7 - : 255 - ((255 - src[0]) * (255 - dst[0]) >> 7); - result[1] = src[1] < 128 ? dst[1] * src[1] >> 7 - : 255 - ((255 - src[1]) * (255 - dst[1]) >> 7); - result[2] = src[2] < 128 ? dst[2] * src[2] >> 7 - : 255 - ((255 - src[2]) * (255 - dst[2]) >> 7); - result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); - } - }, - - /** - * Burns or dodges the colors by increasing or decreasing the contrast, depending on the - * blend color. If the blend color is lighter than 50% grey, the image is lightened by - * decreasing the contrast. If the blend color is darker than 50% grey, the image is - * darkened by increasing the contrast. - */ - VIVID_LIGHT { - @Override - void blend(int[] src, int[] dst, int[] result) { - result[0] = src[0] < 128 - ? src[0] == 0 ? 0 : Math.max(0, 255 - ((255 - dst[0]) << 7) / src[0]) - : src[0] == 255 ? 255 : Math.min(255, (dst[0] << 7) / (255 - src[0])); - result[1] = src[1] < 128 - ? src[1] == 0 ? 0 : Math.max(0, 255 - ((255 - dst[1]) << 7) / src[1]) - : src[1] == 255 ? 255 : Math.min(255, (dst[1] << 7) / (255 - src[1])); - result[2] = src[2] < 128 - ? src[2] == 0 ? 0 : Math.max(0, 255 - ((255 - dst[2]) << 7) / src[2]) - : src[2] == 255 ? 255 : Math.min(255, (dst[2] << 7) / (255 - src[2])); - result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); - } - }, - - LINEAR_LIGHT { - @Override - void blend(int[] src, int[] dst, int[] result) { - result[0] = src[0] < 128 ? Math.max(0, dst[0] + (src[0] << 1) - 255) - : Math.min(255, dst[0] + (src[0] - 128 << 1)); - result[1] = src[1] < 128 ? Math.max(0, dst[1] + (src[1] << 1) - 255) - : Math.min(255, dst[1] + (src[1] - 128 << 1)); - result[2] = src[2] < 128 ? Math.max(0, dst[2] + (src[2] << 1) - 255) - : Math.min(255, dst[2] + (src[2] - 128 << 1)); - result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); - } - }, - - PIN_LIGHT { - @Override - void blend(int[] src, int[] dst, int[] result) { - result[0] = src[0] < 128 ? Math.min(dst[0], src[0] << 1) - : Math.max(dst[0], (src[0] - 128) << 1); - result[1] = src[1] < 128 ? Math.min(dst[1], src[1] << 1) - : Math.max(dst[1], (src[1] - 128) << 1); - result[2] = src[2] < 128 ? Math.min(dst[2], src[2] << 1) - : Math.max(dst[2], (src[2] - 128) << 1); - result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); - } - }, - - HARD_MIX { - @Override - void blend(int[] src, int[] dst, int[] result) { - result[0] = src[0] < 256 - dst[0] ? 0 : 255; - result[1] = src[1] < 256 - dst[1] ? 0 : 255; - result[2] = src[2] < 256 - dst[2] ? 0 : 255; - result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); - } - }, - - REFLECT { - @Override - void blend(int[] src, int[] dst, int[] result) { - result[0] = src[0] == 255 ? 255 : Math.min(255, dst[0] * dst[0] / (255 - src[0])); - result[1] = src[1] == 255 ? 255 : Math.min(255, dst[1] * dst[1] / (255 - src[1])); - result[2] = src[2] == 255 ? 255 : Math.min(255, dst[2] * dst[2] / (255 - src[2])); - result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); - } - }, - - GLOW { - @Override - void blend(int[] src, int[] dst, int[] result) { - result[0] = dst[0] == 255 ? 255 : Math.min(255, src[0] * src[0] / (255 - dst[0])); - result[1] = dst[1] == 255 ? 255 : Math.min(255, src[1] * src[1] / (255 - dst[1])); - result[2] = dst[2] == 255 ? 255 : Math.min(255, src[2] * src[2] / (255 - dst[2])); - result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); - } - }, - - FREEZE { - @Override - void blend(int[] src, int[] dst, int[] result) { - result[0] = src[0] == 0 ? 0 : Math.max(0, 255 - (255 - dst[0]) * (255 - dst[0]) - / src[0]); - result[1] = src[1] == 0 ? 0 : Math.max(0, 255 - (255 - dst[1]) * (255 - dst[1]) - / src[1]); - result[2] = src[2] == 0 ? 0 : Math.max(0, 255 - (255 - dst[2]) * (255 - dst[2]) - / src[2]); - result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); - } - }, - - HEAT { - @Override - void blend(int[] src, int[] dst, int[] result) { - result[0] = dst[0] == 0 ? 0 : Math.max(0, 255 - (255 - src[0]) * (255 - src[0]) - / dst[0]); - result[1] = dst[1] == 0 ? 0 : Math.max(0, 255 - (255 - src[1]) * (255 - src[1]) - / dst[1]); - result[2] = dst[2] == 0 ? 0 : Math.max(0, 255 - (255 - src[2]) * (255 - src[2]) - / dst[2]); - result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); - } - }, - - /** - * The {@code Difference} blending mode highlights the differences between the blend layer - * and the base layer. The more technical explanation is that the blend color is subtracted - * from the base color--or vice-versa, depending on the brightness--and the result is the - * difference between them. When white is the blend color, the base image is inverted. When - * black is the blend color, there is no change. - */ - DIFFERENCE { - @Override - void blend(int[] src, int[] dst, int[] result) { - result[0] = Math.abs(dst[0] - src[0]); - result[1] = Math.abs(dst[1] - src[1]); - result[2] = Math.abs(dst[2] - src[2]); - result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); - } - }, - - /** - * The {@code Exclusion} blending mode works very much like {@link #DIFFERENCE Difference} - * but the contrast is lower. When white is the blend color, the base image is inverted. - * When black is the blend color, there is no change. - */ - EXCLUSION { - @Override - void blend(int[] src, int[] dst, int[] result) { - result[0] = dst[0] + src[0] - (dst[0] * src[0] >> 7); - result[1] = dst[1] + src[1] - (dst[1] * src[1] >> 7); - result[2] = dst[2] + src[2] - (dst[2] * src[2] >> 7); - result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); - } - }, - - /** - * The {@code Hue} blend mode applies the hue of the blend color to the base image while retaining - * the luminance and saturation of the base image. It gives the base image a tinted effect - * where the tinting is darkest in areas of high saturation. Where the blend color is a - * shade of gray (0% saturation), the base image is desaturated and where the base image is - * gray, the Hue blending mode has no effect. - */ - HUE { - @Override - void blend(int[] src, int[] dst, int[] result) { - float[] srcHSL = new float[3]; - ColorUtilities.RGBtoHSL(src[0], src[1], src[2], srcHSL); - float[] dstHSL = new float[3]; - ColorUtilities.RGBtoHSL(dst[0], dst[1], dst[2], dstHSL); - - ColorUtilities.HSLtoRGB(srcHSL[0], dstHSL[1], dstHSL[2], result); - result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); - } - }, - - /** - * The {@code Saturation} blending mode applies the saturation of the blend color to the - * base image while retaining the hue and luminance of the base image. Neutral tones (black, - * white, and gray) in the blend will desaturate the base image. Neutral areas in the base - * image will not be changed by the saturation blending mode. - */ - SATURATION { - @Override - void blend(int[] src, int[] dst, int[] result) { - float[] srcHSL = new float[3]; - ColorUtilities.RGBtoHSL(src[0], src[1], src[2], srcHSL); - float[] dstHSL = new float[3]; - ColorUtilities.RGBtoHSL(dst[0], dst[1], dst[2], dstHSL); - - ColorUtilities.HSLtoRGB(dstHSL[0], srcHSL[1], dstHSL[2], result); - result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); - } - }, - - /** - * The {@code Color} blending mode applies the hue and saturation of the blend color to the - * base image while retaining the luminance of the base image. Simply put, it colors the - * base image. Neutral blend colors will desaturate the base image. - */ - COLOR { - @Override - void blend(int[] src, int[] dst, int[] result) { - float[] srcHSL = new float[3]; - ColorUtilities.RGBtoHSL(src[0], src[1], src[2], srcHSL); - float[] dstHSL = new float[3]; - ColorUtilities.RGBtoHSL(dst[0], dst[1], dst[2], dstHSL); - - ColorUtilities.HSLtoRGB(srcHSL[0], srcHSL[1], dstHSL[2], result); - result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); - } - }, - - /** - * The {@code Luminosity} blending mode applies the luminosity (brightness) of the blend - * colors to the base image while retaining the hue and saturation of the base image. - * {@code Luminosity} is the opposite of the {@link #COLOR Color} blending mode. - */ - LUMINOSITY { - @Override - void blend(int[] src, int[] dst, int[] result) { - float[] srcHSL = new float[3]; - ColorUtilities.RGBtoHSL(src[0], src[1], src[2], srcHSL); - float[] dstHSL = new float[3]; - ColorUtilities.RGBtoHSL(dst[0], dst[1], dst[2], dstHSL); - - ColorUtilities.HSLtoRGB(dstHSL[0], dstHSL[1], srcHSL[2], result); - result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); - } - }, - - /** - * This one is the "opposite" of difference mode. Note that it is NOT difference mode - * inverted, because black and white return the same result, but colors between become - * brighter instead of darker. This mode can be used to invert parts of the base image, but - * NOT to compare two images. - */ - NEGATION { - @Override - void blend(int[] src, int[] dst, int[] result) { - result[0] = 255 - Math.abs(255 - dst[0] - src[0]); - result[1] = 255 - Math.abs(255 - dst[1] - src[1]); - result[2] = 255 - Math.abs(255 - dst[2] - src[2]); - result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); - } - }, - - /** - * Keeps the red channel from the blend image and the green and blue channels from the base - * image. - */ - RED { - @Override - void blend(int[] src, int[] dst, int[] result) { - result[0] = src[0]; - result[1] = dst[1]; - result[2] = dst[2]; - result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); - } - }, - - /** - * Keeps the green channel from the blend image and the red and blue channels from the base - * image. - */ - GREEN { - @Override - void blend(int[] src, int[] dst, int[] result) { - result[0] = dst[0]; - result[1] = src[1]; - result[2] = dst[2]; - result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); - } - }, - - /** - * Keeps the blue channel from the blend image and the red and green channels from the base - * image. - */ - BLUE { - @Override - void blend(int[] src, int[] dst, int[] result) { - result[0] = dst[0]; - result[1] = dst[1]; - result[2] = src[2]; - result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); - } - }, - ; - - /** - * Blends the input colors into the result. - * - * @param src - * the source RGBA - * @param dst - * the destination RGBA - * @param result - * the result RGBA - * @throws NullPointerException - * if any argument is {@code null} - */ - abstract void blend(int[] src, int[] dst, int[] result); - } - - public static final BlendComposite Average = new BlendComposite(BlendingMode.AVERAGE); - public static final BlendComposite Multiply = new BlendComposite(BlendingMode.MULTIPLY); - public static final BlendComposite Screen = new BlendComposite(BlendingMode.SCREEN); - public static final BlendComposite Darken = new BlendComposite(BlendingMode.DARKEN); - public static final BlendComposite Lighten = new BlendComposite(BlendingMode.LIGHTEN); - public static final BlendComposite Overlay = new BlendComposite(BlendingMode.OVERLAY); - public static final BlendComposite HardLight = new BlendComposite(BlendingMode.HARD_LIGHT); - public static final BlendComposite SoftLight = new BlendComposite(BlendingMode.SOFT_LIGHT); - public static final BlendComposite VividLight = new BlendComposite(BlendingMode.VIVID_LIGHT); - public static final BlendComposite LinearLight = new BlendComposite(BlendingMode.LINEAR_LIGHT); - public static final BlendComposite PinLight = new BlendComposite(BlendingMode.PIN_LIGHT); - public static final BlendComposite HardMix = new BlendComposite(BlendingMode.HARD_MIX); - public static final BlendComposite Difference = new BlendComposite(BlendingMode.DIFFERENCE); - public static final BlendComposite Negation = new BlendComposite(BlendingMode.NEGATION); - public static final BlendComposite Exclusion = new BlendComposite(BlendingMode.EXCLUSION); - public static final BlendComposite ColorDodge = new BlendComposite(BlendingMode.COLOR_DODGE); - public static final BlendComposite InverseColorDodge = new BlendComposite(BlendingMode.INVERSE_COLOR_DODGE); - public static final BlendComposite SoftDodge = new BlendComposite(BlendingMode.SOFT_DODGE); - public static final BlendComposite ColorBurn = new BlendComposite(BlendingMode.COLOR_BURN); - public static final BlendComposite InverseColorBurn = new BlendComposite(BlendingMode.INVERSE_COLOR_BURN); - public static final BlendComposite SoftBurn = new BlendComposite(BlendingMode.SOFT_BURN); - public static final BlendComposite Reflect = new BlendComposite(BlendingMode.REFLECT); - public static final BlendComposite Glow = new BlendComposite(BlendingMode.GLOW); - public static final BlendComposite Freeze = new BlendComposite(BlendingMode.FREEZE); - public static final BlendComposite Heat = new BlendComposite(BlendingMode.HEAT); - public static final BlendComposite Add = new BlendComposite(BlendingMode.ADD); - public static final BlendComposite Subtract = new BlendComposite(BlendingMode.SUBTRACT); - public static final BlendComposite Stamp = new BlendComposite(BlendingMode.STAMP); - public static final BlendComposite Red = new BlendComposite(BlendingMode.RED); - public static final BlendComposite Green = new BlendComposite(BlendingMode.GREEN); - public static final BlendComposite Blue = new BlendComposite(BlendingMode.BLUE); - public static final BlendComposite Hue = new BlendComposite(BlendingMode.HUE); - public static final BlendComposite Saturation = new BlendComposite(BlendingMode.SATURATION); - public static final BlendComposite Color = new BlendComposite(BlendingMode.COLOR); - public static final BlendComposite Luminosity = new BlendComposite(BlendingMode.LUMINOSITY); - - private final float alpha; - private final BlendingMode mode; - - private BlendComposite(BlendingMode mode) { - this(mode, 1.0f); - } - - private BlendComposite(BlendingMode mode, float alpha) { - this.mode = mode; - - if (alpha < 0.0f || alpha > 1.0f) { - throw new IllegalArgumentException( - "alpha must be comprised between 0.0f and 1.0f"); - } - this.alpha = alpha; - } - - /** - *

                    Creates a new composite based on the blending mode passed - * as a parameter. A default opacity of 1.0 is applied.

                    - * - * @param mode the blending mode defining the compositing rule - * @return a new BlendComposite based on the selected blending - * mode, with an opacity of 1.0 - */ - public static BlendComposite getInstance(BlendingMode mode) { - return new BlendComposite(mode); - } - - /** - *

                    Creates a new composite based on the blending mode and opacity passed - * as parameters. The opacity must be a value between 0.0 and 1.0.

                    - * - * @param mode the blending mode defining the compositing rule - * @param alpha the constant alpha to be multiplied with the alpha of the - * source. alpha must be a floating point between 0.0 and 1.0. - * @throws IllegalArgumentException if the opacity is less than 0.0 or - * greater than 1.0 - * @return a new BlendComposite based on the selected blending - * mode and opacity - */ - public static BlendComposite getInstance(BlendingMode mode, float alpha) { - return new BlendComposite(mode, alpha); - } - - /** - *

                    Returns a BlendComposite object that uses the specified - * blending mode and this object's alpha value. If the newly specified - * blending mode is the same as this object's, this object is returned.

                    - * - * @param mode the blending mode defining the compositing rule - * @return a BlendComposite object derived from this object, - * that uses the specified blending mode - */ - public BlendComposite derive(BlendingMode mode) { - return this.mode == mode ? this : new BlendComposite(mode, getAlpha()); - } - - /** - *

                    Returns a BlendComposite object that uses the specified - * opacity, or alpha, and this object's blending mode. If the newly specified - * opacity is the same as this object's, this object is returned.

                    - * - * @param alpha the constant alpha to be multiplied with the alpha of the - * source. alpha must be a floating point between 0.0 and 1.0. - * @throws IllegalArgumentException if the opacity is less than 0.0 or - * greater than 1.0 - * @return a BlendComposite object derived from this object, - * that uses the specified blending mode - */ - public BlendComposite derive(float alpha) { - return this.alpha == alpha ? this : new BlendComposite(getMode(), alpha); - } - - /** - *

                    Returns the opacity of this composite. If no opacity has been defined, - * 1.0 is returned.

                    - * - * @return the alpha value, or opacity, of this object - */ - public float getAlpha() { - return alpha; - } - - /** - *

                    Returns the blending mode of this composite.

                    - * - * @return the blending mode used by this object - */ - public BlendingMode getMode() { - return mode; - } - - /** - * {@inheritDoc} - */ - @Override - public int hashCode() { - return Float.floatToIntBits(alpha) * 31 + mode.ordinal(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean equals(Object obj) { - if (!(obj instanceof BlendComposite)) { - return false; - } - - BlendComposite bc = (BlendComposite) obj; - return mode == bc.mode && alpha == bc.alpha; - } - - private static boolean isRgbColorModel(ColorModel cm) { - if (cm instanceof DirectColorModel && - cm.getTransferType() == DataBuffer.TYPE_INT) { - DirectColorModel directCM = (DirectColorModel) cm; - - return directCM.getRedMask() == 0x00FF0000 && - directCM.getGreenMask() == 0x0000FF00 && - directCM.getBlueMask() == 0x000000FF && - (directCM.getNumComponents() == 3 || - directCM.getAlphaMask() == 0xFF000000); - } - - return false; - } - - private static boolean isBgrColorModel(ColorModel cm) { - if (cm instanceof DirectColorModel && - cm.getTransferType() == DataBuffer.TYPE_INT) { - DirectColorModel directCM = (DirectColorModel) cm; - - return directCM.getRedMask() == 0x000000FF && - directCM.getGreenMask() == 0x0000FF00 && - directCM.getBlueMask() == 0x00FF0000 && - (directCM.getNumComponents() == 3 || - directCM.getAlphaMask() == 0xFF000000); - } - - return false; - } - - /** - * {@inheritDoc} - */ - @Override - public CompositeContext createContext(ColorModel srcColorModel, - ColorModel dstColorModel, - RenderingHints hints) { - if (isRgbColorModel(srcColorModel) && isRgbColorModel(dstColorModel)) { - return new BlendingRgbContext(this); - } else if (isBgrColorModel(srcColorModel) && isBgrColorModel(dstColorModel)) { - return new BlendingBgrContext(this); - } - - throw new RasterFormatException("Incompatible color models:\n " + srcColorModel + "\n " + dstColorModel); - } - - private static abstract class BlendingContext implements CompositeContext { - protected final BlendComposite composite; - - private BlendingContext(BlendComposite composite) { - this.composite = composite; - } - - @Override - public void dispose() { - } - } - - private static class BlendingRgbContext extends BlendingContext { - private BlendingRgbContext(BlendComposite composite) { - super(composite); - } - - @Override - public void compose(Raster src, Raster dstIn, WritableRaster dstOut) { - int width = Math.min(src.getWidth(), dstIn.getWidth()); - int height = Math.min(src.getHeight(), dstIn.getHeight()); - - float alpha = composite.getAlpha(); - - int[] result = new int[4]; - int[] srcPixel = new int[4]; - int[] dstPixel = new int[4]; - int[] srcPixels = new int[width]; - int[] dstPixels = new int[width]; - - for (int y = 0; y < height; y++) { - src.getDataElements(0, y, width, 1, srcPixels); - dstIn.getDataElements(0, y, width, 1, dstPixels); - for (int x = 0; x < width; x++) { - // pixels are stored as INT_ARGB - // our arrays are [R, G, B, A] - int pixel = srcPixels[x]; - srcPixel[0] = (pixel >> 16) & 0xFF; - srcPixel[1] = (pixel >> 8) & 0xFF; - srcPixel[2] = (pixel ) & 0xFF; - srcPixel[3] = (pixel >> 24) & 0xFF; - - pixel = dstPixels[x]; - dstPixel[0] = (pixel >> 16) & 0xFF; - dstPixel[1] = (pixel >> 8) & 0xFF; - dstPixel[2] = (pixel ) & 0xFF; - dstPixel[3] = (pixel >> 24) & 0xFF; - - composite.getMode().blend(srcPixel, dstPixel, result); - - // mixes the result with the opacity - dstPixels[x] = ((int) (dstPixel[3] + (result[3] - dstPixel[3]) * alpha) & 0xFF) << 24 | - ((int) (dstPixel[0] + (result[0] - dstPixel[0]) * alpha) & 0xFF) << 16 | - ((int) (dstPixel[1] + (result[1] - dstPixel[1]) * alpha) & 0xFF) << 8 | - (int) (dstPixel[2] + (result[2] - dstPixel[2]) * alpha) & 0xFF; - } - dstOut.setDataElements(0, y, width, 1, dstPixels); - } - } - } - - private static class BlendingBgrContext extends BlendingContext { - private BlendingBgrContext(BlendComposite composite) { - super(composite); - } - - @Override - public void compose(Raster src, Raster dstIn, WritableRaster dstOut) { - int width = Math.min(src.getWidth(), dstIn.getWidth()); - int height = Math.min(src.getHeight(), dstIn.getHeight()); - - float alpha = composite.getAlpha(); - - int[] result = new int[4]; - int[] srcPixel = new int[4]; - int[] dstPixel = new int[4]; - int[] srcPixels = new int[width]; - int[] dstPixels = new int[width]; - - for (int y = 0; y < height; y++) { - src.getDataElements(0, y, width, 1, srcPixels); - dstIn.getDataElements(0, y, width, 1, dstPixels); - for (int x = 0; x < width; x++) { - // pixels are stored as INT_ABGR - // our arrays are [R, G, B, A] - int pixel = srcPixels[x]; - srcPixel[0] = (pixel ) & 0xFF; - srcPixel[1] = (pixel >> 8) & 0xFF; - srcPixel[2] = (pixel >> 16) & 0xFF; - srcPixel[3] = (pixel >> 24) & 0xFF; - - pixel = dstPixels[x]; - dstPixel[0] = (pixel ) & 0xFF; - dstPixel[1] = (pixel >> 8) & 0xFF; - dstPixel[2] = (pixel >> 16) & 0xFF; - dstPixel[3] = (pixel >> 24) & 0xFF; - - composite.getMode().blend(srcPixel, dstPixel, result); - - // mixes the result with the opacity - dstPixels[x] = ((int) (dstPixel[3] + (result[3] - dstPixel[3]) * alpha) & 0xFF) << 24 | - ((int) (dstPixel[0] + (result[0] - dstPixel[0]) * alpha) & 0xFF) | - ((int) (dstPixel[1] + (result[1] - dstPixel[1]) * alpha) & 0xFF) << 8 | - ((int) (dstPixel[2] + (result[2] - dstPixel[2]) * alpha) & 0xFF) << 16; - } - dstOut.setDataElements(0, y, width, 1, dstPixels); - } - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/graphics/ColorUtilities.java b/src/main/java/org/jdesktop/swingx/graphics/ColorUtilities.java deleted file mode 100644 index c23ed180ee..0000000000 --- a/src/main/java/org/jdesktop/swingx/graphics/ColorUtilities.java +++ /dev/null @@ -1,263 +0,0 @@ -/* - * $Id: ColorUtilities.java 1496 2006-10-22 03:26:24Z gfx $ - * - * Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy). - * - * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * Copyright (c) 2006 Romain Guy - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.jdesktop.swingx.graphics; - -import java.awt.*; - -/** - *

                    ColorUtilities contains a set of tools to perform - * common color operations easily.

                    - * - * @author Romain Guy - */ -public class ColorUtilities { - private ColorUtilities() { - } - - /** - *

                    Returns the HSL (Hue/Saturation/Luminance) equivalent of a given - * RGB color. All three HSL components are between 0.0 and 1.0.

                    - * - * @param color the RGB color to convert - * @return a new array of 3 floats corresponding to the HSL components - */ - public static float[] RGBtoHSL(Color color) { - return RGBtoHSL(color.getRed(), color.getGreen(), color.getBlue(), null); - } - - /** - *

                    Returns the HSL (Hue/Saturation/Luminance) equivalent of a given - * RGB color. All three HSL components are between 0.0 and 1.0.

                    - * - * @param color the RGB color to convert - * @param hsl a pre-allocated array of floats; can be null - * @return hsl if non-null, a new array of 3 floats otherwise - * @throws IllegalArgumentException if hsl has a length lower - * than 3 - */ - public static float[] RGBtoHSL(Color color, float[] hsl) { - return RGBtoHSL(color.getRed(), color.getGreen(), color.getBlue(), hsl); - } - - /** - *

                    Returns the HSL (Hue/Saturation/Luminance) equivalent of a given - * RGB color. All three HSL components are between 0.0 and 1.0.

                    - * - * @param r the red component, between 0 and 255 - * @param g the green component, between 0 and 255 - * @param b the blue component, between 0 and 255 - * @return a new array of 3 floats corresponding to the HSL components - */ - public static float[] RGBtoHSL(int r, int g, int b) { - return RGBtoHSL(r, g, b, null); - } - - /** - *

                    Returns the HSL (Hue/Saturation/Luminance) equivalent of a given - * RGB color. All three HSL components are floats between 0.0 and 1.0.

                    - * - * @param r the red component, between 0 and 255 - * @param g the green component, between 0 and 255 - * @param b the blue component, between 0 and 255 - * @param hsl a pre-allocated array of floats; can be null - * @return hsl if non-null, a new array of 3 floats otherwise - * @throws IllegalArgumentException if hsl has a length lower - * than 3 - */ - public static float[] RGBtoHSL(int r, int g, int b, float[] hsl) { - if (hsl == null) { - hsl = new float[3]; - } else if (hsl.length < 3) { - throw new IllegalArgumentException("hsl array must have a length of" + - " at least 3"); - } - - if (r < 0) r = 0; - else if (r > 255) r = 255; - if (g < 0) g = 0; - else if (g > 255) g = 255; - if (b < 0) b = 0; - else if (b > 255) b = 255; - - float var_R = (r / 255f); - float var_G = (g / 255f); - float var_B = (b / 255f); - - float var_Min; - float var_Max; - float del_Max; - - if (var_R > var_G) { - var_Min = var_G; - var_Max = var_R; - } else { - var_Min = var_R; - var_Max = var_G; - } - if (var_B > var_Max) { - var_Max = var_B; - } - if (var_B < var_Min) { - var_Min = var_B; - } - - del_Max = var_Max - var_Min; - - float H, S, L; - L = (var_Max + var_Min) / 2f; - - if (del_Max - 0.01f <= 0.0f) { - H = 0; - S = 0; - } else { - if (L < 0.5f) { - S = del_Max / (var_Max + var_Min); - } else { - S = del_Max / (2 - var_Max - var_Min); - } - - float del_R = (((var_Max - var_R) / 6f) + (del_Max / 2f)) / del_Max; - float del_G = (((var_Max - var_G) / 6f) + (del_Max / 2f)) / del_Max; - float del_B = (((var_Max - var_B) / 6f) + (del_Max / 2f)) / del_Max; - - if (var_R == var_Max) { - H = del_B - del_G; - } else if (var_G == var_Max) { - H = (1 / 3f) + del_R - del_B; - } else { - H = (2 / 3f) + del_G - del_R; - } - if (H < 0) { - H += 1; - } - if (H > 1) { - H -= 1; - } - } - - hsl[0] = H; - hsl[1] = S; - hsl[2] = L; - - return hsl; - } - - /** - *

                    Returns the RGB equivalent of a given HSL (Hue/Saturation/Luminance) - * color.

                    - * - * @param h the hue component, between 0.0 and 1.0 - * @param s the saturation component, between 0.0 and 1.0 - * @param l the luminance component, between 0.0 and 1.0 - * @return a new Color object equivalent to the HSL components - */ - public static Color HSLtoRGB(float h, float s, float l) { - int[] rgb = HSLtoRGB(h, s, l, null); - return new Color(rgb[0], rgb[1], rgb[2]); - } - - /** - *

                    Returns the RGB equivalent of a given HSL (Hue/Saturation/Luminance) - * color. All three RGB components are integers between 0 and 255.

                    - * - * @param h the hue component, between 0.0 and 1.0 - * @param s the saturation component, between 0.0 and 1.0 - * @param l the luminance component, between 0.0 and 1.0 - * @param rgb a pre-allocated array of ints; can be null - * @return rgb if non-null, a new array of 3 ints otherwise - * @throws IllegalArgumentException if rgb has a length lower - * than 3 - */ - public static int[] HSLtoRGB(float h, float s, float l, int[] rgb) { - if (rgb == null) { - rgb = new int[3]; - } else if (rgb.length < 3) { - throw new IllegalArgumentException("rgb array must have a length of" + - " at least 3"); - } - - if (h < 0) h = 0.0f; - else if (h > 1.0f) h = 1.0f; - if (s < 0) s = 0.0f; - else if (s > 1.0f) s = 1.0f; - if (l < 0) l = 0.0f; - else if (l > 1.0f) l = 1.0f; - - int R, G, B; - - if (s - 0.01f <= 0.0f) { - R = (int) (l * 255.0f); - G = (int) (l * 255.0f); - B = (int) (l * 255.0f); - } else { - float var_1, var_2; - if (l < 0.5f) { - var_2 = l * (1 + s); - } else { - var_2 = (l + s) - (s * l); - } - var_1 = 2 * l - var_2; - - R = (int) (255.0f * hue2RGB(var_1, var_2, h + (1.0f / 3.0f))); - G = (int) (255.0f * hue2RGB(var_1, var_2, h)); - B = (int) (255.0f * hue2RGB(var_1, var_2, h - (1.0f / 3.0f))); - } - - rgb[0] = R; - rgb[1] = G; - rgb[2] = B; - - return rgb; - } - - private static float hue2RGB(float v1, float v2, float vH) { - if (vH < 0.0f) { - vH += 1.0f; - } - if (vH > 1.0f) { - vH -= 1.0f; - } - if ((6.0f * vH) < 1.0f) { - return (v1 + (v2 - v1) * 6.0f * vH); - } - if ((2.0f * vH) < 1.0f) { - return (v2); - } - if ((3.0f * vH) < 2.0f) { - return (v1 + (v2 - v1) * ((2.0f / 3.0f) - vH) * 6.0f); - } - return (v1); - } -} diff --git a/src/main/java/org/jdesktop/swingx/graphics/FilterComposite.java b/src/main/java/org/jdesktop/swingx/graphics/FilterComposite.java deleted file mode 100644 index aab26f0c8d..0000000000 --- a/src/main/java/org/jdesktop/swingx/graphics/FilterComposite.java +++ /dev/null @@ -1,119 +0,0 @@ -package org.jdesktop.swingx.graphics; - -import org.jdesktop.swingx.util.Contract; - -import java.awt.*; -import java.awt.image.*; - -/** - * A {@code FilterComposite} allows the inclusion of arbitrary image filters during the paint - * processing of {@link java.awt.Graphics2D} events. By adding a filter composite, the src and - * destination images are render using a delegated {@code Composite}, then post-processed with the - * filters before returning the result back to the graphics context. This process adds overhead to - * the painting both is terms of time (the actual processing time) and memory (as a temporary raster - * must be created to store the intermediate state). Since it is possible to delegate to a filter - * composite from a filter composite, this may result slow or unresponsive painting. If you are - * attempting to render with many different filters, it may be better to have one filter composite - * with many filters (using a compound filter). - *

                    - * It was decided to use {@link BufferedImageOp} as the filter because many of these filters already - * exist. This gives high reusability to code. - * - * @author Karl Schaefer - * @see org.jdesktop.swingx.image.AbstractFilter - */ -@SuppressWarnings("nls") -public class FilterComposite implements Composite { - private static class FilterContext implements CompositeContext { - private ColorModel dstModel; - private CompositeContext ctx; - private BufferedImageOp filter; - - public FilterContext(ColorModel dstModel, CompositeContext ctx, BufferedImageOp filter) { - Contract.asNotNull(dstModel, "dstModel cannot be null"); - Contract.asNotNull(ctx, "context cannot be null"); - this.dstModel = dstModel; - this.ctx = ctx; - this.filter = filter; - } - - @Override - public void compose(Raster src, Raster dstIn, WritableRaster dstOut) { - if (filter == null) { - ctx.compose(src, dstIn, dstOut); - } else { - WritableRaster tempOut = dstModel.createCompatibleWritableRaster(dstOut.getWidth(), dstOut.getHeight()); - ctx.compose(src, dstIn, tempOut); - - filter.filter(new BufferedImage(dstModel, tempOut, dstModel.isAlphaPremultiplied(), null), - new BufferedImage(dstModel, dstOut, dstModel.isAlphaPremultiplied(), null)); - } - } - - @Override - public void dispose() { - ctx = null; - } - } - - private final Composite composite; - private BufferedImageOp filter; - - /** - * Creates an empty filter composite for the specified composite. - * - * @param composite - * the composite operation to perform prior to filtering - * @throws NullPointerException - * if {@code composite} is {@code null} - */ - public FilterComposite(Composite composite) { - this(composite, null); - } - - /** - * Creates a filter for the specified composite. - * - * @param composite - * the composite operation to perform prior to filtering - * @param filter - * the filter to apply to the composite result - * @throws NullPointerException - * if {@code composite} is {@code null} - */ - public FilterComposite(Composite composite, BufferedImageOp filter) { - Contract.asNotNull(composite, "composite cannot be null"); - this.composite = composite; - this.filter = filter; - } - - /** - * The filter to apply to the graphics context. - * - * @return the current filter - */ - public BufferedImageOp getFilter() { - return filter; - } - - /** - * Sets the filter for manipulating the graphics composites. - *

                    - * A {@code null} filter will result in no filtering. - * - * @param filter - * the new filter - */ - public void setFilter(BufferedImageOp filter) { - this.filter = filter; - } - - /** - * {@inheritDoc} - */ - @Override - public CompositeContext createContext(ColorModel srcColorModel, ColorModel dstColorModel, - RenderingHints hints) { - return new FilterContext(dstColorModel, composite.createContext(srcColorModel, dstColorModel, hints), filter); - } -} diff --git a/src/main/java/org/jdesktop/swingx/graphics/Graphics2DFacade.java b/src/main/java/org/jdesktop/swingx/graphics/Graphics2DFacade.java deleted file mode 100644 index ed4dfa4fd7..0000000000 --- a/src/main/java/org/jdesktop/swingx/graphics/Graphics2DFacade.java +++ /dev/null @@ -1,452 +0,0 @@ -package org.jdesktop.swingx.graphics; - -import org.jdesktop.swingx.util.Contract; - -import java.awt.*; -import java.awt.RenderingHints.Key; -import java.awt.font.FontRenderContext; -import java.awt.font.GlyphVector; -import java.awt.geom.AffineTransform; -import java.awt.image.BufferedImage; -import java.awt.image.BufferedImageOp; -import java.awt.image.ImageObserver; -import java.awt.image.RenderedImage; -import java.awt.image.renderable.RenderableImage; -import java.text.AttributedCharacterIterator; -import java.util.Map; - -/** - * A simple facade that forwards all graphics calls to a delegate. - * - * @author kschaefer - */ -public abstract class Graphics2DFacade extends Graphics2D { - private Graphics2D delegate; - - public Graphics2DFacade(Graphics2D delegate) { - this.delegate = Contract.asNotNull(delegate, "delegate cannot be null"); - } - - @Override - public Graphics create() { - return delegate.create(); - } - - @Override - public Graphics create(int x, int y, int width, int height) { - return delegate.create(x, y, width, height); - } - - @Override - public Color getColor() { - return delegate.getColor(); - } - - @Override - public void setColor(Color c) { - delegate.setColor(c); - } - - @Override - public void setPaintMode() { - delegate.setPaintMode(); - } - - @Override - public void setXORMode(Color c1) { - delegate.setXORMode(c1); - } - - @Override - public Font getFont() { - return delegate.getFont(); - } - - @Override - public void setFont(Font font) { - delegate.setFont(font); - } - - @Override - public FontMetrics getFontMetrics() { - return delegate.getFontMetrics(); - } - - @Override - public FontMetrics getFontMetrics(Font f) { - return delegate.getFontMetrics(f); - } - - @Override - public Rectangle getClipBounds() { - return delegate.getClipBounds(); - } - - @Override - public void clipRect(int x, int y, int width, int height) { - delegate.clipRect(x, y, width, height); - } - - @Override - public void setClip(int x, int y, int width, int height) { - delegate.setClip(x, y, width, height); - } - - @Override - public Shape getClip() { - return delegate.getClip(); - } - - @Override - public void setClip(Shape clip) { - delegate.setClip(clip); - } - - @Override - public void copyArea(int x, int y, int width, int height, int dx, int dy) { - delegate.copyArea(x, y, width, height, dx, dy); - } - - @Override - public void drawLine(int x1, int y1, int x2, int y2) { - delegate.drawLine(x1, y1, x2, y2); - } - - @Override - public void fillRect(int x, int y, int width, int height) { - delegate.fillRect(x, y, width, height); - } - - @Override - public void drawRect(int x, int y, int width, int height) { - delegate.drawRect(x, y, width, height); - } - - @Override - public void clearRect(int x, int y, int width, int height) { - delegate.clearRect(x, y, width, height); - } - - @Override - public void draw3DRect(int x, int y, int width, int height, boolean raised) { - delegate.draw3DRect(x, y, width, height, raised); - } - - @Override - public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { - delegate.drawRoundRect(x, y, width, height, arcWidth, arcHeight); - } - - @Override - public void fill3DRect(int x, int y, int width, int height, boolean raised) { - delegate.fill3DRect(x, y, width, height, raised); - } - - @Override - public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { - delegate.fillRoundRect(x, y, width, height, arcWidth, arcHeight); - } - - @Override - public void draw(Shape s) { - delegate.draw(s); - } - - @Override - public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) { - return delegate.drawImage(img, xform, obs); - } - - @Override - public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) { - delegate.drawImage(img, op, x, y); - } - - @Override - public void drawOval(int x, int y, int width, int height) { - delegate.drawOval(x, y, width, height); - } - - @Override - public void drawRenderedImage(RenderedImage img, AffineTransform xform) { - delegate.drawRenderedImage(img, xform); - } - - @Override - public void fillOval(int x, int y, int width, int height) { - delegate.fillOval(x, y, width, height); - } - - @Override - public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) { - delegate.drawArc(x, y, width, height, startAngle, arcAngle); - } - - @Override - public void drawRenderableImage(RenderableImage img, AffineTransform xform) { - delegate.drawRenderableImage(img, xform); - } - - @Override - public void drawString(String str, int x, int y) { - delegate.drawString(str, x, y); - } - - @Override - public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) { - delegate.fillArc(x, y, width, height, startAngle, arcAngle); - } - - @Override - public void drawString(String str, float x, float y) { - delegate.drawString(str, x, y); - } - - @Override - public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) { - delegate.drawPolyline(xPoints, yPoints, nPoints); - } - - @Override - public void drawString(AttributedCharacterIterator iterator, int x, int y) { - delegate.drawString(iterator, x, y); - } - - @Override - public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) { - delegate.drawPolygon(xPoints, yPoints, nPoints); - } - - @Override - public void drawString(AttributedCharacterIterator iterator, float x, float y) { - delegate.drawString(iterator, x, y); - } - - @Override - public void drawPolygon(Polygon p) { - delegate.drawPolygon(p); - } - - @Override - public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) { - delegate.fillPolygon(xPoints, yPoints, nPoints); - } - - @Override - public void drawGlyphVector(GlyphVector g, float x, float y) { - delegate.drawGlyphVector(g, x, y); - } - - @Override - public void fillPolygon(Polygon p) { - delegate.fillPolygon(p); - } - - @Override - public void fill(Shape s) { - delegate.fill(s); - } - - @Override - public boolean hit(Rectangle rect, Shape s, boolean onStroke) { - return delegate.hit(rect, s, onStroke); - } - - @Override - public void drawChars(char[] data, int offset, int length, int x, int y) { - delegate.drawChars(data, offset, length, x, y); - } - - @Override - public GraphicsConfiguration getDeviceConfiguration() { - return delegate.getDeviceConfiguration(); - } - - @Override - public void setComposite(Composite comp) { - delegate.setComposite(comp); - } - - @Override - public void drawBytes(byte[] data, int offset, int length, int x, int y) { - delegate.drawBytes(data, offset, length, x, y); - } - - @Override - public void setPaint(Paint paint) { - delegate.setPaint(paint); - } - - @Override - public boolean drawImage(Image img, int x, int y, ImageObserver observer) { - return delegate.drawImage(img, x, y, observer); - } - - @Override - public void setStroke(Stroke s) { - delegate.setStroke(s); - } - - @Override - public void setRenderingHint(Key hintKey, Object hintValue) { - delegate.setRenderingHint(hintKey, hintValue); - } - - @Override - public Object getRenderingHint(Key hintKey) { - return delegate.getRenderingHint(hintKey); - } - - @Override - public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) { - return delegate.drawImage(img, x, y, width, height, observer); - } - - @Override - public void setRenderingHints(Map hints) { - delegate.setRenderingHints(hints); - } - - @Override - public void addRenderingHints(Map hints) { - delegate.addRenderingHints(hints); - } - - @Override - public RenderingHints getRenderingHints() { - return delegate.getRenderingHints(); - } - - @Override - public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer) { - return delegate.drawImage(img, x, y, bgcolor, observer); - } - - @Override - public void translate(int x, int y) { - delegate.translate(x, y); - } - - @Override - public void translate(double tx, double ty) { - delegate.translate(tx, ty); - } - - @Override - public void rotate(double theta) { - delegate.rotate(theta); - } - - @Override - public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, - ImageObserver observer) { - return delegate.drawImage(img, x, y, width, height, bgcolor, observer); - } - - @Override - public void rotate(double theta, double x, double y) { - delegate.rotate(theta, x, y); - } - - @Override - public void scale(double sx, double sy) { - delegate.scale(sx, sy); - } - - @Override - public void shear(double shx, double shy) { - delegate.shear(shx, shy); - } - - @Override - public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, - int sx2, int sy2, ImageObserver observer) { - return delegate.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer); - } - - @Override - public void transform(AffineTransform Tx) { - delegate.transform(Tx); - } - - @Override - public void setTransform(AffineTransform Tx) { - delegate.setTransform(Tx); - } - - @Override - public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, - int sx2, int sy2, Color bgcolor, ImageObserver observer) { - return delegate.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, bgcolor, observer); - } - - @Override - public AffineTransform getTransform() { - return delegate.getTransform(); - } - - @Override - public Paint getPaint() { - return delegate.getPaint(); - } - - @Override - public Composite getComposite() { - return delegate.getComposite(); - } - - @Override - public void setBackground(Color color) { - delegate.setBackground(color); - } - - @Override - public Color getBackground() { - return delegate.getBackground(); - } - - @Override - public Stroke getStroke() { - return delegate.getStroke(); - } - - @Override - public void clip(Shape s) { - delegate.clip(s); - } - - @Override - public void dispose() { - delegate.dispose(); - } - - @Override - public FontRenderContext getFontRenderContext() { - return delegate.getFontRenderContext(); - } - - @Override - public void finalize() { - delegate.finalize(); - } - - @Override - public String toString() { - return delegate.toString(); - } - - @Override - public Rectangle getClipRect() { - return delegate.getClipRect(); - } - - @Override - public boolean hitClip(int x, int y, int width, int height) { - return delegate.hitClip(x, y, width, height); - } - - @Override - public Rectangle getClipBounds(Rectangle r) { - return delegate.getClipBounds(r); - } -} diff --git a/src/main/java/org/jdesktop/swingx/graphics/ReflectionRenderer.java b/src/main/java/org/jdesktop/swingx/graphics/ReflectionRenderer.java deleted file mode 100644 index abefac6bdb..0000000000 --- a/src/main/java/org/jdesktop/swingx/graphics/ReflectionRenderer.java +++ /dev/null @@ -1,512 +0,0 @@ -/* - * $Id: ReflectionRenderer.java 4082 2011-11-15 18:39:43Z kschaefe $ - * - * Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy). - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * Copyright (c) 2006 Romain Guy - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.jdesktop.swingx.graphics; - -import org.jdesktop.swingx.image.StackBlurFilter; -import org.jdesktop.swingx.util.GraphicsUtilities; - -import java.awt.*; -import java.awt.image.BufferedImage; -import java.beans.PropertyChangeListener; -import java.beans.PropertyChangeSupport; - -/** - *

                    A reflection renderer generates the reflection of a given picture. The - * result can be either the reflection itself, or an image containing both the - * source image and its reflection.

                    - *

                    Reflection Properties

                    - *

                    A reflection is defined by three properties: - *

                      - *
                    • opacity: the opacity of the reflection. You will usually - * change this valued according to the background color.
                    • - *
                    • length: the length of the reflection. The length is a fraction - * of the height of the source image.
                    • - *
                    • blur enabled: perfect reflections are hardly natural. You can - * blur the reflection to make it look a bit more natural.
                    • - *
                    - * You can set these properties using the provided mutators or the appropriate - * constructor. Here are two ways of creating a blurred reflection, with an - * opacity of 50% and a length of 30% the height of the original image: - *
                    - * ReflectionRenderer renderer = new ReflectionRenderer(0.5f, 0.3f, true);
                    - * // ..
                    - * renderer = new ReflectionRenderer();
                    - * renderer.setOpacity(0.5f);
                    - * renderer.setLength(0.3f);
                    - * renderer.setBlurEnabled(true);
                    - * 
                    - * The default constructor provides the following default values: - *
                      - *
                    • opacity: 35%
                    • - *
                    • length: 40%
                    • - *
                    • blur enabled: false
                    • - *

                    - *

                    Generating Reflections

                    - *

                    A reflection is generated as a BufferedImage from another - * BufferedImage. Once the renderer is set up, you must call - * {@link #createReflection(BufferedImage)} to actually generate - * the reflection: - *

                    - * ReflectionRenderer renderer = new ReflectionRenderer();
                    - * // renderer setup
                    - * BufferedImage reflection = renderer.createReflection(bufferedImage);
                    - * 

                    - *

                    The returned image contains only the reflection. You will have to append - * it to the source image at painting time to get a realistic results. You can - * also asks the rendered to return a picture composed of both the source image - * and its reflection: - *

                    - * ReflectionRenderer renderer = new ReflectionRenderer();
                    - * // renderer setup
                    - * BufferedImage reflection = renderer.appendReflection(bufferedImage);
                    - * 

                    - *

                    Properties Changes

                    - *

                    This renderer allows to register property change listeners with - * {@link #addPropertyChangeListener}. Listening to properties changes is very - * useful when you embed the renderer in a graphical component and give the API - * user the ability to access the renderer. By listening to properties changes, - * you can easily repaint the component when needed.

                    - *

                    Threading Issues

                    - *

                    ReflectionRenderer is not guaranteed to be thread-safe.

                    - * - * @author Romain Guy - */ -public class ReflectionRenderer { - /** - *

                    Identifies a change to the opacity used to render the reflection.

                    - */ - public static final String OPACITY_CHANGED_PROPERTY = "reflection_opacity"; - - /** - *

                    Identifies a change to the length of the rendered reflection.

                    - */ - public static final String LENGTH_CHANGED_PROPERTY = "reflection_length"; - - /** - *

                    Identifies a change to the blurring of the rendered reflection.

                    - */ - public static final String BLUR_ENABLED_CHANGED_PROPERTY = "reflection_blur"; - - // opacity of the reflection - private float opacity; - - // length of the reflection - private float length; - - // should the reflection be blurred? - private boolean blurEnabled; - - // notifies listeners of properties changes - private PropertyChangeSupport changeSupport; - private StackBlurFilter stackBlurFilter; - - /** - *

                    Creates a default good looking reflections generator. - * The default reflection renderer provides the following default values: - *

                      - *
                    • opacity: 35%
                    • - *
                    • length: 40%
                    • - *
                    • blurring: disabled with a radius of 1 pixel
                    • - *

                    - *

                    These properties provide a regular, good looking reflection.

                    - * - * @see #getOpacity() - * @see #setOpacity(float) - * @see #getLength() - * @see #setLength(float) - * @see #isBlurEnabled() - * @see #setBlurEnabled(boolean) - * @see #getBlurRadius() - * @see #setBlurRadius(int) - */ - public ReflectionRenderer() { - this(0.35f, 0.4f, false); - } - - /** - *

                    Creates a default good looking reflections generator with the - * specified opacity. The default reflection renderer provides the following - * default values: - *

                      - *
                    • length: 40%
                    • - *
                    • blurring: disabled with a radius of 1 pixel
                    • - *

                    - * - * @param opacity the opacity of the reflection, between 0.0 and 1.0 - * @see #getOpacity() - * @see #setOpacity(float) - * @see #getLength() - * @see #setLength(float) - * @see #isBlurEnabled() - * @see #setBlurEnabled(boolean) - * @see #getBlurRadius() - * @see #setBlurRadius(int) - */ - public ReflectionRenderer(float opacity) { - this(opacity, 0.4f, false); - } - - /** - *

                    Creates a reflections generator with the specified properties. Both - * opacity and length are numbers between 0.0 (0%) and 1.0 (100%). If the - * provided numbers are outside this range, they are clamped.

                    - *

                    Enabling the blur generates a different kind of reflections that might - * look more natural. The default blur radius is 1 pixel

                    - * - * @param opacity the opacity of the reflection - * @param length the length of the reflection - * @param blurEnabled if true, the reflection is blurred - * @see #getOpacity() - * @see #setOpacity(float) - * @see #getLength() - * @see #setLength(float) - * @see #isBlurEnabled() - * @see #setBlurEnabled(boolean) - * @see #getBlurRadius() - * @see #setBlurRadius(int) - */ - public ReflectionRenderer(float opacity, float length, boolean blurEnabled) { - //noinspection ThisEscapedInObjectConstruction - this.changeSupport = new PropertyChangeSupport(this); - this.stackBlurFilter = new StackBlurFilter(1); - - setOpacity(opacity); - setLength(length); - setBlurEnabled(blurEnabled); - } - - /** - *

                    Add a PropertyChangeListener to the listener list. The listener is - * registered for all properties. The same listener object may be added - * more than once, and will be called as many times as it is added. If - * listener is null, no exception is thrown and no action - * is taken.

                    - * - * @param listener the PropertyChangeListener to be added - */ - public void addPropertyChangeListener(PropertyChangeListener listener) { - changeSupport.addPropertyChangeListener(listener); - } - - /** - *

                    Remove a PropertyChangeListener from the listener list. This removes - * a PropertyChangeListener that was registered for all properties. If - * listener was added more than once to the same event source, - * it will be notified one less time after being removed. If - * listener is null, or was never added, no exception is thrown - * and no action is taken.

                    - * - * @param listener the PropertyChangeListener to be removed - */ - public void removePropertyChangeListener(PropertyChangeListener listener) { - changeSupport.removePropertyChangeListener(listener); - } - - /** - *

                    Gets the opacity used by the factory to generate reflections.

                    - *

                    The opacity is comprised between 0.0f and 1.0f; 0.0f being fully - * transparent and 1.0f fully opaque.

                    - * - * @return this factory's shadow opacity - * @see #getOpacity() - * @see #createReflection(BufferedImage) - * @see #appendReflection(BufferedImage) - */ - public float getOpacity() { - return opacity; - } - - /** - *

                    Sets the opacity used by the factory to generate reflections.

                    - *

                    Consecutive calls to {@link #createReflection} will all use this - * opacity until it is set again.

                    - *

                    The opacity is comprised between 0.0f and 1.0f; 0.0f being fully - * transparent and 1.0f fully opaque. If you provide a value out of these - * boundaries, it will be restrained to the closest boundary.

                    - * - * @param opacity the generated reflection opacity - * @see #setOpacity(float) - * @see #createReflection(BufferedImage) - * @see #appendReflection(BufferedImage) - */ - public void setOpacity(float opacity) { - float oldOpacity = this.opacity; - - if (opacity < 0.0f) { - opacity = 0.0f; - } else if (opacity > 1.0f) { - opacity = 1.0f; - } - - if (oldOpacity != opacity) { - this.opacity = opacity; - changeSupport.firePropertyChange(OPACITY_CHANGED_PROPERTY, - oldOpacity, - this.opacity); - } - } - - /** - *

                    Returns the length of the reflection. The result is a number between - * 0.0 and 1.0. This number is the fraction of the height of the source - * image that is used to compute the size of the reflection.

                    - * - * @return the length of the reflection, as a fraction of the source image - * height - * @see #setLength(float) - * @see #createReflection(BufferedImage) - * @see #appendReflection(BufferedImage) - */ - public float getLength() { - return length; - } - - /** - *

                    Sets the length of the reflection, as a fraction of the height of the - * source image.

                    - *

                    Consecutive calls to {@link #createReflection} will all use this - * opacity until it is set again.

                    - *

                    The opacity is comprised between 0.0f and 1.0f; 0.0f being fully - * transparent and 1.0f fully opaque. If you provide a value out of these - * boundaries, it will be restrained to the closest boundary.

                    - * - * @param length the length of the reflection, as a fraction of the source - * image height - * @see #getLength() - * @see #createReflection(BufferedImage) - * @see #appendReflection(BufferedImage) - */ - public void setLength(float length) { - float oldLength = this.length; - - if (length < 0.0f) { - length = 0.0f; - } else if (length > 1.0f) { - length = 1.0f; - } - - if (oldLength != length) { - this.length = length; - changeSupport.firePropertyChange(LENGTH_CHANGED_PROPERTY, - oldLength, - this.length); - } - } - - /** - *

                    Returns true if the blurring of the reflection is enabled, false - * otherwise. When blurring is enabled, the reflection is blurred to look - * more natural.

                    - * - * @return true if blur is enabled, false otherwise - * @see #setBlurEnabled(boolean) - * @see #createReflection(BufferedImage) - * @see #appendReflection(BufferedImage) - */ - public boolean isBlurEnabled() { - return blurEnabled; - } - - /** - *

                    Setting the blur to true will enable the blurring of the reflection - * when {@link #createReflection} is invoked.

                    - *

                    Enabling the blurring of the reflection can yield to more natural - * results which may or may not be better looking, depending on the source - * picture.

                    - *

                    Consecutive calls to {@link #createReflection} will all use this - * opacity until it is set again.

                    - * - * @param blurEnabled true to enable the blur, false otherwise - * @see #isBlurEnabled() - * @see #createReflection(BufferedImage) - * @see #appendReflection(BufferedImage) - */ - public void setBlurEnabled(boolean blurEnabled) { - if (blurEnabled != this.blurEnabled) { - boolean oldBlur = this.blurEnabled; - this.blurEnabled= blurEnabled; - - changeSupport.firePropertyChange(BLUR_ENABLED_CHANGED_PROPERTY, - oldBlur, - this.blurEnabled); - } - } - - /** - *

                    Returns the effective radius, in pixels, of the blur used by this - * renderer when {@link #isBlurEnabled()} is true.

                    - * - * @return the effective radius of the blur used when - * isBlurEnabled is true - * @see #isBlurEnabled() - * @see #setBlurEnabled(boolean) - * @see #setBlurRadius(int) - * @see #getBlurRadius() - */ - public int getEffectiveBlurRadius() { - return stackBlurFilter.getEffectiveRadius(); - } - - /** - *

                    Returns the radius, in pixels, of the blur used by this renderer when - * {@link #isBlurEnabled()} is true.

                    - * - * @return the radius of the blur used when isBlurEnabled - * is true - * @see #isBlurEnabled() - * @see #setBlurEnabled(boolean) - * @see #setBlurRadius(int) - * @see #getEffectiveBlurRadius() - */ - public int getBlurRadius() { - return stackBlurFilter.getRadius(); - } - - /** - *

                    Sets the radius, in pixels, of the blur used by this renderer when - * {@link #isBlurEnabled()} is true. This radius changes the size of the - * generated image when blurring is applied.

                    - * - * @param radius the radius, in pixels, of the blur - * @see #isBlurEnabled() - * @see #setBlurEnabled(boolean) - * @see #getBlurRadius() - */ - public void setBlurRadius(int radius) { - this.stackBlurFilter = new StackBlurFilter(radius); - } - - /** - *

                    Returns the source image and its reflection. The appearance of the - * reflection is defined by the opacity, the length and the blur - * properties.

                    - * *

                    The width of the generated image will be augmented when - * {@link #isBlurEnabled()} is true. The generated image will have the width - * of the source image plus twice the effective blur radius (see - * {@link #getEffectiveBlurRadius()}). The default blur radius is 1 so the - * width will be augmented by 6. You might need to take this into account - * at drawing time.

                    - *

                    The returned image height depends on the value returned by - * {@link #getLength()} and {@link #getEffectiveBlurRadius()}. For instance, - * if the length is 0.5 (or 50%) and the source image is 480 pixels high, - * then the reflection will be 246 (480 * 0.5 + 3 * 2) pixels high.

                    - *

                    You can create only the reflection by calling - * {@link #createReflection(BufferedImage)}.

                    - * - * @param image the source image - * @return the source image with its reflection below - * @see #createReflection(BufferedImage) - */ - public BufferedImage appendReflection(BufferedImage image) { - BufferedImage reflection = createReflection(image); - BufferedImage buffer = GraphicsUtilities.createCompatibleTranslucentImage( - reflection.getWidth(), image.getHeight() + reflection.getHeight()); - Graphics2D g2 = buffer.createGraphics(); - - try { - int effectiveRadius = isBlurEnabled() ? stackBlurFilter - .getEffectiveRadius() : 0; - g2.drawImage(image, effectiveRadius, 0, null); - g2.drawImage(reflection, 0, image.getHeight() - effectiveRadius, - null); - } finally { - g2.dispose(); - } - - reflection.flush(); - - return buffer; - } - - /** - *

                    Returns the reflection of the source image. The appearance of the - * reflection is defined by the opacity, the length and the blur - * properties.

                    - * *

                    The width of the generated image will be augmented when - * {@link #isBlurEnabled()} is true. The generated image will have the width - * of the source image plus twice the effective blur radius (see - * {@link #getEffectiveBlurRadius()}). The default blur radius is 1 so the - * width will be augmented by 6. You might need to take this into account - * at drawing time.

                    - *

                    The returned image height depends on the value returned by - * {@link #getLength()} and {@link #getEffectiveBlurRadius()}. For instance, - * if the length is 0.5 (or 50%) and the source image is 480 pixels high, - * then the reflection will be 246 (480 * 0.5 + 3 * 2) pixels high.

                    - *

                    The returned image contains only - * the reflection. You will have to append it to the source image to produce - * the illusion of a reflective environment. The method - * {@link #appendReflection(BufferedImage)} provides an easy - * way to create an image containing both the source and the reflection.

                    - * - * @param image the source image - * @return the reflection of the source image - * @see #appendReflection(BufferedImage) - */ - public BufferedImage createReflection(BufferedImage image) { - if (length == 0.0f) { - return GraphicsUtilities.createCompatibleTranslucentImage(1, 1); - } - - int blurOffset = isBlurEnabled() ? - stackBlurFilter.getEffectiveRadius() : 0; - int height = (int) (image.getHeight() * length); - - BufferedImage buffer = - GraphicsUtilities.createCompatibleTranslucentImage( - image.getWidth() + blurOffset * 2, - height + blurOffset * 2); - Graphics2D g2 = buffer.createGraphics(); - - try { - g2.translate(0, image.getHeight()); - g2.scale(1.0, -1.0); - - g2.drawImage(image, blurOffset, -blurOffset, null); - - g2.scale(1.0, -1.0); - g2.translate(0, -image.getHeight()); - - g2.setComposite(AlphaComposite.DstIn); - g2.setPaint(new GradientPaint(0.0f, 0.0f, new Color(0.0f, 0.0f, - 0.0f, getOpacity()), 0.0f, buffer.getHeight(), new Color( - 0.0f, 0.0f, 0.0f, 0.0f), true)); - g2.fillRect(0, 0, buffer.getWidth(), buffer.getHeight()); - } finally { - g2.dispose(); - } - - return isBlurEnabled() ? stackBlurFilter.filter(buffer, null) : - buffer; - } -} diff --git a/src/main/java/org/jdesktop/swingx/graphics/ShadowRenderer.java b/src/main/java/org/jdesktop/swingx/graphics/ShadowRenderer.java deleted file mode 100644 index 1cfaa2a760..0000000000 --- a/src/main/java/org/jdesktop/swingx/graphics/ShadowRenderer.java +++ /dev/null @@ -1,429 +0,0 @@ -/* - * $Id: ShadowRenderer.java 4082 2011-11-15 18:39:43Z kschaefe $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.graphics; - -import org.jdesktop.swingx.util.GraphicsUtilities; - -import java.awt.*; -import java.awt.image.BufferedImage; -import java.beans.PropertyChangeListener; -import java.beans.PropertyChangeSupport; - -import static org.jdesktop.swingx.util.GraphicsUtilities.createCompatibleTranslucentImage; - -/** - *

                    A shadow renderer generates a drop shadow for any given picture, respecting - * the transparency channel if present. The resulting picture contains the - * shadow only and to create a drop shadow effect you will need to stack the - * original picture and the shadow generated by the renderer.

                    - *

                    Shadow Properties

                    - *

                    A shadow is defined by three properties: - *

                      - *
                    • size: The size, in pixels, of the shadow. This property also - * defines the fuzziness.
                    • - *
                    • opacity: The opacity, between 0.0 and 1.0, of the shadow.
                    • - *
                    • color: The color of the shadow. Shadows are not meant to be - * black only.
                    • - *
                    - * You can set these properties using the provided mutators or the appropriate - * constructor. Here are two ways of creating a green shadow of size 10 and - * with an opacity of 50%: - *
                    - * ShadowRenderer renderer = new ShadowRenderer(10, 0.5f, Color.GREEN);
                    - * // ..
                    - * renderer = new ShadowRenderer();
                    - * renderer.setSize(10);
                    - * renderer.setOpacity(0.5f);
                    - * renderer.setColor(Color.GREEN);
                    - * 
                    - * The default constructor provides the following default values: - *
                      - *
                    • size: 5 pixels
                    • - *
                    • opacity: 50%
                    • - *
                    • color: Black
                    • - *

                    - *

                    Generating a Shadow

                    - *

                    A shadow is generated as a BufferedImage from another - * BufferedImage. Once the renderer is set up, you must call - * {@link #createShadow} to actually generate the shadow: - *

                    - * ShadowRenderer renderer = new ShadowRenderer();
                    - * // renderer setup
                    - * BufferedImage shadow = renderer.createShadow(bufferedImage);
                    - * 

                    - *

                    The generated image dimensions are computed as following:

                    - *
                    - * width  = imageWidth  + 2 * shadowSize
                    - * height = imageHeight + 2 * shadowSize
                    - * 
                    - *

                    Properties Changes

                    - *

                    This renderer allows to register property change listeners with - * {@link #addPropertyChangeListener}. Listening to properties changes is very - * useful when you embed the renderer in a graphical component and give the API - * user the ability to access the renderer. By listening to properties changes, - * you can easily repaint the component when needed.

                    - *

                    Threading Issues

                    - *

                    ShadowRenderer is not guaranteed to be thread-safe.

                    - * - * @author Romain Guy - * @author Sebastien Petrucci - */ -public class ShadowRenderer { - /** - *

                    Identifies a change to the size used to render the shadow.

                    - *

                    When the property change event is fired, the old value and the new - * value are provided as Integer instances.

                    - */ - public static final String SIZE_CHANGED_PROPERTY = "shadow_size"; - - /** - *

                    Identifies a change to the opacity used to render the shadow.

                    - *

                    When the property change event is fired, the old value and the new - * value are provided as Float instances.

                    - */ - public static final String OPACITY_CHANGED_PROPERTY = "shadow_opacity"; - - /** - *

                    Identifies a change to the color used to render the shadow.

                    - */ - public static final String COLOR_CHANGED_PROPERTY = "shadow_color"; - - // size of the shadow in pixels (defines the fuzziness) - private int size = 5; - - // opacity of the shadow - private float opacity = 0.5f; - - // color of the shadow - private Color color = Color.BLACK; - - // notifies listeners of properties changes - private PropertyChangeSupport changeSupport; - - /** - *

                    Creates a default good looking shadow generator. - * The default shadow renderer provides the following default values: - *

                      - *
                    • size: 5 pixels
                    • - *
                    • opacity: 50%
                    • - *
                    • color: Black
                    • - *

                    - *

                    These properties provide a regular, good looking shadow.

                    - */ - public ShadowRenderer() { - this(5, 0.5f, Color.BLACK); - } - - /** - *

                    A shadow renderer needs three properties to generate shadows. - * These properties are:

                    - *
                      - *
                    • size: The size, in pixels, of the shadow. This property also - * defines the fuzziness.
                    • - *
                    • opacity: The opacity, between 0.0 and 1.0, of the shadow.
                    • - *
                    • color: The color of the shadow. Shadows are not meant to be - * black only.
                    • - *
                    - * @param size the size of the shadow in pixels. Defines the fuzziness. - * @param opacity the opacity of the shadow. - * @param color the color of the shadow. - */ - public ShadowRenderer(final int size, final float opacity, final Color color) { - //noinspection ThisEscapedInObjectConstruction - changeSupport = new PropertyChangeSupport(this); - - setSize(size); - setOpacity(opacity); - setColor(color); - } - - /** - *

                    Add a PropertyChangeListener to the listener list. The listener is - * registered for all properties. The same listener object may be added - * more than once, and will be called as many times as it is added. If - * listener is null, no exception is thrown and no action - * is taken.

                    - * @param listener the PropertyChangeListener to be added - */ - public void addPropertyChangeListener(PropertyChangeListener listener) { - changeSupport.addPropertyChangeListener(listener); - } - - /** - *

                    Remove a PropertyChangeListener from the listener list. This removes - * a PropertyChangeListener that was registered for all properties. If - * listener was added more than once to the same event source, - * it will be notified one less time after being removed. If - * listener is null, or was never added, no exception is thrown - * and no action is taken.

                    - * @param listener the PropertyChangeListener to be removed - */ - public void removePropertyChangeListener(PropertyChangeListener listener) { - changeSupport.removePropertyChangeListener(listener); - } - - /** - *

                    Gets the color used by the renderer to generate shadows.

                    - * @return this renderer's shadow color - */ - public Color getColor() { - return color; - } - - /** - *

                    Sets the color used by the renderer to generate shadows.

                    - *

                    Consecutive calls to {@link #createShadow} will all use this color - * until it is set again.

                    - *

                    If the color provided is null, the previous color will be retained.

                    - * @param shadowColor the generated shadows color - */ - public void setColor(final Color shadowColor) { - if (shadowColor != null) { - Color oldColor = this.color; - this.color = shadowColor; - changeSupport.firePropertyChange(COLOR_CHANGED_PROPERTY, - oldColor, - this.color); - } - } - - /** - *

                    Gets the opacity used by the renderer to generate shadows.

                    - *

                    The opacity is comprised between 0.0f and 1.0f; 0.0f being fully - * transparent and 1.0f fully opaque.

                    - * @return this renderer's shadow opacity - */ - public float getOpacity() { - return opacity; - } - - /** - *

                    Sets the opacity used by the renderer to generate shadows.

                    - *

                    Consecutive calls to {@link #createShadow} will all use this opacity - * until it is set again.

                    - *

                    The opacity is comprised between 0.0f and 1.0f; 0.0f being fully - * transparent and 1.0f fully opaque. If you provide a value out of these - * boundaries, it will be restrained to the closest boundary.

                    - * @param shadowOpacity the generated shadows opacity - */ - public void setOpacity(final float shadowOpacity) { - float oldOpacity = this.opacity; - - if (shadowOpacity < 0.0) { - this.opacity = 0.0f; - } else if (shadowOpacity > 1.0f) { - this.opacity = 1.0f; - } else { - this.opacity = shadowOpacity; - } - - changeSupport.firePropertyChange(OPACITY_CHANGED_PROPERTY, - oldOpacity, - this.opacity); - } - - /** - *

                    Gets the size in pixel used by the renderer to generate shadows.

                    - * @return this renderer's shadow size - */ - public int getSize() { - return size; - } - - /** - *

                    Sets the size, in pixels, used by the renderer to generate shadows.

                    - *

                    The size defines the blur radius applied to the shadow to create the - * fuzziness.

                    - *

                    There is virtually no limit to the size. The size cannot be negative. - * If you provide a negative value, the size will be 0 instead.

                    - * @param shadowSize the generated shadows size in pixels (fuzziness) - */ - public void setSize(final int shadowSize) { - int oldSize = this.size; - - if (shadowSize < 0) { - this.size = 0; - } else { - this.size = shadowSize; - } - - changeSupport.firePropertyChange(SIZE_CHANGED_PROPERTY, - oldSize, - this.size); - } - - /** - *

                    Generates the shadow for a given picture and the current properties - * of the renderer.

                    - *

                    The generated image dimensions are computed as following:

                    - *
                    -     * width  = imageWidth  + 2 * shadowSize
                    -     * height = imageHeight + 2 * shadowSize
                    -     * 
                    - * @param image the picture from which the shadow must be cast - * @return the picture containing the shadow of image - */ - public BufferedImage createShadow(final BufferedImage image) { - // Written by Sesbastien Petrucci - int shadowSize = size * 2; - - int srcWidth = image.getWidth(); - int srcHeight = image.getHeight(); - - int dstWidth = srcWidth + shadowSize; - int dstHeight = srcHeight + shadowSize; - - int left = size; - int right = shadowSize - left; - - int yStop = dstHeight - right; - - int shadowRgb = color.getRGB() & 0x00FFFFFF; - int[] aHistory = new int[shadowSize]; - int historyIdx; - - int aSum; - - BufferedImage dst = createCompatibleTranslucentImage(dstWidth, dstHeight); - - int[] dstBuffer = new int[dstWidth * dstHeight]; - int[] srcBuffer = new int[srcWidth * srcHeight]; - - GraphicsUtilities.getPixels(image, 0, 0, srcWidth, srcHeight, srcBuffer); - - int lastPixelOffset = right * dstWidth; - float hSumDivider = 1.0f / shadowSize; - float vSumDivider = opacity / shadowSize; - - int[] hSumLookup = new int[256 * shadowSize]; - for (int i = 0; i < hSumLookup.length; i++) { - hSumLookup[i] = (int) (i * hSumDivider); - } - - int[] vSumLookup = new int[256 * shadowSize]; - for (int i = 0; i < vSumLookup.length; i++) { - vSumLookup[i] = (int) (i * vSumDivider); - } - - int srcOffset; - - // horizontal pass : extract the alpha mask from the source picture and - // blur it into the destination picture - for (int srcY = 0, dstOffset = left * dstWidth; srcY < srcHeight; srcY++) { - - // first pixels are empty - for (historyIdx = 0; historyIdx < shadowSize; ) { - aHistory[historyIdx++] = 0; - } - - aSum = 0; - historyIdx = 0; - srcOffset = srcY * srcWidth; - - // compute the blur average with pixels from the source image - for (int srcX = 0; srcX < srcWidth; srcX++) { - - int a = hSumLookup[aSum]; - dstBuffer[dstOffset++] = a << 24; // store the alpha value only - // the shadow color will be added in the next pass - - aSum -= aHistory[historyIdx]; // substract the oldest pixel from the sum - - // extract the new pixel ... - a = srcBuffer[srcOffset + srcX] >>> 24; - aHistory[historyIdx] = a; // ... and store its value into history - aSum += a; // ... and add its value to the sum - - if (++historyIdx >= shadowSize) { - historyIdx -= shadowSize; - } - } - - // blur the end of the row - no new pixels to grab - for (int i = 0; i < shadowSize; i++) { - - int a = hSumLookup[aSum]; - dstBuffer[dstOffset++] = a << 24; - - // substract the oldest pixel from the sum ... and nothing new to add ! - aSum -= aHistory[historyIdx]; - - if (++historyIdx >= shadowSize) { - historyIdx -= shadowSize; - } - } - } - - // vertical pass - for (int x = 0, bufferOffset = 0; x < dstWidth; x++, bufferOffset = x) { - - aSum = 0; - - // first pixels are empty - for (historyIdx = 0; historyIdx < left;) { - aHistory[historyIdx++] = 0; - } - - // and then they come from the dstBuffer - for (int y = 0; y < right; y++, bufferOffset += dstWidth) { - int a = dstBuffer[bufferOffset] >>> 24; // extract alpha - aHistory[historyIdx++] = a; // store into history - aSum += a; // and add to sum - } - - bufferOffset = x; - historyIdx = 0; - - // compute the blur avera`ge with pixels from the previous pass - for (int y = 0; y < yStop; y++, bufferOffset += dstWidth) { - - int a = vSumLookup[aSum]; - dstBuffer[bufferOffset] = a << 24 | shadowRgb; // store alpha value + shadow color - - aSum -= aHistory[historyIdx]; // substract the oldest pixel from the sum - - a = dstBuffer[bufferOffset + lastPixelOffset] >>> 24; // extract the new pixel ... - aHistory[historyIdx] = a; // ... and store its value into history - aSum += a; // ... and add its value to the sum - - if (++historyIdx >= shadowSize) { - historyIdx -= shadowSize; - } - } - - // blur the end of the column - no pixels to grab anymore - for (int y = yStop; y < dstHeight; y++, bufferOffset += dstWidth) { - - int a = vSumLookup[aSum]; - dstBuffer[bufferOffset] = a << 24 | shadowRgb; - - aSum -= aHistory[historyIdx]; // substract the oldest pixel from the sum - - if (++historyIdx >= shadowSize) { - historyIdx -= shadowSize; - } - } - } - - GraphicsUtilities.setPixels(dst, 0, 0, dstWidth, dstHeight, dstBuffer); - return dst; - } -} diff --git a/src/main/java/org/jdesktop/swingx/graphics/package-info.java b/src/main/java/org/jdesktop/swingx/graphics/package-info.java deleted file mode 100644 index ef3e217756..0000000000 --- a/src/main/java/org/jdesktop/swingx/graphics/package-info.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -/** - * Contains graphic utilities and effects for working with images. - */ -package org.jdesktop.swingx.graphics; - diff --git a/src/main/java/org/jdesktop/swingx/hyperlink/AbstractHyperlinkAction.java b/src/main/java/org/jdesktop/swingx/hyperlink/AbstractHyperlinkAction.java deleted file mode 100644 index 575581d2da..0000000000 --- a/src/main/java/org/jdesktop/swingx/hyperlink/AbstractHyperlinkAction.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * $Id: AbstractHyperlinkAction.java 3484 2009-09-03 15:55:34Z kleopatra $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.hyperlink; - -import org.jdesktop.swingx.action.AbstractActionExt; - -import java.awt.event.ItemEvent; - -/** - * Convenience implementation to simplify {@link org.jdesktop.swingx.JXHyperlink} configuration and - * provide minimal api.

                    - * - * @author Jeanette Winzenburg - */ -public abstract class AbstractHyperlinkAction extends AbstractActionExt { - - /** - * Key for the visited property value. - */ - public static final String VISITED_KEY = "visited"; - /** - * the object the actionPerformed can act on. - */ - protected T target; - - - /** - * Instantiates a LinkAction with null target. - * - */ - public AbstractHyperlinkAction () { - this(null); } - - /** - * Instantiates a LinkAction with a target of type targetClass. - * The visited property is initialized as defined by - * {@link AbstractHyperlinkAction#installTarget()} - * - * @param target the target this action should act on. - */ - public AbstractHyperlinkAction(T target) { - setTarget(target); - } - - /** - * Set the visited property. - * - * @param visited - */ - public void setVisited(boolean visited) { - putValue(VISITED_KEY, visited); - } - - /** - * - * @return visited state - */ - public boolean isVisited() { - Boolean visited = (Boolean) getValue(VISITED_KEY); - return Boolean.TRUE.equals(visited); - } - - - public T getTarget() { - return target; - } - - /** - * PRE: isTargetable(target) - * @param target - */ - public void setTarget(T target) { - T oldTarget = getTarget(); - uninstallTarget(); - this.target = target; - installTarget(); - firePropertyChange("target", oldTarget, getTarget()); - - } - - /** - * hook for subclasses to update internal state after - * a new target has been set.

                    - * - * Subclasses are free to decide the details. - * Here: - *

                      - *
                    • the text property is set to target.toString or empty String if - * the target is null - *
                    • visited is set to false. - *
                    - */ - protected void installTarget() { - setName(target != null ? target.toString() : "" ); - setVisited(false); - } - - /** - * hook for subclasses to cleanup before the old target - * is overwritten.

                    - * - * Subclasses are free to decide the details. - * Here: does nothing. - */ - protected void uninstallTarget() { - - } - - @Override - public void itemStateChanged(ItemEvent e) { - // do nothing - } - - /** - * Set the state property. - * Overridden to to nothing. - * PENDING: really? - * @param state if true then this action will fire ItemEvents - */ - @Override - public void setStateAction(boolean state) { - } - - - -} diff --git a/src/main/java/org/jdesktop/swingx/hyperlink/EditorPaneLinkVisitor.java b/src/main/java/org/jdesktop/swingx/hyperlink/EditorPaneLinkVisitor.java deleted file mode 100644 index eee6b3c517..0000000000 --- a/src/main/java/org/jdesktop/swingx/hyperlink/EditorPaneLinkVisitor.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * $Id: EditorPaneLinkVisitor.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.hyperlink; - -import org.jdesktop.swingx.JXEditorPane; - -import javax.swing.*; -import javax.swing.event.HyperlinkEvent; -import javax.swing.event.HyperlinkListener; -import javax.swing.text.Document; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.IOException; -import java.net.URL; - - -/** - * A ActionListener using a JXEditorPane to "visit" a LinkModel. - * - * adds an internal HyperlinkListener to visit links contained - * in the document. - * - * @author Jeanette Winzenburg - */ -public class EditorPaneLinkVisitor implements ActionListener { - private JXEditorPane editorPane; - private HyperlinkListener hyperlinkListener; - private LinkModel internalLink; - - public EditorPaneLinkVisitor() { - this(null); - } - - public EditorPaneLinkVisitor(JXEditorPane pane) { - if (pane == null) { - pane = createDefaultEditorPane(); - } - this.editorPane = pane; - pane.addHyperlinkListener(getHyperlinkListener()); - } - - - public JXEditorPane getOutputComponent() { - return editorPane; - } - - @Override - public void actionPerformed(ActionEvent e) { - if (e.getSource() instanceof LinkModel) { - final LinkModel link = (LinkModel) e.getSource(); - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - visit(link); - - } - }); - } - - } - - public void visit(LinkModel link) { - try { - // make sure to reload - editorPane.getDocument().putProperty(Document.StreamDescriptionProperty, null); - // JW: editorPane defaults to asynchronous loading - // no need to explicitly start a thread - really? - editorPane.setPage(link.getURL()); - link.setVisited(true); - } catch (IOException e1) { - editorPane.setText("Error 404: couldn't show " + link.getURL() + " "); - } - } - - protected JXEditorPane createDefaultEditorPane() { - final JXEditorPane editorPane = new JXEditorPane(); - editorPane.setEditable(false); - editorPane.setContentType("text/html"); - return editorPane; - } - - protected HyperlinkListener getHyperlinkListener() { - if (hyperlinkListener == null) { - hyperlinkListener = createHyperlinkListener(); - } - return hyperlinkListener; - } - - protected HyperlinkListener createHyperlinkListener() { - return new HyperlinkListener() { - @Override - public void hyperlinkUpdate(HyperlinkEvent e) { - if (HyperlinkEvent.EventType.ACTIVATED == e.getEventType()) { - visitInternal(e.getURL()); - } - - } - - }; - } - - protected LinkModel getInternalLink() { - if (internalLink == null) { - internalLink = new LinkModel("internal"); - } - return internalLink; - } - - protected void visitInternal(URL url) { - try { - getInternalLink().setURL(url); - visit(getInternalLink()); - } catch (Exception e) { - // todo: error feedback - } - } - - -} diff --git a/src/main/java/org/jdesktop/swingx/hyperlink/HyperlinkAction.java b/src/main/java/org/jdesktop/swingx/hyperlink/HyperlinkAction.java deleted file mode 100644 index 7e29e52398..0000000000 --- a/src/main/java/org/jdesktop/swingx/hyperlink/HyperlinkAction.java +++ /dev/null @@ -1,310 +0,0 @@ -/* - * $Id$ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.hyperlink; - -import java.awt.*; -import java.awt.Desktop.Action; -import java.awt.event.ActionEvent; -import java.io.IOException; -import java.net.URI; -import java.util.logging.Logger; - -/** - * A implementation wrapping Desktop actions BROWSE and MAIL, that is - * URI-related. - * - * @author Jeanette Winzenburg - */ -public class HyperlinkAction extends AbstractHyperlinkAction { - @SuppressWarnings("unused") - private static final Logger LOG = Logger.getLogger(HyperlinkAction.class - .getName()); - - private Action desktopAction; - private URIVisitor visitor; - - /** - * Factory method to create and return a HyperlinkAction for the given uri. Tries - * to guess the appropriate type from the uri. If uri is not null and has a - * scheme of mailto, create one of type Mail. In all other cases, creates one - * for BROWSE. - * - * @param uri to uri to create a HyperlinkAction for, maybe null. - * @return a HyperlinkAction for the given URI. - * @throws HeadlessException if {@link - * GraphicsEnvironment#isHeadless()} returns {@code true} - * @throws UnsupportedOperationException if the current platform doesn't support - * Desktop - */ - public static HyperlinkAction createHyperlinkAction(URI uri) { - Action type = isMailURI(uri) ? Action.MAIL : Action.BROWSE; - return createHyperlinkAction(uri, type); - } - - /** - * Creates and returns a HyperlinkAction with the given target and action type. - * @param uri the target uri, maybe null. - * @param desktopAction the type of desktop action this class should perform, must be - * BROWSE or MAIL - * @return a HyperlinkAction - * @throws HeadlessException if {@link - * GraphicsEnvironment#isHeadless()} returns {@code true} - * @throws UnsupportedOperationException if the current platform doesn't support - * Desktop - * @throws IllegalArgumentException if unsupported action type - */ - public static HyperlinkAction createHyperlinkAction(URI uri, Action type) { - return new HyperlinkAction(uri, type); - } - - /** - * @param uri - * @return - */ - private static boolean isMailURI(URI uri) { - return uri != null && "mailto".equalsIgnoreCase(uri.getScheme()); - } - - /** - * Instantiates a HyperlinkAction with action type BROWSE. - * - * @throws HeadlessException if {@link - * GraphicsEnvironment#isHeadless()} returns {@code true} - * @throws UnsupportedOperationException if the current platform doesn't support - * Desktop - * @throws IllegalArgumentException if unsupported action type - */ - public HyperlinkAction() { - this(Action.BROWSE); - } - - /** - * Instantiates a HyperlinkAction with the given action type. - * - * @param desktopAction the type of desktop action this class should perform, must be - * BROWSE or MAIL - * @throws HeadlessException if {@link - * GraphicsEnvironment#isHeadless()} returns {@code true} - * @throws UnsupportedOperationException if the current platform doesn't support - * Desktop - * @throws IllegalArgumentException if unsupported action type - */ - public HyperlinkAction(Action desktopAction) { - this(null, desktopAction); - } - - /** - * - * @param uri the target uri, maybe null. - * @param desktopAction the type of desktop action this class should perform, must be - * BROWSE or MAIL - * @throws HeadlessException if {@link - * GraphicsEnvironment#isHeadless()} returns {@code true} - * @throws UnsupportedOperationException if the current platform doesn't support - * Desktop - * @throws IllegalArgumentException if unsupported action type - */ - public HyperlinkAction(URI uri, Action desktopAction) { - super(); - if (!Desktop.isDesktopSupported()) { - throw new UnsupportedOperationException("Desktop API is not " + - "supported on the current platform"); - } - if (desktopAction != Action.BROWSE && desktopAction != Action.MAIL) { - throw new IllegalArgumentException("Illegal action type: " + desktopAction + - ". Must be BROWSE or MAIL"); - } - this.desktopAction = desktopAction; - getURIVisitor(); - setTarget(uri); - } - - /** - * {@inheritDoc}

                    - * - * Implemented to perform the appropriate Desktop action if supported on the current - * target. Sets the visited property to true if the desktop action doesn't throw - * an exception or to false if it did. - * - * Does nothing if the action isn't supported. - */ - @Override - public void actionPerformed(ActionEvent e) { - if (!getURIVisitor().isEnabled(getTarget())) return; - try { - getURIVisitor().visit(getTarget()); - setVisited(true); - } catch (IOException e1) { - setVisited(false); - LOG.fine("cant visit Desktop " + e); - } - } - - /** - * @return - */ - public Action getDesktopAction() { - return desktopAction; - } - - - @Override - protected void installTarget() { - // doohh ... this is called from super's constructor before we are - // fully initialized - if (visitor == null) return; - super.installTarget(); - updateEnabled(); - } - - /** - * - */ - private void updateEnabled() { - setEnabled(getURIVisitor().isEnabled(getTarget())); - } - - /** - * @return - */ - private URIVisitor getURIVisitor() { - if (visitor == null) { - visitor = createURIVisitor(); - } - return visitor; - } - - /** - * @return - */ - private URIVisitor createURIVisitor() { - return getDesktopAction() == Action.BROWSE ? - new BrowseVisitor() : new MailVisitor(); - } - - /** - * Thin wrapper around Desktop functionality to allow uniform handling of - * different actions in HyperlinkAction. - * - */ - private abstract class URIVisitor { - protected boolean desktopSupported = Desktop.isDesktopSupported(); - - /** - * Returns a boolean indicating whether the action is supported on the - * given URI. This implementation returns true if both the Desktop is - * generally supported and isActionSupported(). - * - * PENDING JW: hmm ... which class exactly has to check for valid combination - * of Action and URI? - * - * @param uri - * @return - * - * @see #isActionSupported() - */ - public boolean isEnabled(URI uri) { - return desktopSupported && isActionSupported(); - } - - /** - * Visits the given URI via Desktop functionality. Must not be called if not - * enabled. - * - * @param uri the URI to visit - * @throws IOException if the Desktop method throws IOException. - * - */ - public abstract void visit(URI uri) throws IOException; - - /** - * Returns a boolean indicating if the action is supported by the current - * Desktop. - * - * @return true if the Action is supported by the current desktop, false - * otherwise. - */ - protected abstract boolean isActionSupported(); - } - - private class BrowseVisitor extends URIVisitor { - - /** - * {@inheritDoc}

                    - * - * Implemented to message the browse method of Desktop. - */ - @Override - public void visit(URI uri) throws IOException { - Desktop.getDesktop().browse(uri); - } - - /** - * {@inheritDoc}

                    - * - * Implemented to query the Desktop for support of BROWSE action. - */ - @Override - protected boolean isActionSupported() { - return Desktop.getDesktop().isSupported(Action.BROWSE); - } - - /** - * {@inheritDoc}

                    - * - * Implemented to guard against null URI in addition to super. - */ - @Override - public boolean isEnabled(URI uri) { - return uri != null && super.isEnabled(uri); - } - - - } - - private class MailVisitor extends URIVisitor { - - /** - * {@inheritDoc}

                    - * - * Implemented to message the mail function of Desktop. - */ - @Override - public void visit(URI uri) throws IOException { - if (uri == null) { - Desktop.getDesktop().mail(); - } else { - Desktop.getDesktop().mail(uri); - } - } - /** - * {@inheritDoc}

                    - * - * Implemented to query the Desktop for support of MAIL action. - */ - @Override - protected boolean isActionSupported() { - return Desktop.getDesktop().isSupported(Action.MAIL); - } - - } -} diff --git a/src/main/java/org/jdesktop/swingx/hyperlink/LinkModel.java b/src/main/java/org/jdesktop/swingx/hyperlink/LinkModel.java deleted file mode 100644 index 4c3fc35c7e..0000000000 --- a/src/main/java/org/jdesktop/swingx/hyperlink/LinkModel.java +++ /dev/null @@ -1,317 +0,0 @@ -/* - * $Id: LinkModel.java 2951 2008-06-17 10:07:49Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.hyperlink; - -import java.beans.PropertyChangeListener; -import java.beans.PropertyChangeSupport; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.logging.Logger; - -/** - * An bean which represents an URL link. - * - * Text, URL and visited are bound properties. Compares by Text. - * - * @author Mark Davidson - * @author Jeanette Winzenburg - */ -public class LinkModel implements Comparable { - - private static final Logger LOG = Logger.getLogger(LinkModel.class - .getName()); - - private String text; // display text - - private URL url; // url of the link - - private String target; // url target frame - - private boolean visited = false; - - private PropertyChangeSupport propertyChangeSupport; - - public static final String VISITED_PROPERTY = "visited"; - - // hack - this class assumes that the url always != null - // need to cleanup - private static String defaultURLString = "https://jdnc.dev.java.net"; - - private static URL defaultURL; - - /** - * - * @param text - * @param target - * @param url - */ - public LinkModel(String text, String target, URL url) { - setText(text); - setTarget(target); - setURL(url != null ? url : getDefaultURL()); - } - - public LinkModel() { - this(" ", null, null); - } - - public LinkModel(String text) { - this(text, null, null); - } - - /** - * @param text text to that a renderer would display - * @param target the target that a URL should load into. - * @param template a string that represents a URL with - * &{N} place holders for string substitution - * @param args an array of strings which will be used for substitition - */ - public LinkModel(String text, String target, String template, String[] args) { - setText(text); - setTarget(target); - setURL(createURL(template, args)); - } - - /** - * Set the display text. - */ - public void setText(String text) { - String old = getText(); - this.text = text; - firePropertyChange("text", old, getText()); - } - - public String getText() { - if (text != null) { - return text; - } else if (url != null) { - return getURL().toString(); - } - return null; - } - - public void setURLString(String howToURLString) { - URL url = null; - try { - url = new URL(howToURLString); - } catch (MalformedURLException e) { - url = getDefaultURL(); - LOG.warning("the given urlString is malformed: " + howToURLString + - "\n falling back to default url: " + url); - } - setURL(url); - } - - private URL getDefaultURL() { - if (defaultURL == null) { - try { - defaultURL = new URL(defaultURLString); - } catch (MalformedURLException e) { - LOG.fine("should not happen - defaultURL is wellFormed: " - + defaultURLString); - } - } - return defaultURL; - } - - /** - * Set the url and resets the visited flag. - * - * Think: keep list of visited urls here? - */ - public void setURL(URL url) { - if (url == null) { - throw new IllegalArgumentException("URL for link cannot be null"); - } - if (url.equals(getURL())) - return; - URL old = getURL(); - this.url = url; - firePropertyChange("URL", old, url); - setVisited(false); - } - - public URL getURL() { - return url; - } - - /** - * Create a URL from a template string that has place holders and an array - * of strings which will be substituted into the place holders. The place - * holders are represented as - * - * @{N} where N = { 1..n } - *

                    - * For example, if the template contains a string like: - * http://bugz.sfbay/cgi-bin/showbug?cat=@{1}&sub_cat=@{2} and a two - * arg array contains: java, classes_swing The resulting URL will be: - * http://bugz.sfbay/cgi-bin/showbug?cat=java&sub_cat=classes_swing - *

                    - * @param template a url string that contains the placeholders - * @param args an array of strings that will be substituted - */ - private URL createURL(String template, String[] args) { - URL url = null; - try { - String urlStr = template; - for (int i = 0; i < args.length; i++) { - urlStr = urlStr.replaceAll("@\\{" + (i + 1) + "\\}", args[i]); - } - url = new URL(urlStr); - } catch (MalformedURLException ex) { - // - } - return url; - } - - /** - * Set the target that the URL should load into. This can be a uri - * representing another control or the name of a window or special targets. - * See: http://www.w3c.org/TR/html401/present/frames.html#adef-target - */ - public void setTarget(String target) { - this.target = target; - } - - /** - * Return the target for the URL. - * - * @return value of the target. If null then "_blank" will be returned. - */ - public String getTarget() { - if (target != null) { - return target; - } else { - return "_blank"; - } - } - - /** - * Sets a flag to indicate if the link has been visited. The state of this - * flag can be used to render the color of the link. - */ - public void setVisited(boolean visited) { - boolean old = getVisited(); - this.visited = visited; - firePropertyChange(VISITED_PROPERTY, old, getVisited()); - } - - public boolean getVisited() { - return visited; - } - - // ---------------------- property change notification - - public void addPropertyChangeListener(PropertyChangeListener l) { - getPropertyChangeSupport().addPropertyChangeListener(l); - - } - - public void removePropertyChangeListener(PropertyChangeListener l) { - if (propertyChangeSupport == null) - return; - propertyChangeSupport.removePropertyChangeListener(l); - } - - protected void firePropertyChange(String property, Object oldValue, - Object newValue) { - if (propertyChangeSupport == null) - return; - propertyChangeSupport.firePropertyChange(property, oldValue, newValue); - } - - protected void firePropertyChange(String property, boolean oldValue, - boolean newValue) { - if (propertyChangeSupport == null) - return; - propertyChangeSupport.firePropertyChange(property, oldValue, newValue); - - } - - private PropertyChangeSupport getPropertyChangeSupport() { - if (propertyChangeSupport == null) { - propertyChangeSupport = new PropertyChangeSupport(this); - } - return propertyChangeSupport; - } - - // Comparable interface for sorting. - public int compareTo(Object obj) { - if (obj == null) { - return 1; - } - if (obj == this) { - return 0; - } - return text.compareTo(((LinkModel) obj).text); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj != null && obj instanceof LinkModel) { - LinkModel other = (LinkModel) obj; - if (!getText().equals(other.getText())) { - return false; - } - - if (!getTarget().equals(other.getTarget())) { - return false; - } - - return getURL().equals(other.getURL()); - } - return false; - } - - @Override - public int hashCode() { - int result = 7; - - result = 37 * result + ((getText() == null) ? 0 : getText().hashCode()); - result = 37 * result - + ((getTarget() == null) ? 1 : getTarget().hashCode()); - result = 37 * result + ((getURL() == null) ? 2 : getURL().hashCode()); - - return result; - } - - @Override - public String toString() { - - StringBuffer buffer = new StringBuffer("["); - // RG: Fix for J2SE 5.0; Can't cascade append() calls because - // return type in StringBuffer and AbstractStringBuilder are different - buffer.append("url="); - buffer.append(url); - buffer.append(", target="); - buffer.append(target); - buffer.append(", text="); - buffer.append(text); - buffer.append("]"); - - return buffer.toString(); - } - -} diff --git a/src/main/java/org/jdesktop/swingx/hyperlink/LinkModelAction.java b/src/main/java/org/jdesktop/swingx/hyperlink/LinkModelAction.java deleted file mode 100644 index b697479a32..0000000000 --- a/src/main/java/org/jdesktop/swingx/hyperlink/LinkModelAction.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * $Id: LinkModelAction.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.hyperlink; - -import javax.swing.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; - - -/** - * Specialized LinkAction for a target of type {@link LinkModel}. - *

                    - * - * This action delegates actionPerformed to vistingDelegate. - * - * PENDING: move to swingx package? - * - * @author Jeanette Winzenburg - */ -public class LinkModelAction extends AbstractHyperlinkAction { - - private ActionListener delegate; - public static final String VISIT_ACTION = "visit"; - private PropertyChangeListener linkListener; - - - public LinkModelAction() { - this((T) null); - } - - public LinkModelAction(ActionListener visitingDelegate) { - this(null, visitingDelegate); - } - - public LinkModelAction(T target) { - this(target, null); - } - - public LinkModelAction(T target, ActionListener visitingDelegate) { - super(target); - setVisitingDelegate(visitingDelegate); - } - - /** - * The delegate to invoke on actionPerformed. - *

                    - * The delegates actionPerformed is invoked with an ActionEvent - * having the target as source. Delegates are expected to - * cope gracefully with the T. - *

                    - * - * PENDING: JW - How to formalize? - * - * @param delegate the action invoked on the target. - */ - public void setVisitingDelegate(ActionListener delegate) { - this.delegate = delegate; - } - - /** - * This action delegates to the visitingDelegate if both - * delegate and target are != null, does nothing otherwise. - * The actionEvent carries the target as source. - * - * PENDING: pass through a null target? - most probably! - * - * - * - */ - @Override - public void actionPerformed(ActionEvent e) { - if ((delegate != null) && (getTarget() != null)) { - delegate.actionPerformed(new ActionEvent(getTarget(), ActionEvent.ACTION_PERFORMED, VISIT_ACTION)); - } - - } - - /** - * installs a propertyChangeListener on the target and - * updates the visual properties from the target. - */ - @Override - protected void installTarget() { - if (getTarget() != null) { - getTarget().addPropertyChangeListener(getTargetListener()); - } - updateFromTarget(); - } - - /** - * removes the propertyChangeListener.

                    - * - * Implementation NOTE: this does not clean-up internal state! There is - * no need to because updateFromTarget handles both null and not-null - * targets. Hmm... - * - */ - @Override - protected void uninstallTarget() { - if (getTarget() == null) return; - getTarget().removePropertyChangeListener(getTargetListener()); - } - - protected void updateFromTarget() { - if (getTarget() != null) { - putValue(Action.NAME, getTarget().getText()); - putValue(Action.SHORT_DESCRIPTION, getTarget().getURL().toString()); - putValue(VISITED_KEY, getTarget().getVisited()); - } else { - Object[] keys = getKeys(); - if (keys == null) return; - for (int i = 0; i < keys.length; i++) { - putValue(keys[i].toString(), null); - } - } - } - - private PropertyChangeListener getTargetListener() { - if (linkListener == null) { - linkListener = new PropertyChangeListener() { - - @Override - public void propertyChange(PropertyChangeEvent evt) { - updateFromTarget(); - } - - }; - } - return linkListener; - } - - -} diff --git a/src/main/java/org/jdesktop/swingx/icon/ColumnControlIcon.java b/src/main/java/org/jdesktop/swingx/icon/ColumnControlIcon.java deleted file mode 100644 index 443e222eef..0000000000 --- a/src/main/java/org/jdesktop/swingx/icon/ColumnControlIcon.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * $Id: ColumnControlIcon.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.icon; - -import javax.swing.*; -import javax.swing.plaf.UIResource; -import java.awt.*; - -/** - * Icon class for rendering icon which indicates user control of - * column visibility. - * @author Amy Fowler - * @version 1.0 - */ -public class ColumnControlIcon implements Icon, UIResource { - private int width = 10; - private int height = 10; - - /** TODO: need to support small, medium, large */ - public ColumnControlIcon() { - } - - @Override - public int getIconWidth() { - return width; - } - - @Override - public int getIconHeight() { - return height; - } - - @Override - public void paintIcon(Component c, Graphics g, int x, int y) { - Color color = c.getForeground(); - g.setColor(color); - - // draw horizontal lines - g.drawLine(x, y, x+8, y); - g.drawLine(x, y+2, x+8, y+2); - g.drawLine(x, y+8, x+2, y+8); - - // draw vertical lines - g.drawLine(x, y+1, x, y+7); - g.drawLine(x+4, y+1, x+4, y+4); - g.drawLine(x+8, y+1, x+8, y+4); - - // draw arrow - g.drawLine(x+3, y+6, x+9, y+6); - g.drawLine(x+4, y+7, x+8, y+7); - g.drawLine(x+5, y+8, x+7, y+8); - g.drawLine(x+6, y+9, x+6, y+9); - - } - - public static void main(String args[]) { - JFrame frame = new JFrame(); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - JLabel label = new JLabel(new ColumnControlIcon()); - frame.getContentPane().add(BorderLayout.CENTER, label); - frame.pack(); - frame.setVisible(true); - } - -} diff --git a/src/main/java/org/jdesktop/swingx/icon/EmptyIcon.java b/src/main/java/org/jdesktop/swingx/icon/EmptyIcon.java deleted file mode 100644 index 7cd2e26e47..0000000000 --- a/src/main/java/org/jdesktop/swingx/icon/EmptyIcon.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * $Id: EmptyIcon.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.icon; - -import javax.swing.*; -import java.awt.*; -import java.io.Serializable; - -/** - * An empty icon with arbitrary width and height. - */ -public final class EmptyIcon implements Icon, Serializable { - - private int width; - - private int height; - - public EmptyIcon() { - this(0, 0); - } - - public EmptyIcon(int width, int height) { - this.width = width; - this.height = height; - } - - @Override - public int getIconHeight() { - return height; - } - - @Override - public int getIconWidth() { - return width; - } - - @Override - public void paintIcon(Component c, Graphics g, int x, int y) { - } - -} diff --git a/src/main/java/org/jdesktop/swingx/icon/PainterIcon.java b/src/main/java/org/jdesktop/swingx/icon/PainterIcon.java deleted file mode 100644 index 38cca8decf..0000000000 --- a/src/main/java/org/jdesktop/swingx/icon/PainterIcon.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * $Id: PainterIcon.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.icon; - -import org.jdesktop.swingx.painter.Painter; - -import javax.swing.*; -import java.awt.*; - -public class PainterIcon implements Icon { - Dimension size; - private Painter painter; - public PainterIcon(Dimension size) { - this.size = size; - } - - @Override - public int getIconHeight() { - return size.height; - } - - @Override - public int getIconWidth() { - return size.width; - } - - - @Override - public void paintIcon(Component c, Graphics g, int x, int y) { - if (getPainter() != null && g instanceof Graphics2D) { - g = g.create(); - - try { - g.translate(x, y); - getPainter().paint((Graphics2D) g, c, size.width, size.height); - g.translate(-x, -y); - } finally { - g.dispose(); - } - } - } - - public Painter getPainter() { - return painter; - } - - public void setPainter(Painter painter) { - this.painter = painter; - } -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/icon/package-info.java b/src/main/java/org/jdesktop/swingx/icon/package-info.java deleted file mode 100644 index 41cdce7bbb..0000000000 --- a/src/main/java/org/jdesktop/swingx/icon/package-info.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * $Id: package-info.java 3165 2009-01-02 13:26:07Z rah003 $ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -/**Contains Swing Icon classes used by JDNC's Swing Extensions. -

                    Package Specification

                    - - - -

                    Related Documentation

                    - - - -*/ -package org.jdesktop.swingx.icon; \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/image/AbstractFilter.java b/src/main/java/org/jdesktop/swingx/image/AbstractFilter.java deleted file mode 100644 index ed4614a57e..0000000000 --- a/src/main/java/org/jdesktop/swingx/image/AbstractFilter.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * $Id: AbstractFilter.java 3863 2010-10-26 02:53:32Z kschaefe $ - * - * Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy). - * - * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * Copyright (c) 2006 Romain Guy - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.jdesktop.swingx.image; - -import org.jdesktop.beans.AbstractBean; - -import java.awt.*; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; -import java.awt.image.BufferedImage; -import java.awt.image.BufferedImageOp; -import java.awt.image.ColorModel; - -/** - *

                    Provides an abstract implementation of the BufferedImageOp - * interface. This class can be used to created new image filters based - * on BufferedImageOp.

                    - * - * @author Romain Guy - */ - -public abstract class AbstractFilter extends AbstractBean implements BufferedImageOp { - @Override - public abstract BufferedImage filter(BufferedImage src, BufferedImage dest); - - /** - * {@inheritDoc} - */ - @Override - public Rectangle2D getBounds2D(BufferedImage src) { - return new Rectangle(0, 0, src.getWidth(), src.getHeight()); - } - - /** - * {@inheritDoc} - */ - @Override - public BufferedImage createCompatibleDestImage(BufferedImage src, - ColorModel destCM) { - if (destCM == null) { - destCM = src.getColorModel(); - } - - return new BufferedImage(destCM, - destCM.createCompatibleWritableRaster( - src.getWidth(), src.getHeight()), - destCM.isAlphaPremultiplied(), null); - } - - /** - * {@inheritDoc} - */ - @Override - public Point2D getPoint2D(Point2D srcPt, Point2D dstPt) { - return (Point2D) srcPt.clone(); - } - - /** - * {@inheritDoc} - */ - @Override - public RenderingHints getRenderingHints() { - return null; - } -} diff --git a/src/main/java/org/jdesktop/swingx/image/ColorTintFilter.java b/src/main/java/org/jdesktop/swingx/image/ColorTintFilter.java deleted file mode 100644 index ebf7bceb93..0000000000 --- a/src/main/java/org/jdesktop/swingx/image/ColorTintFilter.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * $Id: ColorTintFilter.java 4082 2011-11-15 18:39:43Z kschaefe $ - * - * Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy). - * - * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * Copyright (c) 2006 Romain Guy - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.jdesktop.swingx.image; - -import org.jdesktop.swingx.util.GraphicsUtilities; - -import java.awt.*; -import java.awt.image.BufferedImage; - -/** - *

                    A color tint filter can be used to mix a solid color to an image. The - * result is an image tinted by the specified color. The force of the effect - * can be controlled with the mixValue, a number between 0.0 and - * 1.0 that can be seen as the percentage of the mix (0.0 does not affect the - * source image and 1.0 replaces all the pixels by the solid color).

                    - *

                    The color of the pixels in the resulting image is computed as follows:

                    - *
                    - * cR = cS * (1 - mixValue) + cM * mixValue
                    - * 
                    - *

                    Definition of the parameters:

                    - *
                      - *
                    • cR: color of the resulting pixel
                    • - *
                    • cS: color of the source pixel
                    • - *
                    • cM: the solid color to mix with the source image
                    • - *
                    • mixValue: strength of the mix, a value between 0.0 and 1.0
                    • - *
                    - * - * @author Romain Guy - */ - -public class ColorTintFilter extends AbstractFilter { - private final Color mixColor; - private final float mixValue; - - private int[] preMultipliedRed; - private int[] preMultipliedGreen; - private int[] preMultipliedBlue; - - /** - *

                    Creates a new color mixer filter. The specified color will be used - * to tint the source image, with a mixing strength defined by - * mixValue.

                    - * - * @param mixColor the solid color to mix with the source image - * @param mixValue the strength of the mix, between 0.0 and 1.0; if the - * specified value lies outside this range, it is clamped - * @throws IllegalArgumentException if mixColor is null - */ - public ColorTintFilter(Color mixColor, float mixValue) { - if (mixColor == null) { - throw new IllegalArgumentException("mixColor cannot be null"); - } - - this.mixColor = mixColor; - if (mixValue < 0.0f) { - mixValue = 0.0f; - } else if (mixValue > 1.0f) { - mixValue = 1.0f; - } - this.mixValue = mixValue; - - int mix_r = (int) (mixColor.getRed() * mixValue); - int mix_g = (int) (mixColor.getGreen() * mixValue); - int mix_b = (int) (mixColor.getBlue() * mixValue); - - // Since we use only lookup tables to apply the filter, this filter - // could be implemented as a LookupOp. - float factor = 1.0f - mixValue; - preMultipliedRed = new int[256]; - preMultipliedGreen = new int[256]; - preMultipliedBlue = new int[256]; - - for (int i = 0; i < 256; i++) { - int value = (int) (i * factor); - preMultipliedRed[i] = value + mix_r; - preMultipliedGreen[i] = value + mix_g; - preMultipliedBlue[i] = value + mix_b; - } - } - - /** - *

                    Returns the mix value of this filter.

                    - * - * @return the mix value, between 0.0 and 1.0 - */ - public float getMixValue() { - return mixValue; - } - - /** - *

                    Returns the solid mix color of this filter.

                    - * - * @return the solid color used for mixing - */ - public Color getMixColor() { - return mixColor; - } - - /** - * {@inheritDoc} - */ - @Override - public BufferedImage filter(BufferedImage src, BufferedImage dst) { - if (dst == null) { - dst = createCompatibleDestImage(src, null); - } - - int width = src.getWidth(); - int height = src.getHeight(); - - int[] pixels = new int[width * height]; - GraphicsUtilities.getPixels(src, 0, 0, width, height, pixels); - mixColor(pixels); - GraphicsUtilities.setPixels(dst, 0, 0, width, height, pixels); - - return dst; - } - - private void mixColor(int[] pixels) { - for (int i = 0; i < pixels.length; i++) { - int argb = pixels[i]; - pixels[i] = (argb & 0xFF000000) | - preMultipliedRed[(argb >> 16) & 0xFF] << 16 | - preMultipliedGreen[(argb >> 8) & 0xFF] << 8 | - preMultipliedBlue[argb & 0xFF]; - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/image/FastBlurFilter.java b/src/main/java/org/jdesktop/swingx/image/FastBlurFilter.java deleted file mode 100644 index c5881c9e54..0000000000 --- a/src/main/java/org/jdesktop/swingx/image/FastBlurFilter.java +++ /dev/null @@ -1,216 +0,0 @@ -/* - * $Id: FastBlurFilter.java 4082 2011-11-15 18:39:43Z kschaefe $ - * - * Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy). - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * Copyright (c) 2006 Romain Guy - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.jdesktop.swingx.image; - -import org.jdesktop.swingx.util.GraphicsUtilities; - -import java.awt.image.BufferedImage; - -/** - *

                    A fast blur filter can be used to blur pictures quickly. This filter is an - * implementation of the box blur algorithm. The blurs generated by this - * algorithm might show square artifacts, especially on pictures containing - * straight lines (rectangles, text, etc.) On most pictures though, the - * result will look very good.

                    - *

                    The force of the blur can be controlled with a radius and the - * default radius is 3. Since the blur clamps values on the edges of the - * source picture, you might need to provide a picture with empty borders - * to avoid artifacts at the edges. The performance of this filter are - * independent from the radius.

                    - * - * @author Romain Guy - */ -public class FastBlurFilter extends AbstractFilter { - private final int radius; - - /** - *

                    Creates a new blur filter with a default radius of 3.

                    - */ - public FastBlurFilter() { - this(3); - } - - /** - *

                    Creates a new blur filter with the specified radius. If the radius - * is lower than 1, a radius of 1 will be used automatically.

                    - * - * @param radius the radius, in pixels, of the blur - */ - public FastBlurFilter(int radius) { - if (radius < 1) { - radius = 1; - } - - this.radius = radius; - } - - /** - *

                    Returns the radius used by this filter, in pixels.

                    - * - * @return the radius of the blur - */ - public int getRadius() { - return radius; - } - - /** - * {@inheritDoc} - */ - @Override - public BufferedImage filter(BufferedImage src, BufferedImage dst) { - int width = src.getWidth(); - int height = src.getHeight(); - - if (dst == null) { - dst = createCompatibleDestImage(src, null); - } - - int[] srcPixels = new int[width * height]; - int[] dstPixels = new int[width * height]; - - GraphicsUtilities.getPixels(src, 0, 0, width, height, srcPixels); - // horizontal pass - blur(srcPixels, dstPixels, width, height, radius); - // vertical pass - //noinspection SuspiciousNameCombination - blur(dstPixels, srcPixels, height, width, radius); - // the result is now stored in srcPixels due to the 2nd pass - GraphicsUtilities.setPixels(dst, 0, 0, width, height, srcPixels); - - return dst; - } - - /** - *

                    Blurs the source pixels into the destination pixels. The force of - * the blur is specified by the radius which must be greater than 0.

                    - *

                    The source and destination pixels arrays are expected to be in the - * INT_ARGB format.

                    - *

                    After this method is executed, dstPixels contains a transposed and - * filtered copy of srcPixels.

                    - * - * @param srcPixels the source pixels - * @param dstPixels the destination pixels - * @param width the width of the source picture - * @param height the height of the source picture - * @param radius the radius of the blur effect - */ - static void blur(int[] srcPixels, int[] dstPixels, - int width, int height, int radius) { - final int windowSize = radius * 2 + 1; - final int radiusPlusOne = radius + 1; - - int sumAlpha; - int sumRed; - int sumGreen; - int sumBlue; - - int srcIndex = 0; - int dstIndex; - int pixel; - - int[] sumLookupTable = new int[256 * windowSize]; - for (int i = 0; i < sumLookupTable.length; i++) { - sumLookupTable[i] = i / windowSize; - } - - int[] indexLookupTable = new int[radiusPlusOne]; - if (radius < width) { - for (int i = 0; i < indexLookupTable.length; i++) { - indexLookupTable[i] = i; - } - } else { - for (int i = 0; i < width; i++) { - indexLookupTable[i] = i; - } - for (int i = width; i < indexLookupTable.length; i++) { - indexLookupTable[i] = width - 1; - } - } - - for (int y = 0; y < height; y++) { - sumAlpha = sumRed = sumGreen = sumBlue = 0; - dstIndex = y; - - pixel = srcPixels[srcIndex]; - sumAlpha += radiusPlusOne * ((pixel >> 24) & 0xFF); - sumRed += radiusPlusOne * ((pixel >> 16) & 0xFF); - sumGreen += radiusPlusOne * ((pixel >> 8) & 0xFF); - sumBlue += radiusPlusOne * ( pixel & 0xFF); - - for (int i = 1; i <= radius; i++) { - pixel = srcPixels[srcIndex + indexLookupTable[i]]; - sumAlpha += (pixel >> 24) & 0xFF; - sumRed += (pixel >> 16) & 0xFF; - sumGreen += (pixel >> 8) & 0xFF; - sumBlue += pixel & 0xFF; - } - - for (int x = 0; x < width; x++) { - dstPixels[dstIndex] = sumLookupTable[sumAlpha] << 24 | - sumLookupTable[sumRed] << 16 | - sumLookupTable[sumGreen] << 8 | - sumLookupTable[sumBlue]; - dstIndex += height; - - int nextPixelIndex = x + radiusPlusOne; - if (nextPixelIndex >= width) { - nextPixelIndex = width - 1; - } - - int previousPixelIndex = x - radius; - if (previousPixelIndex < 0) { - previousPixelIndex = 0; - } - - int nextPixel = srcPixels[srcIndex + nextPixelIndex]; - int previousPixel = srcPixels[srcIndex + previousPixelIndex]; - - sumAlpha += (nextPixel >> 24) & 0xFF; - sumAlpha -= (previousPixel >> 24) & 0xFF; - - sumRed += (nextPixel >> 16) & 0xFF; - sumRed -= (previousPixel >> 16) & 0xFF; - - sumGreen += (nextPixel >> 8) & 0xFF; - sumGreen -= (previousPixel >> 8) & 0xFF; - - sumBlue += nextPixel & 0xFF; - sumBlue -= previousPixel & 0xFF; - } - - srcIndex += width; - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/image/GaussianBlurFilter.java b/src/main/java/org/jdesktop/swingx/image/GaussianBlurFilter.java deleted file mode 100644 index 4f83c00f6d..0000000000 --- a/src/main/java/org/jdesktop/swingx/image/GaussianBlurFilter.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * $Id: GaussianBlurFilter.java 4219 2012-08-07 04:11:12Z kschaefe $ - * - * Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy). - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * Copyright (c) 2006 Romain Guy - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.jdesktop.swingx.image; - -import org.jdesktop.swingx.util.GraphicsUtilities; - -import java.awt.image.BufferedImage; - -public class GaussianBlurFilter extends AbstractFilter { - private final int radius; - - /** - *

                    Creates a new blur filter with a default radius of 3.

                    - */ - public GaussianBlurFilter() { - this(3); - } - - /** - *

                    Creates a new blur filter with the specified radius. If the radius - * is lower than 1, a radius of 1 will be used automatically.

                    - * - * @param radius the radius, in pixels, of the blur - */ - public GaussianBlurFilter(int radius) { - if (radius < 1) { - radius = 1; - } - - this.radius = radius; - } - - /** - *

                    Returns the radius used by this filter, in pixels.

                    - * - * @return the radius of the blur - */ - public int getRadius() { - return radius; - } - - /** - * {@inheritDoc} - */ - @Override - public BufferedImage filter(BufferedImage src, BufferedImage dst) { - int width = src.getWidth(); - int height = src.getHeight(); - - if (dst == null) { - dst = createCompatibleDestImage(src, null); - } - - int[] srcPixels = new int[width * height]; - int[] dstPixels = new int[width * height]; - - float[] kernel = createGaussianKernel(radius); - - GraphicsUtilities.getPixels(src, 0, 0, width, height, srcPixels); - // horizontal pass - blur(srcPixels, dstPixels, width, height, kernel, radius); - // vertical pass - blur(dstPixels, srcPixels, height, width, kernel, radius); - // the result is now stored in srcPixels due to the 2nd pass - GraphicsUtilities.setPixels(dst, 0, 0, width, height, srcPixels); - - return dst; - } - - /** - *

                    Blurs the source pixels into the destination pixels. The force of - * the blur is specified by the radius which must be greater than 0.

                    - *

                    The source and destination pixels arrays are expected to be in the - * INT_ARGB format.

                    - *

                    After this method is executed, dstPixels contains a transposed and - * filtered copy of srcPixels.

                    - * - * @param srcPixels the source pixels - * @param dstPixels the destination pixels - * @param width the width of the source picture - * @param height the height of the source picture - * @param kernel the kernel of the blur effect - * @param radius the radius of the blur effect - */ - static void blur(int[] srcPixels, int[] dstPixels, - int width, int height, - float[] kernel, int radius) { - float a; - float r; - float g; - float b; - - int ca; - int cr; - int cg; - int cb; - - for (int y = 0; y < height; y++) { - int index = y; - int offset = y * width; - - for (int x = 0; x < width; x++) { - a = r = g = b = 0.0f; - - for (int i = -radius; i <= radius; i++) { - int subOffset = x + i; - if (subOffset < 0 || subOffset >= width) { - subOffset = (x + width) % width; - } - - int pixel = srcPixels[offset + subOffset]; - float blurFactor = kernel[radius + i]; - - a += blurFactor * ((pixel >> 24) & 0xFF); - r += blurFactor * ((pixel >> 16) & 0xFF); - g += blurFactor * ((pixel >> 8) & 0xFF); - b += blurFactor * ((pixel ) & 0xFF); - } - - ca = (int) (a + 0.5f); - cr = (int) (r + 0.5f); - cg = (int) (g + 0.5f); - cb = (int) (b + 0.5f); - - dstPixels[index] = ((ca > 255 ? 255 : ca) << 24) | - ((cr > 255 ? 255 : cr) << 16) | - ((cg > 255 ? 255 : cg) << 8) | - (cb > 255 ? 255 : cb); - index += height; - } - } - } - - static float[] createGaussianKernel(int radius) { - if (radius < 1) { - throw new IllegalArgumentException("Radius must be >= 1"); - } - - float[] data = new float[radius * 2 + 1]; - - float sigma = radius / 3.0f; - float twoSigmaSquare = 2.0f * sigma * sigma; - float sigmaRoot = (float) Math.sqrt(twoSigmaSquare * Math.PI); - float total = 0.0f; - - for (int i = -radius; i <= radius; i++) { - float distance = i * i; - int index = i + radius; - data[index] = (float) Math.exp(-distance / twoSigmaSquare) / sigmaRoot; - total += data[index]; - } - - for (int i = 0; i < data.length; i++) { - data[i] /= total; - } - - return data; - } -} diff --git a/src/main/java/org/jdesktop/swingx/image/StackBlurFilter.java b/src/main/java/org/jdesktop/swingx/image/StackBlurFilter.java deleted file mode 100644 index 31834d485a..0000000000 --- a/src/main/java/org/jdesktop/swingx/image/StackBlurFilter.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * $Id: StackBlurFilter.java 4082 2011-11-15 18:39:43Z kschaefe $ - * - * Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy). - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * Copyright (c) 2006 Romain Guy - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.jdesktop.swingx.image; - -import org.jdesktop.swingx.util.GraphicsUtilities; - -import java.awt.image.BufferedImage; - -/** - *

                    A stack blur filter can be used to create an approximation of a - * Gaussian blur. The approximation is controlled by the number of times the - * {@link FastBlurFilter} is applied onto the source - * picture. The default number of iterations, 3, provides a decent compromise - * between speed and rendering quality.

                    - *

                    The force of the blur can be controlled with a radius and the - * default radius is 3. Since the blur clamps values on the edges of the - * source picture, you might need to provide a picture with empty borders - * to avoid artifacts at the edges. The performance of this filter are - * independent from the radius.

                    - * - * @author Romain Guy -*/ -public class StackBlurFilter extends AbstractFilter { - private final int radius; - private final int iterations; - - /** - *

                    Creates a new blur filter with a default radius of 3 and 3 iterations.

                    - */ - public StackBlurFilter() { - this(3, 3); - } - - /** - *

                    Creates a new blur filter with the specified radius and 3 iterations. - * If the radius is lower than 1, a radius of 1 will be used automatically.

                    - * - * @param radius the radius, in pixels, of the blur - */ - public StackBlurFilter(int radius) { - this(radius, 3); - } - - /** - *

                    Creates a new blur filter with the specified radius. If the radius - * is lower than 1, a radius of 1 will be used automatically. The number - * of iterations controls the approximation to a Gaussian blur. If the - * number of iterations is lower than 1, one iteration will be used - * automatically.

                    - * - * @param radius the radius, in pixels, of the blur - * @param iterations the number of iterations to approximate a Gaussian blur - */ - public StackBlurFilter(int radius, int iterations) { - if (radius < 1) { - radius = 1; - } - if (iterations < 1) { - iterations = 1; - } - - this.radius = radius; - this.iterations = iterations; - } - - /** - *

                    Returns the effective radius of the stack blur. If the radius of the - * blur is 1 and the stack iterations count is 3, then the effective blur - * radius is 1 * 3 = 3.

                    - * @return the number of iterations times the blur radius - */ - public int getEffectiveRadius() { - return getIterations() * getRadius(); - } - - /** - *

                    Returns the radius used by this filter, in pixels.

                    - * - * @return the radius of the blur - */ - public int getRadius() { - return radius; - } - - /** - *

                    Returns the number of iterations used to approximate a Gaussian - * blur.

                    - * - * @return the number of iterations used by this blur - */ - public int getIterations() { - return iterations; - } - - /** - * {@inheritDoc} - */ - @Override - public BufferedImage filter(BufferedImage src, BufferedImage dst) { - int width = src.getWidth(); - int height = src.getHeight(); - - if (dst == null) { - dst = createCompatibleDestImage(src, null); - } - - int[] srcPixels = new int[width * height]; - int[] dstPixels = new int[width * height]; - - GraphicsUtilities.getPixels(src, 0, 0, width, height, srcPixels); - for (int i = 0; i < iterations; i++) { - // horizontal pass - FastBlurFilter.blur(srcPixels, dstPixels, width, height, radius); - // vertical pass - FastBlurFilter.blur(dstPixels, srcPixels, height, width, radius); - } - // the result is now stored in srcPixels due to the 2nd pass - GraphicsUtilities.setPixels(dst, 0, 0, width, height, srcPixels); - - return dst; - } -} diff --git a/src/main/java/org/jdesktop/swingx/image/package-info.java b/src/main/java/org/jdesktop/swingx/image/package-info.java deleted file mode 100644 index 83344bfbe8..0000000000 --- a/src/main/java/org/jdesktop/swingx/image/package-info.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -/** - * Contains set of useful filters for image operations like Gausian or Fast or Star Blur or Color Tint filter. - */ -package org.jdesktop.swingx.image; - diff --git a/src/main/java/org/jdesktop/swingx/multislider/AbstractMultiThumbModel.java b/src/main/java/org/jdesktop/swingx/multislider/AbstractMultiThumbModel.java deleted file mode 100644 index 46ad640b00..0000000000 --- a/src/main/java/org/jdesktop/swingx/multislider/AbstractMultiThumbModel.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * $Id: AbstractMultiThumbModel.java 3259 2009-02-17 21:06:19Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.multislider; - -import java.util.ArrayList; -import java.util.List; - -/** - * - * @author jm158417 - */ -public abstract class AbstractMultiThumbModel implements MultiThumbModel { - /** Creates a new instance of AbstractMultiThumbModel */ - public AbstractMultiThumbModel() { - } - - protected float maximumValue = 1.0f; - protected float minimumValue = 0.0f; - - public float getMaximumValue() { - return maximumValue; - } - - public float getMinimumValue() { - return minimumValue; - } - - public void setMaximumValue(float maximumValue) { - this.maximumValue = maximumValue; - } - - public void setMinimumValue(float minimumValue) { - this.minimumValue = minimumValue; - } - - protected List thumbDataListeners = new ArrayList(); - - public void addThumbDataListener(ThumbDataListener listener) { - thumbDataListeners.add(listener); - } - - public void removeThumbDataListener(ThumbDataListener listener) { - thumbDataListeners.remove(listener); - } - - - - public void thumbPositionChanged(Thumb thumb) { - fireThumbPositionChanged(thumb); - } - - protected void fireThumbPositionChanged(Thumb thumb) { - if(getThumbIndex(thumb) >= 0) { - ThumbDataEvent evt = new ThumbDataEvent(this,-1,getThumbIndex(thumb),thumb); - for(ThumbDataListener l : thumbDataListeners) { - l.positionChanged(evt); - } - } - } - public void thumbValueChanged(Thumb thumb) { - fireThumbValueChanged(thumb); - } - - protected void fireThumbValueChanged(Thumb thumb) { - ThumbDataEvent evt = new ThumbDataEvent(this,-1,getThumbIndex(thumb),thumb); - for(ThumbDataListener l : thumbDataListeners) { - l.valueChanged(evt); - } - } - -} - diff --git a/src/main/java/org/jdesktop/swingx/multislider/DefaultMultiThumbModel.java b/src/main/java/org/jdesktop/swingx/multislider/DefaultMultiThumbModel.java deleted file mode 100644 index f6f3bfbbd0..0000000000 --- a/src/main/java/org/jdesktop/swingx/multislider/DefaultMultiThumbModel.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * $Id: DefaultMultiThumbModel.java 3935 2011-03-02 19:06:41Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.multislider; - - -import java.util.*; - -/** - * - * @author joshy - */ -public class DefaultMultiThumbModel extends AbstractMultiThumbModel { - - protected List>thumbs = new ArrayList>(); - - /** Creates a new instance of DefaultMultiThumbModel */ - public DefaultMultiThumbModel() { - setMinimumValue(0.0f); - setMaximumValue(1.0f); - } - // returns the index of the newly added thumb - @Override - public int addThumb(float value, E obj) { - Thumb thumb = new Thumb(this); - thumb.setPosition(value); - thumb.setObject(obj); - thumbs.add(thumb); - int n = thumbs.size(); - ThumbDataEvent evt = new ThumbDataEvent(this,-1,thumbs.size()-1,thumb); - for(ThumbDataListener tdl : thumbDataListeners) { - tdl.thumbAdded(evt); - } - return n-1; - } - - @Override - public void insertThumb(float value, E obj, int index) { - Thumb thumb = new Thumb(this); - thumb.setPosition(value); - thumb.setObject(obj); - thumbs.add(index,thumb); - ThumbDataEvent evt = new ThumbDataEvent(this,-1,index,thumb); - for(ThumbDataListener tdl : thumbDataListeners) { - tdl.thumbAdded(evt); - } - } - - @Override - public void removeThumb(int index) { - Thumb thumb = thumbs.remove(index); - ThumbDataEvent evt = new ThumbDataEvent(this,-1,index,thumb); - for(ThumbDataListener tdl : thumbDataListeners) { - tdl.thumbRemoved(evt); - } - } - - @Override - public int getThumbCount() { - return thumbs.size(); - } - - @Override - public Thumb getThumbAt(int index) { - return thumbs.get(index); - } - - @Override - public List> getSortedThumbs() { - List> list = new ArrayList>(); - list.addAll(thumbs); - Collections.sort(list, new Comparator>() { - @Override - public int compare(Thumb o1, Thumb o2) { - float f1 = o1.getPosition(); - float f2 = o2.getPosition(); - if(f1f2) { - return 1; - } - return 0; - } - }); - return list; - } - - @Override - public Iterator> iterator() { - return thumbs.iterator(); - } - - @Override - public int getThumbIndex(Thumb thumb) { - return thumbs.indexOf(thumb); - } -} diff --git a/src/main/java/org/jdesktop/swingx/multislider/MultiThumbModel.java b/src/main/java/org/jdesktop/swingx/multislider/MultiThumbModel.java deleted file mode 100644 index debf826196..0000000000 --- a/src/main/java/org/jdesktop/swingx/multislider/MultiThumbModel.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * $Id: MultiThumbModel.java 3259 2009-02-17 21:06:19Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.multislider; - - -import java.util.List; - -/** - * - * @author joshy - */ -public interface MultiThumbModel extends Iterable> { - - public float getMinimumValue(); - public void setMinimumValue(float minimumValue); - public float getMaximumValue(); - public void setMaximumValue(float maximumValue); - - public int addThumb(float value, E obj); - public void insertThumb(float value, E obj, int index); - public void removeThumb(int index); - public int getThumbCount(); - public Thumb getThumbAt(int index); - public int getThumbIndex(Thumb thumb); - public List> getSortedThumbs(); - public void thumbPositionChanged(Thumb thumb); - public void thumbValueChanged(Thumb thumb); - - public void addThumbDataListener(ThumbDataListener listener); - public void removeThumbDataListener(ThumbDataListener listener); -} diff --git a/src/main/java/org/jdesktop/swingx/multislider/Thumb.java b/src/main/java/org/jdesktop/swingx/multislider/Thumb.java deleted file mode 100644 index c5eb64bc0f..0000000000 --- a/src/main/java/org/jdesktop/swingx/multislider/Thumb.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * $Id: Thumb.java 3259 2009-02-17 21:06:19Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.multislider; - -/** - * - * @author jm158417 - */ -public class Thumb { - private float position; - private E object; - private MultiThumbModel model; - - /** Creates a new instance of Thumb */ - public Thumb(MultiThumbModel model) { - this.model = model; - } - - public float getPosition() { - return position; - } - - public void setPosition(float position) { - this.position = position; - model.thumbPositionChanged(this); - } - - public E getObject() { - return object; - } - - public void setObject(E object) { - this.object = object; - model.thumbValueChanged(this); - } -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/multislider/ThumbDataEvent.java b/src/main/java/org/jdesktop/swingx/multislider/ThumbDataEvent.java deleted file mode 100644 index d3286339f5..0000000000 --- a/src/main/java/org/jdesktop/swingx/multislider/ThumbDataEvent.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * $Id: ThumbDataEvent.java 3475 2009-08-28 08:30:47Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.multislider; - -import java.util.EventObject; - -/** - * - * @author jm158417 - */ -public class ThumbDataEvent extends EventObject { - private int type, index; - private Thumb thumb; - - /** Creates a new instance of ThumbDataEvent */ - public ThumbDataEvent(Object source, int type, int index, Thumb thumb) { - super(source); - this.type = type; - this.thumb = thumb; - this.index = index; - } - - public int getType() { - return type; - } - - public int getIndex() { - return index; - } - - public Thumb getThumb() { - return thumb; - } - - @Override - public String toString() { - return this.getClass().getName() + " : " + type + " " + index + " " + thumb; - } -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/multislider/ThumbDataListener.java b/src/main/java/org/jdesktop/swingx/multislider/ThumbDataListener.java deleted file mode 100644 index b66ab1718a..0000000000 --- a/src/main/java/org/jdesktop/swingx/multislider/ThumbDataListener.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * $Id: ThumbDataListener.java 957 2006-03-29 02:44:44Z rbair $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.multislider; - -/** - * - * @author jm158417 - */ -public interface ThumbDataListener { - public void valueChanged(ThumbDataEvent e); - public void positionChanged(ThumbDataEvent e); - public void thumbAdded(ThumbDataEvent e); - public void thumbRemoved(ThumbDataEvent e); -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/multislider/ThumbListener.java b/src/main/java/org/jdesktop/swingx/multislider/ThumbListener.java deleted file mode 100644 index abca22996a..0000000000 --- a/src/main/java/org/jdesktop/swingx/multislider/ThumbListener.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * $Id: ThumbListener.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.multislider; - -import java.awt.event.MouseEvent; - -public interface ThumbListener { - public void thumbMoved(int thumb, float pos); - public void thumbSelected(int thumb); - public void mousePressed(MouseEvent evt); -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/multislider/ThumbRenderer.java b/src/main/java/org/jdesktop/swingx/multislider/ThumbRenderer.java deleted file mode 100644 index 446fdef02e..0000000000 --- a/src/main/java/org/jdesktop/swingx/multislider/ThumbRenderer.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * $Id: ThumbRenderer.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.multislider; - -import org.jdesktop.swingx.JXMultiThumbSlider; - -import javax.swing.*; - -public interface ThumbRenderer { - public JComponent getThumbRendererComponent(JXMultiThumbSlider slider, int index, boolean selected); -} diff --git a/src/main/java/org/jdesktop/swingx/multislider/TrackRenderer.java b/src/main/java/org/jdesktop/swingx/multislider/TrackRenderer.java deleted file mode 100644 index e23000b29e..0000000000 --- a/src/main/java/org/jdesktop/swingx/multislider/TrackRenderer.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * $Id: TrackRenderer.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.multislider; - -import org.jdesktop.swingx.JXMultiThumbSlider; - -import javax.swing.*; - - -public interface TrackRenderer { - public JComponent getRendererComponent(JXMultiThumbSlider slider); -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/multislider/package-info.java b/src/main/java/org/jdesktop/swingx/multislider/package-info.java deleted file mode 100644 index e7a4644f47..0000000000 --- a/src/main/java/org/jdesktop/swingx/multislider/package-info.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -/** - * Contains classes used by slider classes, such as {@code JXMultiThumbSlider}. - */ -package org.jdesktop.swingx.multislider; - diff --git a/src/main/java/org/jdesktop/swingx/multisplitpane/DefaultSplitPaneModel.java b/src/main/java/org/jdesktop/swingx/multisplitpane/DefaultSplitPaneModel.java deleted file mode 100644 index 4344e5db61..0000000000 --- a/src/main/java/org/jdesktop/swingx/multisplitpane/DefaultSplitPaneModel.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * $Id: DefaultSplitPaneModel.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.multisplitpane; - -import org.jdesktop.swingx.MultiSplitLayout.Divider; -import org.jdesktop.swingx.MultiSplitLayout.Leaf; -import org.jdesktop.swingx.MultiSplitLayout.Split; -/** - * A simplified SplitPaneLayout for common split pane needs. A common multi splitpane - * need is: - * - * +-----------+-----------+ - * | | | - * | +-----------+ - * | | | - * +-----------+-----------+ - * - * @author rbair - */ -public class DefaultSplitPaneModel extends Split { - public static final String LEFT = "left"; - public static final String TOP = "top"; - public static final String BOTTOM = "bottom"; - - /** Creates a new instance of DefaultSplitPaneLayout */ - public DefaultSplitPaneModel() { - Split row = new Split(); - Split col = new Split(); - col.setRowLayout(false); - setChildren(new Leaf(LEFT), new Divider(), col); - col.setChildren(new Leaf(TOP), new Divider(), new Leaf(BOTTOM)); - } - -} diff --git a/src/main/java/org/jdesktop/swingx/multisplitpane/package-info.java b/src/main/java/org/jdesktop/swingx/multisplitpane/package-info.java deleted file mode 100644 index 75b034d606..0000000000 --- a/src/main/java/org/jdesktop/swingx/multisplitpane/package-info.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * $Id: package-info.java 3165 2009-01-02 13:26:07Z rah003 $ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -/** Contains support classes for the MultiSplitLayout layout manager. -*/ -package org.jdesktop.swingx.multisplitpane; \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/package-info.java b/src/main/java/org/jdesktop/swingx/package-info.java deleted file mode 100644 index 39bee0e064..0000000000 --- a/src/main/java/org/jdesktop/swingx/package-info.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * $Id: package-info.java 3933 2011-03-02 19:02:29Z kschaefe $ - * - * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -/** - * Contains extensions to the Swing GUI toolkit, including new and enhanced - * components that provide functionality commonly required by rich, - * data-centric client applications. Many of these features will eventually - * be incorporated into the Swing toolkit, although API compatibility will - * not be guaranteed. - *

                    - * - *

                    New or Enhanced Functionality

                    - * - *

                    Auto-completion for TextFields and ComboBoxes

                    - * - * For more information, see the - * AutoComplete documentation. - * - *

                    Enhanced Rendering Support for Collection Components

                    - * - *

                    Built-In Search Support for Collection Components and JXEditorPane

                    - * - *

                    Login/Authentication Framework

                    - * - *

                    Painter-Enabled Components

                    - * - * Components that use painters for background rendering alter the functionality - * of how {@link java.awt.Component#setBackground(java.awt.Color)} works. - * Setting the background color of a painter-enabled component effectively sets - * the background painter to paint the requested color. - *

                    - * Look and Feel implementors should note that setting a - * {@link java.swing.plaf.UIResource} to {@code setBackground} will cause a - * {@code Painter} {@code UIResource} to be installed. This means that - * implementors should set the background before setting the painter as the last - * one set wins. - * - *

                    New and Enhanced components

                    - * - *

                    Buttons and Labels

                    - *
                      - *
                    • {@link org.jdesktop.swingx.JXButton} - *
                    • {@link org.jdesktop.swingx.JXHyperlink Hyperlink} - *
                    • {@link org.jdesktop.swingx.JXLabel} - *
                    • {@link org.jdesktop.swingx.JXBusyLabel} - *
                    • {@link org.jdesktop.swingx.JXRadioGroup} - *
                    - * - * - *

                    Collection Components

                    - * - * These are sortable/filterable (with the exception of hierarchical - * components) with consistent and uniform SwingX rendering, highlighting, - * searching and rollover support. - *
                      - *
                    • {@link org.jdesktop.swingx.JXTable Table} uses the enhanced - * {@link org.jdesktop.swingx.JXTableHeader TableHeader} - *
                    • {@link org.jdesktop.swingx.JXList List} - rollover and sort/filter - * functionality is disabled by default - *
                    • {@link org.jdesktop.swingx.JXTree Tree} - *
                    • {@link org.jdesktop.swingx.JXTreeTable TreeTable} - a new - * hierarchical component with support of tabular node properties - *
                    - * - *

                    Top-level Windows, General and Special Purpose Containers

                    - *
                      - *
                    • Enhanced {@link org.jdesktop.swingx.JXFrame Frame} using an extended - * {@link org.jdesktop.swingx.JXRootPane RootPane RootPane} to support a - * {@link org.jdesktop.swingx.JXStatusBar StatusBar} - *
                    • {@link org.jdesktop.swingx.JXDialog Dialog} - *
                    • {@link org.jdesktop.swingx.JXPanel Panel} - *
                    • {@link org.jdesktop.swingx.JXErrorPane ErrorPane} - * - *
                    • Search components: {@link org.jdesktop.swingx.JXFindBar FindBar} used - * for incremental search (similar to FireFox), - * {@link org.jdesktop.swingx.JXFindPanel FindPanel} used in a find dialog, - * and {@link org.jdesktop.swingx.JXSearchPanel SearchPanel} used for what - * was it? - *
                    • Nested SplitPane {@link org.jdesktop.swingx.JXMultiSplitPane - * MultiSplitPane} - *
                    • Vertical collapsing/expansion functionality is provided by a - * {@link org.jdesktop.swingx.JXCollapsiblePane CollapsiblePane}. A special - * purpose collapsible is the {@link org.jdesktop.swingx.JXTaskPane - * TaskPane} which typically is used to group buttons/hyperlinks which - * perform related tasks. A special - * {@link org.jdesktop.swingx.JXTaskPaneContainer TaskPaneContainer} is - * responsible for the layout of several TaskPanes. - *
                    • Easily configurable {@link org.jdesktop.swingx.JXTipOfTheDay - * TipOfTheDay} - *
                    • {@link org.jdesktop.swingx.JXTitledPanel TitledPanel} - * - *
                    - * - *

                    Miscellaneous Components

                    - * - *
                      - *
                    • New calendar components: the {@link org.jdesktop.swingx.JXDatePicker - * DatePicker} allows to select a single Date and a - * {@link org.jdesktop.swingx.JXMonthView MonthView} showing the overview of - * one or more months. - * - *
                    • {@link org.jdesktop.swingx.JXHeader Header} - *
                    • {@link org.jdesktop.swingx.JXTitledSeparator TitledSeparator} - * - *
                    • {@link org.jdesktop.swingx.JXColorSelectionButton} - *
                    • {@link org.jdesktop.swingx.JXEditorPane} - *
                    • {@link org.jdesktop.swingx.JXGradientChooser} - *
                    • {@link org.jdesktop.swingx.JXMultiThumbSlider MultiThumbSlider} - * - *
                    - * - *

                    External Information Sources

                    - * - * SwingX Twiki - * Change History - * SwingLabs User and - * Developer Discussion Forum - */ -package org.jdesktop.swingx; - diff --git a/src/main/java/org/jdesktop/swingx/painter/AbstractAreaPainter.java b/src/main/java/org/jdesktop/swingx/painter/AbstractAreaPainter.java deleted file mode 100644 index f53f541b45..0000000000 --- a/src/main/java/org/jdesktop/swingx/painter/AbstractAreaPainter.java +++ /dev/null @@ -1,267 +0,0 @@ -/* - * $Id: AbstractAreaPainter.java 4082 2011-11-15 18:39:43Z kschaefe $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.painter; - -import org.jdesktop.swingx.painter.effects.AreaEffect; -import org.jdesktop.swingx.util.PaintUtils; - -import java.awt.*; - -/** - * The abstract base class for all painters that fill a vector path area. This - * includes Shapes, Rectangles, Text, and the MattePainter - * which fills in the entire background of a component. The defining - * feature of AbstractAreaPainter subclasses - * is that they implement the provideShape() method which returns - * the outline shape of the area that this - * painter will fill. Subclasses must implement the provideShape() method. - * - * The AbstractAreaPainter provides support for the following common painting properties - * - *
                      - *
                    • fillPaint
                    • - *
                    • paintStretched
                    • - *
                    • borderPaint
                    • - *
                    • borderWidth
                    • - *
                    • style
                    • - *
                    - * - * The AbstractAreaPainter also provides support for path effects like dropshadows and glows. - * - * @author joshua@marinacci.org - */ -@SuppressWarnings("nls") -public abstract class AbstractAreaPainter extends AbstractLayoutPainter { - - /** - * Different available fill styles. BOTH indicates that both the outline, - * and the fill should be painted. This is the default. FILLED indicates that - * the shape should be filled, but no outline painted. OUTLINE specifies that - * the shape should be outlined, but not filled. NONE indicates that neither - * the fill area nor the outline should be painted. - */ - public enum Style {BOTH, FILLED, OUTLINE, - /** - * {@code NONE} has different semantics that {@link AbstractAreaPainter#setVisible(boolean) - * setVisible(false)}. With {@code setVisible(false)}, nothing is painted. With style - * {@code NONE}, any {@link AreaEffect effects} are still painted. - */ - NONE} - - // controls if the paint should be stretched to fill the available area - private boolean stretchPaint; - - private AreaEffect[] areaEffects = new AreaEffect[0]; - - private Style style = Style.BOTH; - /** - * The stroke width to use when painting. If null, the default Stroke for - * the Graphics2D is used - */ - private float borderWidth; - - /** - * The paint to use when filling the shape - */ - private Paint fillPaint; - - /** - * The Paint to use when stroking the shape (drawing the outline). If null, - * then the component foreground color is used - */ - private Paint borderPaint; - - /** - * Creates a new instance of AbstractAreaPainter - */ - public AbstractAreaPainter() { - fillPaint = Color.RED; - } - - /** - * Creates a new instance of AbstractAreaPainter - * @param paint the default paint to fill this area painter with - */ - public AbstractAreaPainter(Paint paint) { - this.fillPaint = paint; - } - - /** - * Gets the current fill paint. This is the Paint object that will be used to fill the path area. - * @return Gets the Paint being used. May be null - */ - public Paint getFillPaint() { - return fillPaint; - } - - /** - * Sets the Paint to use. This is the Paint object that will be used to fill the path area. If null, nothing is painted - * @param p the Paint to use - */ - public void setFillPaint(Paint p) { - Paint old = getFillPaint(); - this.fillPaint = p; - setDirty(true); - firePropertyChange("fillPaint", old, getFillPaint()); - } - - /** - * Indicates if the paint will be snapped. This means that the paint will be scaled and aligned along the 4 axis of (horizontal, vertical, - * and both diagonals). Snapping allows the paint to be stretched across the component when it is drawn, even if the component is - * resized. This setting is only used for gradient paints. It will have no effect on Color or Texture paints. - * @return the current value of the snapPaint property - */ - public boolean isPaintStretched() { - return stretchPaint; - } - - /** - * Specifies whether this Painter should attempt to resize the Paint to fit the area being painted. - * For example, if true, then a gradient specified as (0, 0), (1, 0) would stretch horizontally such that - * the beginning of the gradient is on the left edge of the painted region, and the end of the gradient - * is at the right edge of the painted region. - * Specifically, if true, the resizePaint method will be called to perform the actual resizing of the Paint - * @param paintStretched true if the paint should be stretched, false otherwise. - */ - public void setPaintStretched(boolean paintStretched) { - boolean old = this.isPaintStretched(); - this.stretchPaint = paintStretched; - setDirty(true); - firePropertyChange("paintStretched", old, isPaintStretched()); - } - - /** - * The Paint to use for stroking the shape (painting the outline). - * Can be a Color, GradientPaint, TexturePaint, or any other kind of Paint. - * If null, the component foreground is used. - * - * @param p the Paint to use for stroking the shape. May be null. - */ - public void setBorderPaint(Paint p) { - Paint old = getBorderPaint(); - this.borderPaint = p; - setDirty(true); - firePropertyChange("borderPaint", old, getBorderPaint()); - } - - /** - * Gets the current Paint to use for stroking the shape (painting the outline). - * Can be a Color, GradientPaint, TexturePaint, or any other kind of Paint. - * If null, the component foreground is used. - * @return the Paint used when stroking the shape. May be null - */ - public Paint getBorderPaint() { - return borderPaint; - } - - /** - * The shape can be filled or simply stroked (outlined), or both or none. By default, - * the shape is both filled and stroked. This property specifies the strategy to - * use. - * @param s the Style to use. If null, Style.BOTH is used - */ - public void setStyle(Style s) { - Style old = getStyle(); - this.style = s == null ? Style.BOTH : s; - setDirty(true); - firePropertyChange("style", old, getStyle()); - } - - /** - * Gets the current Style. The shape can be filled or simply stroked (outlined), or both or none. By default, - * the shape is both filled and stroked. This property specifies the strategy to - * use. - * @return the Style used - */ - public Style getStyle() { - return style; - } - - /** - * Sets the border width to use for painting. If null, then the default Graphics2D - * stroke will be used. The stroke will be centered on the actual shape outline. - * @param s the Stroke to fillPaint with - */ - public void setBorderWidth(float s) { - float old = getBorderWidth(); - this.borderWidth = s; - setDirty(true); - firePropertyChange("borderWidth", old, getBorderWidth()); - } - - /** - * Gets the current border width. - * @return the Stroke to use for painting - */ - public float getBorderWidth() { - return borderWidth; - } - - /** - * Resizes the given Paint. By default, only Gradients, LinearGradients, and RadialGradients are resized - * in this method. If you have special resizing needs, override this method. This - * method is mainly used to make gradient paints resize with the component this - * painter is attached to. This method is internal to the painter api and should - * not be called elsewhere. It is used by the paintStretched property and - * painter subclasses. In the future it may be made public for use by other classes. - * If this happens it should probably be turned into a static utility method. - */ - Paint calculateSnappedPaint(Paint p, int width, int height) { - return PaintUtils.resizeGradient(p, width, height); - } - - /** - * Returns the outline shape of this painter. Subclasses must implement this method. This shape - * will be used for filling, stroking, and clipping. - * @return the outline shape of this painter - * @param g graphics - * @param comp The Object this painter will be painted on. - * @param width the width to paint - * @param height the height to paint - */ - protected abstract Shape provideShape(Graphics2D g, T comp, int width, int height); - - /** - * Sets the path effects to be drawn on this painter. Set this to null in order to remove all installed effects. - * @param areaEffects the effects to apply to this painter - */ - public void setAreaEffects(AreaEffect... areaEffects) { - AreaEffect[] old = getAreaEffects(); - this.areaEffects = new AreaEffect[areaEffects == null ? 0 : areaEffects.length]; - if (areaEffects != null) { - System.arraycopy(areaEffects, 0, this.areaEffects, 0, this.areaEffects.length); - } - setDirty(true); - firePropertyChange("areaEffects", old, getAreaEffects()); - } - - /** - * Gets the current set of path effects applied to this painter. Returned array is guarantied to be not null. - * @return the effects applied to this path painter - */ - public AreaEffect[] getAreaEffects() { - AreaEffect[] results = new AreaEffect[areaEffects.length]; - System.arraycopy(areaEffects, 0, results, 0, results.length); - return results; - } - -} diff --git a/src/main/java/org/jdesktop/swingx/painter/AbstractLayoutPainter.java b/src/main/java/org/jdesktop/swingx/painter/AbstractLayoutPainter.java deleted file mode 100644 index 48cb37addb..0000000000 --- a/src/main/java/org/jdesktop/swingx/painter/AbstractLayoutPainter.java +++ /dev/null @@ -1,270 +0,0 @@ -/* - * $Id: AbstractLayoutPainter.java 4079 2011-11-15 16:05:13Z kschaefe $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.painter; - -import java.awt.*; - -/** - * An abstract base class for any painter which can be positioned. This means - * the painter has some intrinsic size to what it is drawing and - * can be stretched or aligned both horizontally and vertically. - * - * - * The AbstractLayoutPainter class provides the following configuraable properties: - * - *
                      - *
                    • horizonalAlignment - the horizonal alignment (left, center, and right)
                    • - *
                    • verticalAlignment - the verticalAlignment alignment (top, center, and bottom)
                    • - *
                    • fillHorizontal - indicates if the painter should stretch to fill the available space horizontally
                    • - *
                    • fillVertical - indicates if the painter should stretch to fill the available space vertically
                    • - *
                    • insets - whitespace on the top, bottom, left, and right. - *
                    - * - * By combining these five properties any AbstractLayoutPainter subclass can position it's content - * within the paintable area. For example, an ImagePainter has an intrinsic size based on the image - * it is painting. If you wanted to paint the image in the lower right hand corner of the paintable - * area, but inset by 5 pixels, you could do the following: - * - *
                    
                    - *     ImagePainter p = new ImagePainter(null);
                    - *     p.setVerticalAlignment(AbstractLayoutPainter.VerticalAlignment.BOTTOM);
                    - *     p.setHorizontalAlignment(AbstractLayoutPainter.HorizontalAlignment.RIGHT);
                    - *     p.setInsets(new Insets(0,0,5,5));
                    - * 
                    - * - * - * For something which is resizable, like a RectanglePainter, you can use the fill properties - * to make it resize along with the paintable area. For example, to make a rectangle with 20 px - * rounded corners, and which resizes with the paintable area but is inset - * by 10 pixels on all sides, you could do - * the following: - * - *
                    
                    - *     RectanglePainter p = new RectanglePainter();
                    - *     p.setRoundHeight(20);
                    - *     p.setRoundWidth(20);
                    - *     p.setInsets(new Insets(10,10,10,10));
                    - *     p.setFillHorizontal(true);
                    - *     p.setFillVertical(true);
                    - * 
                    - * - * - * @author joshua@marinacci.org - */ -@SuppressWarnings("nls") -public abstract class AbstractLayoutPainter extends AbstractPainter { - - /** - * Specifies how to draw the image, i.e. what kind of Style to use - * when drawing - */ - private VerticalAlignment verticalAlignment = VerticalAlignment.CENTER; - private HorizontalAlignment horizontalAlignment = HorizontalAlignment.CENTER; - private Insets insets = new Insets(0,0,0,0); - private boolean fillVertical = false; - private boolean fillHorizontal = false; - - /** - * Creates a new instance of AbstractLayoutPainter - */ - public AbstractLayoutPainter() { - } - - /** - * An enum which controls horizontalAlignment alignment - */ - public static enum HorizontalAlignment { LEFT, CENTER, RIGHT } - - /** - * An enum which controls verticalAlignment alignment - */ - public static enum VerticalAlignment { TOP, CENTER, BOTTOM } - - /** - * Gets the current horizontalAlignment alignment. - * - * @return the current horizontalAlignment alignment - */ - public HorizontalAlignment getHorizontalAlignment() { - return horizontalAlignment; - } - - /** - * Gets the current whitespace insets. - * @return the current insets - */ - public Insets getInsets() { - return insets; - } - - /** - * gets the current verticalAlignment alignment - * - * @return current verticalAlignment alignment - */ - public VerticalAlignment getVerticalAlignment() { - return verticalAlignment; - } - - /** - * indicates if the painter content is stretched horizontally - * - * @return the current horizontalAlignment stretch value - */ - public boolean isFillHorizontal() { - return fillHorizontal; - } - - /** - * indicates if the painter content is stretched vertically - * - * @return the current verticalAlignment stretch value - */ - public boolean isFillVertical() { - return fillVertical; - } - - /** - * Sets a new horizontalAlignment alignment. Used to position the content at the left, right, or center. - * - * @param horizontal new horizontalAlignment alignment - */ - public void setHorizontalAlignment(HorizontalAlignment horizontal) { - HorizontalAlignment old = this.getHorizontalAlignment(); - this.horizontalAlignment = horizontal; - setDirty(true); - firePropertyChange("horizontalAlignment", old, getHorizontalAlignment()); - } - - /** - * Sets if the content should be stretched horizontally to fill all available horizontalAlignment - * space (minus the left and right insets). - * - * @param fillHorizontal new horizontal stretch value - */ - public void setFillHorizontal(boolean fillHorizontal) { - boolean old = this.isFillHorizontal(); - this.fillHorizontal = fillHorizontal; - setDirty(true); - firePropertyChange("fillHorizontal", old, isFillHorizontal()); - } - - /** - * Sets the current whitespace insets. - * @param insets new insets - */ - public void setInsets(Insets insets) { - Insets old = this.getInsets(); - this.insets = insets; - setDirty(true); - firePropertyChange("insets", old, getInsets()); - } - - /** - * Sets a new verticalAlignment alignment. Used to position the content at the top, bottom, or center. - * - * @param vertical new verticalAlignment alignment - */ - public void setVerticalAlignment(VerticalAlignment vertical) { - VerticalAlignment old = this.getVerticalAlignment(); - this.verticalAlignment = vertical; - setDirty(true); - firePropertyChange("verticalAlignment", old, getVerticalAlignment()); - } - - /** - * Sets if the content should be stretched vertically to fill all available verticalAlignment - * space (minus the top and bottom insets). - * - * @param verticalStretch new verticalAlignment stretch value - */ - public void setFillVertical(boolean verticalStretch) { - boolean old = this.isFillVertical(); - this.fillVertical = verticalStretch; - setDirty(true); - firePropertyChange("fillVertical", old, isFillVertical()); - } - - /** - * A protected method used by subclasses to calculate the final position of the - * content. This will position the content using the fillHorizontal, fillVertical - * horizontalAlignment, and verticalAlignment properties. This method - * is typically called by subclasses in their doPaint() methods. - * - * @param contentWidth The width of the content to be painted - * @param contentHeight The height of the content to be painted - * @param width the width of the area that the content will be positioned in - * @param height the height of the area that the content will be positioned in - * @return the rectangle for the content to be painted in - */ - protected final Rectangle calculateLayout(final int contentWidth, final int contentHeight, - final int width, final int height) { - - Rectangle rect = new Rectangle(); - rect.width = contentWidth; - rect.height = contentHeight; - - if(isFillHorizontal()) { - rect.width = width - insets.left - insets.right; - } - - if(isFillVertical()) { - rect.height = height - insets.top - insets.bottom; - } - rect.x = calculateX(rect.width, width); - rect.y = calculateY(rect.height, height); - return rect; - } - - private int calculateY(final int imgHeight, final int height) { - int y = 0; - if(getVerticalAlignment() == VerticalAlignment.TOP) { - y = 0; - y+= insets.top; - } - if(getVerticalAlignment() == VerticalAlignment.CENTER) { - y = (height-imgHeight)/2; - y += insets.top; - } - if(getVerticalAlignment() == VerticalAlignment.BOTTOM) { - y = height-imgHeight; - y-= insets.bottom; - } - return y; - } - - private int calculateX(final int imgWidth, final int width) { - int x = 0; - if(getHorizontalAlignment() == HorizontalAlignment.LEFT) { - x = 0; - x+= insets.left; - } - if(getHorizontalAlignment() == HorizontalAlignment.CENTER) { - x = (width-imgWidth)/2; - x += insets.left; - } - if(getHorizontalAlignment() == HorizontalAlignment.RIGHT) { - x = width-imgWidth; - x-= insets.right; - } - return x; - } -} diff --git a/src/main/java/org/jdesktop/swingx/painter/AbstractPainter.java b/src/main/java/org/jdesktop/swingx/painter/AbstractPainter.java deleted file mode 100644 index 58751cf343..0000000000 --- a/src/main/java/org/jdesktop/swingx/painter/AbstractPainter.java +++ /dev/null @@ -1,438 +0,0 @@ -/* - * $Id: AbstractPainter.java 4082 2011-11-15 18:39:43Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.painter; - -import org.jdesktop.beans.AbstractBean; -import org.jdesktop.swingx.util.GraphicsUtilities; - -import java.awt.*; -import java.awt.image.BufferedImage; -import java.awt.image.BufferedImageOp; -import java.lang.ref.SoftReference; - -/** - *

                    A convenient base class from which concrete {@link Painter} implementations may - * extend. It extends {@link AbstractBean} as a convenience for - * adding property change notification support. In addition, AbstractPainter - * provides subclasses with the ability to cacheable painting operations, configure the - * drawing surface with common settings (such as antialiasing and interpolation), and - * toggle whether a subclass paints or not via the visibility property.

                    - * - *

                    Subclasses of AbstractPainter generally need only override the - * {@link #doPaint(Graphics2D, Object, int, int)} method. If a subclass requires more control - * over whether caching is enabled, or for configuring the graphics state, then it - * may override the appropriate protected methods to interpose its own behavior.

                    - * - *

                    For example, here is the doPaint method of a simple Painter that - * paints an opaque rectangle: - *

                    
                    - *  public void doPaint(Graphics2D g, T obj, int width, int height) {
                    - *      g.setPaint(Color.BLUE);
                    - *      g.fillRect(0, 0, width, height);
                    - *  }
                    - * 

                    - * - * @author rbair - */ -@SuppressWarnings("nls") -public abstract class AbstractPainter extends AbstractBean implements Painter { - /** - * An enum representing the possible interpolation values of Bicubic, Bilinear, and - * Nearest Neighbor. These map to the underlying RenderingHints, - * but are easier to use and serialization safe. - */ - public enum Interpolation { - /** - * use bicubic interpolation - */ - Bicubic(RenderingHints.VALUE_INTERPOLATION_BICUBIC), - /** - * use bilinear interpolation - */ - Bilinear(RenderingHints.VALUE_INTERPOLATION_BILINEAR), - /** - * use nearest neighbor interpolation - */ - NearestNeighbor(RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); - - private Object value; - - Interpolation(Object value) { - this.value = value; - } - - private void configureGraphics(Graphics2D g) { - g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, value); - } - } - - //--------------------------------------------------- Instance Variables - /** - * The cached image, if shouldUseCache() returns true - */ - private transient SoftReference cachedImage; - private boolean cacheCleared = true; - private boolean cacheable = false; - private boolean dirty = false; - private BufferedImageOp[] filters = new BufferedImageOp[0]; - private boolean antialiasing = true; - private Interpolation interpolation = Interpolation.NearestNeighbor; - private boolean visible = true; - private boolean inPaintContext; - - /** - * Creates a new instance of AbstractPainter. - */ - public AbstractPainter() { } - - /** - * Creates a new instance of AbstractPainter. - * @param cacheable indicates if this painter should be cacheable - */ - public AbstractPainter(boolean cacheable) { - setCacheable(cacheable); - } - - /** - * A defensive copy of the Effects to apply to the results - * of the AbstractPainter's painting operation. The array may - * be empty but it will never be null. - * @return the array of filters applied to this painter - */ - public final BufferedImageOp[] getFilters() { - BufferedImageOp[] results = new BufferedImageOp[filters.length]; - System.arraycopy(filters, 0, results, 0, results.length); - return results; - } - - /** - *

                    A convenience method for specifying the filters to use based on - * BufferedImageOps. These will each be individually wrapped by an ImageFilter - * and then setFilters(Effect... filters) will be called with the resulting - * array

                    - * - * - * @param effects the BufferedImageOps to wrap as filters - */ - public void setFilters(BufferedImageOp ... effects) { - if (effects == null) effects = new BufferedImageOp[0]; - BufferedImageOp[] old = getFilters(); - this.filters = new BufferedImageOp[effects.length]; - System.arraycopy(effects, 0, this.filters, 0, this.filters.length); - setDirty(true); - firePropertyChange("filters", old, getFilters()); - } - - /** - * Returns if antialiasing is turned on or not. The default value is true. - * This is a bound property. - * @return the current antialiasing setting - */ - public boolean isAntialiasing() { - return antialiasing; - } - /** - * Sets the antialiasing setting. This is a bound property. - * @param value the new antialiasing setting - */ - public void setAntialiasing(boolean value) { - boolean old = isAntialiasing(); - antialiasing = value; - if (old != value) setDirty(true); - firePropertyChange("antialiasing", old, isAntialiasing()); - } - - /** - * Gets the current interpolation setting. This property determines if interpolation will - * be used when drawing scaled images. @see java.awt.RenderingHints.KEY_INTERPOLATION. - * @return the current interpolation setting - */ - public Interpolation getInterpolation() { - return interpolation; - } - - /** - * Sets a new value for the interpolation setting. This setting determines if interpolation - * should be used when drawing scaled images. @see java.awt.RenderingHints.KEY_INTERPOLATION. - * @param value the new interpolation setting - */ - public void setInterpolation(Interpolation value) { - Object old = getInterpolation(); - this.interpolation = value == null ? Interpolation.NearestNeighbor : value; - if (old != value) setDirty(true); - firePropertyChange("interpolation", old, getInterpolation()); - } - - /** - * Gets the visible property. This controls if the painter should - * paint itself. It is true by default. Setting visible to false - * is good when you want to temporarily turn off a painter. An example - * of this is a painter that you only use when a button is highlighted. - * - * @return current value of visible property - */ - public boolean isVisible() { - return this.visible; - } - - /** - *

                    Sets the visible property. This controls if the painter should - * paint itself. It is true by default. Setting visible to false - * is good when you want to temporarily turn off a painter. An example - * of this is a painter that you only use when a button is highlighted.

                    - * - * @param visible New value of visible property. - */ - public void setVisible(boolean visible) { - boolean old = isVisible(); - this.visible = visible; - if (old != visible) setDirty(true); //not the most efficient, but I must do this otherwise a CompoundPainter - //or other aggregate painter won't know that it is now invalid - //there might be a tricky solution but that is a performance optimization - firePropertyChange("visible", old, isVisible()); - } - - /** - *

                    Gets whether this AbstractPainter can be cached as an image. - * If caching is enabled, then it is the responsibility of the developer to - * invalidate the painter (via {@link #clearCache}) if external state has - * changed in such a way that the painter is invalidated and needs to be - * repainted.

                    - * - * @return whether this is cacheable - */ - public boolean isCacheable() { - return cacheable; - } - - /** - *

                    Sets whether this AbstractPainter can be cached as an image. - * If true, this is treated as a hint. That is, a cacheable may or may not be used. - * The {@link #shouldUseCache} method actually determines whether the cacheable is used. - * However, if false, then this is treated as an absolute value. That is, no - * cacheable will be used.

                    - * - *

                    If set to false, then #clearCache is called to free system resources.

                    - * - * @param cacheable - */ - public void setCacheable(boolean cacheable) { - boolean old = isCacheable(); - this.cacheable = cacheable; - firePropertyChange("cacheable", old, isCacheable()); - if (!isCacheable()) { - clearCache(); - } - } - - /** - *

                    Call this method to clear the cacheable. This may be called whether there is - * a cacheable being used or not. If cleared, on the next call to paint, - * the painting routines will be called.

                    - * - *

                    SubclassesIf overridden in subclasses, you - * must call super.clearCache, or physical - * resources (such as an Image) may leak.

                    - */ - public void clearCache() { - BufferedImage cache = cachedImage == null ? null : cachedImage.get(); - if (cache != null) { - cache.flush(); - } - cacheCleared = true; - if (!isCacheable()) { - cachedImage = null; - } - } - - /** - * Only made package private for testing. Don't call this method outside - * of this class! This is NOT a bound property - */ - boolean isCacheCleared() { - return cacheCleared; - } - - /** - *

                    Called to allow Painter subclasses a chance to see if any state - * in the given object has changed from the last paint operation. If it has, then - * the Painter has a chance to mark itself as dirty, thus causing a - * repaint, even if cached.

                    - * - * @param object - */ - protected void validate(T object) { } - - /** - * Ye olde dirty bit. If true, then the painter is considered dirty and in need of - * being repainted. This is a bound property. - * - * @return true if the painter state has changed and the painter needs to be - * repainted. - */ - protected boolean isDirty() { - return dirty; - } - - /** - * Sets the dirty bit. If true, then the painter is considered dirty, and the cache - * will be cleared. This property is bound. - * - * @param d whether this Painter is dirty. - */ - protected void setDirty(boolean d) { - boolean old = isDirty(); - this.dirty = d; - firePropertyChange("dirty", old, isDirty()); - if (isDirty()) { - clearCache(); - } - } - - boolean isInPaintContext() { - return inPaintContext; - } - - void setInPaintContext(boolean inPaintContext) { - this.inPaintContext = inPaintContext; - } - - /** - *

                    Returns true if the painter should use caching. This method allows subclasses to - * specify the heuristics regarding whether to cache or not. If a Painter - * has intelligent rules regarding painting times, and can more accurately indicate - * whether it should be cached, it could implement that logic in this method.

                    - * - * @return whether or not a cache should be used - */ - protected boolean shouldUseCache() { - return isCacheable() || filters.length > 0; //NOTE, I can only do this because getFilters() is final - } - - /** - *

                    This method is called by the paint method prior to - * any drawing operations to configure the drawing surface. The default - * implementation sets the rendering hints that have been specified for - * this AbstractPainter.

                    - * - *

                    This method can be overridden by subclasses to modify the drawing - * surface before any painting happens.

                    - * - * @param g the graphics surface to configure. This will never be null. - * @see #paint(Graphics2D, Object, int, int) - */ - protected void configureGraphics(Graphics2D g) { - //configure antialiasing - if(isAntialiasing()) { - g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_ON); - } else { - g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_OFF); - } - - getInterpolation().configureGraphics(g); - } - - /** - * Subclasses must implement this method and perform custom painting operations - * here. - * @param width - * @param height - * @param g The Graphics2D object in which to paint - * @param object - */ - protected abstract void doPaint(Graphics2D g, T object, int width, int height); - - /** - * @inheritDoc - */ - @Override - public final void paint(Graphics2D g, T obj, int width, int height) { - if (g == null) { - throw new NullPointerException("The Graphics2D must be supplied"); - } - - if(!isVisible() || width < 1 || height < 1) { - return; - } - - configureGraphics(g); - - //paint to a temporary image if I'm caching, or if there are filters to apply - if (shouldUseCache() || filters.length > 0) { - validate(obj); - BufferedImage cache = cachedImage == null ? null : cachedImage.get(); - boolean invalidCache = null == cache || - cache.getWidth() != width || - cache.getHeight() != height; - - if (cacheCleared || invalidCache || isDirty()) { - //rebuild the cacheable. I do this both if a cacheable is needed, and if any - //filters exist. I only *save* the resulting image if caching is turned on - if (invalidCache) { - cache = GraphicsUtilities.createCompatibleTranslucentImage(width, height); - } - Graphics2D gfx = cache.createGraphics(); - - try { - gfx.setClip(0, 0, width, height); - - if (!invalidCache) { - // If we are doing a repaint, but we didn't have to - // recreate the image, we need to clear it back - // to a fully transparent background. - Composite composite = gfx.getComposite(); - gfx.setComposite(AlphaComposite.Clear); - gfx.fillRect(0, 0, width, height); - gfx.setComposite(composite); - } - - configureGraphics(gfx); - doPaint(gfx, obj, width, height); - } finally { - gfx.dispose(); - } - - if (!isInPaintContext()) { - for (BufferedImageOp f : getFilters()) { - cache = f.filter(cache, null); - } - } - - //only save the temporary image as the cacheable if I'm caching - if (shouldUseCache()) { - cachedImage = new SoftReference(cache); - cacheCleared = false; - } - } - - g.drawImage(cache, 0, 0, null); - } else { - //can't use the cacheable, so just paint - doPaint(g, obj, width, height); - } - - //painting has occured, so restore the dirty bit to false - setDirty(false); - } -} diff --git a/src/main/java/org/jdesktop/swingx/painter/AlphaPainter.java b/src/main/java/org/jdesktop/swingx/painter/AlphaPainter.java deleted file mode 100644 index f3dd03cd93..0000000000 --- a/src/main/java/org/jdesktop/swingx/painter/AlphaPainter.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * $Id: AlphaPainter.java 4147 2012-02-01 17:13:24Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - - -package org.jdesktop.swingx.painter; - -import org.jdesktop.beans.JavaBean; - -import java.awt.*; - -/** - * Applies an alpha value to an entire stack of painters. - * - * @author joshy - */ -@JavaBean -@SuppressWarnings("nls") -public class AlphaPainter extends CompoundPainter { - private float alpha = 1.0f; - - /** - * {@inheritDoc} - */ - @Override - protected void doPaint(Graphics2D g, T component, int width, int height) { - Graphics2D g2 = (Graphics2D) g.create(); - - try { - if(getTransform() != null) { - g2.setTransform(getTransform()); - } - if(alpha < 1) { - g2.setComposite(AlphaComposite.getInstance( - AlphaComposite.SRC_OVER, alpha)); - } - - super.doPaint(g2, component, width, height); - } finally { - g2.dispose(); - } - } - /* - public static void main(String ... args) { - JXPanel panel = new JXPanel(); - AlphaPainter alpha = new AlphaPainter(); - alpha.setAlpha(1f); - alpha.setPainters(new PinstripePainter(new Color(255,255,255,125),45,20,20)); - - panel.setBackgroundPainter(new CompoundPainter( - new MattePainter(Color.RED), - alpha - )); - - JFrame frame = new JFrame(); - frame.add(panel); - frame.pack(); - frame.setSize(200,200); - frame.setVisible(true); - }*/ - - /** - * Returns the current alpha value for this painter. This is the alpha value that will be applied - * to all painters set inside this painter. Alpha values will be multiplied. This means if you set an - * alpha of 0.5 on this painter and you nest a painter inside which uses an alpha of 0.5 then the final - * pixels drawn will have an alpha of 0.25. - * @return the current value of alpha property - */ - public float getAlpha() { - return alpha; - } - - /** - * Sets the current alpha value for this painter. This is the alpha value that will be applied - * to all painters set inside this painter. Alpha values will be multiplied. This means if you set an - * alpha of 0.5 on this painter and you nest a painter inside which uses an alpha of 0.5 then the final - * pixels drawn will have an alpha of 0.25. - * @param alpha the new value of the alpha property - */ - public void setAlpha(float alpha) { - float old = getAlpha(); - this.alpha = alpha; - firePropertyChange("alpha", old, getAlpha()); - } -} diff --git a/src/main/java/org/jdesktop/swingx/painter/BusyPainter.java b/src/main/java/org/jdesktop/swingx/painter/BusyPainter.java deleted file mode 100644 index cb11c6fd3e..0000000000 --- a/src/main/java/org/jdesktop/swingx/painter/BusyPainter.java +++ /dev/null @@ -1,622 +0,0 @@ -/* - * $Id: BusyPainter.java 4156 2012-02-02 19:54:38Z kschaefe $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.painter; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.util.PaintUtils; - -import java.awt.*; -import java.awt.geom.Ellipse2D; -import java.awt.geom.PathIterator; -import java.awt.geom.Point2D.Float; -import java.awt.geom.RoundRectangle2D; -import java.util.ArrayList; -import java.util.List; -import java.util.NoSuchElementException; - -/** - * A specific painter that paints an "infinite progress" like animation. - */ -@JavaBean -@SuppressWarnings("nls") -public class BusyPainter extends AbstractPainter { - - /** - * Direction is used to set the initial direction in which the - * animation starts. - * - * @see BusyPainter#setDirection(Direction) - */ - public static enum Direction { - /** - * cycle proceeds forward - */ - RIGHT, - /** cycle proceeds backward */ - LEFT, - } - - private int frame = -1; - - private int points = 8; - - private Color baseColor = new Color(200, 200, 200); - - private Color highlightColor = Color.BLACK; - - private int trailLength = 4; - - private Shape pointShape; - - private Shape trajectory; - - private Direction direction = Direction.RIGHT; - - private boolean paintCentered; - - /** - * Creates new busy painter initialized to the shape of circle and bounds size 26x26 points. - */ - public BusyPainter() { - this(26); - } - - /** - * Creates new painter initialized to the shape of circle and bounds of square of specified height. - * @param height Painter height. - */ - public BusyPainter(int height) { - this(getScaledDefaultPoint(height), getScaledDefaultTrajectory(height)); - } - - /** - * Initializes painter to the specified trajectory and and point shape. Bounds are dynamically calculated to so the specified trajectory fits in. - * @param point Point shape. - * @param trajectory Trajectory shape. - */ - public BusyPainter(Shape point, Shape trajectory) { - init(point, trajectory, Color.LIGHT_GRAY, Color.BLACK); - } - - protected static Shape getScaledDefaultTrajectory(int height) { - return new Ellipse2D.Float(((height * 8) / 26) / 2, ((height * 8) / 26) / 2, height - - ((height * 8) / 26), height - ((height * 8) / 26)); - } - - protected static Shape getScaledDefaultPoint(int height) { - return new RoundRectangle2D.Float(0, 0, (height * 8) / 26, 4, - 4, 4); - } - - /** - * Initializes painter to provided shapes and default colors. - * @param point Point shape. - * @param trajectory Trajectory shape. - */ - protected void init(Shape point, Shape trajectory, Color baseColor, Color highlightColor) { - this.baseColor = baseColor; - this.highlightColor = highlightColor; - this.pointShape = point; - this.trajectory = trajectory; - } - - /** - * @inheritDoc - */ - @Override - protected void doPaint(Graphics2D g, Object t, int width, int height) { - Rectangle r = getTrajectory().getBounds(); - int tw = width - r.width - 2*r.x; - int th = height - r.height - 2*r.y; - if (isPaintCentered()) { - g.translate(tw/2, th/2); - } - - PathIterator pi = trajectory.getPathIterator(null); - float[] coords = new float[6]; - Float cp = new Float(); - Float sp = new Float(); - int ret; - float totalDist = 0; - List segStack = new ArrayList(); - do { - try { - ret = pi.currentSegment(coords); - } catch (NoSuchElementException e) { - // invalid object definition - one of the bounds is zero or less - return; - } - if (ret == PathIterator.SEG_LINETO || (ret == PathIterator.SEG_CLOSE && (sp.x != cp.x || sp.y != cp.y))) { - //close by line - float c = calcLine(coords, cp); - totalDist += c; - // move the point to the end (just so it is same for all of them - segStack.add(new float[] { c, 0, 0, 0, 0, coords[0], coords[1], ret }); - cp.x = coords[0]; - cp.y = coords[1]; - } - if (ret == PathIterator.SEG_MOVETO) { - sp.x = cp.x = coords[0]; - sp.y = cp.y = coords[1]; - - } - if (ret == PathIterator.SEG_CUBICTO) { - float c = calcCube(coords, cp); - totalDist += c; - segStack.add(new float[] { c, coords[0], coords[1], coords[2], - coords[3], coords[4], coords[5], ret }); - cp.x = coords[4]; - cp.y = coords[5]; - } - if (ret == PathIterator.SEG_QUADTO) { - float c = calcLengthOfQuad(coords, cp); - totalDist += c; - segStack.add(new float[] { c, coords[0], coords[1], 0 ,0 , coords[2], - coords[3], ret }); - cp.x = coords[2]; - cp.y = coords[3]; - } - // got a starting point, center point on it. - pi.next(); - } while (!pi.isDone()); - float nxtP = totalDist / getPoints(); - List pList = new ArrayList(); - pList.add(new Float(sp.x, sp.y)); - int sgIdx = 0; - float[] sgmt = segStack.get(sgIdx); - float len = sgmt[0]; - float travDist = nxtP; - Float center = new Float(sp.x, sp.y); - for (int i = 1; i < getPoints(); i++) { - while (len < nxtP) { - sgIdx++; - // Be carefull when messing around with points. - sp.x = sgmt[5]; - sp.y = sgmt[6]; - sgmt = segStack.get(sgIdx); - travDist = nxtP - len; - len += sgmt[0]; - } - len -= nxtP; - Float p = calcPoint(travDist, sp, sgmt, width, height); - pList.add(p); - center.x += p.x; - center.y += p.y; - travDist += nxtP; - } - // calculate center - center.x = ((float) width) / 2; - center.y = ((float) height) / 2; - - // draw the stuff - int i = 0; - g.translate(center.x, center.y); - for (Float p : pList) { - drawAt(g, i++, p, center); - } - g.translate(-center.x, -center.y); - - if (isPaintCentered()) { - g.translate(-tw/2, -th/2); - } - } - - /** - * Gets value of centering hint. If true, shape will be positioned in the center of painted area. - * @return Whether shape will be centered over painting area or not. - */ - public boolean isPaintCentered() { - return this.paintCentered; - } - - /** - * Centers shape in the area covered by the painter. - * @param paintCentered Centering hint. - */ - public void setPaintCentered(boolean paintCentered) { - boolean old = isPaintCentered(); - this.paintCentered = paintCentered; - firePropertyChange("paintCentered", old, isPaintCentered()); - } - - private void drawAt(Graphics2D g, int i, Float p, Float c) { - g.setColor(calcFrameColor(i)); - paintRotatedCenteredShapeAtPoint(p, c, g); - } - - private void paintRotatedCenteredShapeAtPoint(Float p, Float c, Graphics2D g) { - Shape s = getPointShape(); - double hh = s.getBounds().getHeight() / 2; - double wh = s.getBounds().getWidth() / 2; - double t, x, y; - double a = c.y - p.y; - double b = p.x - c.x; - double sa = Math.signum(a); - double sb = Math.signum(b); - sa = sa == 0 ? 1 : sa; - sb = sb == 0 ? 1 : sb; - a = Math.abs(a); - b = Math.abs(b); - t = Math.atan(a / b); - t = sa > 0 ? sb > 0 ? -t : -Math.PI + t : sb > 0 ? t : Math.PI - t; - x = Math.sqrt(a * a + b * b) - wh; - y = -hh; - g.rotate(t); - g.translate(x, y); - g.fill(s); - g.translate(-x, -y); - g.rotate(-t); - - } - - private Float calcPoint(float dist2go, Float startPoint, - float[] sgmt, int w, int h) { - Float f = new Float(); - if (sgmt[7] == PathIterator.SEG_LINETO) { - // linear - float a = sgmt[5] - startPoint.x; - float b = sgmt[6] - startPoint.y; - float pathLen = sgmt[0]; - f.x = startPoint.x + a * dist2go / pathLen; - f.y = startPoint.y + b * dist2go / pathLen; - } else if (sgmt[7] == PathIterator.SEG_QUADTO) { - // quadratic curve - Float ctrl = new Float(sgmt[1]/w, sgmt[2]/h); - Float end = new Float(sgmt[5]/w, sgmt[6]/h); - Float start = new Float(startPoint.x/w, startPoint.y/h); - - // trans coords from abs to rel - f = getXY(dist2go / sgmt[0], start, ctrl, end); - f.x *= w; - f.y *= h; - - } else if (sgmt[7] == PathIterator.SEG_CUBICTO) { - // bezier curve - float x = Math.abs(startPoint.x - sgmt[5]); - float y = Math.abs(startPoint.y - sgmt[6]); - - // trans coords from abs to rel - float c1rx = Math.abs(startPoint.x - sgmt[1]) / x; - float c1ry = Math.abs(startPoint.y - sgmt[2]) / y; - float c2rx = Math.abs(startPoint.x - sgmt[3]) / x; - float c2ry = Math.abs(startPoint.y - sgmt[4]) / y; - f = getXY(dist2go / sgmt[0], c1rx, c1ry, c2rx, c2ry); - - float a = startPoint.x - sgmt[5]; - float b = startPoint.y - sgmt[6]; - - f.x = startPoint.x - f.x * a; - f.y = startPoint.y - f.y * b; - } - return f; - } - - - /** - * Calculates length of the linear segment. - * @param coords Segment coordinates. - * @param cp Start point. - * @return Length of the segment. - */ - private float calcLine(float[] coords, Float cp) { - float a = cp.x - coords[0]; - float b = cp.y - coords[1]; - float c = (float) Math.sqrt(a * a + b * b); - return c; - } - - /** - * Claclulates length of the cubic segment. - * @param coords Segment coordinates. - * @param cp Start point. - * @return Length of the segment. - */ - private float calcCube(float[] coords, Float cp) { - float x = Math.abs(cp.x - coords[4]); - float y = Math.abs(cp.y - coords[5]); - - // trans coords from abs to rel - float c1rx = Math.abs(cp.x - coords[0]) / x; - float c1ry = Math.abs(cp.y - coords[1]) / y; - float c2rx = Math.abs(cp.x - coords[2]) / x; - float c2ry = Math.abs(cp.y - coords[3]) / y; - float prevLength = 0, prevX = 0, prevY = 0; - for (float t = 0.01f; t <= 1.0f; t += .01f) { - Float xy = getXY(t, c1rx, c1ry, c2rx, c2ry); - prevLength += (float) Math.sqrt((xy.x - prevX) * (xy.x - prevX) - + (xy.y - prevY) * (xy.y - prevY)); - prevX = xy.x; - prevY = xy.y; - } - // prev len is a fraction num of the real path length - float z = ((Math.abs(x) + Math.abs(y)) / 2) * prevLength; - return z; - } - - /** - * Calculates length of the quadratic segment - * @param coords Segment coordinates - * @param cp Start point. - * @return Length of the segment. - */ - private float calcLengthOfQuad(float[] coords, Float cp) { - Float ctrl = new Float(coords[0], coords[1]); - Float end = new Float(coords[2], coords[3]); - // get abs values - // ctrl1 - float c1ax = Math.abs(cp.x - ctrl.x) ; - float c1ay = Math.abs(cp.y - ctrl.y) ; - // end1 - float e1ax = Math.abs(cp.x - end.x) ; - float e1ay = Math.abs(cp.y - end.y) ; - // get max value on each axis - float maxX = Math.max(c1ax, e1ax); - float maxY = Math.max(c1ay, e1ay); - - // trans coords from abs to rel - // ctrl1 - ctrl.x = c1ax / maxX; - ctrl.y = c1ay / maxY; - // end1 - end.x = e1ax / maxX; - end.y = e1ay / maxY; - - // claculate length - float prevLength = 0, prevX = 0, prevY = 0; - for (float t = 0.01f; t <= 1.0f; t += .01f) { - Float xy = getXY(t, new Float(0,0), ctrl, end); - prevLength += (float) Math.sqrt((xy.x - prevX) * (xy.x - prevX) - + (xy.y - prevY) * (xy.y - prevY)); - prevX = xy.x; - prevY = xy.y; - } - // prev len is a fraction num of the real path length - float a = Math.abs(coords[2] - cp.x); - float b = Math.abs(coords[3] - cp.y); - float dist = (float) Math.sqrt(a*a+b*b); - return prevLength * dist; - } - - /** - * Calculates the XY point for a given t value. - * - * The general spline equation is: x = b0*x0 + b1*x1 + b2*x2 + b3*x3 y = - * b0*y0 + b1*y1 + b2*y2 + b3*y3 where: b0 = (1-t)^3 b1 = 3 * t * (1-t)^2 b2 = - * 3 * t^2 * (1-t) b3 = t^3 We know that (x0,y0) == (0,0) and (x1,y1) == - * (1,1) for our splines, so this simplifies to: x = b1*x1 + b2*x2 + b3 y = - * b1*x1 + b2*x2 + b3 - * - * @author chet - * - * @param t parametric value for spline calculation - */ - private Float getXY(float t, float x1, float y1, float x2, float y2) { - Float xy; - float invT = (1 - t); - float b1 = 3 * t * (invT * invT); - float b2 = 3 * (t * t) * invT; - float b3 = t * t * t; - xy = new Float((b1 * x1) + (b2 * x2) + b3, (b1 * y1) - + (b2 * y2) + b3); - return xy; - } - - /** - * Calculates relative position of the point on the quad curve in time t<0,1>. - * @param t distance on the curve - * @param ctrl Control point in rel coords - * @param end End point in rel coords - * @return Solution of the quad equation for time T in non complex space in rel coords. - */ - public static Float getXY(float t, Float begin, Float ctrl, Float end) { - /* - * P1 = (x1, y1) - start point of curve - * P2 = (x2, y2) - end point of curve - * Pc = (xc, yc) - control point - * - * Pq(t) = P1*(1 - t)^2 + 2*Pc*t*(1 - t) + P2*t^2 = - * = (P1 - 2*Pc + P2)*t^2 + 2*(Pc - P1)*t + P1 - * t = [0:1] - * // thx Jim ... - * - * b0 = (1 -t)^2, b1 = 2*t*(1-t), b2 = t^2 - */ - Float xy; - float invT = (1 - t); - float b0 = invT * invT; - float b1 = 2 * t * invT ; - float b2 = t * t; - xy = new Float(b0 * begin.x + (b1 * ctrl.x) + b2* end.x, b0 * begin.y + (b1 * ctrl.y) + b2* end.y); - - return xy; - } - - /** - * Selects appropriate color for given frame based on the frame position and gradient difference. - * @param i Frame. - * @return Frame color. - */ - private Color calcFrameColor(final int i) { - if (frame == -1) { - return getBaseColor(); - } - - for (int t = 0; t < getTrailLength(); t++) { - if (direction == Direction.RIGHT - && i == (frame - t + getPoints()) % getPoints()) { - float terp = 1 - ((float) (getTrailLength() - t)) - / (float) getTrailLength(); - return PaintUtils.interpolate(getBaseColor(), - getHighlightColor(), terp); - } else if (direction == Direction.LEFT - && i == (frame + t) % getPoints()) { - float terp = ((float) (t)) / (float) getTrailLength(); - return PaintUtils.interpolate(getBaseColor(), - getHighlightColor(), terp); - } - } - return getBaseColor(); - } - - /** - * Gets current frame. - * @return Current frame. - */ - public int getFrame() { - return frame; - } - - /**Sets current frame. - * @param frame Current frame. - */ - public void setFrame(int frame) { - int old = getFrame(); - this.frame = frame; - firePropertyChange("frame", old, getFrame()); - } - - /** - * Gets base color. - * @return Base color. - */ - public Color getBaseColor() { - return baseColor; - } - - /** - * Sets new base color. Bound property. - * @param baseColor Base color. - */ - public void setBaseColor(Color baseColor) { - Color old = getBaseColor(); - this.baseColor = baseColor; - firePropertyChange("baseColor", old, getBaseColor()); - } - - /** - * Gets highlight color. - * @return Current highlight color. - */ - public Color getHighlightColor() { - return highlightColor; - } - - /** - * Sets new highlight color. Bound property. - * @param highlightColor New highlight color. - */ - public void setHighlightColor(Color highlightColor) { - Color old = getHighlightColor(); - this.highlightColor = highlightColor; - firePropertyChange("highlightColor", old, getHighlightColor()); - } - - /** - * Gets total amount of distinct points in spinner. - * @return Total amount of points. - */ - public int getPoints() { - return points; - } - - /** - * Sets total amount of points in spinner. Bound property. - * @param points Total amount of points. - */ - public void setPoints(int points) { - int old = getPoints(); - this.points = points; - firePropertyChange("points", old, getPoints()); - } - - /** - * Gets length of trail in number of points. - * @return Trail lenght. - */ - public int getTrailLength() { - return trailLength; - } - - /** - * Sets length of the trail in points. Bound property. - * @param trailLength Trail length in points. - */ - public void setTrailLength(int trailLength) { - int old = getTrailLength(); - this.trailLength = trailLength; - firePropertyChange("trailLength", old, getTrailLength()); - } - - /** - * Gets shape of current point. - * @return Shape of the point. - */ - public final Shape getPointShape() { - return pointShape; - } - - /** - * Sets new point shape. Bound property. - * @param pointShape new Shape. - */ - public final void setPointShape(Shape pointShape) { - Shape old = getPointShape(); - this.pointShape = pointShape; - firePropertyChange("pointShape", old, getPointShape()); - } - - /** - * Gets current trajectory. - * @return Current spinner trajectory . - */ - public final Shape getTrajectory() { - return trajectory; - } - - /** - * Sets new trajectory. Expected trajectory have to be closed shape. Bound property. - * @param trajectory New trajectory. - */ - public final void setTrajectory(Shape trajectory) { - Shape old = getTrajectory(); - this.trajectory = trajectory; - firePropertyChange("trajectory", old, getTrajectory()); - } - - /** - * Gets current direction of spinning. - * @return Current spinning direction. - */ - public Direction getDirection() { - return direction; - } - - /** - * Sets new spinning direction. - * @param dir Spinning direction. - */ - public void setDirection(Direction dir) { - Direction old = getDirection(); - this.direction = dir; - firePropertyChange("direction", old, getDirection()); - } -} diff --git a/src/main/java/org/jdesktop/swingx/painter/CheckerboardPainter.java b/src/main/java/org/jdesktop/swingx/painter/CheckerboardPainter.java deleted file mode 100644 index bd9d7c0980..0000000000 --- a/src/main/java/org/jdesktop/swingx/painter/CheckerboardPainter.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * $Id: CheckerboardPainter.java 4147 2012-02-01 17:13:24Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.painter; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.util.PaintUtils; - -import java.awt.*; - -/** - *

                    A Painter implementation that paints a checkerboard pattern. The light - * and dark colors (Paint instances) are configurable, as are the size of the - * squares (squareSize).

                    - * - *

                    To configure a checkerboard pattern that used a gradient for the dark - * tiles and Color.WHITE for the light tiles, you could: - *

                    
                    - *  GradientPaint gp = new GradientPaint(
                    - *      new Point2D.Double(0, 0),
                    - *      Color.BLACK,
                    - *      new Point2D.Double(0, 32),
                    - *      Color.GRAY);
                    - *  CheckerboardPainter p = new CheckerboardPainter();
                    - *  p.setDarkPaint(gp);
                    - *  p.setLightPaint(Color.WHITE);
                    - *  p.setSquareSize(32);
                    - *  panel.seBackgroundPainter(p);
                    - * 

                    - * - *

                    Note that in this example, the "32" in the GradientPaint matches the "32" - * set for the squareSize. This is necessary because GradientPaints don't - * readjust themselves for the size of the square. They are fixed and immutable - * at the time of creation.

                    - * - * @author rbair - */ -@JavaBean -@SuppressWarnings("nls") -public class CheckerboardPainter extends AbstractPainter { - private transient Paint checkerPaint; - - private Paint darkPaint = new Color(204, 204, 204); - private Paint lightPaint = Color.WHITE; - private double squareSize = 8; - - /** - * Create a new CheckerboardPainter. By default the light color is Color.WHITE, - * the dark color is a light gray, and the square length is 8. - */ - public CheckerboardPainter() { - } - - /** - * Create a new CheckerboardPainter with the specified light and dark paints. - * By default the square length is 8. - * - * @param darkPaint the paint used to draw the dark squares - * @param lightPaint the paint used to draw the light squares - */ - public CheckerboardPainter(Paint darkPaint, Paint lightPaint) { - this(darkPaint, lightPaint, 8); - } - - /** - * Create a new CheckerboardPainter with the specified light and dark paints - * and the specified square size. - * - * @param darkPaint the paint used to draw the dark squares - * @param lightPaint the paint used to draw the light squares - * @param squareSize the squareSize of the checker board squares - */ - //TODO squareSize should become int? only ever treated as one - public CheckerboardPainter(Paint darkPaint, Paint lightPaint, double squareSize) { - this.darkPaint = darkPaint; - this.lightPaint = lightPaint; - this.squareSize = squareSize; - } - - - /** - * Specifies the squareSize of the squares. By default, it is 8. A squareSize of <= - * 0 will cause an IllegalArgumentException to be thrown. - * - * @param squareSize the squareSize of one side of a square tile. Must be > 0. - */ - public void setSquareSize(double squareSize) { - if (squareSize <= 0) { - throw new IllegalArgumentException("Length must be > 0"); - } - - double old = getSquareSize(); - this.squareSize = squareSize; - checkerPaint = null; - setDirty(true); - firePropertyChange("squareSize", old, getSquareSize()); - } - - /** - * Gets the current square length. - * - * @return the squareSize. Will be > 0 - */ - public double getSquareSize() { - return squareSize; - } - - /** - * Specifies the paint to use for dark tiles. This is a Paint and - * may be anything, including a TexturePaint for painting images. If null, - * the background color of the component is used. - * - * @param color the Paint to use for painting the "dark" tiles. May be null. - */ - public void setDarkPaint(Paint color) { - Paint old = getDarkPaint(); - this.darkPaint = color; - checkerPaint = null; - setDirty(true); - firePropertyChange("darkPaint", old, getDarkPaint()); - } - - /** - * - * Gets the current dark paint. - * @return the Paint used for painting the "dark" tiles. May be null - */ - public Paint getDarkPaint() { - return darkPaint; - } - - /** - * Specifies the paint to use for light tiles. This is a Paint and - * may be anything, including a TexturePaint for painting images. If null, - * the foreground color of the component is used. - * - * @param color the Paint to use for painting the "light" tiles. May be null. - */ - public void setLightPaint(Paint color) { - Paint old = getLightPaint(); - this.lightPaint = color; - checkerPaint = null; - setDirty(true); - firePropertyChange("lightPaint", old, getLightPaint()); - } - - /** - * - * gets the current light paint - * @return the Paint used for painting the "light" tiles. May be null - */ - public Paint getLightPaint() { - return lightPaint; - } - - /** - * Helper method that creates and returns the Paint that incorporates the - * sizes and light and dark Paints in one TexturePaint. I may want to cache - * this value in the future for performance reasons - */ - private Paint getCheckerPaint(Object c) { - if (checkerPaint == null) { - Paint p1 = PainterUtils.getForegroundPaint(getLightPaint(), c); - Paint p2 = PainterUtils.getBackgroundPaint(getDarkPaint(), c); - - checkerPaint = PaintUtils.getCheckerPaint(p1, p2, (int)(getSquareSize() * 2)); - } - return checkerPaint; - } - - /** - * {@inheritDoc} - */ - @Override - protected void doPaint(Graphics2D g, Object t, int width, int height) { - g.setPaint(getCheckerPaint(t)); - g.fillRect(0, 0, width, height); - } -} diff --git a/src/main/java/org/jdesktop/swingx/painter/CompoundPainter.java b/src/main/java/org/jdesktop/swingx/painter/CompoundPainter.java deleted file mode 100644 index 74ae90a950..0000000000 --- a/src/main/java/org/jdesktop/swingx/painter/CompoundPainter.java +++ /dev/null @@ -1,389 +0,0 @@ -/* - * $Id: CompoundPainter.java 4156 2012-02-02 19:54:38Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.painter; - -import org.jdesktop.beans.JavaBean; - -import java.awt.*; -import java.awt.geom.AffineTransform; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.lang.ref.WeakReference; - -/** - *

                    A {@link Painter} implementation composed of an array of Painters. - * CompoundPainter provides a means for combining several individual - * Painters, or groups of them, into one logical unit. Each of the - * Painters are executed in order. BufferedImageOp filter effects can - * be applied to them together as a whole. The entire set of painting operations - * may be cached together.

                    - * - *

                    - * - *

                    For example, if I want to create a CompoundPainter that started with a blue - * background, had pinstripes on it running at a 45 degree angle, and those - * pinstripes appeared to "fade in" from left to right, I would write the following: - *

                    
                    - *  Color blue = new Color(0x417DDD);
                    - *  Color translucent = new Color(blue.getRed(), blue.getGreen(), blue.getBlue(), 0);
                    - *  panel.setBackground(blue);
                    - *  panel.setForeground(Color.LIGHT_GRAY);
                    - *  GradientPaint blueToTranslucent = new GradientPaint(
                    - *    new Point2D.Double(.4, 0),
                    - *    blue,
                    - *    new Point2D.Double(1, 0),
                    - *    translucent);
                    - *  MattePainter veil = new MattePainter(blueToTranslucent);
                    - *  veil.setPaintStretched(true);
                    - *  Painter pinstripes = new PinstripePainter(45);
                    - *  Painter backgroundPainter = new RectanglePainter(this.getBackground(), null);
                    - *  Painter p = new CompoundPainter(backgroundPainter, pinstripes, veil);
                    - *  panel.setBackgroundPainter(p);
                    - * 

                    - * - * @author rbair - */ -@JavaBean -@SuppressWarnings("nls") -public class CompoundPainter extends AbstractPainter { - private static class Handler implements PropertyChangeListener { - private final WeakReference> ref; - - public Handler(CompoundPainter painter) { - ref = new WeakReference>(painter); - } - - /** - * {@inheritDoc} - */ - @Override - public void propertyChange(PropertyChangeEvent evt) { - CompoundPainter painter = ref.get(); - - if (painter == null) { - AbstractPainter src = (AbstractPainter) evt.getSource(); - src.removePropertyChangeListener(this); - } else { - String property = evt.getPropertyName(); - - if ("dirty".equals(property) && evt.getNewValue() == Boolean.FALSE) { - return; - } - - painter.setDirty(true); - } - } - } - - private Handler handler; - - private Painter[] painters = new Painter[0]; - private AffineTransform transform; - private boolean clipPreserved = false; - - private boolean checkForDirtyChildPainters = true; - - /** Creates a new instance of CompoundPainter */ - public CompoundPainter() { - this((Painter[]) null); - } - - /** - * Convenience constructor for creating a CompoundPainter for an array - * of painters. A defensive copy of the given array is made, so that future - * modification to the array does not result in changes to the CompoundPainter. - * - * @param painters array of painters, which will be painted in order - */ - public CompoundPainter(Painter... painters) { - handler = new Handler(this); - - setPainters(painters); - } - - /** - * Sets the array of Painters to use. These painters will be executed in - * order. A null value will be treated as an empty array. To prevent unexpected - * behavior all values in provided array are copied to internally held array. - * Any changes to the original array will not be reflected. - * - * @param painters array of painters, which will be painted in order - */ - public void setPainters(Painter... painters) { - Painter[] old = getPainters(); - - for (Painter p : old) { - if (p instanceof AbstractPainter) { - ((AbstractPainter) p).removePropertyChangeListener(handler); - } - } - - this.painters = new Painter[painters == null ? 0 : painters.length]; - if (painters != null) { - System.arraycopy(painters, 0, this.painters, 0, this.painters.length); - } - - for (Painter p : this.painters) { - if (p instanceof AbstractPainter) { - ((AbstractPainter) p).addPropertyChangeListener(handler); - } - } - - setDirty(true); - firePropertyChange("painters", old, getPainters()); - } - - /** - * Gets the array of painters used by this CompoundPainter - * @return a defensive copy of the painters used by this CompoundPainter. - * This will never be null. - */ - public final Painter[] getPainters() { - Painter[] results = new Painter[painters.length]; - System.arraycopy(painters, 0, results, 0, results.length); - return results; - } - - - /** - * Indicates if the clip produced by any painter is left set once it finishes painting. - * Normally the clip will be reset between each painter. Setting clipPreserved to - * true can be used to let one painter mask other painters that come after it. - * @return if the clip should be preserved - * @see #setClipPreserved(boolean) - */ - public boolean isClipPreserved() { - return clipPreserved; - } - - /** - * Sets if the clip should be preserved. - * Normally the clip will be reset between each painter. Setting clipPreserved to - * true can be used to let one painter mask other painters that come after it. - * - * @param shouldRestoreState new value of the clipPreserved property - * @see #isClipPreserved() - */ - public void setClipPreserved(boolean shouldRestoreState) { - boolean oldShouldRestoreState = isClipPreserved(); - this.clipPreserved = shouldRestoreState; - setDirty(true); - firePropertyChange("clipPreserved",oldShouldRestoreState,shouldRestoreState); - } - - /** - * Gets the current transform applied to all painters in this CompoundPainter. May be null. - * @return the current AffineTransform - */ - public AffineTransform getTransform() { - return transform; - } - - /** - * Set a transform to be applied to all painters contained in this CompoundPainter - * @param transform a new AffineTransform - */ - public void setTransform(AffineTransform transform) { - AffineTransform old = getTransform(); - this.transform = transform; - setDirty(true); - firePropertyChange("transform",old,transform); - } - - /** - *

                    Iterates over all child Painters and gives them a chance - * to validate themselves. If any of the child painters are dirty, then - * this CompoundPainter marks itself as dirty.

                    - * - * {@inheritDoc} - */ - @Override - protected void validate(T object) { - boolean dirty = false; - for (Painter p : painters) { - if (p instanceof AbstractPainter) { - AbstractPainter ap = (AbstractPainter) p; - ap.validate(object); - if (ap.isDirty()) { - dirty = true; - break; - } - } - } - clearLocalCacheOnly = true; - setDirty(dirty); //super will call clear cache - clearLocalCacheOnly = false; - } - - //indicates whether the local cache should be cleared only, as opposed to the - //cache's of all of the children. This is needed to optimize the caching strategy - //when, during validate, the CompoundPainter is marked as dirty - private boolean clearLocalCacheOnly = false; - - /** - * Used by {@link #isDirty()} to check if the child Painters - * should be checked for their dirty flag as part of - * processing.
                    - * Default value is: true
                    - * This should be set to false if the cacheable state - * of the child Painters are different from each other. This - * will allow the cacheable == true Painters to - * keep their cached image during regular repaints. In this case, - * client code should call {@link #clearCache()} manually when the cacheable - * Painters should be updated. - * - * - * @see #isDirty() - */ - public boolean isCheckingDirtyChildPainters() { - return checkForDirtyChildPainters; - } - /** - * Set the flag used by {@link #isDirty()} to check if the - * child Painters should be checked for their - * dirty flag as part of processing. - * - * @see #isCheckingDirtyChildPainters() - * @see #isDirty() - */ - public void setCheckingDirtyChildPainters(boolean b) { - boolean old = isCheckingDirtyChildPainters(); - this.checkForDirtyChildPainters = b; - firePropertyChange("checkingDirtyChildPainters",old, isCheckingDirtyChildPainters()); - } - - /** - * {@inheritDoc} - * - * @impl This CompoundPainter is dirty if it, or (optionally) any of its children, - * are dirty. If the super implementation returns true, we return - * true. Otherwise, if {@link #isCheckingDirtyChildPainters()} is - * true, we iterate over all child Painters and query them to - * see if they are dirty. If so, then true is returned. Otherwise, we return - * false. - * @see #isCheckingDirtyChildPainters() - */ - @Override - protected boolean isDirty() { - boolean dirty = super.isDirty(); - if (dirty) { - return true; - } - else if (isCheckingDirtyChildPainters()) { - for (Painter p : painters) { - if (p instanceof AbstractPainter) { - AbstractPainter ap = (AbstractPainter) p; - if (ap.isDirty()) { - return true; - } - } - } - } - return false; - } - - /** - * {@inheritDoc} - */ - @Override - protected void setDirty(boolean d) { - boolean old = super.isDirty(); - boolean ours = isDirty(); - - super.setDirty(d); - - //must perform this check to ensure we do not double notify - if (d != old && d == ours) { - firePropertyChange("dirty", old, isDirty()); - } - } - - /** - *

                    Clears the cache of this Painter, and all child - * Painters. This is done to ensure that resources - * are collected, even if clearCache is called by some framework - * or other code that doesn't realize this is a CompoundPainter.

                    - * - *

                    Call #clearLocalCache if you only want to clear the cache of this - * CompoundPainter - * - * {@inheritDoc} - */ - @Override - public void clearCache() { - if (!clearLocalCacheOnly) { - for (Painter p : painters) { - if (p instanceof AbstractPainter) { - AbstractPainter ap = (AbstractPainter) p; - ap.clearCache(); - } - } - } - super.clearCache(); - } - - /** - *

                    Clears the cache of this painter only, and not of any of the children.

                    - */ - public void clearLocalCache() { - super.clearCache(); - } - - /** - * {@inheritDoc} - */ - @Override - protected void doPaint(Graphics2D g, T component, int width, int height) { - for (Painter p : getPainters()) { - Graphics2D temp = (Graphics2D) g.create(); - - try { - p.paint(temp, component, width, height); - if(isClipPreserved()) { - g.setClip(temp.getClip()); - } - } finally { - temp.dispose(); - } - } - } - - /** - * {@inheritDoc} - */ - @Override - protected void configureGraphics(Graphics2D g) { - //applies the transform - AffineTransform tx = getTransform(); - if (tx != null) { - g.setTransform(tx); - } - } - - /** - * {@inheritDoc} - */ - @Override - protected boolean shouldUseCache() { - return super.shouldUseCache(); // || (painters != null && painters.length > 1); - } -} diff --git a/src/main/java/org/jdesktop/swingx/painter/GlossPainter.java b/src/main/java/org/jdesktop/swingx/painter/GlossPainter.java deleted file mode 100644 index 855ff3175b..0000000000 --- a/src/main/java/org/jdesktop/swingx/painter/GlossPainter.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * $Id: GlossPainter.java 4147 2012-02-01 17:13:24Z kschaefe $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.painter; - -import org.jdesktop.beans.JavaBean; - -import java.awt.*; -import java.awt.geom.Area; -import java.awt.geom.Ellipse2D; - -/** - *

                    A Painter implementation that simulates a gloss effect. The gloss can - * be positioned at the top or bottom of the drawing area. To fill the gloss, - * this painter uses a Paint instance which can be used to fill with a color - * (opaque or translucent), a texture, a gradient...

                    - *

                    The following example creates a white gloss at the top of the drawing - * area:

                    - *
                    - *  GlossPainter p = new GlossPainter();
                    - *  p.setPaint(new Color(1.0f, 1.0f, 1.0f, 0.2f);
                    - *  p.setPosition(GlossPainter.GlossPosition.TOP);
                    - *  panel.setBackgroundPainter(p);
                    - * 
                    - *

                    The values shown in this examples are the values used by default if - * they are not specified.

                    - * - * @author Romain Guy - */ -@JavaBean -@SuppressWarnings("nls") -public class GlossPainter extends AbstractPainter { - /** - *

                    Used to define the position of the gloss on the painted area.

                    - */ - public enum GlossPosition { - TOP, BOTTOM - } - - private Paint paint; - private GlossPosition position; - - /** - *

                    Creates a new gloss painter positioned at the top of the painted - * area with a 20% translucent white color.

                    - */ - public GlossPainter() { - this(new Color(1.0f, 1.0f, 1.0f, 0.2f), GlossPosition.TOP); - } - - /** - *

                    Creates a new gloss painter positioned at the top of the painted - * area with the specified paint.

                    - * - * @param paint The paint to be used when filling the gloss - */ - public GlossPainter(Paint paint) { - this(paint, GlossPosition.TOP); - } - - /** - *

                    Creates a new gloss painter positioned at the specified position - * and using a white, 20% translucent paint.

                    - * - * @param position The position of the gloss on the painted area - */ - public GlossPainter(GlossPosition position) { - this(new Color(1.0f, 1.0f, 1.0f, 0.2f), position); - } - - /** - *

                    Creates a new gloss painter positioned at the specified position - * and painted with the specified paint.

                    - * - * @param paint The paint to be used when filling the gloss - * @param position The position of the gloss on the painted area - */ - public GlossPainter(Paint paint, GlossPosition position) { - this.setPaint(paint); - this.setPosition(position); - } - - /** - * {@inheritDoc} - */ - @Override - protected void doPaint(Graphics2D g, Object component, int width, int height) { - if (getPaint() != null) { - Ellipse2D ellipse = new Ellipse2D.Double(-width / 2.0, - height / 2.7, width * 2.0, - height * 2.0); - - Area gloss = new Area(ellipse); - if (getPosition() == GlossPosition.TOP) { - Area area = new Area(new Rectangle(0, 0, - width, height)); - area.subtract(new Area(ellipse)); - gloss = area; - } - /* - if(getClip() != null) { - gloss.intersect(new Area(getClip())); - }*/ - g.setPaint(getPaint()); - g.fill(gloss); - } - } - - /** - *

                    Returns the paint currently used by the painter to fill the gloss.

                    - * - * @return the current Paint instance used by the painter - */ - public Paint getPaint() { - return paint; - } - - /** - *

                    Changes the paint to be used to fill the gloss. When the specified - * paint is null, nothing is painted. A paint can be an instance of - * Color.

                    - * - * @param paint The Paint instance to be used to fill the gloss - */ - public void setPaint(Paint paint) { - Paint old = this.paint; - this.paint = paint; - setDirty(true); - firePropertyChange("paint", old, getPaint()); - } - - /** - *

                    Returns the position at which the gloss is painted.

                    - * - * @return the position of the gloss in the painted area - */ - public GlossPosition getPosition() { - return position; - } - - /** - *

                    Changes the position of the gloss in the painted area. Only the - * values defined in the GlossPosition enum are valid.

                    - * - * @param position The position at which the gloss is painted - */ - public void setPosition(GlossPosition position) { - GlossPosition old = this.position; - this.position = position; - setDirty(true); - firePropertyChange("position", old, getPosition()); - } -} diff --git a/src/main/java/org/jdesktop/swingx/painter/ImagePainter.java b/src/main/java/org/jdesktop/swingx/painter/ImagePainter.java deleted file mode 100644 index b21f8e3580..0000000000 --- a/src/main/java/org/jdesktop/swingx/painter/ImagePainter.java +++ /dev/null @@ -1,400 +0,0 @@ -/* - * $Id: ImagePainter.java 4261 2012-11-19 18:38:46Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.painter; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.painter.effects.AreaEffect; - -import java.awt.*; -import java.awt.geom.Area; -import java.awt.image.BufferedImage; -import java.util.logging.Logger; - -/** - *

                    A Painter instance that paints an image. Any Image is acceptable. This - * Painter also allows the developer to specify a "Style" -- CENTERED, TILED, - * SCALED, POSITIONED, and CSS_POSITIONED; with the following meanings:

                    - * - *
                      - *
                    • CENTERED: draws the image unscaled and positioned in the center of - * the component
                    • - *
                    • TILED: draws the image repeatedly across the component, filling the - * entire background.
                    • - *
                    • SCALED: draws the image stretched large enough (or small enough) to - * cover the entire component. The stretch may not preserve the aspect ratio of the - * original image.
                    • - *
                    • POSITIONED: draws the image at the location specified by the imageLocation - * property. This style of drawing will respect the imageScale property.
                    • - *
                    • CSS_POSITIONED: draws the image using CSS style background positioning. - *It will use the location specified by the imageLocation property. This property should - *contain a point with the x and y values between 0 and 1. 0,0 will put the image in the - *upper left hand corner, 1,1 in the lower right, and 0.5,0.5 in the center. All other values - *will be interpolated accordingly. For a more - * complete definition of the positioning algorithm see the - * CSS 2.1 spec. - *
                    • - *
                    - * - * @author Richard - */ -@JavaBean -@SuppressWarnings("nls") -public class ImagePainter extends AbstractAreaPainter { - public enum ScaleType { InsideFit, OutsideFit, Distort } - - /** - * Logger to use - */ - @SuppressWarnings("unused") - private static final Logger LOG = Logger.getLogger(ImagePainter.class.getName()); - - /** - * The image to draw - */ - private transient BufferedImage img; - - private boolean horizontalRepeat; - private boolean verticalRepeat; - - private boolean scaleToFit = false; - private ScaleType scaleType = ScaleType.InsideFit; - - private double imageScale = 1.0; - - /** - * Create a new ImagePainter. By default there is no image, and the alignment - * is centered. - */ - public ImagePainter() { - this((BufferedImage)null); - } - - /** - * Create a new ImagePainter with the specified image and the Style - * Style.CENTERED - * - * @param image the image to be painted - */ - public ImagePainter(BufferedImage image) { - this(image,HorizontalAlignment.CENTER, VerticalAlignment.CENTER); - } - - /** - * Create a new ImagePainter with the specified image and alignment. - * @param horizontal the horizontal alignment - * @param vertical the vertical alignment - * @param image the image to be painted - */ - public ImagePainter(BufferedImage image, HorizontalAlignment horizontal, VerticalAlignment vertical) { - super(); - setCacheable(true); - this.img = image; - this.setVerticalAlignment(vertical); - this.setHorizontalAlignment(horizontal); - this.setFillPaint(null); - this.setBorderPaint(null); - this.setDirty(false); - } - - /** - * Sets the image to paint with. - * @param image if null, clears the image. Otherwise, this will set the - * image to be painted. - */ - public void setImage(BufferedImage image) { - if (image != img) { - Image oldImage = img; - img = image; - setDirty(true); - firePropertyChange("image", oldImage, img); - } - } - - /** - * Gets the current image used for painting. - * @return the image used for painting - */ - public BufferedImage getImage() { - return img; - } - - /** - * {@inheritDoc} - */ - @Override - protected void doPaint(Graphics2D g, Object component, int width, int height) { - Shape shape = provideShape(g, component,width,height); - - switch (getStyle()) { - case BOTH: - drawBackground(g,shape,width,height); - drawBorder(g,shape,width,height); - break; - case FILLED: - drawBackground(g,shape,width,height); - break; - case OUTLINE: - drawBorder(g,shape,width,height); - break; - case NONE: - break; - default: - break; - } - } - - private void drawBackground(Graphics2D g, Shape shape, int width, int height) { - Paint p = getFillPaint(); - - if(p != null) { - if(isPaintStretched()) { - p = calculateSnappedPaint(p, width, height); - } - g.setPaint(p); - g.fill(shape); - } - - if(getAreaEffects() != null) { - for(AreaEffect ef : getAreaEffects()) { - ef.apply(g, shape, width, height); - } - } - - - if (img != null) { - int imgWidth = img.getWidth(null); - int imgHeight = img.getHeight(null); - if (imgWidth == -1 || imgHeight == -1) { - //image hasn't completed loading, do nothing - } else { - Rectangle rect = shape.getBounds(); - - if(verticalRepeat || horizontalRepeat) { - Shape oldClip = g.getClip(); - Shape clip = g.getClip(); - if(clip == null) { - clip = new Rectangle(0,0,width,height); - } - Area area = new Area(clip); - Insets insets = getInsets(); - area.intersect(new Area(new Rectangle(insets.left, insets.top, width - insets.left - insets.right, height - insets.top - insets.bottom))); - - if (verticalRepeat && horizontalRepeat) { - area.intersect(new Area(new Rectangle(0, 0, width, height))); - g.setClip(area); - } else if (verticalRepeat) { - area.intersect(new Area(new Rectangle(rect.x, 0, rect.width, height))); - g.setClip(area); - } else { - area.intersect(new Area(new Rectangle(0, rect.y, width, rect.height))); - g.setClip(area); - } - - TexturePaint tp = new TexturePaint(img, rect); - g.setPaint(tp); - g.fillRect(0,0,width,height); - g.setClip(oldClip); - } else { - if(scaleToFit) { - int sw = imgWidth; - int sh = imgHeight; - if(scaleType == ScaleType.InsideFit) { - if(sw > width) { - float scale = (float)width/(float)sw; - sw = (int)(sw * scale); - sh = (int)(sh * scale); - } - if(sh > height) { - float scale = (float)height/(float)sh; - sw = (int)(sw * scale); - sh = (int)(sh * scale); - } - } - if(scaleType == ScaleType.OutsideFit) { - if(sw > width) { - float scale = (float)width/(float)sw; - sw = (int)(sw * scale); - sh = (int)(sh * scale); - } - if(sh < height) { - float scale = (float)height/(float)sh; - sw = (int)(sw * scale); - sh = (int)(sh * scale); - } - } - if(scaleType == ScaleType.Distort) { - sw = width; - sh = height; - } - int x=0; - int y=0; - switch(getHorizontalAlignment()) { - case CENTER: - x=(width/2)-(sw/2); - break; - case RIGHT: - x=width-sw; - break; - case LEFT: - break; - default: - break; - } - switch(getVerticalAlignment()) { - case CENTER: - y=(height/2)-(sh/2); - break; - case BOTTOM: - y=height-sh; - break; - case TOP: - break; - default: - break; - } - g.drawImage(img, x, y, sw, sh, null); - } else { - int sw = rect.width; - int sh = rect.height; - if(imageScale != 1.0) { - sw = (int)(sw * imageScale); - sh = (int)(sh * imageScale); - } - g.drawImage(img, rect.x, rect.y, sw, sh, null); - } - } - } - } - - } - - private void drawBorder(Graphics2D g, Shape shape, int width, int height) { - if(getBorderPaint() != null) { - g.setPaint(getBorderPaint()); - g.setStroke(new BasicStroke(getBorderWidth())); - g.draw(shape); - } - } - - public boolean isScaleToFit() { - return scaleToFit; - } - - public void setScaleToFit(boolean scaleToFit) { - boolean old = isScaleToFit(); - this.scaleToFit = scaleToFit; - setDirty(true); - firePropertyChange("scaleToFit", old, isScaleToFit()); - } - - public ScaleType getScaleType() { - return scaleType; - } - - public void setScaleType(ScaleType scaleType) { - ScaleType old = getScaleType(); - this.scaleType = scaleType; - setDirty(true); - firePropertyChange("scaleType", old, getScaleType()); - } - - /** - * Gets the current scaling factor used when drawing an image. - * @return the current scaling factor - */ - public double getImageScale() { - return imageScale; - } - - /** - * Sets the scaling factor used when drawing the image - * @param imageScale the new image scaling factor - */ - public void setImageScale(double imageScale) { - double old = getImageScale(); - this.imageScale = imageScale; - setDirty(true); - firePropertyChange("imageScale",old,this.imageScale); - } - - /** - * Indicates if the image will be repeated horizontally. - * @return if the image will be repeated horizontally - */ - public boolean isHorizontalRepeat() { - return horizontalRepeat; - } - - /** - * Sets if the image should be repeated horizontally. - * @param horizontalRepeat the new horizontal repeat value - */ - public void setHorizontalRepeat(boolean horizontalRepeat) { - boolean old = this.isHorizontalRepeat(); - this.horizontalRepeat = horizontalRepeat; - setDirty(true); - firePropertyChange("horizontalRepeat",old,this.horizontalRepeat); - } - - /** - * Indicates if the image will be repeated vertically. - * @return if the image will be repeated vertically - */ - public boolean isVerticalRepeat() { - return verticalRepeat; - } - - /** - * Sets if the image should be repeated vertically. - * @param verticalRepeat new value for the vertical repeat - */ - public void setVerticalRepeat(boolean verticalRepeat) { - boolean old = this.isVerticalRepeat(); - this.verticalRepeat = verticalRepeat; - setDirty(true); - firePropertyChange("verticalRepeat",old,this.verticalRepeat); - } - - /** - * - */ - @Override - protected Shape provideShape(Graphics2D g, Object comp, int width, int height) { - if(getImage() != null) { - BufferedImage bi = getImage(); - int imgWidth = bi.getWidth(); - int imgHeight = bi.getHeight(); - - return calculateLayout(imgWidth, imgHeight, width, height); - } - return new Rectangle(0,0,0,0); - - } - - /** - * {@inheritDoc} - */ - @Override - public String toString() { - return getClass().getSimpleName() + "[img=" + img + "]"; - } -} diff --git a/src/main/java/org/jdesktop/swingx/painter/MattePainter.java b/src/main/java/org/jdesktop/swingx/painter/MattePainter.java deleted file mode 100644 index fe5027d437..0000000000 --- a/src/main/java/org/jdesktop/swingx/painter/MattePainter.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * $Id: MattePainter.java 4147 2012-02-01 17:13:24Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.painter; - -import org.jdesktop.beans.JavaBean; - -import java.awt.*; - -/** - * A Painter implementation that uses a Paint to fill the entire background - * area. For example, if I wanted to paint the entire background of a panel green, I would: - *
                    
                    - *  MattePainter p = new MattePainter(Color.GREEN);
                    - *  panel.setBackgroundPainter(p);
                    - * 

                    - * - *

                    Since it accepts a Paint, it is also possible to paint a texture or use other - * more exotic Paint implementations. To paint a BufferedImage texture as the - * background: - *

                    
                    - *  TexturePaint paint = new TexturePaint(bufferedImage,
                    - *      new Rectangle2D.Double(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight()));
                    - *  MattePainter p = new MattePainter(paint);
                    - *  panel.setBackgroundPainter(p);
                    - * 

                    - * - *

                    If no paint is specified, then nothing is painted

                    - * @author rbair - */ -@JavaBean -public class MattePainter extends AbstractAreaPainter { - - /** - * Creates a new MattePainter with "null" as the paint used - */ - public MattePainter() { - } - - /** - * Create a new MattePainter for the given Paint. This can be a GradientPaint - * (the gradient will not grow when the component becomes larger unless - * you use the paintStretched boolean property), - * TexturePaint, Color, or other Paint instance. - * - * @param paint Paint to fill with - */ - public MattePainter(Paint paint) { - super(paint); - } - - /** - * Create a new MattePainter for the given Paint. This can be a GradientPaint - * (the gradient will not grow when the component becomes larger unless - * you use the paintStretched boolean property), - * TexturePaint, Color, or other Paint instance. - * - * @param paint Paint to fill with - * @param paintStretched indicates if the paint should be stretched - */ - public MattePainter(Paint paint, boolean paintStretched) { - super(paint); - this.setPaintStretched(paintStretched); - } - - /** - * {@inheritDoc} - */ - @Override - protected void doPaint(Graphics2D g, Object component, int width, int height) { - Paint p = getFillPaint(); - - if (p != null) { - Insets insets = getInsets(); - int w = width - insets.left - insets.right; - int h = height - insets.top - insets.bottom; - - if (isPaintStretched()) { - p = calculateSnappedPaint(p, w, h); - } - - g.translate(insets.left, insets.top); - g.setPaint(p); - g.fill(provideShape(g, component, w, h)); - } - } - - /** - * {@inheritDoc} - */ - @Override - protected Shape provideShape(Graphics2D g, Object comp, int width, int height) { - return new Rectangle(0,0,width,height); - } - -} diff --git a/src/main/java/org/jdesktop/swingx/painter/Painter.java b/src/main/java/org/jdesktop/swingx/painter/Painter.java deleted file mode 100644 index 9bc8439c96..0000000000 --- a/src/main/java/org/jdesktop/swingx/painter/Painter.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * $Id: Painter.java 3860 2010-10-26 01:14:53Z kschaefe $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.painter; - -import java.awt.*; - -/** - *

                    A painting delegate. The Painter interface defines exactly one method, - * paint. It is used in situations where the developer can change - * the painting routine of a component without having to resort to subclassing - * the component.

                    - * - *

                    Painters are simply encapsulations of Java2D code and make - * it fairly trivial to reuse existing Painters or to combine - * them together. Implementations of this interface are also trivial to write, - * such that if you can't find a Painter that does what you need, - * you can write one with minimal effort. Writing a Painter requires - * knowledge of Java2D.

                    - * - *

                    A Painter may be created with a type parameter. This type will be - * expected in the paint method. For example, you may wish to write a - * Painter that only works with subclasses of {@link java.awt.Component}. - * In that case, when the Painter is declared, you may declare that - * it requires a Component, allowing the paint method to be type safe. Ex: - *

                    
                    - *     Painter<Component> p = new Painter<Component>() {
                    - *         public void paint(Graphics2D g, Component c, int width, int height) {
                    - *             g.setColor(c.getBackground());
                    - *             //and so forth
                    - *         }
                    - *     }
                    - * 

                    - * - *

                    This class is not threadsafe.

                    - * - * @author rbair - * @see AbstractPainter - * @see CompoundPainter - */ -public interface Painter { - /** - *

                    Renders to the given {@link Graphics2D} object. Implementations - * of this method may modify state on the Graphics2D, and are not - * required to restore that state upon completion. In most cases, it is recommended - * that the caller pass in a scratch graphics object. The Graphics2D - * must never be null.

                    - * - *

                    State on the graphics object may be honored by the paint method, - * but may not be. For instance, setting the antialiasing rendering hint on the - * graphics may or may not be respected by the Painter implementation.

                    - * - *

                    The supplied object parameter acts as an optional configuration argument. - * For example, it could be of type Component. A Painter - * that expected it could then read state from that Component and - * use the state for painting. For example, an implementation may read the - * backgroundColor and use that.

                    - * - *

                    Generally, to enhance reusability, most standard Painters ignore - * this parameter. They can thus be reused in any context. The object - * may be null. Implementations must not throw a NullPointerException if the object - * parameter is null.

                    - * - *

                    Finally, the width and height arguments specify the - * width and height that the Painter should paint into. More - * specifically, the specified width and height instruct the painter that it should - * paint fully within this width and height. Any specified clip on the - * g param will further constrain the region.

                    - * - *

                    For example, suppose I have a Painter implementation that draws - * a gradient. The gradient goes from white to black. It "stretches" to fill the - * painted region. Thus, if I use this Painter to paint a 500 x 500 - * region, the far left would be black, the far right would be white, and a smooth - * gradient would be painted between. I could then, without modification, reuse the - * Painter to paint a region that is 20x20 in size. This region would - * also be black on the left, white on the right, and a smooth gradient painted - * between.

                    - * - * @param g The Graphics2D to render to. This must not be null. - * @param object an optional configuration parameter. This may be null. - * @param width width of the area to paint. - * @param height height of the area to paint. - */ - public void paint(Graphics2D g, T object, int width, int height); -} diff --git a/src/main/java/org/jdesktop/swingx/painter/PainterPaint.java b/src/main/java/org/jdesktop/swingx/painter/PainterPaint.java deleted file mode 100644 index 9d848240c7..0000000000 --- a/src/main/java/org/jdesktop/swingx/painter/PainterPaint.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * $Id: PainterPaint.java 4082 2011-11-15 18:39:43Z kschaefe $ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.painter; - -import org.jdesktop.swingx.util.GraphicsUtilities; - -import java.awt.*; -import java.awt.geom.AffineTransform; -import java.awt.geom.Rectangle2D; -import java.awt.image.BufferedImage; -import java.awt.image.ColorModel; -import java.awt.image.Raster; - -/** - * - * @author Karl George Schaefer - */ -public class PainterPaint implements Paint { - protected static class PainterPaintContext implements PaintContext { - private Painter painter; - private T object; - private BufferedImage saved; - - public PainterPaintContext(Painter painter, T object) { - painter.getClass(); // null check - this.painter = painter; - this.object = object; - } - - /** - * {@inheritDoc} - */ - @Override - public void dispose() { - //does nothing - } - - /** - * {@inheritDoc} - */ - @Override - public ColorModel getColorModel() { - if (saved == null) { - return GraphicsUtilities.createCompatibleImage(1, 1).getColorModel(); - } - - return saved.getColorModel(); - } - - /** - * {@inheritDoc} - */ - @Override - public Raster getRaster(int x, int y, int w, int h) { - if (saved == null || saved.getWidth() != w || saved.getHeight() != h) { - saved = GraphicsUtilities.createCompatibleImage(w, h); - Graphics2D g2d = saved.createGraphics(); - - try { - if (painter instanceof AbstractPainter) { - ((AbstractPainter) painter).setInPaintContext(true); - } - painter.paint(g2d, object, w, h); - } finally { - g2d.dispose(); - if (painter instanceof AbstractPainter) { - ((AbstractPainter) painter).setInPaintContext(false); - } - } - } - - return saved.getData(); - } - } - - private final PainterPaintContext context; - - public PainterPaint(Painter painter, T object) { - context = new PainterPaintContext(painter, object); - } - - /** - * {@inheritDoc} - */ - @Override - public PaintContext createContext(ColorModel cm, Rectangle deviceBounds, - Rectangle2D userBounds, AffineTransform xform, RenderingHints hints) { - return context; - } - - /** - * {@inheritDoc} - */ - @Override - public int getTransparency() { - return Transparency.BITMASK; - } -} diff --git a/src/main/java/org/jdesktop/swingx/painter/PainterUtils.java b/src/main/java/org/jdesktop/swingx/painter/PainterUtils.java deleted file mode 100644 index 2d417a3e8a..0000000000 --- a/src/main/java/org/jdesktop/swingx/painter/PainterUtils.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.jdesktop.swingx.painter; - -import java.awt.*; - -final class PainterUtils { - private PainterUtils() { - //prevent instantiation - } - - static Paint getForegroundPaint(Paint current, Object o) { - if (current == null) { - if (o instanceof Component) { - return ((Component) o).getForeground(); - } - } - - return current; - } - - static Paint getBackgroundPaint(Paint current, Object o) { - if (current == null) { - if (o instanceof Component) { - return ((Component) o).getBackground(); - } - } - - return current; - } - - static Font getComponentFont(Font current, Object o) { - if (current == null) { - if (o instanceof Component) { - return ((Component) o).getFont(); - } - } - - return current; - } -} diff --git a/src/main/java/org/jdesktop/swingx/painter/Painters.java b/src/main/java/org/jdesktop/swingx/painter/Painters.java deleted file mode 100644 index 6f50ca006c..0000000000 --- a/src/main/java/org/jdesktop/swingx/painter/Painters.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * $Id: Painters.java 3590 2010-01-12 19:19:45Z kschaefe $ - * - * Copyright 2010 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.painter; - -import java.awt.*; - -/** - * A collection of static painters. These painters do not store state and are safe to reuse. - * @author kschaefer - */ -public final class Painters { - public static final Painter EMPTY_PAINTER = new Painter() { - @Override - public void paint(Graphics2D g, Object object, int width, int height) { - //does nothing - } - }; - - private Painters() { - //prevent instantiation - } -} diff --git a/src/main/java/org/jdesktop/swingx/painter/PinstripePainter.java b/src/main/java/org/jdesktop/swingx/painter/PinstripePainter.java deleted file mode 100644 index d8b8b15dae..0000000000 --- a/src/main/java/org/jdesktop/swingx/painter/PinstripePainter.java +++ /dev/null @@ -1,277 +0,0 @@ -/* - * $Id: PinstripePainter.java 4259 2012-11-14 20:07:00Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.painter; - -import org.jdesktop.beans.JavaBean; - -import java.awt.*; -import java.awt.geom.Area; -import java.awt.geom.Line2D; - -import static java.lang.Math.*; -import static org.jdesktop.swingx.painter.PainterUtils.getForegroundPaint; - -/** - *

                    - * A fun Painter that paints pinstripes. You can specify the Paint to paint those pinstripes in - * (could even be a texture paint!), the angle at which to paint the pinstripes, and the spacing - * between stripes. - *

                    - * - *

                    - * The default PinstripePainter configuration will paint the pinstripes using the foreground color - * of the component (the default behavior if a Paint is not specified) at a 45 degree angle with 8 - * pixels between stripes - *

                    - * - *

                    - * Here is a custom code snippet that paints Color.GRAY pinstripes at a 135 degree angle: - * - *

                    - * 
                    - *  PinstripePainter p = new PinstripePainter();
                    - *  p.setAngle(135);
                    - *  p.setPaint(Color.GRAY);
                    - * 
                    - * 
                    - * - * @author rbair - */ -@JavaBean -public class PinstripePainter extends AbstractPainter { - /** - * The angle in degrees to paint the pinstripes at. The default value is 45. The value will be - * between 0 and 360 inclusive. The setAngle method will ensure this. - */ - private double angle; - - /** - * The spacing between pinstripes - */ - private double spacing; - - /** - * The stroke width of the pinstripes - */ - private double stripeWidth; - - /** - * The Paint to use when drawing the pinstripes - */ - private Paint paint; - - /** - * Create a new PinstripePainter. By default the angle with be 45 degrees, the spacing will be 8 - * pixels, and the color will be the Component foreground color. - */ - public PinstripePainter() { - this(null); - } - - /** - * Create a new PinstripePainter using an angle of 45, 8 pixel spacing, and the given Paint. - * - * @param paint - * the paint used when drawing the stripes - */ - public PinstripePainter(Paint paint) { - this(paint, 45); - } - - /** - * Create a new PinstripePainter using the given angle, 8 pixel spacing, and the foreground - * color of the Component - * - * @param angle - * the angle, in degrees, in which to paint the pinstripes - */ - public PinstripePainter(double angle) { - this(null, angle); - } - - /** - * Create a new PinstripePainter using the given angle, 8 pixel spacing, and the given Paint - * - * @param paint - * the paint used when drawing the stripes - * @param angle - * the angle, in degrees, in which to paint the pinstripes - */ - public PinstripePainter(Paint paint, double angle) { - this(paint, angle, 1, 8); - } - - /** - * Create a new PinstripePainter with the specified paint, angle, stripe width, and stripe - * spacing. - * - * @param paint - * @param angle - * @param stripeWidth - * @param spacing - */ - public PinstripePainter(Paint paint, double angle, double stripeWidth, double spacing) { - this.paint = paint; - this.angle = angle; - this.stripeWidth = stripeWidth; - this.spacing = spacing; - } - - /** - * Get the current paint used for drawing the pinstripes - * - * @return the Paint to use to draw the pinstripes - */ - public Paint getPaint() { - return paint; - } - - /** - * Set the paint to use for drawing the pinstripes - * - * @param p - * the Paint to use. May be a Color. - */ - public void setPaint(Paint p) { - Paint old = getPaint(); - this.paint = p; - firePropertyChange("paint", old, getPaint()); - } - - /** - * Gets the current angle of the pinstripes - * - * @return the angle, in degrees, at which the pinstripes are painted - */ - public double getAngle() { - return angle; - } - - /** - * Sets the angle, in degrees, at which to paint the pinstripes. If the given angle is < 0 or > - * 360, it will be appropriately constrained. For example, if a value of 365 is given, it will - * result in 5 degrees. The conversion is not perfect, but "a man on a galloping horse won't be - * able to tell the difference". - * - * @param angle - * the Angle in degrees at which to paint the pinstripes - */ - public void setAngle(double angle) { - if (angle > 360) { - angle = angle % 360; - } - - if (angle < 0) { - angle = 360 - ((angle * -1) % 360); - } - - double old = getAngle(); - this.angle = angle; - firePropertyChange("angle", old, getAngle()); - } - - /** - * Gets the current width of the pinstripes - * - * @return the current pinstripe width - */ - public double getStripeWidth() { - return stripeWidth; - } - - /** - * Set the width of the pinstripes - * - * @param stripeWidth - * a new width for the pinstripes - */ - public void setStripeWidth(double stripeWidth) { - double oldSripeWidth = getStripeWidth(); - this.stripeWidth = stripeWidth; - firePropertyChange("stripeWidth", oldSripeWidth, getStripeWidth()); - } - - /** - * Get the current spacing between the stripes - * - * @return the spacing between pinstripes - */ - public double getSpacing() { - return spacing; - } - - /** - * Sets the spacing between pinstripes - * - * @param spacing - * spacing between pinstripes - */ - public void setSpacing(double spacing) { - double old = getSpacing(); - this.spacing = spacing; - firePropertyChange("spacing", old, getSpacing()); - } - - /** - * {@inheritDoc} - */ - @Override - protected void doPaint(Graphics2D g, Object component, int width, int height) { - // draws pinstripes at the angle specified in this class and at the given distance apart - Shape oldClip = g.getClip(); - Shape newClip; - - if (oldClip == null) { - newClip = new Rectangle(width, height); - } else { - Rectangle r = oldClip.getBounds(); - r.width = width; - r.height = height; - - Area a = new Area(r); - a.intersect(new Area(oldClip)); - - newClip = a; - } - - int startX = newClip.getBounds().x; - int startY = newClip.getBounds().y; - - g.setClip(newClip); - g.setPaint(getForegroundPaint(getPaint(), component)); - g.setStroke(new BasicStroke((float) getStripeWidth())); - g.rotate(toRadians(getAngle())); - - double hypLength = hypot(width, height); - double gap = getSpacing() + getStripeWidth(); - - int numLines = (int) round(hypLength / gap); - - for (int i = 0; i < numLines; i++) { - double x = i * gap; - - g.draw(new Line2D.Double(startX + x, startY - hypLength, startX + x, startY + hypLength)); - } - - g.setClip(oldClip); - } -} diff --git a/src/main/java/org/jdesktop/swingx/painter/RectanglePainter.java b/src/main/java/org/jdesktop/swingx/painter/RectanglePainter.java deleted file mode 100644 index 0f1321e220..0000000000 --- a/src/main/java/org/jdesktop/swingx/painter/RectanglePainter.java +++ /dev/null @@ -1,265 +0,0 @@ -/* - * $Id: RectanglePainter.java 4147 2012-02-01 17:13:24Z kschaefe $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.painter; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.painter.effects.AreaEffect; -import org.jdesktop.swingx.util.ShapeUtils; - -import java.awt.*; -import java.awt.geom.Rectangle2D; -import java.awt.geom.RectangularShape; -import java.awt.geom.RoundRectangle2D; - -/** - * A painter which paints square and rounded rectangles - * - * @author joshua.marinacci@sun.com - */ -@JavaBean -@SuppressWarnings("nls") -public class RectanglePainter extends AbstractAreaPainter { - private boolean rounded = false; - //private Insets insets = new Insets(0,0,0,0); - private int roundWidth = 20; - private int roundHeight = 20; - private int width = -1; - private int height = -1; - //private double strokeWidth = 1; - - /** Creates a new instance of RectanglePainter */ - public RectanglePainter() { - this(0, 0, 0, 0); - } - - public RectanglePainter(int top, int left, int bottom, int right) { - this(top, left, bottom, right, 0, 0); - } - - public RectanglePainter(int top, int left, int bottom, int right, - int roundWidth, int roundHeight) { - this(top, left, bottom, right, roundWidth, roundHeight, roundWidth != 0 || roundHeight != 0, Color.RED, 1f, Color.BLACK); - } - - public RectanglePainter(int top, int left, int bottom, int right, int roundWidth, - int roundHeight, boolean rounded, Paint fillPaint, float strokeWidth, Paint borderPaint) { - this(new Insets(top, left, bottom, right), -1, -1, roundWidth, roundHeight, rounded, fillPaint, strokeWidth, borderPaint); - } - - public RectanglePainter(Color fillPaint, Color borderPaint) { - this(fillPaint, borderPaint, 1f, null); - } - - public RectanglePainter(Paint fillPaint, Paint borderPaint, float borderWidth, Style style) { - this(new Insets(0, 0, 0, 0), -1, -1, 0, 0, false, fillPaint, borderWidth, borderPaint); - setStyle(style); - setDirty(false); - } - - public RectanglePainter(int width, int height, int cornerRadius, Paint fillPaint) { - this(new Insets(0, 0, 0, 0), width, height, cornerRadius, cornerRadius, true, fillPaint, 1f, Color.BLACK); - } - - public RectanglePainter(Insets insets, int width, int height, int roundWidth, int roundHeight, - boolean rounded, Paint fillPaint, float strokeWidth, Paint borderPaint) { - this.width = width; - this.height = height; - setFillHorizontal(width < 0); - setFillVertical(height < 0); - setInsets(insets); - this.roundWidth = roundWidth; - this.roundHeight = roundHeight; - this.rounded = rounded; - this.setFillPaint(fillPaint); - this.setBorderWidth(strokeWidth); - this.setBorderPaint(borderPaint); - this.setDirty(false); - } - - /** - * Indicates if the rectangle is rounded - * @return if the rectangle is rounded - */ - public boolean isRounded() { - return rounded; - } - - /** - * sets if the rectangle should be rounded - * @param rounded if the rectangle should be rounded - */ - public void setRounded(boolean rounded) { - boolean oldRounded = isRounded(); - this.rounded = rounded; - setDirty(true); - firePropertyChange("rounded",oldRounded,rounded); - } - - /** - * gets the round width of the rectangle - * @return the current round width - */ - public int getRoundWidth() { - return roundWidth; - } - - /** - * sets the round width of the rectangle - * @param roundWidth a new round width - */ - public void setRoundWidth(int roundWidth) { - int oldRoundWidth = getRoundWidth(); - this.roundWidth = roundWidth; - setDirty(true); - firePropertyChange("roundWidth",oldRoundWidth,roundWidth); - } - - /** - * gets the round height of the rectangle - * @return the current round height - */ - public int getRoundHeight() { - return roundHeight; - } - - /** - * sets the round height of the rectangle - * @param roundHeight a new round height - */ - public void setRoundHeight(int roundHeight) { - int oldRoundHeight = getRoundHeight(); - this.roundHeight = roundHeight; - setDirty(true); - firePropertyChange("roundHeight",oldRoundHeight,roundHeight); - } - - - /* ======== drawing code ============ */ - protected RectangularShape calculateShape(int width, int height) { - Insets insets = getInsets(); - int x = insets.left; - int y = insets.top; - - // use the position calcs from the super class - Rectangle bounds = calculateLayout(this.width, this.height, width, height); - if(this.width != -1 && !isFillHorizontal()) { - width = this.width; - x = bounds.x; - } - if(this.height != -1 && !isFillVertical()) { - height = this.height; - y = bounds.y; - } - - if(isFillHorizontal()) { - width = width - insets.left - insets.right; - } - if(isFillVertical()) { - height = height - insets.top - insets.bottom; - } - - - RectangularShape shape = new Rectangle2D.Double(x, y, width, height); - if(rounded) { - shape = new RoundRectangle2D.Double(x, y, width, height, roundWidth, roundHeight); - } - return shape; - } - - - - @Override - protected void doPaint(Graphics2D g, Object component, int width, int height) { - Shape shape = provideShape(g, component, width, height); - switch (getStyle()) { - case BOTH: - drawBackground(g,shape,width,height); - drawBorder(g,shape,width,height); - break; - case FILLED: - drawBackground(g,shape,width,height); - break; - case OUTLINE: - drawBorder(g,shape,width,height); - break; - } - - // background - // border - // leave the clip to support masking other painters - ShapeUtils.mergeClip(g,shape); - /* - Area area = new Area(g.getClip()); - area.intersect(new Area(shape));//new Rectangle(0,0,width,height))); - g.setClip(area);*/ - //g.setClip(shape); - } - - private void drawBorder(Graphics2D g, Shape shape, int width, int height) { - Paint p = getBorderPaint(); - if(isPaintStretched()) { - p = calculateSnappedPaint(p, width, height); - } - - g.setPaint(p); - - g.setStroke(new BasicStroke(getBorderWidth())); - - // shrink the border by 1 px - if(shape instanceof Rectangle2D) { - Rectangle2D rect = (Rectangle2D) shape; - - g.draw(new Rectangle2D.Double(rect.getX(), rect.getY(), - rect.getWidth()-1, rect.getHeight()-1)); - } else if(shape instanceof RoundRectangle2D) { - RoundRectangle2D rect = (RoundRectangle2D) shape; - - g.draw(new RoundRectangle2D.Double(rect.getX(), rect.getY(), - rect.getWidth()-1, rect.getHeight()-1, - rect.getArcWidth(), rect.getArcHeight())); - } else { - g.draw(shape); - } - } - - private void drawBackground(Graphics2D g, Shape shape, int width, int height) { - Paint p = getFillPaint(); - if(isPaintStretched()) { - p = calculateSnappedPaint(p, width, height); - } - - g.setPaint(p); - - g.fill(shape); - if(getAreaEffects() != null) { - for(AreaEffect ef : getAreaEffects()) { - ef.apply(g, shape, width, height); - } - } - } - - @Override - protected Shape provideShape(Graphics2D g, Object comp, int width, int height) { - return calculateShape(width,height); - } -} - diff --git a/src/main/java/org/jdesktop/swingx/painter/ShapePainter.java b/src/main/java/org/jdesktop/swingx/painter/ShapePainter.java deleted file mode 100644 index a4c18ed9d4..0000000000 --- a/src/main/java/org/jdesktop/swingx/painter/ShapePainter.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * $Id: ShapePainter.java 4147 2012-02-01 17:13:24Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.painter; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.painter.effects.AreaEffect; - -import java.awt.*; -import java.awt.geom.Ellipse2D; - -import static org.jdesktop.swingx.painter.PainterUtils.getBackgroundPaint; -import static org.jdesktop.swingx.painter.PainterUtils.getForegroundPaint; - - -/** - *

                    A Painter that paints java.awt.Shapes. It uses a stroke and a fillPaint to do so. The - * shape is painted as is, at a specific location. If no Shape is specified, nothing - * will be painted. If no stroke is specified, the default for the Graphics2D - * will be used. If no fillPaint is specified, the component background color - * will be used. The shape can be positioned using the insets, horizontal, and - * vertical properties.

                    - * - *

                    Here is an example that draws a rectangle aligned on the center right: - *

                    
                    - *  Rectangle2D.Double rect = new Rectangle2D.Double(0, 0, 50, 50);
                    - *  ShapePainter p = new ShapePainter(rect);
                    - * p.setHorizontal(HorizontalAlignment.RIGHT);
                    - * p.setVertical(VerticalAlignment.CENTER);
                    - * 
                    - * @author rbair - */ -@JavaBean -@SuppressWarnings("nls") -public class ShapePainter extends AbstractAreaPainter { - /** - * The Shape to fillPaint. If null, nothing is painted. - */ - private Shape shape; - - /** - * Create a new ShapePainter - */ - public ShapePainter() { - super(); - this.shape = new Ellipse2D.Double(0,0,100,100); - this.setBorderWidth(3); - this.setFillPaint(Color.RED); - this.setBorderPaint(Color.BLACK); - } - - /** - * Create a new ShapePainter with the specified shape. - * - * - * @param shape the shape to fillPaint - */ - public ShapePainter(Shape shape) { - super(); - this.shape = shape; - } - - /** - * Create a new ShapePainter with the specified shape and fillPaint. - * - * - * @param shape the shape to fillPaint - * @param paint the fillPaint to be used to fillPaint the shape - */ - public ShapePainter(Shape shape, Paint paint) { - super(); - this.shape = shape; - this.setFillPaint(paint); - } - - /** - * Create a new ShapePainter with the specified shape and fillPaint. The shape - * can be filled or stroked (only the outline is painted). - * - * - * @param shape the shape to fillPaint - * @param paint the fillPaint to be used to fillPaint the shape - * @param style specifies the ShapePainter.Style to use for painting this shape. - * If null, then Style.BOTH is used - */ - public ShapePainter(Shape shape, Paint paint, Style style) { - super(); - this.shape = shape; - this.setFillPaint(paint); - this.setStyle(style == null ? Style.BOTH : style); - } - - /** - * Sets the shape to fillPaint. This shape is not resized when the component - * bounds are. To do that, create a custom shape that is bound to the - * component width/height - * - * - * @param s the Shape to fillPaint. May be null - */ - public void setShape(Shape s) { - Shape old = getShape(); - this.shape = s; - setDirty(true); - firePropertyChange("shape", old, getShape()); - } - - /** - * Gets the current shape - * @return the Shape to fillPaint. May be null - */ - public Shape getShape() { - return shape; - } - - /** - * {@inheritDoc} - */ - @Override - protected void doPaint(Graphics2D g, Object component, int w, int h) { - g.setStroke(new BasicStroke(this.getBorderWidth())); - - if(getShape() != null) { - Shape s = provideShape(g,component, w, h); - Rectangle bounds = s.getBounds(); - Rectangle rect = calculateLayout(bounds.width, bounds.height, w, h); - //u.p("rect = " + rect); - g = (Graphics2D)g.create(); - - try { - g.translate(rect.x, rect.y); - //draw/fill the shape - drawPathEffects(g, s, rect.width, rect.height); - switch (getStyle()) { - case BOTH: - drawShape(g, s, component, rect.width, rect.height); - fillShape(g, s, component, rect.width, rect.height); - break; - case FILLED: - fillShape(g, s, component, rect.width, rect.height); - break; - case OUTLINE: - drawShape(g, s, component, rect.width, rect.height); - break; - } - } finally { - g.dispose(); - } - } - } - - private void drawShape(Graphics2D g, Shape s, Object component, int w, int h) { - g.setPaint(calculateStrokePaint(component, w, h)); - g.draw(s); - } - - private void fillShape(Graphics2D g, Shape s, Object component, int w, int h) { - g.setPaint(calculateFillPaint(component, w, h)); - g.fill(s); - } - - // shape effect stuff - @Override - protected Shape provideShape(Graphics2D g, Object comp, int width, int height) { - return getShape(); - } - - private Paint calculateStrokePaint(Object component, int width, int height) { - Paint p = getForegroundPaint(getBorderPaint(), component); - if(isPaintStretched()) { - p = calculateSnappedPaint(p, width, height); - } - return p; - } - - private Paint calculateFillPaint(Object component, int width, int height) { - //set the fillPaint - Paint p = getBackgroundPaint(getFillPaint(), component); - if(isPaintStretched()) { - p = calculateSnappedPaint(p, width, height); - } - return p; - } - - private void drawPathEffects(Graphics2D g, Shape s, int w, int h) { - if(getAreaEffects() != null) { - //Paint pt = calculateFillPaint(component, w, h); - for(AreaEffect ef : getAreaEffects()) { - ef.apply(g, s, w, h); - } - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/painter/TextPainter.java b/src/main/java/org/jdesktop/swingx/painter/TextPainter.java deleted file mode 100644 index 02e8a056ef..0000000000 --- a/src/main/java/org/jdesktop/swingx/painter/TextPainter.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * $Id: TextPainter.java 4147 2012-02-01 17:13:24Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.painter; - -import org.jdesktop.beans.JavaBean; -import org.jdesktop.swingx.painter.effects.AreaEffect; - -import javax.swing.*; -import javax.swing.text.JTextComponent; -import java.awt.*; -import java.awt.font.GlyphVector; - -import static org.jdesktop.swingx.painter.PainterUtils.getComponentFont; -import static org.jdesktop.swingx.painter.PainterUtils.getForegroundPaint; - -/** - * A painter which draws text. If the font, text, and paint are not provided they will be - * obtained from the object being painted if it is a Swing text component. - * - * @author rbair - */ -@JavaBean -@SuppressWarnings("nls") -public class TextPainter extends AbstractAreaPainter { - private String text = ""; - private Font font = null; - - /** Creates a new instance of TextPainter */ - public TextPainter() { - this(""); - } - - /** - * Create a new TextPainter which will paint the specified text - * @param text the text to paint - */ - public TextPainter(String text) { - this(text, null, null); - } - - /** - * Create a new TextPainter which will paint the specified text with the specified font. - * @param text the text to paint - * @param font the font to paint the text with - */ - public TextPainter(String text, Font font) { - this(text, font, null); - } - - /** - * Create a new TextPainter which will paint the specified text with the specified paint. - * @param text the text to paint - * @param paint the paint to paint with - */ - public TextPainter(String text, Paint paint) { - this(text, null, paint); - } - - /** - * Create a new TextPainter which will paint the specified text with the specified font and paint. - * @param text the text to paint - * @param font the font to paint the text with - * @param paint the paint to paint with - */ - public TextPainter(String text, Font font, Paint paint) { - this.text = text; - this.font = font; - setFillPaint(paint); - } - - /** - * Set the font (and font size and style) to be used when drawing the text - * @param f the new font - */ - public void setFont(Font f) { - Font old = getFont(); - this.font = f; - setDirty(true); - firePropertyChange("font", old, getFont()); - } - - /** - * gets the font (and font size and style) to be used when drawing the text - * @return the current font - */ - public Font getFont() { - return font; - } - - /** - * Sets the text to draw - * @param text the text to draw - */ - public void setText(String text) { - String old = getText(); - this.text = text == null ? "" : text; - setDirty(true); - firePropertyChange("text", old, getText()); - } - - /** - * gets the text currently used to draw - * @return the text to be drawn - */ - public String getText() { - return text; - } - - /** - * {@inheritDoc} - */ - @Override - protected void doPaint(Graphics2D g, Object component, int width, int height) { - Font f = calculateFont(component); - if (f != null) { - g.setFont(f); - } - - Paint paint = getForegroundPaint(getFillPaint(), component); - String t = calculateText(component); - - // get the font metrics - FontMetrics metrics = g.getFontMetrics(g.getFont()); - //Rectangle2D rect = metrics.getStringBounds(text,g); - - int tw = metrics.stringWidth(t); - int th = metrics.getHeight(); - Rectangle res = calculateLayout(tw, th, width, height); - - g.translate(res.x, res.y); - - if(isPaintStretched()) { - paint = calculateSnappedPaint(paint, res.width, res.height); - } - - if (paint != null) { - g.setPaint(paint); - } - - g.drawString(t, 0, 0 + metrics.getAscent()); - if(getAreaEffects() != null) { - Shape shape = provideShape(g, component, width, height); - for(AreaEffect ef : getAreaEffects()) { - ef.apply(g, shape, width, height); - } - } - g.translate(-res.x,-res.y); - } - - private String calculateText(final Object component) { - // prep the text - String t = getText(); - //make components take priority if(text == null || text.trim().equals("")) { - if(t != null && !t.trim().equals("")) { - return t; - } - if(component instanceof JTextComponent) { - t = ((JTextComponent)component).getText(); - } - if(component instanceof JLabel) { - t = ((JLabel)component).getText(); - } - if(component instanceof AbstractButton) { - t = ((AbstractButton)component).getText(); - } - return t; - } - - private Font calculateFont(final Object component) { - // prep the various text attributes - Font f = getComponentFont(getFont(), component); - if (f == null) { - f = new Font("Dialog", Font.PLAIN, 18); - } - return f; - } - - /** - * {@inheritDoc} - */ - @Override - protected Shape provideShape(Graphics2D g2, Object comp, int width, int height) { - Font f = calculateFont(comp); - String t = calculateText(comp); - FontMetrics metrics = g2.getFontMetrics(f); - GlyphVector vect = f.createGlyphVector(g2.getFontRenderContext(),t); - return vect.getOutline(0f,0f+ metrics.getAscent()); - } -} diff --git a/src/main/java/org/jdesktop/swingx/painter/effects/AbstractAreaEffect.java b/src/main/java/org/jdesktop/swingx/painter/effects/AbstractAreaEffect.java deleted file mode 100644 index 75d1d85f33..0000000000 --- a/src/main/java/org/jdesktop/swingx/painter/effects/AbstractAreaEffect.java +++ /dev/null @@ -1,399 +0,0 @@ -/* - * $Id: AbstractAreaEffect.java 4082 2011-11-15 18:39:43Z kschaefe $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - - -package org.jdesktop.swingx.painter.effects; - -import java.awt.*; -import java.awt.geom.Area; -import java.awt.geom.Point2D; -import java.awt.image.BufferedImage; - -import static org.jdesktop.swingx.util.GraphicsUtilities.createCompatibleTranslucentImage; - -/** - * The abstract base class for path effects. It takes care - * of soft clipping and interpolating brush sizes and colors. Subclasses - * can change these values to provide prefab effect behavior, like - * dropshadows and glows. - * @author joshy - */ -@SuppressWarnings("nls") -public class AbstractAreaEffect implements AreaEffect { - private static final boolean debug = false; - /** - * Creates a new instance of AreaEffect - */ - public AbstractAreaEffect() { - setBrushColor(Color.BLACK); - setBrushSteps(10); - setEffectWidth(8); - setRenderInsideShape(false); - setOffset(new Point(4,4)); - setShouldFillShape(true); - setShapeMasked(true); - } - - @Override - public void apply(Graphics2D g, Shape clipShape, int width, int height) { - // create a rect to hold the bounds - width = (int)(clipShape.getBounds2D().getWidth() + clipShape.getBounds2D().getX()); - height = (int)(clipShape.getBounds2D().getHeight() + clipShape.getBounds2D().getY()); - Rectangle effectBounds = new Rectangle(0,0, - width + getEffectWidth()*2 + 1, - height + getEffectWidth()*2 + 1); - - // Apply the border glow effect - if (isShapeMasked()) { - BufferedImage clipImage = getClipImage(effectBounds); - Graphics2D g2 = clipImage.createGraphics(); - - try { - // clear the buffer - g2.setPaint(Color.BLACK); - g2.setComposite(AlphaComposite.Clear); - g2.fillRect(0, 0, effectBounds.width, effectBounds.height); - - if (debug) { - g2.setPaint(Color.WHITE); - g2.setComposite(AlphaComposite.SrcOver); - g2.drawRect(0, 0, effectBounds.width - 1, - effectBounds.height - 1); - } - - // turn on smoothing - g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_ON); - g2.translate(getEffectWidth() - getOffset().getX(), - getEffectWidth() - getOffset().getY()); - paintBorderGlow(g2, clipShape, width, height); - - // clip out the parts we don't want - g2.setComposite(AlphaComposite.Clear); - g2.setColor(Color.WHITE); - if (isRenderInsideShape()) { - // clip the outside - Area area = new Area(effectBounds); - area.subtract(new Area(clipShape)); - g2.fill(area); - } else { - // clip the inside - g2.fill(clipShape); - } - } finally { - // draw the final image - g2.dispose(); - } - - g.drawImage(clipImage, -getEffectWidth() + (int) getOffset().getX(), -getEffectWidth() + (int) getOffset().getY(), null); - } else { - g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - paintBorderGlow(g, clipShape, width, height); - } - - //g.setColor(Color.MAGENTA); - //g.draw(clipShape.getBounds2D()); - //g.drawRect(0,0,width,height); - - } - - BufferedImage _clipImage = null; - private BufferedImage getClipImage(final Rectangle effectBounds) { - // set up a temp buffer - if(_clipImage == null || - _clipImage.getWidth() != effectBounds.width || - _clipImage.getHeight() != effectBounds.height) { - _clipImage = createCompatibleTranslucentImage(effectBounds.width, effectBounds.height); - } - _clipImage.getGraphics().clearRect(0,0,_clipImage.getWidth(), _clipImage.getHeight()); - return _clipImage; - } - - - /* - private BufferedImage createClipImage(Shape s, Graphics2D g, int width, int height) { - // Create a translucent intermediate image in which we can perform - // the soft clipping - - GraphicsConfiguration gc = g.getDeviceConfiguration(); - BufferedImage img = gc.createCompatibleImage(width, height, Transparency.TRANSLUCENT); - Graphics2D g2 = img.createGraphics(); - - // Clear the image so all pixels have zero alpha - g2.setComposite(AlphaComposite.Clear); - g2.fillRect(0, 0, width, height); - - // Render our clip shape into the image. Note that we enable - // antialiasing to achieve the soft clipping effect. Try - // commenting out the line that enables antialiasing, and - // you will see that you end up with the usual hard clipping. - g2.setComposite(AlphaComposite.Src); - g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - g2.setColor(Color.WHITE); - g2.fill(s); - g2.dispose(); - - return img; - }*/ - - - /* draws the actual shaded border to the specified graphics - */ - /** - * Paints the border glow - * @param g2 - * @param clipShape - * @param width - * @param height - */ - protected void paintBorderGlow(Graphics2D g2, - Shape clipShape, int width, int height) { - - int steps = getBrushSteps(); - float brushAlpha = 1f/steps; - - boolean inside = isRenderInsideShape(); - - g2.setPaint(getBrushColor()); - - g2.translate(offset.getX(), offset.getY()); - - if(isShouldFillShape()) { - // set the inside/outside mode - if(inside) { - g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 1f)); - Area a1 = new Area(new Rectangle( - (int)-offset.getX()-20, - (int)-offset.getY()-20, - width+40,height+40)); - Area a2 = new Area(clipShape); - a1.subtract(a2); - g2.fill(a1); - } else { - g2.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_OVER, 1f)); - g2.fill(clipShape); - } - - } - - // set the inside/outside mode - /* - if(inside) { - g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, brushAlpha)); - } else { - g2.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_OVER, brushAlpha)); - }*/ - g2.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_OVER, brushAlpha)); - - // draw the effect - for(float i=0; i=0; i=i-1f) { - float brushWidth = i * getEffectWidth()/steps; - gfx.setPaint(interpolateColor(i/steps,edgeColor,centerColor)); - gfx.setStroke(new BasicStroke(brushWidth, - BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); - gfx.draw(clipShape); - }*/ - - /* // an interesting outline effect. stroke the shape with a wide brush - * // then stroke again with slightly less wide one, then don't fill the middle - for(int i=0; i<2; i++) { - float brushWidth = (2-i)*5; - p("widdth = " + brushWidth); - gfx.setPaint(interpolateColor((float)(1-i), Color.BLACK, Color.WHITE)); - gfx.setStroke(new BasicStroke(brushWidth)); - gfx.draw(clipShape); - } - */ - gfx.translate(getOffset().getX(), getOffset().getY()); - gfx.setComposite(AlphaComposite.SrcOver); - int steps = getEffectWidth(); - if(borderPosition == BorderPosition.Centered) { - steps = steps/2; - } - for(int i=0; i - * - * @author Frederic Lavigne - * @author Karl Schaefer - */ -@SuppressWarnings("nls") -public abstract class AbstractComponentAddon implements ComponentAddon { - - private String name; - - protected AbstractComponentAddon(String name) { - this.name = name; - } - - @Override - public final String getName() { - return name; - } - - @Override - public void initialize(LookAndFeelAddons addon) { - addon.loadDefaults(getDefaults(addon)); - } - - @Override - public void uninitialize(LookAndFeelAddons addon) { - // commented after Issue 446. Maybe addon should keep track of its - // added defaults to correctly remove them on uninitialize - // addon.unloadDefaults(getDefaults(addon)); - } - - /** - * Adds default key/value pairs to the given list. - * - * @param addon - * @param defaults - */ - protected void addBasicDefaults(LookAndFeelAddons addon, - DefaultsList defaults) { - } - - /** - * Default implementation calls - * {@link #addBasicDefaults(LookAndFeelAddons, DefaultsList)} - * - * @param addon - * @param defaults - */ - protected void addMacDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - addBasicDefaults(addon, defaults); - } - - /** - * Default implementation calls - * {@link #addBasicDefaults(LookAndFeelAddons, DefaultsList)} - * - * @param addon - * @param defaults - */ - protected void addMetalDefaults(LookAndFeelAddons addon, - DefaultsList defaults) { - addBasicDefaults(addon, defaults); - } - - /** - * Default implementation calls - * {@link #addBasicDefaults(LookAndFeelAddons, DefaultsList)} - * - * @param addon - * @param defaults - */ - protected void addMotifDefaults(LookAndFeelAddons addon, - DefaultsList defaults) { - addBasicDefaults(addon, defaults); - } - - /** - * Default implementation calls - * {@link #addBasicDefaults(LookAndFeelAddons, DefaultsList)} - * - * @param addon - * @param defaults - */ - protected void addWindowsDefaults(LookAndFeelAddons addon, - DefaultsList defaults) { - addBasicDefaults(addon, defaults); - } - - /** - * Default implementation calls - * {@link #addBasicDefaults(LookAndFeelAddons, DefaultsList)} - * - * @param addon - * @param defaults - */ - protected void addLinuxDefaults(LookAndFeelAddons addon, - DefaultsList defaults) { - addBasicDefaults(addon, defaults); - } - - /** - * Default implementation calls - * {@link #addBasicDefaults(LookAndFeelAddons, DefaultsList)} - * - * @param addon - * @param defaults - */ - protected void addNimbusDefaults(LookAndFeelAddons addon, - DefaultsList defaults) { - addBasicDefaults(addon, defaults); - } - - /** - * Gets the defaults for the given addon. - * - * Based on the addon, it calls - * {@link #addMacDefaults(LookAndFeelAddons, DefaultsList)} if isMac() or - * {@link #addMetalDefaults(LookAndFeelAddons, DefaultsList)} if isMetal() - * or {@link #addMotifDefaults(LookAndFeelAddons, DefaultsList)} if - * isMotif() or {@link #addWindowsDefaults(LookAndFeelAddons, DefaultsList)} - * if isWindows() or - * {@link #addBasicDefaults(LookAndFeelAddons, DefaultsList)} if none of the - * above was called. - * - * @param addon - * @return an array of key/value pairs. For example: - * - *
                    -     * Object[] uiDefaults = { "Font", new Font("Dialog", Font.BOLD, 12), "Color",
                    -     *         Color.red, "five", new Integer(5) };
                    -     * 
                    - */ - private Object[] getDefaults(LookAndFeelAddons addon) { - DefaultsList defaults = new DefaultsList(); - if (isWindows(addon)) { - addWindowsDefaults(addon, defaults); - } else if (isMetal(addon)) { - addMetalDefaults(addon, defaults); - } else if (isMac(addon)) { - addMacDefaults(addon, defaults); - } else if (isLinux(addon)) { - addLinuxDefaults(addon, defaults); - } else if (isNimbus(addon)) { - addNimbusDefaults(addon, defaults); - } else { - // at least add basic defaults - addBasicDefaults(addon, defaults); - } - return defaults.toArray(); - } - - // - // Helper methods to make ComponentAddon developer life easier - // - - /** - * @return true if the addon is the Windows addon or its subclasses - */ - protected boolean isWindows(LookAndFeelAddons addon) { - return addon instanceof WindowsLookAndFeelAddons; - } - - /** - * @return true if the addon is the Metal addon or its subclasses - */ - protected boolean isMetal(LookAndFeelAddons addon) { - return addon instanceof MetalLookAndFeelAddons; - } - - /** - * @return true if the addon is the Mac OS X addon or its subclasses - */ - protected boolean isMac(LookAndFeelAddons addon) { - return addon instanceof MacOSXLookAndFeelAddons; - } - - /** - * @return true if the current look and feel is Linux - */ - protected boolean isLinux(LookAndFeelAddons addon) { - return addon instanceof LinuxLookAndFeelAddons; - } - - /** - * @return true if the current look and feel is Nimbus - */ - protected boolean isNimbus(LookAndFeelAddons addon) { - return addon instanceof NimbusLookAndFeelAddons; - } - -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/AbstractUIChangeHandler.java b/src/main/java/org/jdesktop/swingx/plaf/AbstractUIChangeHandler.java deleted file mode 100644 index f491d15f6a..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/AbstractUIChangeHandler.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.jdesktop.swingx.plaf; - -import javax.swing.*; -import java.beans.PropertyChangeListener; -import java.util.Map; -import java.util.WeakHashMap; - -@SuppressWarnings("nls") -public abstract class AbstractUIChangeHandler implements PropertyChangeListener { - //prevent double installation. - private final Map installed = new WeakHashMap(); - - public void install(JComponent c){ - if(isInstalled(c)){ - return; - } - - c.addPropertyChangeListener("UI", this); - installed.put(c, Boolean.FALSE); - } - - public boolean isInstalled(JComponent c) { - return installed.containsKey(c); - } - - public void uninstall(JComponent c){ - c.removePropertyChangeListener("UI", this); - installed.remove(c); - } -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/plaf/BuddyLayoutAndBorder.java b/src/main/java/org/jdesktop/swingx/plaf/BuddyLayoutAndBorder.java deleted file mode 100644 index b9a60ec2d1..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/BuddyLayoutAndBorder.java +++ /dev/null @@ -1,264 +0,0 @@ -package org.jdesktop.swingx.plaf; - -import org.jdesktop.swingx.prompt.BuddySupport; -import org.jdesktop.swingx.prompt.BuddySupport.Position; - -import javax.swing.*; -import javax.swing.border.Border; -import javax.swing.plaf.UIResource; -import javax.swing.plaf.basic.BasicBorders.MarginBorder; -import java.awt.*; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; - -public class BuddyLayoutAndBorder implements LayoutManager, Border, PropertyChangeListener, UIResource { - private JTextField textField; - - private Border borderDelegate; - - /** - * Installs a {@link BuddyLayoutAndBorder} as a layout and border of the - * given text field. Registers a {@link PropertyChangeListener} to wrap any - * subsequently set border on the text field. - */ - protected void install(JTextField textField) { - uninstall(); - this.textField = textField; - - textField.setLayout(this); - - replaceBorderIfNecessary(); - textField.addPropertyChangeListener("border", this); - } - - public Border getBorderDelegate() { - return borderDelegate; - } - - /** - * Wraps and replaces the text fields default border with this object, to - * honor the button margins and sizes of the search, clear and popup buttons - * and the layout style. - */ - protected void replaceBorderIfNecessary() { - Border original = textField.getBorder(); - - if (!(original instanceof BuddyLayoutAndBorder)) { - borderDelegate = original; - textField.setBorder(this); - } - } - - /** - * Does nothing. - * - * @see BuddySupport#add(javax.swing.JComponent, Position, JTextField) - */ - @Override - public void addLayoutComponent(String name, Component comp) { - } - - @Override - public Dimension minimumLayoutSize(Container parent) { - return preferredLayoutSize(parent); - } - - @Override - public Dimension preferredLayoutSize(Container parent) { - Dimension d = new Dimension(); - - // height of highest buddy. - for (Component c : BuddySupport.getLeft(textField)) { - d.height = Math.max(d.height, c.getPreferredSize().height); - } - for (Component c : BuddySupport.getRight(textField)) { - d.height = Math.max(d.height, c.getPreferredSize().height); - } - - Insets insets = getRealBorderInsets(); - d.height += insets.top + insets.bottom; - d.width += insets.left + insets.right; - - Insets outerMargin = BuddySupport.getOuterMargin(textField); - if (outerMargin != null) { - d.width += outerMargin.left + outerMargin.right; - d.height += outerMargin.bottom + outerMargin.top; - } - - return d; - } - - /** - * Does nothing. - * - * @see BuddySupport#remove(javax.swing.JComponent, JTextField) - */ - @Override - public void removeLayoutComponent(Component comp) { - } - - @Override - public void layoutContainer(Container parent) { - Rectangle visibleRect = getVisibleRect(); - Dimension size; - - for (Component comp : BuddySupport.getLeft(textField)) { - if (!comp.isVisible()) { - continue; - } - size = comp.getPreferredSize(); - comp.setBounds(visibleRect.x, centerY(visibleRect, size), size.width, size.height); - - visibleRect.x += size.width; - visibleRect.width -= size.width; - } - - for (Component comp : BuddySupport.getRight(textField)) { - if (!comp.isVisible()) { - continue; - } - - size = comp.getPreferredSize(); - comp.setBounds(visibleRect.x + visibleRect.width - size.width, centerY(visibleRect, size), size.width, - size.height); - visibleRect.width -= size.width; - } - } - - protected int centerY(Rectangle rect, Dimension size) { - return (int) (rect.getCenterY() - (size.height / 2)); - } - - /** - * @return the rectangle allocated by the text field, including the space - * allocated by the child components left and right, the text fields - * original border insets and the outer margin. - * - */ - protected Rectangle getVisibleRect() { - Rectangle alloc = SwingUtilities.getLocalBounds(textField); - - substractInsets(alloc, getRealBorderInsets()); - substractInsets(alloc, BuddySupport.getOuterMargin(textField)); - - return alloc; - } - - private void substractInsets(Rectangle alloc, Insets insets) { - if (insets != null) { - alloc.x += insets.left; - alloc.y += insets.top; - alloc.width -= insets.left + insets.right; - alloc.height -= insets.top + insets.bottom; - } - } - - /** - * Returns the {@link Insets} of the original {@link Border} plus the space - * required by the child components. - * - * @see Border#getBorderInsets(Component) - */ - @Override - public Insets getBorderInsets(Component c) { - Insets insets = null; - if (borderDelegate != null) { - // Original insets are cloned to make it work in Mac OS X Aqua LnF. - // Seems that this LnF uses a shared insets instance which should - // not be modified. - // Include margin here - insets = (Insets) borderDelegate.getBorderInsets(textField).clone(); - } else { - insets = new Insets(0, 0, 0, 0); - } - //somehow this happens sometimes - if (textField == null) { - return insets; - } - - for (Component comp : BuddySupport.getLeft(textField)) { - insets.left += comp.isVisible() ? comp.getPreferredSize().width : 0; - } - for (Component comp : BuddySupport.getRight(textField)) { - insets.right += comp.isVisible() ? comp.getPreferredSize().width : 0; - } - - Insets outerMargin = BuddySupport.getOuterMargin(textField); - if (outerMargin != null) { - insets.left += outerMargin.left; - insets.right += outerMargin.right; - insets.top += outerMargin.top; - insets.bottom += outerMargin.bottom; - } - - return insets; - } - - /** - * Returns the insets of the original border (without the margin! Beware of - * {@link MarginBorder}!). - * - * @return the insets of the border delegate - */ - public Insets getRealBorderInsets() { - if (borderDelegate == null) { - //SwingX 1287: null borders are possible and give no insets - return new Insets(0, 0, 0, 0); - } - - Insets insets = borderDelegate.getBorderInsets(textField); - - // for some reason, all LnFs add the margin to the insets. - // we want the insets without the margin, so substract the margin here!! - // TODO: consider checking, if the current border really includes the - // margin. Consider: - // 1. Not only MarginBorder adds margin - // 2. Calling getBorderInsets(null) is not appropriate, since some - // Borders can't handle null values. - Insets margin = textField.getMargin(); - if (margin != null) { - insets.left -= margin.left; - insets.right -= margin.right; - insets.top -= margin.top; - insets.bottom -= margin.bottom; - } - - return insets; - } - - @Override - public boolean isBorderOpaque() { - if (borderDelegate == null) { - return false; - } - return borderDelegate.isBorderOpaque(); - } - - @Override - public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { - if (borderDelegate != null) { - borderDelegate.paintBorder(c, g, x, y, width, height); - } - } - - @Override - public void propertyChange(PropertyChangeEvent evt) { - replaceBorderIfNecessary(); - } - - public void uninstall() { - if (textField != null) { - textField.removePropertyChangeListener("border", this); - if (textField.getBorder() == this) { - textField.setBorder(borderDelegate); - } - textField.setLayout(null); - textField = null; - } - } - - @Override - public String toString() { - return String.format("%s (%s): %s", getClass().getName(), getBorderInsets(null), borderDelegate); - } -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/plaf/BuddyTextFieldUI.java b/src/main/java/org/jdesktop/swingx/plaf/BuddyTextFieldUI.java deleted file mode 100644 index af72700cd1..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/BuddyTextFieldUI.java +++ /dev/null @@ -1,88 +0,0 @@ -package org.jdesktop.swingx.plaf; - -import javax.swing.*; -import javax.swing.border.Border; -import javax.swing.plaf.TextUI; -import java.awt.*; - -/** - *

                    - * TODO: queries the text components layout manager for the preferred size. - *

                    - * - * @author Peter Weishapl - * - */ -public class BuddyTextFieldUI extends PromptTextFieldUI { - protected BuddyLayoutAndBorder layoutAndBorder; - - // Bad hacking: FIXME when know how to get the real margin. - private static final Insets MAC_MARGIN = new Insets(0, 2, 1, 2); - - @Override - public void paint(Graphics g, JComponent c) { - // yet another dirty mac hack to prevent painting background outside of - // border. - if (hasMacTextFieldBorder(c)) { - Insets borderInsets = layoutAndBorder.getRealBorderInsets(); - - borderInsets.left -= MAC_MARGIN.left; - int height = c.getHeight() - borderInsets.bottom - borderInsets.top + MAC_MARGIN.bottom + MAC_MARGIN.top; - int width = c.getWidth() - borderInsets.left - borderInsets.right + MAC_MARGIN.right; - g.clipRect(borderInsets.left, borderInsets.top, width, height); - } - super.paint(g, c); - } - - private boolean hasMacTextFieldBorder(JComponent c) { - Border border = c.getBorder(); - if (border == layoutAndBorder) { - border = layoutAndBorder.getBorderDelegate(); - } - return border != null && border.getClass().getName().equals("apple.laf.CUIAquaTextFieldBorder"); - } - - /** - * Creates a new {@link BuddyTextFieldUI} which delegates most work to - * another {@link TextUI}. - * - * @param delegate - */ - public BuddyTextFieldUI(TextUI delegate) { - super(delegate); - } - - @Override - public void installUI(JComponent c) { - super.installUI(c); - layoutAndBorder = createBuddyLayoutAndBorder(); - layoutAndBorder.install((JTextField) c); - } - - protected BuddyLayoutAndBorder createBuddyLayoutAndBorder() { - return new BuddyLayoutAndBorder(); - } - - @Override - public void uninstallUI(JComponent c) { - layoutAndBorder.uninstall(); - super.uninstallUI(c); - } - - /** - * TODO: comment - * - * @see javax.swing.plaf.ComponentUI#getPreferredSize(JComponent) - */ - @Override - public Dimension getPreferredSize(JComponent c) { - Dimension d = new Dimension(); - Dimension cd = super.getPreferredSize(c); - Dimension ld = c.getLayout().preferredLayoutSize(c); - - d.height = Math.max(cd.height, ld.height); - d.width = Math.max(cd.width, ld.width); - - return d; - } -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/plaf/BusyLabelAddon.java b/src/main/java/org/jdesktop/swingx/plaf/BusyLabelAddon.java deleted file mode 100644 index d8abf83e1f..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/BusyLabelAddon.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * $Id: BusyLabelAddon.java 2565 2008-01-03 19:08:32Z rah003 $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf; - -import org.jdesktop.swingx.JXBusyLabel; - -import javax.swing.plaf.ColorUIResource; -import java.awt.*; -import java.awt.geom.Ellipse2D; -import java.awt.geom.RoundRectangle2D; - -/** - * Addon for JXBusyLabel.
                    - * - * @author rah003 - */ -public class BusyLabelAddon extends AbstractComponentAddon { - - public BusyLabelAddon() { - super("JXBusyLabel"); - } - - @Override - protected void addBasicDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - defaults.add(JXBusyLabel.uiClassID, "org.jdesktop.swingx.plaf.basic.BasicBusyLabelUI"); - defaults.add("JXBusyLabel.delay", 100); - defaults.add("JXBusyLabel.baseColor", new ColorUIResource(Color.LIGHT_GRAY)); - defaults.add("JXBusyLabel.highlightColor", new ColorUIResource(UIManagerExt.getSafeColor("Label.foreground", Color.BLACK))); - float barLength = 8; - float barWidth = 4; - float height = 26; - defaults.add("JXBusyLabel.pointShape", new ShapeUIResource( - new RoundRectangle2D.Float(0, 0, barLength, barWidth, - barWidth, barWidth))); - defaults.add("JXBusyLabel.trajectoryShape", new ShapeUIResource( - new Ellipse2D.Float(barLength / 2, barLength / 2, height - - barLength, height - barLength))); - - - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/BusyLabelUI.java b/src/main/java/org/jdesktop/swingx/plaf/BusyLabelUI.java deleted file mode 100644 index ee9c5f15b6..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/BusyLabelUI.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * $Id: BusyLabelUI.java 3964 2011-03-17 19:12:29Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.plaf; - -import org.jdesktop.swingx.painter.BusyPainter; - -import java.awt.*; - -/** - * - * @author rah003 - */ -public interface BusyLabelUI { - /** - * @return The BusyPainter for the JXBusyLabel. If - * this method returns null, then no progress indication will be shown by busy label. - */ - public BusyPainter getBusyPainter(Dimension dim); - - /** - * Delay between moving from one point to another. The exact timing will be close to the selected value but is not guaranteed to be precise (subject to the timing precision of underlaying jvm). - * @return Delay in ms. - */ - public int getDelay(); -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/ColumnControlButtonAddon.java b/src/main/java/org/jdesktop/swingx/plaf/ColumnControlButtonAddon.java deleted file mode 100644 index 1b396fc0a1..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/ColumnControlButtonAddon.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * $Id: ColumnControlButtonAddon.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf; - -import org.jdesktop.swingx.icon.ColumnControlIcon; - -import javax.swing.plaf.InsetsUIResource; - -/** - * Addon to load LF specific properties for the ColumnControlButton. - * - * @author Jeanette Winzenburg - */ -public class ColumnControlButtonAddon extends AbstractComponentAddon { - - /** - * Instantiates the addon for ColumnControlButton. - */ - public ColumnControlButtonAddon() { - super("ColumnControlButton"); - } - - @Override - protected void addBasicDefaults(LookAndFeelAddons addon, - DefaultsList defaults) { - super.addBasicDefaults(addon, defaults); - defaults.add("ColumnControlButton.actionIcon", new ColumnControlIcon()); - defaults.add("ColumnControlButton.margin", new InsetsUIResource(1, 2, 2, 1)); - } - - -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/ComponentAddon.java b/src/main/java/org/jdesktop/swingx/plaf/ComponentAddon.java deleted file mode 100644 index 142f132511..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/ComponentAddon.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * $Id: ComponentAddon.java 4028 2011-06-03 19:32:19Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf; - -/** - * Each new component type of the library will contribute an addon to - * the LookAndFeelAddons. A ComponentAddon is the - * equivalent of a {@link javax.swing.LookAndFeel}but focused on one - * component.
                    - * - * @author Frederic Lavigne - */ -public interface ComponentAddon { - - /** - * @return the name of this addon - */ - String getName(); - - /** - * Initializes this addon (i.e register UI classes, colors, fonts, - * borders, any UIResource used by the component class). When - * initializing, the addon can register different resources based on - * the addon or the current look and feel. - * - * @param addon the current addon - */ - void initialize(LookAndFeelAddons addon); - - /** - * Uninitializes this addon. - * - * @param addon - */ - void uninitialize(LookAndFeelAddons addon); - -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/plaf/DatePickerAddon.java b/src/main/java/org/jdesktop/swingx/plaf/DatePickerAddon.java deleted file mode 100644 index 0f30a856c3..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/DatePickerAddon.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * $Id: DatePickerAddon.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf; - -import org.jdesktop.swingx.JXDatePicker; -import org.jdesktop.swingx.plaf.basic.BasicDatePickerUI; - -import javax.swing.*; -import javax.swing.border.LineBorder; -import javax.swing.plaf.BorderUIResource; - -/** - * @author Joshua Outwater - */ -public class DatePickerAddon extends AbstractComponentAddon { - public DatePickerAddon() { - super("JXDatePicker"); - } - - /** - * {@inheritDoc} - */ - @Override - protected void addBasicDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addBasicDefaults(addon, defaults); - - defaults.add(JXDatePicker.uiClassID, BasicDatePickerUI.class.getName()); - defaults.add("JXDatePicker.border", - new BorderUIResource(BorderFactory.createCompoundBorder( - LineBorder.createGrayLineBorder(), - BorderFactory.createEmptyBorder(3, 3, 3, 3)))); - - UIManagerExt.addResourceBundle( - "org.jdesktop.swingx.plaf.basic.resources.DatePicker"); - } - - /** - * {@inheritDoc} - */ - @Override - protected void addWindowsDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addWindowsDefaults(addon, defaults); - defaults.add("JXDatePicker.arrowIcon", - LookAndFeel.makeIcon(DatePickerAddon.class, "windows/resources/combo-xp.png")); - } - - /** - * {@inheritDoc} - */ - @Override - protected void addLinuxDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addLinuxDefaults(addon, defaults); - - defaults.add("JXDatePicker.arrowIcon", - LookAndFeel.makeIcon(DatePickerAddon.class, "linux/resources/combo-gtk.png")); - - if (isGTK()) { - // Issue #667-swingx: ugly border in GTK - // remove the border which was installed in addBasicDefaults - defaults.add("JXDatePicker.border", null); - } - } - - /** - * - * @return true if the LF is GTK. - */ - private boolean isGTK() { - return "GTK".equals(UIManager.getLookAndFeel().getID()); - } - - /** - * {@inheritDoc} - */ - @Override - protected void addMacDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addMacDefaults(addon, defaults); - - defaults.add("JXDatePicker.arrowIcon", - LookAndFeel.makeIcon(DatePickerAddon.class, "macosx/resources/combo-osx.png")); - - defaults.add("JXDatePicker.border", "none"); - - } - - /** {@inheritDoc} */ - @Override - protected void addNimbusDefaults (LookAndFeelAddons addon, - DefaultsList defaults) { - super.addNimbusDefaults (addon, defaults); - - // Issue #913-swingx: ugly in Nimbus - // TODO: don't use an image here, Nimbus uses Painters for everything - // => e.g. reuse the -// com.sun.java.swing.plaf.nimbus.ComboBoxComboBoxArrowButtonPainter - // (at the moment the OS-X icon looks most similar, it's much better - // than no icon...) - defaults.add ("JXDatePicker.arrowIcon", - LookAndFeel.makeIcon (DatePickerAddon.class, - "macosx/resources/combo-osx.png")); - - // Issue #913-swingx: ugly in Nimbus - // remove the border which was installed in addBasicDefaults - // this is done by Nimbus - defaults.add ("JXDatePicker.border", null); - } - -} - diff --git a/src/main/java/org/jdesktop/swingx/plaf/DatePickerUI.java b/src/main/java/org/jdesktop/swingx/plaf/DatePickerUI.java deleted file mode 100644 index c4a3730017..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/DatePickerUI.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * $Id: DatePickerUI.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf; - -import javax.swing.plaf.ComponentUI; -import java.beans.PropertyVetoException; -import java.util.Date; - -/** - * The ComponentUI for a JXDatePicker. - *

                    - * - * Responsible for keeping the date property of all participants synchronized at - * all "stable" points in their life-cycle. That is the following invariant is - * guaranteed: - * - *

                    
                    - * Date selected = datePicker.getMonthView().getSelectedDate();
                    - * assertEquals(selected, datePicker.getDate());
                    - * assertEquals(selected, datePicker.getEditor().getValue());
                    - * 
                    - * - * @author Joshua Outwater - * @author Jeanette Winzenburg - */ -public abstract class DatePickerUI extends ComponentUI { - /** - * Get the baseline for the specified component, or a value less - * than 0 if the baseline can not be determined. The baseline is measured - * from the top of the component. - * - * @param width Width of the component to determine baseline for. - * @param height Height of the component to determine baseline for. - * @return baseline for the specified component - */ - public int getBaseline(int width, int height) { - return -1; - } - - /** - * Checks the given date for validity for selection. If valid, - * returns the date as appropriate in the picker's context, otherwise - * throws a propertyVetoException. Note that the returned date might - * be different from the input date, f.i. the time fields might be - * cleared. The input date is guaranteed to be unchanged. - * - * @param date date to check - * @return the date as allowed in the context of the picker. - * - * @throws PropertyVetoException if the given date is not valid for - * selection - */ - public abstract Date getSelectableDate(Date date) throws PropertyVetoException; - -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/DefaultsList.java b/src/main/java/org/jdesktop/swingx/plaf/DefaultsList.java deleted file mode 100644 index 3a7bf64ce5..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/DefaultsList.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * $Id: DefaultsList.java 4047 2011-07-19 18:51:12Z kschaefe $ - * - * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf; - -import org.jdesktop.swingx.painter.Painter; -import org.jdesktop.swingx.util.Contract; - -import javax.swing.*; -import javax.swing.border.Border; -import javax.swing.plaf.UIResource; -import java.awt.*; -import java.util.ArrayList; -import java.util.List; - -/** - * A specialty "list" for working with UI defaults. Requires adds to be done - * using key/value pairs. The purpose of this list is to enforce additions as - * pairs. - * - * @author Karl George Schaefer - */ -@SuppressWarnings("nls") -public final class DefaultsList { - private List delegate; - - /** - * Creates a {@code DefaultsList}. - */ - public DefaultsList() { - delegate = new ArrayList(); - } - - /** - * Adds a key/value pair to the defaults list. This implementation defers to - * {@link #add(Object, Object, boolean)} with {@code enableChecking} set to - * {@code true}. - * - * @param key - * the key that will be used to query {@code UIDefaults} - * @param value - * the value associated with the key - * @throws NullPointerException - * if {@code key} is {@code null} - * @throws IllegalArgumentException - * if {@code value} is a type that should be a - * {@code UIResource} but is not. For instance, passing in a - * {@code Border} that is not a {@code UIResource} will - * cause an exception. This checking must be enabled. - */ - public void add(Object key, Object value) { - add(key, value, true); - } - - /** - * Adds a key/value pair to the defaults list. A pair with a {@code null} - * value is treated specially. A {@code null}-value pair is never added to - * the list and, furthermore, if a key/value pair exists in this list with - * the same key as the newly added one, it is removed. - * - * @param key - * the key that will be used to query {@code UIDefaults} - * @param value - * the value associated with the key - * @param enableChecking - * if {@code true} then the value is checked to ensure that - * it is a {@code UIResource}, if appropriate - * @throws NullPointerException - * if {@code key} is {@code null} - * @throws IllegalArgumentException - * if {@code value} is a type that should be a - * {@code UIResource} but is not. For instance, passing in a - * {@code Border} that is not a {@code UIResource} will - * cause an exception. This checking must be enabled. - */ - public void add(Object key, Object value, boolean enableChecking) { - if (enableChecking) { - asUIResource(value, value + " must be a UIResource"); - } - - if (value == null && delegate.contains(key)) { - int i = delegate.indexOf(key); - - delegate.remove(i + 1); - delegate.remove(i); - } else if (value != null) { - delegate.add(Contract.asNotNull(key, "key cannot be null")); - delegate.add(value); - } - } - - //TODO move to Contract? - private static T asUIResource(T value, String message) { - if (!(value instanceof UIResource)) { - boolean shouldThrow = false; - - shouldThrow |= value instanceof ActionMap; - shouldThrow |= value instanceof Border; - shouldThrow |= value instanceof Color; - shouldThrow |= value instanceof Dimension; - shouldThrow |= value instanceof Font; - shouldThrow |= value instanceof Icon; - shouldThrow |= value instanceof InputMap; - shouldThrow |= value instanceof Insets; - shouldThrow |= value instanceof Painter; - //FIXME how to handle UIResource testing -// shouldThrow |= value instanceof StringValue; - - if (shouldThrow) { - throw new IllegalArgumentException(message); - } - } - - return value; - } - - /** - * Gets a copy of this list as an array. - * - * @return an array containing all of the key/value pairs added to this list - */ - public Object[] toArray() { - return delegate.toArray(); - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/ErrorPaneAddon.java b/src/main/java/org/jdesktop/swingx/plaf/ErrorPaneAddon.java deleted file mode 100644 index d9d55a8b00..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/ErrorPaneAddon.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * $Id: ErrorPaneAddon.java 3475 2009-08-28 08:30:47Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.plaf; - -import org.jdesktop.swingx.JXErrorPane; - -/** - * - * @author rbair - */ -public class ErrorPaneAddon extends AbstractComponentAddon { - - /** Creates a new instance of ErrorPaneAddon */ - public ErrorPaneAddon() { - super("JXErrorPane"); - } - - /** - * {@inheritDoc} - */ - @Override - protected void addBasicDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addBasicDefaults(addon, defaults); - - defaults.add(JXErrorPane.uiClassID, "org.jdesktop.swingx.plaf.basic.BasicErrorPaneUI"); - - UIManagerExt.addResourceBundle( - "org.jdesktop.swingx.plaf.basic.resources.ErrorPane"); - } - - /** - * {@inheritDoc} - */ - @Override - protected void addMacDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addMacDefaults(addon, defaults); - - defaults.add(JXErrorPane.uiClassID, "org.jdesktop.swingx.plaf.macosx.MacOSXErrorPaneUI"); - - UIManagerExt.addResourceBundle( - "org.jdesktop.swingx.plaf.macosx.resources.ErrorPane"); - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/ErrorPaneUI.java b/src/main/java/org/jdesktop/swingx/plaf/ErrorPaneUI.java deleted file mode 100644 index f9c8a092bf..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/ErrorPaneUI.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * $Id: ErrorPaneUI.java 3105 2008-10-16 21:09:43Z rah003 $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.plaf; - -import javax.swing.*; -import javax.swing.plaf.PanelUI; -import java.awt.*; - -/** - * The ComponentUI for a JXErrorPane. - *

                    - * - * @author rbair - */ -public abstract class ErrorPaneUI extends PanelUI { - /** - * Creates new ErrorPane wrapped in the frame window centered at provided owner component. - * @param owner component to center created error frame at. - * @return New ErrorPane instance wrapped in JFrame. - */ - public abstract JFrame getErrorFrame(Component owner); - - /** - * Creates new ErrorPane wrapped in the dialog window centered at provided owner component. - * @param owner component to center created error dialog at. - * @return New ErrorPane instance wrapped in JDialog. - */ - public abstract JDialog getErrorDialog(Component owner); - - /** - * Creates new ErrorPane wrapped in the internal frame window centered at provided owner component. - * @param owner component to center created error frame at. - * @return New ErrorPane instance wrapped in JInternalFrame. - */ - public abstract JInternalFrame getErrorInternalFrame(Component owner); - /** - * Calculates default prefered size for JXErrorPane on given platform/LAF. - * @return Preferred size. - */ - public abstract Dimension calculatePreferredSize(); -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/HeaderAddon.java b/src/main/java/org/jdesktop/swingx/plaf/HeaderAddon.java deleted file mode 100644 index 91c2e6df99..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/HeaderAddon.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * $Id: HeaderAddon.java 2474 2007-11-21 17:32:04Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf; - -import org.jdesktop.swingx.JXHeader; - -import javax.swing.*; -import javax.swing.plaf.ColorUIResource; -import javax.swing.plaf.FontUIResource; -import java.awt.*; - -/** - * Addon for JXHeader.
                    - * - */ -public class HeaderAddon extends AbstractComponentAddon { - - public HeaderAddon() { - super("JXHeader"); - } - - /** - * {@inheritDoc} - */ - @Override - protected void addBasicDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addBasicDefaults(addon, defaults); - - defaults.add(JXHeader.uiClassID, "org.jdesktop.swingx.plaf.basic.BasicHeaderUI"); - //TODO image is missing - defaults.add("JXHeader.defaultIcon", - LookAndFeel.makeIcon(HeaderAddon.class, "basic/resources/header-default.png")); - //TODO use safe methods - defaults.add("JXHeader.titleFont", new FontUIResource(UIManager.getFont("Label.font").deriveFont(Font.BOLD))); - defaults.add("JXHeader.titleForeground", UIManager.getColor("Label.foreground")); - defaults.add("JXHeader.descriptionFont", UIManager.getFont("Label.font")); - defaults.add("JXHeader.descriptionForeground", UIManager.getColor("Label.foreground")); - defaults.add("JXHeader.background", - UIManagerExt.getSafeColor("control", new ColorUIResource(Color.decode("#C0C0C0")))); - defaults.add("JXHeader.startBackground", new ColorUIResource(Color.WHITE)); - } - - /** - * {@inheritDoc} - */ - @Override - protected void addMacDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addMacDefaults(addon, defaults); - - defaults.add("JXHeader.background", new ColorUIResource(new Color(218, 218, 218))); - defaults.add("JXHeader.startBackground", new ColorUIResource(new Color(235, 235, 235))); - } - - /** - * {@inheritDoc} - */ - @Override - protected void addNimbusDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addNimbusDefaults(addon, defaults); - - defaults.add("JXHeader.background", new ColorUIResource(new Color(214, 217, 223, 255))); - } -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/plaf/HeaderUI.java b/src/main/java/org/jdesktop/swingx/plaf/HeaderUI.java deleted file mode 100644 index 0bf490bc46..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/HeaderUI.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * $Id: HeaderUI.java 1422 2006-09-25 23:06:11Z rbair $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.plaf; - -import javax.swing.plaf.PanelUI; - -/** - * - * @author rbair - */ -public abstract class HeaderUI extends PanelUI { -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/HyperlinkAddon.java b/src/main/java/org/jdesktop/swingx/plaf/HyperlinkAddon.java deleted file mode 100644 index 7652c8c935..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/HyperlinkAddon.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * $Id: HyperlinkAddon.java 3745 2010-08-06 03:02:52Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf; - - -import org.jdesktop.swingx.JXHyperlink; - -import javax.swing.plaf.ColorUIResource; - -/** - * Addon for JXHyperlink.
                    - */ -public class HyperlinkAddon extends AbstractComponentAddon { - public HyperlinkAddon() { - super("JXHyperlink"); - } - - @Override - protected void addBasicDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addBasicDefaults(addon, defaults); - - defaults.add(JXHyperlink.uiClassID, "org.jdesktop.swingx.plaf.basic.BasicHyperlinkUI"); - //using CSS pseudo classes for Color types - defaults.add("Hyperlink.linkColor", new ColorUIResource(0, 0x33, 0xFF)); - defaults.add("Hyperlink.visitedColor", new ColorUIResource(0x99, 0, 0x99)); - defaults.add("Hyperlink.hoverColor", new ColorUIResource(0xFF, 0x33, 0)); - defaults.add("Hyperlink.activeColor", new ColorUIResource(0xFF, 0x33, 0)); - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/LookAndFeelAddons.java b/src/main/java/org/jdesktop/swingx/plaf/LookAndFeelAddons.java deleted file mode 100644 index f896ec7240..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/LookAndFeelAddons.java +++ /dev/null @@ -1,533 +0,0 @@ -/* - * $Id: LookAndFeelAddons.java 4250 2012-11-13 18:15:34Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf; - -import org.jdesktop.swingx.BackgroundPaintable; -import org.jdesktop.swingx.painter.Painter; - -import javax.swing.*; -import javax.swing.plaf.ComponentUI; -import javax.swing.plaf.UIResource; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.lang.reflect.Method; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.ServiceLoader; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Provides additional pluggable UI for new components added by the library. By default, the library - * uses the pluggable UI returned by {@link #getBestMatchAddonClassName()}. - *

                    - * The default addon can be configured using the swing.addon system property as follow: - *

                      - *
                    • on the command line, java -Dswing.addon=ADDONCLASSNAME ...
                    • - *
                    • at runtime and before using the library components - * System.getProperties().put("swing.addon", ADDONCLASSNAME);
                    • - *
                    - *

                    - * The default {@link #getCrossPlatformAddonClassName() cross platform addon} can be configured - * using the swing.crossplatformlafaddon system property as follow: - *

                      - *
                    • on the command line, java -Dswing.crossplatformlafaddon=ADDONCLASSNAME ...
                    • - *
                    • at runtime and before using the library components - * System.getProperties().put("swing.crossplatformlafaddon", ADDONCLASSNAME);
                      - * Note: changing this property after the UI has been initialized may result in unexpected behavior. - *
                    • - *
                    - *

                    - * The addon can also be installed directly by calling the {@link #setAddon(String)}method. For - * example, to install the Windows addons, add the following statement - * LookAndFeelAddons.setAddon("org.jdesktop.swingx.plaf.windows.WindowsLookAndFeelAddons");. - * - * @author Frederic Lavigne - * @author Karl Schaefer - */ -@SuppressWarnings("nls") -public abstract class LookAndFeelAddons { - - private static List contributedComponents = new ArrayList(); - - /** - * Key used to ensure the current UIManager has been populated by the LookAndFeelAddons. - */ - private static final Object APPCONTEXT_INITIALIZED = new Object(); - - private static boolean trackingChanges = false; - private static PropertyChangeListener changeListener; - - static { - // load the default addon - String addonClassname = getBestMatchAddonClassName(); - - try { - addonClassname = System.getProperty("swing.addon", addonClassname); - } catch (SecurityException e) { - // security exception may arise in Java Web Start - } - - try { - setAddon(addonClassname); - } catch (Exception e) { - // PENDING(fred) do we want to log an error and continue with a default - // addon class or do we just fail? - throw new ExceptionInInitializerError(e); - } - - setTrackingLookAndFeelChanges(true); - } - - private static LookAndFeelAddons currentAddon; - - /** - * Determines if the addon is a match for the {@link UIManager#getLookAndFeel() current Look and - * Feel}. - * - * @return {@code true} if this addon matches (is compatible); {@code false} otherwise - */ - protected boolean matches() { - return false; - } - - /** - * Determines if the addon is a match for the system Look and Feel. - * - * @return {@code true} if this addon matches (is compatible with) the system Look and Feel; - * {@code false} otherwise - */ - protected boolean isSystemAddon() { - return false; - } - - /** - * Initializes the look and feel addon. This method is - * - * @see #uninitialize - * @see UIManager#setLookAndFeel - */ - public void initialize() { - for (Iterator iter = contributedComponents.iterator(); iter.hasNext();) { - ComponentAddon addon = iter.next(); - addon.initialize(this); - } - } - - public void uninitialize() { - for (Iterator iter = contributedComponents.iterator(); iter.hasNext();) { - ComponentAddon addon = iter.next(); - addon.uninitialize(this); - } - } - - /** - * Adds the given defaults in UIManager. - * - * Note: the values are added only if they do not exist in the existing look and feel defaults. - * This makes it possible for look and feel implementors to override SwingX defaults. - * - * Note: the array is traversed in reverse order. If a key is found twice in the array, the - * key/value with the highest position in the array gets precedence over the other key in the - * array - * - * @param keysAndValues - */ - public void loadDefaults(Object[] keysAndValues) { - // Go in reverse order so the most recent keys get added first... - for (int i = keysAndValues.length - 2; i >= 0; i = i - 2) { - if (UIManager.getLookAndFeelDefaults().get(keysAndValues[i]) == null) { - UIManager.getLookAndFeelDefaults().put(keysAndValues[i], keysAndValues[i + 1]); - } - } - } - - public void unloadDefaults(@SuppressWarnings("unused") Object[] keysAndValues) { - // commented after Issue 446. - /* - * for (int i = 0, c = keysAndValues.length; i < c; i = i + 2) { - * UIManager.getLookAndFeelDefaults().put(keysAndValues[i], null); } - */ - } - - public static void setAddon(String addonClassName) throws InstantiationException, - IllegalAccessException, ClassNotFoundException { - setAddon(Class.forName(addonClassName, true, getClassLoader())); - } - - public static void setAddon(Class addonClass) throws InstantiationException, - IllegalAccessException { - LookAndFeelAddons addon = (LookAndFeelAddons) addonClass.newInstance(); - setAddon(addon); - } - - public static void setAddon(LookAndFeelAddons addon) { - if (currentAddon != null) { - currentAddon.uninitialize(); - } - - addon.initialize(); - currentAddon = addon; - // JW: we want a marker to discover if the LookAndFeelDefaults have been - // swept from under our feet. The following line looks suspicious, - // as it is setting a user default instead of a LF default. User defaults - // are not touched when resetting a LF - UIManager.put(APPCONTEXT_INITIALIZED, Boolean.TRUE); - // trying to fix #784-swingx: frequent NPE on getUI - // JW: we want a marker to discover if the LookAndFeelDefaults have been - // swept from under our feet. - UIManager.getLookAndFeelDefaults().put(APPCONTEXT_INITIALIZED, Boolean.TRUE); - } - - public static LookAndFeelAddons getAddon() { - return currentAddon; - } - - private static ClassLoader getClassLoader() { - ClassLoader cl = null; - - try { - cl = AccessController.doPrivileged(new PrivilegedAction() { - @Override - public ClassLoader run() { - return LookAndFeelAddons.class.getClassLoader(); - } - }); - } catch (SecurityException ignore) { } - - if (cl == null) { - final Thread t = Thread.currentThread(); - - try { - cl = AccessController.doPrivileged(new PrivilegedAction() { - @Override - public ClassLoader run() { - return t.getContextClassLoader(); - } - }); - } catch (SecurityException ignore) { } - } - - if (cl == null) { - try { - cl = AccessController.doPrivileged(new PrivilegedAction() { - @Override - public ClassLoader run() { - return ClassLoader.getSystemClassLoader(); - } - }); - } catch (SecurityException ignore) { } - } - - return cl; - } - - /** - * Based on the current look and feel (as returned by UIManager.getLookAndFeel()), - * this method returns the name of the closest LookAndFeelAddons to use. - * - * @return the addon matching the currently installed look and feel - */ - public static String getBestMatchAddonClassName() { - LookAndFeel laf = UIManager.getLookAndFeel(); - String className = null; - - if (UIManager.getCrossPlatformLookAndFeelClassName().equals(laf.getClass().getName())) { - className = getCrossPlatformAddonClassName(); - } else if (UIManager.getSystemLookAndFeelClassName().equals(laf.getClass().getName())) { - className = getSystemAddonClassName(); - } else { - ServiceLoader addonLoader = ServiceLoader.load(LookAndFeelAddons.class, - getClassLoader()); - - for (LookAndFeelAddons addon : addonLoader) { - if (addon.matches()) { - className = addon.getClass().getName(); - break; - } - } - } - - if (className == null) { - className = getSystemAddonClassName(); - } - - return className; - } - - public static String getCrossPlatformAddonClassName() { - try { - return AccessController.doPrivileged(new PrivilegedAction() { - @Override - public String run() { - return System.getProperty("swing.crossplatformlafaddon", - "org.jdesktop.swingx.plaf.metal.MetalLookAndFeelAddons"); - } - }); - } catch (SecurityException ignore) { - } - - return "org.jdesktop.swing.plaf.metal.MetalLookAndFeelAddons"; - } - - /** - * Gets the addon best suited for the operating system where the virtual machine is running. - * - * @return the addon matching the native operating system platform. - */ - public static String getSystemAddonClassName() { - ServiceLoader addonLoader = ServiceLoader.load(LookAndFeelAddons.class, - getClassLoader()); - String className = null; - - for (LookAndFeelAddons addon : addonLoader) { - if (addon.isSystemAddon()) { - className = addon.getClass().getName(); - break; - } - } - - if (className == null) { - className = getCrossPlatformAddonClassName(); - } - - return className; - } - - /** - * Each new component added by the library will contribute its default UI classes, colors and - * fonts to the LookAndFeelAddons. See {@link ComponentAddon}. - * - * @param component - */ - public static void contribute(ComponentAddon component) { - contributedComponents.add(component); - - if (currentAddon != null) { - // make sure to initialize any addons added after the - // LookAndFeelAddons has been installed - component.initialize(currentAddon); - } - } - - /** - * Removes the contribution of the given addon - * - * @param component - */ - public static void uncontribute(ComponentAddon component) { - contributedComponents.remove(component); - - if (currentAddon != null) { - component.uninitialize(currentAddon); - } - } - - /** - * Workaround for IDE mixing up with classloaders and Applets environments. Consider this method - * as API private. It must not be called directly. - * - * @param component - * @param expectedUIClass - * @return an instance of expectedUIClass - */ - public static ComponentUI getUI(JComponent component, Class expectedUIClass) { - maybeInitialize(); - - // solve issue with ClassLoader not able to find classes - String uiClassname = (String) UIManager.get(component.getUIClassID()); - // possible workaround and more debug info on #784 - if (uiClassname == null) { - Logger logger = Logger.getLogger("LookAndFeelAddons"); - logger.warning("Failed to retrieve UI for " + component.getClass().getName() - + " with UIClassID " + component.getUIClassID()); - if (logger.isLoggable(Level.FINE)) { - logger.fine("Existing UI defaults keys: " - + new ArrayList(UIManager.getDefaults().keySet())); - } - // really ugly hack. Should be removed as soon as we figure out what is causing the - // issue - uiClassname = "org.jdesktop.swingx.plaf.basic.Basic" + expectedUIClass.getSimpleName(); - } - try { - Class uiClass = Class.forName(uiClassname); - UIManager.put(uiClassname, uiClass); - } catch (ClassNotFoundException e) { - // we ignore the ClassNotFoundException - } - - ComponentUI ui = UIManager.getUI(component); - - if (expectedUIClass.isInstance(ui)) { - return ui; - } else if (ui == null) { - barkOnUIError("no ComponentUI class for: " + component); - } else { - String realUI = ui.getClass().getName(); - Class realUIClass = null; - - try { - realUIClass = expectedUIClass.getClassLoader().loadClass(realUI); - } catch (ClassNotFoundException e) { - barkOnUIError("failed to load class " + realUI); - } - - if (realUIClass != null) { - try { - Method createUIMethod = realUIClass.getMethod("createUI", - new Class[] { JComponent.class }); - - return (ComponentUI) createUIMethod.invoke(null, new Object[] { component }); - } catch (NoSuchMethodException e) { - barkOnUIError("static createUI() method not found in " + realUIClass); - } catch (Exception e) { - barkOnUIError("createUI() failed for " + component + " " + e); - } - } - } - - return null; - } - - // this is how core UIDefaults yells about bad components; we do the same - private static void barkOnUIError(String message) { - System.err.println(message); - new Error().printStackTrace(); - } - - /** - * With applets, if you reload the current applet, the UIManager will be reinitialized (entries - * previously added by LookAndFeelAddons will be removed) but the addon will not reinitialize - * because addon initialize itself through the static block in components and the classes do not - * get reloaded. This means component.updateUI will fail because it will not find its UI. - * - * This method ensures LookAndFeelAddons get re-initialized if needed. It must be called in - * every component updateUI methods. - */ - private static synchronized void maybeInitialize() { - if (currentAddon != null) { - // this is to ensure "UIManager#maybeInitialize" gets called and the - // LAFState initialized - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - // if (!UIManager.getBoolean(APPCONTEXT_INITIALIZED)) { - // JW: trying to fix #784-swingx: frequent NPE in getUI - // moved the "marker" property into the LookAndFeelDefaults - if (!defaults.getBoolean(APPCONTEXT_INITIALIZED)) { - setAddon(currentAddon); - } - } - } - - // - // TRACKING OF THE CURRENT LOOK AND FEEL - // - private static class UpdateAddon implements PropertyChangeListener { - @Override - public void propertyChange(PropertyChangeEvent evt) { - try { - setAddon(getBestMatchAddonClassName()); - } catch (Exception e) { - // should not happen - throw new RuntimeException(e); - } - } - } - - /** - * If true, everytime the Swing look and feel is changed, the addon which best matches the - * current look and feel will be automatically selected. - * - * @param tracking - * true to automatically update the addon, false to not automatically track the - * addon. Defaults to false. - * @see #getBestMatchAddonClassName() - */ - public static synchronized void setTrackingLookAndFeelChanges(boolean tracking) { - if (trackingChanges != tracking) { - if (tracking) { - if (changeListener == null) { - changeListener = new UpdateAddon(); - } - UIManager.addPropertyChangeListener(changeListener); - } else { - if (changeListener != null) { - UIManager.removePropertyChangeListener(changeListener); - } - changeListener = null; - } - trackingChanges = tracking; - } - } - - /** - * @return true if the addon will be automatically change to match the current look and feel - * @see #setTrackingLookAndFeelChanges(boolean) - */ - public static synchronized boolean isTrackingLookAndFeelChanges() { - return trackingChanges; - } - - /** - * Convenience method for setting a component's background painter property with a value from - * the defaults. The painter is only set if the painter is {@code null} or an instance of - * {@code UIResource}. - * - * @param c - * component to set the painter on - * @param painter - * key specifying the painter - * @throws NullPointerException - * if the component or painter is {@code null} - * @throws IllegalArgumentException - * if the component does not contain the "backgroundPainter" property or the - * property cannot be set - */ - public static void installBackgroundPainter(T c, String painter) { - Painter p = c.getBackgroundPainter(); - - if (p == null || p instanceof UIResource) { - c.setBackgroundPainter(UIManagerExt.getPainter(painter)); - } - } - - /** - * Convenience method for uninstalling a background painter. If the painter of the component is - * a {@code UIResource}, it is set to {@code null}. - * - * @param c - * component to uninstall the painter on - * @throws NullPointerException - * if {@code c} is {@code null} - * @throws IllegalArgumentException - * if the component does not contain the "backgroundPainter" property or the - * property cannot be set - */ - public static void uninstallBackgroundPainter(T c) { - Painter p = c.getBackgroundPainter(); - - if (p == null || p instanceof UIResource) { - c.setBackgroundPainter(null); - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/LookAndFeelUtils.java b/src/main/java/org/jdesktop/swingx/plaf/LookAndFeelUtils.java deleted file mode 100644 index af37b9eaf3..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/LookAndFeelUtils.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * $Id$ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.plaf; - -import javax.swing.plaf.ComponentUI; -import javax.swing.plaf.UIResource; -import java.awt.*; - -/** - * Collection of helpers. Could move to LookAndFeelAddon? - * - * @author Jeanette Winzenburg - */ -public class LookAndFeelUtils { - - /** - * Returns the ui that is of type klass, or null if - * one can not be found. - */ - public static Object getUIOfType(ComponentUI ui, Class klass) { - if (klass.isInstance(ui)) { - return ui; - } - return null; - } - - /** - * Returns a Font based on the param which is not of type UIResource. - * - * @param font the base font - * @return a font not of type UIResource, may be null. - */ - public static Font getAsNotUIResource(Font font) { - if (!(font instanceof UIResource)) return font; - // PENDING JW: correct way to create another font instance? - return font.deriveFont(font.getAttributes()); - } - - /** - * Returns a Color based on the param which is not of type UIResource. - * - * @param color the base color - * @return a color not of type UIResource, may be null. - */ - public static Color getAsNotUIResource(Color color) { - if (!(color instanceof UIResource)) return color; - // PENDING JW: correct way to create another color instance? - float[] rgb = color.getRGBComponents(null); - return new Color(rgb[0], rgb[1], rgb[2], rgb[3]); - } - - -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/MonthViewAddon.java b/src/main/java/org/jdesktop/swingx/plaf/MonthViewAddon.java deleted file mode 100644 index f83a121613..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/MonthViewAddon.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf; - -import org.jdesktop.swingx.JXMonthView; - -import javax.swing.*; -import javax.swing.plaf.ColorUIResource; -import javax.swing.plaf.FontUIResource; -import java.awt.*; - -public class MonthViewAddon extends AbstractComponentAddon { - public MonthViewAddon() { - super("JXMonthView"); - } - - /** - * {@inheritDoc} - */ - @Override - protected void addBasicDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addBasicDefaults(addon, defaults); - - defaults.add(JXMonthView.uiClassID, "org.jdesktop.swingx.plaf.basic.BasicMonthViewUI"); - defaults.add("JXMonthView.background", new ColorUIResource(Color.WHITE)); - defaults.add("JXMonthView.monthStringBackground", new ColorUIResource(138, 173, 209)); - defaults.add("JXMonthView.monthStringForeground", new ColorUIResource(68, 68, 68)); - defaults.add("JXMonthView.daysOfTheWeekForeground", new ColorUIResource(68, 68, 68)); - defaults.add("JXMonthView.weekOfTheYearForeground", new ColorUIResource(68, 68, 68)); - defaults.add("JXMonthView.unselectableDayForeground", new ColorUIResource(Color.RED)); - defaults.add("JXMonthView.selectedBackground", new ColorUIResource(197, 220, 240)); - defaults.add("JXMonthView.flaggedDayForeground", new ColorUIResource(Color.RED)); - defaults.add("JXMonthView.leadingDayForeground", new ColorUIResource(Color.LIGHT_GRAY)); - defaults.add("JXMonthView.trailingDayForeground", new ColorUIResource(Color.LIGHT_GRAY)); - defaults.add("JXMonthView.font", UIManagerExt.getSafeFont("Button.font", - new FontUIResource("Dialog", Font.PLAIN, 12))); - defaults.add("JXMonthView.monthDownFileName", - LookAndFeel.makeIcon(MonthViewAddon.class, "basic/resources/month-down.png")); - defaults.add("JXMonthView.monthUpFileName", - LookAndFeel.makeIcon(MonthViewAddon.class, "basic/resources/month-up.png")); - defaults.add("JXMonthView.boxPaddingX", 3); - defaults.add("JXMonthView.boxPaddingY", 3); - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/MonthViewUI.java b/src/main/java/org/jdesktop/swingx/plaf/MonthViewUI.java deleted file mode 100644 index 1b615ad916..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/MonthViewUI.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * $Id: MonthViewUI.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf; - -import javax.swing.plaf.ComponentUI; -import java.util.Date; - -public abstract class MonthViewUI extends ComponentUI { - - /** - * Returns an array of String to use as names for the days of the week. - * - * @return array of names for the days of the week. - */ - public abstract String[] getDaysOfTheWeek(); - - - /** - * Returns the Date at the given location. May be null if the - * coordinates don't map to a day in the month which contains the - * coordinates. Specifically: hitting leading/trailing dates returns null. - * - * Mapping pixel to calendar day. - * - * @param x the x position of the location in pixel - * @param y the y position of the location in pixel - * @return the day at the given location or null if the location - * doesn't map to a day in the month which contains the coordinates. - */ - public abstract Date getDayAtLocation(int x, int y); - - - - - /** - * Returns the last possible date that can be displayed. - * This is implemented by the UI since it is in control of layout - * and may possibly yeild different results based on implementation.

                    - * - * It's up to the UI to keep this property, based on internal state and - * the firstDisplayed as controlled by the JXMonthView. - * - * @return Date The date. - */ - public abstract Date getLastDisplayedDay(); - - - -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/MultiThumbSliderAddon.java b/src/main/java/org/jdesktop/swingx/plaf/MultiThumbSliderAddon.java deleted file mode 100644 index b977e7e39c..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/MultiThumbSliderAddon.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * $Id: MultiThumbSliderAddon.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf; - -import org.jdesktop.swingx.JXMultiThumbSlider; - -/** - * - * @author jm158417 - */ -public class MultiThumbSliderAddon extends AbstractComponentAddon { - - /** Creates a new instance of MultiThumbSliderAddon */ - public MultiThumbSliderAddon() { - super("JXMultiThumbSlider"); - } - - @Override - protected void addBasicDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addBasicDefaults(addon, defaults); - - defaults.add(JXMultiThumbSlider.uiClassID, - "org.jdesktop.swingx.plaf.basic.BasicMultiThumbSliderUI"); - } - -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/MultiThumbSliderUI.java b/src/main/java/org/jdesktop/swingx/plaf/MultiThumbSliderUI.java deleted file mode 100644 index 33253e311b..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/MultiThumbSliderUI.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * $Id: MultiThumbSliderUI.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf; - -import javax.swing.plaf.ComponentUI; - -/** - * - * @author Joshua Marinacci - */ -public abstract class MultiThumbSliderUI extends ComponentUI { - -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/PainterUIResource.java b/src/main/java/org/jdesktop/swingx/plaf/PainterUIResource.java deleted file mode 100644 index 5f26a65b70..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/PainterUIResource.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * $Id: PainterUIResource.java 4046 2011-07-19 18:45:40Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.plaf; - -import org.jdesktop.swingx.painter.Painter; - -import javax.swing.*; -import javax.swing.plaf.UIResource; -import java.awt.*; - -/** - * An implementation of Painter as a UIResource. UI classes that create Painters - * should use this class. - * - * @author rbair - * @author Karl George Schaefer - * @param a subclass of JComponent - */ -public class PainterUIResource implements Painter, UIResource { - private Painter p; - - /** - * Creates a new instance of PainterUIResource with the specified delegate - * painter. - * - * @param p - * the delegate painter - */ - public PainterUIResource(Painter p) { - this.p = p; - } - - /** - * {@inheritDoc} - */ - @Override - public void paint(Graphics2D g, T component, int width, int height) { - if (p != null) { - p.paint(g, component, width, height); - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/PromptTextAreaUI.java b/src/main/java/org/jdesktop/swingx/plaf/PromptTextAreaUI.java deleted file mode 100644 index 590ed709ef..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/PromptTextAreaUI.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.jdesktop.swingx.plaf; - -import javax.swing.*; -import javax.swing.plaf.TextUI; -import javax.swing.text.JTextComponent; - -/** - * {@link PromptTextUI} implementation for rendering prompts on - * {@link JTextArea}s and uses a {@link JTextArea} as a prompt component. - * - * @author Peter Weishapl - * - */ -public class PromptTextAreaUI extends PromptTextUI { - /** - * Shared prompt renderer. - */ - private final static JTextArea txt = new JTextArea(); - - /** - * Creates a new {@link PromptTextAreaUI}. - * - * @param delegate - */ - public PromptTextAreaUI(TextUI delegate) { - super(delegate); - } - - /** - * Overrides {@link #getPromptComponent(JTextComponent)} to additionally - * update {@link JTextArea} specific properties. - */ - @Override - public JTextComponent getPromptComponent(JTextComponent txt) { - JTextArea lbl = (JTextArea) super.getPromptComponent(txt); - JTextArea txtArea = (JTextArea) txt; - - lbl.setColumns(txtArea.getColumns()); - lbl.setRows(txtArea.getRows()); - - return lbl; - } - - /** - * Returns a shared {@link JTextArea}. - */ - @Override - protected JTextComponent createPromptComponent() { - txt.updateUI(); - return txt; - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/PromptTextFieldUI.java b/src/main/java/org/jdesktop/swingx/plaf/PromptTextFieldUI.java deleted file mode 100644 index d61db8f2e3..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/PromptTextFieldUI.java +++ /dev/null @@ -1,187 +0,0 @@ -package org.jdesktop.swingx.plaf; - -import org.apache.commons.lang3.SystemUtils; -import org.jdesktop.swingx.search.NativeSearchFieldSupport; - -import javax.swing.*; -import javax.swing.border.Border; -import javax.swing.plaf.TextUI; -import javax.swing.text.JTextComponent; -import java.awt.*; - -import static javax.swing.BorderFactory.createEmptyBorder; - -/** - * {@link PromptTextUI} implementation for rendering prompts on - * {@link JTextField}s and uses a {@link JTextField} as a prompt component. - * - * @author Peter Weishapl - * - */ -public class PromptTextFieldUI extends PromptTextUI { - /** - * Creates a new {@link PromptTextFieldUI}. - * - * @param delegate - */ - public PromptTextFieldUI(TextUI delegate) { - super(delegate); - } - - /** - * Overrides {@link #getPromptComponent(JTextComponent)} to additionally - * update {@link JTextField} specific properties. - */ - @Override - public JTextComponent getPromptComponent(JTextComponent txt) { - LabelField lbl = (LabelField) super.getPromptComponent(txt); - JTextField txtField = (JTextField) txt; - - lbl.setHorizontalAlignment(txtField.getHorizontalAlignment()); - lbl.setColumns(txtField.getColumns()); - - // Make search field in Leopard paint focused border. - lbl.hasFocus = txtField.hasFocus() - && NativeSearchFieldSupport.isNativeSearchField(txtField); - - // leopard client properties. see - // http://developer.apple.com/technotes/tn2007/tn2196.html#JTEXTFIELD_VARIANT - NativeSearchFieldSupport.setSearchField(lbl, NativeSearchFieldSupport - .isSearchField(txtField)); - NativeSearchFieldSupport.setFindPopupMenu(lbl, NativeSearchFieldSupport - .getFindPopupMenu(txtField)); - - // here we need to copy the border again for Mac OS X, because the above - // calls may have replaced it. - Border b = txt.getBorder(); - - if (b == null) { - lbl.setBorder(txt.getBorder()); - } else { - Insets insets = b.getBorderInsets(txt); - lbl.setBorder( - createEmptyBorder(insets.top, insets.left, insets.bottom, insets.right)); - } - // lbl.setBorder(txtField.getBorder()); - - // buddy support: not needed, because BuddyLayoutAndBorder queries - // original text field - // BuddySupport.setOuterMargin(lbl, - // BuddySupport.getOuterMargin(txtField)); - // BuddySupport.setLeft(lbl, BuddySupport.getLeft(txtField)); - // BuddySupport.setRight(lbl, BuddySupport.getRight(txtField)); - - return lbl; - } - - /** - * Returns a shared {@link JTextField}. - */ - @Override - protected JTextComponent createPromptComponent() { - return new LabelField(); - } - - private static final class LabelField extends JTextField { - boolean hasFocus; - - @Override - public boolean hasFocus() { - return hasFocus; - } - - /** - * {@inheritDoc}

                    - * - * Overridden to not automatically de/register itself from/to the ToolTipManager. - * As rendering component it is not considered to be active in any way, so the - * manager must not listen. - */ - @Override - public void setToolTipText(String text) { - putClientProperty(TOOL_TIP_TEXT_KEY, text); - } - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - * - * @since 1.5 - */ - @Override - public void invalidate() {} - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void validate() {} - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void revalidate() {} - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void repaint(long tm, int x, int y, int width, int height) {} - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void repaint(Rectangle r) { } - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - * - * @since 1.5 - */ - @Override - public void repaint() { - } - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { - if (SystemUtils.IS_OS_MAC_OSX) { - super.firePropertyChange(propertyName, oldValue, newValue); - } else { - // Strings get interned... - if ("document".equals(propertyName)) { - super.firePropertyChange(propertyName, oldValue, newValue); - } - } - } - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { - if (SystemUtils.IS_OS_MAC_OSX) { - super.firePropertyChange(propertyName, oldValue, newValue); - } - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/PromptTextUI.java b/src/main/java/org/jdesktop/swingx/plaf/PromptTextUI.java deleted file mode 100644 index 5079b68ec6..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/PromptTextUI.java +++ /dev/null @@ -1,457 +0,0 @@ -package org.jdesktop.swingx.plaf; - -import org.jdesktop.swingx.painter.Painter; -import org.jdesktop.swingx.prompt.PromptSupport; -import org.jdesktop.swingx.prompt.PromptSupport.FocusBehavior; - -import javax.accessibility.Accessible; -import javax.swing.*; -import javax.swing.border.Border; -import javax.swing.plaf.ComponentUI; -import javax.swing.plaf.TextUI; -import javax.swing.text.*; -import javax.swing.text.DefaultHighlighter.DefaultHighlightPainter; -import javax.swing.text.Position.Bias; -import java.awt.Component.BaselineResizeBehavior; -import java.awt.*; -import java.awt.event.FocusAdapter; -import java.awt.event.FocusEvent; -import java.lang.reflect.Method; - -import static javax.swing.BorderFactory.createEmptyBorder; - -/** - *

                    - * Abstract {@link TextUI} class that delegates most work to another - * {@link TextUI} and additionally renders a prompt text as specified in the - * {@link JTextComponent}s client properties by {@link PromptSupport}. - *

                    - * Subclasses of this class must provide a prompt component used for rendering - * the prompt text. - *

                    - * - * @author Peter Weishapl - * - */ -public abstract class PromptTextUI extends TextUI { - protected class PainterHighlighter implements Highlighter { - private final Painter painter; - - private JTextComponent c; - - public PainterHighlighter(Painter painter) { - this.painter = painter; - } - - /** - * {@inheritDoc} - */ - @Override - public Object addHighlight(int p0, int p1, HighlightPainter p) - throws BadLocationException { - return new Object(); - } - - /** - * {@inheritDoc} - */ - @Override - public void changeHighlight(Object tag, int p0, int p1) - throws BadLocationException { - - } - - /** - * {@inheritDoc} - */ - @Override - public void deinstall(JTextComponent c) { - c = null; - } - - /** - * {@inheritDoc} - */ - @Override - public Highlight[] getHighlights() { - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public void install(JTextComponent c) { - this.c = c; - } - - /** - * {@inheritDoc} - */ - @Override - public void paint(Graphics g) { - Graphics2D g2d = (Graphics2D) g.create(); - - try { - painter.paint(g2d, c, c.getWidth(), c.getHeight()); - } finally { - g2d.dispose(); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void removeAllHighlights() { - // TODO Auto-generated method stub - - } - - /** - * {@inheritDoc} - */ - @Override - public void removeHighlight(Object tag) { - // TODO Auto-generated method stub - - } - } - - static final FocusHandler focusHandler = new FocusHandler(); - - /** - * Delegate the hard work to this object. - */ - protected final TextUI delegate; - - /** - * This component ist painted when rendering the prompt text. - */ - protected JTextComponent promptComponent; - - /** - * Creates a new {@link PromptTextUI} which delegates most work to another - * {@link TextUI}. - * - * @param delegate - */ - public PromptTextUI(TextUI delegate) { - this.delegate = delegate; - } - - /** - * Creates a component which should be used to render the prompt text. - * - * @return - */ - protected abstract JTextComponent createPromptComponent(); - - /** - * Calls TextUI#installUI(JComponent) on the delegate and installs a focus - * listener on c which repaints the component when it gains or - * loses the focus. - */ - @Override - public void installUI(JComponent c) { - delegate.installUI(c); - - JTextComponent txt = (JTextComponent) c; - - // repaint to correctly highlight text if FocusBehavior is - // HIGHLIGHT_LABEL in Metal and Windows LnF - txt.addFocusListener(focusHandler); - } - - /** - * Delegates, then uninstalls the focus listener. - */ - @Override - public void uninstallUI(JComponent c) { - delegate.uninstallUI(c); - c.removeFocusListener(focusHandler); - promptComponent = null; - } - - /** - * Creates a label component, if none has already been created. Sets the - * prompt components properties to reflect the given {@link JTextComponent}s - * properties and returns it. - * - * @param txt - * @return the adjusted prompt component - */ - public JTextComponent getPromptComponent(JTextComponent txt) { - if (promptComponent == null) { - promptComponent = createPromptComponent(); - } - if (txt.isFocusOwner() - && PromptSupport.getFocusBehavior(txt) == FocusBehavior.HIDE_PROMPT) { - promptComponent.setText(null); - } else { - promptComponent.setText(PromptSupport.getPrompt(txt)); - } - - promptComponent.getHighlighter().removeAllHighlights(); - if (txt.isFocusOwner() - && PromptSupport.getFocusBehavior(txt) == FocusBehavior.HIGHLIGHT_PROMPT) { - promptComponent.setForeground(txt.getSelectedTextColor()); - try { - promptComponent.getHighlighter().addHighlight(0, - promptComponent.getText().length(), - new DefaultHighlightPainter(txt.getSelectionColor())); - } catch (BadLocationException e) { - e.printStackTrace(); - } - } else { - promptComponent.setForeground(PromptSupport.getForeground(txt)); - } - - if (PromptSupport.getFontStyle(txt) == null) { - promptComponent.setFont(txt.getFont()); - } else { - promptComponent.setFont(txt.getFont().deriveFont( - PromptSupport.getFontStyle(txt))); - } - - promptComponent.setBackground(PromptSupport.getBackground(txt)); - promptComponent.setHighlighter(new PainterHighlighter(PromptSupport - .getBackgroundPainter(txt))); - promptComponent.setEnabled(txt.isEnabled()); - promptComponent.setOpaque(txt.isOpaque()); - promptComponent.setBounds(txt.getBounds()); - Border b = txt.getBorder(); - - if (b == null) { - promptComponent.setBorder(txt.getBorder()); - } else { - Insets insets = b.getBorderInsets(txt); - promptComponent.setBorder( - createEmptyBorder(insets.top, insets.left, insets.bottom, insets.right)); - } - - promptComponent.setSelectedTextColor(txt.getSelectedTextColor()); - promptComponent.setSelectionColor(txt.getSelectionColor()); - promptComponent.setEditable(txt.isEditable()); - promptComponent.setMargin(txt.getMargin()); - - return promptComponent; - } - - /** - * When {@link #shouldPaintPrompt(JTextComponent)} returns true, the prompt - * component is retrieved by calling - * {@link #getPromptComponent(JTextComponent)} and it's preferred size is - * returned. Otherwise super{@link #getPreferredSize(JComponent)} is called. - */ - @Override - public Dimension getPreferredSize(JComponent c) { - JTextComponent txt = (JTextComponent) c; - if (shouldPaintPrompt(txt)) { - return getPromptComponent(txt).getPreferredSize(); - } - return delegate.getPreferredSize(c); - } - - /** - * Delegates painting when {@link #shouldPaintPrompt(JTextComponent)} - * returns false. Otherwise the prompt component is retrieved by calling - * {@link #getPromptComponent(JTextComponent)} and painted. Then the caret - * of the given text component is painted. - */ - @Override - public void paint(Graphics g, final JComponent c) { - JTextComponent txt = (JTextComponent) c; - - if (shouldPaintPrompt(txt)) { - paintPromptComponent(g, txt); - } else { - delegate.paint(g, c); - } - } - - protected void paintPromptComponent(Graphics g, JTextComponent txt) { - JTextComponent lbl = getPromptComponent(txt); - SwingUtilities.paintComponent(g, lbl, txt, 0, 0, txt.getWidth(), txt.getHeight()); - - if (txt.getCaret() != null) { - txt.getCaret().paint(g); - } - } - - /** - * Returns if the prompt or the text field should be painted, depending on - * the state of txt. - * - * @param txt - * @return true when txt contains not text, otherwise false - */ - public boolean shouldPaintPrompt(JTextComponent txt) { - return txt.getText() == null || txt.getText().length() == 0; - } - - /** - * Calls super.{@link #update(Graphics, JComponent)}, which in turn calls - * the paint method of this object. - */ - @Override - public void update(Graphics g, JComponent c) { - if (shouldPaintPrompt((JTextComponent) c)) { - super.update(g, c); - } else { - delegate.update(g, c); - } - } - - /** - * Delegate when {@link #shouldPaintPrompt(JTextComponent)} returns false. - * Otherwise get the prompt component's UI and delegate to it. This ensures - * that the {@link Caret} is painted on the correct position (this is - * important when the text is centered, so that the caret will not be - * painted inside the label text) - */ - @Override - public Rectangle modelToView(JTextComponent t, int pos, Bias bias) - throws BadLocationException { - if (shouldPaintPrompt(t)) { - return getPromptComponent(t).getUI().modelToView(t, pos, bias); - } else { - return delegate.modelToView(t, pos, bias); - } - } - - /** - * Calls {@link #modelToView(JTextComponent, int, Bias)} with - * {@link Bias#Forward}. - */ - @Override - public Rectangle modelToView(JTextComponent t, int pos) - throws BadLocationException { - return modelToView(t, pos, Bias.Forward); - } - - // ********************* Delegate methods *************************/// - // ****************************************************************/// - - @Override - public boolean contains(JComponent c, int x, int y) { - return delegate.contains(c, x, y); - } - - @Override - public void damageRange(JTextComponent t, int p0, int p1, Bias firstBias, - Bias secondBias) { - delegate.damageRange(t, p0, p1, firstBias, secondBias); - } - - @Override - public void damageRange(JTextComponent t, int p0, int p1) { - delegate.damageRange(t, p0, p1); - } - - @Override - public boolean equals(Object obj) { - return delegate.equals(obj); - } - - @Override - public Accessible getAccessibleChild(JComponent c, int i) { - return delegate.getAccessibleChild(c, i); - } - - @Override - public int getAccessibleChildrenCount(JComponent c) { - return delegate.getAccessibleChildrenCount(c); - } - - @Override - public EditorKit getEditorKit(JTextComponent t) { - return delegate.getEditorKit(t); - } - - @Override - public Dimension getMaximumSize(JComponent c) { - return delegate.getMaximumSize(c); - } - - @Override - public Dimension getMinimumSize(JComponent c) { - return delegate.getMinimumSize(c); - } - - @Override - public int getNextVisualPositionFrom(JTextComponent t, int pos, Bias b, - int direction, Bias[] biasRet) throws BadLocationException { - return delegate - .getNextVisualPositionFrom(t, pos, b, direction, biasRet); - } - - @Override - public View getRootView(JTextComponent t) { - return delegate.getRootView(t); - } - - @Override - public String getToolTipText(JTextComponent t, Point pt) { - return delegate.getToolTipText(t, pt); - } - - @Override - public int hashCode() { - return delegate.hashCode(); - } - - @Override - public String toString() { - return String.format("%s (%s)", getClass().getName(), delegate - .toString()); - } - - @Override - public int viewToModel(JTextComponent t, Point pt, Bias[] biasReturn) { - return delegate.viewToModel(t, pt, biasReturn); - } - - @Override - public int viewToModel(JTextComponent t, Point pt) { - return delegate.viewToModel(t, pt); - } - - /** - * Tries to call {@link ComponentUI#getBaseline(int, int)} on the delegate - * via Reflection. Workaround to maintain compatibility with Java 5. Ideally - * we should also override {@link #getBaselineResizeBehavior(JComponent)}, - * but that's impossible since the {@link BaselineResizeBehavior} class, - * which does not exist in Java 5, is involved. - * - * @return the baseline, or -2 if getBaseline could not be - * invoked on the delegate. - */ - @Override - public int getBaseline(JComponent c, int width, int height) { - try { - Method m = delegate.getClass().getMethod("getBaseline", - JComponent.class, int.class, int.class); - Object o = m.invoke(delegate, new Object[] { c, width, height }); - return (Integer) o; - } catch (Exception ex) { - // ignore - return -2; - } - } - - /** - * Repaint the {@link TextComponent} when it loses or gains the focus. - */ - private static final class FocusHandler extends FocusAdapter { - @Override - public void focusGained(FocusEvent e) { - e.getComponent().repaint(); - } - - @Override - public void focusLost(FocusEvent e) { - e.getComponent().repaint(); - } - } -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/plaf/SafeBorder.java b/src/main/java/org/jdesktop/swingx/plaf/SafeBorder.java deleted file mode 100644 index 4bfa0e18f3..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/SafeBorder.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Created on 04.11.2010 - * - */ -package org.jdesktop.swingx.plaf; - -import javax.swing.border.AbstractBorder; -import javax.swing.plaf.UIResource; -import java.awt.*; -import java.awt.Component.BaselineResizeBehavior; - -/** - * Wrapper around a delegate with the same behaviour as the delegate except that - * it catches null insets (hack around Issue 1297-swingx which is core bug - * 6739738) - */ -public class SafeBorder extends AbstractBorder implements UIResource { - - private AbstractBorder delegate; - - public SafeBorder(AbstractBorder delegate) { - this.delegate = delegate; - } - - /** - * {@inheritDoc} - */ - @Override - public int getBaseline(Component c, int width, int height) { - return delegate.getBaseline(c, width, height); - } - - /** - * {@inheritDoc} - */ - @Override - public BaselineResizeBehavior getBaselineResizeBehavior(Component c) { - return delegate.getBaselineResizeBehavior(c); - } - - /** - * {@inheritDoc} - */ - @Override - public Insets getBorderInsets(Component c, Insets insets) { - Insets result = delegate.getBorderInsets(c, safeInsets(insets)); - return safeInsets(result); - } - - /** - * @param insets - * the insets to query - * @return the insets supplied or an empty insets if the value is {@code null} - */ - private Insets safeInsets(Insets insets) { - return insets != null ? insets : new Insets(0, 0, 0, 0); - } - - /** - * {@inheritDoc} - */ - @Override - public Insets getBorderInsets(Component c) { - Insets result = delegate.getBorderInsets(c); - return safeInsets(result); - } - - /** - * {@inheritDoc} - */ - @Override - public Rectangle getInteriorRectangle(Component c, int x, int y, int width, - int height) { - return delegate.getInteriorRectangle(c, x, y, width, height); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isBorderOpaque() { - return delegate.isBorderOpaque(); - } - - /** - * {@inheritDoc} - */ - @Override - public void paintBorder(Component c, Graphics g, int x, int y, int width, - int height) { - delegate.paintBorder(c, g, x, y, width, height); - } - - -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/SearchFieldAddon.java b/src/main/java/org/jdesktop/swingx/plaf/SearchFieldAddon.java deleted file mode 100644 index b64b24cf67..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/SearchFieldAddon.java +++ /dev/null @@ -1,123 +0,0 @@ -package org.jdesktop.swingx.plaf; - -import org.jdesktop.swingx.JXSearchField.LayoutStyle; - -import javax.swing.*; -import javax.swing.plaf.IconUIResource; -import javax.swing.plaf.InsetsUIResource; -import java.awt.*; -import java.net.URL; - -@SuppressWarnings("nls") -public class SearchFieldAddon extends AbstractComponentAddon { - public static final String SEARCH_FIELD_SOURCE = "searchField"; - public static final String BUTTON_SOURCE = "button"; - - public SearchFieldAddon() { - super("JXSearchField"); - } - - @Override - protected void addBasicDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addBasicDefaults(addon, defaults); - defaults.add("SearchField.layoutStyle", LayoutStyle.MAC); - defaults.add("SearchField.icon", getIcon("basic/resources/search.gif")); - defaults.add("SearchField.rolloverIcon", getIcon("basic/resources/search_rollover.gif")); - defaults.add("SearchField.pressedIcon", getIcon("basic/resources/search.gif")); - defaults.add("SearchField.popupIcon", getIcon("basic/resources/search_popup.gif")); - defaults.add("SearchField.popupRolloverIcon", getIcon("basic/resources/search_popup_rollover.gif")); - defaults.add("SearchField.clearIcon", getIcon("basic/resources/clear.gif")); - defaults.add("SearchField.clearRolloverIcon", getIcon("basic/resources/clear_rollover.gif")); - defaults.add("SearchField.clearPressedIcon", getIcon("basic/resources/clear_pressed.gif")); - defaults.add("SearchField.buttonMargin", new InsetsUIResource(1, 1, 1, 1)); - defaults.add("SearchField.popupSource", BUTTON_SOURCE); - - //webstart fix - UIManagerExt.addResourceBundle("org.jdesktop.swingx.plaf.basic.resources.SearchField"); -// UIManager.getDefaults().addResourceBundle("org.jdesktop.swingx.plaf.basic.resources.SearchField"); - } - - @Override - protected void addMetalDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addMetalDefaults(addon, defaults); - - defaults.add("SearchField.buttonMargin", new InsetsUIResource(0, 0, 1, 1)); - } - - @Override - protected void addWindowsDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addWindowsDefaults(addon, defaults); - - defaults.add("SearchField.promptFontStyle", Font.ITALIC); - defaults.add("SearchField.layoutStyle", LayoutStyle.VISTA); - defaults.add("SearchField.icon", getIcon("windows/resources/search.gif")); - defaults.add("SearchField.rolloverIcon", getIcon("windows/resources/search_rollover.gif")); - defaults.add("SearchField.pressedIcon", getIcon("windows/resources/search_pressed.gif")); - defaults.add("SearchField.popupIcon", getIcon("windows/resources/search_popup.gif")); - defaults.add("SearchField.popupRolloverIcon", getIcon("windows/resources/search_popup_rollover.gif")); - defaults.add("SearchField.popupPressedIcon", getIcon("windows/resources/search_popup_pressed.gif")); - defaults.add("SearchField.clearIcon", getIcon("windows/resources/clear.gif")); - defaults.add("SearchField.clearRolloverIcon", getIcon("windows/resources/clear_rollover.gif")); - defaults.add("SearchField.clearPressedIcon", getIcon("windows/resources/clear_pressed.gif")); - defaults.add("SearchField.useSeperatePopupButton", Boolean.TRUE); - defaults.add("SearchField.popupOffset", -1); - - // Do it like 'Windows Media Player' in XP: - // Replace the border line with the search button line on rollover. - // But not in classic mode! - if (UIManager.getLookAndFeel().getClass().getName().indexOf("Classic") == -1) { - defaults.add("SearchField.buttonMargin", new InsetsUIResource(0, -1, 0, -1)); - } else { - defaults.add("SearchField.buttonMargin", new InsetsUIResource(0, 0, 0, 0)); - } - } - - @Override - protected void addMotifDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addMotifDefaults(addon, defaults); - - defaults.add("SearchField.icon", getIcon("macosx/resources/search.png")); - defaults.add("SearchField.rolloverIcon", getIcon("macosx/resources/search.png")); - defaults.add("SearchField.pressedIcon", getIcon("macosx/resources/search.png")); - defaults.add("SearchField.popupIcon", getIcon("macosx/resources/search_popup.png")); - defaults.add("SearchField.popupRolloverIcon", getIcon("macosx/resources/search_popup.png")); - defaults.add("SearchField.popupPressedIcon", getIcon("macosx/resources/search_popup.png")); - defaults.add("SearchField.clearIcon", getIcon("macosx/resources/clear.png")); - defaults.add("SearchField.clearRolloverIcon", getIcon("macosx/resources/clear_rollover.png")); - defaults.add("SearchField.clearPressedIcon", getIcon("macosx/resources/clear_pressed.png")); - } - - @Override - protected void addMacDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addMacDefaults(addon, defaults); - - defaults.add("SearchField.icon", getIcon("macosx/resources/search.png")); - defaults.add("SearchField.rolloverIcon", getIcon("macosx/resources/search.png")); - defaults.add("SearchField.pressedIcon", getIcon("macosx/resources/search.png")); - defaults.add("SearchField.popupIcon", getIcon("macosx/resources/search_popup.png")); - defaults.add("SearchField.popupRolloverIcon", getIcon("macosx/resources/search_popup.png")); - defaults.add("SearchField.popupPressedIcon", getIcon("macosx/resources/search_popup.png")); - defaults.add("SearchField.clearIcon", getIcon("macosx/resources/clear.png")); - defaults.add("SearchField.clearRolloverIcon", getIcon("macosx/resources/clear_rollover.png")); - defaults.add("SearchField.clearPressedIcon", getIcon("macosx/resources/clear_pressed.png")); - defaults.add("SearchField.buttonMargin", new InsetsUIResource(0, 0, 0, 0)); - defaults.add("SearchField.popupSource", SEARCH_FIELD_SOURCE); - } - - // Workaround: Only return true, when the current LnF is Windows or PlasticXP. - @Override - protected boolean isWindows(LookAndFeelAddons addon) { - return super.isWindows(addon) - || UIManager.getLookAndFeel().getClass().getName().indexOf("Windows") != -1 - || UIManager.getLookAndFeel().getClass().getName().indexOf("PlasticXP") != -1; - } - - private IconUIResource getIcon(String resourceName) { - URL url = getClass().getResource(resourceName); - if (url == null) { - return null; - } else { - return new IconUIResource(new ImageIcon(url)); - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/SearchFieldUI.java b/src/main/java/org/jdesktop/swingx/plaf/SearchFieldUI.java deleted file mode 100644 index 9280e5b768..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/SearchFieldUI.java +++ /dev/null @@ -1,482 +0,0 @@ -package org.jdesktop.swingx.plaf; - -import org.jdesktop.swingx.JXSearchField; -import org.jdesktop.swingx.JXSearchField.LayoutStyle; -import org.jdesktop.swingx.prompt.BuddySupport; -import org.jdesktop.swingx.search.NativeSearchFieldSupport; - -import javax.swing.*; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; -import javax.swing.plaf.TextUI; -import javax.swing.plaf.UIResource; -import javax.swing.text.Document; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; - -/** - * The default {@link JXSearchField} UI delegate. - * - * @author Peter Weishapl - * - */ -public class SearchFieldUI extends BuddyTextFieldUI { - /** - * The search field that we're a UI delegate for. Initialized by the - * installUI method, and reset to null by - * uninstallUI. - * - * @see #installUI - * @see #uninstallUI - */ - protected JXSearchField searchField; - - private Handler handler; - - public static final Insets NO_INSETS = new Insets(0, 0, 0, 0); - - public SearchFieldUI(TextUI delegate) { - super(delegate); - } - - private Handler getHandler() { - if (handler == null) { - handler = new Handler(); - } - return handler; - } - - /** - * Calls {@link #installDefaults()}, adds the search, clear and popup button - * to the search field and registers a {@link PropertyChangeListener} ad - * {@link DocumentListener} and an {@link ActionListener} on the popup - * button. - */ - @Override - public void installUI(JComponent c) { - searchField = (JXSearchField) c; - - super.installUI(c); - - installDefaults(); - layoutButtons(); - - configureListeners(); - } - - private void configureListeners() { - if (isNativeSearchField()) { - popupButton().removeActionListener(getHandler()); - searchField.removePropertyChangeListener(getHandler()); - } else { - popupButton().addActionListener(getHandler()); - searchField.addPropertyChangeListener(getHandler()); - } - - // add support for instant search mode in any case. - searchField.getDocument().addDocumentListener(getHandler()); - } - - private boolean isNativeSearchField() { - return NativeSearchFieldSupport.isNativeSearchField(searchField); - } - - @Override - protected BuddyLayoutAndBorder createBuddyLayoutAndBorder() { - return new BuddyLayoutAndBorder() { - /** - * This does nothing, if the search field is rendered natively on - * Leopard. - */ - @Override - protected void replaceBorderIfNecessary() { - if (!isNativeSearchField()) { - super.replaceBorderIfNecessary(); - } - } - - /** - * Return zero, when the search field is rendered natively on - * Leopard, to make painting work correctly. - */ - @Override - public Dimension preferredLayoutSize(Container parent) { - if (isNativeSearchField()) { - return new Dimension(); - } else { - return super.preferredLayoutSize(parent); - } - } - - /** - * Prevent 'jumping' when text is entered: Include the clear button, - * when layout style is Mac. When layout style is Vista: Take the - * clear button's preferred width if its either greater than the - * search button's pref. width or greater than the popup button's - * pref. width when a popup menu is installed and not using a - * seperate popup button. - */ - @Override - public Insets getBorderInsets(Component c) { - Insets insets = super.getBorderInsets(c); - if (searchField != null && !isNativeSearchField()) { - if (isMacLayoutStyle()) { - if (!clearButton().isVisible()) { - insets.right += clearButton().getPreferredSize().width; - } - } else { - JButton refButton = popupButton(); - if (searchField.getFindPopupMenu() == null - ^ searchField.isUseSeperatePopupButton()) { - refButton = searchButton(); - } - - int clearWidth = clearButton().getPreferredSize().width; - int refWidth = refButton.getPreferredSize().width; - int overSize = clearButton().isVisible() ? refWidth - - clearWidth : clearWidth - refWidth; - if (overSize > 0) { - insets.right += overSize; - } - } - - } - return insets; - } - }; - } - - private void layoutButtons() { - BuddySupport.removeAll(searchField); - - if (isNativeSearchField()) { - return; - } - - if (isMacLayoutStyle()) { - BuddySupport.addLeft(searchButton(), searchField); - } else { - BuddySupport.addRight(searchButton(), searchField); - } - - BuddySupport.addRight(clearButton(), searchField); - - if (usingSeperatePopupButton()) { - BuddySupport.addRight(BuddySupport.createGap(getPopupOffset()), - searchField); - } - - if (usingSeperatePopupButton() || !isMacLayoutStyle()) { - BuddySupport.addRight(popupButton(), searchField); - } else { - BuddySupport.addLeft(popupButton(), searchField); - } - } - - private boolean isMacLayoutStyle() { - return searchField.getLayoutStyle() == LayoutStyle.MAC; - } - - /** - * Initialize the search fields various properties based on the - * corresponding "SearchField.*" properties from defaults table. The - * {@link JXSearchField}s layout is set to the value returned by - * createLayout. Also calls {@link #replaceBorderIfNecessary()} - * and {@link #updateButtons()}. This method is called by - * {@link #installUI(JComponent)}. - * - * @see #installUI - * @see #createLayout - * @see JXSearchField#customSetUIProperty(String, Object) - */ - protected void installDefaults() { - if (isNativeSearchField()) { - return; - } - - if (UIManager.getBoolean("SearchField.useSeperatePopupButton")) { - searchField.customSetUIProperty("useSeperatePopupButton", - Boolean.TRUE); - } else { - searchField.customSetUIProperty("useSeperatePopupButton", - Boolean.FALSE); - } - - searchField.customSetUIProperty("layoutStyle", UIManager - .get("SearchField.layoutStyle")); - searchField.customSetUIProperty("promptFontStyle", UIManager - .get("SearchField.promptFontStyle")); - - if (shouldReplaceResource(searchField.getOuterMargin())) { - searchField.setOuterMargin(UIManager - .getInsets("SearchField.buttonMargin")); - } - - updateButtons(); - - if (shouldReplaceResource(clearButton().getIcon())) { - clearButton().setIcon(UIManager.getIcon("SearchField.clearIcon")); - } - if (shouldReplaceResource(clearButton().getPressedIcon())) { - clearButton().setPressedIcon( - UIManager.getIcon("SearchField.clearPressedIcon")); - } - if (shouldReplaceResource(clearButton().getRolloverIcon())) { - clearButton().setRolloverIcon( - UIManager.getIcon("SearchField.clearRolloverIcon")); - } - - searchButton().setIcon( - getNewIcon(searchButton().getIcon(), "SearchField.icon")); - - popupButton().setIcon( - getNewIcon(popupButton().getIcon(), "SearchField.popupIcon")); - popupButton().setRolloverIcon( - getNewIcon(popupButton().getRolloverIcon(), - "SearchField.popupRolloverIcon")); - popupButton().setPressedIcon( - getNewIcon(popupButton().getPressedIcon(), - "SearchField.popupPressedIcon")); - } - - /** - * Removes all installed listeners, the layout and resets the search field - * original border and removes all children. - */ - @Override - public void uninstallUI(JComponent c) { - super.uninstallUI(c); - - searchField.removePropertyChangeListener(getHandler()); - searchField.getDocument().removeDocumentListener(getHandler()); - popupButton().removeActionListener(getHandler()); - - searchField.setLayout(null); - searchField.removeAll(); - searchField = null; - } - - /** - * Returns true if o is null or of instance - * {@link UIResource}. - * - * @param o an object - * @return true if o is null or of instance - * {@link UIResource} - */ - protected boolean shouldReplaceResource(Object o) { - return o == null || o instanceof UIResource; - } - - /** - * Convience method for only replacing icons if they have not been - * customized by the user. Returns the icon from the defaults table - * belonging to resKey, if - * {@link #shouldReplaceResource(Object)} with the icon as a - * parameter returns true. Otherwise returns icon. - * - * @param icon the current icon - * @param resKey the resource key identifying the default icon - * @return the new icon - */ - protected Icon getNewIcon(Icon icon, String resKey) { - Icon uiIcon = UIManager.getIcon(resKey); - if (shouldReplaceResource(icon)) { - return uiIcon; - } - return icon; - } - - /** - * Convienence method. - * - * @see JXSearchField#getCancelButton() - * @return the clear button - */ - protected final JButton clearButton() { - return searchField.getCancelButton(); - } - - /** - * Convienence method. - * - * @see JXSearchField#getFindButton() - * @return the search button - */ - protected final JButton searchButton() { - return searchField.getFindButton(); - } - - /** - * Convienence method. - * - * @see JXSearchField#getPopupButton() - * @return the popup button - */ - protected final JButton popupButton() { - return searchField.getPopupButton(); - } - - /** - * Returns true if - * {@link JXSearchField#isUseSeperatePopupButton()} is true and - * a search popup menu has been set. - * - * @return the popup button is used in addition to the search button - */ - public boolean usingSeperatePopupButton() { - return searchField.isUseSeperatePopupButton() - && searchField.getFindPopupMenu() != null; - } - - /** - * Returns the number of pixels between the popup button and the clear (or - * search) button as specified in the default table by - * 'SearchField.popupOffset'. Returns 0 if - * {@link #usingSeperatePopupButton()} returns false - * - * @return number of pixels between the popup button and the clear (or - * search) button - */ - protected int getPopupOffset() { - if (usingSeperatePopupButton()) { - return UIManager.getInt("SearchField.popupOffset"); - } - return 0; - } - - /** - * Sets the visibility of the search, clear and popup buttons depending on - * the search mode, layout stye, search text, search popup menu and the use - * of a seperate popup button. Also resets the search buttons pressed and - * rollover icons if the search field is in regular search mode or clears - * the icons when the search field is in instant search mode. - */ - protected void updateButtons() { - clearButton().setVisible( - (!searchField.isRegularSearchMode() || searchField - .isMacLayoutStyle()) - && hasText()); - - boolean clearNotHere = (searchField.isMacLayoutStyle() || !clearButton() - .isVisible()); - - searchButton() - .setVisible( - (searchField.getFindPopupMenu() == null || usingSeperatePopupButton()) - && clearNotHere); - popupButton().setVisible( - searchField.getFindPopupMenu() != null - && (clearNotHere || usingSeperatePopupButton())); - - if (searchField.isRegularSearchMode()) { - searchButton().setRolloverIcon( - getNewIcon(searchButton().getRolloverIcon(), - "SearchField.rolloverIcon")); - searchButton().setPressedIcon( - getNewIcon(searchButton().getPressedIcon(), - "SearchField.pressedIcon")); - } else { - // no action, therefore no rollover icon. - if (shouldReplaceResource(searchButton().getRolloverIcon())) { - searchButton().setRolloverIcon(null); - } - if (shouldReplaceResource(searchButton().getPressedIcon())) { - searchButton().setPressedIcon(null); - } - } - } - - private boolean hasText() { - return searchField.getText() != null - && searchField.getText().length() > 0; - } - - class Handler implements PropertyChangeListener, ActionListener, - DocumentListener { - @Override - public void propertyChange(PropertyChangeEvent evt) { - String prop = evt.getPropertyName(); - Object src = evt.getSource(); - - if (src.equals(searchField)) { - if ("findPopupMenu".equals(prop) || "searchMode".equals(prop) - || "useSeperatePopupButton".equals(prop) - || "searchMode".equals(prop) - || "layoutStyle".equals(prop)) { - layoutButtons(); - updateButtons(); - } else if ("document".equals(prop)) { - Document doc = (Document) evt.getOldValue(); - if (doc != null) { - doc.removeDocumentListener(this); - } - doc = (Document) evt.getNewValue(); - if (doc != null) { - doc.addDocumentListener(this); - } - } - } - } - - /** - * Shows the search popup menu, if installed. - */ - @Override - public void actionPerformed(ActionEvent e) { - if (searchField.getFindPopupMenu() != null) { - Component src = SearchFieldAddon.SEARCH_FIELD_SOURCE - .equals(UIManager.getString("SearchField.popupSource")) ? searchField - : (Component) e.getSource(); - - Rectangle r = SwingUtilities.getLocalBounds(src); - int popupWidth = searchField.getFindPopupMenu() - .getPreferredSize().width; - int x = searchField.isVistaLayoutStyle() - || usingSeperatePopupButton() ? r.x + r.width - - popupWidth : r.x; - searchField.getFindPopupMenu().show(src, x, r.y + r.height); - } - } - - @Override - public void changedUpdate(DocumentEvent e) { - update(); - } - - @Override - public void insertUpdate(DocumentEvent e) { - update(); - } - - @Override - public void removeUpdate(DocumentEvent e) { - update(); - } - - /** - * Called when the search text changes. Calls - * {@link JXSearchField#postActionEvent()} In instant search mode or - * starts the search field instant search timer if the instant search - * delay is greater 0. - */ - private void update() { - if (searchField.isInstantSearchMode()) { - searchField.getInstantSearchTimer().stop(); - // only use timer when delay greater 0. - if (searchField.getInstantSearchDelay() > 0) { - searchField.getInstantSearchTimer().setInitialDelay( - searchField.getInstantSearchDelay()); - searchField.getInstantSearchTimer().start(); - } else { - searchField.postActionEvent(); - } - } - - updateButtons(); - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/ShapeUIResource.java b/src/main/java/org/jdesktop/swingx/plaf/ShapeUIResource.java deleted file mode 100644 index f3321c8d93..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/ShapeUIResource.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * $Id: ShapeUIResource.java 4028 2011-06-03 19:32:19Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.plaf; - -import javax.swing.plaf.UIResource; -import java.awt.*; -import java.awt.geom.AffineTransform; -import java.awt.geom.PathIterator; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; - -/** - * An implementation of Shape that implements UIResource. UI - * classes that create Shapes should use this class. - * - * @author rah003 - */ -public class ShapeUIResource implements Shape, UIResource { - private Shape s; - - /** Creates a new instance of PainterUIResource */ - public ShapeUIResource(Shape p) { - this.s = p; - } - - @Override - public boolean contains(Point2D p) { - return s.contains(p); - } - - @Override - public boolean contains(Rectangle2D r) { - return s.contains(r); - } - - @Override - public boolean contains(double x, double y) { - return s.contains(x, y); - } - - @Override - public boolean contains(double x, double y, double w, double h) { - return s.contains(x, y, w, h); - } - - @Override - public Rectangle getBounds() { - return s.getBounds(); - } - - @Override - public Rectangle2D getBounds2D() { - return s.getBounds2D(); - } - - @Override - public PathIterator getPathIterator(AffineTransform at) { - return s.getPathIterator(at); - } - - @Override - public PathIterator getPathIterator(AffineTransform at, double flatness) { - return s.getPathIterator(at, flatness); - } - - @Override - public boolean intersects(Rectangle2D r) { - return s.intersects(r); - } - - @Override - public boolean intersects(double x, double y, double w, double h) { - return s.intersects(x, y, w, h); - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/StatusBarAddon.java b/src/main/java/org/jdesktop/swingx/plaf/StatusBarAddon.java deleted file mode 100644 index 1e3d8c925b..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/StatusBarAddon.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * $Id: StatusBarAddon.java 3475 2009-08-28 08:30:47Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf; - -import org.jdesktop.swingx.JXStatusBar; -import org.jdesktop.swingx.plaf.windows.WindowsLookAndFeelAddons; -import org.jdesktop.swingx.util.OS; - -/** - * Addon for JXStatusBar.
                    - * - */ -public class StatusBarAddon extends AbstractComponentAddon { - - public StatusBarAddon() { - super("JXStatusBar"); - } - - @Override - protected void addBasicDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addBasicDefaults(addon, defaults); - - defaults.add(JXStatusBar.uiClassID, - "org.jdesktop.swingx.plaf.basic.BasicStatusBarUI"); - } - - @Override - protected void addMacDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addMacDefaults(addon, defaults); - - defaults.add(JXStatusBar.uiClassID, - "org.jdesktop.swingx.plaf.macosx.MacOSXStatusBarUI"); - } - - @Override - protected void addMetalDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addMetalDefaults(addon, defaults); - - defaults.add(JXStatusBar.uiClassID, - "org.jdesktop.swingx.plaf.metal.MetalStatusBarUI"); - } - - @Override - protected void addWindowsDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addWindowsDefaults(addon, defaults); - if (OS.isUsingWindowsVisualStyles()) { - defaults.add(JXStatusBar.uiClassID, - "org.jdesktop.swingx.plaf.windows.WindowsStatusBarUI"); - - String xpStyle = OS.getWindowsVisualStyle(); - - if (WindowsLookAndFeelAddons.SILVER_VISUAL_STYLE.equalsIgnoreCase(xpStyle) - || WindowsLookAndFeelAddons.VISTA_VISUAL_STYLE.equalsIgnoreCase(xpStyle)) { - defaults.add("StatusBar.leftImage", "resources/silver-statusbar-left.png"); - defaults.add("StatusBar.middleImage", "resources/silver-statusbar-middle.png"); - defaults.add("StatusBar.rightImage", "resources/silver-statusbar-right.png"); - } else { - defaults.add("StatusBar.leftImage", "resources/statusbar-left.png"); - defaults.add("StatusBar.middleImage", "resources/statusbar-middle.png"); - defaults.add("StatusBar.rightImage", "resources/statusbar-right.png"); - } - } else { - defaults.add(JXStatusBar.uiClassID, - "org.jdesktop.swingx.plaf.windows.WindowsClassicStatusBarUI"); - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/StatusBarUI.java b/src/main/java/org/jdesktop/swingx/plaf/StatusBarUI.java deleted file mode 100644 index 456c3812dd..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/StatusBarUI.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * $Id: StatusBarUI.java 3246 2009-02-04 15:43:14Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.plaf; - -import javax.swing.plaf.ComponentUI; - -/** - * Pluggable look and feel interface for StatusBar. - * - * @author rbair - */ -public abstract class StatusBarUI extends ComponentUI { -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/TableAddon.java b/src/main/java/org/jdesktop/swingx/plaf/TableAddon.java deleted file mode 100644 index c030478adc..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/TableAddon.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * $Id$ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.plaf; - -import javax.swing.*; -import javax.swing.border.AbstractBorder; -import javax.swing.border.Border; -import javax.swing.plaf.UIResource; -import java.awt.*; -import java.util.logging.Logger; - -/** - * TODO add type doc - * - * @author Jeanette Winzenburg - */ -public class TableAddon extends AbstractComponentAddon { - - @SuppressWarnings("unused") - private static final Logger LOG = Logger.getLogger(TableAddon.class - .getName()); - - /** - * @param name - */ - public TableAddon() { - super("JXTable"); - } - - - - @Override - protected void addNimbusDefaults(LookAndFeelAddons addon, - DefaultsList defaults) { - super.addNimbusDefaults(addon, defaults); - // JW: Hacking around core issue #6882917 - // which is the underlying reason for issue #1180-swingx - // (SwingX vs Nimbus table striping) - if (Boolean.TRUE.equals(UIManager.get("Nimbus.keepAlternateRowColor"))) return; - Object value = UIManager.getLookAndFeelDefaults().remove("Table.alternateRowColor"); - if (value instanceof Color) { - defaults.add("UIColorHighlighter.stripingBackground", value, false); - } - } - - - - /** - * @inherited

                    - * - * PENDING JW: move to addLinuxDefaults after testing - */ - @Override - protected void addBasicDefaults(LookAndFeelAddons addon, - DefaultsList defaults) { - super.addBasicDefaults(addon, defaults); - if (isGTK()) { - replaceListTableBorders(addon, defaults); - } - } - - private void replaceListTableBorders(LookAndFeelAddons addon, - DefaultsList defaults) { - replaceBorder(defaults, "Table.", "focusCellHighlightBorder"); - replaceBorder(defaults, "Table.", "focusSelectedCellHighlightBorder"); - replaceBorder(defaults, "Table.", "noFocusBorder"); - } - - - - /** - * @param defaults - * @param componentPrefix - * @param borderKey - */ - private void replaceBorder(DefaultsList defaults, String componentPrefix, - String borderKey) { - String key = componentPrefix + borderKey; - Border border = UIManager.getBorder(componentPrefix + borderKey); - if (border instanceof AbstractBorder && border instanceof UIResource - && border.getClass().getName().contains("ListTable")) { - border = new SafeBorder((AbstractBorder) border); - // PENDING JW: this is fishy ... adding to lookAndFeelDefaults is taken - UIManager.getLookAndFeelDefaults().put(key, border); - // adding to defaults is not -// defaults.add(key, border); - - } - } - - - - /** - * - * @return true if the LF is GTK. - */ - private boolean isGTK() { - return "GTK".equals(UIManager.getLookAndFeel().getID()); - } - - -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/TableHeaderAddon.java b/src/main/java/org/jdesktop/swingx/plaf/TableHeaderAddon.java deleted file mode 100644 index 6fcf451b18..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/TableHeaderAddon.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * $Id$ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.plaf; - -import javax.swing.*; -import javax.swing.border.Border; -import javax.swing.plaf.BorderUIResource; -import javax.swing.plaf.metal.MetalBorders; - -/** - * Addon for JXTableHeader. - * - * Implemented to hack around core issue ??: Metal header renderer appears squeezed. - * - * @author Jeanette Winzenburg - */ -public class TableHeaderAddon extends AbstractComponentAddon { - - /** - * @param name - */ - public TableHeaderAddon() { - super("JXTableHeader"); - } - - @Override - protected void addMetalDefaults(LookAndFeelAddons addon, - DefaultsList defaults) { - super.addMetalDefaults(addon, defaults); - String key = "TableHeader.cellBorder"; - Border border = UIManager.getBorder(key); - if (border instanceof MetalBorders.TableHeaderBorder) { - border = new BorderUIResource.CompoundBorderUIResource(border, - BorderFactory.createEmptyBorder()); - // PENDING JW: this is fishy ... adding to lookAndFeelDefaults is taken - UIManager.getLookAndFeelDefaults().put(key, border); - // adding to defaults is not -// defaults.add(key, border); - } - - } - - - -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/TaskPaneAddon.java b/src/main/java/org/jdesktop/swingx/plaf/TaskPaneAddon.java deleted file mode 100644 index c4485b16b2..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/TaskPaneAddon.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * $Id: TaskPaneAddon.java 3622 2010-02-25 04:34:55Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf; - -import org.jdesktop.swingx.JXTaskPane; -import org.jdesktop.swingx.plaf.windows.WindowsLookAndFeelAddons; -import org.jdesktop.swingx.util.OS; - -import javax.swing.*; -import javax.swing.plaf.ColorUIResource; -import javax.swing.plaf.FontUIResource; -import javax.swing.plaf.metal.MetalLookAndFeel; -import javax.swing.plaf.metal.OceanTheme; -import java.awt.*; - -/** - * Addon for JXTaskPane.
                    - * - * @author Frederic Lavigne - * @author Karl Schaefer - */ -public class TaskPaneAddon extends AbstractComponentAddon { - - public TaskPaneAddon() { - super("JXTaskPane"); - } - - @Override - protected void addBasicDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - Font taskPaneFont = UIManagerExt.getSafeFont("Label.font", new Font( - "Dialog", Font.PLAIN, 12)); - taskPaneFont = taskPaneFont.deriveFont(Font.BOLD); - - Color menuBackground = new ColorUIResource(SystemColor.menu); - - defaults.add(JXTaskPane.uiClassID, "org.jdesktop.swingx.plaf.basic.BasicTaskPaneUI"); - defaults.add("TaskPane.font", new FontUIResource(taskPaneFont)); - defaults.add("TaskPane.background", UIManagerExt.getSafeColor("List.background", - new ColorUIResource(Color.decode("#005C5C")))); - defaults.add("TaskPane.specialTitleBackground", new ColorUIResource(menuBackground.darker())); - defaults.add("TaskPane.titleBackgroundGradientStart", menuBackground); - defaults.add("TaskPane.titleBackgroundGradientEnd", menuBackground); - defaults.add("TaskPane.titleForeground", new ColorUIResource(SystemColor.menuText)); - defaults.add("TaskPane.specialTitleForeground", new ColorUIResource(SystemColor.menuText.brighter())); - defaults.add("TaskPane.animate", Boolean.TRUE); - defaults.add("TaskPane.focusInputMap", new UIDefaults.LazyInputMap(new Object[] { - "ENTER", "toggleCollapsed", - "SPACE", "toggleCollapsed"})); - } - - @Override - protected void addLinuxDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - addMetalDefaults(addon, defaults); - } - - @Override - protected void addMetalDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addMetalDefaults(addon, defaults); - - if (MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme) { - defaults.add(JXTaskPane.uiClassID, "org.jdesktop.swingx.plaf.misc.GlossyTaskPaneUI"); - } else { - defaults.add(JXTaskPane.uiClassID, "org.jdesktop.swingx.plaf.metal.MetalTaskPaneUI"); - } - - //TODO use safe methods - defaults.add("TaskPane.foreground", UIManager.getColor("activeCaptionText")); - defaults.add("TaskPane.background", MetalLookAndFeel.getControl()); - defaults.add("TaskPane.specialTitleBackground", MetalLookAndFeel.getPrimaryControl()); - defaults.add("TaskPane.titleBackgroundGradientStart", MetalLookAndFeel.getPrimaryControl()); - defaults.add("TaskPane.titleBackgroundGradientEnd", MetalLookAndFeel.getPrimaryControlHighlight()); - defaults.add("TaskPane.titleForeground", MetalLookAndFeel.getControlTextColor()); - defaults.add("TaskPane.specialTitleForeground", MetalLookAndFeel.getControlTextColor()); - defaults.add("TaskPane.borderColor", MetalLookAndFeel.getPrimaryControl()); - defaults.add("TaskPane.titleOver", new ColorUIResource(MetalLookAndFeel.getControl().darker())); - defaults.add("TaskPane.specialTitleOver", MetalLookAndFeel.getPrimaryControlHighlight()); - } - - @Override - protected void addWindowsDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addWindowsDefaults(addon, defaults); - - if (addon instanceof WindowsLookAndFeelAddons) { - defaults.add(JXTaskPane.uiClassID, "org.jdesktop.swingx.plaf.windows.WindowsTaskPaneUI"); - - String xpStyle = OS.getWindowsVisualStyle(); - if (WindowsLookAndFeelAddons.HOMESTEAD_VISUAL_STYLE - .equalsIgnoreCase(xpStyle)) { - defaults.add("TaskPane.foreground", new ColorUIResource(86, 102, 45)); - defaults.add("TaskPane.background", new ColorUIResource(246, 246, 236)); - defaults.add("TaskPane.specialTitleBackground", new ColorUIResource(224, 231, 184)); - defaults.add("TaskPane.titleBackgroundGradientStart", new ColorUIResource(Color.WHITE)); - defaults.add("TaskPane.titleBackgroundGradientEnd", new ColorUIResource(224, 231, 184)); - defaults.add("TaskPane.titleForeground", new ColorUIResource(86, 102, 45)); - defaults.add("TaskPane.titleOver", new ColorUIResource(114, 146, 29)); - defaults.add("TaskPane.specialTitleForeground", new ColorUIResource(86, 102, 45)); - defaults.add("TaskPane.specialTitleOver", new ColorUIResource(114, 146, 29)); - defaults.add("TaskPane.borderColor", new ColorUIResource(Color.WHITE)); - } else if (WindowsLookAndFeelAddons.SILVER_VISUAL_STYLE - .equalsIgnoreCase(xpStyle)) { - defaults.add("TaskPane.foreground", new ColorUIResource(Color.BLACK)); - defaults.add("TaskPane.background", new ColorUIResource(240, 241, 245)); - defaults.add("TaskPane.specialTitleBackground", new ColorUIResource(222, 222, 222)); - defaults.add("TaskPane.titleBackgroundGradientStart", new ColorUIResource(Color.WHITE)); - defaults.add("TaskPane.titleBackgroundGradientEnd", new ColorUIResource(214, 215, 224)); - defaults.add("TaskPane.titleForeground", new ColorUIResource(Color.BLACK)); - defaults.add("TaskPane.titleOver", new ColorUIResource(126, 124, 124)); - defaults.add("TaskPane.specialTitleForeground", new ColorUIResource(Color.BLACK)); - defaults.add("TaskPane.specialTitleOver", new ColorUIResource(126, 124, 124)); - defaults.add("TaskPane.borderColor", new ColorUIResource(Color.WHITE)); - } else { - //do not need to use safe method since the properties can never return null - final Toolkit toolkit = Toolkit.getDefaultToolkit(); - - defaults.add("TaskPane.foreground", new ColorUIResource(Color.WHITE)); - defaults.add("TaskPane.background", - new ColorUIResource((Color)toolkit.getDesktopProperty("win.3d.backgroundColor"))); - defaults.add("TaskPane.specialTitleBackground", new ColorUIResource(33, 89, 201)); - defaults.add("TaskPane.titleBackgroundGradientStart", new ColorUIResource(Color.WHITE)); - defaults.add("TaskPane.titleBackgroundGradientEnd", - new ColorUIResource((Color)toolkit.getDesktopProperty("win.frame.inactiveCaptionColor"))); - defaults.add("TaskPane.titleForeground", - new ColorUIResource((Color)toolkit.getDesktopProperty("win.frame.inactiveCaptionTextColor"))); - defaults.add("TaskPane.specialTitleForeground", new ColorUIResource(Color.WHITE)); - defaults.add("TaskPane.borderColor", new ColorUIResource(Color.WHITE)); - } - } - } - - @Override - protected void addMacDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addMacDefaults(addon, defaults); - - defaults.add(JXTaskPane.uiClassID, "org.jdesktop.swingx.plaf.misc.GlossyTaskPaneUI"); - defaults.add("TaskPane.background", new ColorUIResource(245, 245, 245)); - defaults.add("TaskPane.titleForeground", new ColorUIResource(Color.BLACK)); - defaults.add("TaskPane.specialTitleBackground", new ColorUIResource(188,188,188)); - defaults.add("TaskPane.specialTitleForeground", new ColorUIResource(Color.BLACK)); - defaults.add("TaskPane.titleBackgroundGradientStart", new ColorUIResource(250,250,250)); - defaults.add("TaskPane.titleBackgroundGradientEnd", new ColorUIResource(188,188,188)); - defaults.add("TaskPane.borderColor", new ColorUIResource(97, 97, 97)); - defaults.add("TaskPane.titleOver", new ColorUIResource(125, 125, 97)); - defaults.add("TaskPane.specialTitleOver", new ColorUIResource(125, 125, 97)); - } - - @Override - protected void addNimbusDefaults(LookAndFeelAddons addon, - DefaultsList defaults) { - super.addNimbusDefaults(addon, defaults); - - defaults.add(JXTaskPane.uiClassID, - "org.jdesktop.swingx.plaf.nimbus.NimbusTaskPaneUI"); - // dynamically changing the LaF to Nimbus does not refresh correctly the - // control colors if they are not hard-coded due to Nimbus DerivedColors - // lazy initialization - // defaults.add("TaskPane.foreground", new - // ColorUIResource(UIManager.getColor("activeCaption"))); - defaults.add("TaskPane.foreground", new ColorUIResource(186, 190, 198)); - // defaults.add("TaskPane.background", new - // ColorUIResource(UIManager.getColor("control"))); - defaults.add("TaskPane.background", new ColorUIResource(214, 217, 223)); - // defaults.add("TaskPane.specialTitleBackground", new - // ColorUIResource(UIManager.getColor("nimbusBlueGrey"))); - defaults.add("TaskPane.specialTitleBackground", new ColorUIResource( - 169, 176, 190)); - // defaults.add("TaskPane.titleBackgroundGradientStart", new - // ColorUIResource(UIManager.getColor("background"))); - defaults.add("TaskPane.titleBackgroundGradientStart", - new ColorUIResource(214, 217, 223)); - // defaults.add("TaskPane.titleBackgroundGradientEnd", new - // ColorUIResource(UIManager.getColor("controlLHighlight"))); - defaults.add("TaskPane.titleBackgroundGradientEnd", - new ColorUIResource(247, 248, 250)); - defaults.add("TaskPane.titleForeground", new ColorUIResource( - Color.BLACK)); - defaults.add("TaskPane.specialTitleForeground", new ColorUIResource( - Color.BLACK)); - // defaults.add("TaskPane.borderColor", new - // ColorUIResource(UIManager.getColor("nimbusBorder"))); - defaults - .add("TaskPane.borderColor", new ColorUIResource(146, 151, 161)); - // defaults.add("TaskPane.titleOver", new - // ColorUIResource(UIManager.getColor("nimbusSelection"))); - defaults.add("TaskPane.titleOver", new ColorUIResource(57, 105, 138)); - // defaults.add("TaskPane.specialTitleOver", new - // ColorUIResource(UIManager.getColor("nimbusSelection"))); - defaults.add("TaskPane.specialTitleOver", new ColorUIResource(57, 105, - 138)); - - } - - -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/TaskPaneContainerAddon.java b/src/main/java/org/jdesktop/swingx/plaf/TaskPaneContainerAddon.java deleted file mode 100644 index 4dfc7e027b..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/TaskPaneContainerAddon.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * $Id: TaskPaneContainerAddon.java 3500 2009-09-10 10:28:24Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf; - -import org.jdesktop.swingx.JXTaskPaneContainer; -import org.jdesktop.swingx.painter.MattePainter; -import org.jdesktop.swingx.plaf.windows.WindowsLookAndFeelAddons; -import org.jdesktop.swingx.util.OS; - -import javax.swing.*; -import javax.swing.plaf.BorderUIResource; -import javax.swing.plaf.ColorUIResource; -import javax.swing.plaf.metal.MetalLookAndFeel; -import java.awt.*; - -/** - * Addon for JXTaskPaneContainer. This addon defines the following properties: - * - * - * - * - * - * - *
                    TaskPaneContainer.backgroundbackground color
                    TaskPaneContainer.backgroundPainterbackground painter
                    TaskPaneContainer.bordercontainer border
                    TaskPaneContainer.fontfont (currently unused)
                    TaskPaneContainer.foregroundforeground color (currently unused)
                    - * - * @author Frederic Lavigne - * @author Karl Schaefer - */ -public class TaskPaneContainerAddon extends AbstractComponentAddon { - - public TaskPaneContainerAddon() { - super("JXTaskPaneContainer"); - } - - @Override - protected void addBasicDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addBasicDefaults(addon, defaults); - - defaults.add(JXTaskPaneContainer.uiClassID, "org.jdesktop.swingx.plaf.basic.BasicTaskPaneContainerUI"); - defaults.add("TaskPaneContainer.background", UIManagerExt.getSafeColor("Desktop.background", - new ColorUIResource(Color.decode("#005C5C")))); - defaults.add("TaskPaneContainer.border", new BorderUIResource(BorderFactory.createEmptyBorder(10, 10, 0, 10))); - } - - /** - * {@inheritDoc} - */ - @Override - protected void addMetalDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addMetalDefaults(addon, defaults); - - defaults.add("TaskPaneContainer.background", MetalLookAndFeel.getDesktopColor()); - } - - /** - * {@inheritDoc} - */ - @Override - protected void addWindowsDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addWindowsDefaults(addon, defaults); - if (addon instanceof WindowsLookAndFeelAddons) { - String xpStyle = OS.getWindowsVisualStyle(); - ColorUIResource background; - Color backgroundGradientStart; - Color backgroundGradientEnd; - - if (WindowsLookAndFeelAddons.HOMESTEAD_VISUAL_STYLE - .equalsIgnoreCase(xpStyle)) { - background = new ColorUIResource(201, 215, 170); - backgroundGradientStart = new Color(204, 217, 173); - backgroundGradientEnd = new Color(165, 189, 132); - } else if (WindowsLookAndFeelAddons.SILVER_VISUAL_STYLE - .equalsIgnoreCase(xpStyle)) { - background = new ColorUIResource(192, 195, 209); - backgroundGradientStart = new Color(196, 200, 212); - backgroundGradientEnd = new Color(177, 179, 200); - } else { - final Toolkit toolkit = Toolkit.getDefaultToolkit(); - background = new ColorUIResource((Color)toolkit.getDesktopProperty("win.3d.backgroundColor")); - backgroundGradientStart = (Color)toolkit.getDesktopProperty("win.frame.activeCaptionColor"); - backgroundGradientEnd = (Color)toolkit.getDesktopProperty("win.frame.inactiveCaptionColor"); - } - - defaults.add("TaskPaneContainer.backgroundPainter", new PainterUIResource( - new MattePainter(new GradientPaint( - 0f, 0f, backgroundGradientStart, - 0f, 1f, backgroundGradientEnd), - true))); - defaults.add("TaskPaneContainer.background", background); - } - } - - /** - * {@inheritDoc} - */ - @Override - protected void addMacDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addMacDefaults(addon, defaults); - - defaults.add("TaskPaneContainer.background", new ColorUIResource(238, 238, 238)); - } - - @Override - protected void addNimbusDefaults(LookAndFeelAddons addon, - DefaultsList defaults) { - super.addNimbusDefaults(addon, defaults); - // dynamically changing the LaF to Nimbus does not refresh correctly the - // control colors if they are not hard-coded due to Nimbus DerivedColors - // lazy initialization -// defaults.add("TaskPaneContainer.background", new ColorUIResource( -// UIManager.getColor("control"))); - defaults.add("TaskPaneContainer.background", new ColorUIResource(214,217,223)); - } - -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/TaskPaneContainerUI.java b/src/main/java/org/jdesktop/swingx/plaf/TaskPaneContainerUI.java deleted file mode 100644 index cc5770de0d..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/TaskPaneContainerUI.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * $Id: TaskPaneContainerUI.java 2363 2007-10-31 13:24:06Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf; - -import javax.swing.plaf.PanelUI; - -/** - * Pluggable UI for JXTaskPaneContainer. - * - * @author Frederic Lavigne - */ -public abstract class TaskPaneContainerUI extends PanelUI { - -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/TaskPaneUI.java b/src/main/java/org/jdesktop/swingx/plaf/TaskPaneUI.java deleted file mode 100644 index b6a5702f6e..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/TaskPaneUI.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * $Id: TaskPaneUI.java 2365 2007-11-01 13:38:15Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf; - -import javax.swing.*; -import javax.swing.plaf.PanelUI; -import java.awt.*; - -/** - * Pluggable UI for JXTaskPane. - * - * @author Frederic Lavigne - */ -public abstract class TaskPaneUI extends PanelUI { - - /** - * Called by the component when an action is added to the component through - * the {@link org.jdesktop.swingx.JXTaskPane#add(Action)} method. - * - * @param action - * @return a component built from the action. - */ - public Component createAction(Action action) { - return new JButton(action); - } - -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/TextUIWrapper.java b/src/main/java/org/jdesktop/swingx/plaf/TextUIWrapper.java deleted file mode 100644 index 5edb374cf3..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/TextUIWrapper.java +++ /dev/null @@ -1,164 +0,0 @@ -package org.jdesktop.swingx.plaf; - -import org.jdesktop.swingx.JXSearchField; -import org.jdesktop.swingx.prompt.BuddySupport; - -import javax.swing.*; -import javax.swing.plaf.TextUI; -import javax.swing.plaf.basic.BasicTextUI; -import javax.swing.text.JTextComponent; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; - -/** - * TODO: - * - * @author Peter Weishapl - * - * @param - */ -public abstract class TextUIWrapper { - private static final DefaultWrapper defaultWrapper = new DefaultWrapper(); - - public static final TextUIWrapper getDefaultWrapper() { - return defaultWrapper; - } - - private Class wrapperClass; - - protected TextUIWrapper(Class wrapperClass) { - this.wrapperClass = wrapperClass; - } - - /** - *

                    - * Wraps and replaces the current UI of the given textComponent, by calling - * {@link #wrapUI(JTextComponent)} if necessary. - *

                    - * - * @param textComponent - * @param stayOnUIChange - * if true, a {@link PropertyChangeListener} is registered, which - * listens for UI changes and wraps any new UI object. - */ - public final void install(final JTextComponent textComponent, boolean stayOnUIChange) { - replaceUIIfNeeded(textComponent); - if (stayOnUIChange) { - uiChangeHandler.install(textComponent); - } - } - - /** - * Wraps and replaces the text components current UI by calling {@link #wrapUI(TextUI)}, if the - * text components current UI is not an instance of the given wrapper class. - * - * @param textComponent - * @return true if the UI has been replaced - */ - protected boolean replaceUIIfNeeded(JTextComponent textComponent) { - if (wrapperClass.isAssignableFrom(textComponent.getUI().getClass())) { - return false; - } - - textComponent.setUI(wrapUI(textComponent)); - - return true; - } - - /** - * Override to return the appropriate UI wrapper object for the given {@link TextUI}. - * - * @param textUI - * @return the wrapping UI - */ - public abstract UI wrapUI(JTextComponent textComponent); - - /** - * Returns the wrapper class. - * - * @return the wrapper class - */ - public Class getWrapperClass() { - return wrapperClass; - } - - /** - *

                    - * Removes the {@link PropertyChangeListener}, which listens for "UI" property changes (if - * installed) and then calls {@link JComponent#updateUI()} on the textComponent to - * set the UI object provided by the current {@link UIDefaults}. - *

                    - * - * @param textComponent - */ - public final void uninstall(final JTextComponent textComponent) { - uiChangeHandler.uninstall(textComponent); - textComponent.updateUI(); - } - - private final TextUIChangeHandler uiChangeHandler = new TextUIChangeHandler(); - - private final class TextUIChangeHandler extends AbstractUIChangeHandler { - @Override - public void propertyChange(PropertyChangeEvent evt) { - JTextComponent txt = (JTextComponent) evt.getSource(); - - replaceUIIfNeeded(txt); - } - } - - public static final class DefaultWrapper extends TextUIWrapper { - private DefaultWrapper() { - super(PromptTextUI.class); - } - - /** - *

                    - * Creates a new {@link PromptTextUI}, which wraps the given textComponents UI. - *

                    - *

                    - * If the UI is already of type {@link PromptTextUI}, it will be returned. If - * textComponent is of type {@link JXSearchField} a new {@link SearchFieldUI} - * object will be returned. If textComponent is of type {@link JTextField} or - * {@link JTextArea} a {@link BuddyTextFieldUI} or {@link PromptTextAreaUI} will be - * returned, respectively. If the UI is of any other type, a - * {@link IllegalArgumentException} will be thrown. - *

                    - * - * @param textComponent - * wrap this components UI - * @return a {@link PromptTextUI} which wraps the textComponents UI. - */ - @Override - public PromptTextUI wrapUI(JTextComponent textComponent) { - TextUI textUI = textComponent.getUI(); - - if (textUI instanceof PromptTextUI) { - return (PromptTextUI) textUI; - } else if (textComponent instanceof JXSearchField) { - return new SearchFieldUI(textUI); - } else if (textComponent instanceof JTextField) { - return new BuddyTextFieldUI(textUI); - } else if (textComponent instanceof JTextArea) { - return new PromptTextAreaUI(textUI); - } - throw new IllegalArgumentException("ui implementation not supported: " - + textUI.getClass()); - } - - /** - * Every time the UI needs to be replaced we also need to make sure, that all buddy - * components are also in the component hierarchy. (That's because {@link BasicTextUI} - * removes all our buddies upon UI changes). - */ - @Override - protected boolean replaceUIIfNeeded(JTextComponent textComponent) { - boolean replaced = super.replaceUIIfNeeded(textComponent); - - if (replaced && textComponent instanceof JTextField) { - BuddySupport.ensureBuddiesAreInComponentHierarchy((JTextField) textComponent); - } - return replaced; - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/TipOfTheDayAddon.java b/src/main/java/org/jdesktop/swingx/plaf/TipOfTheDayAddon.java deleted file mode 100644 index 83bc94bca1..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/TipOfTheDayAddon.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * $Id: TipOfTheDayAddon.java 2474 2007-11-21 17:32:04Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf; - -import org.jdesktop.swingx.JXTipOfTheDay; -import org.jdesktop.swingx.plaf.basic.BasicTipOfTheDayUI; -import org.jdesktop.swingx.plaf.windows.WindowsTipOfTheDayUI; - -import javax.swing.*; -import javax.swing.plaf.BorderUIResource; -import javax.swing.plaf.ColorUIResource; -import javax.swing.plaf.FontUIResource; -import java.awt.*; - -/** - * Addon for JXTipOfTheDay.
                    - * - * @author Frederic Lavigne - */ -public class TipOfTheDayAddon extends AbstractComponentAddon { - - public TipOfTheDayAddon() { - super("JXTipOfTheDay"); - } - - @Override - protected void addBasicDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addBasicDefaults(addon, defaults); - - Font font = UIManagerExt.getSafeFont("Label.font", new Font("Dialog", Font.PLAIN, 12)); - font = font.deriveFont(Font.BOLD, 13f); - - defaults.add(JXTipOfTheDay.uiClassID, BasicTipOfTheDayUI.class.getName()); - defaults.add("TipOfTheDay.font", UIManagerExt.getSafeFont("TextPane.font", - new FontUIResource("Serif", Font.PLAIN, 12))); - defaults.add("TipOfTheDay.tipFont", new FontUIResource(font)); - defaults.add("TipOfTheDay.background", new ColorUIResource(Color.WHITE)); - defaults.add("TipOfTheDay.icon", - LookAndFeel.makeIcon(BasicTipOfTheDayUI.class, "resources/TipOfTheDay24.gif")); - defaults.add("TipOfTheDay.border", new BorderUIResource( - BorderFactory.createLineBorder(new Color(117, 117, 117)))); - - UIManagerExt.addResourceBundle( - "org.jdesktop.swingx.plaf.basic.resources.TipOfTheDay"); - } - - /** - * {@inheritDoc} - */ - @Override - protected void addWindowsDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addWindowsDefaults(addon, defaults); - - Font font = UIManagerExt.getSafeFont("Label.font", - new Font("Dialog", Font.PLAIN, 12)); - font = font.deriveFont(13f); - - defaults.add(JXTipOfTheDay.uiClassID, WindowsTipOfTheDayUI.class.getName()); - defaults.add("TipOfTheDay.background", new ColorUIResource(Color.GRAY)); - defaults.add("TipOfTheDay.font", new FontUIResource(font)); - defaults.add("TipOfTheDay.icon", - LookAndFeel.makeIcon(WindowsTipOfTheDayUI.class, "resources/tipoftheday.png")); - defaults.add("TipOfTheDay.border" ,new BorderUIResource(new WindowsTipOfTheDayUI.TipAreaBorder())); - - UIManagerExt.addResourceBundle( - "org.jdesktop.swingx.plaf.windows.resources.TipOfTheDay"); - } - -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/TipOfTheDayUI.java b/src/main/java/org/jdesktop/swingx/plaf/TipOfTheDayUI.java deleted file mode 100644 index 5854622511..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/TipOfTheDayUI.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * $Id: TipOfTheDayUI.java 542 2005-10-10 18:03:15Z rbair $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf; - -import org.jdesktop.swingx.JXTipOfTheDay; - -import javax.swing.*; -import javax.swing.plaf.PanelUI; -import java.awt.*; - -/** - * Pluggable UI for JXTipOfTheDay. - * - * @author Frederic Lavigne - */ -public abstract class TipOfTheDayUI extends PanelUI { - - /** - * Creates a new JDialog to display a JXTipOfTheDay panel. If - * choice is not null then the window will offer a way for the - * end-user to not show the tip of the day dialog. - * - * @param parentComponent - * @param choice - * @return a new JDialog to display a JXTipOfTheDay panel - */ - public abstract JDialog createDialog(Component parentComponent, - JXTipOfTheDay.ShowOnStartupChoice choice); - -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/TitledPanelAddon.java b/src/main/java/org/jdesktop/swingx/plaf/TitledPanelAddon.java deleted file mode 100644 index 5a8c02762f..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/TitledPanelAddon.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * $Id: TitledPanelAddon.java 3288 2009-03-10 14:36:28Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf; - - -import org.jdesktop.swingx.JXTitledPanel; -import org.jdesktop.swingx.painter.MattePainter; - -import javax.swing.plaf.ColorUIResource; -import javax.swing.plaf.FontUIResource; -import javax.swing.plaf.InsetsUIResource; -import javax.swing.plaf.metal.MetalLookAndFeel; -import java.awt.*; - -/** - * Addon for JXTitledPanel.
                    - * - */ -public class TitledPanelAddon extends AbstractComponentAddon { - - public TitledPanelAddon() { - super("JXTitledPanel"); - } - - @Override - protected void addBasicDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addBasicDefaults(addon, defaults); - - defaults.add(JXTitledPanel.uiClassID, "org.jdesktop.swingx.plaf.basic.BasicTitledPanelUI"); - defaults.add("JXTitledPanel.titleFont", UIManagerExt.getSafeFont("Button.font", - new FontUIResource("Dialog", Font.PLAIN, 12))); - defaults.add("JXTitledPanel.titleForeground", new ColorUIResource(Color.WHITE)); - defaults.add("JXTitledPanel.titlePainter", new PainterUIResource( - new MattePainter( - new GradientPaint(0, 0, Color.LIGHT_GRAY, 0, 1, Color.GRAY), true))); - defaults.add("JXTitledPanel.captionInsets", new InsetsUIResource(4, 12, 4, 12)); - defaults.add("JXTitledPanel.rightDecorationInsets", new InsetsUIResource(1,1,1,1)); - defaults.add("JXTitledPanel.leftDecorationInsets", new InsetsUIResource(1,1,1,1)); - } - - /** - * {@inheritDoc} - */ - @Override - protected void addLinuxDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - addMetalDefaults(addon, defaults); - } - - /** - * {@inheritDoc} - */ - @Override - protected void addMetalDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addMetalDefaults(addon, defaults); - - defaults.add("JXTitledPanel.titleForeground", new ColorUIResource(Color.WHITE)); - defaults.add("JXTitledPanel.titlePainter", new PainterUIResource( - new MattePainter(new GradientPaint(0, 0, - MetalLookAndFeel.getCurrentTheme().getPrimaryControl(), 0, 1, - MetalLookAndFeel.getCurrentTheme().getPrimaryControlDarkShadow()),true))); - } - - @Override - protected void addWindowsDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addWindowsDefaults(addon, defaults); - - // JW: hot fix for #291-swingx - // was tracked down by Neil Weber - the requested colors are not available in - // all LFs, so changed to fall-back to something real - // don't understand why this has blown when trying to toggle to Metal... - // definitely needs deeper digging - // kgs: moved to using getSafeXXX from UIManagerExt - defaults.add("JXTitledPanel.titleForeground", UIManagerExt.getSafeColor( - "InternalFrame.activeTitleForeground", new ColorUIResource(Color.WHITE))); - defaults.add("JXTitledPanel.titlePainter", new PainterUIResource( - new MattePainter(new GradientPaint(0, 0, UIManagerExt.getSafeColor( - "InternalFrame.inactiveTitleGradient", new ColorUIResource(49, 121, 242)), - 0, 1, UIManagerExt.getSafeColor( "InternalFrame.activeTitleBackground", - new ColorUIResource(198, 211, 247))), true))); - - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/TitledPanelUI.java b/src/main/java/org/jdesktop/swingx/plaf/TitledPanelUI.java deleted file mode 100644 index 128c36688d..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/TitledPanelUI.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * $Id: TitledPanelUI.java 997 2006-04-11 20:51:31Z rbair $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.plaf; - -import javax.swing.*; -import javax.swing.plaf.PanelUI; -import java.awt.*; - -/** - * - * @author rbair - */ -public abstract class TitledPanelUI extends PanelUI { - /** - * Adds the given JComponent as a decoration on the right of the title - * @param decoration - */ - public abstract void setRightDecoration(JComponent decoration); - public abstract JComponent getRightDecoration(); - - /** - * Adds the given JComponent as a decoration on the left of the title - * @param decoration - */ - public abstract void setLeftDecoration(JComponent decoration); - public abstract JComponent getLeftDecoration(); - /** - * @return the Container acting as the title bar for this component - */ - public abstract Container getTitleBar(); -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/UIAction.java b/src/main/java/org/jdesktop/swingx/plaf/UIAction.java deleted file mode 100644 index 32bca5a6b2..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/UIAction.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * $Id: UIAction.java 4028 2011-06-03 19:32:19Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.plaf; - -import javax.swing.*; -import java.beans.PropertyChangeListener; - -/** - * UIAction is the basis of all of basic's action classes that are used in - * an ActionMap. Subclasses need to override actionPerformed. - *

                    - * A typical subclass will look like: - *

                    - *    private static class Actions extends UIAction {
                    - *        Actions(String name) {
                    - *            super(name);
                    - *        }
                    - *
                    - *        public void actionPerformed(ActionEvent ae) {
                    - *            if (getName() == "selectAll") {
                    - *                selectAll();
                    - *            }
                    - *            else if (getName() == "cancelEditing") {
                    - *                cancelEditing();
                    - *            }
                    - *        }
                    - *    }
                    - * 
                    - *

                    - * Subclasses that wish to conditionalize the enabled state should override - * isEnabled(Component), and be aware that the passed in - * Component may be null. - *

                    - * This is based on sun.swing.UIAction in J2SE 1.5 - * - * @see Action - * @author Scott Violet - */ -public abstract class UIAction implements Action { - private String name; - - public UIAction(String name) { - this.name = name; - } - - public final String getName() { - return name; - } - - @Override - public Object getValue(String key) { - return NAME.equals(key) ? name : null; - } - - // UIAction is not mutable, this does nothing. - @Override - public void putValue(String key, Object value) { - } - - // UIAction is not mutable, this does nothing. - @Override - public void setEnabled(boolean b) { - } - - /** - * Cover method for isEnabled(null). - */ - @Override - public final boolean isEnabled() { - return isEnabled(null); - } - - /** - * Subclasses that need to conditionalize the enabled state should - * override this. Be aware that sender may be null. - * - * @param sender Widget enabled state is being asked for, may be null. - */ - public boolean isEnabled(Object sender) { - return true; - } - - // UIAction is not mutable, this does nothing. - @Override - public void addPropertyChangeListener(PropertyChangeListener listener) { - } - - // UIAction is not mutable, this does nothing. - @Override - public void removePropertyChangeListener(PropertyChangeListener listener) { - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/UIColorHighlighterAddon.java b/src/main/java/org/jdesktop/swingx/plaf/UIColorHighlighterAddon.java deleted file mode 100644 index 59184500f2..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/UIColorHighlighterAddon.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * $Id: UIColorHighlighterAddon.java 3513 2009-09-22 11:18:09Z kleopatra $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf; - -import org.jdesktop.swingx.plaf.windows.WindowsLookAndFeelAddons; -import org.jdesktop.swingx.util.OS; - -import javax.swing.*; -import javax.swing.plaf.ColorUIResource; -import javax.swing.plaf.metal.MetalLookAndFeel; -import javax.swing.plaf.metal.OceanTheme; -import java.awt.*; -import java.util.logging.Logger; - -/** - * Loads LF specific background striping colors. - * - * The colors are based on the LF selection colors for certain - * LFs and themes, for unknown LFs/themes a generic grey is used. - * - * - * @author Jeanette Winzenburg - * @author Karl Schaefer - */ -public class UIColorHighlighterAddon extends AbstractComponentAddon { - - @SuppressWarnings("unused") - private static final Logger LOG = Logger - .getLogger(UIColorHighlighterAddon.class.getName()); - - public UIColorHighlighterAddon() { - super("UIColorHighlighter"); - } - - /** - * {@inheritDoc} - */ - @Override - protected void addBasicDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addBasicDefaults(addon, defaults); - - defaults.add("UIColorHighlighter.stripingBackground", new ColorUIResource(229, 229, 229)); - } - - /** - * {@inheritDoc} - */ - @Override - protected void addMacDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addMacDefaults(addon, defaults); - - defaults.add("UIColorHighlighter.stripingBackground", new ColorUIResource(237, 243, 254)); - } - - /** - * {@inheritDoc} - */ - @Override - protected void addMetalDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addMetalDefaults(addon, defaults); - - if (MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme) { - defaults.add("UIColorHighlighter.stripingBackground", new ColorUIResource(230, 238, 246)); - } else { - defaults.add("UIColorHighlighter.stripingBackground", new ColorUIResource(235, 235, 255)); - } - } - - /** - * {@inheritDoc} - */ - @Override - protected void addWindowsDefaults(LookAndFeelAddons addon, DefaultsList defaults) { - super.addWindowsDefaults(addon, defaults); - - if (OS.isUsingWindowsVisualStyles()) { - String xpStyle = OS.getWindowsVisualStyle(); - - if (WindowsLookAndFeelAddons.HOMESTEAD_VISUAL_STYLE - .equalsIgnoreCase(xpStyle)) { - defaults.add("UIColorHighlighter.stripingBackground", new ColorUIResource(228, 231, 219)); - } else if (WindowsLookAndFeelAddons.SILVER_VISUAL_STYLE - .equalsIgnoreCase(xpStyle)) { - defaults.add("UIColorHighlighter.stripingBackground", new ColorUIResource(235, 235, 236)); - } else { - // default blue - defaults.add("UIColorHighlighter.stripingBackground", new ColorUIResource(224, 233, 246)); - } - - } else { - defaults.add("UIColorHighlighter.stripingBackground", new ColorUIResource(218, 222, 233)); - } - } - - @Override - protected void addNimbusDefaults(LookAndFeelAddons addon, - DefaultsList defaults) { - super.addNimbusDefaults(addon, defaults); - // JW: Hacking around core issue #6882917 - // which is the underlying reason for issue #1180-swingx - // (SwingX vs Nimbus table striping) - if (Boolean.TRUE.equals(UIManager.get("Nimbus.keepAlternateRowColor"))) return; - // PENDING JW: not entirely sure if it is safe to really grab the color here - // the Nimbus (Derived)Color is not yet fully installed at this moment - // so without a table to instantiate may be rgb = 0,0,0 - Object value = UIManager.getLookAndFeelDefaults().remove("Table.alternateRowColor"); - if (value instanceof Color) { - defaults.add("UIColorHighlighter.stripingBackground", value, false); - } - } - - -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/UIDependent.java b/src/main/java/org/jdesktop/swingx/plaf/UIDependent.java deleted file mode 100644 index 143856464b..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/UIDependent.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * $Id: UIDependent.java 4028 2011-06-03 19:32:19Z kschaefe $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf; - -/** - * Encapsulates state that depends on the UI and needs - * to be updated on LookAndFeel change. - * - * @author Jeanette Winzenburg - */ -public interface UIDependent { - /** - * Updates all internal visuals after changing a UI-delegate. - * - * @see javax.swing.JComponent#updateUI() - */ - void updateUI(); -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/UIManagerExt.java b/src/main/java/org/jdesktop/swingx/plaf/UIManagerExt.java deleted file mode 100644 index b2620df348..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/UIManagerExt.java +++ /dev/null @@ -1,721 +0,0 @@ -/* - * $Id: UIManagerExt.java 4028 2011-06-03 19:32:19Z kschaefe $ - * - * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf; - -import org.jdesktop.swingx.painter.Painter; -import org.jdesktop.swingx.util.Contract; - -import javax.swing.*; -import javax.swing.border.Border; -import javax.swing.plaf.*; -import java.awt.*; -import java.util.*; - -/** - * A utility class for obtaining configuration properties from the - * {@code UIDefaults}. This class handles SwingX-specific L&F needs, such as - * the installation of painters and shapes. There are several categories of - * utility methods: - *

                      - *
                    • Support for the safe creation of {@code UIResource}s.
                    • - *
                    • Support for new {@code UIResource} types, such as - * {@code PainterUIResource}.
                    • - *
                    • Support for the dynamic localization of {@code UIDefaults}.
                    • - *
                    • Support for returning non-{@code String} localizations from - * {@code ResourceBundle}s.
                    • - *
                    - *

                    Safe Methods

                    - *

                    - * The {@code getSafeXXX} methods are designed for use with - * {@code LookAndFeelAddon}s. Any addon that attempts to obtain a property - * defined in the defaults (available from {@code UIManager.get}) to set a - * property that will be added to the defaults for the addon should use the - * "safe" methods. The methods ensure that a valid value is always returned and - * that value is a {@code UIResource}. - *

                    - *

                    Support for New Types

                    - *

                    - * {@code UIManagerExt} supports the retrieval of new {@code UIResource} types. - * There is a {@code getXXX} method for every {@code UIResource} subtype in the - * {@code org.jdesktop.swingx.plaf} package. - *

                    - *

                    Support for Dynamic Localization

                    - *

                    - * {@code UIManagerExt} enables dynamic localization by supporting - * {@code ResourceBundle}s. The - * {@linkplain UIDefaults#addResourceBundle(String)} allows resource bundles to - * be added to the {@code UIDefaults}. While there is support for this feature - * in core, there is a bug with the class loader that prevents user added - * bundles from working correctly when used via Web Start. Therefore, - * {@code UIManagerExt} defines methods to add and remove resource bundles. - * These are the only methods that SwingX classes should use when adding - * resource bundles to the defaults. Since {@code UIManagerExt} is maintaining - * the bundles, any localized {@code String}s must be retrieved from - * the {@code getString} methods in this class. - *

                    - *

                    Support for Non-{@code String} Localization Values

                    - *

                    - * All methods work by first determining if the value is present - * {@code UIDefaults}. If the value is not present, then the installed - * {@code ResourceBundle}s are queried. {@code UIManagerExt} will attempt to - * convert any returned value to the appropriate type. For instance, - * {@code getInt} uses {@code Integer.decode} to convert {@code String}s - * returned from the bundle into {@code int}s. - *

                    - * - * @author Karl George Schaefer - * - * @see UIManager - * @see UIDefaults - */ -@SuppressWarnings("nls") -public class UIManagerExt { - /** - * Used to replicate the resource bundle behavior from the - * {@code UIDefaults}. - */ - private static class UIDefaultsExt { - //use vector; we want synchronization - private Vector resourceBundles; - - /** - * Maps from a Locale to a cached Map of the ResourceBundle. This is done - * so as to avoid an exception being thrown when a value is asked for. - * Access to this should be done while holding a lock on the - * UIDefaults, eg synchronized(this). - */ - private Map> resourceCache; - - UIDefaultsExt() { - resourceCache = new HashMap>(); - } - - //should this just return String? - private Object getFromResourceBundle(Object key, Locale l) { - - if( resourceBundles == null || - resourceBundles.isEmpty() || - !(key instanceof String) ) { - return null; - } - - // A null locale means use the default locale. - if( l == null ) { - l = Locale.getDefault(); - } - - synchronized(this) { - return getResourceCache(l).get(key); - } - } - - /** - * Returns a Map of the known resources for the given locale. - */ - private Map getResourceCache(Locale l) { - Map values = resourceCache.get(l); - - if (values == null) { - values = new HashMap(); - for (int i=resourceBundles.size()-1; i >= 0; i--) { - String bundleName = resourceBundles.get(i); - - try { - ResourceBundle b = ResourceBundle. - getBundle(bundleName, l, UIManagerExt.class.getClassLoader()); - Enumeration keys = b.getKeys(); - - while (keys.hasMoreElements()) { - String key = keys.nextElement(); - - if (values.get(key) == null) { - Object value = b.getObject(key); - - values.put(key, (String) value); - } - } - } catch( MissingResourceException mre ) { - // Keep looking - } - } - resourceCache.put(l, values); - } - return values; - } - - public synchronized void addResourceBundle(String bundleName) { - if( bundleName == null ) { - return; - } - if( resourceBundles == null ) { - resourceBundles = new Vector(5); - } - if (!resourceBundles.contains(bundleName)) { - resourceBundles.add( bundleName ); - resourceCache.clear(); - } - } - - public synchronized void removeResourceBundle( String bundleName ) { - if( resourceBundles != null ) { - resourceBundles.remove( bundleName ); - } - resourceCache.clear(); - } - } - - private static UIDefaultsExt uiDefaultsExt = new UIDefaultsExt(); - - private UIManagerExt() { - //does nothing - } - - /** - * Adds a resource bundle to the list of resource bundles that are searched - * for localized values. Resource bundles are searched in the reverse order - * they were added. In other words, the most recently added bundle is - * searched first. - * - * @param bundleName - * the base name of the resource bundle to be added - * @see ResourceBundle - * @see #removeResourceBundle - */ - public static void addResourceBundle(String bundleName) { - uiDefaultsExt.addResourceBundle(bundleName); - } - - /** - * Removes a resource bundle from the list of resource bundles that are - * searched for localized defaults. - * - * @param bundleName - * the base name of the resource bundle to be removed - * @see ResourceBundle - * @see #addResourceBundle - */ - public static void removeResourceBundle(String bundleName) { - uiDefaultsExt.removeResourceBundle(bundleName); - } - - /** - * Returns a string from the defaults. If the value for {@code key} is not a - * {@code String}, {@code null} is returned. - * - * @param key - * an {@code Object} specifying the string - * @return the {@code String} object - * @throws NullPointerException - * if {@code key} is {@code null} - */ - public static String getString(Object key) { - return getString(key, null); - } - - /** - * Returns a string from the defaults. If the value for {@code key} is not a - * {@code String}, {@code null} is returned. - * - * @param key - * an {@code Object} specifying the string - * @param l - * the {@code Locale} for which the string is desired; refer - * to {@code UIDefaults} for details on how a {@code null} - * {@code Locale} is handled - * @return the {@code String} object - * @throws NullPointerException - * if {@code key} is {@code null} - */ - public static String getString(Object key, Locale l) { - Object value = UIManager.get(key, l); - - if (value instanceof String) { - return (String) value; - } - - //only return resource bundle if not in UIDefaults - if (value == null) { - value = uiDefaultsExt.getFromResourceBundle(key, l); - - if (value instanceof String) { - return (String) value; - } - } - - return null; - } - - /** - * Returns an integer from the defaults. If the value for {@code key} is not - * an {@code int}, {@code 0} is returned. - * - * @param key - * an {@code Object} specifying the integer - * @return the {@code int} - * @throws NullPointerException - * if {@code key} is {@code null} - */ - public static int getInt(Object key) { - return getInt(key, null); - } - - /** - * Returns an integer from the defaults. If the value for {@code key} is not - * an {@code int}, {@code 0} is returned. - * - * @param key - * an {@code Object} specifying the integer - * @param l - * the {@code Locale} for which the integer is desired; refer - * to {@code UIDefaults} for details on how a {@code null} - * {@code Locale} is handled - * @return the {@code int} - * @throws NullPointerException - * if {@code key} is {@code null} - */ - public static int getInt(Object key, Locale l) { - Object value = UIManager.get(key, l); - - if (value instanceof Integer) { - return (Integer) value; - } - - if (value == null) { - value = uiDefaultsExt.getFromResourceBundle(key, l); - - if (value instanceof Integer) { - return (Integer) value; - } - - if (value instanceof String) { - try { - return Integer.decode((String) value); - } catch (NumberFormatException e) { - // ignore - the entry was not parseable, can't do anything - // JW: should we log it? - } - } - } - - return 0; - } - - /** - * Returns an Boolean from the defaults. If the value for {@code key} is not - * a {@code boolean}, {@code false} is returned. - * - * @param key - * an {@code Object} specifying the Boolean - * @return the {@code boolean} - * @throws NullPointerException - * if {@code key} is {@code null} - */ - public static boolean getBoolean(Object key) { - return getBoolean(key, null); - } - - /** - * Returns an Boolean from the defaults. If the value for {@code key} is not - * a {@code boolean}, {@code false} is returned. - * - * @param key - * an {@code Object} specifying the Boolean - * @param l - * the {@code Locale} for which the Boolean is desired; refer - * to {@code UIDefaults} for details on how a {@code null} - * {@code Locale} is handled - * @return the {@code boolean} - * @throws NullPointerException - * if {@code key} is {@code null} - */ - public static boolean getBoolean(Object key, Locale l) { - Object value = UIManager.get(key, l); - - if (value instanceof Boolean) { - return (Boolean) value; - } - - //only return resource bundle if not in UIDefaults - if (value == null) { - value = uiDefaultsExt.getFromResourceBundle(key, l); - - if (value instanceof Boolean) { - return (Boolean) value; - } - - if (value instanceof String) { - return Boolean.valueOf((String) value); - } - } - - return false; - } - - /** - * Returns a color from the defaults. If the value for {@code key} is not - * a {@code Color}, {@code null} is returned. - * - * @param key - * an {@code Object} specifying the color - * @return the {@code Color} object - * @throws NullPointerException - * if {@code key} is {@code null} - */ - public static Color getColor(Object key) { - return getColor(key, null); - } - - /** - * Returns a color from the defaults. If the value for {@code key} is not - * a {@code Color}, {@code null} is returned. - * - * @param key - * an {@code Object} specifying the color - * @param l - * the {@code Locale} for which the color is desired; refer - * to {@code UIDefaults} for details on how a {@code null} - * {@code Locale} is handled - * @return the {@code Color} object - * @throws NullPointerException - * if {@code key} is {@code null} - */ - public static Color getColor(Object key, Locale l) { - Object value = UIManager.get(key, l); - - if (value instanceof Color) { - return (Color) value; - } - - //only return resource bundle if not in UIDefaults - if (value == null) { - value = uiDefaultsExt.getFromResourceBundle(key, l); - - if (value instanceof Color) { - return (Color) value; - } - - if (value instanceof String) { - try { - return Color.decode((String) value); - } catch (NumberFormatException e) { - // incorrect format; does nothing - } - } - } - - return null; - } - - //TODO: Font.decode always returns a valid font. This is not acceptable for UIManager -// /** -// * Returns a font from the defaults. If the value for {@code key} is not -// * a {@code Font}, {@code null} is returned. -// * -// * @param key -// * an {@code Object} specifying the font -// * @return the {@code Font} object -// * @throws NullPointerException -// * if {@code key} is {@code null} -// */ -// public static Font getFont(Object key) { -// return getFont(key, null); -// } -// -// /** -// * Returns a font from the defaults. If the value for {@code key} is not -// * a {@code Font}, {@code null} is returned. -// * -// * @param key -// * an {@code Object} specifying the font -// * @param l -// * the {@code Locale} for which the font is desired; refer -// * to {@code UIDefaults} for details on how a {@code null} -// * {@code Locale} is handled -// * @return the {@code Font} object -// * @throws NullPointerException -// * if {@code key} is {@code null} -// */ -// public static Font getFont(Object key, Locale l) { -// Object value = UIManager.get(key, l); -// -// if (value instanceof Font) { -// return (Font) value; -// } -// -// //only return resource bundle if not in UIDefaults -// if (value == null) { -// value = uiDefaultsExt.getFromResourceBundle(key, l); -// -// if (value instanceof Font) { -// return (Font) value; -// } -// -// if (value instanceof String) { -// return Font.decode((String) value); -// } -// } -// -// return null; -// } - - /** - * Returns a shape from the defaults. If the value for {@code key} is not a - * {@code Shape}, {@code null} is returned. - * - * @param key an {@code Object} specifying the shape - * @return the {@code Shape} object - * @throws NullPointerException if {@code key} is {@code null} - */ - public static Shape getShape(Object key) { - Object value = UIManager.getDefaults().get(key); - return (value instanceof Shape) ? (Shape) value : null; - } - - /** - * Returns a shape from the defaults that is appropriate for the given - * locale. If the value for {@code key} is not a {@code Shape}, - * {@code null} is returned. - * - * @param key - * an {@code Object} specifying the shape - * @param l - * the {@code Locale} for which the shape is desired; refer - * to {@code UIDefaults} for details on how a {@code null} - * {@code Locale} is handled - * @return the {@code Shape} object - * @throws NullPointerException - * if {@code key} is {@code null} - */ - public static Shape getShape(Object key, Locale l) { - Object value = UIManager.getDefaults().get(key, l); - return (value instanceof Shape) ? (Shape) value : null; - } - - /** - * Returns a painter from the defaults. If the value for {@code key} is not - * a {@code Painter}, {@code null} is returned. - * - * @param key - * an {@code Object} specifying the painter - * @return the {@code Painter} object - * @throws NullPointerException - * if {@code key} is {@code null} - */ - public static Painter getPainter(Object key) { - Object value = UIManager.getDefaults().get(key); - return (value instanceof Painter) ? (Painter) value : null; - } - - /** - * Returns a painter from the defaults that is appropriate for the given - * locale. If the value for {@code key} is not a {@code Painter}, - * {@code null} is returned. - * - * @param key - * an {@code Object} specifying the painter - * @param l - * the {@code Locale} for which the painter is desired; refer - * to {@code UIDefaults} for details on how a {@code null} - * {@code Locale} is handled - * @return the {@code Painter} object - * @throws NullPointerException - * if {@code key} is {@code null} - */ - public static Painter getPainter(Object key, Locale l) { - Object value = UIManager.getDefaults().get(key, l); - return (value instanceof Painter) ? (Painter) value : null; - } - - /** - * Returns a border from the defaults. If the value for {@code key} is not a - * {@code Border}, {@code defaultBorder} is returned. - * - * @param key - * an {@code Object} specifying the border - * @param defaultBorder - * the border to return if the border specified by - * {@code key} does not exist - * @return the {@code Border} object - * @throws NullPointerException - * if {@code key} or {@code defaultBorder} is {@code null} - */ - public static Border getSafeBorder(Object key, Border defaultBorder) { - Contract.asNotNull(defaultBorder, "defaultBorder cannot be null"); - - Border safeBorder = UIManager.getBorder(key); - - if (safeBorder == null) { - safeBorder = defaultBorder; - } - - if (!(safeBorder instanceof UIResource)) { - safeBorder = new BorderUIResource(safeBorder); - } - - return safeBorder; - } - - /** - * Returns a color from the defaults. If the value for {@code key} is not a - * {@code Color}, {@code defaultColor} is returned. - * - * @param key - * an {@code Object} specifying the color - * @param defaultColor - * the color to return if the color specified by {@code key} - * does not exist - * @return the {@code Color} object - * @throws NullPointerException - * if {@code key} or {@code defaultColor} is {@code null} - */ - public static Color getSafeColor(Object key, Color defaultColor) { - Contract.asNotNull(defaultColor, "defaultColor cannot be null"); - - Color safeColor = UIManager.getColor(key); - - if (safeColor == null) { - safeColor = defaultColor; - } - - if (!(safeColor instanceof UIResource)) { - safeColor = new ColorUIResource(safeColor); - } - - return safeColor; - } - - /** - * Returns a dimension from the defaults. If the value for {@code key} is - * not a {@code Dimension}, {@code defaultDimension} is returned. - * - * @param key - * an {@code Object} specifying the dimension - * @param defaultDimension - * the dimension to return if the dimension specified by - * {@code key} does not exist - * @return the {@code Dimension} object - * @throws NullPointerException - * if {@code key} or {@code defaultColor} is {@code null} - */ - public static Dimension getSafeDimension(Object key, Dimension defaultDimension) { - Contract.asNotNull(defaultDimension, "defaultDimension cannot be null"); - - Dimension safeDimension = UIManager.getDimension(key); - - if (safeDimension == null) { - safeDimension = defaultDimension; - } - - if (!(safeDimension instanceof UIResource)) { - safeDimension = new DimensionUIResource(safeDimension.width, safeDimension.height); - } - - return safeDimension; - } - - /** - * Returns a font from the defaults. If the value for {@code key} is not a - * {@code Font}, {@code defaultFont} is returned. - * - * @param key - * an {@code Object} specifying the font - * @param defaultFont - * the font to return if the font specified by {@code key} - * does not exist - * @return the {@code Font} object - * @throws NullPointerException - * if {@code key} or {@code defaultFont} is {@code null} - */ - public static Font getSafeFont(Object key, Font defaultFont) { - Contract.asNotNull(defaultFont, "defaultFont cannot be null"); - - Font safeFont = UIManager.getFont(key); - - if (safeFont == null) { - safeFont = defaultFont; - } - - if (!(safeFont instanceof UIResource)) { - safeFont = new FontUIResource(safeFont); - } - - return safeFont; - } - - /** - * Returns an icon from the defaults. If the value for {@code key} is not a - * {@code Icon}, {@code defaultIcon} is returned. - * - * @param key - * an {@code Object} specifying the icon - * @param defaultIcon - * the icon to return if the icon specified by {@code key} - * does not exist - * @return the {@code Icon} object - * @throws NullPointerException - * if {@code key} or {@code defaultIcon} is {@code null} - */ - public static Icon getSafeIcon(Object key, Icon defaultIcon) { - Contract.asNotNull(defaultIcon, "defaultIcon cannot be null"); - - Icon safeIcon = UIManager.getIcon(key); - - if (safeIcon == null) { - safeIcon = defaultIcon; - } - - if (!(safeIcon instanceof UIResource)) { - safeIcon = new IconUIResource(safeIcon); - } - - return safeIcon; - } - - /** - * Returns an insets from the defaults. If the value for {@code key} is not - * a {@code Insets}, {@code defaultInsets} is returned. - * - * @param key - * an {@code Object} specifying the insets - * @param defaultInsets - * the insets to return if the insets specified by - * {@code key} does not exist - * @return the {@code Insets} object - * @throws NullPointerException - * if {@code key} or {@code defaultInsets} is {@code null} - */ - public static Insets getSafeInsets(Object key, Insets defaultInsets) { - Contract.asNotNull(defaultInsets, "defaultInsets cannot be null"); - - Insets safeInsets = UIManager.getInsets(key); - - if (safeInsets == null) { - safeInsets = defaultInsets; - } - - if (!(safeInsets instanceof UIResource)) { - safeInsets = new InsetsUIResource(safeInsets.top, safeInsets.left, - safeInsets.bottom, safeInsets.right); - } - - return safeInsets; - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/XListAddon.java b/src/main/java/org/jdesktop/swingx/plaf/XListAddon.java deleted file mode 100644 index 8096cc6f80..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/XListAddon.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * $Id: BusyLabelAddon.java 2565 2008-01-03 19:08:32Z rah003 $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf; - -import org.jdesktop.swingx.JXList; - -import javax.swing.*; -import javax.swing.border.AbstractBorder; -import javax.swing.border.Border; -import javax.swing.plaf.UIResource; - -/** - * Addon for JXList. - *

                    - * - * Install a custom ui to support sorting/filtering in JXList. - */ -public class XListAddon extends AbstractComponentAddon { - - public XListAddon() { - super("JXList"); - } - - @Override - protected void addBasicDefaults(LookAndFeelAddons addon, - DefaultsList defaults) { - defaults.add(JXList.uiClassID, - "org.jdesktop.swingx.plaf.basic.core.BasicXListUI"); - if (isGTK()) { - replaceListTableBorders(addon, defaults); - } - } - - @Override - protected void addNimbusDefaults(LookAndFeelAddons addon, - DefaultsList defaults) { - defaults.add(JXList.uiClassID, - "org.jdesktop.swingx.plaf.synth.SynthXListUI"); - } - - - private void replaceListTableBorders(LookAndFeelAddons addon, - DefaultsList defaults) { - replaceBorder(defaults, "List.", "focusCellHighlightBorder"); - replaceBorder(defaults, "List.", "focusSelectedCellHighlightBorder"); - replaceBorder(defaults, "List.", "noFocusBorder"); - } - - - - /** - * @param defaults - * @param componentPrefix - * @param borderKey - */ - private void replaceBorder(DefaultsList defaults, String componentPrefix, - String borderKey) { - String key = componentPrefix + borderKey; - Border border = UIManager.getBorder(componentPrefix + borderKey); - if (border instanceof AbstractBorder && border instanceof UIResource - && border.getClass().getName().contains("ListTable")) { - border = new SafeBorder((AbstractBorder) border); - // PENDING JW: this is fishy ... adding to lookAndFeelDefaults is taken - UIManager.getLookAndFeelDefaults().put(key, border); - // adding to defaults is not -// defaults.add(key, border); - - } - } - - - - /** - * - * @return true if the LF is GTK. - */ - private boolean isGTK() { - return "GTK".equals(UIManager.getLookAndFeel().getID()); - } - - -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicBusyLabelUI.java b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicBusyLabelUI.java deleted file mode 100644 index 4f7d0f11b0..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicBusyLabelUI.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * $Id: BasicBusyLabelUI.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf.basic; - -import org.jdesktop.swingx.JXBusyLabel; -import org.jdesktop.swingx.painter.BusyPainter; -import org.jdesktop.swingx.plaf.BusyLabelUI; -import org.jdesktop.swingx.plaf.UIManagerExt; - -import javax.swing.*; -import javax.swing.plaf.ComponentUI; -import javax.swing.plaf.basic.BasicLabelUI; -import java.awt.*; - -/** - * Base implementation of the JXBusyLabel UI. - * - * @author rah003 - */ -public class BasicBusyLabelUI extends BasicLabelUI implements BusyLabelUI { - - /** Creates a new instance of BasicBusyLabelUI */ - public BasicBusyLabelUI(JXBusyLabel lbl) { - } - - public static ComponentUI createUI(JComponent c) { - return new BasicBusyLabelUI((JXBusyLabel)c); - } - - @Override - public BusyPainter getBusyPainter(final Dimension dim) { - BusyPainter p = new BusyPainter() { - @Override - protected void init(Shape point, Shape trajectory, Color b, Color h) { - super.init(dim == null ? UIManagerExt.getShape("JXBusyLabel.pointShape") : getScaledDefaultPoint(dim.height), - dim == null ? UIManagerExt.getShape("JXBusyLabel.trajectoryShape") : getScaledDefaultTrajectory(dim.height), - UIManagerExt.getSafeColor("JXBusyLabel.baseColor", Color.LIGHT_GRAY), - UIManagerExt.getSafeColor("JXBusyLabel.highlightColor", Color.BLACK)); - } - }; - return p; - } - - @Override - public int getDelay() { - return UIManager.getInt("JXBusyLabel.delay"); - } - - -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicCalendarHeaderHandler.java b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicCalendarHeaderHandler.java deleted file mode 100644 index a3754c505b..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicCalendarHeaderHandler.java +++ /dev/null @@ -1,262 +0,0 @@ -/* - * $Id: BasicCalendarHeaderHandler.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.plaf.basic; - -import org.jdesktop.swingx.JXHyperlink; -import org.jdesktop.swingx.JXMonthView; -import org.jdesktop.swingx.JXPanel; -import org.jdesktop.swingx.hyperlink.AbstractHyperlinkAction; -import org.jdesktop.swingx.renderer.StringValue; -import org.jdesktop.swingx.renderer.StringValues; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.text.DateFormatSymbols; -import java.util.Calendar; -import java.util.Locale; - - -/** - * Custom implementation of a CalendarHeaderHandler in preparation of a vista-style - * calendar. Does nothing yet. - * - * @author Jeanette Winzenburg - */ -public class BasicCalendarHeaderHandler extends CalendarHeaderHandler { - - - @Override - public void install(JXMonthView monthView) { - super.install(monthView); - getHeaderComponent().setActions(monthView.getActionMap().get("previousMonth"), - monthView.getActionMap().get("nextMonth"), - monthView.getActionMap().get("zoomOut")); - - } - - - - @Override - protected void installNavigationActions() { - // TODO Auto-generated method stub - super.installNavigationActions(); - ZoomOutAction zoomOutAction = new ZoomOutAction(); - zoomOutAction.setTarget(monthView); - monthView.getActionMap().put("zoomOut", zoomOutAction); - } - - - - @Override - public void uninstall(JXMonthView monthView) { - getHeaderComponent().setActions(null, null, null); - super.uninstall(monthView); - } - - - @Override - public BasicCalendarHeader getHeaderComponent() { - // TODO Auto-generated method stub - return (BasicCalendarHeader) super.getHeaderComponent(); - } - - @Override - protected BasicCalendarHeader createCalendarHeader() { - return new BasicCalendarHeader(); - } - - /** - * Quick fix for Issue #1046-swingx: header text not updated if zoomable. - * - */ - protected static class ZoomOutAction extends AbstractHyperlinkAction { - - private PropertyChangeListener linkListener; - // Formatters/state used by Providers. - /** Localized month strings used in title. */ - private String[] monthNames; - private StringValue tsv ; - - public ZoomOutAction() { - super(); - tsv = new StringValue() { - - @Override - public String getString(Object value) { - if (value instanceof Calendar) { - String month = monthNames[((Calendar) value) - .get(Calendar.MONTH)]; - return month + " " - + ((Calendar) value).get(Calendar.YEAR); - } - return StringValues.TO_STRING.getString(value); - } - - }; - } - - @Override - public void actionPerformed(ActionEvent e) { - // TODO Auto-generated method stub - - } - - - /** - * installs a propertyChangeListener on the target and - * updates the visual properties from the target. - */ - @Override - protected void installTarget() { - if (getTarget() != null) { - getTarget().addPropertyChangeListener(getTargetListener()); - } - updateLocale(); - updateFromTarget(); - } - - /** - * - */ - private void updateLocale() { - Locale current = getTarget() != null ? getTarget().getLocale() : Locale.getDefault(); - monthNames = DateFormatSymbols.getInstance(current).getMonths(); - } - - /** - * removes the propertyChangeListener.

                    - * - * Implementation NOTE: this does not clean-up internal state! There is - * no need to because updateFromTarget handles both null and not-null - * targets. Hmm... - * - */ - @Override - protected void uninstallTarget() { - if (getTarget() == null) return; - getTarget().removePropertyChangeListener(getTargetListener()); - } - - protected void updateFromTarget() { - // this happens on construction with null target - if (tsv == null) return; - Calendar calendar = getTarget() != null ? getTarget().getCalendar() : null; - setName(tsv.getString(calendar)); - } - - private PropertyChangeListener getTargetListener() { - if (linkListener == null) { - linkListener = new PropertyChangeListener() { - - @Override - public void propertyChange(PropertyChangeEvent evt) { - if ("firstDisplayedDay".equals(evt.getPropertyName())) { - updateFromTarget(); - } else if ("locale".equals(evt.getPropertyName())) { - updateLocale(); - updateFromTarget(); - } - } - - }; - } - return linkListener; - } - - - } - - - /** - * Active header for a JXMonthView in zoomable mode.

                    - * - * PENDING JW: very much work-in-progress. - */ - static class BasicCalendarHeader extends JXPanel { - - protected AbstractButton prevButton; - protected AbstractButton nextButton; - protected JXHyperlink zoomOutLink; - - public BasicCalendarHeader() { - setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS)); - prevButton = createNavigationButton(); - nextButton = createNavigationButton(); - zoomOutLink = createZoomLink(); - add(prevButton); - add(Box.createHorizontalGlue()); - add(zoomOutLink); - add(Box.createHorizontalGlue()); - add(nextButton); - setBorder(BorderFactory.createEmptyBorder(2, 4, 2, 4)); - } - - /** - * Sets the actions for backward, forward and zoom out navigation. - * - * @param prev - * @param next - * @param zoomOut - */ - public void setActions(Action prev, Action next, Action zoomOut) { - prevButton.setAction(prev); - nextButton.setAction(next); - zoomOutLink.setAction(zoomOut); - } - - - /** - * {@inheritDoc}

                    - * - * Overridden to set the font of the zoom hyperlink. - */ - @Override - public void setFont(Font font) { - super.setFont(font); - if (zoomOutLink != null) - zoomOutLink.setFont(font); - } - - private JXHyperlink createZoomLink() { - JXHyperlink zoomOutLink = new JXHyperlink(); - Color textColor = new Color(16, 66, 104); - zoomOutLink.setUnclickedColor(textColor); - zoomOutLink.setClickedColor(textColor); - zoomOutLink.setFocusable(false); - return zoomOutLink; - } - - private AbstractButton createNavigationButton() { - JXHyperlink b = new JXHyperlink(); - b.setContentAreaFilled(false); - b.setBorder(BorderFactory.createEmptyBorder()); - b.setRolloverEnabled(true); - b.setFocusable(false); - return b; - } - } - -} - diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicCalendarRenderingHandler.java b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicCalendarRenderingHandler.java deleted file mode 100644 index b5acf10d03..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicCalendarRenderingHandler.java +++ /dev/null @@ -1,346 +0,0 @@ -package org.jdesktop.swingx.plaf.basic; - -import org.jdesktop.swingx.JXMonthView; -import org.jdesktop.swingx.decorator.*; -import org.jdesktop.swingx.plaf.UIManagerExt; -import org.jdesktop.swingx.renderer.*; - -import javax.swing.*; -import java.awt.*; -import java.text.DateFormat; -import java.text.DateFormatSymbols; -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; - -/** - * The RenderingHandler responsible for text rendering. It provides - * and configures a rendering component for the given cell of - * a JXMonthView.

                    - * - * Note: exposing the createXXStringValue methods is an emergency workaround for - * Issue #1062-swingx (core doesn't use arabic digits where appropriate) to allow - * subclasses to do better than core. So beware of future changes! - * - */ -class BasicCalendarRenderingHandler implements CalendarRenderingHandler { - /** The CellContext for content and default visual config. */ - private CalendarCellContext cellContext; - /** The providers to use per DayState. */ - private Map> providers; - //-------- Highlight properties - /** The Painter used for highlighting unselectable dates. */ - private TextCrossingPainter textCross; - /** The foreground color for unselectable date highlight. */ - private Color unselectableDayForeground; - - /** - * Instantiates a RenderingHandler and installs default state. - */ - public BasicCalendarRenderingHandler() { - install(); - } - - private void install() { - unselectableDayForeground = UIManagerExt.getColor("JXMonthView.unselectableDayForeground"); - textCross = new TextCrossingPainter(); - cellContext = new CalendarCellContext(); - installProviders(); - } - - /** - * Creates and stores ComponentProviders for all DayStates. - */ - private void installProviders() { - providers = new HashMap>(); - - StringValue sv = createDayStringValue(null); - ComponentProvider provider = new LabelProvider(sv, JLabel.RIGHT); - providers.put(CalendarState.IN_MONTH, provider); - providers.put(CalendarState.TODAY, provider); - providers.put(CalendarState.TRAILING, provider); - providers.put(CalendarState.LEADING, provider); - - StringValue wsv = createWeekOfYearStringValue(null); - ComponentProvider weekOfYearProvider = new LabelProvider(wsv, - JLabel.RIGHT); - providers.put(CalendarState.WEEK_OF_YEAR, weekOfYearProvider); - - ComponentProvider dayOfWeekProvider = new LabelProvider(JLabel.CENTER) { - - @Override - protected String getValueAsString(CellContext context) { - Object value = context.getValue(); - // PENDING JW: this is breaking provider's contract in its - // role as StringValue! Don't in the general case. - if (value instanceof Calendar) { - int day = ((Calendar) value).get(Calendar.DAY_OF_WEEK); - return ((JXMonthView) context.getComponent()).getDayOfTheWeek(day); - } - return super.getValueAsString(context); - } - - }; - providers.put(CalendarState.DAY_OF_WEEK, dayOfWeekProvider); - - StringValue tsv = createMonthHeaderStringValue(null); - ComponentProvider titleProvider = new LabelProvider(tsv, - JLabel.CENTER); - providers.put(CalendarState.TITLE, titleProvider); - } - - /** - * Creates and returns a StringValue used for rendering the title of a month box. - * The input they are assumed to handle is a Calendar configured to a day of - * the month to render. - * - * @param locale the Locale to use, might be null to indicate usage of the default - * Locale - * @return a StringValue appropriate for rendering month title. - */ - protected StringValue createMonthHeaderStringValue(Locale locale) { - if (locale == null) { - locale = Locale.getDefault(); - } - final String[] monthNames = DateFormatSymbols.getInstance(locale).getMonths(); - StringValue tsv = new StringValue() { - - @Override - public String getString(Object value) { - if (value instanceof Calendar) { - String month = monthNames[((Calendar) value) - .get(Calendar.MONTH)]; - return month + " " - + ((Calendar) value).get(Calendar.YEAR); - } - return StringValues.TO_STRING.getString(value); - } - - }; - return tsv; - } - - /** - * Creates and returns a StringValue used for rendering the week of year. - * The input they are assumed to handle is a Calendar configured to a day of - * the week to render. - * - * @param locale the Locale to use, might be null to indicate usage of the default - * Locale - * @return a StringValue appropriate for rendering week of year. - */ - protected StringValue createWeekOfYearStringValue(Locale locale) { - StringValue wsv = new StringValue() { - - @Override - public String getString(Object value) { - if (value instanceof Calendar) { - value = ((Calendar) value).get(Calendar.WEEK_OF_YEAR); - } - return StringValues.TO_STRING.getString(value); - } - - }; - return wsv; - } - - /** - * Creates and returns a StringValue used for rendering days in a month. - * The input they are assumed to handle is a Calendar configured to the day. - * - * @param locale the Locale to use, might be null to indicate usage of the default - * Locale - * @return a StringValue appropriate for rendering days in a month - */ - protected StringValue createDayStringValue(Locale locale) { - if (locale == null) { - locale = Locale.getDefault(); - } - FormatStringValue sv = new FormatStringValue(new SimpleDateFormat("d", locale)) { - - @Override - public String getString(Object value) { - if (value instanceof Calendar) { - ((DateFormat) getFormat()).setTimeZone(((Calendar) value).getTimeZone()); - value = ((Calendar) value).getTime(); - } - return super.getString(value); - } - - }; - return sv; - } - - - /** - * Updates internal state to the given Locale. - * - * @param locale the new Locale. - */ - @Override - public void setLocale(Locale locale) { - StringValue dayValue = createDayStringValue(locale); - providers.get(CalendarState.IN_MONTH).setStringValue(dayValue); - providers.get(CalendarState.TODAY).setStringValue(dayValue); - providers.get(CalendarState.TRAILING).setStringValue(dayValue); - providers.get(CalendarState.LEADING).setStringValue(dayValue); - - providers.get(CalendarState.WEEK_OF_YEAR).setStringValue(createWeekOfYearStringValue(locale)); - providers.get(CalendarState.TITLE).setStringValue(createMonthHeaderStringValue(locale)); - } - - /** - * Configures and returns a component for rendering of the given monthView cell. - * - * @param monthView the JXMonthView to render onto - * @param calendar the cell value - * @param dayState the DayState of the cell - * @return a component configured for rendering the given cell - */ - @Override - public JComponent prepareRenderingComponent(JXMonthView monthView, Calendar calendar, CalendarState dayState) { - cellContext.installContext(monthView, calendar, - isSelected(monthView, calendar, dayState), - isFocused(monthView, calendar, dayState), - dayState); - JComponent comp = providers.get(dayState).getRendererComponent(cellContext); - return highlight(comp, monthView, calendar, dayState); - } - - - /** - * - * NOTE: it's the responsibility of the CalendarCellContext to detangle - * all "default" (that is: which could be queried from the comp and/or UIManager) - * foreground/background colors based on the given state! Moved out off here. - *

                    - * PENDING JW: replace hard-coded logic by giving over to highlighters. - * - * @param monthView the JXMonthView to render onto - * @param calendar the cell value - * @param dayState the DayState of the cell - * @param dayState - */ - private JComponent highlight(JComponent comp, JXMonthView monthView, - Calendar calendar, CalendarState dayState) { - CalendarAdapter adapter = getCalendarAdapter(monthView, calendar, dayState); - return (JComponent) getHighlighter().highlight(comp, adapter); - } - - /** - * @return - */ - private Highlighter getHighlighter() { - if (highlighter == null) { - highlighter = new CompoundHighlighter(); - installHighlighters(); - } - return highlighter; - } - - /** - * - */ - private void installHighlighters() { - HighlightPredicate boldPredicate = new HighlightPredicate() { - - @Override - public boolean isHighlighted(Component renderer, - ComponentAdapter adapter) { - if (!(adapter instanceof CalendarAdapter)) - return false; - CalendarAdapter ca = (CalendarAdapter) adapter; - return CalendarState.DAY_OF_WEEK == ca.getCalendarState() || - CalendarState.TITLE == ca.getCalendarState(); - } - - }; - Highlighter font = new AbstractHighlighter(boldPredicate) { - - @Override - protected Component doHighlight(Component component, - ComponentAdapter adapter) { - component.setFont(getDerivedFont(component.getFont())); - return component; - } - - }; - highlighter.addHighlighter(font); - - HighlightPredicate unselectable = new HighlightPredicate() { - - @Override - public boolean isHighlighted(Component renderer, - ComponentAdapter adapter) { - if (!(adapter instanceof CalendarAdapter)) - return false; - return ((CalendarAdapter) adapter).isUnselectable(); - } - - }; - textCross.setForeground(unselectableDayForeground); - Highlighter painterHL = new PainterHighlighter(unselectable, textCross); - highlighter.addHighlighter(painterHL); - - } - - /** - * @param monthView - * @param calendar - * @param dayState - * @return - */ - private CalendarAdapter getCalendarAdapter(JXMonthView monthView, - Calendar calendar, CalendarState dayState) { - if (calendarAdapter == null) { - calendarAdapter = new CalendarAdapter(monthView); - } - return calendarAdapter.install(calendar, dayState); - } - - private CalendarAdapter calendarAdapter; - private CompoundHighlighter highlighter; - - /** - * @param font - * @return - */ - private Font getDerivedFont(Font font) { - return font.deriveFont(Font.BOLD); - } - - /** - * @param monthView - * @param calendar - * @param dayState - * @return - */ - private boolean isFocused(JXMonthView monthView, Calendar calendar, - CalendarState dayState) { - return false; - } - - /** - * @param monthView the JXMonthView to render onto - * @param calendar the cell value - * @param dayState the DayState of the cell - * @return - */ - private boolean isSelected(JXMonthView monthView, Calendar calendar, - CalendarState dayState) { - if (!isSelectable(dayState)) return false; - return monthView.isSelected(calendar.getTime()); - } - - - /** - * @param dayState - * @return - */ - private boolean isSelectable(CalendarState dayState) { - return (CalendarState.IN_MONTH == dayState) || (CalendarState.TODAY == dayState); - } - -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicDatePickerUI.java b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicDatePickerUI.java deleted file mode 100644 index c1b5c5d157..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicDatePickerUI.java +++ /dev/null @@ -1,1670 +0,0 @@ -/* - * $Id: BasicDatePickerUI.java 4107 2012-01-19 14:24:15Z kleopatra $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf.basic; - -import org.jdesktop.swingx.JXDatePicker; -import org.jdesktop.swingx.JXMonthView; -import org.jdesktop.swingx.SwingXUtilities; -import org.jdesktop.swingx.calendar.CalendarUtils; -import org.jdesktop.swingx.calendar.DatePickerFormatter; -import org.jdesktop.swingx.calendar.DatePickerFormatter.DatePickerFormatterUIResource; -import org.jdesktop.swingx.calendar.DateSelectionModel; -import org.jdesktop.swingx.event.DateSelectionEvent; -import org.jdesktop.swingx.event.DateSelectionEvent.EventType; -import org.jdesktop.swingx.event.DateSelectionListener; -import org.jdesktop.swingx.plaf.DatePickerUI; - -import javax.swing.*; -import javax.swing.JFormattedTextField.AbstractFormatter; -import javax.swing.JFormattedTextField.AbstractFormatterFactory; -import javax.swing.border.Border; -import javax.swing.event.PopupMenuEvent; -import javax.swing.event.PopupMenuListener; -import javax.swing.plaf.ComponentUI; -import javax.swing.plaf.UIResource; -import javax.swing.text.DefaultFormatterFactory; -import javax.swing.text.View; -import java.awt.*; -import java.awt.event.*; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.beans.PropertyVetoException; -import java.text.DateFormat; -import java.text.ParseException; -import java.util.Calendar; -import java.util.Date; -import java.util.Locale; -import java.util.TimeZone; -import java.util.logging.Logger; - -/** - * The basic implementation of a DatePickerUI. - *

                    - * - * - * @author Joshua Outwater - * @author Jeanette Winzenburg - */ -public class BasicDatePickerUI extends DatePickerUI { - - @SuppressWarnings("all") - private static final Logger LOG = Logger.getLogger(BasicDatePickerUI.class - .getName()); - - protected JXDatePicker datePicker; - private JButton popupButton; - private BasicDatePickerPopup popup; - private Handler handler; - /* - * shared listeners - */ - protected PropertyChangeListener propertyChangeListener; - private FocusListener focusListener; - - /* - * listener's for the arrow button - */ - protected MouseListener mouseListener; - protected MouseMotionListener mouseMotionListener; - - /* - * listeners for the picker's editor - */ - private ActionListener editorActionListener; - private EditorCancelAction editorCancelAction; - private PropertyChangeListener editorPropertyListener; - - /** - * listeners for the picker's monthview - */ - private DateSelectionListener monthViewSelectionListener; - private ActionListener monthViewActionListener; - private PropertyChangeListener monthViewPropertyListener; - - private PopupRemover popupRemover; - - private PopupMenuListener popupMenuListener; - - - @SuppressWarnings({"UnusedDeclaration"}) - public static ComponentUI createUI(JComponent c) { - return new BasicDatePickerUI(); - } - - @Override - public void installUI(JComponent c) { - datePicker = (JXDatePicker)c; - datePicker.setLayout(createLayoutManager()); - installComponents(); - installDefaults(); - installKeyboardActions(); - installListeners(); - } - - @Override - public void uninstallUI(JComponent c) { - uninstallListeners(); - uninstallKeyboardActions(); - uninstallDefaults(); - uninstallComponents(); - datePicker.setLayout(null); - datePicker = null; - } - - protected void installComponents() { - - JFormattedTextField editor = datePicker.getEditor(); - if (SwingXUtilities.isUIInstallable(editor)) { - DateFormat[] formats = getCustomFormats(editor); - // we are not yet listening ... - datePicker.setEditor(createEditor()); - if (formats != null) { - datePicker.setFormats(formats); - } - } - updateFromEditorChanged(null, false); - - popupButton = createPopupButton(); - if (popupButton != null) { - // this is a trick to get hold of the client prop which - // prevents closing of the popup - JComboBox box = new JComboBox(); - Object preventHide = box.getClientProperty("doNotCancelPopup"); - popupButton.putClientProperty("doNotCancelPopup", preventHide); - datePicker.add(popupButton); - popupButton.setEnabled(datePicker.isEnabled()); - popupButton.setInheritsPopupMenu(true); - } - updateChildLocale(datePicker.getLocale()); - - } - - /** - * Checks and returns custom formats on the editor, if any. - * - * @param editor the editor to check - * @return the custom formats uses in the editor or null if it had - * used defaults as defined in the datepicker properties - */ - private DateFormat[] getCustomFormats(JFormattedTextField editor) { - DateFormat[] formats = null; - if (editor != null) { - AbstractFormatterFactory factory = editor.getFormatterFactory(); - if (factory != null) { - AbstractFormatter formatter = factory.getFormatter(editor); - // fix for #1144: classCastException for custom formatters - // PENDING JW: revisit for #1138 - if ((formatter instanceof DatePickerFormatter) && !(formatter instanceof UIResource)) { -// if (!(formatter instanceof DatePickerFormatterUIResource)) { - formats = ((DatePickerFormatter) formatter).getFormats(); - } - } - - } - return formats; - } - - protected void uninstallComponents() { - JFormattedTextField editor = datePicker.getEditor(); - if (editor != null) { - datePicker.remove(editor); - } - - if (popupButton != null) { - datePicker.remove(popupButton); - popupButton = null; - } - } - - /** - * Installs DatePicker default properties. - */ - protected void installDefaults() { - // PENDING JW: currently this is for testing only. - boolean zoomable = Boolean.TRUE.equals(UIManager.get("JXDatePicker.forceZoomable")); - if (zoomable) { - datePicker.getMonthView().setZoomable(true); - } - } - - protected void uninstallDefaults() { - - } - - protected void installKeyboardActions() { - // install picker's actions - ActionMap pickerMap = datePicker.getActionMap(); - pickerMap.put(JXDatePicker.CANCEL_KEY, createCancelAction()); - pickerMap.put(JXDatePicker.COMMIT_KEY, createCommitAction()); - pickerMap.put(JXDatePicker.HOME_NAVIGATE_KEY, createHomeAction(false)); - pickerMap.put(JXDatePicker.HOME_COMMIT_KEY, createHomeAction(true)); - TogglePopupAction popupAction = createTogglePopupAction(); - pickerMap.put("TOGGLE_POPUP", popupAction); - - InputMap pickerInputMap = datePicker.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); - pickerInputMap.put(KeyStroke.getKeyStroke("ENTER"), JXDatePicker.COMMIT_KEY); - pickerInputMap.put(KeyStroke.getKeyStroke("ESCAPE"), JXDatePicker.CANCEL_KEY); - // PENDING: get from LF - pickerInputMap.put(KeyStroke.getKeyStroke("F5"), JXDatePicker.HOME_COMMIT_KEY); - pickerInputMap.put(KeyStroke.getKeyStroke("shift F5"), JXDatePicker.HOME_NAVIGATE_KEY); - pickerInputMap.put(KeyStroke.getKeyStroke("alt DOWN"), "TOGGLE_POPUP"); - - installLinkPanelKeyboardActions(); - } - - protected void uninstallKeyboardActions() { - uninstallLinkPanelKeyboardActions(datePicker.getLinkPanel()); - } - - - /** - * Installs actions and key bindings on the datePicker's linkPanel. Does - * nothing if the linkPanel is null. - * - * PRE: keybindings installed on picker. - */ - protected void installLinkPanelKeyboardActions() { - if (datePicker.getLinkPanel() == null) - return; - ActionMap map = datePicker.getLinkPanel().getActionMap(); - map.put(JXDatePicker.HOME_COMMIT_KEY, datePicker.getActionMap().get( - JXDatePicker.HOME_COMMIT_KEY)); - map.put(JXDatePicker.HOME_NAVIGATE_KEY, datePicker.getActionMap().get( - JXDatePicker.HOME_NAVIGATE_KEY)); - InputMap inputMap = datePicker.getLinkPanel().getInputMap( - JComponent.WHEN_IN_FOCUSED_WINDOW); - // PENDING: get from LF - inputMap.put(KeyStroke.getKeyStroke("F5"), - JXDatePicker.HOME_COMMIT_KEY); - inputMap.put(KeyStroke.getKeyStroke("shift F5"), - JXDatePicker.HOME_NAVIGATE_KEY); - } - - - /** - * Uninstalls actions and key bindings from linkPanel. Does nothing if the - * linkPanel is null. - * - * @param panel the component to uninstall - * - */ - protected void uninstallLinkPanelKeyboardActions(JComponent panel) { - if (panel == null) return; - ActionMap map = panel.getActionMap(); - map.remove(JXDatePicker.HOME_COMMIT_KEY); - map.remove(JXDatePicker.HOME_NAVIGATE_KEY); - InputMap inputMap = panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); - // PENDING: get from LF - inputMap.remove(KeyStroke.getKeyStroke("F5")); - inputMap.remove(KeyStroke.getKeyStroke("shift F5")); - - } - - /** - * Creates and installs all listeners to all components. - * - */ - protected void installListeners() { - /* - * create the listeners. - */ - // propertyListener for datePicker - propertyChangeListener = createPropertyChangeListener(); - - // mouseListener (for popup button only) ? - mouseListener = createMouseListener(); - mouseMotionListener = createMouseMotionListener(); - - // shared focuslistener (installed to picker and editor) - focusListener = createFocusListener(); - - // editor related listeners - editorActionListener = createEditorActionListener(); - editorPropertyListener = createEditorPropertyListener(); - - // montheView related listeners - monthViewSelectionListener = createMonthViewSelectionListener(); - monthViewActionListener = createMonthViewActionListener(); - monthViewPropertyListener = createMonthViewPropertyListener(); - - popupRemover = new PopupRemover(); - /* - * install the listeners - */ - // picker - datePicker.addPropertyChangeListener(propertyChangeListener); - datePicker.addFocusListener(focusListener); - - if (popupButton != null) { - // JW: which property do we want to monitor? - popupButton.addPropertyChangeListener(propertyChangeListener); - popupButton.addMouseListener(mouseListener); - popupButton.addMouseMotionListener(mouseMotionListener); - } - - updateEditorListeners(null); - // JW the following does more than installing the listeners .. - // synchs properties of datepicker to monthView's - // prepares monthview for usage in popup - // synch the date - // Relies on being the last thing done in the install .. - // - updateFromMonthViewChanged(null); - } - /** - * Uninstalls and nulls all listeners which had been installed - * by this delegate. - * - */ - protected void uninstallListeners() { - // datePicker - datePicker.removePropertyChangeListener(propertyChangeListener); - datePicker.removeFocusListener(focusListener); - - // monthView - datePicker.getMonthView().getSelectionModel().removeDateSelectionListener(monthViewSelectionListener); - datePicker.getMonthView().removeActionListener(monthViewActionListener); - datePicker.getMonthView().removePropertyChangeListener(propertyChangeListener); - - // JW: when can that be null? - // maybe in the very beginning? if some code calls ui.uninstall - // before ui.install? The editor is created by the ui. - if (datePicker.getEditor() != null) { - uninstallEditorListeners(datePicker.getEditor()); - } - if (popupButton != null) { - popupButton.removePropertyChangeListener(propertyChangeListener); - popupButton.removeMouseListener(mouseListener); - popupButton.removeMouseMotionListener(mouseMotionListener); - } - - popupRemover.unload(); - - popupRemover = null; - propertyChangeListener = null; - mouseListener = null; - mouseMotionListener = null; - - editorActionListener = null; - editorPropertyListener = null; - - monthViewSelectionListener = null; - monthViewActionListener = null; - monthViewPropertyListener = null; - - handler = null; - } - -// --------------------- wiring listeners - /** - * Wires the picker's monthView related listening. Removes all - * listeners from the given old view and adds the listeners to - * the current monthView.

                    - * - * @param oldMonthView - */ - protected void updateMonthViewListeners(JXMonthView oldMonthView) { - DateSelectionModel oldModel = null; - if (oldMonthView != null) { - oldMonthView.removePropertyChangeListener(monthViewPropertyListener); - oldMonthView.removeActionListener(monthViewActionListener); - oldModel = oldMonthView.getSelectionModel(); - } - datePicker.getMonthView().addPropertyChangeListener(monthViewPropertyListener); - datePicker.getMonthView().addActionListener(monthViewActionListener); - updateSelectionModelListeners(oldModel); - } - - - /** - * Wires the picker's editor related listening and actions. Removes - * listeners/actions from the old editor and adds them to - * the new editor.

                    - * - * @param oldEditor the pickers editor before the change - */ - protected void updateEditorListeners(JFormattedTextField oldEditor) { - if (oldEditor != null) { - uninstallEditorListeners(oldEditor); - } - datePicker.getEditor().addPropertyChangeListener(editorPropertyListener); - datePicker.getEditor().addActionListener(editorActionListener); - datePicker.getEditor().addFocusListener(focusListener); - editorCancelAction = new EditorCancelAction(datePicker.getEditor()); - } - - /** - * Uninstalls all listeners and actions which have been installed - * by this delegate from the given editor. - * - * @param oldEditor the editor to uninstall. - */ - private void uninstallEditorListeners(JFormattedTextField oldEditor) { - oldEditor.removePropertyChangeListener(editorPropertyListener); - oldEditor.removeActionListener(editorActionListener); - oldEditor.removeFocusListener(focusListener); - if (editorCancelAction != null) { - editorCancelAction.uninstall(); - editorCancelAction = null; - } - } - - /** - * Wires monthView's selection model listening. Removes the - * selection listener from the old model and add to the new model. - * - * @param oldModel the dateSelectionModel before the change, may be null. - */ - protected void updateSelectionModelListeners(DateSelectionModel oldModel) { - if (oldModel != null) { - oldModel.removeDateSelectionListener(monthViewSelectionListener); - } - datePicker.getMonthView().getSelectionModel() - .addDateSelectionListener(monthViewSelectionListener); - - } - - - // ---------------- component creation - /** - * Creates the editor used to edit the date selection. The editor is - * configured with the default DatePickerFormatter marked as UIResource. - * - * @return an instance of a JFormattedTextField - */ - protected JFormattedTextField createEditor() { - JFormattedTextField f = new DefaultEditor( - new DatePickerFormatterUIResource(datePicker.getLocale())); - f.setName("dateField"); - // this produces a fixed pref widths, looking a bit funny - // int columns = UIManagerExt.getInt("JXDatePicker.numColumns", null); - // if (columns > 0) { - // f.setColumns(columns); - // } - // that's always 0 as it comes from the resourcebundle - // f.setColumns(UIManager.getInt("JXDatePicker.numColumns")); - Border border = UIManager.getBorder("JXDatePicker.border"); - if (border != null) { - f.setBorder(border); - } - return f; - } - - protected JButton createPopupButton() { - JButton b = new JButton(); - b.setName("popupButton"); - b.setRolloverEnabled(false); - b.setMargin(new Insets(0, 3, 0, 3)); - - Icon icon = UIManager.getIcon("JXDatePicker.arrowIcon"); - if (icon == null) { - icon = (Icon)UIManager.get("Tree.expandedIcon"); - } - b.setIcon(icon); - b.setFocusable(false); - return b; - } - - /** - * - * A subclass of JFormattedTextField which calculates a "reasonable" - * minimum preferred size, independent of value/text.

                    - * - * Note: how to find the "reasonable" width is open to discussion. - * This implementation creates another datepicker, feeds it with - * the formats and asks its prefWidth.

                    - * - * PENDING: there's a resource property JXDatePicker.numColumns - why - * don't we use it? - */ - private class DefaultEditor extends JFormattedTextField implements UIResource { - - - public DefaultEditor(AbstractFormatter formatter) { - super(formatter); - } - - /** - * {@inheritDoc}

                    - * - * Overridden to return a preferred size which has a reasonable lower bound. - */ - @Override - public Dimension getPreferredSize() { - Dimension preferredSize = super.getPreferredSize(); - if (getColumns() <= 0) { - Dimension compare = getCompareMinimumSize(); - if (preferredSize.width < compare.width) { - return compare; - } - } - return preferredSize; - } - - /** - * {@inheritDoc}

                    - * - * Overridden to return the preferred size. - */ - @Override - public Dimension getMinimumSize() { - return getPreferredSize(); - } - - private Dimension getCompareMinimumSize() { - JFormattedTextField field = new JFormattedTextField(getFormatter()); - field.setMargin(getMargin()); - field.setBorder(getBorder()); - field.setFont(getFont()); - field.setValue(new Date()); - Dimension min = field.getPreferredSize(); - field.setValue(null); - min.width += Math.max(field.getPreferredSize().width, 4); - return min; - } - - - } - -// ---------------- Layout - /** - * {@inheritDoc} - */ - @Override - public Dimension getMinimumSize(JComponent c) { - return getPreferredSize(c); - } - - /** - * {@inheritDoc} - */ - @Override - public Dimension getPreferredSize(JComponent c) { - Dimension dim = datePicker.getEditor().getPreferredSize(); - if (popupButton != null) { - dim.width += popupButton.getPreferredSize().width; - } - Insets insets = datePicker.getInsets(); - dim.width += insets.left + insets.right; - dim.height += insets.top + insets.bottom; - return (Dimension)dim.clone(); - } - - - @Override - public int getBaseline(int width, int height) { - JFormattedTextField editor = datePicker.getEditor(); - View rootView = editor.getUI().getRootView(editor); - if (rootView.getViewCount() > 0) { - Insets insets = editor.getInsets(); - Insets insetsOut = datePicker.getInsets(); - int nh = height - insets.top - insets.bottom - - insetsOut.top - insetsOut.bottom; - int y = insets.top + insetsOut.top; - View fieldView = rootView.getView(0); - int vspan = (int) fieldView.getPreferredSpan(View.Y_AXIS); - if (nh != vspan) { - int slop = nh - vspan; - y += slop / 2; - } - FontMetrics fm = editor.getFontMetrics(editor.getFont()); - y += fm.getAscent(); - return y; - } - return -1; - } - - -//------------------------------- controller methods/classes - - /** - * {@inheritDoc} - */ - @Override - public Date getSelectableDate(Date date) throws PropertyVetoException { - Date cleaned = date == null ? null : - datePicker.getMonthView().getSelectionModel().getNormalizedDate(date); - if (CalendarUtils.areEqual(cleaned, datePicker.getDate())) { - // one place to interrupt the update spiral - throw new PropertyVetoException("date not selectable", null); - } - if (cleaned == null) return cleaned; - if (datePicker.getMonthView().isUnselectableDate(cleaned)) { - throw new PropertyVetoException("date not selectable", null); - } - return cleaned; - } - -//-------------------- update methods called from listeners - /** - * Updates internals after picker's date property changed. - */ - protected void updateFromDateChanged() { - Date visibleHook = datePicker.getDate() != null ? - datePicker.getDate() : datePicker.getLinkDay(); - datePicker.getMonthView().ensureDateVisible(visibleHook); - datePicker.getEditor().setValue(datePicker.getDate()); - } - - /** - * Updates date related properties in picker/monthView - * after a change in the editor's value. Reverts the - * value if the new date is unselectable. - * - * @param oldDate the editor value before the change - * @param newDate the editor value after the change - */ - protected void updateFromValueChanged(Date oldDate, Date newDate) { - if ((newDate != null) && datePicker.getMonthView().isUnselectableDate(newDate)) { - revertValue(oldDate); - return; - } - // the other place to interrupt the update spiral - if (!CalendarUtils.areEqual(newDate, datePicker.getMonthView().getSelectionDate())) { - datePicker.getMonthView().setSelectionDate(newDate); - } - datePicker.setDate(newDate); - } - - /** - * PENDING: currently this resets at once - but it's a no-no, - * because it happens during notification - * - * @param oldDate the old date to revert to - */ - private void revertValue(Date oldDate) { - datePicker.getEditor().setValue(oldDate); - } - /** - * Updates date related properties picker/editor - * after a change in the monthView's - * selection. - * - * Here: does nothing if the change is intermediate. - * - * PENDNG JW: shouldn't we listen to actionEvents then? - * - * @param eventType the type of the selection change - * @param adjusting flag to indicate whether the the selection change - * is intermediate - */ - protected void updateFromSelectionChanged(EventType eventType, boolean adjusting) { - if (adjusting) return; - updateEditorValue(); - } - - /** - * Updates internals after the picker's monthView has changed.

                    - * - * Cleans to popup. Wires the listeners. Updates date. - * Updates formats' timezone. - * - * @param oldMonthView the picker's monthView before the change, - * may be null. - */ - protected void updateFromMonthViewChanged(JXMonthView oldMonthView) { - uninstallPopup(); - updateMonthViewListeners(oldMonthView); - TimeZone oldTimeZone = null; - if (oldMonthView != null) { - oldMonthView.setComponentInputMapEnabled(false); - oldTimeZone = oldMonthView.getTimeZone(); - } - datePicker.getMonthView().setComponentInputMapEnabled(true); - updateTimeZone(oldTimeZone); - updateEditorValue(); - } - - - /** - * Updates internals after the picker's editor property - * has changed.

                    - * - * Updates the picker's children. Removes the old editor and - * adds the new editor. Wires the editor listeners, it the flag - * set. Typically, this method is called during installing the - * componentUI with the flag set to false and true at all other - * moments. - * - * - * @param oldEditor the picker's editor before the change, - * may be null. - * @param updateListeners a flag to indicate whether the listeners - * are ready for usage. - */ - protected void updateFromEditorChanged(JFormattedTextField oldEditor, - boolean updateListeners) { - if (oldEditor != null) { - datePicker.remove(oldEditor); - oldEditor.putClientProperty("doNotCancelPopup", null); - } - datePicker.add(datePicker.getEditor()); - // this is a trick to get hold of the client prop which - // prevents closing of the popup - JComboBox box = new JComboBox(); - Object preventHide = box.getClientProperty("doNotCancelPopup"); - datePicker.getEditor().putClientProperty("doNotCancelPopup", preventHide); - datePicker.getEditor().setInheritsPopupMenu(true); - - updateEditorValue(); - updateEditorProperties(); - if (updateListeners) { - updateEditorListeners(oldEditor); - datePicker.revalidate(); - } - } - - - /** - * Synchronizes the properties of the current editor to the properties of - * the JXDatePicker. - */ - private void updateEditorProperties() { - datePicker.getEditor().setEnabled(datePicker.isEnabled()); - datePicker.getEditor().setEditable(datePicker.isEditable()); - } - - /** - * Updates internals after the selection model changed. - * - * @param oldModel the model before the change. - */ - protected void updateFromSelectionModelChanged(DateSelectionModel oldModel) { - updateSelectionModelListeners(oldModel); - updateEditorValue(); - } - - /** - * Sets the editor value to the model's selectedDate. - */ - private void updateEditorValue() { - datePicker.getEditor().setValue(datePicker.getMonthView().getSelectionDate()); - } - - //---------------------- updating other properties - - - /** - * Updates properties which depend on the picker's editable.

                    - * - */ - protected void updateFromEditableChanged() { - boolean isEditable = datePicker.isEditable(); - // PENDING JW: revisit - align with combo's editable? - datePicker.getMonthView().setEnabled(isEditable); - datePicker.getEditor().setEditable(isEditable); - /* - * PatrykRy: Commit today date is not allowed if datepicker is not editable! - */ - setActionEnabled(JXDatePicker.HOME_COMMIT_KEY, isEditable); - // for consistency, synch navigation as well - setActionEnabled(JXDatePicker.HOME_NAVIGATE_KEY, isEditable); - } - - /** - * Update properties which depend on the picker's enabled. - */ - protected void updateFromEnabledChanged() { - boolean isEnabled = datePicker.isEnabled(); - popupButton.setEnabled(isEnabled); - datePicker.getEditor().setEnabled(isEnabled); - } - - - /** - * - * @param key - * @param enabled - */ - private void setActionEnabled(String key, boolean enabled) { - Action action = datePicker.getActionMap().get(key); - if (action != null) { - action.setEnabled(enabled); - } - } - - /** - * Updates the picker's formats to the given TimeZone. - * @param zone the timezone to set on the formats. - */ - protected void updateFormatsFromTimeZone(TimeZone zone) { - for (DateFormat format : datePicker.getFormats()) { - format.setTimeZone(zone); - } - } - - /** - * Updates picker's timezone dependent properties on change notification - * from the associated monthView. - * - * PENDING JW: DatePicker needs to send notification on timezone change? - * - * @param old the timezone before the change. - */ - protected void updateTimeZone(TimeZone old) { - updateFormatsFromTimeZone(datePicker.getTimeZone()); - updateLinkDate(); - } - - /** - * Updates the picker's linkDate to be in synch with monthView's today. - */ - protected void updateLinkDate() { - datePicker.setLinkDay(datePicker.getMonthView().getToday()); - } - - /** - * Called form property listener, updates all components locale, formats - * etc. - * - * @author PeS - */ - protected void updateLocale() { - Locale locale = datePicker.getLocale(); - updateFormatLocale(locale); - updateChildLocale(locale); - } - - private void updateFormatLocale(Locale locale) { - if (locale != null) { - // PENDING JW: timezone? - if (getCustomFormats(datePicker.getEditor()) == null) { - datePicker.getEditor().setFormatterFactory( - new DefaultFormatterFactory( - new DatePickerFormatterUIResource(locale))); - } - } - } - - private void updateChildLocale(Locale locale) { - if (locale != null) { - datePicker.getEditor().setLocale(locale); - datePicker.getLinkPanel().setLocale(locale); - datePicker.getMonthView().setLocale(locale); - } - } - - /** - * @param oldLinkPanel - * - */ - protected void updateLinkPanel(JComponent oldLinkPanel) { - if (oldLinkPanel != null) { - uninstallLinkPanelKeyboardActions(oldLinkPanel); - } - installLinkPanelKeyboardActions(); - if (popup != null) { - popup.updateLinkPanel(oldLinkPanel); - } - } - - -//------------------- methods called by installed actions - - /** - * - */ - protected void commit() { - hidePopup(); - try { - datePicker.commitEdit(); - } catch (ParseException ex) { - // can't help it - } - } - - /** - * - */ - protected void cancel() { - if (isPopupVisible()) { - popup.putClientProperty("JPopupMenu.firePopupMenuCanceled", Boolean.TRUE); - } - hidePopup(); - datePicker.cancelEdit(); - } - - /** - * PENDING: widened access for debugging - need api to - * control popup visibility? - */ - public void hidePopup() { - if (popup != null) popup.setVisible(false); - } - - public boolean isPopupVisible() { - if (popup != null) { - return popup.isVisible(); - } - return false; - } - /** - * Navigates to linkDate. If commit, the linkDate is selected - * and committed. If not commit, the linkDate is scrolled to visible, if the - * monthview is open, does nothing for invisible monthView. - * - * @param commit boolean to indicate whether the linkDate should be - * selected and committed - */ - protected void home(boolean commit) { - if (commit) { - Calendar cal = datePicker.getMonthView().getCalendar(); - cal.setTime(datePicker.getLinkDay()); - datePicker.getMonthView().setSelectionDate(cal.getTime()); - datePicker.getMonthView().commitSelection(); - } else { - datePicker.getMonthView().ensureDateVisible(datePicker.getLinkDay()); - } - } - -//---------------------- other stuff - - /** - * Creates and returns the action for committing the picker's - * input. - * - * @return - */ - private Action createCommitAction() { - Action action = new AbstractAction() { - - @Override - public void actionPerformed(ActionEvent e) { - commit(); - } - - }; - return action; - } - - /** - * Creates and returns the action for cancel the picker's - * edit. - * - * @return - */ - private Action createCancelAction() { - Action action = new AbstractAction() { - - @Override - public void actionPerformed(ActionEvent e) { - cancel(); - } - - }; - return action; - } - - private Action createHomeAction(final boolean commit) { - Action action = new AbstractAction( ) { - - @Override - public void actionPerformed(ActionEvent e) { - home(commit); - - } - - }; - return action ; - } - /** - * The wrapper for the editor cancel action. - * - * PENDING: Need to extend TestAction? - * - */ - public class EditorCancelAction extends AbstractAction { - private JFormattedTextField editor; - private Action cancelAction; - public static final String TEXT_CANCEL_KEY = "reset-field-edit"; - - public EditorCancelAction(JFormattedTextField field) { - install(field); - } - - /** - * Resets the contained editors actionMap to original and - * nulls all fields.

                    - * NOTE: after calling this method the action must not be - * used! Create a new one for the same or another editor. - * - */ - public void uninstall() { - editor.getActionMap().remove(TEXT_CANCEL_KEY); - cancelAction = null; - editor = null; - } - - /** - * @param editor - */ - private void install(JFormattedTextField editor) { - this.editor = editor; - cancelAction = editor.getActionMap().get(TEXT_CANCEL_KEY); - editor.getActionMap().put(TEXT_CANCEL_KEY, this); - } - - @Override - public void actionPerformed(ActionEvent e) { - cancelAction.actionPerformed(null); - cancel(); - } - - } - - /** - * Creates and returns the action which toggles the visibility of the popup. - * - * @return the action which toggles the visibility of the popup. - */ - protected TogglePopupAction createTogglePopupAction() { - return new TogglePopupAction(); - } - - /** - * Toggles the popups visibility after preparing internal state. - * - * - */ - public void toggleShowPopup() { - if (popup == null) { - installPopup(); - } - if (popup.isVisible()) { - popup.setVisible(false); - } else { - // PENDING JW: Issue 757-swing - datePicker firing focusLost on - // opening - // not with following line - but need to run tests - datePicker.getEditor().requestFocusInWindow(); -// datePicker.requestFocusInWindow(); - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { -// if (datePicker.getParent() == null) { -// // Tracking #1372-swingx - parent is null if used as -// // DatePickerCellEditor, -// // two different editors, clickCountToStart == 1 and -// // Metal -// // as a first hot fix, we back out -// LOG.info("couldn't show popup for: " + datePicker.getName()); -// return; -// } - popup.show(datePicker, 0, datePicker.getHeight()); - } - }); - } - } - - /** - * Creates the popup and registers the popup listener. All internal - * methods must use this method instead of calling createPopup directly. - */ - protected void installPopup() { - popup = createMonthViewPopup(); - popup.addPopupMenuListener(getPopupMenuListener()); - } - - /** - * Removes the popup listener from the popup and null it, if - * it was not null. All internal popup removal/replacement must - * use this method instead of nulling directly. - * - */ - protected void uninstallPopup() { - if (popup != null) { - popup.removePopupMenuListener(getPopupMenuListener()); - } - popup = null; - } - - /** - * Returns the PopupMenuListener for the MonthView popup. Lazily created. - * - * @return the popupuMenuListener to install on the popup - */ - protected PopupMenuListener getPopupMenuListener() { - if (popupMenuListener == null) { - popupMenuListener = createPopupMenuListener(); - } - return popupMenuListener; - } - - /** - * Creates and returns a PopupMenuListener. - * - * PENDING JW: the listener management assumes a stateless implementation - * relative to the popup/picker. Custom implementations should take care - * to not keep any references. - * - * @return - */ - protected PopupMenuListener createPopupMenuListener() { - PopupMenuListener l= new PopupMenuListener() { - - @Override - public void popupMenuCanceled(PopupMenuEvent e) { - PopupMenuListener[] ls = datePicker.getPopupMenuListeners(); - PopupMenuEvent retargeted = null; - for (PopupMenuListener listener : ls) { - if (retargeted == null) { - retargeted = new PopupMenuEvent(datePicker); - } - listener.popupMenuCanceled(retargeted); - } - } - - @Override - public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { - PopupMenuListener[] ls = datePicker.getPopupMenuListeners(); - PopupMenuEvent retargeted = null; - for (PopupMenuListener listener : ls) { - if (retargeted == null) { - retargeted = new PopupMenuEvent(datePicker); - } - listener.popupMenuWillBecomeInvisible(retargeted); - } - } - - @Override - public void popupMenuWillBecomeVisible(PopupMenuEvent e) { - PopupMenuListener[] ls = datePicker.getPopupMenuListeners(); - PopupMenuEvent retargeted = null; - for (PopupMenuListener listener : ls) { - if (retargeted == null) { - retargeted = new PopupMenuEvent(datePicker); - } - listener.popupMenuWillBecomeVisible(retargeted); - } - } - - }; - return l; - } - - - /** - * - */ - private BasicDatePickerPopup createMonthViewPopup() { - BasicDatePickerPopup popup = new BasicDatePickerPopup(); - popup.setLightWeightPopupEnabled(datePicker.isLightWeightPopupEnabled()); - return popup; - } - /** - * Action used to commit the current value in the JFormattedTextField. - * This action is used by the keyboard bindings. - */ - private class TogglePopupAction extends AbstractAction { - public TogglePopupAction() { - super("TogglePopup"); - } - - @Override - public void actionPerformed(ActionEvent ev) { - toggleShowPopup(); - } - } - - - /** - * Popup component that shows a JXMonthView component along with controlling - * buttons to allow traversal of the months. Upon selection of a date the - * popup will automatically hide itself and enter the selection into the - * editable field of the JXDatePicker. - * - */ - protected class BasicDatePickerPopup extends JPopupMenu { - - public BasicDatePickerPopup() { - setLayout(new BorderLayout()); - add(datePicker.getMonthView(), BorderLayout.CENTER); - updateLinkPanel(null); - } - - /** - * @param oldLinkPanel - */ - public void updateLinkPanel(JComponent oldLinkPanel) { - if (oldLinkPanel != null) { - remove(oldLinkPanel); - } - if (datePicker.getLinkPanel() != null) { - add(datePicker.getLinkPanel(), BorderLayout.SOUTH); - } - - } - } - - /** - * PENDING: JW - I really hate the one-in-all. Wont touch - * it for now, maybe later. As long as we have it, the new - * listeners (dateSelection) are here too, for consistency. - * Adding the Layout here as well is ... , IMO. - */ - private class Handler implements LayoutManager, MouseListener, MouseMotionListener, - PropertyChangeListener, DateSelectionListener, ActionListener, FocusListener { - -//------------- implement Mouse/MotionListener - private boolean _forwardReleaseEvent = false; - - @Override - public void mouseClicked(MouseEvent ev) { - } - - @Override - public void mousePressed(MouseEvent ev) { - if (!datePicker.isEnabled() || !SwingUtilities.isLeftMouseButton(ev)) { - return; - } - // PENDING JW: why do we need a mouseListener? the - // arrowbutton should have the toggleAction installed? - // Hmm... maybe doesn't ... check! - // reason might be that we want to open on pressed - // typically (or LF-dependent?), - // the button's action is invoked on released. -// LOG.info("opening on mousePressed?"); - toggleShowPopup(); - } - - @Override - public void mouseReleased(MouseEvent ev) { - if (!datePicker.isEnabled() || !datePicker.isEditable()) { - return; - } - - // Retarget mouse event to the month view. - if (_forwardReleaseEvent) { - JXMonthView monthView = datePicker.getMonthView(); - ev = SwingUtilities.convertMouseEvent(popupButton, ev, - monthView); - monthView.dispatchEvent(ev); - _forwardReleaseEvent = false; - } - } - - @Override - public void mouseEntered(MouseEvent ev) { - } - - @Override - public void mouseExited(MouseEvent ev) { - } - - @Override - public void mouseDragged(MouseEvent ev) { - if (!datePicker.isEnabled() || !datePicker.isEditable()) { - return; - } - - _forwardReleaseEvent = true; - - if (!popup.isShowing()) { - return; - } - - // Retarget mouse event to the month view. - JXMonthView monthView = datePicker.getMonthView(); - ev = SwingUtilities.convertMouseEvent(popupButton, ev, monthView); - monthView.dispatchEvent(ev); - } - - @Override - public void mouseMoved(MouseEvent ev) { - } -//------------------ implement DateSelectionListener - - @Override - public void valueChanged(DateSelectionEvent ev) { - updateFromSelectionChanged(ev.getEventType(), ev.isAdjusting()); - } - -//------------------ implement propertyChangeListener - /** - * {@inheritDoc} - */ - @Override - public void propertyChange(PropertyChangeEvent e) { - if (e.getSource() == datePicker) { - datePickerPropertyChange(e); - } else - if (e.getSource() == datePicker.getEditor()) { - editorPropertyChange(e); - } else - if (e.getSource() == datePicker.getMonthView()) { - monthViewPropertyChange(e); - } else - if (e.getSource() == popupButton) { - buttonPropertyChange(e); - } else - // PENDING - move back, ... - if ("value".equals(e.getPropertyName())) { - throw new IllegalStateException( - "editor listening is moved to dedicated propertyChangeLisener"); - } - } - - /** - * Handles property changes from datepicker's editor. - * - * @param e the PropertyChangeEvent object describing the event source - * and the property that has changed - */ - private void editorPropertyChange(PropertyChangeEvent evt) { - if ("value".equals(evt.getPropertyName())) { - updateFromValueChanged((Date) evt.getOldValue(), (Date) evt - .getNewValue()); - } - - } - - /** - * Handles property changes from DatePicker. - * @param e the PropertyChangeEvent object describing the - * event source and the property that has changed - */ - private void datePickerPropertyChange(PropertyChangeEvent e) { - String property = e.getPropertyName(); - if ("date".equals(property)) { - updateFromDateChanged(); - } else if ("enabled".equals(property)) { - updateFromEnabledChanged(); - } else if ("editable".equals(property)) { - updateFromEditableChanged(); - } else if (JComponent.TOOL_TIP_TEXT_KEY.equals(property)) { - String tip = datePicker.getToolTipText(); - datePicker.getEditor().setToolTipText(tip); - popupButton.setToolTipText(tip); - } else if (JXDatePicker.MONTH_VIEW.equals(property)) { - updateFromMonthViewChanged((JXMonthView) e.getOldValue()); - } else if (JXDatePicker.LINK_PANEL.equals(property)) { - updateLinkPanel((JComponent) e.getOldValue()); - } else if (JXDatePicker.EDITOR.equals(property)) { - updateFromEditorChanged((JFormattedTextField) e.getOldValue(), true); - } else if ("componentOrientation".equals(property)) { - datePicker.revalidate(); - } else if ("lightWeightPopupEnabled".equals(property)) { - // Force recreation of the popup when this property changes. - if (popup != null) { - popup.setVisible(false); - } - uninstallPopup(); - } else if ("formats".equals(property)) { - updateFormatsFromTimeZone(datePicker.getTimeZone()); - } - else if ("locale".equals(property)) { - updateLocale(); - } - } - - /** - * Handles propertyChanges from the picker's monthView. - * - * @param e the PropertyChangeEvent object describing the event source - * and the property that has changed - */ - private void monthViewPropertyChange(PropertyChangeEvent e) { - if ("selectionModel".equals(e.getPropertyName())) { - updateFromSelectionModelChanged((DateSelectionModel) e.getOldValue()); - } else if ("timeZone".equals(e.getPropertyName())) { - updateTimeZone((TimeZone) e.getOldValue()); - } else if ("today".equals(e.getPropertyName())) { - updateLinkDate(); - } - } - - /** - * Handles propertyChanges from the picker's popupButton. - * - * PENDING: does nothing, kept while refactoring .. which - * properties from the button do we want to handle? - * - * @param e the PropertyChangeEvent object describing the event source - * and the property that has changed. - */ - private void buttonPropertyChange(PropertyChangeEvent e) { - } - -//-------------- implement LayoutManager - - @Override - public void addLayoutComponent(String name, Component comp) { } - - @Override - public void removeLayoutComponent(Component comp) { } - - @Override - public Dimension preferredLayoutSize(Container parent) { - return parent.getPreferredSize(); - } - - @Override - public Dimension minimumLayoutSize(Container parent) { - return parent.getMinimumSize(); - } - - @Override - public void layoutContainer(Container parent) { - Insets insets = datePicker.getInsets(); - int width = datePicker.getWidth() - insets.left - insets.right; - int height = datePicker.getHeight() - insets.top - insets.bottom; - - int popupButtonWidth = popupButton != null ? popupButton.getPreferredSize().width : 0; - - boolean ltr = datePicker.getComponentOrientation().isLeftToRight(); - - datePicker.getEditor().setBounds(ltr ? insets.left : insets.left + popupButtonWidth, - insets.top, - width - popupButtonWidth, - height); - - if (popupButton != null) { - popupButton.setBounds(ltr ? width - popupButtonWidth + insets.left : insets.left, - insets.top, - popupButtonWidth, - height); - } - } - -// ------------- implement actionListener (listening to monthView actionEvent) - - @Override - public void actionPerformed(ActionEvent e) { - if (e == null) return; - if (e.getSource() == datePicker.getMonthView()) { - monthViewActionPerformed(e); - } else if (e.getSource() == datePicker.getEditor()) { - editorActionPerformed(e); - } - } - - /** - * Listening to actionEvents fired by the picker's editor. - * - * @param e - */ - private void editorActionPerformed(ActionEvent e) { - // pass the commit on to the picker. - commit(); - } - - /** - * Listening to actionEvents fired by the picker's monthView. - * - * @param e - */ - private void monthViewActionPerformed(ActionEvent e) { - if (JXMonthView.CANCEL_KEY.equals(e.getActionCommand())) { - cancel(); - } else if (JXMonthView.COMMIT_KEY.equals(e.getActionCommand())) { - commit(); - } - } - -//------------------- focusListener - - /** - * Issue #573-swingx - F2 in table doesn't focus the editor. - * - * Do the same as combo: manually pass-on the focus to the editor. - * - */ - @Override - public void focusGained(FocusEvent e) { - if (e.isTemporary()) return; - popupRemover.load(); - if (e.getSource() == datePicker) { - datePicker.getEditor().requestFocusInWindow(); - } - } - - /** - * #565-swingx: popup not hidden if clicked into combo. - * The problem is that the combo uses the same trick as - * this datepicker to prevent auto-closing of the popup - * if focus is transfered back to the picker's editor. - * - * The idea is to hide the popup manually when the - * permanentFocusOwner changes to somewhere else. - * - * JW: doesn't work - we only get the temporary lost, - * but no permanent loss if the focus is transfered from - * the focusOwner to a new permanentFocusOwner. - * - * OOOkaay ... looks like exclusively related to a combo: - * we do get the expected focusLost if the focus is - * transferred permanently from the temporary focusowner - * to a new "normal" permanentFocusOwner (like a textfield), - * we don't get it if transfered to a tricksing owner (like - * a combo or picker). So can't do anything here. - * - * listen to keyboardFocusManager? - */ - @Override - public void focusLost(FocusEvent e) { - - } - } - - public class PopupRemover implements PropertyChangeListener { - - private KeyboardFocusManager manager; - private boolean loaded; - - public void load() { - if (manager != KeyboardFocusManager.getCurrentKeyboardFocusManager()) { - unload(); - manager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); - } - if (!loaded) { - manager.addPropertyChangeListener("permanentFocusOwner", this); - loaded = true; - } - } - - /** - * @param b - */ - private void unload(boolean nullManager) { - if (manager != null) { - manager.removePropertyChangeListener("permanentFocusOwner", this); - if (nullManager) { - manager = null; - } - } - loaded = false; - } - - public void unload() { - unload(true); - } - - @Override - public void propertyChange(PropertyChangeEvent evt) { - if (!isPopupVisible()) { - unload(false); - return; - } - Component comp = manager.getPermanentFocusOwner(); - if ((comp != null) && !SwingXUtilities.isDescendingFrom(comp, datePicker)) { - unload(false); - // on hiding the popup the focusmanager transfers - // focus back to the old permanentFocusOwner - // before showing the popup, that is the picker - // or the editor. So we have to force it back ... - hidePopup(); - comp.requestFocusInWindow(); - // this has no effect as focus changes are asynchronous -// inHide = false; - } - } - - - } - - -// ------------------ listener creation - - /** - * Creates and returns the property change listener for the - * picker's monthView - * @return the listener for monthView properties - */ - protected PropertyChangeListener createMonthViewPropertyListener() { - return getHandler(); - } - - /** - * Creates and returns the focuslistener for picker and editor. - * @return the focusListener - */ - protected FocusListener createFocusListener() { - return getHandler(); - } - - - /** - * Creates and returns the ActionListener for the picker's editor. - * @return the Actionlistener for the editor. - */ - protected ActionListener createEditorActionListener() { - return getHandler(); - } - - /** - * Creates and returns the ActionListener for the picker's monthView. - * - * @return the Actionlistener for the monthView. - */ - protected ActionListener createMonthViewActionListener() { - return getHandler(); - } - -/** - * Returns the listener for the dateSelection. - * - * @return the date selection listener - */ - protected DateSelectionListener createMonthViewSelectionListener() { - return getHandler(); - } - - /** - * @return a propertyChangeListener listening to - * editor property changes - */ - protected PropertyChangeListener createEditorPropertyListener() { - return getHandler(); - } - - /** - * Lazily creates and returns the shared all-mighty listener of everything - * - * @return the shared listener. - */ - private Handler getHandler() { - if (handler == null) { - handler = new Handler(); - } - return handler; - } - - protected PropertyChangeListener createPropertyChangeListener() { - return getHandler(); - } - - protected LayoutManager createLayoutManager() { - return getHandler(); - } - - protected MouseListener createMouseListener() { - return getHandler(); - } - - protected MouseMotionListener createMouseMotionListener() { - return getHandler(); - } - - -//------------ utility methods - - -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicErrorPaneUI.java b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicErrorPaneUI.java deleted file mode 100644 index c59e403024..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicErrorPaneUI.java +++ /dev/null @@ -1,1098 +0,0 @@ -/* - * $Id: BasicErrorPaneUI.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf.basic; - -import org.jdesktop.swingx.JXEditorPane; -import org.jdesktop.swingx.JXErrorPane; -import org.jdesktop.swingx.action.AbstractActionExt; -import org.jdesktop.swingx.error.ErrorInfo; -import org.jdesktop.swingx.error.ErrorLevel; -import org.jdesktop.swingx.error.ErrorReporter; -import org.jdesktop.swingx.plaf.ErrorPaneUI; -import org.jdesktop.swingx.plaf.UIManagerExt; -import org.jdesktop.swingx.util.WindowUtils; - -import javax.swing.*; -import javax.swing.border.EmptyBorder; -import javax.swing.plaf.ComponentUI; -import javax.swing.plaf.UIResource; -import javax.swing.plaf.basic.BasicHTML; -import javax.swing.text.JTextComponent; -import javax.swing.text.StyledEditorKit; -import javax.swing.text.html.HTMLEditorKit; -import java.awt.*; -import java.awt.datatransfer.StringSelection; -import java.awt.datatransfer.Transferable; -import java.awt.event.*; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.logging.Level; - -/** - * Base implementation of the JXErrorPane UI. - * - * @author rbair - * @author rah003 - */ -public class BasicErrorPaneUI extends ErrorPaneUI { - /** - * Used as a prefix when pulling data out of UIManager for i18n - */ - protected static final String CLASS_NAME = "JXErrorPane"; - - /** - * The error pane this UI is for - */ - protected JXErrorPane pane; - /** - * Error message text area - */ - protected JEditorPane errorMessage; - - /** - * Error message text scroll pane wrapper. - */ - protected JScrollPane errorScrollPane; - /** - * details text area - */ - protected JXEditorPane details; - /** - * detail button - */ - protected AbstractButton detailButton; - /** - * ok/close button - */ - protected JButton closeButton; - /** - * label used to display the warning/error icon - */ - protected JLabel iconLabel; - /** - * report an error button - */ - protected AbstractButton reportButton; - /** - * details panel - */ - protected JPanel detailsPanel; - protected JScrollPane detailsScrollPane; - protected JButton copyToClipboardButton; - - /** - * Property change listener for the error pane ensures that the pane's UI - * is reinitialized. - */ - protected PropertyChangeListener errorPaneListener; - - /** - * Action listener for the detail button. - */ - protected ActionListener detailListener; - - /** - * Action listener for the copy to clipboard button. - */ - protected ActionListener copyToClipboardListener; - - //------------------------------------------------------ private helpers - /** - * The height of the window when collapsed. This value is stashed when the - * dialog is expanded - */ - private int collapsedHeight = 0; - /** - * The height of the window when last expanded. This value is stashed when - * the dialog is collapsed - */ - private int expandedHeight = 0; - - //---------------------------------------------------------- constructor - - /** - * {@inheritDoc} - */ - public static ComponentUI createUI(JComponent c) { - return new BasicErrorPaneUI(); - } - - /** - * {@inheritDoc} - */ - @Override - public void installUI(JComponent c) { - super.installUI(c); - - this.pane = (JXErrorPane)c; - - installDefaults(); - installComponents(); - installListeners(); - - //if the report action needs to be defined, do so - Action a = c.getActionMap().get(JXErrorPane.REPORT_ACTION_KEY); - if (a == null) { - final JXErrorPane pane = (JXErrorPane)c; - AbstractActionExt reportAction = new AbstractActionExt() { - @Override - public void actionPerformed(ActionEvent e) { - ErrorReporter reporter = pane.getErrorReporter(); - if (reporter != null) { - reporter.reportError(pane.getErrorInfo()); - } - } - }; - configureReportAction(reportAction); - c.getActionMap().put(JXErrorPane.REPORT_ACTION_KEY, reportAction); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void uninstallUI(JComponent c) { - super.uninstallUI(c); - - uninstallListeners(); - uninstallComponents(); - uninstallDefaults(); - } - - /** - * Installs the default colors, and default font into the Error Pane - */ - protected void installDefaults() { - } - - - /** - * Uninstalls the default colors, and default font into the Error Pane. - */ - protected void uninstallDefaults() { - LookAndFeel.uninstallBorder(pane); - } - - - /** - * Create and install the listeners for the Error Pane. - * This method is called when the UI is installed. - */ - protected void installListeners() { - //add a listener to the pane so I can reinit() whenever the - //bean properties change (particularly error info) - errorPaneListener = new ErrorPaneListener(); - pane.addPropertyChangeListener(errorPaneListener); - } - - - /** - * Remove the installed listeners from the Error Pane. - * The number and types of listeners removed and in this method should be - * the same that was added in installListeners - */ - protected void uninstallListeners() { - //remove the property change listener from the pane - pane.removePropertyChangeListener(errorPaneListener); - } - - - // =============================== - // begin Sub-Component Management - // - - /** - * Creates and initializes the components which make up the - * aggregate combo box. This method is called as part of the UI - * installation process. - */ - protected void installComponents() { - iconLabel = new JLabel(pane.getIcon()); - - errorMessage = new JEditorPane(); - errorMessage.setEditable(false); - errorMessage.setContentType("text/html"); - errorMessage.setEditorKitForContentType("text/plain", new StyledEditorKit()); - errorMessage.setEditorKitForContentType("text/html", new HTMLEditorKit()); - - errorMessage.setOpaque(false); - errorMessage.putClientProperty(JXEditorPane.HONOR_DISPLAY_PROPERTIES, Boolean.TRUE); - - closeButton = new JButton(UIManagerExt.getString( - CLASS_NAME + ".ok_button_text", errorMessage.getLocale())); - - reportButton = new EqualSizeJButton(pane.getActionMap().get(JXErrorPane.REPORT_ACTION_KEY)); - - detailButton = new EqualSizeJButton(UIManagerExt.getString( - CLASS_NAME + ".details_expand_text", errorMessage.getLocale())); - - details = new JXEditorPane(); - details.setContentType("text/html"); - details.putClientProperty(JXEditorPane.HONOR_DISPLAY_PROPERTIES, Boolean.TRUE); - details.setTransferHandler(createDetailsTransferHandler(details)); - detailsScrollPane = new JScrollPane(details); - detailsScrollPane.setPreferredSize(new Dimension(10, 250)); - details.setEditable(false); - detailsPanel = new JPanel(); - detailsPanel.setVisible(false); - copyToClipboardButton = new JButton(UIManagerExt.getString( - CLASS_NAME + ".copy_to_clipboard_button_text", errorMessage.getLocale())); - copyToClipboardListener = new ActionListener() { - @Override - public void actionPerformed(ActionEvent ae) { - details.copy(); - } - }; - copyToClipboardButton.addActionListener(copyToClipboardListener); - - detailsPanel.setLayout(createDetailPanelLayout()); - detailsPanel.add(detailsScrollPane); - detailsPanel.add(copyToClipboardButton); - - // Create error scroll pane. Make sure this happens before call to createErrorPaneLayout() in case any extending - // class wants to manipulate the component there. - errorScrollPane = new JScrollPane(errorMessage); - errorScrollPane.setBorder(new EmptyBorder(0,0,5,0)); - errorScrollPane.setOpaque(false); - errorScrollPane.getViewport().setOpaque(false); - - //initialize the gui. Most of this code is similar between Mac and PC, but - //where they differ protected methods have been written allowing the - //mac implementation to alter the layout of the dialog. - pane.setLayout(createErrorPaneLayout()); - - //An empty border which constitutes the padding from the edge of the - //dialog to the content. All content that butts against this border should - //not be padded. - Insets borderInsets = new Insets(16, 24, 16, 17); - pane.setBorder(BorderFactory.createEmptyBorder(borderInsets.top, borderInsets.left, borderInsets.bottom, borderInsets.right)); - - //add the JLabel responsible for displaying the icon. - //TODO: in the future, replace this usage of a JLabel with a JXImagePane, - //which may add additional "coolness" such as allowing the user to drag - //the image off the dialog onto the desktop. This kind of coolness is common - //in the mac world. - pane.add(iconLabel); - pane.add(errorScrollPane); - pane.add(closeButton); - pane.add(reportButton); - reportButton.setVisible(false); // not visible by default - pane.add(detailButton); - pane.add(detailsPanel); - - //make the buttons the same size - EqualSizeJButton[] buttons = new EqualSizeJButton[] { - (EqualSizeJButton)detailButton, (EqualSizeJButton)reportButton }; - ((EqualSizeJButton)reportButton).setGroup(buttons); - ((EqualSizeJButton)detailButton).setGroup(buttons); - - reportButton.setMinimumSize(reportButton.getPreferredSize()); - detailButton.setMinimumSize(detailButton.getPreferredSize()); - - //set the event handling - detailListener = new DetailsClickEvent(); - detailButton.addActionListener(detailListener); - } - - /** - * The aggregate components which compise the combo box are - * unregistered and uninitialized. This method is called as part of the - * UI uninstallation process. - */ - protected void uninstallComponents() { - iconLabel = null; - errorMessage = null; - closeButton = null; - reportButton = null; - - detailButton.removeActionListener(detailListener); - detailButton = null; - - details.setTransferHandler(null); - details = null; - - detailsScrollPane.removeAll(); - detailsScrollPane = null; - - detailsPanel.setLayout(null); - detailsPanel.removeAll(); - detailsPanel = null; - - copyToClipboardButton.removeActionListener(copyToClipboardListener); - copyToClipboardButton = null; - - pane.removeAll(); - pane.setLayout(null); - pane.setBorder(null); - } - - // - // end Sub-Component Management - // =============================== - - /** - * @inheritDoc - */ - @Override - public JFrame getErrorFrame(Component owner) { - reinit(); - expandedHeight = 0; - collapsedHeight = 0; - JXErrorFrame frame = new JXErrorFrame(pane); - centerWindow(frame, owner); - return frame; - } - - /** - * @inheritDoc - */ - @Override - public JDialog getErrorDialog(Component owner) { - reinit(); - expandedHeight = 0; - collapsedHeight = 0; - Window w = WindowUtils.findWindow(owner); - JXErrorDialog dlg = null; - if (w instanceof Dialog) { - dlg = new JXErrorDialog((Dialog)w, pane); - } else if (w instanceof Frame) { - dlg = new JXErrorDialog((Frame)w, pane); - } else { - // default fallback to null - dlg = new JXErrorDialog(JOptionPane.getRootFrame(), pane); - } - centerWindow(dlg, owner); - return dlg; - } - - /** - * @inheritDoc - */ - @Override - public JInternalFrame getErrorInternalFrame(Component owner) { - reinit(); - expandedHeight = 0; - collapsedHeight = 0; - JXInternalErrorFrame frame = new JXInternalErrorFrame(pane); - centerWindow(frame, owner); - return frame; - } - - /** - * Create and return the LayoutManager to use with the error pane. - */ - protected LayoutManager createErrorPaneLayout() { - return new ErrorPaneLayout(); - } - - protected LayoutManager createDetailPanelLayout() { - GridBagLayout layout = new GridBagLayout(); - layout.addLayoutComponent(detailsScrollPane, new GridBagConstraints(0,0,1,1,1.0,1.0,GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(6,0,0,0),0,0)); - GridBagConstraints gbc = new GridBagConstraints(); - gbc.anchor = GridBagConstraints.LINE_END; - gbc.fill = GridBagConstraints.NONE; - gbc.gridwidth = 1; - gbc.gridx = 0; - gbc.gridy = 1; - gbc.weighty = 0.0; - gbc.weightx = 1.0; - gbc.insets = new Insets(6, 0, 6, 0); - layout.addLayoutComponent(copyToClipboardButton, gbc); - return layout; - } - - @Override - public Dimension calculatePreferredSize() { - //TODO returns a Dimension that is either X wide, or as wide as necessary - //to show the title. It is Y high. - return new Dimension(iconLabel.getPreferredSize().width + errorMessage.getPreferredSize().width, 206); - } - - protected int getDetailsHeight() { - return 300; - } - - protected void configureReportAction(AbstractActionExt reportAction) { - reportAction.setName(UIManagerExt.getString(CLASS_NAME + ".report_button_text", pane.getLocale())); - } - - //----------------------------------------------- private helper methods - - /** - * Creates and returns a TransferHandler which can be used to copy the details - * from the details component. It also disallows pasting into the component, or - * cutting from the component. - * - * @return a TransferHandler for the details area - */ - private TransferHandler createDetailsTransferHandler(JTextComponent detailComponent) { - return new DetailsTransferHandler(detailComponent); - } - - /** - * @return the default error icon - */ - protected Icon getDefaultErrorIcon() { - try { - Icon icon = UIManager.getIcon(CLASS_NAME + ".errorIcon"); - return icon == null ? UIManager.getIcon("OptionPane.errorIcon") : icon; - } catch (Exception e) { - return null; - } - } - - /** - * @return the default warning icon - */ - protected Icon getDefaultWarningIcon() { - try { - Icon icon = UIManager.getIcon(CLASS_NAME + ".warningIcon"); - return icon == null ? UIManager.getIcon("OptionPane.warningIcon") : icon; - } catch (Exception e) { - return null; - } - } - - /** - * Set the details section of the error dialog. If the details are either - * null or an empty string, then hide the details button and hide the detail - * scroll pane. Otherwise, just set the details section. - * - * @param details Details to be shown in the detail section of the dialog. - * This can be null if you do not want to display the details section of the - * dialog. - */ - private void setDetails(String details) { - if (details == null || details.equals("")) { - detailButton.setVisible(false); - } else { - this.details.setText(details); - detailButton.setVisible(true); - } - } - - protected void configureDetailsButton(boolean expanded) { - if (expanded) { - detailButton.setText(UIManagerExt.getString( - CLASS_NAME + ".details_contract_text", detailButton.getLocale())); - } else { - detailButton.setText(UIManagerExt.getString( - CLASS_NAME + ".details_expand_text", detailButton.getLocale())); - } - } - - /** - * Set the details section to be either visible or invisible. Set the - * text of the Details button accordingly. - * @param b if true details section will be visible - */ - private void setDetailsVisible(boolean b) { - if (b) { - collapsedHeight = pane.getHeight(); - pane.setSize(pane.getWidth(), expandedHeight == 0 ? collapsedHeight + getDetailsHeight() : expandedHeight); - detailsPanel.setVisible(true); - configureDetailsButton(true); - detailsPanel.applyComponentOrientation(detailButton.getComponentOrientation()); - - // workaround for bidi bug, if the text is not set "again" and the component orientation has changed - // then the text won't be aligned correctly. To reproduce this (in JDK 1.5) show two dialogs in one - // use LTOR orientation and in the second use RTOL orientation and press "details" in both. - // Text in the text box should be aligned to right/left respectively, without this line this doesn't - // occure I assume because bidi properties are tested when the text is set and are not updated later - // on when setComponentOrientation is invoked. - details.setText(details.getText()); - details.setCaretPosition(0); - } else if (collapsedHeight != 0) { //only collapse if the dialog has been expanded - expandedHeight = pane.getHeight(); - detailsPanel.setVisible(false); - configureDetailsButton(false); - // Trick to force errorMessage JTextArea to resize according - // to its columns property. - errorMessage.setSize(0, 0); - errorMessage.setSize(errorMessage.getPreferredSize()); - pane.setSize(pane.getWidth(), collapsedHeight); - } else { - detailsPanel.setVisible(false); - } - - pane.doLayout(); - } - - /** - * Set the error message for the dialog box - * @param errorMessage Message for the error dialog - */ - private void setErrorMessage(String errorMessage) { - if(BasicHTML.isHTMLString(errorMessage)) { - this.errorMessage.setContentType("text/html"); - } else { - this.errorMessage.setContentType("text/plain"); - } - this.errorMessage.setText(errorMessage); - this.errorMessage.setCaretPosition(0); - } - - /** - * Reconfigures the dialog if settings have changed, such as the - * errorInfo, errorIcon, warningIcon, etc - */ - protected void reinit() { - setDetailsVisible(false); - Action reportAction = pane.getActionMap().get(JXErrorPane.REPORT_ACTION_KEY); - reportButton.setAction(reportAction); - reportButton.setVisible(reportAction != null && reportAction.isEnabled() && pane.getErrorReporter() != null); - reportButton.setEnabled(reportButton.isVisible()); - ErrorInfo errorInfo = pane.getErrorInfo(); - if (errorInfo == null) { - iconLabel.setIcon(pane.getIcon()); - setErrorMessage(""); - closeButton.setText(UIManagerExt.getString( - CLASS_NAME + ".ok_button_text", closeButton.getLocale())); - setDetails(""); - //TODO Does this ever happen? It seems like if errorInfo is null and - //this is called, it would be an IllegalStateException. - } else { - //change the "closeButton"'s text to either the default "ok"/"close" text - //or to the "fatal" text depending on the error level of the incident info - if (errorInfo.getErrorLevel() == ErrorLevel.FATAL) { - closeButton.setText(UIManagerExt.getString( - CLASS_NAME + ".fatal_button_text", closeButton.getLocale())); - } else { - closeButton.setText(UIManagerExt.getString( - CLASS_NAME + ".ok_button_text", closeButton.getLocale())); - } - - //if the icon for the pane has not been specified by the developer, - //then set it to the default icon based on the error level - Icon icon = pane.getIcon(); - if (icon == null || icon instanceof UIResource) { - if (errorInfo.getErrorLevel().intValue() <= Level.WARNING.intValue()) { - icon = getDefaultWarningIcon(); - } else { - icon = getDefaultErrorIcon(); - } - } - iconLabel.setIcon(icon); - setErrorMessage(errorInfo.getBasicErrorMessage()); - String details = errorInfo.getDetailedErrorMessage(); - if(details == null) { - details = getDetailsAsHTML(errorInfo); - } - setDetails(details); - } - } - - /** - * Creates and returns HTML representing the details of this incident info. This - * method is only called if the details needs to be generated: ie: the detailed - * error message property of the incident info is null. - */ - protected String getDetailsAsHTML(ErrorInfo errorInfo) { - if(errorInfo.getErrorException() != null) { - //convert the stacktrace into a more pleasent bit of HTML - StringBuffer html = new StringBuffer(""); - html.append("

                    " + escapeXml(errorInfo.getTitle()) + "

                    "); - html.append("
                    "); - html.append("
                    "); - html.append("Message:"); - html.append("
                    ");
                    -            html.append("    " + escapeXml(errorInfo.getErrorException().toString()));
                    -            html.append("
                    "); - html.append("Level:"); - html.append("
                    ");
                    -            html.append("    " + errorInfo.getErrorLevel());
                    -            html.append("
                    "); - html.append("Stack Trace:"); - Throwable ex = errorInfo.getErrorException(); - while(ex != null) { - html.append("

                    "+ex.getMessage()+"

                    "); - html.append("
                    ");
                    -                for (StackTraceElement el : ex.getStackTrace()) {
                    -                    html.append("    " + el.toString().replace("", "<init>") + "\n");
                    -                }
                    -                html.append("
                    "); - ex = ex.getCause(); - } - html.append(""); - return html.toString(); - } else { - return null; - } - } - - //------------------------------------------------ actions/inner classes - - /** - * Default action for closing the JXErrorPane's enclosing window - * (JDialog, JFrame, or JInternalFrame) - */ - private static final class CloseAction extends AbstractAction { - private Window w; - - /** - * @param w cannot be null - */ - private CloseAction(Window w) { - if (w == null) { - throw new NullPointerException("Window cannot be null"); - } - this.w = w; - } - - /** - * @inheritDoc - */ - @Override - public void actionPerformed(ActionEvent e) { - w.setVisible(false); - w.dispose(); - } - } - - - /** - * Listener for Details click events. Alternates whether the details section - * is visible or not. - * - * @author rbair - */ - private final class DetailsClickEvent implements ActionListener { - - /* (non-Javadoc) - * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) - */ - @Override - public void actionPerformed(ActionEvent e) { - setDetailsVisible(!detailsPanel.isVisible()); - } - } - - private final class ResizeWindow implements ActionListener { - private Window w; - private ResizeWindow(Window w) { - if (w == null) { - throw new NullPointerException(); - } - this.w = w; - } - - @Override - public void actionPerformed(ActionEvent ae) { - Dimension contentSize = null; - if (w instanceof JDialog) { - contentSize = ((JDialog)w).getContentPane().getSize(); - } else { - contentSize = ((JFrame)w).getContentPane().getSize(); - } - - Dimension dialogSize = w.getSize(); - int ydiff = dialogSize.height - contentSize.height; - Dimension paneSize = pane.getSize(); - w.setSize(new Dimension(dialogSize.width, paneSize.height + ydiff)); - w.validate(); - w.repaint(); - } - } - - /** - * This is a button that maintains the size of the largest button in the button - * group by returning the largest size from the getPreferredSize method. - * This is better than using setPreferredSize since this will work regardless - * of changes to the text of the button and its language. - */ - private static final class EqualSizeJButton extends JButton { - - public EqualSizeJButton(String text) { - super(text); - } - - public EqualSizeJButton(Action a) { - super(a); - } - - /** - * Buttons whose size should be taken into consideration - */ - private EqualSizeJButton[] group; - - public void setGroup(EqualSizeJButton[] group) { - this.group = group; - } - - /** - * Returns the actual preferred size on a different instance of this button - */ - private Dimension getRealPreferredSize() { - return super.getPreferredSize(); - } - - /** - * If the preferredSize has been set to a - * non-null value just returns it. - * If the UI delegate's getPreferredSize - * method returns a non null value then return that; - * otherwise defer to the component's layout manager. - * - * @return the value of the preferredSize property - * @see #setPreferredSize - * @see ComponentUI - */ - @Override - public Dimension getPreferredSize() { - int width = 0; - int height = 0; - for(int iter = 0 ; iter < group.length ; iter++) { - Dimension size = group[iter].getRealPreferredSize(); - width = Math.max(size.width, width); - height = Math.max(size.height, height); - } - - return new Dimension(width, height); - } - - } - - /** - * Returns the text as non-HTML in a COPY operation, and disabled CUT/PASTE - * operations for the Details pane. - */ - private static final class DetailsTransferHandler extends TransferHandler { - private JTextComponent details; - private DetailsTransferHandler(JTextComponent detailComponent) { - if (detailComponent == null) { - throw new NullPointerException("detail component cannot be null"); - } - this.details = detailComponent; - } - - @Override - protected Transferable createTransferable(JComponent c) { - String text = details.getSelectedText(); - if (text == null || text.equals("")) { - details.selectAll(); - text = details.getSelectedText(); - details.select(-1, -1); - } - return new StringSelection(text); - } - - @Override - public int getSourceActions(JComponent c) { - return TransferHandler.COPY; - } - - } - - private final class JXErrorDialog extends JDialog { - public JXErrorDialog(Frame parent, JXErrorPane p) { - super(parent, true); - init(p); - } - - public JXErrorDialog(Dialog parent, JXErrorPane p) { - super(parent, true); - init(p); - } - - protected void init(JXErrorPane p) { - // FYI: info can be null - setTitle(p.getErrorInfo() == null ? null : p.getErrorInfo().getTitle()); - initWindow(this, p); - } - } - - private final class JXErrorFrame extends JFrame { - public JXErrorFrame(JXErrorPane p) { - setTitle(p.getErrorInfo().getTitle()); - initWindow(this, p); - } - } - - private final class JXInternalErrorFrame extends JInternalFrame { - public JXInternalErrorFrame(JXErrorPane p) { - setTitle(p.getErrorInfo().getTitle()); - - setLayout(new BorderLayout()); - add(p, BorderLayout.CENTER); - final Action closeAction = new AbstractAction() { - @Override - public void actionPerformed(ActionEvent evt) { - setVisible(false); - dispose(); - } - }; - closeButton.addActionListener(closeAction); - addComponentListener(new ComponentAdapter() { - @Override - public void componentHidden(ComponentEvent e) { - //remove the action listener - closeButton.removeActionListener(closeAction); - exitIfFatal(); - } - }); - - getRootPane().setDefaultButton(closeButton); - setResizable(false); - setDefaultCloseOperation(JInternalFrame.DISPOSE_ON_CLOSE); - KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); - getRootPane().registerKeyboardAction(closeAction, ks, JComponent.WHEN_IN_FOCUSED_WINDOW); - //setPreferredSize(calculatePreferredDialogSize()); - } - } - - /** - * Utility method for initializing a Window for displaying a JXErrorPane. - * This is particularly useful because the differences between JFrame and - * JDialog are so minor. - * removed. - */ - private void initWindow(final Window w, final JXErrorPane pane) { - w.setLayout(new BorderLayout()); - w.add(pane, BorderLayout.CENTER); - final Action closeAction = new CloseAction(w); - closeButton.addActionListener(closeAction); - final ResizeWindow resizeListener = new ResizeWindow(w); - //make sure this action listener is last (or, oddly, the first in the list) - ActionListener[] list = detailButton.getActionListeners(); - for (ActionListener a : list) { - detailButton.removeActionListener(a); - } - detailButton.addActionListener(resizeListener); - for (ActionListener a : list) { - detailButton.addActionListener(a); - } - - if (w instanceof JFrame) { - final JFrame f = (JFrame)w; - f.getRootPane().setDefaultButton(closeButton); - f.setResizable(true); - f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); - KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); - f.getRootPane().registerKeyboardAction(closeAction, ks, JComponent.WHEN_IN_FOCUSED_WINDOW); - } else if (w instanceof JDialog) { - final JDialog d = (JDialog)w; - d.getRootPane().setDefaultButton(closeButton); - d.setResizable(true); - d.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); - KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); - d.getRootPane().registerKeyboardAction(closeAction, ks, JComponent.WHEN_IN_FOCUSED_WINDOW); - } - - w.addWindowListener(new WindowAdapter() { - @Override - public void windowClosing(WindowEvent e) { - //remove the action listener - closeButton.removeActionListener(closeAction); - detailButton.removeActionListener(resizeListener); - exitIfFatal(); - } - }); - w.pack(); - } - - private void exitIfFatal() { - ErrorInfo info = pane.getErrorInfo(); - // FYI: info can be null - if (info != null && info.getErrorLevel() == ErrorLevel.FATAL) { - Action fatalAction = pane.getActionMap().get(JXErrorPane.FATAL_ACTION_KEY); - if (fatalAction == null) { - System.exit(1); - } else { - ActionEvent ae = new ActionEvent(closeButton, -1, "fatal"); - fatalAction.actionPerformed(ae); - } - } - } - - private final class ErrorPaneListener implements PropertyChangeListener { - @Override - public void propertyChange(PropertyChangeEvent evt) { - reinit(); - } - } - - /** - * Lays out the BasicErrorPaneUI components. - */ - private final class ErrorPaneLayout implements LayoutManager { - private JEditorPane dummy = new JEditorPane(); - - @Override - public void addLayoutComponent(String name, Component comp) {} - @Override - public void removeLayoutComponent(Component comp) {} - - /** - * The preferred size is: - * The width of the parent container - * The height necessary to show the entire message text - * (as long as said height does not go off the screen) - * plus the buttons - * - * The preferred height changes depending on whether the details - * are visible, or not. - */ - @Override - public Dimension preferredLayoutSize(Container parent) { - int prefWidth = parent.getWidth(); - int prefHeight = parent.getHeight(); - final Insets insets = parent.getInsets(); - int pw = detailButton.isVisible() ? detailButton.getPreferredSize().width : 0; - pw += detailButton.isVisible() ? detailButton.getPreferredSize().width : 0; - pw += reportButton.isVisible() ? (5 + reportButton.getPreferredSize().width) : 0; - pw += closeButton.isVisible() ? (5 + closeButton.getPreferredSize().width) : 0; - prefWidth = Math.max(prefWidth, pw) + insets.left + insets.right; - if (errorMessage != null) { - //set a temp editor to a certain size, just to determine what its - //pref height is - dummy.setContentType(errorMessage.getContentType()); - dummy.setEditorKit(errorMessage.getEditorKit()); - dummy.setText(errorMessage.getText()); - dummy.setSize(prefWidth, 20); - int errorMessagePrefHeight = dummy.getPreferredSize().height; - - prefHeight = - //the greater of the error message height or the icon height - Math.max(errorMessagePrefHeight, iconLabel.getPreferredSize().height) + - //the space between the error message and the button - 10 + - //the button preferred height - closeButton.getPreferredSize().height; - - if (detailsPanel.isVisible()) { - prefHeight += getDetailsHeight(); - } - - } - - if (iconLabel != null && iconLabel.getIcon() != null) { - prefWidth += iconLabel.getIcon().getIconWidth(); - prefHeight += 10; // top of icon is positioned 10px above the text - } - - return new Dimension( - prefWidth + insets.left + insets.right, - prefHeight + insets.top + insets.bottom); - } - - @Override - public Dimension minimumLayoutSize(Container parent) { - return preferredLayoutSize(parent); - } - - @Override - public void layoutContainer(Container parent) { - final Insets insets = parent.getInsets(); - int x = insets.left; - int y = insets.top; - - //place the icon - if (iconLabel != null) { - Dimension dim = iconLabel.getPreferredSize(); - iconLabel.setBounds(x, y, dim.width, dim.height); - x += dim.width + 17; - int leftEdge = x; - - //place the error message - dummy.setContentType(errorMessage.getContentType()); - dummy.setText(errorMessage.getText()); - dummy.setSize(parent.getWidth() - leftEdge - insets.right, 20); - dim = dummy.getPreferredSize(); - int spx = x; - int spy = y; - Dimension spDim = new Dimension (parent.getWidth() - leftEdge - insets.right, dim.height); - y += dim.height + 10; - int rightEdge = parent.getWidth() - insets.right; - x = rightEdge; - dim = detailButton.getPreferredSize(); //all buttons should be the same height! - int buttonY = y + 5; - if (detailButton.isVisible()) { - dim = detailButton.getPreferredSize(); - x -= dim.width; - detailButton.setBounds(x, buttonY, dim.width, dim.height); - } - if (detailButton.isVisible()) { - detailButton.setBounds(x, buttonY, dim.width, dim.height); - } - errorScrollPane.setBounds(spx, spy, spDim.width, buttonY - spy); - if (reportButton.isVisible()) { - dim = reportButton.getPreferredSize(); - x -= dim.width; - x -= 5; - reportButton.setBounds(x, buttonY, dim.width, dim.height); - } - - dim = closeButton.getPreferredSize(); - x -= dim.width; - x -= 5; - closeButton.setBounds(x, buttonY, dim.width, dim.height); - - //if the dialog is expanded... - if (detailsPanel.isVisible()) { - //layout the details - y = buttonY + dim.height + 6; - x = leftEdge; - int width = rightEdge - x; - detailsPanel.setBounds(x, y, width, parent.getHeight() - (y + insets.bottom) ); - } - } - } - } - - private static void centerWindow(Window w, Component owner) { - //center based on the owner component, if it is not null - //otherwise, center based on the center of the screen - if (owner != null) { - Point p = owner.getLocation(); - p.x += owner.getWidth()/2; - p.y += owner.getHeight()/2; - SwingUtilities.convertPointToScreen(p, owner); - w.setLocation(p); - } else { - w.setLocation(WindowUtils.getPointForCentering(w)); - } - } - - private static void centerWindow(JInternalFrame w, Component owner) { - //center based on the owner component, if it is not null - //otherwise, center based on the center of the screen - if (owner != null) { - Point p = owner.getLocation(); - p.x += owner.getWidth()/2; - p.y += owner.getHeight()/2; - SwingUtilities.convertPointToScreen(p, owner); - w.setLocation(p); - } else { - w.setLocation(WindowUtils.getPointForCentering(w)); - } - } - - /** - * Converts the incoming string to an escaped output string. This method - * is far from perfect, only escaping <, > and & characters - */ - private static String escapeXml(String input) { - String s = input == null ? "" : input.replace("&", "&"); - s = s.replace("<", "<"); - return s = s.replace(">", ">"); - } -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicHeaderUI.java b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicHeaderUI.java deleted file mode 100644 index 85edcccfb0..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicHeaderUI.java +++ /dev/null @@ -1,454 +0,0 @@ -/* - * $Id: BasicHeaderUI.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.plaf.basic; - -import org.jdesktop.swingx.JXHeader; -import org.jdesktop.swingx.JXHeader.IconPosition; -import org.jdesktop.swingx.JXLabel; -import org.jdesktop.swingx.JXLabel.MultiLineSupport; -import org.jdesktop.swingx.painter.MattePainter; -import org.jdesktop.swingx.painter.Painter; -import org.jdesktop.swingx.plaf.HeaderUI; -import org.jdesktop.swingx.plaf.PainterUIResource; -import org.jdesktop.swingx.plaf.UIManagerExt; - -import javax.swing.*; -import javax.swing.plaf.ComponentUI; -import javax.swing.plaf.UIResource; -import javax.swing.plaf.basic.BasicHTML; -import javax.swing.text.View; -import java.awt.*; -import java.awt.event.HierarchyBoundsAdapter; -import java.awt.event.HierarchyBoundsListener; -import java.awt.event.HierarchyEvent; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.logging.Logger; - -/** - * Base implementation of Header UI.

                    - * - * PENDING JW: This implementation is unusual in that it does not keep a reference - * to the component it controls. Typically, such is only the case if the ui is - * shared between instances. Historical? A consequence is that the un/install methods - * need to carry the header as parameter. Which looks funny when at the same time - * the children of the header are instance fields in this. Should think about cleanup: - * either get rid off the instance fields here, or reference the header and remove - * the param (would break subclasses).

                    - * - * PENDING JW: keys for uidefaults are inconsistent - most have prefix "JXHeader." while - * defaultIcon has prefix "Header."

                    - * - * @author rbair - * @author rah003 - * @author Jeanette Winzenburg - */ -public class BasicHeaderUI extends HeaderUI { - @SuppressWarnings("unused") - private static final Logger LOG = Logger.getLogger(BasicHeaderUI.class - .getName()); - // Implementation detail. Neeeded to expose getMultiLineSupport() method to allow restoring view - // lost after LAF switch - protected class DescriptionPane extends JXLabel { - @Override - public void paint(Graphics g) { - // switch off jxlabel default antialiasing - // JW: that cost me dearly to track down - it's the default foreground painter - // which is an AbstractPainter which has _global_ antialiased on by default - // and here the _text_ antialiased is turned off - // changed JXLabel default foregroundPainter to have antialiasing false by - // default, so remove interference here - // part of fix for #920 - the other part is in JXLabel, fix 1164 -// ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, -// RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); - super.paint(g); - } - - @Override - public MultiLineSupport getMultiLineSupport() { - return super.getMultiLineSupport(); - } - } - - protected JLabel titleLabel; - protected DescriptionPane descriptionPane; - protected JLabel imagePanel; - private PropertyChangeListener propListener; - private HierarchyBoundsListener boundsListener; - private Color gradientLightColor; - private Color gradientDarkColor; - - /** Creates a new instance of BasicHeaderUI */ - public BasicHeaderUI() { - } - - /** - * Returns an instance of the UI delegate for the specified component. - * Each subclass must provide its own static createUI - * method that returns an instance of that UI delegate subclass. - * If the UI delegate subclass is stateless, it may return an instance - * that is shared by multiple components. If the UI delegate is - * stateful, then it should return a new instance per component. - * The default implementation of this method throws an error, as it - * should never be invoked. - */ - public static ComponentUI createUI(JComponent c) { - return new BasicHeaderUI(); - } - - /** - * Configures the specified component appropriate for the look and feel. - * This method is invoked when the ComponentUI instance is being installed - * as the UI delegate on the specified component. This method should - * completely configure the component for the look and feel, - * including the following: - *

                      - *
                    1. Install any default property values for color, fonts, borders, - * icons, opacity, etc. on the component. Whenever possible, - * property values initialized by the client program should not - * be overridden. - *
                    2. Install a LayoutManager on the component if necessary. - *
                    3. Create/add any required sub-components to the component. - *
                    4. Create/install event listeners on the component. - *
                    5. Create/install a PropertyChangeListener on the component in order - * to detect and respond to component property changes appropriately. - *
                    6. Install keyboard UI (mnemonics, traversal, etc.) on the component. - *
                    7. Initialize any appropriate instance data. - *
                    - * @param c the component where this UI delegate is being installed - * - * @see #uninstallUI - * @see JComponent#setUI - * @see JComponent#updateUI - */ - @Override - public void installUI(JComponent c) { - super.installUI(c); - assert c instanceof JXHeader; - JXHeader header = (JXHeader)c; - - installDefaults(header); - installComponents(header); - installListeners(header); - } - - /** - * Reverses configuration which was done on the specified component during - * installUI. This method is invoked when this - * UIComponent instance is being removed as the UI delegate - * for the specified component. This method should undo the - * configuration performed in installUI, being careful to - * leave the JComponent instance in a clean state (no - * extraneous listeners, look-and-feel-specific property objects, etc.). - * This should include the following: - *
                      - *
                    1. Remove any UI-set borders from the component. - *
                    2. Remove any UI-set layout managers on the component. - *
                    3. Remove any UI-added sub-components from the component. - *
                    4. Remove any UI-added event/property listeners from the component. - *
                    5. Remove any UI-installed keyboard UI from the component. - *
                    6. Nullify any allocated instance data objects to allow for GC. - *
                    - * @param c the component from which this UI delegate is being removed; - * this argument is often ignored, - * but might be used if the UI object is stateless - * and shared by multiple components - * - * @see #installUI - * @see JComponent#updateUI - */ - @Override - public void uninstallUI(JComponent c) { - assert c instanceof JXHeader; - JXHeader header = (JXHeader)c; - - uninstallListeners(header); - uninstallComponents(header); - uninstallDefaults(header); - } - - /** - * Installs default header properties. - *

                    - * - * NOTE: this method is called before the children are created, so must not - * try to access any of those!. - * - * @param header the header to install. - */ - protected void installDefaults(JXHeader header) { - gradientLightColor = UIManagerExt.getColor("JXHeader.startBackground"); - if (gradientLightColor == null) { - // fallback to white - gradientLightColor = Color.WHITE; - } - gradientDarkColor = UIManagerExt.getColor("JXHeader.background"); - // for backwards compatibility (mostly for substance and synthetica, - // I suspect) I'll fall back on the "control" color if - // JXHeader.background - // isn't specified. - if (gradientDarkColor == null) { - gradientDarkColor = UIManagerExt.getColor("control"); - } - - if (isUIInstallable(header.getBackgroundPainter())) { - header.setBackgroundPainter(createBackgroundPainter()); - } - - // title properties - if (isUIInstallable(header.getTitleFont())) { - Font titleFont = UIManager.getFont("JXHeader.titleFont"); - // fallback to label font - header.setTitleFont(titleFont != null ? titleFont : UIManager - .getFont("Label.font")); - } - if (isUIInstallable(header.getTitleForeground())) { - Color titleForeground = UIManagerExt - .getColor("JXHeader.titleForeground"); - // fallback to label foreground - header.setTitleForeground(titleForeground != null ? titleForeground - : UIManagerExt.getColor("Label.foreground")); - } - - // description properties - if (isUIInstallable(header.getDescriptionFont())) { - Font descFont = UIManager.getFont("JXHeader.descriptionFont"); - // fallback to label font - header.setDescriptionFont(descFont != null ? descFont : UIManager - .getFont("Label.font")); - } - if (isUIInstallable(header.getDescriptionForeground())) { - Color descForeground = UIManagerExt - .getColor("JXHeader.descriptionForeground"); - // fallback to label foreground - header.setDescriptionForeground(descForeground != null ? descForeground - : UIManagerExt.getColor("Label.foreground")); - } - - // icon label properties - if (isUIInstallable(header.getIcon())) { - header.setIcon(UIManager.getIcon("Header.defaultIcon")); - } - } - - /** - * Uninstalls the given header's default properties. This implementation - * does nothing. - * - * @param h the header to ininstall the properties from. - */ - protected void uninstallDefaults(JXHeader h) { - } - - /** - * Creates, configures, adds contained components. - * PRE: header's default properties must be set before calling this. - * - * @param header the header to install the components into. - */ - protected void installComponents(JXHeader header) { - titleLabel = new JLabel(); - descriptionPane = new DescriptionPane(); - imagePanel = new JLabel(); - installComponentDefaults(header); - header.setLayout(new GridBagLayout()); - resetLayout(header); - } - - /** - * Unconfigures, removes and nulls contained components. - * - * @param header the header to install the components into. - */ - protected void uninstallComponents(JXHeader header) { - uninstallComponentDefaults(header); - header.remove(titleLabel); - header.remove(descriptionPane); - header.remove(imagePanel); - titleLabel = null; - descriptionPane = null; - imagePanel = null; - } - - /** - * Configures the component default properties from the given header. - * - * @param header the header to install the components into. - */ - protected void installComponentDefaults(JXHeader header) { - // JW: force a not UIResource for properties which have ui default values - // like color, font, ?? - titleLabel.setFont(getAsNotUIResource(header.getTitleFont())); - titleLabel.setForeground(getAsNotUIResource(header.getTitleForeground())); - titleLabel.setText(header.getTitle()); - descriptionPane.setFont(getAsNotUIResource(header.getDescriptionFont())); - descriptionPane.setForeground(getAsNotUIResource(header.getDescriptionForeground())); - descriptionPane.setOpaque(false); - descriptionPane.setText(header.getDescription()); - descriptionPane.setLineWrap(true); - - imagePanel.setIcon(header.getIcon()); - - } - - /** - * Returns a Font based on the param which is not of type UIResource. - * - * @param font the base font - * @return a font not of type UIResource, may be null. - */ - private Font getAsNotUIResource(Font font) { - if (!(font instanceof UIResource)) return font; - // PENDING JW: correct way to create another font instance? - return font.deriveFont(font.getAttributes()); - } - - /** - * Returns a Color based on the param which is not of type UIResource. - * - * @param color the base color - * @return a color not of type UIResource, may be null. - */ - private Color getAsNotUIResource(Color color) { - if (!(color instanceof UIResource)) return color; - // PENDING JW: correct way to create another color instance? - float[] rgb = color.getRGBComponents(null); - return new Color(rgb[0], rgb[1], rgb[2], rgb[3]); - } - - /** - * Checks and returns whether the given property should be replaced - * by the UI's default value.

                    - * - * PENDING JW: move as utility method ... where? - * - * @param property the property to check. - * @return true if the given property should be replaced by the UI#s - * default value, false otherwise. - */ - private boolean isUIInstallable(Object property) { - return (property == null) || (property instanceof UIResource); - } - - /** - * Uninstalls component defaults. This implementation does nothing. - * - * @param header the header to uninstall from. - */ - protected void uninstallComponentDefaults(JXHeader header) { - } - - - protected void installListeners(final JXHeader header) { - propListener = new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - onPropertyChange(header, evt.getPropertyName(), evt.getOldValue(), evt.getNewValue()); - } - }; - boundsListener = new HierarchyBoundsAdapter() { - @Override - public void ancestorResized(HierarchyEvent e) { - if (header == e.getComponent()) { - View v = (View) descriptionPane.getClientProperty(BasicHTML.propertyKey); - // view might get lost on LAF change ... - if (v == null) { - descriptionPane.putClientProperty(BasicHTML.propertyKey, - MultiLineSupport.createView(descriptionPane)); - v = (View) descriptionPane.getClientProperty(BasicHTML.propertyKey); - } - if (v != null) { - Container tla = header.getTopLevelAncestor(); - if (tla == null) { - tla = header.getParent(); - while (tla.getParent() != null) { - tla = tla.getParent(); - } - } - int h = Math.max(descriptionPane.getHeight(), tla.getHeight()); - int w = Math.min(tla.getWidth(), header.getParent().getWidth()); - // 35 = description pane insets, TODO: obtain dynamically - w -= 35 + header.getInsets().left + header.getInsets().right + descriptionPane.getInsets().left + descriptionPane.getInsets().right + imagePanel.getInsets().left + imagePanel.getInsets().right + imagePanel.getWidth() + descriptionPane.getBounds().x; - v.setSize(w, h); - descriptionPane.setSize(w, (int) Math.ceil(v.getPreferredSpan(View.Y_AXIS))); - } - } - }}; - header.addPropertyChangeListener(propListener); - header.addHierarchyBoundsListener(boundsListener); - } - - protected void uninstallListeners(JXHeader h) { - h.removePropertyChangeListener(propListener); - h.removeHierarchyBoundsListener(boundsListener); - } - - protected void onPropertyChange(JXHeader h, String propertyName, Object oldValue, final Object newValue) { - if ("title".equals(propertyName)) { - titleLabel.setText(h.getTitle()); - } else if ("description".equals(propertyName)) { - descriptionPane.setText(h.getDescription()); - } else if ("icon".equals(propertyName)) { - imagePanel.setIcon(h.getIcon()); - } else if ("enabled".equals(propertyName)) { - boolean enabled = h.isEnabled(); - titleLabel.setEnabled(enabled); - descriptionPane.setEnabled(enabled); - imagePanel.setEnabled(enabled); - } else if ("titleFont".equals(propertyName)) { - titleLabel.setFont((Font)newValue); - } else if ("descriptionFont".equals(propertyName)) { - descriptionPane.setFont((Font)newValue); - } else if ("titleForeground".equals(propertyName)) { - titleLabel.setForeground((Color)newValue); - } else if ("descriptionForeground".equals(propertyName)) { - descriptionPane.setForeground((Color)newValue); - } else if ("iconPosition".equals(propertyName)) { - resetLayout(h); - } - } - - private void resetLayout(JXHeader h) { - h.remove(titleLabel); - h.remove(descriptionPane); - h.remove(imagePanel); - if (h.getIconPosition() == null || h.getIconPosition() == IconPosition.RIGHT) { - h.add(titleLabel, new GridBagConstraints(0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(12, 12, 0, 11), 0, 0)); - h.add(descriptionPane, new GridBagConstraints(0, 1, 1, 1, 1.0, 1.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.BOTH, new Insets(0, 24, 12, 11), 0, 0)); - h.add(imagePanel, new GridBagConstraints(1, 0, 1, 2, 0.0, 1.0, GridBagConstraints.FIRST_LINE_END, GridBagConstraints.NONE, new Insets(12, 0, 11, 11), 0, 0)); - } else { - h.add(titleLabel, new GridBagConstraints(1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(12, 12, 0, 11), 0, 0)); - h.add(descriptionPane, new GridBagConstraints(1, 1, 1, 1, 1.0, 1.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.BOTH, new Insets(0, 24, 12, 11), 0, 0)); - h.add(imagePanel, new GridBagConstraints(0, 0, 1, 2, 0.0, 1.0, GridBagConstraints.FIRST_LINE_END, GridBagConstraints.NONE, new Insets(12, 11, 0, 11), 0, 0)); - } - } - - - - protected Painter createBackgroundPainter() { - MattePainter p = new MattePainter(new GradientPaint(0, 0, gradientLightColor, 1, 0, gradientDarkColor)); - p.setPaintStretched(true); - return new PainterUIResource(p); - } - - -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicHyperlinkUI.java b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicHyperlinkUI.java deleted file mode 100644 index f9967cb7b6..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicHyperlinkUI.java +++ /dev/null @@ -1,859 +0,0 @@ -/* - * $Id: BasicHyperlinkUI.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf.basic; - -import org.jdesktop.swingx.JXHyperlink; -import org.jdesktop.swingx.SwingXUtilities; - -import javax.swing.*; -import javax.swing.event.ChangeEvent; -import javax.swing.plaf.BorderUIResource; -import javax.swing.plaf.ComponentUI; -import javax.swing.plaf.basic.BasicButtonListener; -import javax.swing.plaf.basic.BasicButtonUI; -import javax.swing.plaf.basic.BasicGraphicsUtils; -import javax.swing.plaf.basic.BasicHTML; -import javax.swing.text.*; -import javax.swing.text.html.HTMLDocument; -import javax.swing.text.html.HTMLEditorKit; -import javax.swing.text.html.ImageView; -import javax.swing.text.html.StyleSheet; -import java.awt.*; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.io.Reader; -import java.io.StringReader; -import java.net.URL; -import java.util.logging.Logger; - -/** - * Basic implementation of the JXHyperlink UI.
                    - * This is copied from org.jdesktop.jdnc.plaf.basic.BasicLinkButtonUI - */ -public class BasicHyperlinkUI extends BasicButtonUI { - - @SuppressWarnings("unused") - private static final Logger LOG = Logger.getLogger(BasicHyperlinkUI.class.getName()); - private static final Rectangle viewRect = new Rectangle(); - private static final Rectangle textRect = new Rectangle(); - private static final Rectangle iconRect = new Rectangle(); - protected int dashedRectGapX; - - // private static MouseListener handCursorListener = new HandCursor(); - protected int dashedRectGapY; - protected int dashedRectGapWidth; - protected int dashedRectGapHeight; - private Color focusColor; - private View ulv; - private final PropertyChangeListener pcListener = new PropertyChangeListener() { - - @Override - public void propertyChange(PropertyChangeEvent evt) { - // this method is called from the edt. only other place where ulv is used is in - // painting which also happens on edt so it should be safe even without synchronization - // sole purpose of this call is to reinitialize view on every property change - ulv = null; - } - }; - - public static ComponentUI createUI(JComponent c) { - return new BasicHyperlinkUI(); - } - - @Override - protected void installDefaults(AbstractButton b) { - super.installDefaults(b); - - JXHyperlink link = (JXHyperlink) b; - - LookAndFeel.installProperty(b, "opaque", false); - - if (SwingXUtilities.isUIInstallable(link.getUnclickedColor())) { - link.setUnclickedColor(UIManager.getColor("Hyperlink.linkColor")); - } - - if (SwingXUtilities.isUIInstallable(link.getClickedColor())) { - link.setClickedColor(UIManager.getColor("Hyperlink.visitedColor")); - } - - b.setBorderPainted(false); - b.setRolloverEnabled(true); - - if (SwingXUtilities.isUIInstallable(b.getBorder())) { - b.setBorder(new BorderUIResource(BorderFactory.createEmptyBorder(0, 1, 0, 0))); - } - - dashedRectGapX = UIManager.getInt("ButtonUI.dashedRectGapX"); - dashedRectGapY = UIManager.getInt("ButtonUI.dashedRectGapY"); - dashedRectGapWidth = UIManager.getInt("ButtonUI.dashedRectGapWidth"); - dashedRectGapHeight = UIManager.getInt("ButtonUI.dashedRectGapHeight"); - focusColor = UIManager.getColor("ButtonUI.focus"); - - b.setHorizontalAlignment(SwingConstants.LEADING); - } - - @Override - protected void installListeners(AbstractButton b) { - super.installListeners(b); -// b.addMouseListener(handCursorListener); - b.addPropertyChangeListener(pcListener); - } - - @Override - protected void uninstallListeners(AbstractButton b) { - super.uninstallListeners(b); -// b.removeMouseListener(handCursorListener); - b.removePropertyChangeListener(pcListener); - } - - protected Color getFocusColor() { - return focusColor; - } - - @Override - public void paint(Graphics g, JComponent c) { - AbstractButton b = (AbstractButton) c; - ButtonModel model = b.getModel(); - - FontMetrics fm = g.getFontMetrics(); - - Insets i = c.getInsets(); - - viewRect.x = i.left; - viewRect.y = i.top; - viewRect.width = b.getWidth() - (i.right + viewRect.x); - viewRect.height = b.getHeight() - (i.bottom + viewRect.y); - - textRect.x = textRect.y = textRect.width = textRect.height = 0; - iconRect.x = iconRect.y = iconRect.width = iconRect.height = 0; - - Font f = c.getFont(); - g.setFont(f); - - // layout the text and icon - String text = SwingUtilities.layoutCompoundLabel(c, fm, b.getText(), b - .getIcon(), b.getVerticalAlignment(), b - .getHorizontalAlignment(), b.getVerticalTextPosition(), b - .getHorizontalTextPosition(), viewRect, iconRect, textRect, b - .getText() == null ? 0 : b.getIconTextGap()); - - clearTextShiftOffset(); - - // perform UI specific press action, e.g. Windows L&F shifts text - if (model.isArmed() && model.isPressed()) { - paintButtonPressed(g, b); - } - - // Paint the Icon - if (b.getIcon() != null) { - paintIcon(g, c, iconRect); - } - -// Composite oldComposite = ((Graphics2D) g).getComposite(); -// -// if (model.isRollover()) { -// ((Graphics2D) g).setComposite(AlphaComposite.getInstance( -// AlphaComposite.SRC_OVER, 0.5f)); -// } - - if (text != null && !text.isEmpty()) { - View v = (View) c.getClientProperty(BasicHTML.propertyKey); - if (v != null) { - paintHTMLText(g, b, textRect, text, v); - } else { - paintText(g, b, textRect, text); - } - } - - if (b.isFocusPainted() && b.hasFocus()) { - // paint UI specific focus - paintFocus(g, b, viewRect, textRect, iconRect); - } - -// ((Graphics2D) g).setComposite(oldComposite); - } - - /** - * Method which renders the text of the current button if html. - *

                    - * - * @param g Graphics context - * @param b Current button to render - * @param textRect Bounding rectangle to render the text. - * @param text String to render - * @param v the View to use. - */ - protected void paintHTMLText(Graphics g, AbstractButton b, - Rectangle textRect, String text, View v) { - textRect.x += getTextShiftOffset(); - textRect.y += getTextShiftOffset(); - // fix #441-swingx - underline not painted for html - if (b.getModel().isRollover()) { - //paintUnderline(g, b, textRect, text); - if (ulv == null) { - ulv = ULHtml.createHTMLView(b, text); - } - ulv.paint(g, textRect); - } else { - v.paint(g, textRect); - } - textRect.x -= getTextShiftOffset(); - textRect.y -= getTextShiftOffset(); - } - - /** - * {@inheritDoc}

                    - * Overridden to paint the underline on rollover. - */ - @Override - protected void paintText(Graphics g, AbstractButton b, Rectangle textRect, - String text) { - //kgs -- SwingX #415: pixel-shift when disabled - //BasicButtonUI shifts disabled text to the left by 1 pixel - //we compensate for that here, so that all Hyperlinks paint - //at the same location regardless of state - if (!b.getModel().isEnabled()) { - textRect.x += 1; - } - - super.paintText(g, b, textRect, text); - if (b.getModel().isRollover()) { - paintUnderline(g, b, textRect, text); - } - } - - private void paintUnderline(Graphics g, AbstractButton b, Rectangle rect, - String text) { - // JW: copied from JXTable.LinkRenderer - FontMetrics fm = g.getFontMetrics(); - int descent = fm.getDescent(); - - // REMIND(aim): should we be basing the underline on - // the font's baseline instead of the text bounds? - g.drawLine(rect.x + getTextShiftOffset(), - (rect.y + rect.height) - descent + 1 + getTextShiftOffset(), - rect.x + rect.width + getTextShiftOffset(), - (rect.y + rect.height) - descent + 1 + getTextShiftOffset()); - } - - @Override - protected void paintFocus(Graphics g, AbstractButton b, Rectangle viewRect, - Rectangle textRect, Rectangle iconRect) { - if (b.getParent() instanceof JToolBar) { - // Windows doesn't draw the focus rect for buttons in a toolbar. - return; - } - - // focus painted same color as text - g.setColor(getFocusColor()); - // paint the focus rect around the union of text rect and icon rect - // PENDING JW: duplicated to handle insets - Rectangle iconTextRect = getIconTextRect(b); - // PENDING JW: better factor handling of insets - the bare union doesn't respect insets -// Rectangle iconTextRect = textRect.union(iconRect); - BasicGraphicsUtils.drawDashedRect(g, iconTextRect.x, iconTextRect.y, - iconTextRect.width, iconTextRect.height); - // pre-#167-swingx: active area too large -// int width = b.getWidth(); -// int height = b.getHeight(); -// BasicGraphicsUtils.drawDashedRect(g, dashedRectGapX, dashedRectGapY, -// width - dashedRectGapWidth, height - dashedRectGapHeight); - } - - @Override - protected void paintButtonPressed(Graphics g, AbstractButton b) { - setTextShiftOffset(); - } - - - @Override - protected BasicButtonListener createButtonListener(AbstractButton b) { - return new BasicHyperlinkListener(b); - } - - /** - * {@inheritDoc}

                    - *

                    - * Overridden to return true if the position is inside the union of the - * text and icon rectangle, false otherwise. - */ - @Override - public boolean contains(JComponent c, int x, int y) { - AbstractButton button = (AbstractButton) c; - return isInside(getIconTextRect(button), x, y); - } - - /** - * @param iconTextRect - * @param point - * @return - */ - private boolean isInside(Rectangle iconTextRect, int x, int y) { - if (iconTextRect == null) return false; - return iconTextRect.contains(x, y); - } - - /** - * C&p'ed from BasicGraphicsUtils (getPreferredButtonSize). - * - * @param b the button to analyse. - * @return the union of the text and icon rectangle of the AbstractButton - * or null if the button has children (??) - */ - protected Rectangle getIconTextRect(AbstractButton b) { - if (b.getComponentCount() > 0) { - return null; - } - - Icon icon = b.getIcon(); - String text = b.getText(); - - Font font = b.getFont(); - FontMetrics fm = b.getFontMetrics(font); - - Rectangle iconR = new Rectangle(); - Rectangle textR = new Rectangle(); - Rectangle viewR = new Rectangle(b.getSize()); - - SwingUtilities.layoutCompoundLabel(b, fm, text, icon, - b.getVerticalAlignment(), b.getHorizontalAlignment(), b - .getVerticalTextPosition(), b - .getHorizontalTextPosition(), viewR, iconR, textR, - (text == null ? 0 : b.getIconTextGap())); - - /* - * The preferred size of the button is the size of the text and icon - * rectangles plus the buttons insets. - */ - - Rectangle r = iconR.union(textR); - - Insets insets = b.getInsets(); - r.width += insets.left + insets.right; - r.height += insets.top + insets.bottom; - // PENDING JW: why not? -// r.x -= insets.left; - r.y -= insets.top; - return r; - } - - /** - * A BasicButtonListener specialized to the needs of a Hyperlink. - * - * @author Jeanette Winzenburg - */ - public static class BasicHyperlinkListener extends BasicButtonListener { - - /** - * @param b - */ - public BasicHyperlinkListener(AbstractButton b) { - super(b); - } - - - @Override - public void stateChanged(ChangeEvent e) { - AbstractButton button = (AbstractButton) e.getSource(); - if (button.isRolloverEnabled()) { - button.setCursor(button.getModel().isRollover() ? - // PENDING JW: support customizable cursor - Cursor.getPredefinedCursor(Cursor.HAND_CURSOR) : null); - } - super.stateChanged(e); - } - } - - static class ULHtml extends BasicHTML { - /** - * Overrides to the default stylesheet. Should consider - * just creating a completely fresh stylesheet. - */ - private static final String styleChanges = - "p { margin-top: 0; margin-bottom: 0; margin-left: 0; margin-right: 0; text-decoration: underline }" + - "body { margin-top: 0; margin-bottom: 0; margin-left: 0; margin-right: 0; text-decoration: underline }" + - "font {text-decoration: underline}"; - /** - * The source of the html renderers - */ - private static BasicEditorKit basicHTMLFactory; - /** - * Creates the Views that visually represent the model. - */ - private static ViewFactory basicHTMLViewFactory; - - /** - * Create an html renderer for the given component and - * string of html. - */ - public static View createHTMLView(JComponent c, String html) { - BasicEditorKit kit = getFactory(); - Document doc = kit.createDefaultDocument(c.getFont(), - c.getForeground()); - Object base = c.getClientProperty(documentBaseKey); - if (base instanceof URL) { - ((HTMLDocument) doc).setBase((URL) base); - } - Reader r = new StringReader(html); - try { - kit.read(r, doc, 0); - } catch (Throwable ignored) { - } - ViewFactory f = kit.getViewFactory(); - View hview = f.create(doc.getDefaultRootElement()); - return new Renderer(c, f, hview); - } - - static BasicEditorKit getFactory() { - if (basicHTMLFactory == null) { - basicHTMLViewFactory = new BasicHTMLViewFactory(); - basicHTMLFactory = new BasicEditorKit(); - } - return basicHTMLFactory; - } - - static class BasicEditorKit extends HTMLEditorKit { - /** - * Shared base style for all documents created by us use. - */ - private static StyleSheet defaultStyles; - - /** - * Overriden to return our own slimmed down style sheet. - */ - @Override - public StyleSheet getStyleSheet() { - if (defaultStyles == null) { - defaultStyles = new StyleSheet(); - StringReader r = new StringReader(styleChanges); - try { - defaultStyles.loadRules(r, null); - } catch (Throwable e) { - // don't want to die in static initialization... - // just display things wrong. - } - r.close(); - defaultStyles.addStyleSheet(super.getStyleSheet()); - } - return defaultStyles; - } - - /** - * Sets the async policy to flush everything in one chunk, and - * to not display unknown tags. - */ - public Document createDefaultDocument(Font defaultFont, - Color foreground) { - StyleSheet styles = getStyleSheet(); - StyleSheet ss = new StyleSheet(); - ss.addStyleSheet(styles); - BasicDocument doc = new BasicDocument(ss, defaultFont, foreground); - doc.setAsynchronousLoadPriority(Integer.MAX_VALUE); - doc.setPreservesUnknownTags(false); - return doc; - } - - /** - * Returns the ViewFactory that is used to make sure the Views don't - * load in the background. - */ - @Override - public ViewFactory getViewFactory() { - return basicHTMLViewFactory; - } - } - - - /** - * BasicHTMLViewFactory extends HTMLFactory to force images to be loaded - * synchronously. - */ - static class BasicHTMLViewFactory extends HTMLEditorKit.HTMLFactory { - @Override - public View create(Element elem) { - View view = super.create(elem); - - if (view instanceof ImageView) { - ((ImageView) view).setLoadsSynchronously(true); - } - return view; - } - } - - - /** - * The subclass of HTMLDocument that is used as the model. getForeground - * is overridden to return the foreground property from the Component this - * was created for. - */ - static class BasicDocument extends HTMLDocument { - - BasicDocument(StyleSheet s, Font defaultFont, Color foreground) { - super(s); - setPreservesUnknownTags(false); - setFontAndColor(defaultFont, foreground); - } - - // --------- EO 1.5 x 1.6 incompatibility handling .... - - private static String displayPropertiesToCSS(Font f, Color c) { - StringBuilder rule = new StringBuilder("body {"); - if (f != null) { - rule.append(" font-family: "); - rule.append(f.getFamily()); - rule.append(" ; "); - rule.append(" font-size: "); - rule.append(f.getSize()); - rule.append("pt ;"); - if (f.isBold()) { - rule.append(" font-weight: 700 ; "); - } - if (f.isItalic()) { - rule.append(" font-style: italic ; "); - } - } - if (c != null) { - rule.append(" color: #"); - if (c.getRed() < 16) { - rule.append('0'); - } - rule.append(Integer.toHexString(c.getRed())); - if (c.getGreen() < 16) { - rule.append('0'); - } - rule.append(Integer.toHexString(c.getGreen())); - if (c.getBlue() < 16) { - rule.append('0'); - } - rule.append(Integer.toHexString(c.getBlue())); - rule.append(" ; "); - } - rule.append(" }"); - return rule.toString(); - } - - /** - * Sets the default font and default color. These are set by - * adding a rule for the body that specifies the font and color. - * This allows the html to override these should it wish to have - * a custom font or color. - */ - private void setFontAndColor(Font font, Color fg) { - getStyleSheet().addRule(displayPropertiesToCSS(font, fg)); - } - } - - - /** - * Root text view that acts as an HTML renderer. - */ - static class Renderer extends View { - - private final View view; - private final ViewFactory factory; - private final JComponent host; - private int width; - - Renderer(JComponent c, ViewFactory f, View v) { - super(null); - host = c; - factory = f; - view = v; - view.setParent(this); - // initially layout to the preferred size - setSize(view.getPreferredSpan(X_AXIS), view.getPreferredSpan(Y_AXIS)); - } - - /** - * Fetches the attributes to use when rendering. At the root - * level there are no attributes. If an attribute is resolved - * up the view hierarchy this is the end of the line. - */ - @Override - public AttributeSet getAttributes() { - return null; - } - - /** - * Determines the preferred span for this view along an axis. - * - * @param axis may be either X_AXIS or Y_AXIS - * @return the span the view would like to be rendered into. - * Typically the view is told to render into the span - * that is returned, although there is no guarantee. - * The parent may choose to resize or break the view. - */ - @Override - public float getPreferredSpan(int axis) { - if (axis == X_AXIS) { - // width currently laid out to - return width; - } - return view.getPreferredSpan(axis); - } - - /** - * Determines the minimum span for this view along an axis. - * - * @param axis may be either X_AXIS or Y_AXIS - * @return the span the view would like to be rendered into. - * Typically the view is told to render into the span - * that is returned, although there is no guarantee. - * The parent may choose to resize or break the view. - */ - @Override - public float getMinimumSpan(int axis) { - return view.getMinimumSpan(axis); - } - - /** - * Determines the maximum span for this view along an axis. - * - * @param axis may be either X_AXIS or Y_AXIS - * @return the span the view would like to be rendered into. - * Typically the view is told to render into the span - * that is returned, although there is no guarantee. - * The parent may choose to resize or break the view. - */ - @Override - public float getMaximumSpan(int axis) { - return Integer.MAX_VALUE; - } - - /** - * Specifies that a preference has changed. - * Child views can call this on the parent to indicate that - * the preference has changed. The root view routes this to - * invalidate on the hosting component. - *

                    - * This can be called on a different thread from the - * event dispatching thread and is basically unsafe to - * propagate into the component. To make this safe, - * the operation is transferred over to the event dispatching - * thread for completion. It is a design goal that all view - * methods be safe to call without concern for concurrency, - * and this behavior helps make that true. - * - * @param child the child view - * @param width true if the width preference has changed - * @param height true if the height preference has changed - */ - @Override - public void preferenceChanged(View child, boolean width, boolean height) { - host.revalidate(); - host.repaint(); - } - - /** - * Determines the desired alignment for this view along an axis. - * - * @param axis may be either X_AXIS or Y_AXIS - * @return the desired alignment, where 0.0 indicates the origin - * and 1.0 the full span away from the origin - */ - @Override - public float getAlignment(int axis) { - return view.getAlignment(axis); - } - - /** - * Renders the view. - * - * @param g the graphics context - * @param allocation the region to render into - */ - @Override - public void paint(Graphics g, Shape allocation) { - Rectangle alloc = allocation.getBounds(); - view.setSize(alloc.width, alloc.height); - view.paint(g, allocation); - } - - /** - * Sets the view parent. - * - * @param parent the parent view - */ - @Override - public void setParent(View parent) { - throw new Error("Can't set parent on root view"); - } - - /** - * Returns the number of views in this view. Since - * this view simply wraps the root of the view hierarchy - * it has exactly one child. - * - * @return the number of views - * @see #getView - */ - @Override - public int getViewCount() { - return 1; - } - - /** - * Gets the n-th view in this container. - * - * @param n the number of the view to get - * @return the view - */ - @Override - public View getView(int n) { - return view; - } - - /** - * Provides a mapping from the document model coordinate space - * to the coordinate space of the view mapped to it. - * - * @param pos the position to convert - * @param a the allocated region to render into - * @return the bounding box of the given position - */ - @Override - public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException { - return view.modelToView(pos, a, b); - } - - /** - * Provides a mapping from the document model coordinate space - * to the coordinate space of the view mapped to it. - * - * @param p0 the position to convert >= 0 - * @param b0 the bias toward the previous character or the - * next character represented by p0, in case the - * position is a boundary of two views. - * @param p1 the position to convert >= 0 - * @param b1 the bias toward the previous character or the - * next character represented by p1, in case the - * position is a boundary of two views. - * @param a the allocated region to render into - * @return the bounding box of the given position is returned - * @throws BadLocationException if the given position does - * not represent a valid location in the associated document - * @throws IllegalArgumentException for an invalid bias argument - * @see View#viewToModel - */ - @Override - public Shape modelToView(int p0, Position.Bias b0, int p1, - Position.Bias b1, Shape a) throws BadLocationException { - return view.modelToView(p0, b0, p1, b1, a); - } - - /** - * Provides a mapping from the view coordinate space to the logical - * coordinate space of the model. - * - * @param x x coordinate of the view location to convert - * @param y y coordinate of the view location to convert - * @param a the allocated region to render into - * @return the location within the model that best represents the - * given point in the view - */ - @Override - public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) { - return view.viewToModel(x, y, a, bias); - } - - /** - * Returns the document model underlying the view. - * - * @return the model - */ - @Override - public Document getDocument() { - return view.getDocument(); - } - - /** - * Returns the starting offset into the model for this view. - * - * @return the starting offset - */ - @Override - public int getStartOffset() { - return view.getStartOffset(); - } - - /** - * Returns the ending offset into the model for this view. - * - * @return the ending offset - */ - @Override - public int getEndOffset() { - return view.getEndOffset(); - } - - /** - * Gets the element that this view is mapped to. - * - * @return the view - */ - @Override - public Element getElement() { - return view.getElement(); - } - - /** - * Sets the view size. - * - * @param width the width - * @param height the height - */ - @Override - public void setSize(float width, float height) { - this.width = (int) width; - view.setSize(width, height); - } - - /** - * Fetches the container hosting the view. This is useful for - * things like scheduling a repaint, finding out the host - * components font, etc. The default implementation - * of this is to forward the query to the parent view. - * - * @return the container - */ - @Override - public Container getContainer() { - return host; - } - - /** - * Fetches the factory to be used for building the - * various view fragments that make up the view that - * represents the model. This is what determines - * how the model will be represented. This is implemented - * to fetch the factory provided by the associated - * EditorKit. - * - * @return the factory - */ - @Override - public ViewFactory getViewFactory() { - return factory; - } - - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicLookAndFeelAddons.java b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicLookAndFeelAddons.java deleted file mode 100644 index 3b0afb5464..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicLookAndFeelAddons.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * $Id: BasicLookAndFeelAddons.java 4034 2011-07-19 17:18:03Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf.basic; - -import org.jdesktop.swingx.plaf.LookAndFeelAddons; -import org.jdesktop.swingx.plaf.UIManagerExt; - -/** - * Install simple pluggable UI. Usually not used directly, subclasses should be - * preferred as this addon may not provide complete implementation of the - * additional pluggable UIs. - */ -public abstract class BasicLookAndFeelAddons extends LookAndFeelAddons { - /** - * {@inheritDoc} - */ - @Override - public void initialize() { - super.initialize(); - //must add resource bundle after adding component values - UIManagerExt.addResourceBundle( - "org.jdesktop.swingx.plaf.basic.resources.swingx"); - } - - /** - * {@inheritDoc} - */ - @Override - public void uninitialize() { - //must remove resource bundle before adding component values - UIManagerExt.removeResourceBundle( - "org.jdesktop.swingx.plaf.basic.resources.swingx"); - super.uninitialize(); - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicMonthViewUI.java b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicMonthViewUI.java deleted file mode 100644 index c3b9c2e918..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicMonthViewUI.java +++ /dev/null @@ -1,2323 +0,0 @@ -/* - * $Id: BasicMonthViewUI.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf.basic; - -import org.jdesktop.swingx.JXMonthView; -import org.jdesktop.swingx.SwingXUtilities; -import org.jdesktop.swingx.action.AbstractActionExt; -import org.jdesktop.swingx.calendar.CalendarUtils; -import org.jdesktop.swingx.calendar.DateSelectionModel; -import org.jdesktop.swingx.calendar.DateSelectionModel.SelectionMode; -import org.jdesktop.swingx.event.DateSelectionEvent; -import org.jdesktop.swingx.event.DateSelectionListener; -import org.jdesktop.swingx.plaf.MonthViewUI; -import org.jdesktop.swingx.plaf.UIManagerExt; - -import javax.swing.*; -import javax.swing.plaf.ComponentUI; -import java.awt.*; -import java.awt.event.*; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.text.DateFormatSymbols; -import java.util.Calendar; -import java.util.Date; -import java.util.Locale; -import java.util.SortedSet; -import java.util.logging.Logger; - -/** - * Base implementation of the JXMonthView UI.

                    - * - * Note: The api changed considerably between releases 0.9.4 and 0.9.5. - *

                    - * - * The general drift of the change was to delegate all text rendering to a dedicated - * rendering controller (currently named RenderingHandler), similar to - * the collection view rendering. The UI itself keeps layout and positioning of - * the rendering components. Plus updating on property changes received from the - * monthView.

                    - * - * - *

                    - * Painting: coordinate systems. - * - *

                      - *
                    • Screen coordinates of months/days, accessible via the getXXBounds() methods. These - * coordinates are absolute in the system of the monthView. - *
                    • The grid of visible months with logical row/column coordinates. The logical - * coordinates are adjusted to ComponentOrientation. - *
                    • The grid of days in a month with logical row/column coordinates. The logical - * coordinates are adjusted to ComponentOrientation. The columns - * are the (optional) week header and the days of the week. The rows are the day header - * and the weeks in a month. The day header shows the localized names of the days and - * has the row coordinate DAY_HEADER_ROW. It is shown always. - * The row header shows the week number in the year and has the column coordinate WEEK_HEADER_COLUMN. It - * is shown only if the showingWeekNumber property is true. - *
                    - * - * On the road to "zoomable" date range views (Vista-style).

                    - * - * Added support (doesn't do anything yet, zoom-logic must yet be defined) - * by way of an active calendar header which is added to the monthView if zoomable. - * It is disabled by default. In this mode, the view is always - * traversable and shows exactly one calendar. It is orthogonal to the classic - * mode, that is client code should not be effected in any way as long as the mode - * is not explicitly enabled.

                    - * - * NOTE to LAF implementors: the active calendar header is very, very, very raw and - * sure to change without much notice. Better not yet to support it right now. - * - * @author dmouse - * @author rbair - * @author rah003 - * @author Jeanette Winzenburg - */ -public class BasicMonthViewUI extends MonthViewUI { - @SuppressWarnings("all") - private static final Logger LOG = Logger.getLogger(BasicMonthViewUI.class - .getName()); - - private static final int CALENDAR_SPACING = 10; - - /** Return value used to identify when the month down button is pressed. */ - public static final int MONTH_DOWN = 1; - /** Return value used to identify when the month up button is pressed. */ - public static final int MONTH_UP = 2; - - // constants for day columns - protected static final int WEEK_HEADER_COLUMN = 0; - protected static final int DAYS_IN_WEEK = 7; - protected static final int FIRST_DAY_COLUMN = WEEK_HEADER_COLUMN + 1; - protected static final int LAST_DAY_COLUMN = FIRST_DAY_COLUMN + DAYS_IN_WEEK -1; - - // constants for day rows (aka: weeks) - protected static final int DAY_HEADER_ROW = 0; - protected static final int WEEKS_IN_MONTH = 6; - protected static final int FIRST_WEEK_ROW = DAY_HEADER_ROW + 1; - protected static final int LAST_WEEK_ROW = FIRST_WEEK_ROW + WEEKS_IN_MONTH - 1; - - - /** the component we are installed for. */ - protected JXMonthView monthView; - // listeners - private PropertyChangeListener propertyChangeListener; - private MouseListener mouseListener; - private MouseMotionListener mouseMotionListener; - private Handler handler; - - // fields related to visible date range - /** end of day of the last visible month. */ - private Date lastDisplayedDate; - /** - - //---------- fields related to selection/navigation - - - /** flag indicating keyboard navigation. */ - private boolean usingKeyboard = false; - /** For interval selections we need to record the date we pivot around. */ - private Date pivotDate = null; - /** - * Date span used by the keyboard actions to track the original selection. - */ - private SortedSet originalDateSpan; - - //------------------ visuals - - protected boolean isLeftToRight; - protected Icon monthUpImage; - protected Icon monthDownImage; - - - /** - * The padding for month traversal icons. - * PENDING JW: decouple rendering and hit-detection. - */ - private int arrowPaddingX = 3; - private int arrowPaddingY = 3; - - - /** height of month header including the monthView's box padding. */ - private int fullMonthBoxHeight; - /** - * width of a "day" box including the monthView's box padding - * this is the same for days-of-the-week, weeks-of-the-year and days - */ - private int fullBoxWidth; - /** - * height of a "day" box including the monthView's box padding - * this is the same for days-of-the-week, weeks-of-the-year and days - */ - private int fullBoxHeight; - /** the width of a single month display. */ - private int calendarWidth; - /** the height of a single month display. */ - private int calendarHeight; - /** the height of a single month grid cell, including padding. */ - private int fullCalendarHeight; - /** the width of a single month grid cell, including padding. */ - private int fullCalendarWidth; - /** The number of calendars displayed vertically. */ - private int calendarRowCount = 1; - /** The number of calendars displayed horizontally. */ - private int calendarColumnCount = 1; - - /** - * The bounding box of the grid of visible months. - */ - protected Rectangle calendarGrid = new Rectangle(); - - /** - * The Strings used for the day headers. This is the fall-back for - * the monthView if no custom strings are set. - * PENDING JW: delegate to RenderingHandler? - */ - private String[] daysOfTheWeek; - - /** - * Provider of configured components for text rendering. - */ - private CalendarRenderingHandler renderingHandler; - /** - * The CellRendererPane for stamping rendering comps. - */ - private CellRendererPane rendererPane; - - /** - * The CalendarHeaderHandler which provides the header component if zoomable. - */ - private CalendarHeaderHandler calendarHeaderHandler; - - - @SuppressWarnings({"UnusedDeclaration"}) - public static ComponentUI createUI(JComponent c) { - return new BasicMonthViewUI(); - } - - /** - * Installs the component as appropriate for the current lf. - * - * PENDING JW: clarify sequence of installXX methods. - */ - @Override - public void installUI(JComponent c) { - monthView = (JXMonthView)c; - monthView.setLayout(createLayoutManager()); - - // PENDING JW: move to installDefaults or installComponents? - installRenderingHandler(); - - installDefaults(); - installDelegate(); - installKeyboardActions(); - installComponents(); - updateLocale(false); - updateZoomable(); - installListeners(); - } - - - @Override - public void uninstallUI(JComponent c) { - uninstallRenderingHandler(); - uninstallListeners(); - uninstallKeyboardActions(); - uninstallDefaults(); - uninstallComponents(); - monthView.setLayout(null); - monthView = null; - } - - /** - * Creates and installs the calendar header handler. - */ - protected void installComponents() { - setCalendarHeaderHandler(createCalendarHeaderHandler()); - getCalendarHeaderHandler().install(monthView); - } - - /** - * Uninstalls the calendar header handler. - */ - protected void uninstallComponents() { - getCalendarHeaderHandler().uninstall(monthView); - setCalendarHeaderHandler(null); - } - - /** - * Installs default values.

                    - * - * This is refactored to only install default properties on the monthView. - * Extracted install of this delegate's properties into installDelegate. - * - */ - protected void installDefaults() { - LookAndFeel.installProperty(monthView, "opaque", Boolean.TRUE); - - // @KEEP JW: do not use the core install methods (might have classloader probs) - // instead access all properties via the UIManagerExt .. - // BasicLookAndFeel.installColorsAndFont(monthView, -// "JXMonthView.background", "JXMonthView.foreground", "JXMonthView.font"); - - if (SwingXUtilities.isUIInstallable(monthView.getBackground())) { - monthView.setBackground(UIManagerExt.getColor("JXMonthView.background")); - } - if (SwingXUtilities.isUIInstallable(monthView.getForeground())) { - monthView.setForeground(UIManagerExt.getColor("JXMonthView.foreground")); - } - if (SwingXUtilities.isUIInstallable(monthView.getFont())) { - // PENDING JW: missing in managerExt? Or not applicable anyway? - monthView.setFont(UIManager.getFont("JXMonthView.font")); - } - if (SwingXUtilities.isUIInstallable(monthView.getMonthStringBackground())) { - monthView.setMonthStringBackground(UIManagerExt.getColor("JXMonthView.monthStringBackground")); - } - if (SwingXUtilities.isUIInstallable(monthView.getMonthStringForeground())) { - monthView.setMonthStringForeground(UIManagerExt.getColor("JXMonthView.monthStringForeground")); - } - if (SwingXUtilities.isUIInstallable(monthView.getDaysOfTheWeekForeground())) { - monthView.setDaysOfTheWeekForeground(UIManagerExt.getColor("JXMonthView.daysOfTheWeekForeground")); - } - if (SwingXUtilities.isUIInstallable(monthView.getSelectionBackground())) { - monthView.setSelectionBackground(UIManagerExt.getColor("JXMonthView.selectedBackground")); - } - if (SwingXUtilities.isUIInstallable(monthView.getSelectionForeground())) { - monthView.setSelectionForeground(UIManagerExt.getColor("JXMonthView.selectedForeground")); - } - if (SwingXUtilities.isUIInstallable(monthView.getFlaggedDayForeground())) { - monthView.setFlaggedDayForeground(UIManagerExt.getColor("JXMonthView.flaggedDayForeground")); - } - - monthView.setBoxPaddingX(UIManagerExt.getInt("JXMonthView.boxPaddingX")); - monthView.setBoxPaddingY(UIManagerExt.getInt("JXMonthView.boxPaddingY")); - } - - /** - * Installs this ui delegate's properties. - */ - protected void installDelegate() { - isLeftToRight = monthView.getComponentOrientation().isLeftToRight(); - // PENDING JW: remove here if rendererHandler takes over control completely - // as is, some properties are duplicated - monthDownImage = UIManager.getIcon("JXMonthView.monthDownFileName"); - monthUpImage = UIManager.getIcon("JXMonthView.monthUpFileName"); - // install date related state - setFirstDisplayedDay(monthView.getFirstDisplayedDay()); - } - - - protected void uninstallDefaults() {} - - protected void installKeyboardActions() { - // Setup the keyboard handler. - // JW: changed (0.9.6) to when-ancestor just to be on the safe side - // if the title contain active comps - installKeyBindings(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); - // JW: removed the automatic keybindings in WHEN_IN_FOCUSED - // which caused #555-swingx (binding active if not focused) - ActionMap actionMap = monthView.getActionMap(); - KeyboardAction acceptAction = new KeyboardAction(KeyboardAction.ACCEPT_SELECTION); - actionMap.put("acceptSelection", acceptAction); - KeyboardAction cancelAction = new KeyboardAction(KeyboardAction.CANCEL_SELECTION); - actionMap.put("cancelSelection", cancelAction); - - actionMap.put("selectPreviousDay", new KeyboardAction(KeyboardAction.SELECT_PREVIOUS_DAY)); - actionMap.put("selectNextDay", new KeyboardAction(KeyboardAction.SELECT_NEXT_DAY)); - actionMap.put("selectDayInPreviousWeek", new KeyboardAction(KeyboardAction.SELECT_DAY_PREVIOUS_WEEK)); - actionMap.put("selectDayInNextWeek", new KeyboardAction(KeyboardAction.SELECT_DAY_NEXT_WEEK)); - - actionMap.put("adjustSelectionPreviousDay", new KeyboardAction(KeyboardAction.ADJUST_SELECTION_PREVIOUS_DAY)); - actionMap.put("adjustSelectionNextDay", new KeyboardAction(KeyboardAction.ADJUST_SELECTION_NEXT_DAY)); - actionMap.put("adjustSelectionPreviousWeek", new KeyboardAction(KeyboardAction.ADJUST_SELECTION_PREVIOUS_WEEK)); - actionMap.put("adjustSelectionNextWeek", new KeyboardAction(KeyboardAction.ADJUST_SELECTION_NEXT_WEEK)); - - - actionMap.put(JXMonthView.COMMIT_KEY, acceptAction); - actionMap.put(JXMonthView.CANCEL_KEY, cancelAction); - - // PENDING JW: complete (year-, decade-, ?? ) and consolidate with KeyboardAction - // additional navigation actions - AbstractActionExt prev = new AbstractActionExt() { - - @Override - public void actionPerformed(ActionEvent e) { - previousMonth(); - } - - }; - monthView.getActionMap().put("scrollToPreviousMonth", prev); - AbstractActionExt next = new AbstractActionExt() { - - @Override - public void actionPerformed(ActionEvent e) { - nextMonth(); - } - - }; - monthView.getActionMap().put("scrollToNextMonth", next); - - } - - - /** - * @param inputMap - */ - private void installKeyBindings(int type) { - InputMap inputMap = monthView.getInputMap(type); - inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, false), "acceptSelection"); - inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false), "cancelSelection"); - - // @KEEP quickly check #606-swingx: keybindings not working in internalframe - // eaten somewhere -// inputMap.put(KeyStroke.getKeyStroke("F1"), "selectPreviousDay"); - - inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), "selectPreviousDay"); - inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), "selectNextDay"); - inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false), "selectDayInPreviousWeek"); - inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), "selectDayInNextWeek"); - - inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, InputEvent.SHIFT_MASK, false), "adjustSelectionPreviousDay"); - inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, InputEvent.SHIFT_MASK, false), "adjustSelectionNextDay"); - inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, InputEvent.SHIFT_MASK, false), "adjustSelectionPreviousWeek"); - inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, InputEvent.SHIFT_MASK, false), "adjustSelectionNextWeek"); - } - - /** - * @param inputMap - */ - private void uninstallKeyBindings(int type) { - InputMap inputMap = monthView.getInputMap(type); - inputMap.clear(); - } - - protected void uninstallKeyboardActions() {} - - protected void installListeners() { - propertyChangeListener = createPropertyChangeListener(); - mouseListener = createMouseListener(); - mouseMotionListener = createMouseMotionListener(); - - monthView.addPropertyChangeListener(propertyChangeListener); - monthView.addMouseListener(mouseListener); - monthView.addMouseMotionListener(mouseMotionListener); - - monthView.getSelectionModel().addDateSelectionListener(getHandler()); - } - - protected void uninstallListeners() { - monthView.getSelectionModel().removeDateSelectionListener(getHandler()); - monthView.removeMouseMotionListener(mouseMotionListener); - monthView.removeMouseListener(mouseListener); - monthView.removePropertyChangeListener(propertyChangeListener); - - mouseMotionListener = null; - mouseListener = null; - propertyChangeListener = null; - } - - /** - * Creates and installs the renderingHandler and infrastructure to use it. - */ - protected void installRenderingHandler() { - setRenderingHandler(createRenderingHandler()); - if (getRenderingHandler() != null) { - rendererPane = new CellRendererPane(); - monthView.add(rendererPane); - } - } - - /** - * Uninstalls the renderingHandler and infrastructure that used it. - */ - protected void uninstallRenderingHandler() { - if (getRenderingHandler() == null) return; - monthView.remove(rendererPane); - rendererPane = null; - setRenderingHandler(null); - } - - /** - * Returns the CalendarRenderingHandler to use. Subclasses may override to - * plug-in custom implementations.

                    - * - * This implementation returns an instance of RenderingHandler. - * - * @return the endering handler to use for painting, must not be null - */ - protected CalendarRenderingHandler createRenderingHandler() { - return new RenderingHandler(); - } - - /** - * @param renderingHandler the renderingHandler to set - */ - protected void setRenderingHandler(CalendarRenderingHandler renderingHandler) { - this.renderingHandler = renderingHandler; - } - - /** - * @return the renderingHandler - */ - protected CalendarRenderingHandler getRenderingHandler() { - return renderingHandler; - } - - /** - * - * Empty subclass for backward compatibility. The original implementation was - * extracted as standalone class and renamed to BasicCalendarRenderingHandler.

                    - * - * This will be available for extension by LAF providers until all collaborators - * in the new rendering pipeline are ready for public exposure. - */ - protected static class RenderingHandler extends BasicCalendarRenderingHandler { - - } - /** - * Binds/clears the keystrokes in the component input map, - * based on the monthView's componentInputMap enabled property. - * - * @see JXMonthView#isComponentInputMapEnabled() - */ - protected void updateComponentInputMap() { - if (monthView.isComponentInputMapEnabled()) { - installKeyBindings(JComponent.WHEN_IN_FOCUSED_WINDOW); - } else { - uninstallKeyBindings(JComponent.WHEN_IN_FOCUSED_WINDOW); - } - } - - - - /** - * Updates internal state according to monthView's locale. Revalidates the - * monthView if the boolean parameter is true. - * - * @param revalidate a boolean indicating whether the monthView should be - * revalidated after the change. - */ - protected void updateLocale(boolean revalidate) { - Locale locale = monthView.getLocale(); - if (getRenderingHandler() != null) { - getRenderingHandler().setLocale(locale); - } - - // fixed JW: respect property in UIManager if available - // PENDING JW: what to do if weekdays had been set - // with JXMonthView method? how to detect? - daysOfTheWeek = (String[]) UIManager.get("JXMonthView.daysOfTheWeek"); - - if (daysOfTheWeek == null) { - daysOfTheWeek = new String[7]; - String[] dateFormatSymbols = DateFormatSymbols.getInstance(locale) - .getShortWeekdays(); - daysOfTheWeek = new String[JXMonthView.DAYS_IN_WEEK]; - for (int i = Calendar.SUNDAY; i <= Calendar.SATURDAY; i++) { - daysOfTheWeek[i - 1] = dateFormatSymbols[i]; - } - } - if (revalidate) { - monthView.invalidate(); - monthView.validate(); - } - } - - @Override - public String[] getDaysOfTheWeek() { - String[] days = new String[daysOfTheWeek.length]; - System.arraycopy(daysOfTheWeek, 0, days, 0, days.length); - return days; - } - - -//---------------------- listener creation - protected PropertyChangeListener createPropertyChangeListener() { - return getHandler(); - } - - protected LayoutManager createLayoutManager() { - return getHandler(); - } - - protected MouseListener createMouseListener() { - return getHandler(); - } - - protected MouseMotionListener createMouseMotionListener() { - return getHandler(); - } - - private Handler getHandler() { - if (handler == null) { - handler = new Handler(); - } - - return handler; - } - - public boolean isUsingKeyboard() { - return usingKeyboard; - } - - public void setUsingKeyboard(boolean val) { - usingKeyboard = val; - } - - - - // ----------------------- mapping day coordinates - - /** - * Returns the bounds of the day in the grid of days which contains the - * given location. The bounds are in monthView screen coordinate system. - *

                    - * - * Note: this is a pure geometric mapping. The returned rectangle need not - * necessarily map to a date in the month which contains the location, it - * can represent a week-number/column header or a leading/trailing date. - * - * @param x the x position of the location in pixel - * @param y the y position of the location in pixel - * @return the bounds of the day which contains the location, or null if - * outside - */ - protected Rectangle getDayBoundsAtLocation(int x, int y) { - Rectangle monthDetails = getMonthDetailsBoundsAtLocation(x, y); - if ((monthDetails == null) || (!monthDetails.contains(x, y))) - return null; - // calculate row/column in absolute grid coordinates - int row = (y - monthDetails.y) / fullBoxHeight; - int column = (x - monthDetails.x) / fullBoxWidth; - return new Rectangle(monthDetails.x + column * fullBoxWidth, monthDetails.y - + row * fullBoxHeight, fullBoxWidth, fullBoxHeight); - } - - /** - * Returns the bounds of the day box at logical coordinates in the given month. - * The row's range is from DAY_HEADER_ROW to LAST_WEEK_ROW. Column's range is from - * WEEK_HEADER_COLUMN to LAST_DAY_COLUMN. - * - * @param month the month containing the day box - * @param row the logical row (== week) coordinate in the day grid - * @param column the logical column (== day) coordinate in the day grid - * @return the bounds of the daybox or null if not showing - * @throws IllegalArgumentException if row or column are out off range. - * - * @see #getDayGridPositionAtLocation(int, int) - */ - protected Rectangle getDayBoundsInMonth(Date month, int row, final int column) { - checkValidRow(row, column); - if ((WEEK_HEADER_COLUMN == column) && !monthView.isShowingWeekNumber()) return null; - Rectangle monthBounds = getMonthBounds(month); - if (monthBounds == null) return null; - // dayOfWeek header is shown always - monthBounds.y += getMonthHeaderHeight() + (row - DAY_HEADER_ROW) * fullBoxHeight; - // PENDING JW: still looks fishy ... - int absoluteColumn = column - FIRST_DAY_COLUMN; - if (monthView.isShowingWeekNumber()) { - absoluteColumn++; - } - if (isLeftToRight) { - monthBounds.x += absoluteColumn * fullBoxWidth; - } else { - int leading = monthBounds.x + monthBounds.width - fullBoxWidth; - monthBounds.x = leading - absoluteColumn * fullBoxWidth; - } - monthBounds.width = fullBoxWidth; - monthBounds.height = fullBoxHeight; - return monthBounds; - } - - - /** - * Returns the logical coordinates of the day which contains the given - * location. The p.x of the returned value represents the week header or the - * day of week, ranging from WEEK_HEADER_COLUMN to LAST_DAY_COLUMN. The - * p.y represents the day header or week of the month, ranging from DAY_HEADER_ROW - * to LAST_WEEK_ROW. The transformation takes care of - * ComponentOrientation. - *

                    - * - * Note: The returned grid position need not - * necessarily map to a date in the month which contains the location, it - * can represent a week-number/column header or a leading/trailing date. - * - * @param x the x position of the location in pixel - * @param y the y position of the location in pixel - * @return the logical coordinates of the day in the grid of days in a month - * or null if outside. - * - * @see #getDayBoundsInMonth(Date, int, int) - */ - protected Point getDayGridPositionAtLocation(int x, int y) { - Rectangle monthDetailsBounds = getMonthDetailsBoundsAtLocation(x, y); - if ((monthDetailsBounds == null) ||(!monthDetailsBounds.contains(x, y))) return null; - int calendarRow = (y - monthDetailsBounds.y) / fullBoxHeight + DAY_HEADER_ROW; - int absoluteColumn = (x - monthDetailsBounds.x) / fullBoxWidth; - int calendarColumn = absoluteColumn + FIRST_DAY_COLUMN; - if (!isLeftToRight) { - int leading = monthDetailsBounds.x + monthDetailsBounds.width; - calendarColumn = (leading - x) / fullBoxWidth + FIRST_DAY_COLUMN; - } - if (monthView.isShowingWeekNumber()) { - calendarColumn -= 1; - } - return new Point(calendarColumn, calendarRow); - } - - /** - * Returns the Date defined by the logical - * grid coordinates relative to the given month. May be null if the - * logical coordinates represent a header in the day grid or is outside of the - * given month. - * - * Mapping logical day grid coordinates to Date.

                    - * - * PENDING JW: relax the startOfMonth pre? Why did I require it? - * - * @param month a calendar representing the first day of the month, must not - * be null. - * @param row the logical row index in the day grid of the month - * @param column the logical column index in the day grid of the month - * @return the day at the logical grid coordinates in the given month or null - * if the coordinates are day/week header or leading/trailing dates - * @throws IllegalStateException if the month is not the start of the month. - * - * @see #getDayGridPosition(Date) - */ - protected Date getDayInMonth(Date month, int row, int column) { - if ((row == DAY_HEADER_ROW) || (column == WEEK_HEADER_COLUMN)) return null; - Calendar calendar = getCalendar(month); - int monthField = calendar.get(Calendar.MONTH); - if (!CalendarUtils.isStartOfMonth(calendar)) - throw new IllegalStateException("calendar must be start of month but was: " + month.getTime()); - CalendarUtils.startOfWeek(calendar); - // PENDING JW: correctly mapped now? - calendar.add(Calendar.DAY_OF_MONTH, - (row - FIRST_WEEK_ROW) * DAYS_IN_WEEK + (column - FIRST_DAY_COLUMN)); - if (calendar.get(Calendar.MONTH) == monthField) { - return calendar.getTime(); - } - return null; - - } - - /** - * Returns the given date's position in the grid of the month it is contained in. - * - * @param date the Date to get the logical position for, must not be null. - * @return the logical coordinates of the day in the grid of days in a - * month or null if the Date is not visible. - * - * @see #getDayInMonth(Date, int, int) - */ - protected Point getDayGridPosition(Date date) { - if (!isVisible(date)) return null; - Calendar calendar = getCalendar(date); - Date startOfDay = CalendarUtils.startOfDay(calendar, date); - // there must be a less ugly way? - // columns - CalendarUtils.startOfWeek(calendar); - int column = FIRST_DAY_COLUMN; - while (calendar.getTime().before(startOfDay)) { - column++; - calendar.add(Calendar.DAY_OF_MONTH, 1); - } - - Date startOfWeek = CalendarUtils.startOfWeek(calendar, date); - calendar.setTime(date); - CalendarUtils.startOfMonth(calendar); - int row = FIRST_WEEK_ROW; - while (calendar.getTime().before(startOfWeek)) { - row++; - calendar.add(Calendar.WEEK_OF_YEAR, 1); - } - return new Point(column, row); - } - - - /** - * Returns the Date at the given location. May be null if the - * coordinates don't map to a day in the month which contains the - * coordinates. Specifically: hitting leading/trailing dates returns null. - * - * Mapping pixel to calendar day. - * - * @param x the x position of the location in pixel - * @param y the y position of the location in pixel - * @return the day at the given location or null if the location - * doesn't map to a day in the month which contains the coordinates. - * - * @see #getDayBounds(Date) - */ - @Override - public Date getDayAtLocation(int x, int y) { - Point dayInGrid = getDayGridPositionAtLocation(x, y); - if ((dayInGrid == null) - || (dayInGrid.x == WEEK_HEADER_COLUMN) || (dayInGrid.y == DAY_HEADER_ROW)) return null; - Date month = getMonthAtLocation(x, y); - return getDayInMonth(month, dayInGrid.y, dayInGrid.x); - } - - /** - * Returns the bounds of the given day. - * The bounds are in monthView coordinate system.

                    - * - * PENDING JW: this most probably should be public as it is the logical - * reverse of getDayAtLocation

                    - * - * @param date the Date to return the bounds for. Must not be null. - * @return the bounds of the given date or null if not visible. - * - * @see #getDayAtLocation(int, int) - */ - protected Rectangle getDayBounds(Date date) { - if (!isVisible(date)) return null; - Point position = getDayGridPosition(date); - Rectangle monthBounds = getMonthBounds(date); - monthBounds.y += getMonthHeaderHeight() + (position.y - DAY_HEADER_ROW) * fullBoxHeight; - if (monthView.isShowingWeekNumber()) { - position.x++; - } - position.x -= FIRST_DAY_COLUMN; - if (isLeftToRight) { - monthBounds.x += position.x * fullBoxWidth; - } else { - int start = monthBounds.x + monthBounds.width - fullBoxWidth; - monthBounds.x = start - position.x * fullBoxWidth; - } - monthBounds.width = fullBoxWidth; - monthBounds.height = fullBoxHeight; - return monthBounds; - } - - /** - * @param row - */ - private void checkValidRow(int row, int column) { - if ((column < WEEK_HEADER_COLUMN) || (column > LAST_DAY_COLUMN)) - throw new IllegalArgumentException("illegal column in day grid " + column); - if ((row < DAY_HEADER_ROW) || (row > LAST_WEEK_ROW)) - throw new IllegalArgumentException("illegal row in day grid" + row); - } - - /** - * Returns a boolean indicating if the given Date is visible. Trailing/leading - * dates of the last/first displayed month are considered to be invisible. - * - * @param date the Date to check for visibility. Must not be null. - * @return true if the date is visible, false otherwise. - */ - private boolean isVisible(Date date) { - if (getFirstDisplayedDay().after(date) || getLastDisplayedDay().before(date)) return false; - return true; - } - - - // ------------------- mapping month parts - - - /** - * Mapping pixel to bounds.

                    - * - * PENDING JW: define the "action grid". Currently this replaces the old - * version to remove all internal usage of deprecated methods. - * - * @param x the x position of the location in pixel - * @param y the y position of the location in pixel - * @return the bounds of the active header area in containing the location - * or null if outside. - */ - protected int getTraversableGridPositionAtLocation(int x, int y) { - Rectangle headerBounds = getMonthHeaderBoundsAtLocation(x, y); - if (headerBounds == null) return -1; - if (y < headerBounds.y + arrowPaddingY) return -1; - if (y > headerBounds.y + headerBounds.height - arrowPaddingY) return -1; - headerBounds.setBounds(headerBounds.x + arrowPaddingX, y, - headerBounds.width - 2 * arrowPaddingX, headerBounds.height); - if (!headerBounds.contains(x, y)) return -1; - Rectangle hitArea = new Rectangle(headerBounds.x, headerBounds.y, monthUpImage.getIconWidth(), monthUpImage.getIconHeight()); - if (hitArea.contains(x, y)) { - return isLeftToRight ? MONTH_DOWN : MONTH_UP; - } - hitArea.translate(headerBounds.width - monthUpImage.getIconWidth(), 0); - if (hitArea.contains(x, y)) { - return isLeftToRight ? MONTH_UP : MONTH_DOWN; - } - return -1; - } - - /** - * Returns the bounds of the month header which contains the - * given location. The bounds are in monthView coordinate system. - * - *

                    - * - * @param x the x position of the location in pixel - * @param y the y position of the location in pixel - * @return the bounds of the month which contains the location, - * or null if outside - */ - protected Rectangle getMonthHeaderBoundsAtLocation(int x, int y) { - Rectangle header = getMonthBoundsAtLocation(x, y); - if (header == null) return null; - header.height = getMonthHeaderHeight(); - return header; - } - - /** - * Returns the bounds of the month details which contains the - * given location. The bounds are in monthView coordinate system. - * - * @param x the x position of the location in pixel - * @param y the y position of the location in pixel - * @return the bounds of the details grid in the month at - * location or null if outside. - */ - protected Rectangle getMonthDetailsBoundsAtLocation(int x, int y) { - Rectangle month = getMonthBoundsAtLocation(x, y); - if (month == null) return null; - int startOfDaysY = month.y + getMonthHeaderHeight(); - if (y < startOfDaysY) return null; - month.y = startOfDaysY; - month.height = month.height - getMonthHeaderHeight(); - return month; - } - - - // ---------------------- mapping month coordinates - - /** - * Returns the bounds of the month which contains the - * given location. The bounds are in monthView coordinate system. - * - *

                    - * - * Mapping pixel to bounds. - * - * @param x the x position of the location in pixel - * @param y the y position of the location in pixel - * @return the bounds of the month which contains the location, - * or null if outside - */ - protected Rectangle getMonthBoundsAtLocation(int x, int y) { - if (!calendarGrid.contains(x, y)) return null; - int calendarRow = (y - calendarGrid.y) / fullCalendarHeight; - int calendarColumn = (x - calendarGrid.x) / fullCalendarWidth; - return new Rectangle( - calendarGrid.x + calendarColumn * fullCalendarWidth, - calendarGrid.y + calendarRow * fullCalendarHeight, - calendarWidth, calendarHeight); - } - - - /** - * - * Returns the logical coordinates of the month which contains - * the given location. The p.x of the returned value represents the column, the - * p.y represents the row the month is shown in. The transformation takes - * care of ComponentOrientation.

                    - * - * Mapping pixel to logical grid coordinates. - * - * @param x the x position of the location in pixel - * @param y the y position of the location in pixel - * @return the logical coordinates of the month in the grid of month shown by - * this monthView or null if outside. - */ - protected Point getMonthGridPositionAtLocation(int x, int y) { - if (!calendarGrid.contains(x, y)) return null; - int calendarRow = (y - calendarGrid.y) / fullCalendarHeight; - int calendarColumn = (x - calendarGrid.x) / fullCalendarWidth; - if (!isLeftToRight) { - int start = calendarGrid.x + calendarGrid.width; - calendarColumn = (start - x) / fullCalendarWidth; - - } - return new Point(calendarColumn, calendarRow); - } - - /** - * Returns the Date representing the start of the month which - * contains the given location.

                    - * - * Mapping pixel to calendar day. - * - * @param x the x position of the location in pixel - * @param y the y position of the location in pixel - * @return the start of the month which contains the given location or - * null if the location is outside the grid of months. - */ - protected Date getMonthAtLocation(int x, int y) { - Point month = getMonthGridPositionAtLocation(x, y); - if (month == null) return null; - return getMonth(month.y, month.x); - } - - /** - * Returns the Date representing the start of the month at the given - * logical position in the grid of months.

                    - * - * Mapping logical grid coordinates to Calendar. - * - * @param row the rowIndex in the grid of months. - * @param column the columnIndex in the grid months. - * @return a Date representing the start of the month at the given - * logical coordinates. - * - * @see #getMonthGridPosition(Date) - */ - protected Date getMonth(int row, int column) { - Calendar calendar = getCalendar(); - calendar.add(Calendar.MONTH, - row * calendarColumnCount + column); - return calendar.getTime(); - - } - - /** - * Returns the logical grid position of the month containing the given date. - * The Point's x value is the column in the grid of months, the y value - * is the row in the grid of months. - * - * Mapping Date to logical grid position, this is the reverse of getMonth(int, int). - * - * @param date the Date to return the bounds for. Must not be null. - * @return the postion of the month that contains the given date or null if not visible. - * - * @see #getMonth(int, int) - * @see #getMonthBounds(int, int) - */ - protected Point getMonthGridPosition(Date date) { - if (!isVisible(date)) return null; - // start of grid - Calendar calendar = getCalendar(); - int firstMonth = calendar.get(Calendar.MONTH); - int firstYear = calendar.get(Calendar.YEAR); - - // - calendar.setTime(date); - int month = calendar.get(Calendar.MONTH); - int year = calendar.get(Calendar.YEAR); - - int diffMonths = month - firstMonth - + ((year - firstYear) * JXMonthView.MONTHS_IN_YEAR); - - int row = diffMonths / calendarColumnCount; - int column = diffMonths % calendarColumnCount; - - return new Point(column, row); - } - - /** - * Returns the bounds of the month at the given logical coordinates - * in the grid of visible months.

                    - * - * Mapping logical grip position to pixel. - * - * @param row the rowIndex in the grid of months. - * @param column the columnIndex in the grid months. - * @return the bounds of the month at the given logical logical position. - * - * @see #getMonthGridPositionAtLocation(int, int) - * @see #getMonthBoundsAtLocation(int, int) - */ - protected Rectangle getMonthBounds(int row, int column) { - int startY = calendarGrid.y + row * fullCalendarHeight; - int startX = calendarGrid.x + column * fullCalendarWidth; - if (!isLeftToRight) { - startX = calendarGrid.x + (calendarColumnCount - 1 - column) * fullCalendarWidth; - } - return new Rectangle(startX, startY, calendarWidth, calendarHeight); - } - - /** - * Returns the bounds of the month containing the given date. - * The bounds are in monthView coordinate system.

                    - * - * Mapping Date to pixel. - * - * @param date the Date to return the bounds for. Must not be null. - * @return the bounds of the month that contains the given date or null if not visible. - * - * @see #getMonthAtLocation(int, int) - */ - protected Rectangle getMonthBounds(Date date) { - Point position = getMonthGridPosition(date); - return position != null ? getMonthBounds(position.y, position.x) : null; - } - - /** - * Returns the bounds of the month containing the given date. - * The bounds are in monthView coordinate system.

                    - * - * Mapping Date to pixel. - * - * @param date the Date to return the bounds for. Must not be null. - * @return the bounds of the month that contains the given date or null if not visible. - * - * @see #getMonthAtLocation(int, int) - */ - protected Rectangle getMonthHeaderBounds(Date date, boolean includeInsets) { - Point position = getMonthGridPosition(date); - if (position == null) return null; - Rectangle bounds = getMonthBounds(position.y, position.x); - bounds.height = getMonthHeaderHeight(); - if (!includeInsets) { - - } - return bounds; - } - - - //---------------- accessors for sizes - - /** - * Returns the size of a month. - * @return the size of a month. - */ - protected Dimension getMonthSize() { - return new Dimension(calendarWidth, calendarHeight); - } - - /** - * Returns the size of a day including the padding. - * @return the size of a month. - */ - protected Dimension getDaySize() { - return new Dimension(fullBoxWidth, fullBoxHeight); - } - /** - * Returns the height of the month header. - * - * @return the height of the month header. - */ - protected int getMonthHeaderHeight() { - return fullMonthBoxHeight; - } - - - - //------------------- layout - - /** - * Called from layout: calculates properties - * of grid of months. - */ - private void calculateMonthGridLayoutProperties() { - calculateMonthGridRowColumnCount(); - calculateMonthGridBounds(); - } - - /** - * Calculates the bounds of the grid of months. - * - * CalendarRow/ColumnCount and calendarWidth/Height must be - * initialized before calling this. - */ - private void calculateMonthGridBounds() { - calendarGrid.setBounds(calculateCalendarGridX(), - calculateCalendarGridY(), - calculateCalendarGridWidth(), - calculateCalendarGridHeight()); - } - - - private int calculateCalendarGridY() { - return (monthView.getHeight() - calculateCalendarGridHeight()) / 2; - } - - private int calculateCalendarGridX() { - return (monthView.getWidth() - calculateCalendarGridWidth()) / 2; - } - - private int calculateCalendarGridHeight() { - return ((calendarHeight * calendarRowCount) + - (CALENDAR_SPACING * (calendarRowCount - 1 ))); - } - - private int calculateCalendarGridWidth() { - return ((calendarWidth * calendarColumnCount) + - (CALENDAR_SPACING * (calendarColumnCount - 1))); - } - - /** - * Calculates and updates the numCalCols/numCalRows that determine the - * number of calendars that can be displayed. Updates the last displayed - * date if appropriate. - * - */ - private void calculateMonthGridRowColumnCount() { - int oldNumCalCols = calendarColumnCount; - int oldNumCalRows = calendarRowCount; - - calendarRowCount = 1; - calendarColumnCount = 1; - if (!isZoomable()) { - // Determine how many columns of calendars we want to paint. - int addColumns = (monthView.getWidth() - calendarWidth) - / (calendarWidth + CALENDAR_SPACING); - // happens if used as renderer in a tree.. don't know yet why - if (addColumns > 0) { - calendarColumnCount += addColumns; - } - - // Determine how many rows of calendars we want to paint. - int addRows = (monthView.getHeight() - calendarHeight) - / (calendarHeight + CALENDAR_SPACING); - if (addRows > 0) { - calendarRowCount += addRows; - } - } - if (oldNumCalCols != calendarColumnCount - || oldNumCalRows != calendarRowCount) { - updateLastDisplayedDay(getFirstDisplayedDay()); - } - } - - /** - * @return true if the month view can be zoomed, false otherwise - */ - protected boolean isZoomable() { - return monthView.isZoomable(); - } - - - - -//-------------------- painting - - - /** - * Overridden to extract the background painting for ease-of-use of - * subclasses. - */ - @Override - public void update(Graphics g, JComponent c) { - paintBackground(g); - paint(g, c); - } - - /** - * Paints the background of the component. This implementation fill the - * monthView's area with its background color if opaque, does nothing - * if not opaque. Subclasses can override but must comply to opaqueness - * contract. - * - * @param g the Graphics to fill. - * - */ - protected void paintBackground(Graphics g) { - if (monthView.isOpaque()) { - g.setColor(monthView.getBackground()); - g.fillRect(0, 0, monthView.getWidth(), monthView.getHeight()); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void paint(Graphics g, JComponent c) { - Rectangle clip = g.getClipBounds(); - // Get a calender set to the first displayed date - Calendar cal = getCalendar(); - // loop through grid of months - for (int row = 0; row < calendarRowCount; row++) { - for (int column = 0; column < calendarColumnCount; column++) { - // get the bounds of the current month. - Rectangle bounds = getMonthBounds(row, column); - // Check if this row falls in the clip region. - if (bounds.intersects(clip)) { - paintMonth(g, cal); - } - cal.add(Calendar.MONTH, 1); - } - } - - } - - /** - * Paints the month represented by the given Calendar. - * - * Note: the given calendar must not be changed. - * @param g the graphics to paint into - * @param month the calendar specifying the first day of the month to - * paint, must not be null - */ - protected void paintMonth(Graphics g, Calendar month) { - paintMonthHeader(g, month); - paintDayHeader(g, month); - paintWeekHeader(g, month); - paintDays(g, month); - } - - /** - * Paints the header of a month. - * - * Note: the given calendar must not be changed. - * @param g the graphics to paint into - * @param month the calendar specifying the first day of the month to - * paint, must not be null - */ - protected void paintMonthHeader(Graphics g, Calendar month) { - Rectangle page = getMonthHeaderBounds(month.getTime(), false); - paintDayOfMonth(g, page, month, CalendarState.TITLE); - } - - /** - * Paints the day column header. - * - * Note: the given calendar must not be changed. - * @param g the graphics to paint into - * @param month the calendar specifying the first day of the month to - * paint, must not be null - */ - protected void paintDayHeader(Graphics g, Calendar month) { - paintDaysOfWeekSeparator(g, month); - Calendar cal = (Calendar) month.clone(); - CalendarUtils.startOfWeek(cal); - for (int i = FIRST_DAY_COLUMN; i <= LAST_DAY_COLUMN; i++) { - Rectangle dayBox = getDayBoundsInMonth(month.getTime(), DAY_HEADER_ROW, i); - paintDayOfMonth(g, dayBox, cal, CalendarState.DAY_OF_WEEK); - cal.add(Calendar.DATE, 1); - } - } - - /** - * Paints the day column header. - * - * Note: the given calendar must not be changed. - * @param g the graphics to paint into - * @param month the calendar specifying the first day of the month to - * paint, must not be null - */ - protected void paintWeekHeader(Graphics g, Calendar month) { - if (!monthView.isShowingWeekNumber()) - return; - paintWeekOfYearSeparator(g, month); - - int weeks = getWeeks(month); - // the calendar passed to the renderers - Calendar weekCalendar = (Calendar) month.clone(); - // we loop by logical row (== week in month) coordinates - for (int week = FIRST_WEEK_ROW; week < FIRST_WEEK_ROW + weeks; week++) { - // get the day bounds based on logical row/column coordinates - Rectangle dayBox = getDayBoundsInMonth(month.getTime(), week, WEEK_HEADER_COLUMN); - // NOTE: this can be set to any day in the week to render the weeknumber of - // categorized by CalendarState - paintDayOfMonth(g, dayBox, weekCalendar, CalendarState.WEEK_OF_YEAR); - weekCalendar.add(Calendar.WEEK_OF_YEAR, 1); - } - } - - /** - * Paints the days of the given month. - * - * Note: the given calendar must not be changed. - * @param g the graphics to paint into - * @param month the calendar specifying the first day of the month to - * paint, must not be null - */ - protected void paintDays(Graphics g, Calendar month) { - Calendar clonedCal = (Calendar) month.clone(); - CalendarUtils.startOfMonth(clonedCal); - Date startOfMonth = clonedCal.getTime(); - CalendarUtils.endOfMonth(clonedCal); - Date endOfMonth = clonedCal.getTime(); - // reset the clone - clonedCal.setTime(month.getTime()); - // adjust to start of week - clonedCal.setTime(month.getTime()); - CalendarUtils.startOfWeek(clonedCal); - for (int week = FIRST_WEEK_ROW; week <= LAST_WEEK_ROW; week++) { - for (int day = FIRST_DAY_COLUMN; day <= LAST_DAY_COLUMN; day++) { - CalendarState state = null; - if (clonedCal.getTime().before(startOfMonth)) { - if (monthView.isShowingLeadingDays()) { - state = CalendarState.LEADING; - } - } else if (clonedCal.getTime().after(endOfMonth)) { - if (monthView.isShowingTrailingDays()) { - state = CalendarState.TRAILING; - } - - } else { - state = isToday(clonedCal.getTime()) ? CalendarState.TODAY : CalendarState.IN_MONTH; - } - if (state != null) { - Rectangle bounds = getDayBoundsInMonth(startOfMonth, week, day); - paintDayOfMonth(g, bounds, clonedCal, state); - } - clonedCal.add(Calendar.DAY_OF_MONTH, 1); - } - } - } - - - /** - * Paints a day which is of the current month with the given state.

                    - * - * PENDING JW: mis-nomer - this is in fact called for rendering any day-related - * state (including weekOfYear, dayOfWeek headers) and for rendering - * the month header as well, that is from everywhere. - * Rename to paintSomethingGeneral. Think about impact for subclasses - * (what do they really need? feedback please!) - * - * @param g the graphics to paint into. - * @param bounds the rectangle to paint the day into - * @param calendar the calendar representing the day to paint - * @param state the calendar state - */ - protected void paintDayOfMonth(Graphics g, Rectangle bounds, Calendar calendar, CalendarState state) { - JComponent comp = getRenderingHandler().prepareRenderingComponent(monthView, calendar, - state); - rendererPane.paintComponent(g, comp, monthView, bounds.x, bounds.y, - bounds.width, bounds.height, true); - } - - /** - * Paints the separator between row header (weeks of year) and days. - * - * Note: the given calendar must not be changed. - * @param g the graphics to paint into - * @param month the calendar specifying the first day of the month to - * paint, must not be null - */ - protected void paintWeekOfYearSeparator(Graphics g, Calendar month) { - Rectangle r = getSeparatorBounds(month, FIRST_WEEK_ROW, WEEK_HEADER_COLUMN); - if (r == null) return; - g.setColor(monthView.getForeground()); - g.drawLine(r.x, r.y, r.x, r.y + r.height); - } - - /** - * Paints the separator between column header (days of week) and days. - * - * Note: the given calendar must not be changed. - * @param g the graphics to paint into - * @param month the calendar specifying the the first day of the month to - * paint, must not be null - */ - protected void paintDaysOfWeekSeparator(Graphics g, Calendar month) { - Rectangle r = getSeparatorBounds(month, DAY_HEADER_ROW, FIRST_DAY_COLUMN); - if (r == null) return; - g.setColor(monthView.getForeground()); - g.drawLine(r.x, r.y, r.x + r.width, r.y); - } - - /** - * @param month - * @param row - * @param column - * @return - */ - private Rectangle getSeparatorBounds(Calendar month, int row, int column) { - Rectangle separator = getDayBoundsInMonth(month.getTime(), row, column); - if (separator == null) return null; - if (column == WEEK_HEADER_COLUMN) { - separator.height *= WEEKS_IN_MONTH; - if (isLeftToRight) { - separator.x += separator.width - 1; - } - separator.width = 1; - } else if (row == DAY_HEADER_ROW) { - int oldWidth = separator.width; - separator.width *= DAYS_IN_WEEK; - if (!isLeftToRight) { - separator.x -= separator.width - oldWidth; - } - separator.y += separator.height - 1; - separator.height = 1; - } - return separator; - } - - /** - * Returns the number of weeks to paint in the current month, as represented - * by the given calendar. The calendar is expected to be set to the first - * of the month. - * - * Note: the given calendar must not be changed. - * - * @param month the calendar specifying the the first day of the month to - * paint, must not be null - * @return the number of weeks of this month. - */ - protected int getWeeks(Calendar month) { - Calendar cloned = (Calendar) month.clone(); - // the calendar is set to the first of month, get date for last - CalendarUtils.endOfMonth(cloned); - // marker for end - Date last = cloned.getTime(); - // start again - cloned.setTime(month.getTime()); - CalendarUtils.startOfWeek(cloned); - int weeks = 0; - while (last.after(cloned.getTime())) { - weeks++; - cloned.add(Calendar.WEEK_OF_MONTH, 1); - } - return weeks; - } - - - - private void traverseMonth(int arrowType) { - if (arrowType == MONTH_DOWN) { - previousMonth(); - } else if (arrowType == MONTH_UP) { - nextMonth(); - } - } - - private void nextMonth() { - Date upperBound = monthView.getUpperBound(); - if (upperBound == null - || upperBound.after(getLastDisplayedDay()) ){ - Calendar cal = getCalendar(); - cal.add(Calendar.MONTH, 1); - monthView.setFirstDisplayedDay(cal.getTime()); - } - } - - private void previousMonth() { - Date lowerBound = monthView.getLowerBound(); - if (lowerBound == null - || lowerBound.before(getFirstDisplayedDay())){ - Calendar cal = getCalendar(); - cal.add(Calendar.MONTH, -1); - monthView.setFirstDisplayedDay(cal.getTime()); - } - } - -//--------------------------- displayed dates, calendar - - - /** - * Returns the monthViews calendar configured to the firstDisplayedDate. - * - * NOTE: it's safe to change the calendar state without resetting because - * it's JXMonthView's responsibility to protect itself. - * - * @return the monthView's calendar, configured with the firstDisplayedDate. - */ - protected Calendar getCalendar() { - return getCalendar(getFirstDisplayedDay()); - } - - /** - * Returns the monthViews calendar configured to the given time. - * - * NOTE: it's safe to change the calendar state without resetting because - * it's JXMonthView's responsibility to protect itself. - * - * @param date the date to configure the calendar with - * @return the monthView's calendar, configured with the given date. - */ - protected Calendar getCalendar(Date date) { - Calendar calendar = monthView.getCalendar(); - calendar.setTime(date); - return calendar; - } - - - - /** - * Updates the lastDisplayedDate property based on the given first and - * visible # of months. - * - * @param first the date of the first visible day. - */ - private void updateLastDisplayedDay(Date first) { - Calendar cal = getCalendar(first); - cal.add(Calendar.MONTH, ((calendarColumnCount * calendarRowCount) - 1)); - CalendarUtils.endOfMonth(cal); - lastDisplayedDate = cal.getTime(); - } - - - /** - * {@inheritDoc} - */ - @Override - public Date getLastDisplayedDay() { - return lastDisplayedDate; - } - - /*-------------- refactored: encapsulate aliased fields - */ - - /** - * Updates internal state that depends on the MonthView's firstDisplayedDay - * property.

                    - * - * Here: updates lastDisplayedDay. - *

                    - * - * - * @param firstDisplayedDay the firstDisplayedDate to set - */ - protected void setFirstDisplayedDay(Date firstDisplayedDay) { - updateLastDisplayedDay(firstDisplayedDay); - } - - /** - * Returns the first displayed day. Convenience delegate to - * - * @return the firstDisplayed - */ - protected Date getFirstDisplayedDay() { - return monthView.getFirstDisplayedDay(); - } - - /** - * @return the firstDisplayedMonth - */ - protected int getFirstDisplayedMonth() { - return getCalendar().get(Calendar.MONTH); - } - - - /** - * @return the firstDisplayedYear - */ - protected int getFirstDisplayedYear() { - return getCalendar().get(Calendar.YEAR); - } - - - /** - * @return the selection - */ - protected SortedSet getSelection() { - return monthView.getSelection(); - } - - - /** - * @return the start of today. - */ - protected Date getToday() { - return monthView.getToday(); - } - - /** - * Returns true if the date passed in is the same as today. - * - * PENDING JW: really want the exact test? - * - * @param date long representing the date you want to compare to today. - * @return true if the date passed is the same as today. - */ - protected boolean isToday(Date date) { - return date.equals(getToday()); - } - - -//-----------------------end encapsulation - - -//------------------ Handler implementation -// - /** - * temporary: removed SelectionMode.NO_SELECTION, replaced - * all access by this method to enable easy re-adding, if we want it. - * If not - remove. - */ - private boolean canSelectByMode() { - return true; - } - - - private class Handler implements - MouseListener, MouseMotionListener, LayoutManager, - PropertyChangeListener, DateSelectionListener { - private boolean armed; - private Date startDate; - private Date endDate; - - @Override - public void mouseClicked(MouseEvent e) {} - - @Override - public void mousePressed(MouseEvent e) { - // If we were using the keyboard we aren't anymore. - setUsingKeyboard(false); - - if (!monthView.isEnabled()) { - return; - } - - if (!monthView.hasFocus() && monthView.isFocusable()) { - monthView.requestFocusInWindow(); - } - - // Check if one of the month traverse buttons was pushed. - if (monthView.isTraversable()) { - int arrowType = getTraversableGridPositionAtLocation(e.getX(), e.getY()); - if (arrowType != -1) { - traverseMonth(arrowType); - return; - } - } - - if (!canSelectByMode()) { - return; - } - - Date cal = getDayAtLocation(e.getX(), e.getY()); - if (cal == null) { - return; - } - - // Update the selected dates. - startDate = cal; - endDate = cal; - - if (monthView.getSelectionMode() == SelectionMode.SINGLE_INTERVAL_SELECTION || -// selectionMode == SelectionMode.WEEK_INTERVAL_SELECTION || - monthView.getSelectionMode() == SelectionMode.MULTIPLE_INTERVAL_SELECTION) { - pivotDate = startDate; - } - - monthView.getSelectionModel().setAdjusting(true); - - if (monthView.getSelectionMode() == SelectionMode.MULTIPLE_INTERVAL_SELECTION && e.isControlDown()) { - monthView.addSelectionInterval(startDate, endDate); - } else { - monthView.setSelectionInterval(startDate, endDate); - } - - // Arm so we fire action performed on mouse release. - armed = true; - } - - - @Override - public void mouseReleased(MouseEvent e) { - // If we were using the keyboard we aren't anymore. - setUsingKeyboard(false); - - if (!monthView.isEnabled()) { - return; - } - - if (!monthView.hasFocus() && monthView.isFocusable()) { - monthView.requestFocusInWindow(); - } - - if (armed) { - monthView.commitSelection(); - } - armed = false; - } - - @Override - public void mouseEntered(MouseEvent e) {} - - @Override - public void mouseExited(MouseEvent e) {} - - @Override - public void mouseDragged(MouseEvent e) { - // If we were using the keyboard we aren't anymore. - setUsingKeyboard(false); - if (!monthView.isEnabled() || !canSelectByMode()) { - return; - } - Date cal = getDayAtLocation(e.getX(), e.getY()); - if (cal == null) { - return; - } - - Date selected = cal; - Date oldStart = startDate; - Date oldEnd = endDate; - - if (monthView.getSelectionMode() == SelectionMode.SINGLE_SELECTION) { - if (selected.equals(oldStart)) { - return; - } - startDate = selected; - endDate = selected; - } else if (pivotDate != null){ - if (selected.before(pivotDate)) { - startDate = selected; - endDate = pivotDate; - } else if (selected.after(pivotDate)) { - startDate = pivotDate; - endDate = selected; - } - } else { // pivotDate had not yet been initialiased - // might happen on first click into leading/trailing dates - // JW: fix of #996-swingx: NPE when dragging - startDate = selected; - endDate = selected; - pivotDate = selected; - } - - if (startDate.equals(oldStart) && endDate.equals(oldEnd)) { - return; - } - - if (monthView.getSelectionMode() == SelectionMode.MULTIPLE_INTERVAL_SELECTION && e.isControlDown()) { - monthView.addSelectionInterval(startDate, endDate); - } else { - monthView.setSelectionInterval(startDate, endDate); - } - - // Set trigger. - armed = true; - } - - @Override - public void mouseMoved(MouseEvent e) {} - -//------------------------ layout - - - private Dimension preferredSize = new Dimension(); - - @Override - public void addLayoutComponent(String name, Component comp) {} - - @Override - public void removeLayoutComponent(Component comp) {} - - @Override - public Dimension preferredLayoutSize(Container parent) { - layoutContainer(parent); - return new Dimension(preferredSize); - } - - @Override - public Dimension minimumLayoutSize(Container parent) { - return preferredLayoutSize(parent); - } - - @Override - public void layoutContainer(Container parent) { - - int maxMonthWidth = 0; - int maxMonthHeight = 0; - Calendar calendar = getCalendar(); - for (int i = calendar.getMinimum(Calendar.MONTH); i <= calendar.getMaximum(Calendar.MONTH); i++) { - calendar.set(Calendar.MONTH, i); - CalendarUtils.startOfMonth(calendar); - JComponent comp = getRenderingHandler().prepareRenderingComponent(monthView, calendar, CalendarState.TITLE); - Dimension pref = comp.getPreferredSize(); - maxMonthWidth = Math.max(maxMonthWidth, pref.width); - maxMonthHeight = Math.max(maxMonthHeight, pref.height); - } - - int maxBoxWidth = 0; - int maxBoxHeight = 0; - calendar = getCalendar(); - CalendarUtils.startOfWeek(calendar); - for (int i = 0; i < JXMonthView.DAYS_IN_WEEK; i++) { - JComponent comp = getRenderingHandler().prepareRenderingComponent(monthView, calendar, CalendarState.DAY_OF_WEEK); - Dimension pref = comp.getPreferredSize(); - maxBoxWidth = Math.max(maxBoxWidth, pref.width); - maxBoxHeight = Math.max(maxBoxHeight, pref.height); - calendar.add(Calendar.DATE, 1); - } - - calendar = getCalendar(); - for (int i = 0; i < calendar.getMaximum(Calendar.DAY_OF_MONTH); i++) { - JComponent comp = getRenderingHandler().prepareRenderingComponent(monthView, calendar, CalendarState.IN_MONTH); - Dimension pref = comp.getPreferredSize(); - maxBoxWidth = Math.max(maxBoxWidth, pref.width); - maxBoxHeight = Math.max(maxBoxHeight, pref.height); - calendar.add(Calendar.DATE, 1); - } - - int dayColumns = JXMonthView.DAYS_IN_WEEK; - if (monthView.isShowingWeekNumber()) { - dayColumns++; - } - - if (maxMonthWidth > maxBoxWidth * dayColumns) { - // monthHeader pref > sum of box widths - // handle here: increase day box width accordingly - double diff = maxMonthWidth - (maxBoxWidth * dayColumns); - maxBoxWidth += Math.ceil(diff/(double) dayColumns); - - } - - fullBoxWidth = maxBoxWidth; - fullBoxHeight = maxBoxHeight; - // PENDING JW: huuh? what we doing here? - int boxHeight = maxBoxHeight - 2 * monthView.getBoxPaddingY(); - fullMonthBoxHeight = Math.max(boxHeight, maxMonthHeight) ; - - // Keep track of calendar width and height for use later. - calendarWidth = fullBoxWidth * JXMonthView.DAYS_IN_WEEK; - if (monthView.isShowingWeekNumber()) { - calendarWidth += fullBoxWidth; - } - fullCalendarWidth = calendarWidth + CALENDAR_SPACING; - - calendarHeight = (fullBoxHeight * 7) + fullMonthBoxHeight; - fullCalendarHeight = calendarHeight + CALENDAR_SPACING; - // Calculate minimum width/height for the component. - int prefRows = getPreferredRows(); - preferredSize.height = (calendarHeight * prefRows) + - (CALENDAR_SPACING * (prefRows - 1)); - - int prefCols = getPreferredColumns(); - preferredSize.width = (calendarWidth * prefCols) + - (CALENDAR_SPACING * (prefCols - 1)); - - // Add insets to the dimensions. - Insets insets = monthView.getInsets(); - preferredSize.width += insets.left + insets.right; - preferredSize.height += insets.top + insets.bottom; - - calculateMonthGridLayoutProperties(); - - if (isZoomable()) { - getCalendarHeaderHandler().getHeaderComponent().setBounds(getMonthHeaderBounds(monthView.getFirstDisplayedDay(), false)); - } - } - - /** - * @return - */ - private int getPreferredColumns() { - return isZoomable() ? 1 : monthView.getPreferredColumnCount(); - } - - /** - * @return - */ - private int getPreferredRows() { - return isZoomable() ? 1 : monthView.getPreferredRowCount(); - } - - - - @Override - public void propertyChange(PropertyChangeEvent evt) { - String property = evt.getPropertyName(); - - if ("componentOrientation".equals(property)) { - isLeftToRight = monthView.getComponentOrientation().isLeftToRight(); - monthView.revalidate(); - monthView.repaint(); - } else if (JXMonthView.SELECTION_MODEL.equals(property)) { - DateSelectionModel selectionModel = (DateSelectionModel) evt.getOldValue(); - selectionModel.removeDateSelectionListener(getHandler()); - selectionModel = (DateSelectionModel) evt.getNewValue(); - selectionModel.addDateSelectionListener(getHandler()); - } else if ("firstDisplayedDay".equals(property)) { - setFirstDisplayedDay(((Date) evt.getNewValue())); - monthView.repaint(); - } else if (JXMonthView.BOX_PADDING_X.equals(property) - || JXMonthView.BOX_PADDING_Y.equals(property) - || JXMonthView.TRAVERSABLE.equals(property) - || JXMonthView.DAYS_OF_THE_WEEK.equals(property) - || "border".equals(property) - || "showingWeekNumber".equals(property) - || "traversable".equals(property) - - ) { - monthView.revalidate(); - monthView.repaint(); - } else if ("zoomable".equals(property)) { - updateZoomable(); -// } else if ("font".equals(property)) { -// calendarHeaderHandler.getHeaderComponent().setFont(getAsNotUIResource(createDerivedFont())); -// monthView.revalidate(); - } else if ("componentInputMapEnabled".equals(property)) { - updateComponentInputMap(); - } else if ("locale".equals(property)) { // "locale" is bound property - updateLocale(true); - } else { - monthView.repaint(); -// LOG.info("got propertyChange:" + property); - } - } - - @Override - public void valueChanged(DateSelectionEvent ev) { - monthView.repaint(); - } - - - } - - /** - * Class that supports keyboard traversal of the JXMonthView component. - */ - private class KeyboardAction extends AbstractAction { - public static final int ACCEPT_SELECTION = 0; - public static final int CANCEL_SELECTION = 1; - public static final int SELECT_PREVIOUS_DAY = 2; - public static final int SELECT_NEXT_DAY = 3; - public static final int SELECT_DAY_PREVIOUS_WEEK = 4; - public static final int SELECT_DAY_NEXT_WEEK = 5; - public static final int ADJUST_SELECTION_PREVIOUS_DAY = 6; - public static final int ADJUST_SELECTION_NEXT_DAY = 7; - public static final int ADJUST_SELECTION_PREVIOUS_WEEK = 8; - public static final int ADJUST_SELECTION_NEXT_WEEK = 9; - - private int action; - - public KeyboardAction(int action) { - this.action = action; - } - - @Override - public void actionPerformed(ActionEvent ev) { - if (!canSelectByMode()) - return; - if (!isUsingKeyboard()) { - originalDateSpan = getSelection(); - } - // JW: removed the isUsingKeyboard from the condition - // need to fire always. - if (action >= ACCEPT_SELECTION && action <= CANCEL_SELECTION) { - // refactor the logic ... - if (action == CANCEL_SELECTION) { - // Restore the original selection. - if ((originalDateSpan != null) - && !originalDateSpan.isEmpty()) { - monthView.setSelectionInterval( - originalDateSpan.first(), originalDateSpan - .last()); - } else { - monthView.clearSelection(); - } - monthView.cancelSelection(); - } else { - // Accept the keyboard selection. - monthView.commitSelection(); - } - setUsingKeyboard(false); - } else if (action >= SELECT_PREVIOUS_DAY - && action <= SELECT_DAY_NEXT_WEEK) { - setUsingKeyboard(true); - monthView.getSelectionModel().setAdjusting(true); - pivotDate = null; - traverse(action); - } else if (isIntervalMode() - && action >= ADJUST_SELECTION_PREVIOUS_DAY - && action <= ADJUST_SELECTION_NEXT_WEEK) { - setUsingKeyboard(true); - monthView.getSelectionModel().setAdjusting(true); - addToSelection(action); - } - } - - - /** - * @return - */ - private boolean isIntervalMode() { - return !(monthView.getSelectionMode() == SelectionMode.SINGLE_SELECTION); - } - - private void traverse(int action) { - Date oldStart = monthView.isSelectionEmpty() ? - monthView.getToday() : monthView.getFirstSelectionDate(); - Calendar cal = getCalendar(oldStart); - switch (action) { - case SELECT_PREVIOUS_DAY: - cal.add(Calendar.DAY_OF_MONTH, -1); - break; - case SELECT_NEXT_DAY: - cal.add(Calendar.DAY_OF_MONTH, 1); - break; - case SELECT_DAY_PREVIOUS_WEEK: - cal.add(Calendar.DAY_OF_MONTH, -JXMonthView.DAYS_IN_WEEK); - break; - case SELECT_DAY_NEXT_WEEK: - cal.add(Calendar.DAY_OF_MONTH, JXMonthView.DAYS_IN_WEEK); - break; - } - - Date newStartDate = cal.getTime(); - if (!newStartDate.equals(oldStart)) { - monthView.setSelectionInterval(newStartDate, newStartDate); - monthView.ensureDateVisible(newStartDate); - } - } - - /** - * If we are in a mode that allows for range selection this method - * will extend the currently selected range. - * - * NOTE: This may not be the expected behavior for the keyboard controls - * and we ay need to update this code to act in a way that people expect. - * - * @param action action for adjusting selection - */ - private void addToSelection(int action) { - Date newStartDate; - Date newEndDate; - Date selectionStart; - Date selectionEnd; - if (!monthView.isSelectionEmpty()) { - newStartDate = selectionStart = monthView.getFirstSelectionDate(); - newEndDate = selectionEnd = monthView.getLastSelectionDate(); - } else { - newStartDate = selectionStart = monthView.getToday(); - newEndDate = selectionEnd = newStartDate; - } - - if (pivotDate == null) { - pivotDate = newStartDate; - } - - // want a copy to play with - each branch sets and reads the time - // actually don't care about the pre-set time. - Calendar cal = getCalendar(); - boolean isStartMoved; - switch (action) { - case ADJUST_SELECTION_PREVIOUS_DAY: - if (newEndDate.after(pivotDate)) { - newEndDate = previousDay(cal, newEndDate); - isStartMoved = false; - } else { - newStartDate = previousDay(cal, newStartDate); - newEndDate = pivotDate; - isStartMoved = true; - } - break; - case ADJUST_SELECTION_NEXT_DAY: - if (newStartDate.before(pivotDate)) { - newStartDate = nextDay(cal, newStartDate); - isStartMoved = true; - } else { - newEndDate = nextDay(cal, newEndDate); - isStartMoved = false; - newStartDate = pivotDate; - } - break; - case ADJUST_SELECTION_PREVIOUS_WEEK: - if (newEndDate.after(pivotDate)) { - Date newTime = previousWeek(cal, newEndDate); - if (newTime.after(pivotDate)) { - newEndDate = newTime; - isStartMoved = false; - } else { - newStartDate = newTime; - newEndDate = pivotDate; - isStartMoved = true; - } - } else { - newStartDate = previousWeek(cal, newStartDate); - isStartMoved = true; - } - break; - case ADJUST_SELECTION_NEXT_WEEK: - if (newStartDate.before(pivotDate)) { - Date newTime = nextWeek(cal, newStartDate); - if (newTime.before(pivotDate)) { - newStartDate = newTime; - isStartMoved = true; - } else { - newStartDate = pivotDate; - newEndDate = newTime; - isStartMoved = false; - } - } else { - newEndDate = nextWeek(cal, newEndDate); - isStartMoved = false; - } - break; - default : throw new IllegalArgumentException("invalid adjustment action: " + action); - } - - if (!newStartDate.equals(selectionStart) || !newEndDate.equals(selectionEnd)) { - monthView.setSelectionInterval(newStartDate, newEndDate); - monthView.ensureDateVisible(isStartMoved ? newStartDate : newEndDate); - } - - } - - /** - * @param cal - * @param date - * @return - */ - private Date nextWeek(Calendar cal, Date date) { - cal.setTime(date); - cal.add(Calendar.DAY_OF_MONTH, JXMonthView.DAYS_IN_WEEK); - return cal.getTime(); - } - - /** - * @param cal - * @param date - * @return - */ - private Date previousWeek(Calendar cal, Date date) { - cal.setTime(date); - cal.add(Calendar.DAY_OF_MONTH, -JXMonthView.DAYS_IN_WEEK); - return cal.getTime(); - } - - /** - * @param cal - * @param date - * @return - */ - private Date nextDay(Calendar cal, Date date) { - cal.setTime(date); - cal.add(Calendar.DAY_OF_MONTH, 1); - return cal.getTime(); - } - - /** - * @param cal - * @param date - * @return - */ - private Date previousDay(Calendar cal, Date date) { - cal.setTime(date); - cal.add(Calendar.DAY_OF_MONTH, -1); - return cal.getTime(); - } - - - } - -//--------------------- zoomable - - /** - * Updates state after the monthView's zoomable property has been changed. - * This implementation adds/removes the header component if zoomable is true/false - * respectively. - */ - protected void updateZoomable() { - if (monthView.isZoomable()) { - monthView.add(getCalendarHeaderHandler().getHeaderComponent()); - } else { - monthView.remove(getCalendarHeaderHandler().getHeaderComponent()); - } - monthView.revalidate(); - monthView.repaint(); - } - - /** - * Creates and returns a calendar header handler which provides and configures - * a component for use in a zoomable monthView. Subclasses may override to return - * a custom handler.

                    - * - * This implementation first queries the UIManager for class to use and returns - * that if available, returns a BasicCalendarHeaderHandler if not. - * - * @return a calendar header handler providing a component for use in zoomable - * monthView. - * - * @see #getHeaderFromUIManager() - * @see CalendarHeaderHandler - * @see BasicCalendarHeaderHandler - */ - protected CalendarHeaderHandler createCalendarHeaderHandler() { - CalendarHeaderHandler handler = getHeaderFromUIManager(); - return handler != null ? handler : new BasicCalendarHeaderHandler(); - } - - - /** - * Returns a CalendarHeaderHandler looked up in the UIManager. This implementation - * looks for a String registered with a key of CalendarHeaderHandler.uiControllerID. If - * found it assumes that the value is the class name of the handler and tries - * to instantiate the handler. - * - * @return a CalendarHeaderHandler from the UIManager or null if none - * available or instantiation failed. - */ - protected CalendarHeaderHandler getHeaderFromUIManager() { - Object handlerClass = UIManager.get(CalendarHeaderHandler.uiControllerID); - if (handlerClass instanceof String) { - return instantiateClass((String) handlerClass); - } - return null; - } - - /** - * @param handlerClassName - * @return - */ - private CalendarHeaderHandler instantiateClass(String handlerClassName) { - Class handler = null; - try { - handler = Class.forName(handlerClassName); - return instantiateClass(handler); - } catch (ClassNotFoundException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - return null; - } - - /** - * @param handlerClass - * @return - */ - private CalendarHeaderHandler instantiateClass(Class handlerClass) { - Constructor constructor = null; - try { - constructor = handlerClass.getConstructor(); - } catch (SecurityException e) { - LOG.finer("cant instantiate CalendarHeaderHandler (security) " + handlerClass); - } catch (NoSuchMethodException e) { - LOG.finer("cant instantiate CalendarHeaderHandler (missing parameterless constructo?)" + handlerClass); - } - if (constructor != null) { - try { - return (CalendarHeaderHandler) constructor.newInstance(); - } catch (IllegalArgumentException e) { - LOG.finer("cant instantiate CalendarHeaderHandler (missing parameterless constructo?)" + handlerClass); - } catch (InstantiationException e) { - LOG.finer("cant instantiate CalendarHeaderHandler (not instantiable) " + handlerClass); - } catch (IllegalAccessException e) { - LOG.finer("cant instantiate CalendarHeaderHandler (constructor not public) " + handlerClass); - } catch (InvocationTargetException e) { - LOG.finer("cant instantiate CalendarHeaderHandler (Invocation target)" + handlerClass); - } - } - return null; - } - - /** - * @param calendarHeaderHandler the calendarHeaderHandler to set - */ - protected void setCalendarHeaderHandler(CalendarHeaderHandler calendarHeaderHandler) { - this.calendarHeaderHandler = calendarHeaderHandler; - } - - /** - * @return the calendarHeaderHandler - */ - protected CalendarHeaderHandler getCalendarHeaderHandler() { - return calendarHeaderHandler; - } - - -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicMultiThumbSliderUI.java b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicMultiThumbSliderUI.java deleted file mode 100644 index a1c99f16f4..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicMultiThumbSliderUI.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * $Id: BasicMultiThumbSliderUI.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf.basic; - -import org.jdesktop.swingx.JXMultiThumbSlider; -import org.jdesktop.swingx.multislider.ThumbRenderer; -import org.jdesktop.swingx.multislider.TrackRenderer; -import org.jdesktop.swingx.plaf.MultiThumbSliderUI; - -import javax.swing.*; -import javax.swing.plaf.ComponentUI; -import java.awt.*; - -/** - * - * @author Joshua Marinacci - */ -public class BasicMultiThumbSliderUI extends MultiThumbSliderUI { - - protected JXMultiThumbSlider slider; - - public static ComponentUI createUI(JComponent c) { - return new BasicMultiThumbSliderUI(); - } - - @Override - public void installUI(JComponent c) { - slider = (JXMultiThumbSlider)c; - slider.setThumbRenderer(new BasicThumbRenderer()); - slider.setTrackRenderer(new BasicTrackRenderer()); - } - @Override - public void uninstallUI(JComponent c) { - slider = null; - } - - private class BasicThumbRenderer extends JComponent implements ThumbRenderer { - public BasicThumbRenderer() { - setPreferredSize(new Dimension(14,14)); - } - - @Override - protected void paintComponent(Graphics g) { - g.setColor(Color.green); - Polygon poly = new Polygon(); - JComponent thumb = this; - poly.addPoint(thumb.getWidth()/2,0); - poly.addPoint(0,thumb.getHeight()/2); - poly.addPoint(thumb.getWidth()/2,thumb.getHeight()); - poly.addPoint(thumb.getWidth(),thumb.getHeight()/2); - g.fillPolygon(poly); - } - - @Override - public JComponent getThumbRendererComponent(JXMultiThumbSlider slider, int index, boolean selected) { - return this; - } - } - - private class BasicTrackRenderer extends JComponent implements TrackRenderer { - private JXMultiThumbSlider slider; - @Override - public void paintComponent(Graphics g) { - g.setColor(slider.getBackground()); - g.fillRect(0, 0, slider.getWidth(), slider.getHeight()); - g.setColor(Color.black); - g.drawLine(0,slider.getHeight()/2,slider.getWidth(),slider.getHeight()/2); - g.drawLine(0,slider.getHeight()/2+1,slider.getWidth(),slider.getHeight()/2+1); - } - - @Override - public JComponent getRendererComponent(JXMultiThumbSlider slider) { - this.slider = slider; - return this; - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicStatusBarUI.java b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicStatusBarUI.java deleted file mode 100644 index fd614c622b..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicStatusBarUI.java +++ /dev/null @@ -1,625 +0,0 @@ -/* - * $Id: BasicStatusBarUI.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.plaf.basic; - -import org.jdesktop.swingx.JXStatusBar; -import org.jdesktop.swingx.JXStatusBar.Constraint; -import org.jdesktop.swingx.plaf.StatusBarUI; -import org.jdesktop.swingx.plaf.UIManagerExt; - -import javax.swing.*; -import javax.swing.border.Border; -import javax.swing.plaf.BorderUIResource; -import javax.swing.plaf.ComponentUI; -import javax.swing.plaf.UIResource; -import java.awt.*; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.awt.event.MouseMotionListener; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.HashMap; -import java.util.Map; - -/** - * - * @author rbair - * @author Karl Schaefer - */ -public class BasicStatusBarUI extends StatusBarUI { - private class Handler implements MouseListener, MouseMotionListener, PropertyChangeListener { - private Window window = SwingUtilities.getWindowAncestor(statusBar); - private int handleBoundary = getHandleBoundary(); - private boolean validPress = false; - private Point startingPoint; - - private int getHandleBoundary() { - Border border = statusBar.getBorder(); - - if (border == null || !statusBar.isResizeHandleEnabled()) { - return 0; - } - - if (statusBar.getComponentOrientation().isLeftToRight()) { - return border.getBorderInsets(statusBar).right; - } else { - return border.getBorderInsets(statusBar).left; - } - } - - private boolean isHandleAreaPoint(Point point) { - if (window == null || window.isMaximumSizeSet()) { - return false; - } - - if (statusBar.getComponentOrientation().isLeftToRight()) { - return point.x >= statusBar.getWidth() - handleBoundary; - } else { - return point.x <= handleBoundary; - } - } - - /** - * {@inheritDoc} - */ - @Override - public void mouseClicked(MouseEvent e) { - //does nothing - } - - /** - * {@inheritDoc} - */ - @Override - public void mouseEntered(MouseEvent e) { - if (isHandleAreaPoint(e.getPoint())) { - if (statusBar.getComponentOrientation().isLeftToRight()) { - window.setCursor(Cursor.getPredefinedCursor( - Cursor.SE_RESIZE_CURSOR)); - } else { - window.setCursor(Cursor.getPredefinedCursor( - Cursor.SW_RESIZE_CURSOR)); - } - } else { - window.setCursor(null); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void mouseExited(MouseEvent e) { - if (!validPress) { - window.setCursor(null); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void mousePressed(MouseEvent e) { - validPress = SwingUtilities.isLeftMouseButton(e) && isHandleAreaPoint(e.getPoint()); - startingPoint = e.getPoint(); - SwingUtilities.convertPointToScreen(startingPoint, statusBar); - } - - /** - * {@inheritDoc} - */ - @Override - public void mouseReleased(MouseEvent e) { - validPress = !SwingUtilities.isLeftMouseButton(e); - window.validate(); - window.setCursor(null); - } - - /** - * {@inheritDoc} - */ - @Override - public void mouseDragged(MouseEvent e) { - if (validPress) { - Rectangle wb = window.getBounds(); - Point p = e.getPoint(); - SwingUtilities.convertPointToScreen(p, statusBar); - - wb.height += (p.y - startingPoint.y); - if (statusBar.getComponentOrientation().isLeftToRight()) { - wb.width += (p.x - startingPoint.x); - } else { - wb.x += (p.x - startingPoint.x); - wb.width += (startingPoint.x - p.x); - } - - window.setBounds(wb); - window.validate(); - startingPoint = p; - } - } - - /** - * {@inheritDoc} - */ - @Override - public void mouseMoved(MouseEvent e) { - if (isHandleAreaPoint(e.getPoint())) { - if (statusBar.getComponentOrientation().isLeftToRight()) { - window.setCursor(Cursor.getPredefinedCursor( - Cursor.SE_RESIZE_CURSOR)); - } else { - window.setCursor(Cursor.getPredefinedCursor( - Cursor.SW_RESIZE_CURSOR)); - } - } else { - window.setCursor(null); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void propertyChange(PropertyChangeEvent evt) { - if ("ancestor".equals(evt.getPropertyName())) { - window = SwingUtilities.getWindowAncestor(statusBar); - - boolean useResizeHandle = statusBar.getParent() != null - && statusBar.getRootPane() != null - && (statusBar.getParent() == statusBar.getRootPane() - || statusBar.getParent() == statusBar.getRootPane().getContentPane()); - statusBar.setResizeHandleEnabled(useResizeHandle); - } else if ("border".equals(evt.getPropertyName())) { - handleBoundary = getHandleBoundary(); - } else if ("componentOrientation".equals(evt.getPropertyName())) { - handleBoundary = getHandleBoundary(); - } else if ("resizeHandleEnabled".equals(evt.getPropertyName())) { - //TODO disable handle display - handleBoundary = getHandleBoundary(); - } - } - } - - public static final String AUTO_ADD_SEPARATOR = new StringBuffer("auto-add-separator").toString(); - /** - * Used to help reduce the amount of trash being generated - */ - private static Insets TEMP_INSETS; - /** - * The one and only JXStatusBar for this UI delegate - */ - protected JXStatusBar statusBar; - - protected MouseListener mouseListener; - - protected MouseMotionListener mouseMotionListener; - - protected PropertyChangeListener propertyChangeListener; - - private Handler handler; - - /** Creates a new instance of BasicStatusBarUI */ - public BasicStatusBarUI() { - } - - /** - * Returns an instance of the UI delegate for the specified component. - * Each subclass must provide its own static createUI - * method that returns an instance of that UI delegate subclass. - * If the UI delegate subclass is stateless, it may return an instance - * that is shared by multiple components. If the UI delegate is - * stateful, then it should return a new instance per component. - * The default implementation of this method throws an error, as it - * should never be invoked. - */ - public static ComponentUI createUI(JComponent c) { - return new BasicStatusBarUI(); - } - - /** - * {@inheritDoc} - */ - @Override - public void installUI(JComponent c) { - assert c instanceof JXStatusBar; - statusBar = (JXStatusBar)c; - - installDefaults(statusBar); - installListeners(statusBar); - - // only set the layout manager if the layout manager of the component is - // null or a UIResource. Do not replace custom layout managers. - LayoutManager m = statusBar.getLayout(); - if (m == null || m instanceof UIResource) { - statusBar.setLayout(createLayout()); - } - } - - protected void installDefaults(JXStatusBar sb) { - //only set the border if it is an instanceof UIResource - //In other words, only replace the border if it has not been - //set by the developer. UIResource is the flag we use to indicate whether - //the value was set by the UIDelegate, or by the developer. - Border b = statusBar.getBorder(); - if (b == null || b instanceof UIResource) { - statusBar.setBorder(createBorder()); - } - - LookAndFeel.installProperty(sb, "opaque", Boolean.TRUE); - } - - private Handler getHandler() { - if (handler == null) { - handler = new Handler(); - } - - return handler; - } - - /** - * Creates a {@code MouseListener} which will be added to the - * status bar. If this method returns null then it will not - * be added to the status bar. - *

                    - * Subclasses may override this method to return instances of their own - * MouseEvent handlers. - * - * @return an instance of a {@code MouseListener} or null - */ - protected MouseListener createMouseListener() { - return getHandler(); - } - - /** - * Creates a {@code MouseMotionListener} which will be added to the - * status bar. If this method returns null then it will not - * be added to the status bar. - *

                    - * Subclasses may override this method to return instances of their own - * MouseEvent handlers. - * - * @return an instance of a {@code MouseMotionListener} or null - */ - protected MouseMotionListener createMouseMotionListener() { - return getHandler(); - } - - /** - * Creates a {@code PropertyChangeListener} which will be added to the - * status bar. If this method returns null then it will not - * be added to the status bar. - *

                    - * Subclasses may override this method to return instances of their own - * PropertyChangeEvent handlers. - * - * @return an instance of a {@code PropertyChangeListener} or null - */ - protected PropertyChangeListener createPropertyChangeListener() { - return getHandler(); - } - - /** - * Create and install the listeners for the status bar. - * This method is called when the UI is installed. - */ - protected void installListeners(JXStatusBar sb) { - if ((mouseListener = createMouseListener()) != null) { - statusBar.addMouseListener(mouseListener); - } - - if ((mouseMotionListener = createMouseMotionListener()) != null) { - statusBar.addMouseMotionListener(mouseMotionListener); - } - - if ((propertyChangeListener = createPropertyChangeListener()) != null) { - statusBar.addPropertyChangeListener(propertyChangeListener); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void uninstallUI(JComponent c) { - assert c instanceof JXStatusBar; - - uninstallDefaults(statusBar); - uninstallListeners(statusBar); - - if (statusBar.getLayout() instanceof UIResource) { - statusBar.setLayout(null); - } - } - - protected void uninstallDefaults(JXStatusBar sb) { - if (sb.getBorder() instanceof UIResource) { - sb.setBorder(null); - } - } - - /** - * Remove the installed listeners from the status bar. - * The number and types of listeners removed in this method should be - * the same that were added in installListeners - */ - protected void uninstallListeners(JXStatusBar sb) { - if (mouseListener != null) { - statusBar.removeMouseListener(mouseListener); - } - - if (mouseMotionListener != null) { - statusBar.removeMouseMotionListener(mouseMotionListener); - } - - if (propertyChangeListener != null) { - statusBar.removePropertyChangeListener(propertyChangeListener); - } - } - - @Override - public void paint(Graphics g, JComponent c) { - //paint the background if opaque - if (statusBar.isOpaque()) { - Graphics2D g2 = (Graphics2D)g; - paintBackground(g2, statusBar); - } - - if (includeSeparators()) { - //now paint the separators - TEMP_INSETS = getSeparatorInsets(TEMP_INSETS); - for (int i=0; i constraints = new HashMap(); - - @Override - public void addLayoutComponent(String name, Component comp) {addLayoutComponent(comp, null);} - @Override - public void removeLayoutComponent(Component comp) {constraints.remove(comp);} - @Override - public Dimension minimumLayoutSize(Container parent) {return preferredLayoutSize(parent);} - @Override - public Dimension maximumLayoutSize(Container target) {return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);} - @Override - public float getLayoutAlignmentX(Container target) {return .5f;} - @Override - public float getLayoutAlignmentY(Container target) {return .5f;} - @Override - public void invalidateLayout(Container target) {} - - @Override - public void addLayoutComponent(Component comp, Object constraint) { - //we accept an Insets, a ResizeBehavior, or a Constraint. - if (constraint instanceof Insets) { - constraint = new Constraint((Insets)constraint); - } else if (constraint instanceof Constraint.ResizeBehavior) { - constraint = new Constraint((Constraint.ResizeBehavior)constraint); - } - - constraints.put(comp, (Constraint)constraint); - } - - @Override - public Dimension preferredLayoutSize(Container parent) { - Dimension prefSize = new Dimension(); - int count = 0; - for (Component comp : constraints.keySet()) { - Constraint c = constraints.get(comp); - Dimension d = comp.getPreferredSize(); - int prefWidth = 0; - if (c != null) { - Insets i = c.getInsets(); - d.width += i.left + i.right; - d.height += i.top + i.bottom; - prefWidth = c.getFixedWidth(); - } - prefSize.height = Math.max(prefSize.height, d.height); - prefSize.width += Math.max(d.width, prefWidth); - - //If this is not the last component, add extra space between each - //component (for the separator). - count++; - if (includeSeparators() && constraints.size() < count) { - prefSize.width += getSeparatorWidth(); - } - } - - Insets insets = parent.getInsets(); - prefSize.height += insets.top + insets.bottom; - prefSize.width += insets.left + insets.right; - return prefSize; - } - - @Override - public void layoutContainer(Container parent) { - /* - * Layout algorithm: - * If the parent width is less than the sum of the preferred - * widths of the components (including separators), where - * preferred width means either the component preferred width + - * constraint insets, or fixed width + constraint insets, then - * simply layout the container from left to right and let the - * right hand components flow off the parent. - * - * Otherwise, lay out each component according to its preferred - * width except for components with a FILL constraint. For these, - * resize them evenly for each FILL constraint. - */ - - //the insets of the parent component. - Insets parentInsets = parent.getInsets(); - //the available width for putting components. - int availableWidth = parent.getWidth() - parentInsets.left - parentInsets.right; - if (includeSeparators()) { - //remove from availableWidth the amount of space the separators will take - availableWidth -= (parent.getComponentCount() - 1) * getSeparatorWidth(); - } - - //the preferred widths of all of the components -- where preferred - //width mean the preferred width after calculating fixed widths and - //constraint insets - int[] preferredWidths = new int[parent.getComponentCount()]; - int sumPreferredWidths = 0; - for (int i=0; i sumPreferredWidths) { - //the number of components with a fill constraint - int numFilledComponents = 0; - for (Component comp : parent.getComponents()) { - Constraint c = constraints.get(comp); - if (c != null && c.getResizeBehavior() == Constraint.ResizeBehavior.FILL) { - numFilledComponents++; - } - } - - if (numFilledComponents > 0) { - //calculate the share of free space each FILL component will take - availableWidth -= sumPreferredWidths; - double weight = 1.0 / (double)numFilledComponents; - int share = (int)(availableWidth * weight); - int remaining = numFilledComponents; - for (int i=0; i 1) { - preferredWidths[i] += share; - availableWidth -= share; - } else { - preferredWidths[i] += availableWidth; - } - remaining--; - } - } - } - } - - //now lay out the components - int nextX = parentInsets.left; - int height = parent.getHeight() - parentInsets.top - parentInsets.bottom; - for (int i=0; iJXTaskPaneContainer UI. - * - * @author Frederic Lavigne - * @author Karl Schaefer - */ -public class BasicTaskPaneContainerUI extends TaskPaneContainerUI { - /** - * A {@code UIResource} implementation of {@code VerticalLayout}. - * - * @author Karl George Schaefer - */ - protected class VerticalLayoutUIResource extends VerticalLayout implements UIResource { - /** - * The default layout. - */ - public VerticalLayoutUIResource() { - super(); - } - - /** - * Defines a layout with the specified gap. - * - * @param gap - * the gap between components - */ - public VerticalLayoutUIResource(int gap) { - super(gap); - } - } - - /** - * Returns a new instance of BasicTaskPaneContainerUI. - * BasicTaskPaneContainerUI delegates are allocated one per - * JXTaskPaneContainer. - * - * @return A new TaskPaneContainerUI implementation for the Basic look and - * feel. - */ - public static ComponentUI createUI(JComponent c) { - return new BasicTaskPaneContainerUI(); - } - - /** - * The task pane container managed by this UI delegate. - */ - protected JXTaskPaneContainer taskPane; - - /** - * {@inheritDoc} - */ - @Override -public void installUI(JComponent c) { - super.installUI(c); - taskPane = (JXTaskPaneContainer)c; - installDefaults(); - - LayoutManager manager = taskPane.getLayout(); - - if (manager == null || manager instanceof UIResource) { - taskPane.setLayout(createDefaultLayout()); - } - } - - /** - * Installs the default colors, border, and painter of the task pane - * container. - */ - protected void installDefaults() { - LookAndFeel.installColors(taskPane, "TaskPaneContainer.background", - "TaskPaneContainer.foreground"); - LookAndFeel.installBorder(taskPane, "TaskPaneContainer.border"); - LookAndFeelAddons.installBackgroundPainter(taskPane, - "TaskPaneContainer.backgroundPainter"); - LookAndFeel.installProperty(taskPane, "opaque", Boolean.TRUE); - } - - /** - * Constructs a layout manager to be used by the Look and Feel. - * @return the layout manager for the current Look and Feel - */ - protected LayoutManager createDefaultLayout() { - return new VerticalLayoutUIResource(14); - } - - /** - * {@inheritDoc} - */ - @Override - public void uninstallUI(JComponent c) { - uninstallDefaults(); - - super.uninstallUI(c); - } - - /** - * Uninstalls the default colors, border, and painter of the task pane - * container. - */ - protected void uninstallDefaults() { - LookAndFeel.uninstallBorder(taskPane); - LookAndFeelAddons.uninstallBackgroundPainter(taskPane); - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicTaskPaneUI.java b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicTaskPaneUI.java deleted file mode 100644 index 11d52f4ca4..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicTaskPaneUI.java +++ /dev/null @@ -1,874 +0,0 @@ -/* - * $Id: BasicTaskPaneUI.java 4260 2012-11-14 20:59:08Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf.basic; - -import org.jdesktop.swingx.JXCollapsiblePane; -import org.jdesktop.swingx.JXHyperlink; -import org.jdesktop.swingx.JXTaskPane; -import org.jdesktop.swingx.SwingXUtilities; -import org.jdesktop.swingx.icon.EmptyIcon; -import org.jdesktop.swingx.plaf.TaskPaneUI; - -import javax.swing.*; -import javax.swing.border.Border; -import javax.swing.border.CompoundBorder; -import javax.swing.event.MouseInputAdapter; -import javax.swing.event.MouseInputListener; -import javax.swing.plaf.*; -import javax.swing.plaf.basic.BasicGraphicsUtils; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; -import java.awt.event.MouseEvent; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; - -import static org.jdesktop.swingx.SwingXUtilities.isUIInstallable; - -/** - * Base implementation of the JXTaskPane UI. - * - * @author Frederic Lavigne - */ -public class BasicTaskPaneUI extends TaskPaneUI { - - private static FocusListener focusListener = new RepaintOnFocus(); - - public static ComponentUI createUI(JComponent c) { - return new BasicTaskPaneUI(); - } - - protected int titleHeight = 25; - protected int roundHeight = 5; - - protected JXTaskPane group; - - protected boolean mouseOver; - protected MouseInputListener mouseListener; - - protected PropertyChangeListener propertyListener; - - /** - * {@inheritDoc} - */ - @Override - public void installUI(JComponent c) { - super.installUI(c); - group = (JXTaskPane) c; - - installDefaults(); - installListeners(); - installKeyboardActions(); - } - - /** - * Installs default properties. Following properties are installed: - *

                      - *
                    • TaskPane.background
                    • - *
                    • TaskPane.foreground
                    • - *
                    • TaskPane.font
                    • - *
                    • TaskPane.borderColor
                    • - *
                    • TaskPane.titleForeground
                    • - *
                    • TaskPane.titleBackgroundGradientStart
                    • - *
                    • TaskPane.titleBackgroundGradientEnd
                    • - *
                    • TaskPane.titleOver
                    • - *
                    • TaskPane.specialTitleOver
                    • - *
                    • TaskPane.specialTitleForeground
                    • - *
                    • TaskPane.specialTitleBackground
                    • - *
                    - */ - protected void installDefaults() { - LookAndFeel.installColorsAndFont(group, "TaskPane.background", - "TaskPane.foreground", "TaskPane.font"); - LookAndFeel.installProperty(group, "opaque", false); - - if (isUIInstallable(group.getBorder())) { - group.setBorder(createPaneBorder()); - } - - if (group.getContentPane() instanceof JComponent) { - JComponent content = (JComponent) group.getContentPane(); - - LookAndFeel.installColorsAndFont(content, - "TaskPane.background", "TaskPane.foreground", "TaskPane.font"); - - if (isUIInstallable(content.getBorder())) { - content.setBorder(createContentPaneBorder()); - } - } - } - - /** - * Installs listeners for UI delegate. - */ - protected void installListeners() { - mouseListener = createMouseInputListener(); - group.addMouseMotionListener(mouseListener); - group.addMouseListener(mouseListener); - - group.addFocusListener(focusListener); - propertyListener = createPropertyListener(); - group.addPropertyChangeListener(propertyListener); - } - - /** - * Installs keyboard actions to allow task pane to react on hot keys. - */ - protected void installKeyboardActions() { - InputMap inputMap = (InputMap) UIManager.get("TaskPane.focusInputMap"); - if (inputMap != null) { - SwingUtilities.replaceUIInputMap(group, JComponent.WHEN_FOCUSED, - inputMap); - } - - ActionMap map = getActionMap(); - if (map != null) { - SwingUtilities.replaceUIActionMap(group, map); - } - } - - ActionMap getActionMap() { - ActionMap map = new ActionMapUIResource(); - map.put("toggleCollapsed", new ToggleCollapsedAction()); - return map; - } - - @Override - public void uninstallUI(JComponent c) { - uninstallListeners(); - super.uninstallUI(c); - } - - /** - * Uninstalls previously installed listeners to free component for garbage collection. - */ - protected void uninstallListeners() { - group.removeMouseListener(mouseListener); - group.removeMouseMotionListener(mouseListener); - group.removeFocusListener(focusListener); - group.removePropertyChangeListener(propertyListener); - } - - /** - * Creates new toggle listener. - * @return MouseInputListener reacting on toggle events of task pane. - */ - protected MouseInputListener createMouseInputListener() { - return new ToggleListener(); - } - - /** - * Creates property change listener for task pane. - * @return Property change listener reacting on changes to the task pane. - */ - protected PropertyChangeListener createPropertyListener() { - return new ChangeListener(); - } - - /** - * Evaluates whenever given mouse even have occurred within borders of task pane. - * @param event Evaluated event. - * @return True if event occurred within task pane area, false otherwise. - */ - protected boolean isInBorder(MouseEvent event) { - return event.getY() < getTitleHeight(event.getComponent()); - } - - /** - * Gets current title height. Default value is 25 if not specified otherwise. Method checks - * provided component for user set font (!instanceof FontUIResource), if font is set, height - * will be calculated from font metrics instead of using internal preset height. - * @return Current title height. - */ - protected int getTitleHeight(Component c) { - if (c instanceof JXTaskPane) { - JXTaskPane taskPane = (JXTaskPane) c; - Font font = taskPane.getFont(); - int height = titleHeight; - - if (font != null && !(font instanceof FontUIResource)) { - height = Math.max(height, taskPane.getFontMetrics(font).getHeight()); - } - - Icon icon = taskPane.getIcon(); - - if (icon != null) { - height = Math.max(height, icon.getIconHeight() + 4); - } - - return height; - } - - return titleHeight; - } - - /** - * Creates new border for task pane. - * @return Fresh border on every call. - */ - protected Border createPaneBorder() { - return new PaneBorder(); - } - - @Override - public Dimension getPreferredSize(JComponent c) { - Component component = group.getComponent(0); - if (!(component instanceof JXCollapsiblePane)) { - // something wrong in this JXTaskPane - return super.getPreferredSize(c); - } - - JXCollapsiblePane collapsible = (JXCollapsiblePane) component; - Dimension dim = collapsible.getPreferredSize(); - - Border groupBorder = group.getBorder(); - if (groupBorder instanceof PaneBorder) { - ((PaneBorder) groupBorder).label.setDisplayedMnemonic(group - .getMnemonic()); - Dimension border = ((PaneBorder) groupBorder) - .getPreferredSize(group); - dim.width = Math.max(dim.width, border.width); - dim.height += border.height; - } else { - dim.height += getTitleHeight(c); - } - - return dim; - } - - /** - * Creates content pane border. - * @return Fresh content pane border initialized with current value of TaskPane.borderColor - * on every call. - */ - protected Border createContentPaneBorder() { - Color borderColor = UIManager.getColor("TaskPane.borderColor"); - return new CompoundBorder(new ContentPaneBorder(borderColor), - BorderFactory.createEmptyBorder(10, 10, 10, 10)); - } - - @Override - public Component createAction(Action action) { - JXHyperlink link = new JXHyperlink(action) { - @Override - public void updateUI() { - super.updateUI(); - // ensure the ui of this link is correctly update on l&f changes - configure(this); - } - }; - configure(link); - return link; - } - - /** - * Configures internally used hyperlink on new action creation and on every call to - * updateUI(). - * @param link Configured hyperlink. - */ - protected void configure(JXHyperlink link) { - link.setOpaque(false); - link.setBorderPainted(false); - link.setFocusPainted(true); - link.setForeground(UIManager.getColor("TaskPane.titleForeground")); - } - - /** - * Ensures expanded group is visible. Issues delayed request for scrolling to visible. - */ - protected void ensureVisible() { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - group.scrollRectToVisible(new Rectangle(group.getWidth(), group - .getHeight())); - } - }); - } - - /** - * Focus listener responsible for repainting of the taskpane on focus change. - */ - static class RepaintOnFocus implements FocusListener { - @Override - public void focusGained(FocusEvent e) { - e.getComponent().repaint(); - } - - @Override - public void focusLost(FocusEvent e) { - e.getComponent().repaint(); - } - } - - /** - * Change listener responsible for change handling. - */ - class ChangeListener implements PropertyChangeListener { - @Override - public void propertyChange(PropertyChangeEvent evt) { - // if group is expanded but not animated - // or if animated has reached expanded state - // scroll to visible if scrollOnExpand is enabled - if (("collapsed".equals(evt.getPropertyName()) - && Boolean.TRUE.equals(evt.getNewValue()) && !group - .isAnimated())) { - if (group.isScrollOnExpand()) { - ensureVisible(); - } - } else if (JXTaskPane.ICON_CHANGED_KEY - .equals(evt.getPropertyName()) - || JXTaskPane.TITLE_CHANGED_KEY.equals(evt - .getPropertyName()) - || JXTaskPane.SPECIAL_CHANGED_KEY.equals(evt - .getPropertyName())) { - // icon, title, special must lead to a repaint() - group.repaint(); - } else if ("mnemonic".equals(evt.getPropertyName())) { - SwingXUtilities.updateMnemonicBinding(group, "toggleCollapsed"); - - Border b = group.getBorder(); - - if (b instanceof PaneBorder) { - int key = (Integer) evt.getNewValue(); - ((PaneBorder) b).label.setDisplayedMnemonic(key); - } - } - } - } - - /** - * Mouse listener responsible for handling of toggle events. - */ - class ToggleListener extends MouseInputAdapter { - @Override - public void mouseEntered(MouseEvent e) { - if (isInBorder(e)) { - e.getComponent().setCursor( - Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - } else { - mouseOver = false; - group.repaint(0, 0, group.getWidth(), getTitleHeight(group)); - } - } - - @Override - public void mouseExited(MouseEvent e) { - e.getComponent().setCursor(null); - mouseOver = false; - group.repaint(0, 0, group.getWidth(), getTitleHeight(group)); - } - - @Override - public void mouseMoved(MouseEvent e) { - if (isInBorder(e)) { - e.getComponent().setCursor( - Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - mouseOver = true; - } else { - e.getComponent().setCursor(null); - mouseOver = false; - } - - group.repaint(0, 0, group.getWidth(), getTitleHeight(group)); - } - - @Override - public void mouseReleased(MouseEvent e) { - if (SwingUtilities.isLeftMouseButton(e) && isInBorder(e)) { - group.setCollapsed(!group.isCollapsed()); - } - } - } - - /** - * Toggle expanded action. - */ - class ToggleCollapsedAction extends AbstractAction { - /** - * Serial version UID. - */ - private static final long serialVersionUID = 5676859881615358815L; - - public ToggleCollapsedAction() { - super("toggleCollapsed"); - } - - @Override - public void actionPerformed(ActionEvent e) { - group.setCollapsed(!group.isCollapsed()); - } - - @Override - public boolean isEnabled() { - return group.isVisible(); - } - } - - /** - * Toggle icon. - */ - protected static class ChevronIcon implements Icon { - boolean up = true; - - public ChevronIcon(boolean up) { - this.up = up; - } - - @Override - public int getIconHeight() { - return 3; - } - - @Override - public int getIconWidth() { - return 6; - } - - @Override - public void paintIcon(Component c, Graphics g, int x, int y) { - if (up) { - g.drawLine(x + 3, y, x, y + 3); - g.drawLine(x + 3, y, x + 6, y + 3); - } else { - g.drawLine(x, y, x + 3, y + 3); - g.drawLine(x + 3, y + 3, x + 6, y); - } - } - } - - /** - * The border around the content pane - */ - protected static class ContentPaneBorder implements Border, UIResource { - Color color; - - public ContentPaneBorder(Color color) { - this.color = color; - } - - @Override - public Insets getBorderInsets(Component c) { - return new Insets(0, 1, 1, 1); - } - - @Override - public boolean isBorderOpaque() { - return true; - } - - @Override - public void paintBorder(Component c, Graphics g, int x, int y, - int width, int height) { - g.setColor(color); - g.drawLine(x, y, x, y + height - 1); - g.drawLine(x, y + height - 1, x + width - 1, y + height - 1); - g.drawLine(x + width - 1, y, x + width - 1, y + height - 1); - } - } - - /** - * The border of the taskpane group paints the "text", the "icon", the - * "expanded" status and the "special" type. - * - */ - protected class PaneBorder implements Border, UIResource { - - protected Color borderColor; - protected Color titleForeground; - protected Color specialTitleBackground; - protected Color specialTitleForeground; - protected Color titleBackgroundGradientStart; - protected Color titleBackgroundGradientEnd; - - protected Color titleOver; - protected Color specialTitleOver; - - protected JLabel label; - - /** - * Creates new instance of individual pane border. - */ - public PaneBorder() { - borderColor = UIManager.getColor("TaskPane.borderColor"); - - titleForeground = UIManager.getColor("TaskPane.titleForeground"); - - specialTitleBackground = UIManager - .getColor("TaskPane.specialTitleBackground"); - specialTitleForeground = UIManager - .getColor("TaskPane.specialTitleForeground"); - - titleBackgroundGradientStart = UIManager - .getColor("TaskPane.titleBackgroundGradientStart"); - titleBackgroundGradientEnd = UIManager - .getColor("TaskPane.titleBackgroundGradientEnd"); - - titleOver = UIManager.getColor("TaskPane.titleOver"); - if (titleOver == null) { - titleOver = specialTitleBackground.brighter(); - } - specialTitleOver = UIManager.getColor("TaskPane.specialTitleOver"); - if (specialTitleOver == null) { - specialTitleOver = specialTitleBackground.brighter(); - } - - label = new JLabel(); - label.setOpaque(false); - label.setIconTextGap(8); - } - - @Override - public Insets getBorderInsets(Component c) { - return new Insets(getTitleHeight(c), 0, 0, 0); - } - - /** - * Overwritten to always return true to speed up - * painting. Don't use transparent borders unless providing UI delegate - * that provides proper return value when calling this method. - * - * @see Border#isBorderOpaque() - */ - @Override - public boolean isBorderOpaque() { - return true; - } - - /** - * Calculates the preferred border size, its size so all its content - * fits. - * - * @param group - * Selected group. - */ - public Dimension getPreferredSize(JXTaskPane group) { - // calculate the title width so it is fully visible - // it starts with the title width - configureLabel(group); - Dimension dim = label.getPreferredSize(); - // add the title left offset - dim.width += 3; - // add the controls width - dim.width += getTitleHeight(group); - // and some space between label and controls - dim.width += 3; - - dim.height = getTitleHeight(group); - return dim; - } - - /** - * Paints background of the title. This may differ based on properties - * of the group. - * - * @param group - * Selected group. - * @param g - * Target graphics. - */ - protected void paintTitleBackground(JXTaskPane group, Graphics g) { - if (group.isSpecial()) { - g.setColor(specialTitleBackground); - } else { - g.setColor(titleBackgroundGradientStart); - } - g.fillRect(0, 0, group.getWidth(), getTitleHeight(group) - 1); - } - - /** - * Paints current group title. - * - * @param group - * Selected group. - * @param g - * Target graphics. - * @param textColor - * Title color. - * @param x - * X coordinate of the top left corner. - * @param y - * Y coordinate of the top left corner. - * @param width - * Width of the box. - * @param height - * Height of the box. - */ - protected void paintTitle(JXTaskPane group, Graphics g, - Color textColor, int x, int y, int width, int height) { - configureLabel(group); - label.setForeground(textColor); - if (group.getFont() != null && ! (group.getFont() instanceof FontUIResource)) { - label.setFont(group.getFont()); - } - g.translate(x, y); - label.setBounds(0, 0, width, height); - label.paint(g); - g.translate(-x, -y); - } - - /** - * Configures label for the group using its title, font, icon and - * orientation. - * - * @param group - * Selected group. - */ - protected void configureLabel(JXTaskPane group) { - label.applyComponentOrientation(group.getComponentOrientation()); - label.setFont(group.getFont()); - label.setText(group.getTitle()); - label.setIcon(group.getIcon() == null ? new EmptyIcon() : group - .getIcon()); - } - - /** - * Paints expanded controls. Default implementation does nothing. - * - * @param group - * Expanded group. - * @param g - * Target graphics. - * @param x - * X coordinate of the top left corner. - * @param y - * Y coordinate of the top left corner. - * @param width - * Width of the box. - * @param height - * Height of the box. - */ - protected void paintExpandedControls(JXTaskPane group, Graphics g, - int x, int y, int width, int height) { - } - - /** - * Gets current paint color. - * - * @param group - * Selected group. - * @return Color to be used for painting provided group. - */ - protected Color getPaintColor(JXTaskPane group) { - Color paintColor; - if (isMouseOverBorder()) { - if (mouseOver) { - if (group.isSpecial()) { - paintColor = specialTitleOver; - } else { - paintColor = titleOver; - } - } else { - if (group.isSpecial()) { - paintColor = specialTitleForeground; - } else { - paintColor = group.getForeground() == null || group.getForeground() instanceof ColorUIResource ? titleForeground : group.getForeground(); - } - } - } else { - if (group.isSpecial()) { - paintColor = specialTitleForeground; - } else { - paintColor = group.getForeground() == null || group.getForeground() instanceof ColorUIResource ? titleForeground : group.getForeground(); - } - } - return paintColor; - } - - /* - * @see javax.swing.border.Border#paintBorder(java.awt.Component, - * java.awt.Graphics, int, int, int, int) - */ - @Override - public void paintBorder(Component c, Graphics g, int x, int y, - int width, int height) { - - JXTaskPane group = (JXTaskPane) c; - - // calculate position of title and toggle controls - int controlWidth = getTitleHeight(group) - 2 * getRoundHeight(); - int controlX = group.getWidth() - getTitleHeight(group); - int controlY = getRoundHeight() - 1; - int titleX = 3; - int titleY = 0; - int titleWidth = group.getWidth() - getTitleHeight(group) - 3; - int titleHeight = getTitleHeight(group); - - if (!group.getComponentOrientation().isLeftToRight()) { - controlX = group.getWidth() - controlX - controlWidth; - titleX = group.getWidth() - titleX - titleWidth; - } - - // paint the title background - paintTitleBackground(group, g); - - // paint the the toggles - paintExpandedControls(group, g, controlX, controlY, controlWidth, - controlWidth); - - // paint the title text and icon - Color paintColor = getPaintColor(group); - - // focus painted same color as text - if (group.hasFocus()) { - paintFocus(g, paintColor, 3, 3, width - 6, getTitleHeight(group) - 6); - } - - paintTitle(group, g, paintColor, titleX, titleY, titleWidth, - titleHeight); - } - - /** - * Paints oval 'border' area around the control itself. - * - * @param group - * Expanded group. - * @param g - * Target graphics. - * @param x - * X coordinate of the top left corner. - * @param y - * Y coordinate of the top left corner. - * @param width - * Width of the box. - * @param height - * Height of the box. - */ - protected void paintRectAroundControls(JXTaskPane group, Graphics g, - int x, int y, int width, int height, Color highColor, - Color lowColor) { - if (mouseOver) { - int x2 = x + width; - int y2 = y + height; - g.setColor(highColor); - g.drawLine(x, y, x2, y); - g.drawLine(x, y, x, y2); - g.setColor(lowColor); - g.drawLine(x2, y, x2, y2); - g.drawLine(x, y2, x2, y2); - } - } - - /** - * Paints oval 'border' area around the control itself. - * - * @param group - * Expanded group. - * @param g - * Target graphics. - * @param x - * X coordinate of the top left corner. - * @param y - * Y coordinate of the top left corner. - * @param width - * Width of the box. - * @param height - * Height of the box. - */ - protected void paintOvalAroundControls(JXTaskPane group, Graphics g, - int x, int y, int width, int height) { - if (group.isSpecial()) { - g.setColor(specialTitleBackground.brighter()); - g.drawOval(x, y, width, height); - } else { - g.setColor(titleBackgroundGradientStart); - g.fillOval(x, y, width, height); - - g.setColor(titleBackgroundGradientEnd.darker()); - g.drawOval(x, y, width, width); - } - } - - /** - * Paints controls for the group. - * - * @param group - * Expanded group. - * @param g - * Target graphics. - * @param x - * X coordinate of the top left corner. - * @param y - * Y coordinate of the top left corner. - * @param width - * Width of the box. - * @param height - * Height of the box. - */ - protected void paintChevronControls(JXTaskPane group, Graphics g, - int x, int y, int width, int height) { - ChevronIcon chevron; - if (group.isCollapsed()) { - chevron = new ChevronIcon(false); - } else { - chevron = new ChevronIcon(true); - } - int chevronX = x + width / 2 - chevron.getIconWidth() / 2; - int chevronY = y + (height / 2 - chevron.getIconHeight()); - chevron.paintIcon(group, g, chevronX, chevronY); - chevron.paintIcon(group, g, chevronX, chevronY - + chevron.getIconHeight() + 1); - } - - /** - * Paints focused group. - * - * @param g - * Target graphics. - * @param paintColor - * Focused group color. - * @param x - * X coordinate of the top left corner. - * @param y - * Y coordinate of the top left corner. - * @param width - * Width of the box. - * @param height - * Height of the box. - */ - protected void paintFocus(Graphics g, Color paintColor, int x, int y, - int width, int height) { - g.setColor(paintColor); - BasicGraphicsUtils.drawDashedRect(g, x, y, width, height); - } - - /** - * Default implementation returns false. - * - * @return true if this border wants to display things differently when - * the mouse is over it - */ - protected boolean isMouseOverBorder() { - return false; - } - } - - /** - * Gets size of arc used to round corners. - * - * @return size of arc used to round corners of the panel. - */ - protected int getRoundHeight() { - return roundHeight; - } - -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicTipOfTheDayUI.java b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicTipOfTheDayUI.java deleted file mode 100644 index 0e05fc5537..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicTipOfTheDayUI.java +++ /dev/null @@ -1,352 +0,0 @@ -/* - * $Id: BasicTipOfTheDayUI.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf.basic; - -import org.jdesktop.swingx.JXTipOfTheDay; -import org.jdesktop.swingx.JXTipOfTheDay.ShowOnStartupChoice; -import org.jdesktop.swingx.SwingXUtilities; -import org.jdesktop.swingx.plaf.TipOfTheDayUI; -import org.jdesktop.swingx.plaf.UIManagerExt; -import org.jdesktop.swingx.tips.TipOfTheDayModel.Tip; - -import javax.swing.*; -import javax.swing.plaf.ActionMapUIResource; -import javax.swing.plaf.ComponentUI; -import javax.swing.plaf.basic.BasicHTML; -import javax.swing.text.html.HTMLDocument; -import java.awt.*; -import java.awt.event.*; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.Locale; - -/** - * Base implementation of the JXTipOfTheDay UI. - * - * @author Frederic Lavigne - */ -public class BasicTipOfTheDayUI extends TipOfTheDayUI { - - public static ComponentUI createUI(JComponent c) { - return new BasicTipOfTheDayUI((JXTipOfTheDay)c); - } - - protected JXTipOfTheDay tipPane; - protected JPanel tipArea; - protected Component currentTipComponent; - - protected Font tipFont; - protected PropertyChangeListener changeListener; - - public BasicTipOfTheDayUI(JXTipOfTheDay tipPane) { - this.tipPane = tipPane; - } - - @Override - public JDialog createDialog(Component parentComponent, - final ShowOnStartupChoice choice) { - return createDialog(parentComponent, choice, true); - } - - protected JDialog createDialog(Component parentComponent, - final ShowOnStartupChoice choice, - boolean showPreviousButton) { - Locale locale = parentComponent==null ? null : parentComponent.getLocale(); - String title = UIManagerExt.getString("TipOfTheDay.dialogTitle", locale); - - final JDialog dialog; - - Window window; - if (parentComponent == null) { - window = JOptionPane.getRootFrame(); - } else { - window = (parentComponent instanceof Window)?(Window)parentComponent - :SwingUtilities.getWindowAncestor(parentComponent); - } - - if (window instanceof Frame) { - dialog = new JDialog((Frame)window, title, true); - } else { - dialog = new JDialog((Dialog)window, title, true); - } - - dialog.getContentPane().setLayout(new BorderLayout(10, 10)); - dialog.getContentPane().add(tipPane, BorderLayout.CENTER); - ((JComponent)dialog.getContentPane()).setBorder(BorderFactory - .createEmptyBorder(10, 10, 10, 10)); - - final JCheckBox showOnStartupBox; - - // tip controls - JPanel controls = new JPanel(new BorderLayout()); - dialog.add("South", controls); - - if (choice != null) { - showOnStartupBox = new JCheckBox(UIManagerExt - .getString("TipOfTheDay.showOnStartupText", locale), choice - .isShowingOnStartup()); - controls.add(showOnStartupBox, BorderLayout.CENTER); - } else { - showOnStartupBox = null; - } - - JPanel buttons = - new JPanel(new GridLayout(1, showPreviousButton?3:2, 9, 0)); - controls.add(buttons, BorderLayout.LINE_END); - - if (showPreviousButton) { - JButton previousTipButton = new JButton(UIManagerExt - .getString("TipOfTheDay.previousTipText", locale)); - buttons.add(previousTipButton); - previousTipButton.addActionListener(getActionMap().get("previousTip")); - } - - JButton nextTipButton = new JButton(UIManagerExt - .getString("TipOfTheDay.nextTipText", locale)); - buttons.add(nextTipButton); - nextTipButton.addActionListener(getActionMap().get("nextTip")); - - JButton closeButton = new JButton(UIManagerExt - .getString("TipOfTheDay.closeText", locale)); - buttons.add(closeButton); - - final ActionListener saveChoice = new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - if (choice != null) { - choice.setShowingOnStartup(showOnStartupBox.isSelected()); - } - dialog.setVisible(false); - } - }; - - closeButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - dialog.setVisible(false); - saveChoice.actionPerformed(null); - } - }); - dialog.getRootPane().setDefaultButton(closeButton); - - dialog.addWindowListener(new WindowAdapter() { - @Override - public void windowClosing(WindowEvent e) { - saveChoice.actionPerformed(null); - } - }); - - ((JComponent)dialog.getContentPane()).registerKeyboardAction(saveChoice, - KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), - JComponent.WHEN_IN_FOCUSED_WINDOW); - - dialog.pack(); - dialog.setLocationRelativeTo(parentComponent); - - return dialog; - } - - @Override - public void installUI(JComponent c) { - super.installUI(c); - installDefaults(); - installKeyboardActions(); - installComponents(); - installListeners(); - - showCurrentTip(); - } - - protected void installKeyboardActions() { - ActionMap map = getActionMap(); - if (map != null) { - SwingUtilities.replaceUIActionMap(tipPane, map); - } - } - - ActionMap getActionMap() { - ActionMap map = new ActionMapUIResource(); - map.put("previousTip", new PreviousTipAction()); - map.put("nextTip", new NextTipAction()); - return map; - } - - protected void installListeners() { - changeListener = createChangeListener(); - tipPane.addPropertyChangeListener(changeListener); - } - - protected PropertyChangeListener createChangeListener() { - return new ChangeListener(); - } - - protected void installDefaults() { - LookAndFeel.installColorsAndFont(tipPane, "TipOfTheDay.background", - "TipOfTheDay.foreground", "TipOfTheDay.font"); - LookAndFeel.installBorder(tipPane, "TipOfTheDay.border"); - LookAndFeel.installProperty(tipPane, "opaque", Boolean.TRUE); - tipFont = UIManager.getFont("TipOfTheDay.tipFont"); - } - - protected void installComponents() { - tipPane.setLayout(new BorderLayout()); - - // tip icon - JLabel tipIcon = new JLabel(UIManagerExt - .getString("TipOfTheDay.didYouKnowText", tipPane.getLocale())); - tipIcon.setIcon(UIManager.getIcon("TipOfTheDay.icon")); - tipIcon.setBorder(BorderFactory.createEmptyBorder(22, 15, 22, 15)); - tipPane.add("North", tipIcon); - - // tip area - tipArea = new JPanel(new BorderLayout(2, 2)); - tipArea.setOpaque(false); - tipArea.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); - tipPane.add("Center", tipArea); - } - - @Override - public Dimension getPreferredSize(JComponent c) { - return new Dimension(420, 175); - } - - protected void showCurrentTip() { - if (currentTipComponent != null) { - tipArea.remove(currentTipComponent); - } - - int currentTip = tipPane.getCurrentTip(); - if (currentTip == -1) { - JLabel label = new JLabel(); - label.setOpaque(true); - label.setBackground(UIManager.getColor("TextArea.background")); - currentTipComponent = label; - tipArea.add("Center", currentTipComponent); - return; - } - - // tip does not fall in current tip range - if (tipPane.getModel() == null || tipPane.getModel().getTipCount() == 0 - || (currentTip < 0 && currentTip >= tipPane.getModel().getTipCount())) { - currentTipComponent = new JLabel(); - } else { - Tip tip = tipPane.getModel().getTipAt(currentTip); - - Object tipObject = tip.getTip(); - if (tipObject instanceof Component) { - currentTipComponent = (Component)tipObject; - } else if (tipObject instanceof Icon) { - currentTipComponent = new JLabel((Icon)tipObject); - } else { - JScrollPane tipScroll = new JScrollPane(); - tipScroll.setBorder(null); - tipScroll.setOpaque(false); - tipScroll.getViewport().setOpaque(false); - tipScroll.setBorder(null); - - String text = tipObject == null?"":tipObject.toString(); - - if (BasicHTML.isHTMLString(text)) { - JEditorPane editor = new JEditorPane("text/html", text); - editor.setFont(tipPane.getFont()); -// BasicHTML.updateRenderer(editor, text); - SwingXUtilities.setHtmlFont( - (HTMLDocument) editor.getDocument(), tipPane.getFont()); - editor.setEditable(false); - editor.setBorder(null); - editor.setMargin(null); - editor.setOpaque(false); - tipScroll.getViewport().setView(editor); - } else { - JTextArea area = new JTextArea(text); - area.setFont(tipPane.getFont()); - area.setEditable(false); - area.setLineWrap(true); - area.setWrapStyleWord(true); - area.setBorder(null); - area.setMargin(null); - area.setOpaque(false); - tipScroll.getViewport().setView(area); - } - - currentTipComponent = tipScroll; - } - } - - tipArea.add("Center", currentTipComponent); - tipArea.revalidate(); - tipArea.repaint(); - } - - @Override - public void uninstallUI(JComponent c) { - uninstallListeners(); - uninstallComponents(); - uninstallDefaults(); - super.uninstallUI(c); - } - - protected void uninstallListeners() { - tipPane.removePropertyChangeListener(changeListener); - } - - protected void uninstallComponents() {} - - protected void uninstallDefaults() {} - - class ChangeListener implements PropertyChangeListener { - @Override - public void propertyChange(PropertyChangeEvent evt) { - if (JXTipOfTheDay.CURRENT_TIP_CHANGED_KEY.equals(evt.getPropertyName())) { - showCurrentTip(); - } - } - } - - class PreviousTipAction extends AbstractAction { - public PreviousTipAction() { - super("previousTip"); - } - @Override - public void actionPerformed(ActionEvent e) { - tipPane.previousTip(); - } - @Override - public boolean isEnabled() { - return tipPane.isEnabled(); - } - } - - class NextTipAction extends AbstractAction { - public NextTipAction() { - super("nextTip"); - } - @Override - public void actionPerformed(ActionEvent e) { - tipPane.nextTip(); - } - @Override - public boolean isEnabled() { - return tipPane.isEnabled(); - } - } - -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicTitledPanelUI.java b/src/main/java/org/jdesktop/swingx/plaf/basic/BasicTitledPanelUI.java deleted file mode 100644 index 64f1834481..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/BasicTitledPanelUI.java +++ /dev/null @@ -1,339 +0,0 @@ -/* - * $Id: BasicTitledPanelUI.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.plaf.basic; - -import org.jdesktop.swingx.JXPanel; -import org.jdesktop.swingx.JXTitledPanel; -import org.jdesktop.swingx.SwingXUtilities; -import org.jdesktop.swingx.plaf.TitledPanelUI; - -import javax.swing.*; -import javax.swing.plaf.BorderUIResource; -import javax.swing.plaf.ComponentUI; -import javax.swing.plaf.UIResource; -import java.awt.*; -import java.beans.*; -import java.lang.reflect.Method; -import java.util.logging.Level; -import java.util.logging.Logger; - - -/** - * All TitledPanels contain a title section and a content section. The default - * implementation for the title section relies on a Gradient background. All - * title sections can have components embedded to the "left" or - * "right" of the Title. - * - * @author Richard Bair - * @author Jeanette Winzenburg - * @author rah003 - * - */ -public class BasicTitledPanelUI extends TitledPanelUI { - private static final Logger LOG = Logger.getLogger(BasicTitledPanelUI.class.getName()); - - /** - * JLabel used for the title in the Title section of the JTitledPanel. - */ - protected JLabel caption; - /** - * The Title section panel. - */ - protected JXPanel topPanel; - /** - * Listens to changes in the title of the JXTitledPanel component - */ - protected PropertyChangeListener titleChangeListener; - - protected JComponent left; - protected JComponent right; - - /** Creates a new instance of BasicTitledPanelUI */ - public BasicTitledPanelUI() { - } - - /** - * Returns an instance of the UI delegate for the specified component. - * Each subclass must provide its own static createUI - * method that returns an instance of that UI delegate subclass. - * If the UI delegate subclass is stateless, it may return an instance - * that is shared by multiple components. If the UI delegate is - * stateful, then it should return a new instance per component. - * The default implementation of this method throws an error, as it - * should never be invoked. - */ - public static ComponentUI createUI(JComponent c) { - return new BasicTitledPanelUI(); - } - /** - * Configures the specified component appropriate for the look and feel. - * This method is invoked when the ComponentUI instance is being installed - * as the UI delegate on the specified component. This method should - * completely configure the component for the look and feel, - * including the following: - *
                      - *
                    1. Install any default property values for color, fonts, borders, - * icons, opacity, etc. on the component. Whenever possible, - * property values initialized by the client program should not - * be overridden. - *
                    2. Install a LayoutManager on the component if necessary. - *
                    3. Create/add any required sub-components to the component. - *
                    4. Create/install event listeners on the component. - *
                    5. Create/install a PropertyChangeListener on the component in order - * to detect and respond to component property changes appropriately. - *
                    6. Install keyboard UI (mnemonics, traversal, etc.) on the component. - *
                    7. Initialize any appropriate instance data. - *
                    - * @param c the component where this UI delegate is being installed - * - * @see #uninstallUI - * @see JComponent#setUI - * @see JComponent#updateUI - */ - @Override - public void installUI(JComponent c) { - assert c instanceof JXTitledPanel; - JXTitledPanel titledPanel = (JXTitledPanel)c; - installDefaults(titledPanel); - - caption = createAndConfigureCaption(titledPanel); - topPanel = createAndConfigureTopPanel(titledPanel); - - installComponents(titledPanel); - installListeners(titledPanel); - } - - protected void installDefaults(JXTitledPanel titledPanel) { - installProperty(titledPanel, "titlePainter", UIManager.get("JXTitledPanel.titlePainter")); - installProperty(titledPanel, "titleForeground", UIManager.getColor("JXTitledPanel.titleForeground")); - installProperty(titledPanel, "titleFont", UIManager.getFont("JXTitledPanel.titleFont")); - LookAndFeel.installProperty(titledPanel, "opaque", false); - } - - protected void uninstallDefaults(JXTitledPanel titledPanel) { - } - - protected void installComponents(JXTitledPanel titledPanel) { - topPanel.add(caption, new GridBagConstraints(1, 0, 1, 1, 1.0, 1.0, GridBagConstraints.NORTHWEST, - GridBagConstraints.HORIZONTAL, getCaptionInsets(), 0, 0)); - if (titledPanel.getClientProperty(JXTitledPanel.RIGHT_DECORATION) instanceof JComponent) { - setRightDecoration((JComponent) titledPanel.getClientProperty(JXTitledPanel.RIGHT_DECORATION)); - } - if (titledPanel.getClientProperty(JXTitledPanel.LEFT_DECORATION) instanceof JComponent) { - setLeftDecoration((JComponent) titledPanel.getClientProperty(JXTitledPanel.LEFT_DECORATION)); - } - // swingx#500 - if (!(titledPanel.getLayout() instanceof BorderLayout)){ - titledPanel.setLayout(new BorderLayout()); - } - titledPanel.add(topPanel, BorderLayout.NORTH); - // fix #1063-swingx: must respect custom border - if (SwingXUtilities.isUIInstallable(titledPanel.getBorder())) { - // use uiresource border - // old was: BorderFactory.createRaisedBevelBorder()); - titledPanel.setBorder(BorderUIResource.getRaisedBevelBorderUIResource()); - } - } - - protected void uninstallComponents(JXTitledPanel titledPanel) { - titledPanel.remove(topPanel); - } - - protected Insets getCaptionInsets() { - return UIManager.getInsets("JXTitledPanel.captionInsets"); - } - - protected JXPanel createAndConfigureTopPanel(JXTitledPanel titledPanel) { - JXPanel topPanel = new JXPanel(); - topPanel.setBackgroundPainter(titledPanel.getTitlePainter()); - topPanel.setBorder(BorderFactory.createEmptyBorder()); - topPanel.setLayout(new GridBagLayout()); - topPanel.setOpaque(false); - return topPanel; - } - - protected JLabel createAndConfigureCaption(final JXTitledPanel titledPanel) { - JLabel caption = new JLabel(titledPanel.getTitle()){ - //#501 - @Override - public void updateUI(){ - super.updateUI(); - setForeground(titledPanel.getTitleForeground()); - setFont(titledPanel.getTitleFont()); - } - }; - caption.setFont(titledPanel.getTitleFont()); - caption.setForeground(titledPanel.getTitleForeground()); - return caption; - } - - /** - * Reverses configuration which was done on the specified component during - * installUI. This method is invoked when this - * UIComponent instance is being removed as the UI delegate - * for the specified component. This method should undo the - * configuration performed in installUI, being careful to - * leave the JComponent instance in a clean state (no - * extraneous listeners, look-and-feel-specific property objects, etc.). - * This should include the following: - *
                      - *
                    1. Remove any UI-set borders from the component. - *
                    2. Remove any UI-set layout managers on the component. - *
                    3. Remove any UI-added sub-components from the component. - *
                    4. Remove any UI-added event/property listeners from the component. - *
                    5. Remove any UI-installed keyboard UI from the component. - *
                    6. Nullify any allocated instance data objects to allow for GC. - *
                    - * @param c the component from which this UI delegate is being removed; - * this argument is often ignored, - * but might be used if the UI object is stateless - * and shared by multiple components - * - * @see #installUI - * @see JComponent#updateUI - */ - @Override - public void uninstallUI(JComponent c) { - assert c instanceof JXTitledPanel; - JXTitledPanel titledPanel = (JXTitledPanel) c; - uninstallListeners(titledPanel); - // JW: this is needed to make the gradient paint work correctly... - // LF changes will remove the left/right components... - topPanel.removeAll(); - titledPanel.remove(topPanel); - titledPanel.putClientProperty(JXTitledPanel.LEFT_DECORATION, left); - titledPanel.putClientProperty(JXTitledPanel.RIGHT_DECORATION, right); - caption = null; - topPanel = null; - titledPanel = null; - left = null; - right = null; - } - - protected void installListeners(final JXTitledPanel titledPanel) { - titleChangeListener = new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - if (evt.getPropertyName().equals("title")) { - caption.setText((String)evt.getNewValue()); - } else if (evt.getPropertyName().equals("titleForeground")) { - caption.setForeground((Color)evt.getNewValue()); - } else if (evt.getPropertyName().equals("titleFont")) { - caption.setFont((Font)evt.getNewValue()); - } else if ("titlePainter".equals(evt.getPropertyName())) { - topPanel.setBackgroundPainter(titledPanel.getTitlePainter()); - topPanel.repaint(); - } - } - }; - titledPanel.addPropertyChangeListener(titleChangeListener); - } - - protected void uninstallListeners(JXTitledPanel titledPanel) { - titledPanel.removePropertyChangeListener(titleChangeListener); - } - - protected void installProperty(JComponent c, String propName, Object value) { - try { - BeanInfo bi = Introspector.getBeanInfo(c.getClass()); - for (PropertyDescriptor pd : bi.getPropertyDescriptors()) { - if (pd.getName().equals(propName)) { - Method m = pd.getReadMethod(); - Object oldVal = m.invoke(c); - if (oldVal == null || oldVal instanceof UIResource) { - m = pd.getWriteMethod(); - m.invoke(c, value); - } - } - } - } catch (Exception e) { - LOG.log(Level.FINE, "Failed to install property " + propName, e); - } - } - - /** - * Paints the specified component appropriate for the look and feel. - * This method is invoked from the ComponentUI.update method when - * the specified component is being painted. Subclasses should override - * this method and use the specified Graphics object to - * render the content of the component.

                    - * - * PENDING JW: we don't need this, do we - remove! - * - * @param g the Graphics context in which to paint - * @param c the component being painted; - * this argument is often ignored, - * but might be used if the UI object is stateless - * and shared by multiple components - * - * @see #update - */ - @Override - public void paint(Graphics g, JComponent c) { - super.paint(g, c); - } - - /** - * Adds the given JComponent as a decoration on the right of the title - * @param decoration - */ - @Override - public void setRightDecoration(JComponent decoration) { - if (right != null) topPanel.remove(right); - right = decoration; - if (right != null) { - topPanel.add(decoration, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, UIManager.getInsets("JXTitledPanel.rightDecorationInsets"), 0, 0)); - - } - } - - @Override - public JComponent getRightDecoration() { - return right; - } - - /** - * Adds the given JComponent as a decoration on the left of the title - * @param decoration - */ - @Override - public void setLeftDecoration(JComponent decoration) { - if (left != null) topPanel.remove(left); - left = decoration; - if (left != null) { - topPanel.add(left, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, UIManager.getInsets("JXTitledPanel.leftDecorationInsets"), 0, 0)); - } - } - - @Override - public JComponent getLeftDecoration() { - return left; - } - - /** - * @return the Container acting as the title bar for this component - */ - @Override - public Container getTitleBar() { - return topPanel; - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/CalendarAdapter.java b/src/main/java/org/jdesktop/swingx/plaf/basic/CalendarAdapter.java deleted file mode 100644 index f695a33d0d..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/CalendarAdapter.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * $Id: CalendarAdapter.java 3877 2010-11-03 11:36:33Z kleopatra $ - * - * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.plaf.basic; - -import org.jdesktop.swingx.JXMonthView; -import org.jdesktop.swingx.decorator.ComponentAdapter; - -import java.util.Calendar; - -/** - * ComponentAdapter for a JXMonthView (experimental for internal use of BasicMonthViewUI).

                    - * - * For now, this effectively disables all notion of row/column coordinates. It's focused - * on an externally provided date (as Calendar) and CalendarState. Yeah, I know, that's - * tweaking too much but then, I want to use highlighters which need an adapter... - * - * - * @author Jeanette Winzenburg - */ -class CalendarAdapter extends ComponentAdapter { - - Calendar calendar; - CalendarState dayState; - - /** - * @param component - */ - public CalendarAdapter(JXMonthView component) { - super(component); - } - - /** - * @param calendar2 - * @param dayState2 - * @return - */ - public CalendarAdapter install(Calendar calendar, CalendarState dayState) { - this.calendar = calendar; - this.dayState = dayState; - return this; - } - - - @Override - public JXMonthView getComponent() { - return (JXMonthView) super.getComponent(); - } - - public CalendarState getCalendarState() { - return dayState; - } - - public boolean isFlagged() { - if (getComponent() == null || calendar == null) { - return false; - } - return getComponent().isFlaggedDate(calendar.getTime()); - } - - public boolean isUnselectable() { - if (getComponent() == null || calendar == null || !isSelectable()) { - return false; - } - return getComponent().isUnselectableDate(calendar.getTime()); - } - - /** - * @param dayState - * @return - */ - private boolean isSelectable() { - return (CalendarState.IN_MONTH == getCalendarState()) || (CalendarState.TODAY == getCalendarState()); - } - - @Override - public boolean isSelected() { - if (getComponent() == null || calendar == null) { - return false; - } - return getComponent().isSelected(calendar.getTime()); - } - - - @Override - public Object getFilteredValueAt(int row, int column) { - return getValueAt(row, column); - } - - @Override - public Object getValueAt(int row, int column) { - return calendar; - } - - @Override - public boolean hasFocus() { - return false; - } - - @Override - public boolean isCellEditable(int row, int column) { - return false; - } - - @Override - public boolean isEditable() { - return false; - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/CalendarCellContext.java b/src/main/java/org/jdesktop/swingx/plaf/basic/CalendarCellContext.java deleted file mode 100644 index 0acaf32ec8..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/CalendarCellContext.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * $Id: CalendarCellContext.java 3286 2009-03-10 12:13:43Z kleopatra $ - * - * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf.basic; - -import org.jdesktop.swingx.JXMonthView; -import org.jdesktop.swingx.border.IconBorder; -import org.jdesktop.swingx.plaf.UIManagerExt; -import org.jdesktop.swingx.renderer.CellContext; - -import javax.swing.*; -import javax.swing.border.Border; -import java.awt.*; -import java.util.Calendar; - -/** - * MonthView specific CellContext. This is internally used by BasisMonthViewUI rendering. - * - * @author Jeanette Winzenburg - */ -class CalendarCellContext extends CellContext { - - /** - * The padding for month traversal icons. - * PENDING JW: decouple rendering and hit-detection. As is, these are - * hard-coded "magic numbers" which must be the same in both - * the ui-delegate (which does the hit-detection) and here (which - * returns the default title border) - * - * Added as preliminary fix for #1028-swingx: title border incorrect if box-padding 0 - */ - private int arrowPaddingX = 3; - private int arrowPaddingY = 3; - - private CalendarState dayState; - - public void installContext(JXMonthView component, Calendar value, - boolean selected, boolean focused, CalendarState dayState) { - this.component = component; - this.dayState = dayState; - installState(value, -1, -1, selected, focused, true, true); - } - - - @Override - public JXMonthView getComponent() { - return (JXMonthView) super.getComponent(); - } - - - public CalendarState getCalendarState() { - return dayState; - } - - - public Calendar getCalendar() { - return (getValue() instanceof Calendar) ? (Calendar) getValue() : null; - } - - @Override - protected Color getForeground() { - if (CalendarState.LEADING == dayState) { - return getUIColor("leadingDayForeground"); - } - if (CalendarState.TRAILING == dayState) { - return getUIColor("trailingDayForeground"); - } - if ((CalendarState.TITLE == dayState) && (getComponent() != null)) { - return getComponent().getMonthStringForeground(); - } - if (CalendarState.WEEK_OF_YEAR == dayState) { - Color weekOfTheYearForeground = getUIColor("weekOfTheYearForeground"); - if (weekOfTheYearForeground != null) { - return weekOfTheYearForeground; - } - } - if (CalendarState.DAY_OF_WEEK == dayState) { - Color daysOfTheWeekForeground = getComponent() != null - ? getComponent().getDaysOfTheWeekForeground() : null; - if (daysOfTheWeekForeground != null) { - return daysOfTheWeekForeground; - } - } - - Color flaggedOrPerDayForeground = getFlaggedOrPerDayForeground(); - return flaggedOrPerDayForeground != null ? flaggedOrPerDayForeground : super.getForeground(); - } - - /** - * @param key - * @return - */ - private Color getUIColor(String key) { - return UIManagerExt.getColor(getUIPrefix() + key); - } - - /** - * Returns the special color used for flagged days or per weekday or null if none is - * set, the component or the calendar are null. - * - * @return the special foreground color for flagged days or per dayOfWeek. - */ - protected Color getFlaggedOrPerDayForeground() { - - if (getComponent() != null && (getCalendar() != null)) { - if (getComponent().isFlaggedDate(getCalendar().getTime())) { - return getComponent().getFlaggedDayForeground(); - } else { - Color perDay = getComponent().getPerDayOfWeekForeground(getCalendar().get(Calendar.DAY_OF_WEEK)); - if (perDay != null) { - return perDay; - } - - } - } - return null; - } - - @Override - protected Color getBackground() { - if ((CalendarState.TITLE == dayState) && (getComponent() != null)) { - return getComponent().getMonthStringBackground(); - } - return super.getBackground(); - } - - @Override - protected Color getSelectionBackground() { - if (CalendarState.LEADING == dayState || CalendarState.TRAILING == dayState) return getBackground(); - return getComponent() != null ? getComponent().getSelectionBackground() : null; - } - - @Override - protected Color getSelectionForeground() { - if (CalendarState.LEADING == dayState || CalendarState.TRAILING == dayState) return getForeground(); - Color flaggedOrPerDayForeground = getFlaggedOrPerDayForeground(); - if (flaggedOrPerDayForeground != null) { - return flaggedOrPerDayForeground; - } - return getComponent() != null ? getComponent().getSelectionForeground() : null; - } - - - - @Override - protected Border getBorder() { - if (getComponent() == null) { - return super.getBorder(); - } - if (CalendarState.TITLE == dayState) { - return getTitleBorder(); - } - if (isToday()) { - int x = getComponent().getBoxPaddingX(); - int y = getComponent().getBoxPaddingY(); - Border todayBorder = BorderFactory.createLineBorder(getComponent().getTodayBackground()); - Border empty = BorderFactory.createEmptyBorder(y - 1, x - 1, y - 1, x -1); - return BorderFactory.createCompoundBorder(todayBorder, empty); - } - return BorderFactory.createEmptyBorder(getComponent().getBoxPaddingY(), getComponent().getBoxPaddingX(), getComponent().getBoxPaddingY(), getComponent().getBoxPaddingX()); - } - - /** - * @return - */ - private Border getTitleBorder() { - if (getComponent().isTraversable()) { - Icon downIcon = UIManager.getIcon("JXMonthView.monthDownFileName"); - Icon upIcon = UIManager.getIcon("JXMonthView.monthUpFileName"); - - // fix for #1028-swingx: title border whacky for boxpadding 0 - // in fact there had been a deeper issue - without using the arrowPadding here - // the hit-detection of the buttons is slightly off target - IconBorder up = new IconBorder(upIcon, SwingConstants.EAST, arrowPaddingX); - IconBorder down = new IconBorder(downIcon, SwingConstants.WEST, arrowPaddingX); - Border compound = BorderFactory.createCompoundBorder(up, down); - Border empty = BorderFactory.createEmptyBorder(2* arrowPaddingY, 0, 2*arrowPaddingY, 0); - return BorderFactory.createCompoundBorder(compound, empty); - } - - return BorderFactory.createEmptyBorder(getComponent().getBoxPaddingY(), getComponent().getBoxPaddingX(), getComponent().getBoxPaddingY(), getComponent().getBoxPaddingX()); - } - - /** - * @return - */ - protected boolean isToday() { - return CalendarState.TODAY == dayState; - } - - @Override - protected String getUIPrefix() { - return "JXMonthView."; - } - - -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/CalendarHeaderHandler.java b/src/main/java/org/jdesktop/swingx/plaf/basic/CalendarHeaderHandler.java deleted file mode 100644 index a6b0d6df35..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/CalendarHeaderHandler.java +++ /dev/null @@ -1,312 +0,0 @@ -/* - * $Id: CalendarHeaderHandler.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.plaf.basic; - -import org.jdesktop.swingx.JXMonthView; -import org.jdesktop.swingx.action.AbstractActionExt; - -import javax.swing.*; -import javax.swing.plaf.UIResource; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.logging.Logger; - -/** - * Provides and wires a component appropriate as a calendar navigation header. - * The design idea is to support a pluggable header for a zoomable (PENDING JW: - * naming!) JXMonthView. Then custom implementations can be tailored to exactly - * fit their needs. - *

                    - * - * To install a custom implementation, register the class name of the custom - * header handler with the key CalendarHeaderHandler.uiControllerID - * , example: - * - *

                    - * 
                    - *  UIManager.put(CalendarHeaderHandler.uiControllerID, "com.foo.bar.MagicHeaderHandler")
                    - * 
                    - * 
                    - * - * Basic navigation action should (will) be defined by the ui delegate itself (PENDING - * JW: still incomplete in BasicMonthViewUI). This handler can modify/enhance - * them as appropriate for its context. - *

                    - * - * PENDING JW: those icons ... who's responsible? Shouldn't we use any of the - * default arrows as defined in the laf anyway (are there any?) - *

                    - * - * Note: this is work-in-progress, be prepared to change if subclassing - * for custom requirements! - * - * @author Jeanette Winzenburg - */ -public abstract class CalendarHeaderHandler { - - @SuppressWarnings("unused") - private static final Logger LOG = Logger - .getLogger(CalendarHeaderHandler.class.getName()); - - public static final String uiControllerID = "CalendarHeaderHandler"; - - protected JXMonthView monthView; - - private JComponent calendarHeader; - - protected Icon monthDownImage; - - protected Icon monthUpImage; - - private PropertyChangeListener monthViewPropertyChangeListener; - - /** - * Installs this handler to the given month view. - * - * @param monthView the target month view to install to. - */ - public void install(JXMonthView monthView) { - this.monthView = monthView; - // PENDING JW: remove here if rendererHandler takes over control - // completely - // as is, some properties are duplicated - monthDownImage = UIManager.getIcon("JXMonthView.monthDownFileName"); - monthUpImage = UIManager.getIcon("JXMonthView.monthUpFileName"); - installNavigationActions(); - installListeners(); - componentOrientationChanged(); - monthStringBackgroundChanged(); - fontChanged(); - } - - /** - * Uninstalls this handler from the given target month view. - * - * @param monthView the target month view to install from. - */ - public void uninstall(JXMonthView monthView) { - this.monthView.remove(getHeaderComponent()); - uninstallListeners(); - this.monthView = null; - } - - /** - * Returns a component to be used as header in a zoomable month view, - * guaranteed to be not null. - * - * @return a component to be used as header in a zoomable JXMonthView - */ - public JComponent getHeaderComponent() { - if (calendarHeader == null) { - calendarHeader = createCalendarHeader(); - } - return calendarHeader; - } - - /** - * Creates and registered listeners on the monthView as appropriate. This - * implementation registers a PropertyChangeListener which synchronizes - * internal state on changes of componentOrientation, font and - * monthStringBackground. - */ - protected void installListeners() { - monthView - .addPropertyChangeListener(getMonthViewPropertyChangeListener()); - } - - /** - * Unregisters listeners which had been installed to the monthView. - */ - protected void uninstallListeners() { - monthView.removePropertyChangeListener(monthViewPropertyChangeListener); - } - - /** - * Returns the propertyChangelistener for the monthView. Lazily created. - * - * @return the propertyChangeListener for the monthView. - */ - private PropertyChangeListener getMonthViewPropertyChangeListener() { - if (monthViewPropertyChangeListener == null) { - monthViewPropertyChangeListener = new PropertyChangeListener() { - - @Override - public void propertyChange(PropertyChangeEvent evt) { - if ("componentOrientation".equals(evt.getPropertyName())) { - componentOrientationChanged(); - } else if ("font".equals(evt.getPropertyName())) { - fontChanged(); - } else if ("monthStringBackground".equals(evt - .getPropertyName())) { - monthStringBackgroundChanged(); - } - - } - }; - } - return monthViewPropertyChangeListener; - } - - /** - * Synchronizes internal state which depends on the month view's - * monthStringBackground. - */ - protected void monthStringBackgroundChanged() { - getHeaderComponent().setBackground( - getAsNotUIResource(monthView.getMonthStringBackground())); - - } - - /** - * Synchronizes internal state which depends on the month view's font. - */ - protected void fontChanged() { - getHeaderComponent().setFont(getAsNotUIResource(createDerivedFont())); - monthView.revalidate(); - } - - /** - * Synchronizes internal state which depends on the month view's - * componentOrientation. - * - * This implementation updates the month navigation icons and the header - * component's orientation. - */ - protected void componentOrientationChanged() { - getHeaderComponent().applyComponentOrientation( - monthView.getComponentOrientation()); - if (monthView.getComponentOrientation().isLeftToRight()) { - updateMonthNavigationIcons(monthDownImage, monthUpImage); - } else { - updateMonthNavigationIcons(monthUpImage, monthDownImage); - } - } - - /** - * @param previous the icon to use in the previousMonth action - * @param next the icon to use on the nextMonth action - */ - private void updateMonthNavigationIcons(Icon previous, Icon next) { - updateActionIcon("previousMonth", previous); - updateActionIcon("nextMonth", next); - } - - /** - * @param previousKey - * @param previous - */ - private void updateActionIcon(String previousKey, Icon previous) { - Action action = monthView.getActionMap().get(previousKey); - if (action != null) { - action.putValue(Action.SMALL_ICON, previous); - } - } - - /** - * Creates and returns the component used as header in a zoomable monthView. - * - * @return the component used as header in a zoomable monthView, guaranteed - * to be not null. - */ - protected abstract JComponent createCalendarHeader(); - - /** - * Installs and configures navigational actions. - *

                    - * - * This implementation creates and installs wrappers around the - * scrollToPrevious/-NextMonth actions installed by the ui and configures - * them with the appropriate next/previous icons. - */ - protected void installNavigationActions() { - installWrapper("scrollToPreviousMonth", "previousMonth", monthView - .getComponentOrientation().isLeftToRight() ? monthDownImage - : monthUpImage); - installWrapper("scrollToNextMonth", "nextMonth", monthView - .getComponentOrientation().isLeftToRight() ? monthUpImage - : monthDownImage); - } - - /** - * Creates an life action wrapper around the action registered with - * actionKey, sets its SMALL_ICON property to the given icon and installs - * itself with the newActionKey. - * - * @param actionKey the key of the action to wrap around - * @param newActionKey the key of the wrapper action - * @param icon the icon to use in the wrapper action - */ - private void installWrapper(final String actionKey, String newActionKey, - Icon icon) { - AbstractActionExt wrapper = new AbstractActionExt(null, icon) { - - @Override - public void actionPerformed(ActionEvent e) { - Action action = monthView.getActionMap().get(actionKey); - if (action != null) { - action.actionPerformed(e); - } - } - - }; - monthView.getActionMap().put(newActionKey, wrapper); - } - - /** - * Returns a Font based on the param which is not of type UIResource. - * - * @param font the base font - * @return a font not of type UIResource, may be null. - */ - private Font getAsNotUIResource(Font font) { - if (!(font instanceof UIResource)) - return font; - // PENDING JW: correct way to create another font instance? - return font.deriveFont(font.getAttributes()); - } - - /** - * Returns a Color based on the param which is not of type UIResource. - * - * @param color the base color - * @return a color not of type UIResource, may be null. - */ - private Color getAsNotUIResource(Color color) { - if (!(color instanceof UIResource)) - return color; - // PENDING JW: correct way to create another color instance? - float[] rgb = color.getRGBComponents(null); - return new Color(rgb[0], rgb[1], rgb[2], rgb[3]); - } - - /** - * Create a derived font used to when painting various pieces of the month - * view component. This method will be called whenever the font on the - * component is set so a new derived font can be created. - */ - protected Font createDerivedFont() { - return monthView.getFont().deriveFont(Font.BOLD); - } - -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/CalendarRenderingHandler.java b/src/main/java/org/jdesktop/swingx/plaf/basic/CalendarRenderingHandler.java deleted file mode 100644 index e706d5f9f1..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/CalendarRenderingHandler.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * $Id: CalendarRenderingHandler.java 3166 2009-01-02 13:27:18Z rah003 $ - * - * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf.basic; - -import org.jdesktop.swingx.JXMonthView; - -import javax.swing.*; -import java.util.Calendar; -import java.util.Locale; - -/** - * The RenderingHandler responsible for text rendering. It provides - * and configures a rendering component for the given cell of - * a JXMonthView.

                    - * - * @author Jeanette Winzenburg - */ -public interface CalendarRenderingHandler { - - /** - * Configures and returns a component for rendering of the given monthView cell. - * - * @param monthView the JXMonthView to render onto - * @param calendar the cell value - * @param state the DayState of the cell - * @return a component configured for rendering the given cell - */ - public JComponent prepareRenderingComponent(JXMonthView monthView, - Calendar calendar, CalendarState state); - - /** - * Updates internal state to the given Locale. - * - * PENDING JW: ideally, the handler should be stateless and this method - * removed. Currently needed because there is no way to get the Locale - * from a Calendar. - * - * @param locale the new Locale. - */ - public void setLocale(Locale locale); - -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/CalendarState.java b/src/main/java/org/jdesktop/swingx/plaf/basic/CalendarState.java deleted file mode 100644 index 41747edad4..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/CalendarState.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * $Id: CalendarState.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf.basic; - -/** - * States of a Day in a MonthView page. - * - * @author Jeanette Winzenburg - */ -public enum CalendarState { - TODAY, - IN_MONTH, - LEADING, - TRAILING, - WEEK_OF_YEAR, - DAY_OF_WEEK, TITLE -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/CapsLockSupport.java b/src/main/java/org/jdesktop/swingx/plaf/basic/CapsLockSupport.java deleted file mode 100644 index d8b4802140..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/CapsLockSupport.java +++ /dev/null @@ -1,142 +0,0 @@ -package org.jdesktop.swingx.plaf.basic; - -import org.jdesktop.beans.AbstractBean; - -import java.awt.*; -import java.awt.event.KeyEvent; - -import static java.awt.event.KeyEvent.VK_CAPS_LOCK; - -/** - * A class for determining the state of the {@link KeyEvent.VK_CAPS_LOCK CAPS LOCK - * key}. It also supports notification when the locking state changes. - *

                    - * Although it is possible to use {@link Toolkit#getLockingKeyState(int)} to determine the current - * state of the CAPS LOCK key, that method is not guaranteed to work on all platforms. This class - * attempts to handle those shortfalls and provide an easy mechanism for listening to state changes. - * - *

                    - * CapsLockSupport cls = CapsLockSupport.getInstance();
                    - * // for get the current state of the caps lock key
                    - * boolean currentState = cls.isCapsLockEnabled();
                    - * // for listening to changes in the caps lock state
                    - * cls.addPropertyChangeListener("capsLockEnabled", myListener);
                    - * 
                    - * - * There is one special case to be aware of. If {@code CapsLockSupport} is not able to determine the - * state of the CAPS LOCK key, then {@link #isInitialized()} will return {@code false} until it is - * able to introspect a {@link KeyEvent} and determine the current locking state. If - * {@code CapsLockSupport} must use delayed initialization, it will fire a property change to notify - * listeners that it is now in an accurate state. - * - * @author kschaefer - */ -public final class CapsLockSupport extends AbstractBean implements KeyEventDispatcher { - private boolean useToolkit; - private boolean capsLockeEnabled; - private boolean updateViaKeyEvent; - - private static class SingletonHolder { - private static final CapsLockSupport INSTANCE = new CapsLockSupport(); - - static { - KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(INSTANCE); - } - } - - private CapsLockSupport() { - try { - capsLockeEnabled = Toolkit.getDefaultToolkit().getLockingKeyState(VK_CAPS_LOCK); - useToolkit = true; - updateViaKeyEvent = false; - } catch (UnsupportedOperationException e) { - capsLockeEnabled = false; - useToolkit = false; - updateViaKeyEvent = true; - } - } - - /** - * Gets the only instance of {@code CapsLockSupport}. - * - * @return the {@code CapsLockSupport} instance - */ - public static CapsLockSupport getInstance() { - return SingletonHolder.INSTANCE; - } - - /** - * Determines if {@code CapsLockSupport} is accurately synchronized with the state of the CAPS - * LOCK key. When not initialized, {@link #isCapsLockEnabled()} will always return {@code false} - * . {@code CapsLockSupport} will fail to initialize only if - * {@code Toolkit#getLockingKeyState(int)} throws an exception; in that case, it will initialize - * as soon as it receives a valid key event (that can be used to determine the current locking - * state). - * - * @return {@code true} if {@code CapsLockSupport} accurately knows the state of the CAPS LOCK - * key - */ - public boolean isInitialized() { - return useToolkit || (useToolkit ^ updateViaKeyEvent); - } - - /** - * Determines the current state of the {@link KeyEvent.VK_CAPS_LOCK CAPS LOCK key}. - * - * @return {@code true} if CAPS LOCK is enabled; {@code false} otherwise - */ - public boolean isCapsLockEnabled() { - if (useToolkit) { - try { - return Toolkit.getDefaultToolkit().getLockingKeyState(VK_CAPS_LOCK); - } catch (UnsupportedOperationException shouldNeverHappen) { - return capsLockeEnabled; - } - } - - return capsLockeEnabled; - } - - void setCapsLockEnabled(boolean capsLockEnabled) { - boolean oldValue = this.capsLockeEnabled; - this.capsLockeEnabled = capsLockEnabled; - firePropertyChange("capsLockEnabled", oldValue, this.capsLockeEnabled); //$NON-NLS-1$ - } - - // updateViaKeyEvent is use to find the initial state of the CAPS LOCK key when the Toolkit does - // not support it - /** - * This is an implementation detail and should not be considered public. - */ - @Override - public boolean dispatchKeyEvent(KeyEvent e) { - if (e.getID() == KeyEvent.KEY_PRESSED) { - int keyCode = e.getKeyCode(); - - if (keyCode == VK_CAPS_LOCK) { - if (!updateViaKeyEvent) { - if (useToolkit) { - try { - setCapsLockEnabled(Toolkit.getDefaultToolkit().getLockingKeyState(VK_CAPS_LOCK)); - } catch (UnsupportedOperationException shouldNeverHappen) { - setCapsLockEnabled(!capsLockeEnabled); - } - } else { - setCapsLockEnabled(!capsLockeEnabled); - } - } - } else if (updateViaKeyEvent && Character.isLetter(keyCode)) { - if (keyCode == e.getKeyChar()) { - capsLockeEnabled = !e.isShiftDown(); - } else { - capsLockeEnabled = e.isShiftDown(); - } - - updateViaKeyEvent = false; - firePropertyChange("initialized", false, true); //$NON-NLS-1$ - } - } - - return false; - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/SpinningCalendarHeaderHandler.java b/src/main/java/org/jdesktop/swingx/plaf/basic/SpinningCalendarHeaderHandler.java deleted file mode 100644 index 02f3818781..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/SpinningCalendarHeaderHandler.java +++ /dev/null @@ -1,512 +0,0 @@ -package org.jdesktop.swingx.plaf.basic; - -import org.jdesktop.swingx.JXHyperlink; -import org.jdesktop.swingx.JXMonthView; -import org.jdesktop.swingx.JXPanel; -import org.jdesktop.swingx.renderer.FormatStringValue; - -import javax.swing.*; -import javax.swing.JSpinner.DefaultEditor; -import javax.swing.JSpinner.NumberEditor; -import java.awt.*; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.logging.Logger; - -/** - * Custom CalendarHeaderHandler which supports year-wise navigation. - *

                    - * - * The custom component used as header component of this implementation contains - * month-navigation buttons, a label with localized month text and a spinner for - * .. well ... spinning the years. There is minimal configuration control via - * the UIManager: - * - *

                      - *
                    • control the position of the nextMonth button: the default is at the - * trailing edge of the header. Option is to insert it directly after the month - * text, to enable set a Boolean.TRUE as value for key - * ARROWS_SURROUNDS_MONTH. - *
                    • control the focusability of the spinner's text field: the default is - * false. To enable set a Boolean.TRUE as value for key - * FOCUSABLE_SPINNER_TEXT. - *
                    - * - * Note: this header is not used by default. To make it the - * per-application default register it with the UIManager, like - * - *
                    
                    - * UIManager.put(CalendarHeaderHandler.uiControllerID, 
                    - *      "org.jdesktop.swingx.plaf.basic.SpinningCalendarHeaderHandler");
                    - * 
                    - * 
                    - * - * PENDING JW: implement and bind actions for keyboard navigation. These are - * potentially different from navigation by mouse: need to move the selection - * along with the scrolling? - * - */ -public class SpinningCalendarHeaderHandler extends CalendarHeaderHandler { - - /** - * Key for use in UIManager to control the position of the nextMonth arrow. - */ - public static final String ARROWS_SURROUND_MONTH = "SpinningCalendarHeader.arrowsSurroundMonth"; - - /** - * Key for use in UIManager to control the focusable property of the year - * spinner. - */ - public static final String FOCUSABLE_SPINNER_TEXT = "SpinningCalendarHeader.focusableSpinnerText"; - - @SuppressWarnings("unused") - private static final Logger LOG = Logger - .getLogger(SpinningCalendarHeaderHandler.class.getName()); - - /** the spinner model for year-wise navigation. */ - private SpinnerModel yearSpinnerModel; - - /** listener for property changes of the JXMonthView. */ - private PropertyChangeListener monthPropertyListener; - - /** converter for month text. */ - private FormatStringValue monthStringValue; - - // ----------------- public/protected overrides to manage custom - // creation/config - - /** - * {@inheritDoc} - *

                    - * - * Overridden to configure header specifics component after calling super. - */ - @Override - public void install(JXMonthView monthView) { - super.install(monthView); - getHeaderComponent().setActions( - monthView.getActionMap().get("previousMonth"), - monthView.getActionMap().get("nextMonth"), - getYearSpinnerModel()); - componentOrientationChanged(); - monthStringBackgroundChanged(); - fontChanged(); - localeChanged(); - } - - /** - * {@inheritDoc} - *

                    - * - * Overridden to cleanup the specifics before calling super. - */ - @Override - public void uninstall(JXMonthView monthView) { - getHeaderComponent().setActions(null, null, null); - getHeaderComponent().setMonthText(""); - super.uninstall(monthView); - } - - /** - * {@inheritDoc} - *

                    - * - * Convenience override to the type created. - */ - @Override - public SpinningCalendarHeader getHeaderComponent() { - return (SpinningCalendarHeader) super.getHeaderComponent(); - } - - /** - * {@inheritDoc} - *

                    - * - * Implemented to create and configure the custom header component. - */ - @Override - protected SpinningCalendarHeader createCalendarHeader() { - SpinningCalendarHeader header = new SpinningCalendarHeader(); - if (Boolean.TRUE.equals(UIManager.getBoolean(FOCUSABLE_SPINNER_TEXT))) { - header.setSpinnerFocusable(true); - } - if (Boolean.TRUE.equals(UIManager.getBoolean(ARROWS_SURROUND_MONTH))) { - header.setArrowsSurroundMonth(true); - } - return header; - } - - /** - * {@inheritDoc} - *

                    - */ - @Override - protected void installListeners() { - super.installListeners(); - monthView.addPropertyChangeListener(getPropertyChangeListener()); - } - - /** - * {@inheritDoc} - *

                    - */ - @Override - protected void uninstallListeners() { - monthView.removePropertyChangeListener(getPropertyChangeListener()); - super.uninstallListeners(); - } - - // ---------------- listening/update triggered by changes of the JXMonthView - - /** - * Updates the formatter of the month text to the JXMonthView's Locale. - */ - protected void updateFormatters() { - SimpleDateFormat monthNameFormat = (SimpleDateFormat) DateFormat - .getDateInstance(DateFormat.SHORT, monthView.getLocale()); - monthNameFormat.applyPattern("MMMM"); - monthStringValue = new FormatStringValue(monthNameFormat); - } - - /** - * Updates internal state to monthView's firstDisplayedDay. - */ - protected void firstDisplayedDayChanged() { - ((YearSpinnerModel) getYearSpinnerModel()).fireStateChanged(); - getHeaderComponent().setMonthText( - monthStringValue.getString(monthView.getFirstDisplayedDay())); - } - - /** - * Updates internal state to monthView's locale. - */ - protected void localeChanged() { - updateFormatters(); - firstDisplayedDayChanged(); - } - - /** - * Returns the property change listener for use on the monthView. This is - * lazyly created if not yet done. This implementation listens to changes of - * firstDisplayedDay and locale property and updates internal state - * accordingly. - * - * @return the property change listener for the monthView, never null. - */ - private PropertyChangeListener getPropertyChangeListener() { - if (monthPropertyListener == null) { - monthPropertyListener = new PropertyChangeListener() { - - @Override - public void propertyChange(PropertyChangeEvent evt) { - if ("firstDisplayedDay".equals(evt.getPropertyName())) { - firstDisplayedDayChanged(); - } else if ("locale".equals(evt.getPropertyName())) { - localeChanged(); - } - - } - - }; - } - return monthPropertyListener; - } - - // ---------------------- methods to back to Spinner model - - /** - * Returns the current year of the monthView. Callback for spinner model. - * - * return the current year of the monthView. - */ - private int getYear() { - Calendar cal = monthView.getCalendar(); - return cal.get(Calendar.YEAR); - } - - /** - * Returns the previous year of the monthView. Callback for spinner model. - *

                    - * - * PENDING JW: check against lower bound. - * - * return the previous year of the monthView. - */ - private int getPreviousYear() { - Calendar cal = monthView.getCalendar(); - cal.add(Calendar.YEAR, -1); - return cal.get(Calendar.YEAR); - } - - /** - * Returns the next year of the monthView. Callback for spinner model. - *

                    - * - * PENDING JW: check against upper bound. - * - * return the next year of the monthView. - */ - private int getNextYear() { - Calendar cal = monthView.getCalendar(); - cal.add(Calendar.YEAR, 1); - return cal.get(Calendar.YEAR); - } - - /** - * Sets the current year of the monthView to the given value. Callback for - * spinner model. - * - * @param value the new value of the year. - * @return a boolean indicating if a change actually happened. - */ - private boolean setYear(Object value) { - int year = ((Integer) value).intValue(); - Calendar cal = monthView.getCalendar(); - if (cal.get(Calendar.YEAR) == year) - return false; - cal.set(Calendar.YEAR, year); - monthView.setFirstDisplayedDay(cal.getTime()); - return true; - } - - /** - * Thin-layer implementation of a SpinnerModel which is actually backed by - * this controller. - */ - private class YearSpinnerModel extends AbstractSpinnerModel { - - @Override - public Object getNextValue() { - return getNextYear(); - } - - @Override - public Object getPreviousValue() { - return getPreviousYear(); - } - - @Override - public Object getValue() { - return getYear(); - } - - @Override - public void setValue(Object value) { - if (setYear(value)) { - fireStateChanged(); - } - } - - @Override - public void fireStateChanged() { - super.fireStateChanged(); - } - - } - - private SpinnerModel getYearSpinnerModel() { - if (yearSpinnerModel == null) { - yearSpinnerModel = new YearSpinnerModel(); - } - return yearSpinnerModel; - } - - /** - * The custom header component controlled and configured by this handler. - * - */ - protected static class SpinningCalendarHeader extends JXPanel { - private AbstractButton prevButton; - - private AbstractButton nextButton; - - private JLabel monthText; - - private JSpinner yearSpinner; - - private boolean surroundMonth; - - public SpinningCalendarHeader() { - initComponents(); - } - - /** - * Installs the actions and models to be used by this component. - * - * @param prev the action to use for the previous button - * @param next the action to use for the next button - * @param model the spinner model to use for the spinner. - */ - public void setActions(Action prev, Action next, SpinnerModel model) { - prevButton.setAction(prev); - nextButton.setAction(next); - uninstallZoomAction(); - installZoomAction(model); - } - - /** - * Sets the focusable property of the spinner's editor's text field. - * - * The default value is false. - * - * @param focusable the focusable property of the spinner's editor. - */ - public void setSpinnerFocusable(boolean focusable) { - ((DefaultEditor) yearSpinner.getEditor()).getTextField() - .setFocusable(focusable); - } - - /** - * The default value is false. - * - * @param surroundMonth - */ - public void setArrowsSurroundMonth(boolean surroundMonth) { - if (this.surroundMonth == surroundMonth) - return; - this.surroundMonth = surroundMonth; - removeAll(); - addComponents(); - } - - /** - * Sets the text to use for the month label. - * - * @param text the text to use for the month label. - */ - public void setMonthText(String text) { - monthText.setText(text); - } - - /** - * {@inheritDoc} - *

                    - * - * Overridden to set the font of its child components. - */ - @Override - public void setFont(Font font) { - super.setFont(font); - if (monthText != null) { - monthText.setFont(font); - yearSpinner.setFont(font); - yearSpinner.getEditor().setFont(font); - ((DefaultEditor) yearSpinner.getEditor()).getTextField() - .setFont(font); - } - } - - /** - * {@inheritDoc} - *

                    - * - * Overridden to set the background of its child compenents. - */ - @Override - public void setBackground(Color bg) { - super.setBackground(bg); - for (int i = 0; i < getComponentCount(); i++) { - getComponent(i).setBackground(bg); - } - if (yearSpinner != null) { - yearSpinner.setBackground(bg); - yearSpinner.setBorder(BorderFactory.createLineBorder(bg, 2)); - yearSpinner.getEditor().setBackground(bg); - ((DefaultEditor) yearSpinner.getEditor()).getTextField() - .setBackground(bg); - } - } - - private void installZoomAction(SpinnerModel model) { - if (model == null) - return; - yearSpinner.setModel(model); - } - - private void uninstallZoomAction() { - } - - private void initComponents() { - createComponents(); - setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS)); - setBorder(BorderFactory.createEmptyBorder(2, 4, 2, 4)); - addComponents(); - } - - /** - * - */ - private void addComponents() { - if (surroundMonth) { - add(prevButton); - add(monthText); - add(nextButton); - add(Box.createHorizontalStrut(5)); - add(yearSpinner); - } else { - add(prevButton); - add(Box.createHorizontalGlue()); - add(monthText); - add(Box.createHorizontalStrut(5)); - add(yearSpinner); - add(Box.createHorizontalGlue()); - add(nextButton); - } - } - - /** - * - */ - private void createComponents() { - prevButton = createNavigationButton(); - nextButton = createNavigationButton(); - monthText = createMonthText(); - yearSpinner = createSpinner(); - } - - private JLabel createMonthText() { - JLabel comp = new JLabel() { - - @Override - public Dimension getMaximumSize() { - Dimension dim = super.getMaximumSize(); - dim.width = Integer.MAX_VALUE; - dim.height = Integer.MAX_VALUE; - return dim; - } - - }; - comp.setHorizontalAlignment(JLabel.CENTER); - return comp; - } - - /** - * Creates and returns the JSpinner used for year navigation. - * - * @return - */ - private JSpinner createSpinner() { - JSpinner spinner = new JSpinner(); - spinner.setFocusable(false); - NumberEditor editor = new NumberEditor(spinner); - editor.getFormat().setGroupingUsed(false); - editor.getTextField().setFocusable(false); - spinner.setEditor(editor); - return spinner; - } - - private AbstractButton createNavigationButton() { - JXHyperlink b = new JXHyperlink(); - b.setContentAreaFilled(false); - b.setBorder(BorderFactory.createEmptyBorder()); - b.setRolloverEnabled(true); - b.setFocusable(false); - return b; - } - - } - -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/TextCrossingPainter.java b/src/main/java/org/jdesktop/swingx/plaf/basic/TextCrossingPainter.java deleted file mode 100644 index f64b7d8923..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/TextCrossingPainter.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * $Id: TextCrossingPainter.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf.basic; - -import org.jdesktop.swingx.painter.AbstractPainter; - -import javax.swing.*; -import java.awt.*; - -/** - * Painter used to cross-out unselectable dates. - * - * PENDING JW: subclass (or maybe even use?) one of the painter subclasses. - * - * @author Jeanette Winzenburg - */ -class TextCrossingPainter extends AbstractPainter { - Rectangle paintIconR = new Rectangle(); - Rectangle paintViewR = new Rectangle(); - Rectangle paintTextR = new Rectangle(); - Insets insetss = new Insets(0, 0, 0, 0); - Color crossColor; - /** - * {@inheritDoc}

                    - * - * Paints a diagonal cross over the text if the comp is of type JLabel, - * does nothing otherwise. - */ - @Override - protected void doPaint(Graphics2D g, JComponent comp, int width, - int height) { - if (!(comp instanceof JLabel)) return; - JLabel label = (JLabel) comp; - Insets insets = label.getInsets(insetss); - 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; - SwingUtilities.layoutCompoundLabel(label, - label.getFontMetrics(label.getFont()), label.getText(), null, - label.getVerticalAlignment(), label.getHorizontalAlignment(), - label.getVerticalTextPosition(), label.getHorizontalTextPosition(), - paintViewR, paintIconR, paintTextR, label.getIconTextGap()); - doPaint(g, paintTextR); - } - - private void doPaint(Graphics2D g, Rectangle r) { - Color old = g.getColor(); - g.setColor(getForeground()); - g.drawLine(r.x, r.y, r.x + r.width, r.y + r.height); - g.drawLine(r.x + 1, r.y, r.x + r.width + 1, r.y + r.height); - g.drawLine(r.x + r.width, r.y, r.x, r.y + r.height); - g.drawLine(r.x + r.width - 1, r.y, r.x - 1, r.y + r.height); - g.setColor(old); - - } - - /** - * - * @param crossColor the color to paint the cross with - */ - public void setForeground(Color crossColor) { - Color old = getForeground(); - this.crossColor = crossColor; - firePropertyChange("foreground", old, getForeground()); - } - - /** - * Returns the color to use for painting the cross. - * - * @return the color used for painting. - */ - public Color getForeground() { - return crossColor; - } -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/core/BasicTransferable.java b/src/main/java/org/jdesktop/swingx/plaf/basic/core/BasicTransferable.java deleted file mode 100644 index d63282f0b2..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/core/BasicTransferable.java +++ /dev/null @@ -1,283 +0,0 @@ -/* - * $Id$ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.plaf.basic.core; - - -import javax.swing.plaf.UIResource; -import java.awt.datatransfer.DataFlavor; -import java.awt.datatransfer.Transferable; -import java.awt.datatransfer.UnsupportedFlavorException; -import java.io.*; - -/** - * A transferable implementation for the default data transfer of some Swing - * components. - * - * @author Timothy Prinzing - * @version 1.10 11/17/05 - */ -public class BasicTransferable implements Transferable, UIResource { - - protected String plainData; - protected String htmlData; - - private static DataFlavor[] htmlFlavors; - private static DataFlavor[] stringFlavors; - private static DataFlavor[] plainFlavors; - - static { - try { - htmlFlavors = new DataFlavor[3]; - htmlFlavors[0] = new DataFlavor("text/html;class=java.lang.String"); - htmlFlavors[1] = new DataFlavor("text/html;class=java.io.Reader"); - htmlFlavors[2] = new DataFlavor("text/html;charset=unicode;class=java.io.InputStream"); - - plainFlavors = new DataFlavor[3]; - plainFlavors[0] = new DataFlavor("text/plain;class=java.lang.String"); - plainFlavors[1] = new DataFlavor("text/plain;class=java.io.Reader"); - plainFlavors[2] = new DataFlavor("text/plain;charset=unicode;class=java.io.InputStream"); - - stringFlavors = new DataFlavor[2]; - stringFlavors[0] = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType+";class=java.lang.String"); - stringFlavors[1] = DataFlavor.stringFlavor; - - } catch (ClassNotFoundException cle) { - System.err.println("error initializing javax.swing.plaf.basic.BasicTranserable"); - } - } - - public BasicTransferable(String plainData, String htmlData) { - this.plainData = plainData; - this.htmlData = htmlData; - } - - - /** - * Returns an array of DataFlavor objects indicating the flavors the data - * can be provided in. The array should be ordered according to preference - * for providing the data (from most richly descriptive to least descriptive). - * @return an array of data flavors in which this data can be transferred - */ - public DataFlavor[] getTransferDataFlavors() { - DataFlavor[] richerFlavors = getRicherFlavors(); - int nRicher = (richerFlavors != null) ? richerFlavors.length : 0; - int nHTML = (isHTMLSupported()) ? htmlFlavors.length : 0; - int nPlain = (isPlainSupported()) ? plainFlavors.length: 0; - int nString = (isPlainSupported()) ? stringFlavors.length : 0; - int nFlavors = nRicher + nHTML + nPlain + nString; - DataFlavor[] flavors = new DataFlavor[nFlavors]; - - // fill in the array - int nDone = 0; - if (nRicher > 0) { - System.arraycopy(richerFlavors, 0, flavors, nDone, nRicher); - nDone += nRicher; - } - if (nHTML > 0) { - System.arraycopy(htmlFlavors, 0, flavors, nDone, nHTML); - nDone += nHTML; - } - if (nPlain > 0) { - System.arraycopy(plainFlavors, 0, flavors, nDone, nPlain); - nDone += nPlain; - } - if (nString > 0) { - System.arraycopy(stringFlavors, 0, flavors, nDone, nString); - nDone += nString; - } - return flavors; - } - - /** - * Returns whether or not the specified data flavor is supported for - * this object. - * @param flavor the requested flavor for the data - * @return boolean indicating whether or not the data flavor is supported - */ - public boolean isDataFlavorSupported(DataFlavor flavor) { - DataFlavor[] flavors = getTransferDataFlavors(); - for (int i = 0; i < flavors.length; i++) { - if (flavors[i].equals(flavor)) { - return true; - } - } - return false; - } - - /** - * Returns an object which represents the data to be transferred. The class - * of the object returned is defined by the representation class of the flavor. - * - * @param flavor the requested flavor for the data - * @see DataFlavor#getRepresentationClass - * @exception IOException if the data is no longer available - * in the requested flavor. - * @exception UnsupportedFlavorException if the requested data flavor is - * not supported. - */ - public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { - DataFlavor[] richerFlavors = getRicherFlavors(); - if (isRicherFlavor(flavor)) { - return getRicherData(flavor); - } else if (isHTMLFlavor(flavor)) { - String data = getHTMLData(); - data = (data == null) ? "" : data; - if (String.class.equals(flavor.getRepresentationClass())) { - return data; - } else if (Reader.class.equals(flavor.getRepresentationClass())) { - return new StringReader(data); - } else if (InputStream.class.equals(flavor.getRepresentationClass())) { - return new StringBufferInputStream(data); - } - // fall through to unsupported - } else if (isPlainFlavor(flavor)) { - String data = getPlainData(); - data = (data == null) ? "" : data; - if (String.class.equals(flavor.getRepresentationClass())) { - return data; - } else if (Reader.class.equals(flavor.getRepresentationClass())) { - return new StringReader(data); - } else if (InputStream.class.equals(flavor.getRepresentationClass())) { - return new StringBufferInputStream(data); - } - // fall through to unsupported - - } else if (isStringFlavor(flavor)) { - String data = getPlainData(); - data = (data == null) ? "" : data; - return data; - } - throw new UnsupportedFlavorException(flavor); - } - - // --- richer subclass flavors ---------------------------------------------- - - protected boolean isRicherFlavor(DataFlavor flavor) { - DataFlavor[] richerFlavors = getRicherFlavors(); - int nFlavors = (richerFlavors != null) ? richerFlavors.length : 0; - for (int i = 0; i < nFlavors; i++) { - if (richerFlavors[i].equals(flavor)) { - return true; - } - } - return false; - } - - /** - * Some subclasses will have flavors that are more descriptive than HTML - * or plain text. If this method returns a non-null value, it will be - * placed at the start of the array of supported flavors. - */ - protected DataFlavor[] getRicherFlavors() { - return null; - } - - protected Object getRicherData(DataFlavor flavor) throws UnsupportedFlavorException { - return null; - } - - // --- html flavors ---------------------------------------------------------- - - /** - * Returns whether or not the specified data flavor is an HTML flavor that - * is supported. - * @param flavor the requested flavor for the data - * @return boolean indicating whether or not the data flavor is supported - */ - protected boolean isHTMLFlavor(DataFlavor flavor) { - DataFlavor[] flavors = htmlFlavors; - for (int i = 0; i < flavors.length; i++) { - if (flavors[i].equals(flavor)) { - return true; - } - } - return false; - } - - /** - * Should the HTML flavors be offered? If so, the method - * getHTMLData should be implemented to provide something reasonable. - */ - protected boolean isHTMLSupported() { - return htmlData != null; - } - - /** - * Fetch the data in a text/html format - */ - protected String getHTMLData() { - return htmlData; - } - - // --- plain text flavors ---------------------------------------------------- - - /** - * Returns whether or not the specified data flavor is an plain flavor that - * is supported. - * @param flavor the requested flavor for the data - * @return boolean indicating whether or not the data flavor is supported - */ - protected boolean isPlainFlavor(DataFlavor flavor) { - DataFlavor[] flavors = plainFlavors; - for (int i = 0; i < flavors.length; i++) { - if (flavors[i].equals(flavor)) { - return true; - } - } - return false; - } - - /** - * Should the plain text flavors be offered? If so, the method - * getPlainData should be implemented to provide something reasonable. - */ - protected boolean isPlainSupported() { - return plainData != null; - } - - /** - * Fetch the data in a text/plain format. - */ - protected String getPlainData() { - return plainData; - } - - // --- string flavorss -------------------------------------------------------- - - /** - * Returns whether or not the specified data flavor is a String flavor that - * is supported. - * @param flavor the requested flavor for the data - * @return boolean indicating whether or not the data flavor is supported - */ - protected boolean isStringFlavor(DataFlavor flavor) { - DataFlavor[] flavors = stringFlavors; - for (int i = 0; i < flavors.length; i++) { - if (flavors[i].equals(flavor)) { - return true; - } - } - return false; - } - - -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/core/BasicXListUI.java b/src/main/java/org/jdesktop/swingx/plaf/basic/core/BasicXListUI.java deleted file mode 100644 index e3af7b9853..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/core/BasicXListUI.java +++ /dev/null @@ -1,3119 +0,0 @@ -/* - * $Id$ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.plaf.basic.core; - -import org.jdesktop.swingx.JXList; -import org.jdesktop.swingx.SwingXUtilities; -import org.jdesktop.swingx.plaf.LookAndFeelUtils; -import org.jdesktop.swingx.plaf.UIAction; -import org.jdesktop.swingx.plaf.basic.core.DragRecognitionSupport.BeforeDrag; - -import javax.swing.*; -import javax.swing.event.*; -import javax.swing.plaf.ComponentUI; -import javax.swing.plaf.UIResource; -import javax.swing.plaf.basic.BasicListUI; -import javax.swing.text.Position; -import java.awt.*; -import java.awt.datatransfer.Transferable; -import java.awt.event.*; -import java.awt.geom.Point2D; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; - -//import sun.swing.DefaultLookup; -//import sun.swing.SwingUtilities2; -//import sun.swing.UIAction; - -/** - * An extensible implementation of {@code ListUI} for JXList. - * {@code BasicXListUI} instances cannot be shared between multiple - * lists.

                    - * - * The heart of added functionality is to support sorting/filtering, that is keep - * model-selection and RowSorter state synchronized. The details are delegated to a ListSortUI, - * but this class is responsible to manage the sortUI on changes of list properties, model and - * view selection (same strategy as in JXTable).

                    - * - * Note: this delegate is mostly a 1:1 copy of BasicListUI. The difference is that - * it accesses the list elements and list elementCount exclusively through the - * JXList api. This allows a clean implementation of sorting/filtering.

                    - * - * The differences (goal was to touch as little code as possible as this needs - * to be updated on every change to core until that is changed to not access - * the list's model directly, sigh) for core functionality: - *

                      - *
                    • extracted method for list.getModel().getSize (for the delegate class and - * all contained static classes) and use that method exclusively - *
                    • similar for remaining list.getModel(): implemented wrapping listModel - * which messages the list - *
                    • rename key for shared actionMap to keep core list actions separate - * (just in case somebody wants both) - they point to the wrong delegate - *
                    • replaced references to SwingUtilities2 in sun packages by references to - * c&p'ed methods in SwingXUtilities - *
                    • replaced storage of shared Input/ActionMap in defaultLookup by direct - * storage in UIManager. - *
                    - * - * Differences to achieve extended functionality: - *
                      - *
                    • added methods to un/-installSortUI and call in un/installUI(component) - *
                    • changed PropertyChangeHandler to call a) hasHandledPropertyChange to - * allow this class to replace super handler functinality and - * b) updateSortUI after handling all not-sorter related properties. - *
                    • changed createPropertyChangeListener to return a PropertyChangeHandler - *
                    • changed ListDataHandler to check if event handled by SortUI and delegate - * to handler only if not - *
                    • changed createListDataListener to return a ListDataHandler - *
                    • changed ListSelectionHandler to check if event handled by SortUI and - * delegate to handler only if not - *
                    changed createListSelectionListener to return a ListSelectionHandler - * - * Differences for bug fixes (due to incorrectly extending super): - *
                      - *
                    • Issue #1495-swingx: getBaseline throughs NPE - *
                    - * - * Note: extension of core (instead of implement from scratch) is to keep - * external (?) code working which expects a ui delegate of type BasicSomething. - * LAF implementors with a custom ListUI extending BasicListUI should be able to - * add support for JXList by adding a separate CustomXListUI extending this, same - * as the default with parent changed. Beware: custom code must not - * call access the model directly or - if they insist - convert the row index to - * account for sorting/filtering! That's the whole point of this class. - * - * @version 1.127 12/02/08 - * @author Hans Muller - * @author Philip Milne - * @author Shannon Hickey (drag and drop) - */ -public class BasicXListUI extends BasicListUI -{ - private static final StringBuilder BASELINE_COMPONENT_KEY = - new StringBuilder("List.baselineComponent"); - - protected JXList list = null; - protected CellRendererPane rendererPane; - - // Listeners that this UI attaches to the JList - protected FocusListener focusListener; - protected MouseInputListener mouseInputListener; - protected ListSelectionListener listSelectionListener; - protected ListDataListener listDataListener; - protected PropertyChangeListener propertyChangeListener; - private Handler handler; - - protected int[] cellHeights = null; - protected int cellHeight = -1; - protected int cellWidth = -1; - protected int updateLayoutStateNeeded = modelChanged; - /** - * Height of the list. When asked to paint, if the current size of - * the list differs, this will update the layout state. - */ - private int listHeight; - - /** - * Width of the list. When asked to paint, if the current size of - * the list differs, this will update the layout state. - */ - private int listWidth; - - /** - * The layout orientation of the list. - */ - private int layoutOrientation; - - // Following ivars are used if the list is laying out horizontally - - /** - * Number of columns to create. - */ - private int columnCount; - /** - * Preferred height to make the list, this is only used if the - * the list is layed out horizontally. - */ - private int preferredHeight; - /** - * Number of rows per column. This is only used if the row height is - * fixed. - */ - private int rowsPerColumn; - - /** - * The time factor to treate the series of typed alphanumeric key - * as prefix for first letter navigation. - */ - private long timeFactor = 1000L; - - /** - * Local cache of JList's client property "List.isFileList" - */ - private boolean isFileList = false; - - /** - * Local cache of JList's component orientation property - */ - private boolean isLeftToRight = true; - - /* The bits below define JList property changes that affect layout. - * When one of these properties changes we set a bit in - * updateLayoutStateNeeded. The change is dealt with lazily, see - * maybeUpdateLayoutState. Changes to the JLists model, e.g. the - * models length changed, are handled similarly, see DataListener. - */ - - protected final static int modelChanged = 1 << 0; - protected final static int selectionModelChanged = 1 << 1; - protected final static int fontChanged = 1 << 2; - protected final static int fixedCellWidthChanged = 1 << 3; - protected final static int fixedCellHeightChanged = 1 << 4; - protected final static int prototypeCellValueChanged = 1 << 5; - protected final static int cellRendererChanged = 1 << 6; - private final static int layoutOrientationChanged = 1 << 7; - private final static int heightChanged = 1 << 8; - private final static int widthChanged = 1 << 9; - private final static int componentOrientationChanged = 1 << 10; - - private static final int DROP_LINE_THICKNESS = 2; - - // FIXME - JW LazyActionMap copy is in different package ... move here? - public static void loadActionMap(LazyActionMap map) { - map.put(new Actions(Actions.SELECT_PREVIOUS_COLUMN)); - map.put(new Actions(Actions.SELECT_PREVIOUS_COLUMN_EXTEND)); - map.put(new Actions(Actions.SELECT_PREVIOUS_COLUMN_CHANGE_LEAD)); - map.put(new Actions(Actions.SELECT_NEXT_COLUMN)); - map.put(new Actions(Actions.SELECT_NEXT_COLUMN_EXTEND)); - map.put(new Actions(Actions.SELECT_NEXT_COLUMN_CHANGE_LEAD)); - map.put(new Actions(Actions.SELECT_PREVIOUS_ROW)); - map.put(new Actions(Actions.SELECT_PREVIOUS_ROW_EXTEND)); - map.put(new Actions(Actions.SELECT_PREVIOUS_ROW_CHANGE_LEAD)); - map.put(new Actions(Actions.SELECT_NEXT_ROW)); - map.put(new Actions(Actions.SELECT_NEXT_ROW_EXTEND)); - map.put(new Actions(Actions.SELECT_NEXT_ROW_CHANGE_LEAD)); - map.put(new Actions(Actions.SELECT_FIRST_ROW)); - map.put(new Actions(Actions.SELECT_FIRST_ROW_EXTEND)); - map.put(new Actions(Actions.SELECT_FIRST_ROW_CHANGE_LEAD)); - map.put(new Actions(Actions.SELECT_LAST_ROW)); - map.put(new Actions(Actions.SELECT_LAST_ROW_EXTEND)); - map.put(new Actions(Actions.SELECT_LAST_ROW_CHANGE_LEAD)); - map.put(new Actions(Actions.SCROLL_UP)); - map.put(new Actions(Actions.SCROLL_UP_EXTEND)); - map.put(new Actions(Actions.SCROLL_UP_CHANGE_LEAD)); - map.put(new Actions(Actions.SCROLL_DOWN)); - map.put(new Actions(Actions.SCROLL_DOWN_EXTEND)); - map.put(new Actions(Actions.SCROLL_DOWN_CHANGE_LEAD)); - map.put(new Actions(Actions.SELECT_ALL)); - map.put(new Actions(Actions.CLEAR_SELECTION)); - map.put(new Actions(Actions.ADD_TO_SELECTION)); - map.put(new Actions(Actions.TOGGLE_AND_ANCHOR)); - map.put(new Actions(Actions.EXTEND_TO)); - map.put(new Actions(Actions.MOVE_SELECTION_TO)); - - map.put(TransferHandler.getCutAction().getValue(Action.NAME), - TransferHandler.getCutAction()); - map.put(TransferHandler.getCopyAction().getValue(Action.NAME), - TransferHandler.getCopyAction()); - map.put(TransferHandler.getPasteAction().getValue(Action.NAME), - TransferHandler.getPasteAction()); - } - -//-------------------- X-Wrapper - - private ListModel modelX; - - private ListSortUI sortUI; - /** - * Compatibility Wrapper: a synthetic model which delegates to list api and throws - * @return - */ - protected ListModel getViewModel() { - if (modelX == null) { - modelX = new ListModel() { - - @Override - public int getSize() { - return ((JXList) list).getElementCount(); - } - - @Override - public Object getElementAt(int index) { - return ((JXList) list).getElementAt(index); - } - - @Override - public void addListDataListener(ListDataListener l) { - throw new UnsupportedOperationException("this is a synthetic model wrapper"); - } - @Override - public void removeListDataListener(ListDataListener l) { - throw new UnsupportedOperationException("this is a synthetic model wrapper"); - - } - - }; - } - return modelX; - } - - /** - * @return - */ - protected int getElementCount() { - return ((JXList) list).getElementCount(); - } - - protected Object getElementAt(int viewIndex) { - return ((JXList) list).getElementAt(viewIndex); - } - - /** - * Fix for Issue #1495: NPE on getBaseline. - * - * As per contract, that methods needs to throw Exceptions on illegal - * parameters. As we by-pass super, need to do the check and throw - * ouerselves. - * - * @param c - * @param width - * @param height - * - * @throws IllegalArgumentException if width or height < 0 - * @throws NPE if c == null - */ - protected void checkBaselinePrecondition(JComponent c, int width, int height) { - if (c == null) { - throw new NullPointerException("Component must be non-null"); - } - if (width < 0 || height < 0) { - throw new IllegalArgumentException( - "Width and height must be >= 0"); - } - } - -//--------------- api to support/control sorting/filtering - - protected ListSortUI getSortUI() { - return sortUI; - } - - /** - * Installs SortUI if the list has a rowSorter. Does nothing if not. - */ - protected void installSortUI() { - if (list.getRowSorter() == null) return; - sortUI = new ListSortUI(list, list.getRowSorter()); - } - - /** - * Dispose and null's the sortUI if installed. Does nothing if not. - */ - protected void uninstallSortUI() { - if (sortUI == null) return; - sortUI.dispose(); - sortUI = null; - } - - /** - * Called from the PropertyChangeHandler. - * - * @param property the name of the changed property. - */ - protected void updateSortUI(String property) { - if ("rowSorter".equals(property)) { - updateSortUIToRowSorterProperty(); - } - } - /** - * - */ - private void updateSortUIToRowSorterProperty() { - uninstallSortUI(); - installSortUI(); - } - - /** - * Returns a boolean indicating whether or not the event has been processed - * by the sortUI. - * @param e - * @return - */ - protected boolean processedBySortUI(ListDataEvent e) { - if (sortUI == null) - return false; - sortUI.modelChanged(e); - updateLayoutStateNeeded = modelChanged; - redrawList(); - return true; - } - - /** - * Returns a boolean indicating whether or not the event has been processed - * by the sortUI. - * @param e - * @return - */ - protected boolean processedBySortUI(ListSelectionEvent e) { - if (sortUI == null) return false; - sortUI.viewSelectionChanged(e); - list.repaint(); - return true; - } - - -//--------------------- enhanced support - /** - * Invalidates the cell size cache and revalidates/-paints the list. - * - */ - public void invalidateCellSizeCache() { - updateLayoutStateNeeded |= 1; - redrawList(); - } - -//--------------------- core copy - - - /** - * Paint one List cell: compute the relevant state, get the "rubber stamp" - * cell renderer component, and then use the CellRendererPane to paint it. - * Subclasses may want to override this method rather than paint(). - * - * @see #paint - */ - protected void paintCell( - Graphics g, - int row, - Rectangle rowBounds, - ListCellRenderer cellRenderer, - ListModel dataModel, - ListSelectionModel selModel, - int leadIndex) - { - Object value = dataModel.getElementAt(row); - boolean cellHasFocus = list.hasFocus() && (row == leadIndex); - boolean isSelected = selModel.isSelectedIndex(row); - - Component rendererComponent = - cellRenderer.getListCellRendererComponent(list, value, row, isSelected, cellHasFocus); - - int cx = rowBounds.x; - int cy = rowBounds.y; - int cw = rowBounds.width; - int ch = rowBounds.height; - - if (isFileList) { - // Shrink renderer to preferred size. This is mostly used on Windows - // where selection is only shown around the file name, instead of - // across the whole list cell. - int w = Math.min(cw, rendererComponent.getPreferredSize().width + 4); - if (!isLeftToRight) { - cx += (cw - w); - } - cw = w; - } - - rendererPane.paintComponent(g, rendererComponent, list, cx, cy, cw, ch, true); - } - - - /** - * Paint the rows that intersect the Graphics objects clipRect. This - * method calls paintCell as necessary. Subclasses - * may want to override these methods. - * - * @see #paintCell - */ - public void paint(Graphics g, JComponent c) { - Shape clip = g.getClip(); - paintImpl(g, c); - g.setClip(clip); - - paintDropLine(g); - } - - private void paintImpl(Graphics g, JComponent c) - { - switch (layoutOrientation) { - case JList.VERTICAL_WRAP: - if (list.getHeight() != listHeight) { - updateLayoutStateNeeded |= heightChanged; - redrawList(); - } - break; - case JList.HORIZONTAL_WRAP: - if (list.getWidth() != listWidth) { - updateLayoutStateNeeded |= widthChanged; - redrawList(); - } - break; - default: - break; - } - maybeUpdateLayoutState(); - - ListCellRenderer renderer = list.getCellRenderer(); - ListModel dataModel = getViewModel(); - ListSelectionModel selModel = list.getSelectionModel(); - int size; - - if ((renderer == null) || (size = dataModel.getSize()) == 0) { - return; - } - - // Determine how many columns we need to paint - Rectangle paintBounds = g.getClipBounds(); - - int startColumn, endColumn; - if (c.getComponentOrientation().isLeftToRight()) { - startColumn = convertLocationToColumn(paintBounds.x, - paintBounds.y); - endColumn = convertLocationToColumn(paintBounds.x + - paintBounds.width, - paintBounds.y); - } else { - startColumn = convertLocationToColumn(paintBounds.x + - paintBounds.width, - paintBounds.y); - endColumn = convertLocationToColumn(paintBounds.x, - paintBounds.y); - } - int maxY = paintBounds.y + paintBounds.height; - int leadIndex = adjustIndex(list.getLeadSelectionIndex(), list); - int rowIncrement = (layoutOrientation == JList.HORIZONTAL_WRAP) ? - columnCount : 1; - - - for (int colCounter = startColumn; colCounter <= endColumn; - colCounter++) { - // And then how many rows in this columnn - int row = convertLocationToRowInColumn(paintBounds.y, colCounter); - int rowCount = getRowCount(colCounter); - int index = getModelIndex(colCounter, row); - Rectangle rowBounds = getCellBounds(list, index, index); - - if (rowBounds == null) { - // Not valid, bail! - return; - } - while (row < rowCount && rowBounds.y < maxY && - index < size) { - rowBounds.height = getHeight(colCounter, row); - g.setClip(rowBounds.x, rowBounds.y, rowBounds.width, - rowBounds.height); - g.clipRect(paintBounds.x, paintBounds.y, paintBounds.width, - paintBounds.height); - paintCell(g, index, rowBounds, renderer, dataModel, selModel, - leadIndex); - rowBounds.y += rowBounds.height; - index += rowIncrement; - row++; - } - } - // Empty out the renderer pane, allowing renderers to be gc'ed. - rendererPane.removeAll(); - } - - - - private void paintDropLine(Graphics g) { - JList.DropLocation loc = list.getDropLocation(); - if (loc == null || !loc.isInsert()) { - return; - } - // PENDING JW: revisit ... side-effects? -// Color c = DefaultLookup.getColor(list, this, "List.dropLineColor", null); - Color c = UIManager.getColor("List.dropLineColor"); - if (c != null) { - g.setColor(c); - Rectangle rect = getDropLineRect(loc); - g.fillRect(rect.x, rect.y, rect.width, rect.height); - } - } - - private Rectangle getDropLineRect(JList.DropLocation loc) { - int size = getElementCount(); - - if (size == 0) { - Insets insets = list.getInsets(); - if (layoutOrientation == JList.HORIZONTAL_WRAP) { - if (isLeftToRight) { - return new Rectangle(insets.left, insets.top, DROP_LINE_THICKNESS, 20); - } else { - return new Rectangle(list.getWidth() - DROP_LINE_THICKNESS - insets.right, - insets.top, DROP_LINE_THICKNESS, 20); - } - } else { - return new Rectangle(insets.left, insets.top, - list.getWidth() - insets.left - insets.right, - DROP_LINE_THICKNESS); - } - } - - Rectangle rect = null; - int index = loc.getIndex(); - boolean decr = false; - - if (layoutOrientation == JList.HORIZONTAL_WRAP) { - if (index == size) { - decr = true; - } else if (index != 0 && convertModelToRow(index) - != convertModelToRow(index - 1)) { - - Rectangle prev = getCellBounds(list, index - 1); - Rectangle me = getCellBounds(list, index); - Point p = loc.getDropPoint(); - - if (isLeftToRight) { - decr = Point2D.distance(prev.x + prev.width, - prev.y + (int)(prev.height / 2.0), - p.x, p.y) - < Point2D.distance(me.x, - me.y + (int)(me.height / 2.0), - p.x, p.y); - } else { - decr = Point2D.distance(prev.x, - prev.y + (int)(prev.height / 2.0), - p.x, p.y) - < Point2D.distance(me.x + me.width, - me.y + (int)(prev.height / 2.0), - p.x, p.y); - } - } - - if (decr) { - index--; - rect = getCellBounds(list, index); - if (isLeftToRight) { - rect.x += rect.width; - } else { - rect.x -= DROP_LINE_THICKNESS; - } - } else { - rect = getCellBounds(list, index); - if (!isLeftToRight) { - rect.x += rect.width - DROP_LINE_THICKNESS; - } - } - - if (rect.x >= list.getWidth()) { - rect.x = list.getWidth() - DROP_LINE_THICKNESS; - } else if (rect.x < 0) { - rect.x = 0; - } - - rect.width = DROP_LINE_THICKNESS; - } else if (layoutOrientation == JList.VERTICAL_WRAP) { - if (index == size) { - index--; - rect = getCellBounds(list, index); - rect.y += rect.height; - } else if (index != 0 && convertModelToColumn(index) - != convertModelToColumn(index - 1)) { - - Rectangle prev = getCellBounds(list, index - 1); - Rectangle me = getCellBounds(list, index); - Point p = loc.getDropPoint(); - if (Point2D.distance(prev.x + (int)(prev.width / 2.0), - prev.y + prev.height, - p.x, p.y) - < Point2D.distance(me.x + (int)(me.width / 2.0), - me.y, - p.x, p.y)) { - - index--; - rect = getCellBounds(list, index); - rect.y += rect.height; - } else { - rect = getCellBounds(list, index); - } - } else { - rect = getCellBounds(list, index); - } - - if (rect.y >= list.getHeight()) { - rect.y = list.getHeight() - DROP_LINE_THICKNESS; - } - - rect.height = DROP_LINE_THICKNESS; - } else { - if (index == size) { - index--; - rect = getCellBounds(list, index); - rect.y += rect.height; - } else { - rect = getCellBounds(list, index); - } - - if (rect.y >= list.getHeight()) { - rect.y = list.getHeight() - DROP_LINE_THICKNESS; - } - - rect.height = DROP_LINE_THICKNESS; - } - - return rect; - } - - /** - * Returns the baseline. - * - * @throws NullPointerException {@inheritDoc} - * @throws IllegalArgumentException {@inheritDoc} - * @see JComponent#getBaseline(int, int) - * @since 1.6 - */ - public int getBaseline(JComponent c, int width, int height) { -// super.getBaseline(c, width, height); - checkBaselinePrecondition(c, width, height); - int rowHeight = list.getFixedCellHeight(); - UIDefaults lafDefaults = UIManager.getLookAndFeelDefaults(); - Component renderer = (Component)lafDefaults.get( - BASELINE_COMPONENT_KEY); - if (renderer == null) { - ListCellRenderer lcr = (ListCellRenderer)UIManager.get( - "List.cellRenderer"); - - // fix for 6711072 some LAFs like Nimbus do not provide this - // UIManager key and we should not through a NPE here because of it - if (lcr == null) { - lcr = new DefaultListCellRenderer(); - } - - renderer = lcr.getListCellRendererComponent( - list, "a", -1, false, false); - lafDefaults.put(BASELINE_COMPONENT_KEY, renderer); - } - renderer.setFont(list.getFont()); - // JList actually has much more complex behavior here. - // If rowHeight != -1 the rowHeight is either the max of all cell - // heights (layout orientation != VERTICAL), or is variable depending - // upon the cell. We assume a default size. - // We could theoretically query the real renderer, but that would - // not work for an empty model and the results may vary with - // the content. - if (rowHeight == -1) { - rowHeight = renderer.getPreferredSize().height; - } - return renderer.getBaseline(Integer.MAX_VALUE, rowHeight) + - list.getInsets().top; - } - - /** - * Returns an enum indicating how the baseline of the component - * changes as the size changes. - * - * @throws NullPointerException {@inheritDoc} - * @see JComponent#getBaseline(int, int) - * @since 1.6 - */ - public Component.BaselineResizeBehavior getBaselineResizeBehavior( - JComponent c) { - super.getBaselineResizeBehavior(c); - return Component.BaselineResizeBehavior.CONSTANT_ASCENT; - } - - /** - * The preferredSize of the list depends upon the layout orientation. - * - * - * - * - * - * - *
                    Layout OrientationPreferred Size
                    JList.VERTICAL - * The preferredSize of the list is total height of the rows - * and the maximum width of the cells. If JList.fixedCellHeight - * is specified then the total height of the rows is just - * (cellVerticalMargins + fixedCellHeight) * model.getSize() where - * rowVerticalMargins is the space we allocate for drawing - * the yellow focus outline. Similarly if fixedCellWidth is - * specified then we just use that. - *
                    JList.VERTICAL_WRAP - * If the visible row count is greater than zero, the preferredHeight - * is the maximum cell height * visibleRowCount. If the visible row - * count is <= 0, the preferred height is either the current height - * of the list, or the maximum cell height, whichever is - * bigger. The preferred width is than the maximum cell width * - * number of columns needed. Where the number of columns needs is - * list.height / max cell height. Max cell height is either the fixed - * cell height, or is determined by iterating through all the cells - * to find the maximum height from the ListCellRenderer. - *
                    JList.HORIZONTAL_WRAP - * If the visible row count is greater than zero, the preferredHeight - * is the maximum cell height * adjustedRowCount. Where - * visibleRowCount is used to determine the number of columns. - * Because this lays out horizontally the number of rows is - * then determined from the column count. For example, lets say - * you have a model with 10 items and the visible row count is 8. - * The number of columns needed to display this is 2, but you no - * longer need 8 rows to display this, you only need 5, thus - * the adjustedRowCount is 5. - *

                    If the visible row - * count is <= 0, the preferred height is dictated by the - * number of columns, which will be as many as can fit in the width - * of the JList (width / max cell width), with at - * least one column. The preferred height then becomes the - * model size / number of columns * maximum cell height. - * Max cell height is either the fixed - * cell height, or is determined by iterating through all the cells - * to find the maximum height from the ListCellRenderer. - *

                    - * The above specifies the raw preferred width and height. The resulting - * preferred width is the above width + insets.left + insets.right and - * the resulting preferred height is the above height + insets.top + - * insets.bottom. Where the Insets are determined from - * list.getInsets(). - * - * @param c The JList component. - * @return The total size of the list. - */ - public Dimension getPreferredSize(JComponent c) { - maybeUpdateLayoutState(); - - int lastRow = getElementCount() - 1; - if (lastRow < 0) { - return new Dimension(0, 0); - } - - Insets insets = list.getInsets(); - int width = cellWidth * columnCount + insets.left + insets.right; - int height; - - if (layoutOrientation != JList.VERTICAL) { - height = preferredHeight; - } - else { - Rectangle bounds = getCellBounds(list, lastRow); - - if (bounds != null) { - height = bounds.y + bounds.height + insets.bottom; - } - else { - height = 0; - } - } - return new Dimension(width, height); - } - - - /** - * Selected the previous row and force it to be visible. - * - * @see JList#ensureIndexIsVisible - */ - protected void selectPreviousIndex() { - int s = list.getSelectedIndex(); - if(s > 0) { - s -= 1; - list.setSelectedIndex(s); - list.ensureIndexIsVisible(s); - } - } - - - /** - * Selected the previous row and force it to be visible. - * - * @see JList#ensureIndexIsVisible - */ - protected void selectNextIndex() - { - int s = list.getSelectedIndex(); - if((s + 1) < getElementCount()) { - s += 1; - list.setSelectedIndex(s); - list.ensureIndexIsVisible(s); - } - } - - - /** - * Registers the keyboard bindings on the JList that the - * BasicXListUI is associated with. This method is called at - * installUI() time. - * - * @see #installUI - */ - protected void installKeyboardActions() { - InputMap inputMap = getInputMap(JComponent.WHEN_FOCUSED); - - SwingUtilities.replaceUIInputMap(list, JComponent.WHEN_FOCUSED, - inputMap); - - LazyActionMap.installLazyActionMap(list, BasicXListUI.class, - "XList.actionMap"); - } - - InputMap getInputMap(int condition) { - if (condition == JComponent.WHEN_FOCUSED) { - // PENDING JW: side-effect when reverting to ui manager? revisit! - InputMap keyMap = (InputMap) UIManager.get("List.focusInputMap"); -// InputMap keyMap = (InputMap)DefaultLookup.get( -// list, this, "List.focusInputMap"); - InputMap rtlKeyMap; - - if (isLeftToRight || - ((rtlKeyMap = (InputMap) UIManager.get("List.focusInputMap.RightToLeft")) - == null)) { -// ((rtlKeyMap = (InputMap)DefaultLookup.get(list, this, -// "List.focusInputMap.RightToLeft")) == null)) { - return keyMap; - } else { - rtlKeyMap.setParent(keyMap); - return rtlKeyMap; - } - } - return null; - } - - /** - * Unregisters keyboard actions installed from - * installKeyboardActions. - * This method is called at uninstallUI() time - subclassess should - * ensure that all of the keyboard actions registered at installUI - * time are removed here. - * - * @see #installUI - */ - protected void uninstallKeyboardActions() { - SwingUtilities.replaceUIActionMap(list, null); - SwingUtilities.replaceUIInputMap(list, JComponent.WHEN_FOCUSED, null); - } - - - /** - * Create and install the listeners for the JList, its model, and its - * selectionModel. This method is called at installUI() time. - * - * @see #installUI - * @see #uninstallListeners - */ - protected void installListeners() - { - TransferHandler th = list.getTransferHandler(); - if (th == null || th instanceof UIResource) { - list.setTransferHandler(defaultTransferHandler); - // default TransferHandler doesn't support drop - // so we don't want drop handling - if (list.getDropTarget() instanceof UIResource) { - list.setDropTarget(null); - } - } - - focusListener = createFocusListener(); - mouseInputListener = createMouseInputListener(); - propertyChangeListener = createPropertyChangeListener(); - listSelectionListener = createListSelectionListener(); - listDataListener = createListDataListener(); - - list.addFocusListener(focusListener); - list.addMouseListener(mouseInputListener); - list.addMouseMotionListener(mouseInputListener); - list.addPropertyChangeListener(propertyChangeListener); - list.addKeyListener(getHandler()); - // JW: here we really want the model - ListModel model = list.getModel(); - if (model != null) { - model.addListDataListener(listDataListener); - } - - ListSelectionModel selectionModel = list.getSelectionModel(); - if (selectionModel != null) { - selectionModel.addListSelectionListener(listSelectionListener); - } - } - - - /** - * Remove the listeners for the JList, its model, and its - * selectionModel. All of the listener fields, are reset to - * null here. This method is called at uninstallUI() time, - * it should be kept in sync with installListeners. - * - * @see #uninstallUI - * @see #installListeners - */ - protected void uninstallListeners() - { - list.removeFocusListener(focusListener); - list.removeMouseListener(mouseInputListener); - list.removeMouseMotionListener(mouseInputListener); - list.removePropertyChangeListener(propertyChangeListener); - list.removeKeyListener(getHandler()); - - ListModel model = list.getModel(); - if (model != null) { - model.removeListDataListener(listDataListener); - } - - ListSelectionModel selectionModel = list.getSelectionModel(); - if (selectionModel != null) { - selectionModel.removeListSelectionListener(listSelectionListener); - } - - focusListener = null; - mouseInputListener = null; - listSelectionListener = null; - listDataListener = null; - propertyChangeListener = null; - handler = null; - } - - - /** - * Initialize JList properties, e.g. font, foreground, and background, - * and add the CellRendererPane. The font, foreground, and background - * properties are only set if their current value is either null - * or a UIResource, other properties are set if the current - * value is null. - * - * @see #uninstallDefaults - * @see #installUI - * @see CellRendererPane - */ - protected void installDefaults() - { - list.setLayout(null); - - LookAndFeel.installBorder(list, "List.border"); - - LookAndFeel.installColorsAndFont(list, "List.background", "List.foreground", "List.font"); - - LookAndFeel.installProperty(list, "opaque", Boolean.TRUE); - - if (list.getCellRenderer() == null) { - list.setCellRenderer((ListCellRenderer)(UIManager.get("List.cellRenderer"))); - } - - Color sbg = list.getSelectionBackground(); - if (sbg == null || sbg instanceof UIResource) { - list.setSelectionBackground(UIManager.getColor("List.selectionBackground")); - } - - Color sfg = list.getSelectionForeground(); - if (sfg == null || sfg instanceof UIResource) { - list.setSelectionForeground(UIManager.getColor("List.selectionForeground")); - } - - Long l = (Long)UIManager.get("List.timeFactor"); - timeFactor = (l!=null) ? l.longValue() : 1000L; - - updateIsFileList(); - } - - private void updateIsFileList() { - boolean b = Boolean.TRUE.equals(list.getClientProperty("List.isFileList")); - if (b != isFileList) { - isFileList = b; - Font oldFont = list.getFont(); - if (oldFont == null || oldFont instanceof UIResource) { - Font newFont = UIManager.getFont(b ? "FileChooser.listFont" : "List.font"); - if (newFont != null && newFont != oldFont) { - list.setFont(newFont); - } - } - } - } - - - /** - * Set the JList properties that haven't been explicitly overridden to - * null. A property is considered overridden if its current value - * is not a UIResource. - * - * @see #installDefaults - * @see #uninstallUI - * @see CellRendererPane - */ - protected void uninstallDefaults() - { - LookAndFeel.uninstallBorder(list); - if (list.getFont() instanceof UIResource) { - list.setFont(null); - } - if (list.getForeground() instanceof UIResource) { - list.setForeground(null); - } - if (list.getBackground() instanceof UIResource) { - list.setBackground(null); - } - if (list.getSelectionBackground() instanceof UIResource) { - list.setSelectionBackground(null); - } - if (list.getSelectionForeground() instanceof UIResource) { - list.setSelectionForeground(null); - } - if (list.getCellRenderer() instanceof UIResource) { - list.setCellRenderer(null); - } - if (list.getTransferHandler() instanceof UIResource) { - list.setTransferHandler(null); - } - } - - - /** - * Initializes this.list by calling installDefaults(), - * installListeners(), and installKeyboardActions() - * in order. - * - * @see #installDefaults - * @see #installListeners - * @see #installKeyboardActions - */ - public void installUI(JComponent c) - { - list = (JXList)c; - - layoutOrientation = list.getLayoutOrientation(); - - rendererPane = new CellRendererPane(); - list.add(rendererPane); - - columnCount = 1; - - updateLayoutStateNeeded = modelChanged; - isLeftToRight = list.getComponentOrientation().isLeftToRight(); - - installDefaults(); - installListeners(); - installKeyboardActions(); - installSortUI(); - } - - - /** - * Uninitializes this.list by calling uninstallListeners(), - * uninstallKeyboardActions(), and uninstallDefaults() - * in order. Sets this.list to null. - * - * @see #uninstallListeners - * @see #uninstallKeyboardActions - * @see #uninstallDefaults - */ - public void uninstallUI(JComponent c) - { - uninstallSortUI(); - uninstallListeners(); - uninstallDefaults(); - uninstallKeyboardActions(); - - cellWidth = cellHeight = -1; - cellHeights = null; - - listWidth = listHeight = -1; - - list.remove(rendererPane); - rendererPane = null; - list = null; - } - - - /** - * Returns a new instance of BasicXListUI. BasicXListUI delegates are - * allocated one per JList. - * - * @return A new ListUI implementation for the Windows look and feel. - */ - public static ComponentUI createUI(JComponent list) { - return new BasicXListUI(); - } - - - /** - * {@inheritDoc} - * @throws NullPointerException {@inheritDoc} - */ - public int locationToIndex(JList list, Point location) { - maybeUpdateLayoutState(); - return convertLocationToModel(location.x, location.y); - } - - - /** - * {@inheritDoc} - */ - public Point indexToLocation(JList list, int index) { - maybeUpdateLayoutState(); - Rectangle rect = getCellBounds(list, index, index); - - if (rect != null) { - return new Point(rect.x, rect.y); - } - return null; - } - - - /** - * {@inheritDoc} - */ - public Rectangle getCellBounds(JList list, int index1, int index2) { - maybeUpdateLayoutState(); - - int minIndex = Math.min(index1, index2); - int maxIndex = Math.max(index1, index2); - - if (minIndex >= getElementCount()) { - return null; - } - - Rectangle minBounds = getCellBounds(list, minIndex); - - if (minBounds == null) { - return null; - } - if (minIndex == maxIndex) { - return minBounds; - } - Rectangle maxBounds = getCellBounds(list, maxIndex); - - if (maxBounds != null) { - if (layoutOrientation == JList.HORIZONTAL_WRAP) { - int minRow = convertModelToRow(minIndex); - int maxRow = convertModelToRow(maxIndex); - - if (minRow != maxRow) { - minBounds.x = 0; - minBounds.width = list.getWidth(); - } - } - else if (minBounds.x != maxBounds.x) { - // Different columns - minBounds.y = 0; - minBounds.height = list.getHeight(); - } - minBounds.add(maxBounds); - } - return minBounds; - } - - /** - * Gets the bounds of the specified model index, returning the resulting - * bounds, or null if index is not valid. - */ - private Rectangle getCellBounds(JList list, int index) { - maybeUpdateLayoutState(); - - int row = convertModelToRow(index); - int column = convertModelToColumn(index); - - if (row == -1 || column == -1) { - return null; - } - - Insets insets = list.getInsets(); - int x; - int w = cellWidth; - int y = insets.top; - int h; - switch (layoutOrientation) { - case JList.VERTICAL_WRAP: - case JList.HORIZONTAL_WRAP: - if (isLeftToRight) { - x = insets.left + column * cellWidth; - } else { - x = list.getWidth() - insets.right - (column+1) * cellWidth; - } - y += cellHeight * row; - h = cellHeight; - break; - default: - x = insets.left; - if (cellHeights == null) { - y += (cellHeight * row); - } - else if (row >= cellHeights.length) { - y = 0; - } - else { - for(int i = 0; i < row; i++) { - y += cellHeights[i]; - } - } - w = list.getWidth() - (insets.left + insets.right); - h = getRowHeight(index); - break; - } - return new Rectangle(x, y, w, h); - } - - /** - * Returns the height of the specified row based on the current layout. - * - * @return The specified row height or -1 if row isn't valid. - * @see #convertYToRow - * @see #convertRowToY - * @see #updateLayoutState - */ - protected int getRowHeight(int row) - { - return getHeight(0, row); - } - - - /** - * Convert the JList relative coordinate to the row that contains it, - * based on the current layout. If y0 doesn't fall within any row, - * return -1. - * - * @return The row that contains y0, or -1. - * @see #getRowHeight - * @see #updateLayoutState - */ - protected int convertYToRow(int y0) - { - return convertLocationToRow(0, y0, false); - } - - - /** - * Return the JList relative Y coordinate of the origin of the specified - * row or -1 if row isn't valid. - * - * @return The Y coordinate of the origin of row, or -1. - * @see #getRowHeight - * @see #updateLayoutState - */ - protected int convertRowToY(int row) - { - if (row >= getRowCount(0) || row < 0) { - return -1; - } - Rectangle bounds = getCellBounds(list, row, row); - return bounds.y; - } - - /** - * Returns the height of the cell at the passed in location. - */ - private int getHeight(int column, int row) { - if (column < 0 || column > columnCount || row < 0) { - return -1; - } - if (layoutOrientation != JList.VERTICAL) { - return cellHeight; - } - if (row >= getElementCount()) { - return -1; - } - return (cellHeights == null) ? cellHeight : - ((row < cellHeights.length) ? cellHeights[row] : -1); - } - - /** - * Returns the row at location x/y. - * - * @param closest If true and the location doesn't exactly match a - * particular location, this will return the closest row. - */ - private int convertLocationToRow(int x, int y0, boolean closest) { - int size = getElementCount(); - - if (size <= 0) { - return -1; - } - Insets insets = list.getInsets(); - if (cellHeights == null) { - int row = (cellHeight == 0) ? 0 : - ((y0 - insets.top) / cellHeight); - if (closest) { - if (row < 0) { - row = 0; - } - else if (row >= size) { - row = size - 1; - } - } - return row; - } - else if (size > cellHeights.length) { - return -1; - } - else { - int y = insets.top; - int row = 0; - - if (closest && y0 < y) { - return 0; - } - int i; - for (i = 0; i < size; i++) { - if ((y0 >= y) && (y0 < y + cellHeights[i])) { - return row; - } - y += cellHeights[i]; - row += 1; - } - return i - 1; - } - } - - /** - * Returns the closest row that starts at the specified y-location - * in the passed in column. - */ - private int convertLocationToRowInColumn(int y, int column) { - int x = 0; - - if (layoutOrientation != JList.VERTICAL) { - if (isLeftToRight) { - x = column * cellWidth; - } else { - x = list.getWidth() - (column+1)*cellWidth - list.getInsets().right; - } - } - return convertLocationToRow(x, y, true); - } - - /** - * Returns the closest location to the model index of the passed in - * location. - */ - private int convertLocationToModel(int x, int y) { - int row = convertLocationToRow(x, y, true); - int column = convertLocationToColumn(x, y); - - if (row >= 0 && column >= 0) { - return getModelIndex(column, row); - } - return -1; - } - - /** - * Returns the number of rows in the given column. - */ - private int getRowCount(int column) { - if (column < 0 || column >= columnCount) { - return -1; - } - if (layoutOrientation == JList.VERTICAL || - (column == 0 && columnCount == 1)) { - return getElementCount(); - } - if (column >= columnCount) { - return -1; - } - if (layoutOrientation == JList.VERTICAL_WRAP) { - if (column < (columnCount - 1)) { - return rowsPerColumn; - } - return getElementCount() - (columnCount - 1) * - rowsPerColumn; - } - // JList.HORIZONTAL_WRAP - int diff = columnCount - (columnCount * rowsPerColumn - - getElementCount()); - - if (column >= diff) { - return Math.max(0, rowsPerColumn - 1); - } - return rowsPerColumn; - } - - /** - * Returns the model index for the specified display location. - * If columnxrow is beyond the length of the - * model, this will return the model size - 1. - */ - private int getModelIndex(int column, int row) { - switch (layoutOrientation) { - case JList.VERTICAL_WRAP: - return Math.min(getElementCount() - 1, rowsPerColumn * - column + Math.min(row, rowsPerColumn-1)); - case JList.HORIZONTAL_WRAP: - return Math.min(getElementCount() - 1, row * columnCount + - column); - default: - return row; - } - } - - /** - * Returns the closest column to the passed in location. - */ - private int convertLocationToColumn(int x, int y) { - if (cellWidth > 0) { - if (layoutOrientation == JList.VERTICAL) { - return 0; - } - Insets insets = list.getInsets(); - int col; - if (isLeftToRight) { - col = (x - insets.left) / cellWidth; - } else { - col = (list.getWidth() - x - insets.right - 1) / cellWidth; - } - if (col < 0) { - return 0; - } - else if (col >= columnCount) { - return columnCount - 1; - } - return col; - } - return 0; - } - - /** - * Returns the row that the model index index will be - * displayed in.. - */ - private int convertModelToRow(int index) { - int size = getElementCount(); - - if ((index < 0) || (index >= size)) { - return -1; - } - - if (layoutOrientation != JList.VERTICAL && columnCount > 1 && - rowsPerColumn > 0) { - if (layoutOrientation == JList.VERTICAL_WRAP) { - return index % rowsPerColumn; - } - return index / columnCount; - } - return index; - } - - /** - * Returns the column that the model index index will be - * displayed in. - */ - private int convertModelToColumn(int index) { - int size = getElementCount(); - - if ((index < 0) || (index >= size)) { - return -1; - } - - if (layoutOrientation != JList.VERTICAL && rowsPerColumn > 0 && - columnCount > 1) { - if (layoutOrientation == JList.VERTICAL_WRAP) { - return index / rowsPerColumn; - } - return index % columnCount; - } - return 0; - } - - /** - * If updateLayoutStateNeeded is non zero, call updateLayoutState() and reset - * updateLayoutStateNeeded. This method should be called by methods - * before doing any computation based on the geometry of the list. - * For example it's the first call in paint() and getPreferredSize(). - * - * @see #updateLayoutState - */ - protected void maybeUpdateLayoutState() - { - if (updateLayoutStateNeeded != 0) { - updateLayoutState(); - updateLayoutStateNeeded = 0; - } - } - - - /** - * Recompute the value of cellHeight or cellHeights based - * and cellWidth, based on the current font and the current - * values of fixedCellWidth, fixedCellHeight, and prototypeCellValue. - * - * @see #maybeUpdateLayoutState - */ - protected void updateLayoutState() - { - /* If both JList fixedCellWidth and fixedCellHeight have been - * set, then initialize cellWidth and cellHeight, and set - * cellHeights to null. - */ - - int fixedCellHeight = list.getFixedCellHeight(); - int fixedCellWidth = list.getFixedCellWidth(); - - cellWidth = (fixedCellWidth != -1) ? fixedCellWidth : -1; - - if (fixedCellHeight != -1) { - cellHeight = fixedCellHeight; - cellHeights = null; - } - else { - cellHeight = -1; - cellHeights = new int[getElementCount()]; - } - - /* If either of JList fixedCellWidth and fixedCellHeight haven't - * been set, then initialize cellWidth and cellHeights by - * scanning through the entire model. Note: if the renderer is - * null, we just set cellWidth and cellHeights[*] to zero, - * if they're not set already. - */ - - if ((fixedCellWidth == -1) || (fixedCellHeight == -1)) { - - ListModel dataModel = getViewModel(); - int dataModelSize = dataModel.getSize(); - ListCellRenderer renderer = list.getCellRenderer(); - - if (renderer != null) { - for(int index = 0; index < dataModelSize; index++) { - Object value = dataModel.getElementAt(index); - Component c = renderer.getListCellRendererComponent(list, value, index, false, false); - rendererPane.add(c); - Dimension cellSize = c.getPreferredSize(); - if (fixedCellWidth == -1) { - cellWidth = Math.max(cellSize.width, cellWidth); - } - if (fixedCellHeight == -1) { - cellHeights[index] = cellSize.height; - } - } - } - else { - if (cellWidth == -1) { - cellWidth = 0; - } - if (cellHeights == null) { - cellHeights = new int[dataModelSize]; - } - for(int index = 0; index < dataModelSize; index++) { - cellHeights[index] = 0; - } - } - } - - columnCount = 1; - if (layoutOrientation != JList.VERTICAL) { - updateHorizontalLayoutState(fixedCellWidth, fixedCellHeight); - } - } - - /** - * Invoked when the list is layed out horizontally to determine how - * many columns to create. - *

                    - * This updates the rowsPerColumn, columnCount, - * preferredHeight and potentially cellHeight - * instance variables. - */ - private void updateHorizontalLayoutState(int fixedCellWidth, - int fixedCellHeight) { - int visRows = list.getVisibleRowCount(); - int dataModelSize = getElementCount(); - Insets insets = list.getInsets(); - - listHeight = list.getHeight(); - listWidth = list.getWidth(); - - if (dataModelSize == 0) { - rowsPerColumn = columnCount = 0; - preferredHeight = insets.top + insets.bottom; - return; - } - - int height; - - if (fixedCellHeight != -1) { - height = fixedCellHeight; - } - else { - // Determine the max of the renderer heights. - int maxHeight = 0; - if (cellHeights.length > 0) { - maxHeight = cellHeights[cellHeights.length - 1]; - for (int counter = cellHeights.length - 2; - counter >= 0; counter--) { - maxHeight = Math.max(maxHeight, cellHeights[counter]); - } - } - height = cellHeight = maxHeight; - cellHeights = null; - } - // The number of rows is either determined by the visible row - // count, or by the height of the list. - rowsPerColumn = dataModelSize; - if (visRows > 0) { - rowsPerColumn = visRows; - columnCount = Math.max(1, dataModelSize / rowsPerColumn); - if (dataModelSize > 0 && dataModelSize > rowsPerColumn && - dataModelSize % rowsPerColumn != 0) { - columnCount++; - } - if (layoutOrientation == JList.HORIZONTAL_WRAP) { - // Because HORIZONTAL_WRAP flows differently, the - // rowsPerColumn needs to be adjusted. - rowsPerColumn = (dataModelSize / columnCount); - if (dataModelSize % columnCount > 0) { - rowsPerColumn++; - } - } - } - else if (layoutOrientation == JList.VERTICAL_WRAP && height != 0) { - rowsPerColumn = Math.max(1, (listHeight - insets.top - - insets.bottom) / height); - columnCount = Math.max(1, dataModelSize / rowsPerColumn); - if (dataModelSize > 0 && dataModelSize > rowsPerColumn && - dataModelSize % rowsPerColumn != 0) { - columnCount++; - } - } - else if (layoutOrientation == JList.HORIZONTAL_WRAP && cellWidth > 0 && - listWidth > 0) { - columnCount = Math.max(1, (listWidth - insets.left - - insets.right) / cellWidth); - rowsPerColumn = dataModelSize / columnCount; - if (dataModelSize % columnCount > 0) { - rowsPerColumn++; - } - } - preferredHeight = rowsPerColumn * cellHeight + insets.top + - insets.bottom; - } - - private Handler getHandler() { - if (handler == null) { - handler = new Handler(); - } - return handler; - } - - /** - * Mouse input, and focus handling for JList. An instance of this - * class is added to the appropriate java.awt.Component lists - * at installUI() time. Note keyboard input is handled with JComponent - * KeyboardActions, see installKeyboardActions(). - *

                    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is - * appropriate for short term storage or RMI between applications running - * the same version of Swing. As of 1.4, support for long term storage - * of all JavaBeansTM - * has been added to the java.beans package. - * Please see {@link java.beans.XMLEncoder}. - * - * @see #createMouseInputListener - * @see #installKeyboardActions - * @see #installUI - */ - public class MouseInputHandler implements MouseInputListener - { - public void mouseClicked(MouseEvent e) { - getHandler().mouseClicked(e); - } - - public void mouseEntered(MouseEvent e) { - getHandler().mouseEntered(e); - } - - public void mouseExited(MouseEvent e) { - getHandler().mouseExited(e); - } - - public void mousePressed(MouseEvent e) { - getHandler().mousePressed(e); - } - - public void mouseDragged(MouseEvent e) { - getHandler().mouseDragged(e); - } - - public void mouseMoved(MouseEvent e) { - getHandler().mouseMoved(e); - } - - public void mouseReleased(MouseEvent e) { - getHandler().mouseReleased(e); - } - } - - - /** - * Creates a delegate that implements MouseInputListener. - * The delegate is added to the corresponding java.awt.Component listener - * lists at installUI() time. Subclasses can override this method to return - * a custom MouseInputListener, e.g. - *

                    -     * class MyListUI extends BasicXListUI {
                    -     *    protected MouseInputListener createMouseInputListener() {
                    -     *        return new MyMouseInputHandler();
                    -     *    }
                    -     *    public class MyMouseInputHandler extends MouseInputHandler {
                    -     *        public void mouseMoved(MouseEvent e) {
                    -     *            // do some extra work when the mouse moves
                    -     *            super.mouseMoved(e);
                    -     *        }
                    -     *    }
                    -     * }
                    -     * 
                    - * - * @see MouseInputHandler - * @see #installUI - */ - protected MouseInputListener createMouseInputListener() { - return getHandler(); - } - - /** - * This inner class is marked "public" due to a compiler bug. - * This class should be treated as a "protected" inner class. - * Instantiate it only within subclasses of BasicTableUI. - */ - public class FocusHandler implements FocusListener - { - protected void repaintCellFocus() - { - getHandler().repaintCellFocus(); - } - - /* The focusGained() focusLost() methods run when the JList - * focus changes. - */ - - public void focusGained(FocusEvent e) { - getHandler().focusGained(e); - } - - public void focusLost(FocusEvent e) { - getHandler().focusLost(e); - } - } - - protected FocusListener createFocusListener() { - return getHandler(); - } - - /** - * The ListSelectionListener that's added to the JLists selection - * model at installUI time, and whenever the JList.selectionModel property - * changes. When the selection changes we repaint the affected rows. - *

                    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is - * appropriate for short term storage or RMI between applications running - * the same version of Swing. As of 1.4, support for long term storage - * of all JavaBeansTM - * has been added to the java.beans package. - * Please see {@link java.beans.XMLEncoder}. - * - * @see #createListSelectionListener - * @see #getCellBounds - * @see #installUI - */ - public class ListSelectionHandler implements ListSelectionListener - { - public void valueChanged(ListSelectionEvent e) - { - if (processedBySortUI(e)) return; - getHandler().valueChanged(e); - } - } - - - /** - * Creates an instance of ListSelectionHandler that's added to - * the JLists by selectionModel as needed. Subclasses can override - * this method to return a custom ListSelectionListener, e.g. - *

                    -     * class MyListUI extends BasicXListUI {
                    -     *    protected ListSelectionListener createListSelectionListener() {
                    -     *        return new MySelectionListener();
                    -     *    }
                    -     *    public class MySelectionListener extends ListSelectionHandler {
                    -     *        public void valueChanged(ListSelectionEvent e) {
                    -     *            // do some extra work when the selection changes
                    -     *            super.valueChange(e);
                    -     *        }
                    -     *    }
                    -     * }
                    -     * 
                    - * - * @see ListSelectionHandler - * @see #installUI - */ - protected ListSelectionListener createListSelectionListener() { - return new ListSelectionHandler(); - } - - - private void redrawList() { - list.revalidate(); - list.repaint(); - } - - - /** - * The ListDataListener that's added to the JLists model at - * installUI time, and whenever the JList.model property changes. - *

                    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is - * appropriate for short term storage or RMI between applications running - * the same version of Swing. As of 1.4, support for long term storage - * of all JavaBeansTM - * has been added to the java.beans package. - * Please see {@link java.beans.XMLEncoder}. - * - * @see JList#getModel - * @see #maybeUpdateLayoutState - * @see #createListDataListener - * @see #installUI - */ - public class ListDataHandler implements ListDataListener - { - public void intervalAdded(ListDataEvent e) { - if (processedBySortUI(e)) - return; - getHandler().intervalAdded(e); - } - - - public void intervalRemoved(ListDataEvent e) { - if (processedBySortUI(e)) - return; - getHandler().intervalRemoved(e); - } - - - public void contentsChanged(ListDataEvent e) { - if (processedBySortUI(e)) - return; - getHandler().contentsChanged(e); - } - } - - - /** - * Creates an instance of ListDataListener that's added to - * the JLists by model as needed. Subclasses can override - * this method to return a custom ListDataListener, e.g. - *

                    -     * class MyListUI extends BasicXListUI {
                    -     *    protected ListDataListener createListDataListener() {
                    -     *        return new MyListDataListener();
                    -     *    }
                    -     *    public class MyListDataListener extends ListDataHandler {
                    -     *        public void contentsChanged(ListDataEvent e) {
                    -     *            // do some extra work when the models contents change
                    -     *            super.contentsChange(e);
                    -     *        }
                    -     *    }
                    -     * }
                    -     * 
                    - * - * @see ListDataListener - * @see JList#getModel - * @see #installUI - */ - protected ListDataListener createListDataListener() { - return new ListDataHandler(); - } - - - /** - * The PropertyChangeListener that's added to the JList at - * installUI time. When the value of a JList property that - * affects layout changes, we set a bit in updateLayoutStateNeeded. - * If the JLists model changes we additionally remove our listeners - * from the old model. Likewise for the JList selectionModel. - *

                    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is - * appropriate for short term storage or RMI between applications running - * the same version of Swing. As of 1.4, support for long term storage - * of all JavaBeansTM - * has been added to the java.beans package. - * Please see {@link java.beans.XMLEncoder}. - * - * @see #maybeUpdateLayoutState - * @see #createPropertyChangeListener - * @see #installUI - */ - public class PropertyChangeHandler implements PropertyChangeListener - { - public void propertyChange(PropertyChangeEvent e) { - getHandler().propertyChange(e); - updateSortUI(e.getPropertyName()); - } - } - - /** - * Creates an instance of PropertyChangeHandler that's added to - * the JList by installUI(). Subclasses can override this method - * to return a custom PropertyChangeListener, e.g. - *

                    -     * class MyListUI extends BasicXListUI {
                    -     *    protected PropertyChangeListener createPropertyChangeListener() {
                    -     *        return new MyPropertyChangeListener();
                    -     *    }
                    -     *    public class MyPropertyChangeListener extends PropertyChangeHandler {
                    -     *        public void propertyChange(PropertyChangeEvent e) {
                    -     *            if (e.getPropertyName().equals("model")) {
                    -     *                // do some extra work when the model changes
                    -     *            }
                    -     *            super.propertyChange(e);
                    -     *        }
                    -     *    }
                    -     * }
                    -     * 
                    - * - * @see PropertyChangeListener - * @see #installUI - */ - protected PropertyChangeListener createPropertyChangeListener() { - return new PropertyChangeHandler(); - } - - /** Used by IncrementLeadSelectionAction. Indicates the action should - * change the lead, and not select it. */ - private static final int CHANGE_LEAD = 0; - /** Used by IncrementLeadSelectionAction. Indicates the action should - * change the selection and lead. */ - private static final int CHANGE_SELECTION = 1; - /** Used by IncrementLeadSelectionAction. Indicates the action should - * extend the selection from the anchor to the next index. */ - private static final int EXTEND_SELECTION = 2; - - // PENDING JW: this is not a complete replacement of sun.UIAction ... - private static class Actions extends UIAction { - private static final String SELECT_PREVIOUS_COLUMN = - "selectPreviousColumn"; - private static final String SELECT_PREVIOUS_COLUMN_EXTEND = - "selectPreviousColumnExtendSelection"; - private static final String SELECT_PREVIOUS_COLUMN_CHANGE_LEAD = - "selectPreviousColumnChangeLead"; - private static final String SELECT_NEXT_COLUMN = "selectNextColumn"; - private static final String SELECT_NEXT_COLUMN_EXTEND = - "selectNextColumnExtendSelection"; - private static final String SELECT_NEXT_COLUMN_CHANGE_LEAD = - "selectNextColumnChangeLead"; - private static final String SELECT_PREVIOUS_ROW = "selectPreviousRow"; - private static final String SELECT_PREVIOUS_ROW_EXTEND = - "selectPreviousRowExtendSelection"; - private static final String SELECT_PREVIOUS_ROW_CHANGE_LEAD = - "selectPreviousRowChangeLead"; - private static final String SELECT_NEXT_ROW = "selectNextRow"; - private static final String SELECT_NEXT_ROW_EXTEND = - "selectNextRowExtendSelection"; - private static final String SELECT_NEXT_ROW_CHANGE_LEAD = - "selectNextRowChangeLead"; - private static final String SELECT_FIRST_ROW = "selectFirstRow"; - private static final String SELECT_FIRST_ROW_EXTEND = - "selectFirstRowExtendSelection"; - private static final String SELECT_FIRST_ROW_CHANGE_LEAD = - "selectFirstRowChangeLead"; - private static final String SELECT_LAST_ROW = "selectLastRow"; - private static final String SELECT_LAST_ROW_EXTEND = - "selectLastRowExtendSelection"; - private static final String SELECT_LAST_ROW_CHANGE_LEAD = - "selectLastRowChangeLead"; - private static final String SCROLL_UP = "scrollUp"; - private static final String SCROLL_UP_EXTEND = - "scrollUpExtendSelection"; - private static final String SCROLL_UP_CHANGE_LEAD = - "scrollUpChangeLead"; - private static final String SCROLL_DOWN = "scrollDown"; - private static final String SCROLL_DOWN_EXTEND = - "scrollDownExtendSelection"; - private static final String SCROLL_DOWN_CHANGE_LEAD = - "scrollDownChangeLead"; - private static final String SELECT_ALL = "selectAll"; - private static final String CLEAR_SELECTION = "clearSelection"; - - // add the lead item to the selection without changing lead or anchor - private static final String ADD_TO_SELECTION = "addToSelection"; - - // toggle the selected state of the lead item and move the anchor to it - private static final String TOGGLE_AND_ANCHOR = "toggleAndAnchor"; - - // extend the selection to the lead item - private static final String EXTEND_TO = "extendTo"; - - // move the anchor to the lead and ensure only that item is selected - private static final String MOVE_SELECTION_TO = "moveSelectionTo"; - - Actions(String name) { - super(name); - } - public void actionPerformed(ActionEvent e) { - String name = getName(); - JList list = (JList)e.getSource(); - BasicXListUI ui = (BasicXListUI)LookAndFeelUtils.getUIOfType( - list.getUI(), BasicXListUI.class); - - if (name == SELECT_PREVIOUS_COLUMN) { - changeSelection(list, CHANGE_SELECTION, - getNextColumnIndex(list, ui, -1), -1); - } - else if (name == SELECT_PREVIOUS_COLUMN_EXTEND) { - changeSelection(list, EXTEND_SELECTION, - getNextColumnIndex(list, ui, -1), -1); - } - else if (name == SELECT_PREVIOUS_COLUMN_CHANGE_LEAD) { - changeSelection(list, CHANGE_LEAD, - getNextColumnIndex(list, ui, -1), -1); - } - else if (name == SELECT_NEXT_COLUMN) { - changeSelection(list, CHANGE_SELECTION, - getNextColumnIndex(list, ui, 1), 1); - } - else if (name == SELECT_NEXT_COLUMN_EXTEND) { - changeSelection(list, EXTEND_SELECTION, - getNextColumnIndex(list, ui, 1), 1); - } - else if (name == SELECT_NEXT_COLUMN_CHANGE_LEAD) { - changeSelection(list, CHANGE_LEAD, - getNextColumnIndex(list, ui, 1), 1); - } - else if (name == SELECT_PREVIOUS_ROW) { - changeSelection(list, CHANGE_SELECTION, - getNextIndex(list, ui, -1), -1); - } - else if (name == SELECT_PREVIOUS_ROW_EXTEND) { - changeSelection(list, EXTEND_SELECTION, - getNextIndex(list, ui, -1), -1); - } - else if (name == SELECT_PREVIOUS_ROW_CHANGE_LEAD) { - changeSelection(list, CHANGE_LEAD, - getNextIndex(list, ui, -1), -1); - } - else if (name == SELECT_NEXT_ROW) { - changeSelection(list, CHANGE_SELECTION, - getNextIndex(list, ui, 1), 1); - } - else if (name == SELECT_NEXT_ROW_EXTEND) { - changeSelection(list, EXTEND_SELECTION, - getNextIndex(list, ui, 1), 1); - } - else if (name == SELECT_NEXT_ROW_CHANGE_LEAD) { - changeSelection(list, CHANGE_LEAD, - getNextIndex(list, ui, 1), 1); - } - else if (name == SELECT_FIRST_ROW) { - changeSelection(list, CHANGE_SELECTION, 0, -1); - } - else if (name == SELECT_FIRST_ROW_EXTEND) { - changeSelection(list, EXTEND_SELECTION, 0, -1); - } - else if (name == SELECT_FIRST_ROW_CHANGE_LEAD) { - changeSelection(list, CHANGE_LEAD, 0, -1); - } - else if (name == SELECT_LAST_ROW) { - changeSelection(list, CHANGE_SELECTION, - getElementCount(list) - 1, 1); - } - else if (name == SELECT_LAST_ROW_EXTEND) { - changeSelection(list, EXTEND_SELECTION, - getElementCount(list) - 1, 1); - } - else if (name == SELECT_LAST_ROW_CHANGE_LEAD) { - changeSelection(list, CHANGE_LEAD, - getElementCount(list) - 1, 1); - } - else if (name == SCROLL_UP) { - changeSelection(list, CHANGE_SELECTION, - getNextPageIndex(list, -1), -1); - } - else if (name == SCROLL_UP_EXTEND) { - changeSelection(list, EXTEND_SELECTION, - getNextPageIndex(list, -1), -1); - } - else if (name == SCROLL_UP_CHANGE_LEAD) { - changeSelection(list, CHANGE_LEAD, - getNextPageIndex(list, -1), -1); - } - else if (name == SCROLL_DOWN) { - changeSelection(list, CHANGE_SELECTION, - getNextPageIndex(list, 1), 1); - } - else if (name == SCROLL_DOWN_EXTEND) { - changeSelection(list, EXTEND_SELECTION, - getNextPageIndex(list, 1), 1); - } - else if (name == SCROLL_DOWN_CHANGE_LEAD) { - changeSelection(list, CHANGE_LEAD, - getNextPageIndex(list, 1), 1); - } - else if (name == SELECT_ALL) { - selectAll(list); - } - else if (name == CLEAR_SELECTION) { - clearSelection(list); - } - else if (name == ADD_TO_SELECTION) { - int index = adjustIndex( - list.getSelectionModel().getLeadSelectionIndex(), list); - - if (!list.isSelectedIndex(index)) { - int oldAnchor = list.getSelectionModel().getAnchorSelectionIndex(); - list.setValueIsAdjusting(true); - list.addSelectionInterval(index, index); - list.getSelectionModel().setAnchorSelectionIndex(oldAnchor); - list.setValueIsAdjusting(false); - } - } - else if (name == TOGGLE_AND_ANCHOR) { - int index = adjustIndex( - list.getSelectionModel().getLeadSelectionIndex(), list); - - if (list.isSelectedIndex(index)) { - list.removeSelectionInterval(index, index); - } else { - list.addSelectionInterval(index, index); - } - } - else if (name == EXTEND_TO) { - changeSelection( - list, EXTEND_SELECTION, - adjustIndex(list.getSelectionModel().getLeadSelectionIndex(), list), - 0); - } - else if (name == MOVE_SELECTION_TO) { - changeSelection( - list, CHANGE_SELECTION, - adjustIndex(list.getSelectionModel().getLeadSelectionIndex(), list), - 0); - } - } - /** - * @param list - * @return - */ - private int getElementCount(JList list) { - return ((JXList) list).getElementCount(); - } - - public boolean isEnabled(Object c) { - Object name = getName(); - if (name == SELECT_PREVIOUS_COLUMN_CHANGE_LEAD || - name == SELECT_NEXT_COLUMN_CHANGE_LEAD || - name == SELECT_PREVIOUS_ROW_CHANGE_LEAD || - name == SELECT_NEXT_ROW_CHANGE_LEAD || - name == SELECT_FIRST_ROW_CHANGE_LEAD || - name == SELECT_LAST_ROW_CHANGE_LEAD || - name == SCROLL_UP_CHANGE_LEAD || - name == SCROLL_DOWN_CHANGE_LEAD) { - - // discontinuous selection actions are only enabled for - // DefaultListSelectionModel - return c != null && ((JList)c).getSelectionModel() - instanceof DefaultListSelectionModel; - } - - return true; - } - - private void clearSelection(JList list) { - list.clearSelection(); - } - - private void selectAll(JList list) { - int size = getElementCount(list); - if (size > 0) { - ListSelectionModel lsm = list.getSelectionModel(); - int lead = adjustIndex(lsm.getLeadSelectionIndex(), list); - - if (lsm.getSelectionMode() == ListSelectionModel.SINGLE_SELECTION) { - if (lead == -1) { - int min = adjustIndex(list.getMinSelectionIndex(), list); - lead = (min == -1 ? 0 : min); - } - - list.setSelectionInterval(lead, lead); - list.ensureIndexIsVisible(lead); - } else { - list.setValueIsAdjusting(true); - - int anchor = adjustIndex(lsm.getAnchorSelectionIndex(), list); - - list.setSelectionInterval(0, size - 1); - - // this is done to restore the anchor and lead - SwingXUtilities.setLeadAnchorWithoutSelection(lsm, anchor, lead); - - list.setValueIsAdjusting(false); - } - } - } - - private int getNextPageIndex(JList list, int direction) { - if (getElementCount(list) == 0) { - return -1; - } - - int index = -1; - Rectangle visRect = list.getVisibleRect(); - ListSelectionModel lsm = list.getSelectionModel(); - int lead = adjustIndex(lsm.getLeadSelectionIndex(), list); - Rectangle leadRect = - (lead==-1) ? new Rectangle() : list.getCellBounds(lead, lead); - - if (list.getLayoutOrientation() == JList.VERTICAL_WRAP && - list.getVisibleRowCount() <= 0) { - if (!list.getComponentOrientation().isLeftToRight()) { - direction = -direction; - } - // apply for horizontal scrolling: the step for next - // page index is number of visible columns - if (direction < 0) { - // left - visRect.x = leadRect.x + leadRect.width - visRect.width; - Point p = new Point(visRect.x - 1, leadRect.y); - index = list.locationToIndex(p); - Rectangle cellBounds = list.getCellBounds(index, index); - if (visRect.intersects(cellBounds)) { - p.x = cellBounds.x - 1; - index = list.locationToIndex(p); - cellBounds = list.getCellBounds(index, index); - } - // this is necessary for right-to-left orientation only - if (cellBounds.y != leadRect.y) { - p.x = cellBounds.x + cellBounds.width; - index = list.locationToIndex(p); - } - } - else { - // right - visRect.x = leadRect.x; - Point p = new Point(visRect.x + visRect.width, leadRect.y); - index = list.locationToIndex(p); - Rectangle cellBounds = list.getCellBounds(index, index); - if (visRect.intersects(cellBounds)) { - p.x = cellBounds.x + cellBounds.width; - index = list.locationToIndex(p); - cellBounds = list.getCellBounds(index, index); - } - if (cellBounds.y != leadRect.y) { - p.x = cellBounds.x - 1; - index = list.locationToIndex(p); - } - } - } - else { - if (direction < 0) { - // up - // go to the first visible cell - Point p = new Point(leadRect.x, visRect.y); - index = list.locationToIndex(p); - if (lead <= index) { - // if lead is the first visible cell (or above it) - // adjust the visible rect up - visRect.y = leadRect.y + leadRect.height - visRect.height; - p.y = visRect.y; - index = list.locationToIndex(p); - Rectangle cellBounds = list.getCellBounds(index, index); - // go one cell down if first visible cell doesn't fit - // into adjasted visible rectangle - if (cellBounds.y < visRect.y) { - p.y = cellBounds.y + cellBounds.height; - index = list.locationToIndex(p); - cellBounds = list.getCellBounds(index, index); - } - // if index isn't less then lead - // try to go to cell previous to lead - if (cellBounds.y >= leadRect.y) { - p.y = leadRect.y - 1; - index = list.locationToIndex(p); - } - } - } - else { - // down - // go to the last completely visible cell - Point p = new Point(leadRect.x, - visRect.y + visRect.height - 1); - index = list.locationToIndex(p); - Rectangle cellBounds = list.getCellBounds(index, index); - // go up one cell if last visible cell doesn't fit - // into visible rectangle - if (cellBounds.y + cellBounds.height > - visRect.y + visRect.height) { - p.y = cellBounds.y - 1; - index = list.locationToIndex(p); - cellBounds = list.getCellBounds(index, index); - index = Math.max(index, lead); - } - - if (lead >= index) { - // if lead is the last completely visible index - // (or below it) adjust the visible rect down - visRect.y = leadRect.y; - p.y = visRect.y + visRect.height - 1; - index = list.locationToIndex(p); - cellBounds = list.getCellBounds(index, index); - // go one cell up if last visible cell doesn't fit - // into adjasted visible rectangle - if (cellBounds.y + cellBounds.height > - visRect.y + visRect.height) { - p.y = cellBounds.y - 1; - index = list.locationToIndex(p); - cellBounds = list.getCellBounds(index, index); - } - // if index isn't greater then lead - // try to go to cell next after lead - if (cellBounds.y <= leadRect.y) { - p.y = leadRect.y + leadRect.height; - index = list.locationToIndex(p); - } - } - } - } - return index; - } - - private void changeSelection(JList list, int type, - int index, int direction) { - if (index >= 0 && index < getElementCount(list)) { - ListSelectionModel lsm = list.getSelectionModel(); - - // CHANGE_LEAD is only valid with multiple interval selection - if (type == CHANGE_LEAD && - list.getSelectionMode() - != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION) { - - type = CHANGE_SELECTION; - } - - // IMPORTANT - This needs to happen before the index is changed. - // This is because JFileChooser, which uses JList, also scrolls - // the selected item into view. If that happens first, then - // this method becomes a no-op. - adjustScrollPositionIfNecessary(list, index, direction); - - if (type == EXTEND_SELECTION) { - int anchor = adjustIndex(lsm.getAnchorSelectionIndex(), list); - if (anchor == -1) { - anchor = 0; - } - - list.setSelectionInterval(anchor, index); - } - else if (type == CHANGE_SELECTION) { - list.setSelectedIndex(index); - } - else { - // casting should be safe since the action is only enabled - // for DefaultListSelectionModel - if (lsm instanceof DefaultListSelectionModel) - ((DefaultListSelectionModel)lsm).moveLeadSelectionIndex(index); - } - } - } - - /** - * When scroll down makes selected index the last completely visible - * index. When scroll up makes selected index the first visible index. - * Adjust visible rectangle respect to list's component orientation. - */ - private void adjustScrollPositionIfNecessary(JList list, int index, - int direction) { - if (direction == 0) { - return; - } - Rectangle cellBounds = list.getCellBounds(index, index); - Rectangle visRect = list.getVisibleRect(); - if (cellBounds != null && !visRect.contains(cellBounds)) { - if (list.getLayoutOrientation() == JList.VERTICAL_WRAP && - list.getVisibleRowCount() <= 0) { - // horizontal - if (list.getComponentOrientation().isLeftToRight()) { - if (direction > 0) { - // right for left-to-right - int x =Math.max(0, - cellBounds.x + cellBounds.width - visRect.width); - int startIndex = - list.locationToIndex(new Point(x, cellBounds.y)); - Rectangle startRect = list.getCellBounds(startIndex, - startIndex); - if (startRect.x < x && startRect.x < cellBounds.x) { - startRect.x += startRect.width; - startIndex = - list.locationToIndex(startRect.getLocation()); - startRect = list.getCellBounds(startIndex, - startIndex); - } - cellBounds = startRect; - } - cellBounds.width = visRect.width; - } - else { - if (direction > 0) { - // left for right-to-left - int x = cellBounds.x + visRect.width; - int rightIndex = - list.locationToIndex(new Point(x, cellBounds.y)); - Rectangle rightRect = list.getCellBounds(rightIndex, - rightIndex); - if (rightRect.x + rightRect.width > x && - rightRect.x > cellBounds.x) { - rightRect.width = 0; - } - cellBounds.x = Math.max(0, - rightRect.x + rightRect.width - visRect.width); - cellBounds.width = visRect.width; - } - else { - cellBounds.x += Math.max(0, - cellBounds.width - visRect.width); - // adjust width to fit into visible rectangle - cellBounds.width = Math.min(cellBounds.width, - visRect.width); - } - } - } - else { - // vertical - if (direction > 0 && - (cellBounds.y < visRect.y || - cellBounds.y + cellBounds.height - > visRect.y + visRect.height)) { - //down - int y = Math.max(0, - cellBounds.y + cellBounds.height - visRect.height); - int startIndex = - list.locationToIndex(new Point(cellBounds.x, y)); - Rectangle startRect = list.getCellBounds(startIndex, - startIndex); - if (startRect.y < y && startRect.y < cellBounds.y) { - startRect.y += startRect.height; - startIndex = - list.locationToIndex(startRect.getLocation()); - startRect = - list.getCellBounds(startIndex, startIndex); - } - cellBounds = startRect; - cellBounds.height = visRect.height; - } - else { - // adjust height to fit into visible rectangle - cellBounds.height = Math.min(cellBounds.height, visRect.height); - } - } - list.scrollRectToVisible(cellBounds); - } - } - - private int getNextColumnIndex(JList list, BasicXListUI ui, - int amount) { - if (list.getLayoutOrientation() != JList.VERTICAL) { - int index = adjustIndex(list.getLeadSelectionIndex(), list); - int size = getElementCount(list); - - if (index == -1) { - return 0; - } else if (size == 1) { - // there's only one item so we should select it - return 0; - } else if (ui == null || ui.columnCount <= 1) { - return -1; - } - - int column = ui.convertModelToColumn(index); - int row = ui.convertModelToRow(index); - - column += amount; - if (column >= ui.columnCount || column < 0) { - // No wrapping. - return -1; - } - int maxRowCount = ui.getRowCount(column); - if (row >= maxRowCount) { - return -1; - } - return ui.getModelIndex(column, row); - } - // Won't change the selection. - return -1; - } - - private int getNextIndex(JList list, BasicXListUI ui, int amount) { - int index = adjustIndex(list.getLeadSelectionIndex(), list); - int size = getElementCount(list); - - if (index == -1) { - if (size > 0) { - if (amount > 0) { - index = 0; - } - else { - index = size - 1; - } - } - } else if (size == 1) { - // there's only one item so we should select it - index = 0; - } else if (list.getLayoutOrientation() == JList.HORIZONTAL_WRAP) { - if (ui != null) { - index += ui.columnCount * amount; - } - } else { - index += amount; - } - - return index; - } - } - - - private class Handler implements FocusListener, KeyListener, - ListDataListener, ListSelectionListener, - MouseInputListener, PropertyChangeListener, - BeforeDrag { - // - // KeyListener - // - private String prefix = ""; - private String typedString = ""; - private long lastTime = 0L; - - - /** - * Invoked when a key has been typed. - * - * Moves the keyboard focus to the first element whose prefix matches the - * sequence of alphanumeric keys pressed by the user with delay less - * than value of timeFactor property (or 1000 milliseconds - * if it is not defined). Subsequent same key presses move the keyboard - * focus to the next object that starts with the same letter until another - * key is pressed, then it is treated as the prefix with appropriate number - * of the same letters followed by first typed anothe letter. - */ - public void keyTyped(KeyEvent e) { - JList src = (JList)e.getSource(); - - if (getElementCount() == 0 || e.isAltDown() || e.isControlDown() || e.isMetaDown() || - isNavigationKey(e)) { - // Nothing to select - return; - } - boolean startingFromSelection = true; - - char c = e.getKeyChar(); - - long time = e.getWhen(); - int startIndex = adjustIndex(src.getLeadSelectionIndex(), list); - if (time - lastTime < timeFactor) { - typedString += c; - if((prefix.length() == 1) && (c == prefix.charAt(0))) { - // Subsequent same key presses move the keyboard focus to the next - // object that starts with the same letter. - startIndex++; - } else { - prefix = typedString; - } - } else { - startIndex++; - typedString = "" + c; - prefix = typedString; - } - lastTime = time; - - if (startIndex < 0 || startIndex >= getElementCount()) { - startingFromSelection = false; - startIndex = 0; - } - int index = src.getNextMatch(prefix, startIndex, - Position.Bias.Forward); - if (index >= 0) { - src.setSelectedIndex(index); - src.ensureIndexIsVisible(index); - } else if (startingFromSelection) { // wrap - index = src.getNextMatch(prefix, 0, - Position.Bias.Forward); - if (index >= 0) { - src.setSelectedIndex(index); - src.ensureIndexIsVisible(index); - } - } - } - - /** - * Invoked when a key has been pressed. - * - * Checks to see if the key event is a navigation key to prevent - * dispatching these keys for the first letter navigation. - */ - public void keyPressed(KeyEvent e) { - if ( isNavigationKey(e) ) { - prefix = ""; - typedString = ""; - lastTime = 0L; - } - } - - /** - * Invoked when a key has been released. - * See the class description for {@link KeyEvent} for a definition of - * a key released event. - */ - public void keyReleased(KeyEvent e) { - } - - /** - * Returns whether or not the supplied key event maps to a key that is used for - * navigation. This is used for optimizing key input by only passing non- - * navigation keys to the first letter navigation mechanism. - */ - private boolean isNavigationKey(KeyEvent event) { - InputMap inputMap = list.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); - KeyStroke key = KeyStroke.getKeyStrokeForEvent(event); - - if (inputMap != null && inputMap.get(key) != null) { - return true; - } - return false; - } - - // - // PropertyChangeListener - // - public void propertyChange(PropertyChangeEvent e) { - String propertyName = e.getPropertyName(); - - /* If the JList.model property changes, remove our listener, - * listDataListener from the old model and add it to the new one. - */ - if (propertyName == "model") { - ListModel oldModel = (ListModel)e.getOldValue(); - ListModel newModel = (ListModel)e.getNewValue(); - if (oldModel != null) { - oldModel.removeListDataListener(listDataListener); - } - if (newModel != null) { - newModel.addListDataListener(listDataListener); - } - updateLayoutStateNeeded |= modelChanged; - redrawList(); - } - - /* If the JList.selectionModel property changes, remove our listener, - * listSelectionListener from the old selectionModel and add it to the new one. - */ - else if (propertyName == "selectionModel") { - ListSelectionModel oldModel = (ListSelectionModel)e.getOldValue(); - ListSelectionModel newModel = (ListSelectionModel)e.getNewValue(); - if (oldModel != null) { - oldModel.removeListSelectionListener(listSelectionListener); - } - if (newModel != null) { - newModel.addListSelectionListener(listSelectionListener); - } - updateLayoutStateNeeded |= modelChanged; - redrawList(); - } - else if (propertyName == "cellRenderer") { - updateLayoutStateNeeded |= cellRendererChanged; - redrawList(); - } - else if (propertyName == "font") { - updateLayoutStateNeeded |= fontChanged; - redrawList(); - } - else if (propertyName == "prototypeCellValue") { - updateLayoutStateNeeded |= prototypeCellValueChanged; - redrawList(); - } - else if (propertyName == "fixedCellHeight") { - updateLayoutStateNeeded |= fixedCellHeightChanged; - redrawList(); - } - else if (propertyName == "fixedCellWidth") { - updateLayoutStateNeeded |= fixedCellWidthChanged; - redrawList(); - } - else if (propertyName == "cellRenderer") { - updateLayoutStateNeeded |= cellRendererChanged; - redrawList(); - } - else if (propertyName == "selectionForeground") { - list.repaint(); - } - else if (propertyName == "selectionBackground") { - list.repaint(); - } - else if ("layoutOrientation" == propertyName) { - updateLayoutStateNeeded |= layoutOrientationChanged; - layoutOrientation = list.getLayoutOrientation(); - redrawList(); - } - else if ("visibleRowCount" == propertyName) { - if (layoutOrientation != JList.VERTICAL) { - updateLayoutStateNeeded |= layoutOrientationChanged; - redrawList(); - } - } - else if ("componentOrientation" == propertyName) { - isLeftToRight = list.getComponentOrientation().isLeftToRight(); - updateLayoutStateNeeded |= componentOrientationChanged; - redrawList(); - - InputMap inputMap = getInputMap(JComponent.WHEN_FOCUSED); - SwingUtilities.replaceUIInputMap(list, JComponent.WHEN_FOCUSED, - inputMap); - } else if ("List.isFileList" == propertyName) { - updateIsFileList(); - redrawList(); - } else if ("dropLocation" == propertyName) { - JList.DropLocation oldValue = (JList.DropLocation)e.getOldValue(); - repaintDropLocation(oldValue); - repaintDropLocation(list.getDropLocation()); - } - } - - private void repaintDropLocation(JList.DropLocation loc) { - if (loc == null) { - return; - } - - Rectangle r; - - if (loc.isInsert()) { - r = getDropLineRect(loc); - } else { - r = getCellBounds(list, loc.getIndex()); - } - - if (r != null) { - list.repaint(r); - } - } - - // - // ListDataListener - // - public void intervalAdded(ListDataEvent e) { - updateLayoutStateNeeded = modelChanged; - - int minIndex = Math.min(e.getIndex0(), e.getIndex1()); - int maxIndex = Math.max(e.getIndex0(), e.getIndex1()); - - /* Sync the SelectionModel with the DataModel. - */ - - ListSelectionModel sm = list.getSelectionModel(); - if (sm != null) { - sm.insertIndexInterval(minIndex, maxIndex - minIndex+1, true); - } - - /* Repaint the entire list, from the origin of - * the first added cell, to the bottom of the - * component. - */ - redrawList(); - } - - - public void intervalRemoved(ListDataEvent e) - { - updateLayoutStateNeeded = modelChanged; - - /* Sync the SelectionModel with the DataModel. - */ - - ListSelectionModel sm = list.getSelectionModel(); - if (sm != null) { - sm.removeIndexInterval(e.getIndex0(), e.getIndex1()); - } - - /* Repaint the entire list, from the origin of - * the first removed cell, to the bottom of the - * component. - */ - - redrawList(); - } - - - public void contentsChanged(ListDataEvent e) { - updateLayoutStateNeeded = modelChanged; - redrawList(); - } - - - // - // ListSelectionListener - // - public void valueChanged(ListSelectionEvent e) { - maybeUpdateLayoutState(); - int size = getElementCount(); - int firstIndex = Math.min(size - 1, Math.max(e.getFirstIndex(), 0)); - int lastIndex = Math.min(size - 1, Math.max(e.getLastIndex(), 0)); - - Rectangle bounds = getCellBounds(list, firstIndex, lastIndex); - - if (bounds != null) { - list.repaint(bounds.x, bounds.y, bounds.width, bounds.height); - } - } - - // - // MouseListener - // - public void mouseClicked(MouseEvent e) { - } - - public void mouseEntered(MouseEvent e) { - } - - public void mouseExited(MouseEvent e) { - } - - // Whether or not the mouse press (which is being considered as part - // of a drag sequence) also caused the selection change to be fully - // processed. - private boolean dragPressDidSelection; - - public void mousePressed(MouseEvent e) { - if (SwingXUtilities.shouldIgnore(e, list)) { - return; - } - - boolean dragEnabled = list.getDragEnabled(); - boolean grabFocus = true; - - // different behavior if drag is enabled - if (dragEnabled) { - // PENDING JW: this isn't aware of sorting/filtering - fix! - int row = SwingXUtilities.loc2IndexFileList(list, e.getPoint()); - // if we have a valid row and this is a drag initiating event - if (row != -1 && DragRecognitionSupport.mousePressed(e)) { - dragPressDidSelection = false; - - if (e.isControlDown()) { - // do nothing for control - will be handled on release - // or when drag starts - return; - } else if (!e.isShiftDown() && list.isSelectedIndex(row)) { - // clicking on something that's already selected - // and need to make it the lead now - list.addSelectionInterval(row, row); - return; - } - - // could be a drag initiating event - don't grab focus - grabFocus = false; - - dragPressDidSelection = true; - } - } else { - // When drag is enabled mouse drags won't change the selection - // in the list, so we only set the isAdjusting flag when it's - // not enabled - list.setValueIsAdjusting(true); - } - - if (grabFocus) { - SwingXUtilities.adjustFocus(list); - } - - adjustSelection(e); - } - - private void adjustSelection(MouseEvent e) { - // PENDING JW: this isn't aware of sorting/filtering - fix! - int row = SwingXUtilities.loc2IndexFileList(list, e.getPoint()); - if (row < 0) { - // If shift is down in multi-select, we should do nothing. - // For single select or non-shift-click, clear the selection - if (isFileList && - e.getID() == MouseEvent.MOUSE_PRESSED && - (!e.isShiftDown() || - list.getSelectionMode() == ListSelectionModel.SINGLE_SELECTION)) { - list.clearSelection(); - } - } - else { - int anchorIndex = adjustIndex(list.getAnchorSelectionIndex(), list); - boolean anchorSelected; - if (anchorIndex == -1) { - anchorIndex = 0; - anchorSelected = false; - } else { - anchorSelected = list.isSelectedIndex(anchorIndex); - } - - if (e.isControlDown()) { - if (e.isShiftDown()) { - if (anchorSelected) { - list.addSelectionInterval(anchorIndex, row); - } else { - list.removeSelectionInterval(anchorIndex, row); - if (isFileList) { - list.addSelectionInterval(row, row); - list.getSelectionModel().setAnchorSelectionIndex(anchorIndex); - } - } - } else if (list.isSelectedIndex(row)) { - list.removeSelectionInterval(row, row); - } else { - list.addSelectionInterval(row, row); - } - } else if (e.isShiftDown()) { - list.setSelectionInterval(anchorIndex, row); - } else { - list.setSelectionInterval(row, row); - } - } - } - - public void dragStarting(MouseEvent me) { - if (me.isControlDown()) { - // PENDING JW: this isn't aware of sorting/filtering - fix! - int row = SwingXUtilities.loc2IndexFileList(list, me.getPoint()); - list.addSelectionInterval(row, row); - } - } - - public void mouseDragged(MouseEvent e) { - if (SwingXUtilities.shouldIgnore(e, list)) { - return; - } - - if (list.getDragEnabled()) { - DragRecognitionSupport.mouseDragged(e, this); - return; - } - - if (e.isShiftDown() || e.isControlDown()) { - return; - } - - int row = locationToIndex(list, e.getPoint()); - if (row != -1) { - // 4835633. Dragging onto a File should not select it. - if (isFileList) { - return; - } - Rectangle cellBounds = getCellBounds(list, row, row); - if (cellBounds != null) { - list.scrollRectToVisible(cellBounds); - list.setSelectionInterval(row, row); - } - } - } - - public void mouseMoved(MouseEvent e) { - } - - public void mouseReleased(MouseEvent e) { - if (SwingXUtilities.shouldIgnore(e, list)) { - return; - } - - if (list.getDragEnabled()) { - MouseEvent me = DragRecognitionSupport.mouseReleased(e); - if (me != null) { - SwingXUtilities.adjustFocus(list); - if (!dragPressDidSelection) { - adjustSelection(me); - } - } - } else { - list.setValueIsAdjusting(false); - } - } - - // - // FocusListener - // - protected void repaintCellFocus() - { - int leadIndex = adjustIndex(list.getLeadSelectionIndex(), list); - if (leadIndex != -1) { - Rectangle r = getCellBounds(list, leadIndex, leadIndex); - if (r != null) { - list.repaint(r.x, r.y, r.width, r.height); - } - } - } - - /* The focusGained() focusLost() methods run when the JList - * focus changes. - */ - - public void focusGained(FocusEvent e) { - repaintCellFocus(); - } - - public void focusLost(FocusEvent e) { - repaintCellFocus(); - } - } - - private static int adjustIndex(int index, JList list) { - return index < ((JXList) list).getElementCount() ? index : -1; - } - - private static final TransferHandler defaultTransferHandler = new ListTransferHandler(); - - static class ListTransferHandler extends TransferHandler implements UIResource { - - /** - * Create a Transferable to use as the source for a data transfer. - * - * @param c The component holding the data to be transfered. This - * argument is provided to enable sharing of TransferHandlers by - * multiple components. - * @return The representation of the data to be transfered. - * - */ - protected Transferable createTransferable(JComponent c) { - if (c instanceof JList) { - JList list = (JList) c; - Object[] values = list.getSelectedValues(); - - if (values == null || values.length == 0) { - return null; - } - - StringBuffer plainBuf = new StringBuffer(); - StringBuffer htmlBuf = new StringBuffer(); - - htmlBuf.append("\n\n
                      \n"); - - for (int i = 0; i < values.length; i++) { - Object obj = values[i]; - String val = ((obj == null) ? "" : obj.toString()); - plainBuf.append(val + "\n"); - htmlBuf.append("
                    • " + val + "\n"); - } - - // remove the last newline - plainBuf.deleteCharAt(plainBuf.length() - 1); - htmlBuf.append("
                    \n\n"); - - return new BasicTransferable(plainBuf.toString(), htmlBuf.toString()); - } - - return null; - } - - public int getSourceActions(JComponent c) { - return COPY; - } - - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/core/DragRecognitionSupport.java b/src/main/java/org/jdesktop/swingx/plaf/basic/core/DragRecognitionSupport.java deleted file mode 100644 index 35804fc5c6..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/core/DragRecognitionSupport.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * $Id$ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.plaf.basic.core; - -/* - * @(#)DragRecognitionSupport.java 1.2 05/11/17 - * - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. - * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. - */ - -import org.jdesktop.swingx.SwingXUtilities; - -import javax.swing.*; -import java.awt.dnd.DragSource; -import java.awt.event.MouseEvent; -//import sun.awt.dnd.SunDragSourceContextPeer; -//import sun.awt.AppContext; - -/** - * Drag gesture recognition support for classes that have a - * TransferHandler. The gesture for a drag in this class is a mouse - * press followed by movement by DragSource.getDragThreshold() - * pixels. An instance of this class is maintained per AppContext, and the - * public static methods call into the appropriate instance.

                    - * - * This is a c&p of core (package private) needed for BasicXListUI. It differs from - * core in that references to sun packages have been replaced. - *

                      - *
                    • a static method of SunDragSourceContextPeer has been copied into SwingXUtilities - * and is used here - *
                    • the shared instance of this class is maintained in the UIManager instead of - * per appContext. - *
                    - * - * @author Shannon Hickey - * @version 1.2 11/17/05 - */ -public class DragRecognitionSupport { - private int motionThreshold; - private MouseEvent dndArmedEvent; - private JComponent component; - - /** - * This interface allows us to pass in a handler to mouseDragged, - * so that we can be notified immediately before a drag begins. - */ - public static interface BeforeDrag { - public void dragStarting(MouseEvent me); - } - - /** - * Returns the DragRecognitionSupport for the caller's AppContext. - */ - private static DragRecognitionSupport getDragRecognitionSupport() { -// DragRecognitionSupport support = -// (DragRecognitionSupport)AppContext.getAppContext(). -// get(DragRecognitionSupport.class); -// -// if (support == null) { -// support = new DragRecognitionSupport(); -// AppContext.getAppContext().put(DragRecognitionSupport.class, support); -// } - - DragRecognitionSupport support = (DragRecognitionSupport) - UIManager.get("sharedInstance.dragRecognitionSupport"); - if (support == null) { - support = new DragRecognitionSupport(); - UIManager.put("sharedInstance.dragRecognitionSupport", support); - } - return support; - } - - /** - * Returns whether or not the event is potentially part of a drag sequence. - */ - public static boolean mousePressed(MouseEvent me) { - return ((DragRecognitionSupport)getDragRecognitionSupport()). - mousePressedImpl(me); - } - - /** - * If a dnd recognition has been going on, return the MouseEvent - * that started the recognition. Otherwise, return null. - */ - public static MouseEvent mouseReleased(MouseEvent me) { - return ((DragRecognitionSupport)getDragRecognitionSupport()). - mouseReleasedImpl(me); - } - - /** - * Returns whether or not a drag gesture recognition is ongoing. - */ - public static boolean mouseDragged(MouseEvent me, BeforeDrag bd) { - return ((DragRecognitionSupport)getDragRecognitionSupport()). - mouseDraggedImpl(me, bd); - } - - private void clearState() { - dndArmedEvent = null; - component = null; - } - - private int mapDragOperationFromModifiers(MouseEvent me, - TransferHandler th) { - - if (th == null || !SwingUtilities.isLeftMouseButton(me)) { - return TransferHandler.NONE; - } - // PENDING JW: c'p from SunDragSourceContextPeer - return SwingXUtilities. - convertModifiersToDropAction(me.getModifiersEx(), - th.getSourceActions(component)); - } - - /** - * Returns whether or not the event is potentially part of a drag sequence. - */ - private boolean mousePressedImpl(MouseEvent me) { - component = (JComponent)me.getSource(); - - if (mapDragOperationFromModifiers(me, component.getTransferHandler()) - != TransferHandler.NONE) { - - motionThreshold = DragSource.getDragThreshold(); - dndArmedEvent = me; - return true; - } - - clearState(); - return false; - } - - /** - * If a dnd recognition has been going on, return the MouseEvent - * that started the recognition. Otherwise, return null. - */ - private MouseEvent mouseReleasedImpl(MouseEvent me) { - /* no recognition has been going on */ - if (dndArmedEvent == null) { - return null; - } - - MouseEvent retEvent = null; - - if (me.getSource() == component) { - retEvent = dndArmedEvent; - } // else component has changed unexpectedly, so return null - - clearState(); - return retEvent; - } - - /** - * Returns whether or not a drag gesture recognition is ongoing. - */ - private boolean mouseDraggedImpl(MouseEvent me, BeforeDrag bd) { - /* no recognition is in progress */ - if (dndArmedEvent == null) { - return false; - } - - /* component has changed unexpectedly, so bail */ - if (me.getSource() != component) { - clearState(); - return false; - } - - int dx = Math.abs(me.getX() - dndArmedEvent.getX()); - int dy = Math.abs(me.getY() - dndArmedEvent.getY()); - if ((dx > motionThreshold) || (dy > motionThreshold)) { - TransferHandler th = component.getTransferHandler(); - int action = mapDragOperationFromModifiers(me, th); - if (action != TransferHandler.NONE) { - /* notify the BeforeDrag instance */ - if (bd != null) { - bd.dragStarting(dndArmedEvent); - } - th.exportAsDrag(component, dndArmedEvent, action); - clearState(); - } - } - - return true; - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/core/LazyActionMap.java b/src/main/java/org/jdesktop/swingx/plaf/basic/core/LazyActionMap.java deleted file mode 100644 index 036d8e2ff5..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/core/LazyActionMap.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * $Id$ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.plaf.basic.core; - - -import javax.swing.*; -import javax.swing.plaf.ActionMapUIResource; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -/** - * An ActionMap that populates its contents as necessary. The - * contents are populated by invoking the loadActionMap - * method on the passed in Object. - * - * @version 1.6, 11/17/05 - * @author Scott Violet - */ -public class LazyActionMap extends ActionMapUIResource { - /** - * Object to invoke loadActionMap on. This may be - * a Class object. - */ - private transient Object _loader; - - /** - * Installs an ActionMap that will be populated by invoking the - * loadActionMap method on the specified Class - * when necessary. - *

                    - * This should be used if the ActionMap can be shared. - * - * @param c JComponent to install the ActionMap on. - * @param loaderClass Class object that gets loadActionMap invoked - * on. - * @param defaultsKey Key to use to defaults table to check for - * existing map and what resulting Map will be registered on. - */ - public static void installLazyActionMap(JComponent c, Class loaderClass, - String defaultsKey) { - ActionMap map = (ActionMap)UIManager.get(defaultsKey); - if (map == null) { - map = new LazyActionMap(loaderClass); - UIManager.getLookAndFeelDefaults().put(defaultsKey, map); - } - SwingUtilities.replaceUIActionMap(c, map); - } - - /** - * Returns an ActionMap that will be populated by invoking the - * loadActionMap method on the specified Class - * when necessary. - *

                    - * This should be used if the ActionMap can be shared. - * - * @param c JComponent to install the ActionMap on. - * @param loaderClass Class object that gets loadActionMap invoked - * on. - * @param defaultsKey Key to use to defaults table to check for - * existing map and what resulting Map will be registered on. - */ - static ActionMap getActionMap(Class loaderClass, - String defaultsKey) { - ActionMap map = (ActionMap)UIManager.get(defaultsKey); - if (map == null) { - map = new LazyActionMap(loaderClass); - UIManager.getLookAndFeelDefaults().put(defaultsKey, map); - } - return map; - } - - - private LazyActionMap(Class loader) { - _loader = loader; - } - - public void put(Action action) { - put(action.getValue(Action.NAME), action); - } - - public void put(Object key, Action action) { - loadIfNecessary(); - super.put(key, action); - } - - public Action get(Object key) { - loadIfNecessary(); - return super.get(key); - } - - public void remove(Object key) { - loadIfNecessary(); - super.remove(key); - } - - public void clear() { - loadIfNecessary(); - super.clear(); - } - - public Object[] keys() { - loadIfNecessary(); - return super.keys(); - } - - public int size() { - loadIfNecessary(); - return super.size(); - } - - public Object[] allKeys() { - loadIfNecessary(); - return super.allKeys(); - } - - public void setParent(ActionMap map) { - loadIfNecessary(); - super.setParent(map); - } - - private void loadIfNecessary() { - if (_loader != null) { - Object loader = _loader; - - _loader = null; - Class klass = (Class)loader; - try { - Method method = klass.getDeclaredMethod("loadActionMap", - new Class[] { LazyActionMap.class }); - method.invoke(klass, new Object[] { this }); - } catch (NoSuchMethodException nsme) { - assert false : "LazyActionMap unable to load actions " + - klass; - } catch (IllegalAccessException iae) { - assert false : "LazyActionMap unable to load actions " + - iae; - } catch (InvocationTargetException ite) { - assert false : "LazyActionMap unable to load actions " + - ite; - } catch (IllegalArgumentException iae) { - assert false : "LazyActionMap unable to load actions " + - iae; - } - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/core/ListSortUI.java b/src/main/java/org/jdesktop/swingx/plaf/basic/core/ListSortUI.java deleted file mode 100644 index 0291d5b45b..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/core/ListSortUI.java +++ /dev/null @@ -1,503 +0,0 @@ -/* - * $Id$ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.plaf.basic.core; - -import org.jdesktop.swingx.JXList; -import org.jdesktop.swingx.SwingXUtilities; -import org.jdesktop.swingx.util.Contract; - -import javax.swing.*; -import javax.swing.event.ListDataEvent; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.RowSorterEvent; -import javax.swing.event.RowSorterListener; - -/** - * ListSortUI provides support for managing the synchronization between - * RowSorter, SelectionModel and ListModel if a JXList is sortable.

                    - * - * This implementation is an adaption of JTable.SortManager fit to the - * needs of a ListUI. In contrast to JTable tradition, the ui delegate has - * full control about listening to model/selection changes and updating - * the list accordingly. So the role of this class is that of a helper to the ListUI - * (vs. as a helper of the JTable). - *

                    - * It's up to the ListUI to - * listen to model/selection and propagate the notification to this class if - * a sorter is installed, but still do the usual updates (layout, repaint) itself. - * On the other hand, listening to the sorter and updating list state accordingly - * is completely done by this. - * - */ -public final class ListSortUI { - private RowSorter sorter; - private JXList list; - - // Selection, in terms of the model. This is lazily created - // as needed. - private ListSelectionModel modelSelection; - private int modelLeadIndex; - // Set to true while in the process of changing the selection. - // If this is true the selection change is ignored. - private boolean syncingSelection; - // Temporary cache of selection, in terms of model. This is only used - // if we don't need the full weight of modelSelection. - private int[] lastModelSelection; - private boolean sorterChanged; - private boolean ignoreSortChange; - private RowSorterListener sorterListener; - - /** - * Intanstiates a SortUI on the list which has the given RowSorter. - * - * @param list the list to control, must not be null - * @param sorter the rowSorter of the list, must not be null - * @throws NullPointerException if either the list or the sorter is null - * @throws IllegalStateException if the sorter is not the sorter installed - * on the list - */ - public ListSortUI(JXList list, RowSorter sorter) { - this.sorter = Contract.asNotNull(sorter, "RowSorter must not be null"); - this.list = Contract.asNotNull(list, "list must not be null"); - if (sorter != list.getRowSorter()) throw - new IllegalStateException("sorter must be same as the one on list"); - sorterListener = createRowSorterListener(); - sorter.addRowSorterListener(sorterListener); - } - - /** - * Disposes any resources used by this SortManager. - * Note: this instance must not be used after dispose! - */ - public void dispose() { - if (sorter != null) { - sorter.removeRowSorterListener(sorterListener); - } - sorter = null; - list = null; - } - -//----------------------methods called by listeners - - /** - * Called after notification from ListModel. - * @param e the change event from the listModel. - */ - public void modelChanged(ListDataEvent e) { - ModelChange change = new ModelChange(e); - prepareForChange(change); - notifySorter(change); - if (change.type != ListDataEvent.CONTENTS_CHANGED) { - // If the Sorter is unsorted we will not have received - // notification, force treating insert/delete as a change. - sorterChanged = true; - } - processChange(change); - } - - /** - * Called after notification from selectionModel. - * - * Invoked when the selection, on the view, has changed. - */ - public void viewSelectionChanged(ListSelectionEvent e) { - if (!syncingSelection && modelSelection != null) { - modelSelection = null; - } - } - - /** - * Called after notification from RowSorter. - * - * @param e RowSorter event of type SORTED. - */ - protected void sortedChanged(RowSorterEvent e) { - sorterChanged = true; - if (!ignoreSortChange) { - prepareForChange(e); - processChange(null); - // PENDING Jw: this is fix of 1161-swingx - not updated after setting - // rowFilter - // potentially costly? but how to distinguish a mere sort from a - // filterchanged? (only the latter requires a revalidate) - // first fix had only revalidate/repaint but was not - // good enough, see #1261-swingx - no items visible - // after setting rowFilter - // need to invalidate the cell size cache which might be needed - // even after plain sorting as the indi-sizes are now at different - // positions - list.invalidateCellSizeCache(); - } - } - - -//--------------------- prepare change, that is cache selection if needed - /** - * Invoked when the RowSorter has changed. - * Updates the internal cache of the selection based on the change. - * - * @param sortEvent the notification - * @throws NullPointerException if the given event is null. - */ - private void prepareForChange(RowSorterEvent sortEvent) { - Contract.asNotNull(sortEvent, "sorter event not null"); - // sort order changed. If modelSelection is null and filtering - // is enabled we need to cache the selection in terms of the - // underlying model, this will allow us to correctly restore - // the selection even if rows are filtered out. - if (modelSelection == null && - sorter.getViewRowCount() != sorter.getModelRowCount()) { - modelSelection = new DefaultListSelectionModel(); - ListSelectionModel viewSelection = getViewSelectionModel(); - int min = viewSelection.getMinSelectionIndex(); - int max = viewSelection.getMaxSelectionIndex(); - int modelIndex; - for (int viewIndex = min; viewIndex <= max; viewIndex++) { - if (viewSelection.isSelectedIndex(viewIndex)) { - modelIndex = convertRowIndexToModel( - sortEvent, viewIndex); - if (modelIndex != -1) { - modelSelection.addSelectionInterval( - modelIndex, modelIndex); - } - } - } - modelIndex = convertRowIndexToModel(sortEvent, - viewSelection.getLeadSelectionIndex()); - SwingXUtilities.setLeadAnchorWithoutSelection( - modelSelection, modelIndex, modelIndex); - } else if (modelSelection == null) { - // Sorting changed, haven't cached selection in terms - // of model and no filtering. Temporarily cache selection. - cacheModelSelection(sortEvent); - } - } - /** - * Invoked when the list model has changed. This is invoked prior to - * notifying the sorter of the change. - * Updates the internal cache of the selection based on the change. - * - * @param change the notification - * @throws NullPointerException if the given event is null. - */ - private void prepareForChange(ModelChange change) { - Contract.asNotNull(change, "table event not null"); - if (change.allRowsChanged) { - // All the rows have changed, chuck any cached selection. - modelSelection = null; - } else if (modelSelection != null) { - // Table changed, reflect changes in cached selection model. - switch (change.type) { - // JW: core incorrectly uses change.endModelIndex! - // sneaked into here via c&p - // reported as #1536-swingx - case ListDataEvent.INTERVAL_REMOVED: - modelSelection.removeIndexInterval(change.startModelIndex, - // Note: api difference between remove vs. insert - // nothing do do here! - change.endModelIndex); - break; - case ListDataEvent.INTERVAL_ADDED: - modelSelection.insertIndexInterval(change.startModelIndex, - // insert is tested - change.length, true); - break; - default: - break; - } - } else { - // list changed, but haven't cached rows, temporarily - // cache them. - cacheModelSelection(null); - } - } - - private void cacheModelSelection(RowSorterEvent sortEvent) { - lastModelSelection = convertSelectionToModel(sortEvent); - modelLeadIndex = convertRowIndexToModel(sortEvent, - getViewSelectionModel().getLeadSelectionIndex()); - } - -//----------------------- process change, that is restore selection if needed - /** - * Inovked when either the table has changed or the sorter has changed - * and after the sorter has been notified. If necessary this will - * reapply the selection and variable row heights. - */ - private void processChange(ModelChange change) { - if (change != null && change.allRowsChanged) { - allChanged(); - getViewSelectionModel().clearSelection(); - } else if (sorterChanged) { - restoreSelection(change); - } - } - - /** - * Restores the selection from that in terms of the model. - */ - private void restoreSelection(ModelChange change) { - syncingSelection = true; - if (lastModelSelection != null) { - restoreSortingSelection(lastModelSelection, - modelLeadIndex, change); - lastModelSelection = null; - } else if (modelSelection != null) { - ListSelectionModel viewSelection = getViewSelectionModel(); - viewSelection.setValueIsAdjusting(true); - viewSelection.clearSelection(); - int min = modelSelection.getMinSelectionIndex(); - int max = modelSelection.getMaxSelectionIndex(); - int viewIndex; - for (int modelIndex = min; modelIndex <= max; modelIndex++) { - if (modelSelection.isSelectedIndex(modelIndex)) { - viewIndex = sorter.convertRowIndexToView(modelIndex); - if (viewIndex != -1) { - viewSelection.addSelectionInterval(viewIndex, - viewIndex); - } - } - } - // Restore the lead - int viewLeadIndex = modelSelection.getLeadSelectionIndex(); - if (viewLeadIndex != -1) { - viewLeadIndex = sorter.convertRowIndexToView(viewLeadIndex); - } - SwingXUtilities.setLeadAnchorWithoutSelection( - viewSelection, viewLeadIndex, viewLeadIndex); - viewSelection.setValueIsAdjusting(false); - } - syncingSelection = false; - } - - /** - * Restores the selection after a model event/sort order changes. - * All coordinates are in terms of the model. - */ - private void restoreSortingSelection(int[] selection, int lead, - ModelChange change) { - // Convert the selection from model to view - for (int i = selection.length - 1; i >= 0; i--) { - selection[i] = convertRowIndexToView(change, selection[i]); - } - lead = convertRowIndexToView(change, lead); - - // Check for the common case of no change in selection for 1 row - if (selection.length == 0 || - (selection.length == 1 && selection[0] == list.getSelectedIndex())) { - return; - } - ListSelectionModel selectionModel = getViewSelectionModel(); - // And apply the new selection - selectionModel.setValueIsAdjusting(true); - selectionModel.clearSelection(); - for (int i = selection.length - 1; i >= 0; i--) { - if (selection[i] != -1) { - selectionModel.addSelectionInterval(selection[i], - selection[i]); - } - } - SwingXUtilities.setLeadAnchorWithoutSelection( - selectionModel, lead, lead); - selectionModel.setValueIsAdjusting(false); - } - -//------------------- row index conversion methods - /** - * Converts a model index to view index. This is called when the - * sorter or model changes and sorting is enabled. - * - * @param change describes the TableModelEvent that initiated the change; - * will be null if called as the result of a sort - */ - private int convertRowIndexToView(ModelChange change, int modelIndex) { - if (modelIndex < 0) { - return -1; - } -// Contract.asNotNull(change, "change must not be null?"); - if (change != null && modelIndex >= change.startModelIndex) { - if (change.type == ListDataEvent.INTERVAL_ADDED) { - if (modelIndex + change.length >= change.modelRowCount) { - return -1; - } - return sorter.convertRowIndexToView( - modelIndex + change.length); - } - else if (change.type == ListDataEvent.INTERVAL_REMOVED) { - if (modelIndex <= change.endModelIndex) { - // deleted - return -1; - } - else { - if (modelIndex - change.length >= change.modelRowCount) { - return -1; - } - return sorter.convertRowIndexToView( - modelIndex - change.length); - } - } - // else, updated - } - if (modelIndex >= sorter.getModelRowCount()) { - return -1; - } - return sorter.convertRowIndexToView(modelIndex); - } - - - private int convertRowIndexToModel(RowSorterEvent e, int viewIndex) { - // JW: the event is null if the selection is cached in prepareChange - // after model notification. Then the conversion from the - // sorter is still valid as the prepare is called before - // notifying the sorter. - if (e != null) { - if (e.getPreviousRowCount() == 0) { - return viewIndex; - } - // range checking handled by RowSorterEvent - return e.convertPreviousRowIndexToModel(viewIndex); - } - // Make sure the viewIndex is valid - if (viewIndex < 0 || viewIndex >= sorter.getViewRowCount()) { - return -1; - } - return sorter.convertRowIndexToModel(viewIndex); - } - - /** - * Converts the selection to model coordinates. This is used when - * the model changes or the sorter changes. - */ - private int[] convertSelectionToModel(RowSorterEvent e) { - int[] selection = list.getSelectedIndices(); - for (int i = selection.length - 1; i >= 0; i--) { - selection[i] = convertRowIndexToModel(e, selection[i]); - } - return selection; - } - -//------------------ - /** - * Notifies the sorter of a change in the underlying model. - */ - private void notifySorter(ModelChange change) { - try { - ignoreSortChange = true; - sorterChanged = false; - if (change.allRowsChanged) { - sorter.allRowsChanged(); - } else { - switch (change.type) { - case ListDataEvent.CONTENTS_CHANGED: - sorter.rowsUpdated(change.startModelIndex, - change.endModelIndex); - break; - case ListDataEvent.INTERVAL_ADDED: - sorter.rowsInserted(change.startModelIndex, - change.endModelIndex); - break; - case ListDataEvent.INTERVAL_REMOVED: - sorter.rowsDeleted(change.startModelIndex, - change.endModelIndex); - break; - } - } - } finally { - ignoreSortChange = false; - } - } - - - private ListSelectionModel getViewSelectionModel() { - return list.getSelectionModel(); - } - /** - * Invoked when the underlying model has completely changed. - */ - private void allChanged() { - modelLeadIndex = -1; - modelSelection = null; - } - -//------------------- implementing listeners - - /** - * Creates and returns a RowSorterListener. This implementation - * calls sortedChanged if the event is of type SORTED. - * - * @return rowSorterListener to install on sorter. - */ - protected RowSorterListener createRowSorterListener() { - RowSorterListener l = new RowSorterListener() { - - @Override - public void sorterChanged(RowSorterEvent e) { - if (e.getType() == RowSorterEvent.Type.SORTED) { - sortedChanged(e); - } - } - - }; - return l; - } - /** - * ModelChange is used when sorting to restore state, it corresponds - * to data from a TableModelEvent. The values are precalculated as - * they are used extensively.

                    - * - * PENDING JW: this is not yet fully adapted to ListDataEvent. - */ - final static class ModelChange { - // JW: if we received a dataChanged, there _is no_ notion - // of end/start/length of change - // Starting index of the change, in terms of the model, -1 if dataChanged - int startModelIndex; - - // Ending index of the change, in terms of the model, -1 if dataChanged - int endModelIndex; - - // Length of the change (end - start + 1), - 1 if dataChanged - int length; - - // Type of change - int type; - - // Number of rows in the model - int modelRowCount; - - - // True if the event indicates all the contents have changed - boolean allRowsChanged; - - public ModelChange(ListDataEvent e) { - type = e.getType(); - modelRowCount = ((ListModel) e.getSource()).getSize(); - startModelIndex = e.getIndex0(); - endModelIndex = e.getIndex1(); - allRowsChanged = startModelIndex < 0; - length = allRowsChanged ? -1 : endModelIndex - startModelIndex + 1; - } - } - - -} - diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/package-info.java b/src/main/java/org/jdesktop/swingx/plaf/basic/package-info.java deleted file mode 100644 index c6e2ad56f0..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/package-info.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -/** - * Provides basic implementation of pluggable look-and-feel for SwingX components together with a - * mechanism to support custom component look-and-feels. - */ -package org.jdesktop.swingx.plaf.basic; - diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker.properties deleted file mode 100644 index d63b0a1f41..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker.properties +++ /dev/null @@ -1,11 +0,0 @@ -# -# Text, formatting for JXDatePicker -# -JXDatePicker.linkFormat=Today is {0,date} -# Issue #945-swingx: force a default fallback here not the right thing to -# for locales which do not have a properties file. In that case the -# language defaults provide a better fit -#JXDatePicker.longFormat=EEE MM/dd/yyyy -#JXDatePicker.mediumFormat=MM/dd/yy -#JXDatePicker.shortFormat=MM/dd -JXDatePicker.numColumns=10 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_cs.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_cs.properties deleted file mode 100644 index 8f557508d0..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_cs.properties +++ /dev/null @@ -1,9 +0,0 @@ -# -# Text, formatting for JXDatePicker -# -JXDatePicker.linkFormat=Dnes je {0,date, d. MMMM yyyy} -JXDatePicker.longFormat=EEE d.MM.yyyy -#czech grammar vs application admits more variants to use -JXDatePicker.mediumFormat=d.M.yyyy -JXDatePicker.shortFormat=d.M. -JXDatePicker.numColumns=10 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_da.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_da.properties deleted file mode 100644 index d3db54bacc..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_da.properties +++ /dev/null @@ -1,12 +0,0 @@ -# -# Text, formatting for JXDatePicker -# -# Note: Danish official date formating allows for both a traditional -# and the ISO version. Since de-facto is to use the traditional -# that's what JXDatePicker will use default. -# -JXDatePicker.linkFormat=Dags dato {0,date, dd MMMM yyyy} -JXDatePicker.longFormat=EEE dd-MM-yyyy -JXDatePicker.mediumFormat=dd-MM-yyyy -JXDatePicker.shortFormat=dd/MM -JXDatePicker.numColumns=10 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_de.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_de.properties deleted file mode 100644 index 80963be6bf..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_de.properties +++ /dev/null @@ -1,8 +0,0 @@ -# -# Text, formatting for JXDatePicker -# -JXDatePicker.linkFormat=Heute ist der{0,date, dd. MMMM yyyy} -JXDatePicker.longFormat=dd.MM.yyyy -JXDatePicker.mediumFormat=dd.MM.yyyy -JXDatePicker.shortFormat=dd.MM -JXDatePicker.numColumns=10 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_en.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_en.properties deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_en_GB.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_en_GB.properties deleted file mode 100644 index f8b9703039..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_en_GB.properties +++ /dev/null @@ -1,11 +0,0 @@ -# -# Text, formatting for JXDatePicker -# -# - -JXDatePicker.linkFormat=Today is {0,date, dd MMMM yyyy} -JXDatePicker.longFormat=EEE dd/MM/yyyy -JXDatePicker.mediumFormat=dd/MM/yyyy -JXDatePicker.shortFormat=dd/MM -JXDatePicker.numColumns=10 - diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_en_US.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_en_US.properties deleted file mode 100644 index be1346cbb2..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_en_US.properties +++ /dev/null @@ -1,8 +0,0 @@ -# -# Text, formatting for JXDatePicker -# -JXDatePicker.linkFormat=Today is {0,date, dd MMMM yyyy} -JXDatePicker.longFormat=EEE MM/dd/yyyy -JXDatePicker.mediumFormat=MM/dd/yy -JXDatePicker.shortFormat=MM/dd -JXDatePicker.numColumns=10 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_es.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_es.properties deleted file mode 100644 index 34a01e3ef5..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_es.properties +++ /dev/null @@ -1,4 +0,0 @@ -# -# Text, formatting for JXDatePicker -# -JXDatePicker.linkFormat=Hoy es {0,date, dd MMMM yyyy} diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_fr.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_fr.properties deleted file mode 100644 index 54087d804b..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_fr.properties +++ /dev/null @@ -1,8 +0,0 @@ -# -# Text, formatting for JXDatePicker -# -JXDatePicker.linkFormat=Aujourd''hui, nous sommes le {0,date, dd MMMM yyyy} -JXDatePicker.longFormat=EEE dd/MM/yyyy -JXDatePicker.mediumFormat=dd/MM/yyyy -JXDatePicker.shortFormat=dd/MM -JXDatePicker.numColumns=10 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_it.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_it.properties deleted file mode 100644 index 41b633de93..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_it.properties +++ /dev/null @@ -1,8 +0,0 @@ -# -# Text, formatting for JXDatePicker -# -JXDatePicker.linkFormat=Oggi \u00E8 il {0,date, dd MMMM yyyy} -JXDatePicker.longFormat=EEE dd/MM/yyyy -JXDatePicker.mediumFormat=dd/MM/yyyy -JXDatePicker.shortFormat=dd/MM -JXDatePicker.numColumns=10 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_nl.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_nl.properties deleted file mode 100644 index 148aa7d614..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_nl.properties +++ /dev/null @@ -1,8 +0,0 @@ -# -# Text, formatting for JXDatePicker -# -JXDatePicker.linkFormat=Vandaag is het {0,date, dd MMMM yyyy} -JXDatePicker.longFormat=EEE yyyy-MM-dd -JXDatePicker.mediumFormat=yyyy-MM-dd -JXDatePicker.shortFormat=yyyy-MM-dd -JXDatePicker.numColumns=10 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_pl_PL.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_pl_PL.properties deleted file mode 100644 index f8408bbcf3..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_pl_PL.properties +++ /dev/null @@ -1,8 +0,0 @@ -# -# Text, formatting for JXDatePicker -# -JXDatePicker.linkFormat=Dzisiaj jest {0,date, dd MMMM yyyy} -JXDatePicker.longFormat=EEE dd.MM.yyyy -JXDatePicker.mediumFormat=dd.MM.yy -JXDatePicker.shortFormat=MM/dd -JXDatePicker.numColumns=10 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_pt_BR.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_pt_BR.properties deleted file mode 100644 index 9176d39dc9..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_pt_BR.properties +++ /dev/null @@ -1,8 +0,0 @@ -# -# Text, formatting for JXDatePicker -# -JXDatePicker.linkFormat=Hoje \u00E9 {0,date, dd MMMM yyyy} -JXDatePicker.longFormat=EEE dd/MM/yyyy -JXDatePicker.mediumFormat=dd/MM/yyyy -JXDatePicker.shortFormat=dd/MM -JXDatePicker.numColumns=10 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_sv.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_sv.properties deleted file mode 100644 index 62c6068ade..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/DatePicker_sv.properties +++ /dev/null @@ -1,8 +0,0 @@ -# -# Text, formatting for JXDatePicker -# -JXDatePicker.linkFormat=Idag \u00E4r {0,date, yyyy MMMM dd} -JXDatePicker.longFormat=EEE yyyy/MM/dd -JXDatePicker.mediumFormat=yyyy/MM/dd -JXDatePicker.shortFormat=MM/dd -JXDatePicker.numColumns=10 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane.properties deleted file mode 100644 index 7989b7cc10..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane.properties +++ /dev/null @@ -1,9 +0,0 @@ -# -# JXErrorPanel properties -# -JXErrorPane.details_expand_text=Details >> -JXErrorPane.details_contract_text=Details << -JXErrorPane.ok_button_text=Close -JXErrorPane.fatal_button_text=Exit Application -JXErrorPane.report_button_text=Report Error -JXErrorPane.copy_to_clipboard_button_text=Copy to Clipboard diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_cs.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_cs.properties deleted file mode 100644 index 8ab1c97948..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_cs.properties +++ /dev/null @@ -1,9 +0,0 @@ -# -# JXErrorPanel properties -# -JXErrorPane.details_expand_text=Detaily >> -JXErrorPane.details_contract_text=Detaily << -JXErrorPane.ok_button_text=Zav\u0159\u00edt -JXErrorPane.fatal_button_text=Ukon\u010dit Aplikaci -JXErrorPane.report_button_text=Ozn\u00e1mit Chybu -JXErrorPane.copy_to_clipboard_button_text=Zkop\u00edrovat do Schr\u00e1nky diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_da.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_da.properties deleted file mode 100644 index e3c998e619..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_da.properties +++ /dev/null @@ -1,9 +0,0 @@ -# -# JXErrorPanel properties -# -JXErrorPane.details_expand_text=Detaljer >> -JXErrorPane.details_contract_text=Detaljer << -JXErrorPane.ok_button_text=Luk -JXErrorPane.fatal_button_text=Afslut program -JXErrorPane.report_button_text=Rapporter fejl -JXErrorPane.copy_to_clipboard_button_text=Kopier til udklipsholder diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_de.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_de.properties deleted file mode 100644 index 3c11b1ca72..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_de.properties +++ /dev/null @@ -1,9 +0,0 @@ -# -# JXErrorPanel properties -# -JXErrorPane.details_expand_text=Details >> -JXErrorPane.details_contract_text=Details << -JXErrorPane.ok_button_text=Schliessen -JXErrorPane.fatal_button_text=Anwendung beenden -JXErrorPane.report_button_text=Fehler berichten -JXErrorPane.copy_to_clipboard_button_text=In Zwischenablage kopieren diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_en.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_en.properties deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_es.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_es.properties deleted file mode 100644 index 8e1de5d76d..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_es.properties +++ /dev/null @@ -1,10 +0,0 @@ -# spanish properties for swingx -# JXErrorPanel -# @author: Sergio Samayoa - -JXErrorPane.details_expand_text=Detalles >> -JXErrorPane.details_contract_text=Detalles << -JXErrorPane.ok_button_text=Cerrar -JXErrorPane.fatal_button_text=Salir de la aplicaci\u00F3n -JXErrorPane.report_button_text=Reportar error -JXErrorPane.copy_to_clipboard_button_text=Copiar al portapapeles diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_fr.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_fr.properties deleted file mode 100644 index 0fa3b553cc..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_fr.properties +++ /dev/null @@ -1,9 +0,0 @@ -# -# JXErrorPanel properties -# -JXErrorPane.details_expand_text=Détails >> -JXErrorPane.details_contract_text=Détails << -JXErrorPane.ok_button_text=Fermer -JXErrorPane.fatal_button_text=Quitter l'application -JXErrorPane.report_button_text=Rapport d'erreur -JXErrorPane.copy_to_clipboard_button_text=Copier dans le presse-papiers diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_it.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_it.properties deleted file mode 100644 index 8ed51948b2..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_it.properties +++ /dev/null @@ -1,9 +0,0 @@ -# -# JXErrorPanel properties -# -JXErrorPane.details_expand_text=Dettagli >> -JXErrorPane.details_contract_text=Dettagli << -JXErrorPane.ok_button_text=Chiudi -JXErrorPane.fatal_button_text=Esci dall'Applicazione -JXErrorPane.report_button_text=Segnala Errore -JXErrorPane.copy_to_clipboard_button_text=Copia negli Appunti diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_nl.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_nl.properties deleted file mode 100644 index 581a742ae7..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_nl.properties +++ /dev/null @@ -1,9 +0,0 @@ -# -# JXErrorPanel properties -# -JXErrorPane.details_expand_text=Details >> -JXErrorPane.details_contract_text=Details << -JXErrorPane.ok_button_text=Sluiten -JXErrorPane.fatal_button_text=Sluit Applicatie -JXErrorPane.report_button_text=Rapporteer Fout -JXErrorPane.copy_to_clipboard_button_text=Kopieer naar klembord diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_pl.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_pl.properties deleted file mode 100644 index 9158625924..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_pl.properties +++ /dev/null @@ -1,9 +0,0 @@ -# -# JXErrorPanel properties -# -JXErrorPane.details_expand_text=Szczeg\u00F3\u0142y >> -JXErrorPane.details_contract_text=Szczeg\u00F3\u0142y << -JXErrorPane.ok_button_text=Zamknij -JXErrorPane.fatal_button_text=Zako\u0144cz aplikacje -JXErrorPane.report_button_text=Raportuj b\u0142\u0105d -JXErrorPane.copy_to_clipboard_button_text=Kopiuj do schowka diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_pt_BR.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_pt_BR.properties deleted file mode 100644 index 42c5e348ad..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_pt_BR.properties +++ /dev/null @@ -1,9 +0,0 @@ -# -# JXErrorPanel properties -# -JXErrorPane.details_expand_text=Detalhes >> -JXErrorPane.details_contract_text=Detalhes << -JXErrorPane.ok_button_text=Fechar -JXErrorPane.fatal_button_text=Sair da Aplica\u00E7\u00E3o -JXErrorPane.report_button_text=Reportar Erro -JXErrorPane.copy_to_clipboard_button_text=Copia para a \u00E1rea de Transfer\u00EAncia diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_sv.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_sv.properties deleted file mode 100644 index c28a689dd7..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/ErrorPane_sv.properties +++ /dev/null @@ -1,9 +0,0 @@ -# -# JXErrorPanel properties -# -JXErrorPane.details_expand_text=Detaljer >> -JXErrorPane.details_contract_text=Detaljer << -JXErrorPane.ok_button_text=St\u00E4ng -JXErrorPane.fatal_button_text=Avsluta applikation -JXErrorPane.report_button_text=Rapportera fel -JXErrorPane.copy_to_clipboard_button_text=Kopiera till Urklipp diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane.properties deleted file mode 100644 index 811b87e9f1..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane.properties +++ /dev/null @@ -1,26 +0,0 @@ -# Strings used by JXLoginDialog and JXLoginPane -JXLoginPane.okString=OK -JXLoginPane.okString.mnemonic=O -JXLoginPane.cancelString=Cancel -JXLoginPane.cancelString.mnemonic=C -JXLoginPane.stopString=Stop Login -JXLoginPane.stopString.mnemonic=S -JXLoginPane.promptString=Enter your user name and password -JXLoginPane.serverString=Server -JXLoginPane.nameString=User Name -JXLoginPane.loginString=Login -JXLoginPane.bannerString=Login -JXLoginPane.titleString=Login -JXLoginPane.passwordString=Password -JXLoginPane.rememberUserString=Remember Me -JXLoginPane.rememberPasswordString=Remember Password -JXLoginPane.loggingInString=Please wait, logging in... -JXLoginPane.failedString=Login failed; invalid user name/password combination. -JXLoginPane.enterUserNamePassword=Enter your user name and password -JXLoginPane.pleaseWait=Please wait, logging in... -JXLoginPane.cancelWait=Canceling login, please wait... -JXLoginPane.cancelLogin=Stop login -JXLoginPane.errorMessage=Couldn't log in

                    \ - Check your user name and password. Check to see if \ - Caps Lock is turned on. -JXLoginPane.capsOnWarning=CapsLock seems to be ON! diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_cs.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_cs.properties deleted file mode 100644 index 8817f7804f..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_cs.properties +++ /dev/null @@ -1,26 +0,0 @@ -#Czech resource bundle created by Vity (vitywap@seznam.cz) 08/2007 -# Strings used by JXLoginDialog and JXLoginPanel -JXLoginPane.okString=OK -JXLoginPane.okString.mnemonic=O -JXLoginPane.cancelString=Storno -JXLoginPane.cancelString.mnemonic=S -JXLoginPane.stopString=P\u0159eru\u0161it p\u0159ihla\u0161ov\u00e1n\u00ed -JXLoginPane.stopString.mnemonic=P -JXLoginPane.promptString=Zadejte sv\u00e9 u\u017eivatelsk\u00e9 jm\u00e9no a heslo -JXLoginPane.serverString=Server -JXLoginPane.nameString=U\u017eivatelsk\u00e9 Jm\u00e9no -JXLoginPane.loginString=P\u0159ihl\u00e1sit -JXLoginPane.bannerString=P\u0159ihl\u00e1\u0161en\u00ed -JXLoginPane.titleString=P\u0159ihl\u00e1\u0161en\u00ed -JXLoginPane.passwordString=Heslo -JXLoginPane.rememberUserString=Zapamatovat -JXLoginPane.rememberPasswordString=Zapamatovat Heslo -JXLoginPane.loggingInString=Pros\u00edm \u010dekejte, prob\u00edh\u00e1 p\u0159ihla\u0161ov\u00e1n\u00ed.... -JXLoginPane.failedString=P\u0159ihla\u0161ov\u00e1n\u00ed selhalo; \u0161patn\u00e9 u\u017eivatelsk\u00e9 jm\u00e9no nebo heslo. -JXLoginPane.enterUserNamePassword=Zadejte sv\u00e9 u\u017eivatelsk\u00e9 jm\u00e9no a heslo -JXLoginPane.pleaseWait=Pros\u00edm \u010dekejte, prob\u00edh\u00e1 p\u0159ihla\u0161ov\u00e1n\u00ed.... -JXLoginPane.cancelWait=Ukon\u010den\u00ed p\u0159ihla\u0161ov\u00e1n\u00ed, pros\u00edm \u010dekejte.... -JXLoginPane.cancelLogin=P\u0159eru\u0161it p\u0159ihla\u0161ov\u00e1n\u00ed -JXLoginPane.errorMessage=Nepoda\u0159ilo se p\u0159ihl\u00e1sit.

                    \ - Pros\u00edm ov\u011b\u0159te sv\u00e9 u\u017eivatelsk\u00e9 jm\u00e9no a heslo. \ - P\u0159\u00edpadn\u011b zkontrolujte nen\u00ed-li zapnuta kl\u00e1vesa Caps Lock. diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_de.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_de.properties deleted file mode 100644 index 297f6b11a5..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_de.properties +++ /dev/null @@ -1,25 +0,0 @@ -# Strings used by JXLoginDialog and JXLoginPane -JXLoginPane.okString=Ok -JXLoginPane.okString.mnemonic=O -JXLoginPane.cancelString=Abbrechen -JXLoginPane.cancelString.mnemonic=A -JXLoginPane.stopString=Anmeldung abbrechen -JXLoginPane.stopString.mnemonic=A -JXLoginPane.promptString=Bitte geben Sie Ihren Benutzernamen und Ihr Passwort ein -JXLoginPane.serverString=Server -JXLoginPane.nameString=Benutzername -JXLoginPane.loginString=Anmelden -JXLoginPane.bannerString=Anmeldung -JXLoginPane.titleString=Anmeldung -JXLoginPane.passwordString=Passwort -JXLoginPane.rememberUserString=Benutzernamen merken -JXLoginPane.rememberPasswordString=Passwort merken -JXLoginPane.loggingInString=Bitte warten, Sie werden angemeldet... -JXLoginPane.failedString=Anmeldung fehlgeschlagen; Der Benutzername mit diesem Passwort ist ung\u00FCltig. -JXLoginPane.enterUserNamePassword=Bitte geben Sie Ihren Benutzernamen und Ihr Passwort ein -JXLoginPane.pleaseWait=Bitte warten, Sie werden angemeldet... -JXLoginPane.cancelWait=Anmeldung wird abgebrochen, bitte warten... -JXLoginPane.cancelLogin=Anmeldung abbrechen -JXLoginPane.errorMessage=Anmeldung fehlgeschlagen

                    \ - Bitte pr\u00FCfen Sie den Benutzernamen und das Passwort. -JXLoginPane.capsOnWarning=Achtung: Die Hochstelltaste ('CapsLock') ist aktiv ! diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_en.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_en.properties deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_es.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_es.properties deleted file mode 100644 index b81ca85f24..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_es.properties +++ /dev/null @@ -1,28 +0,0 @@ -# Spanish properties for SwingX -# JXLoginPane -# @author: Sergio Samayoa -# @author: Lola Traba Martínez - -JXLoginPane.okString=Aceptar -JXLoginPane.okString.mnemonic='A' -JXLoginPane.cancelString=Cancelar -JXLoginPane.cancelString.mnemonic='C' -JXLoginPane.nameString=Usuario -JXLoginPane.loginString=Conectar -JXLoginPane.passwordString=Contrase\u00F1a -JXLoginPane.rememberUserString=Recu\u00E9rdame -JXLoginPane.rememberPasswordString=Recordar contrase\u00F1a -JXLoginPane.loggingInString=Conectando, espere por favor... -JXLoginPane.bannerString=Conectar -JXLoginPane.cancelLogin=Cancelar conexi\u00F3n -JXLoginPane.cancelWait=Cancelando conexi\u00F3n, espere por favor... -JXLoginPane.capsOnWarning=\u00A1Bloqueo de may\u00FAsculas activado! -JXLoginPane.enterUserNamePassword=Ingrese su nombre de usuario y contrase\u00F1a -JXLoginPane.errorMessage=No fue posible autenticar.

                    Compruebe su nombre de usuario y contrase\u00F1a. Compruebe si el bloqueo de may\u00FAsculas est\u00E1 activado. -JXLoginPane.failedString=Fall\u00F3 la conexi\u00F3n; la combinaci\u00F3n de nombre de usuario/contrase\u00F1a es inv\u00E1lida. -JXLoginPane.pleaseWait=Espere por favor... -JXLoginPane.promptString=Ingrese su nombre de usuario y contrase\u00F1a -JXLoginPane.serverString=Servidor -JXLoginPane.stopString=Detener conexi\u00F3n -JXLoginPane.stopString.mnemonic=D -JXLoginPane.titleString=Conectar \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_fr.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_fr.properties deleted file mode 100644 index 4ad9344f4a..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_fr.properties +++ /dev/null @@ -1,25 +0,0 @@ -# Strings used by JXLoginDialog and JXLoginPanel -JXLoginPane.okString=OK -JXLoginPane.okString.mnemonic='O' -JXLoginPane.cancelString=Annuler -JXLoginPane.cancelString.mnemonic='A' -JXLoginPane.stopString=Interrompre -JXLoginPane.stopString.mnemonic='I' -JXLoginPane.promptString=Entrez votre login et mot de passe -JXLoginPane.serverString=Serveur -JXLoginPane.nameString=Nom -JXLoginPane.loginString=Identifiant -JXLoginPane.bannerString=Identifiant -JXLoginPane.titleString=Identifiant -JXLoginPane.passwordString=Mot de passe -JXLoginPane.rememberUserString=Se souvenir de moi -JXLoginPane.rememberPasswordString=Se souvenir du mot de passe -JXLoginPane.loggingInString=Veuillez patienter, authentification en cours.... -JXLoginPane.failedString=\u00C9ched d'authentification. Identifiant ou mot de passe invalide. -JXLoginPane.enterUserNamePassword=Entrez votre identifiant et mot de pass -JXLoginPane.pleaseWait=Veuillez patienter, authentification en cours.... -JXLoginPane.cancelWait=Annulation de la connexion, veuillez patienter.... -JXLoginPane.cancelLogin=Interrompre -JXLoginPane.errorMessage=\u00C9chec d'authentification

                    \ - V\u00E9rifiez votre identifiant et votre mot de passe. \ - V\u00E9rifiez que la touche Verrouill. Maj. est d\u00E9sactiv\u00E9e. diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_it.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_it.properties deleted file mode 100644 index d4225a534a..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_it.properties +++ /dev/null @@ -1,26 +0,0 @@ -# Strings used by JXLoginDialog and JXLoginPanel -JXLoginPane.okString=OK -JXLoginPane.okString.mnemonic=O -JXLoginPane.cancelString=Annulla -JXLoginPane.cancelString.mnemonic=C -JXLoginPane.stopString=Stop Login -JXLoginPane.stopString.mnemonic=S -JXLoginPane.promptString=Immetti il tuo Nome Utente e Password -JXLoginPane.serverString=Server -JXLoginPane.nameString=Nome Utente -JXLoginPane.loginString=Login -JXLoginPane.bannerString=Identificativo Utente -JXLoginPane.titleString=Identificativo Utente -JXLoginPane.passwordString=Password -JXLoginPane.rememberUserString=Ricordami -JXLoginPane.rememberPasswordString=Ricorda la Password -JXLoginPane.loggingInString=Attendere, autenticazione in corso.... -JXLoginPane.failedString=Login fallito; nome utente/password non validi. -JXLoginPane.enterUserNamePassword=Immetti il tuo Nome Utente e Password -JXLoginPane.pleaseWait=Attendere, autenticazione in corso.... -JXLoginPane.cancelWait=Annullamento login, attendere.... -JXLoginPane.cancelLogin=Stop login -JXLoginPane.errorMessage=Autenticazione fallita

                    \ - Controlla il tuo Nome Utente/Password. Controlla che Bloc-Maiusc \ - non sia attivo. - diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_nl.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_nl.properties deleted file mode 100644 index d7f09b9488..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_nl.properties +++ /dev/null @@ -1,26 +0,0 @@ -# Strings used by JXLoginDialog and JXLoginPane -JXLoginPane.okString=OK -JXLoginPane.okString.mnemonic=O -JXLoginPane.cancelString=Annuleren -JXLoginPane.cancelString.mnemonic=A -JXLoginPane.stopString=Stop Login -JXLoginPane.stopString.mnemonic=S -JXLoginPane.promptString=Voer uw gebruikersnaam en wachtwoord in -JXLoginPane.serverString=Server -JXLoginPane.nameString=Gebruikersnaam -JXLoginPane.loginString=Login -JXLoginPane.bannerString=Login -JXLoginPane.titleString=Login -JXLoginPane.passwordString=Wachtwoord -JXLoginPane.rememberUserString=Onthouden -JXLoginPane.rememberPasswordString=Onthoud wachtwoord -JXLoginPane.loggingInString=Even geduld, er wordt nu ingelogd... -JXLoginPane.failedString=Inloggen mislukt; ongeldige gebruikersnaam/wachtwoord combinatie. -JXLoginPane.enterUserNamePassword=Voer uw gebruikersnaam en wachtwoord in -JXLoginPane.pleaseWait=Even geduld, er wordt nu ingelogd... -JXLoginPane.cancelWait=Login wordt geannuleerd, even geduld... -JXLoginPane.cancelLogin=Stop login -JXLoginPane.errorMessage=Kan niet inloggen

                    \ - Controleer uw gebruikersnaam en wachtwoord. Controleer of Caps Lock aan \ - staat. -JXLoginPane.capsOnWarning=CapsLock staat AAN! diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_pl.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_pl.properties deleted file mode 100644 index 6b4bc963fa..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_pl.properties +++ /dev/null @@ -1,24 +0,0 @@ -# Strings used by JXLoginDialog and JXLoginPane -JXLoginPane.okString=OK -JXLoginPane.okString.mnemonic=O -JXLoginPane.cancelString=Anuluj -JXLoginPane.cancelString.mnemonic=A -JXLoginPane.stopString=Przerwij Logowanie -JXLoginPane.stopString.mnemonic=P -JXLoginPane.promptString= Podaj nazw\u0119 u\u017Cytkownika oraz has\u0142o -JXLoginPane.serverString=Serwer -JXLoginPane.nameString=Nazwa u\u017Cytkownika -JXLoginPane.loginString=Logowanie -JXLoginPane.bannerString=Logowanie -JXLoginPane.titleString=Logowanie -JXLoginPane.passwordString=Has\u0142o -JXLoginPane.rememberUserString=Zapami\u0119taj mnie -JXLoginPane.rememberPasswordString=Zapami\u0119taj has\u0142o -JXLoginPane.loggingInString=Prosz\u0119 czeka\u0107, trwa logowanie... -JXLoginPane.failedString=Nieudane logowanie; nieprawid\u0142owa nazwa u\u017Cytkownika lub has\u0142o. -JXLoginPane.enterUserNamePassword=Podaj nazw\u0119 u\u017Cytkownika oraz has\u0142o -JXLoginPane.pleaseWait=Prosz\u0119 czeka\u0107, trwa logowanie... -JXLoginPane.cancelWait=Anulowanie logowania, prosz\u0119 czeka\u0107... -JXLoginPane.cancelLogin=Anuluj logowanie -JXLoginPane.errorMessage=Nieudane logowanie

                    Sprawd\u017A nazw\u0119 u\u017Cytkownika oraz has\u0142o. Sprawd\u017A czy Caps Lock jest
                    wcisni\u0119ty. -JXLoginPane.capsOnWarning=Uwaga: CapsLock jest aktywny! diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_pt_BR.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_pt_BR.properties deleted file mode 100644 index 907007d911..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_pt_BR.properties +++ /dev/null @@ -1,23 +0,0 @@ -# Strings used by JXLoginDialog and JXLoginPanel -JXLoginPane.okString=OK -JXLoginPane.okString.mnemonic='O' -JXLoginPane.cancelString=Cancelar -JXLoginPane.cancelString.mnemonic='C' -JXLoginPane.stopString=Interromper Autentica\u00E7\u00E3o -JXLoginPane.stopString.mnemonic=P -JXLoginPane.promptString=Informe seu usu\u00E1rio e senha -JXLoginPane.serverString=Servidor -JXLoginPane.nameString=Usu\u00E1rio -JXLoginPane.loginString=Ok -JXLoginPane.bannerString=Autentica\u00E7\u00E3o -JXLoginPane.titleString=Autentica\u00E7\u00E3o -JXLoginPane.passwordString=Senha -JXLoginPane.rememberUserString=Lembrar usu\u00E1rio -JXLoginPane.rememberPasswordString=Lembrar senha -JXLoginPane.loggingInString=Aguarde, autenticando... -JXLoginPane.failedString=Autentica\u00E7\u00E3o falhou; combina\u00E7\u00E3o usu\u00E1rio/senha inv\u00E1lida. -JXLoginPane.enterUserNamePassword=Informe seu usu\u00E1rio e senha -JXLoginPane.pleaseWait=Aguarde, autenticando... -JXLoginPane.cancelWait=Aguarde, cancelando autentica\u00E7\u00E3o... -JXLoginPane.cancelLogin=Parar autentica\u00E7\u00E3o -JXLoginPane.errorMessage=N\u00E3o foi poss\u00edvel autenticar.

                    Confira usu\u00E1rio e senha e verifique se Caps Lock est\u00E1 ativado. diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_sv.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_sv.properties deleted file mode 100644 index 725cd5ab3a..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/LoginPane_sv.properties +++ /dev/null @@ -1,26 +0,0 @@ -# Strings used by JXLoginDialog and JXLoginPanel -JXLoginPane.okString=OK -JXLoginPane.okString.mnemonic=O -JXLoginPane.cancelString=Avbryt -JXLoginPane.cancelString.mnemonic=v -JXLoginPane.stopString=Avbryt inloggning -JXLoginPane.stopString.mnemonic=v -JXLoginPane.promptString=Ange ditt anv\u00E4ndarnamn och l\u00F6senord -JXLoginPane.serverString=Server -JXLoginPane.nameString=Anv\u00E4ndarnamn -JXLoginPane.loginString=Inloggning -JXLoginPane.bannerString=Inloggning -JXLoginPane.titleString=Inloggning -JXLoginPane.passwordString=L\u00F6senord -JXLoginPane.rememberUserString=Kom ih\u00E5g mig -JXLoginPane.rememberPasswordString=Kom ih\u00E5g l\u00F6senord -JXLoginPane.loggingInString=Var god v\u00E4nta, inloggning p\u00E5g\u00E5r.... -JXLoginPane.failedString=Inloggningen misslyckades; felaktig kombination av anv\u00E4ndarnamn/l\u00F6senord. -JXLoginPane.enterUserNamePassword=Ange ditt anv\u00E4ndarnamn och l\u00F6senord -JXLoginPane.pleaseWait=Var god v\u00E4nta, inloggning p\u00E5g\u00E5r.... -JXLoginPane.cancelWait=Avbryter inloggningen, var god v\u00E4nta.... -JXLoginPane.cancelLogin=Avbryt inloggning -JXLoginPane.errorMessage=Inloggningen misslyckades

                    \ - Kontrollera ditt anv\u00E4ndarnamn och l\u00F6senord. Kontrollera om Caps Lock \ - \u00E4r aktiverat. -JXLoginPane.capsOnWarning=Caps Lock \u00E4r aktiverat \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/SearchField.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/SearchField.properties deleted file mode 100644 index dd11693de0..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/SearchField.properties +++ /dev/null @@ -1,4 +0,0 @@ -SearchField.prompt=Search -SearchField.recentsMenuTitle=Recent Searches -SearchField.clearRecentsText=Clear Recent Searches -SearchField.noRecentsText=No Recent Searches diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/SearchField_de.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/SearchField_de.properties deleted file mode 100644 index b0db945076..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/SearchField_de.properties +++ /dev/null @@ -1,4 +0,0 @@ -SearchField.prompt=Suchen -SearchField.recentsMenuTitle=Letzte Sucheinteäge -SearchField.clearRecentsText=Letzte Sucheinträge Löschen -SearchField.noRecentsText=Keine letzten Sucheinträge diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/SearchField_en.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/SearchField_en.properties deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay.properties deleted file mode 100644 index 949d2de8fa..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay.properties +++ /dev/null @@ -1,6 +0,0 @@ -TipOfTheDay.dialogTitle=Tip of the Day -TipOfTheDay.didYouKnowText=Did you know... -TipOfTheDay.showOnStartupText=Show tips on startup -TipOfTheDay.previousTipText=< Back -TipOfTheDay.nextTipText=Next > -TipOfTheDay.closeText=Close diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay24.gif b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay24.gif deleted file mode 100644 index 9376ede8a89675708ff2c429dc2af31f88d1c476..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 742 zcmZ?wbhEHblwgoxc=n$G2>$>7-`F_4u&|-1sG+vDt)`|qFR$?QnV{=8qCb6NSiKsk zfOp3Z-s{(O4 zJ?!k+unQNGE?!K&dpGC7gQ|xQ8?Ifey>_kc@#FRzH`<;&>A!WW_tmSVuU;*G^=iev zd-Lz#U-b6vrnhf5KYqM1EiLfr(;ZKqY)?-Q{rvfCadBcvNpfap?Dy~Y%gb}$zPfta-~h<0gAXYFWMO1r@MX{eISmvi4D25oe4CnETHD%Nd>FdA7`;2%y2bT%HPr;W z`zJIpNE&GAXlba*Df0Qun&4xgXJpF4s-Yw+!N54%M_kL;(wd1`TSY;PciRL8eH~L9 zMr%_YbvY@46{~c$jP^3IXdMz2KGwmYsj+tb#?2D^S37*vR5cc`u`iJkda(VGpq!Gj zx}B|^$^wH#M(aXFtA-y7n2)nHy$;%1+_3NvuasGmPep)^ydI0NLXM1NUa&BOph!>6 zfddPU_;!mK1O&WUa8k{u&VXsck|(|l4ow1@MJ%1ZlPz0$3>Pg~etfp~3=Y*-pVKC< z=w21I_tlG+*VZSPvobL#{_}UvFQ_caOwTA$FfuSOP)Mp&2rkW2@Xbsv$}g@gE=kQT y)=}`xOV(3x%*jy*0x2w32udwZEhL diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_cs.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_cs.properties deleted file mode 100644 index dc596dee4b..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_cs.properties +++ /dev/null @@ -1,7 +0,0 @@ -#Czech resource bundle created by Vity (vitywap@seznam.cz) 08/2007 -TipOfTheDay.dialogTitle=Tip dne -TipOfTheDay.didYouKnowText=V\u011bd\u011bli jste, \u017ee... -TipOfTheDay.showOnStartupText=Zobrazovat tipy p\u0159i startu -TipOfTheDay.previousTipText=< P\u0159edchoz\u00ed -TipOfTheDay.nextTipText=Dal\u0161\u00ed > -TipOfTheDay.closeText=Zav\u0159\u00edt \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_de.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_de.properties deleted file mode 100644 index 48ae901d24..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_de.properties +++ /dev/null @@ -1,6 +0,0 @@ -TipOfTheDay.dialogTitle=Tipp des Tages -TipOfTheDay.didYouKnowText=Wussten Sie schon...? -TipOfTheDay.showOnStartupText=Tipps beim Start anzeigen -TipOfTheDay.previousTipText=< Zur\u00fcck -TipOfTheDay.nextTipText=Weiter > -TipOfTheDay.closeText=Schlie\u00dfen diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_en.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_en.properties deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_es.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_es.properties deleted file mode 100644 index f78a8dd791..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_es.properties +++ /dev/null @@ -1,6 +0,0 @@ -TipOfTheDay.dialogTitle=Sugerencia del D\u00eda -TipOfTheDay.didYouKnowText=Sab\u00edas que ... -TipOfTheDay.showOnStartupText=Mostrar sugerencias al inicio -TipOfTheDay.previousTipText=< Anterior -TipOfTheDay.nextTipText=Siguiente > -TipOfTheDay.closeText=Cerrar diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_fr.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_fr.properties deleted file mode 100644 index bb9e32e748..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_fr.properties +++ /dev/null @@ -1,6 +0,0 @@ -TipOfTheDay.dialogTitle=Astuce du Jour -TipOfTheDay.didYouKnowText=Le saviez-vous... -TipOfTheDay.showOnStartupText=Afficher au d\u00e9marrage -TipOfTheDay.previousTipText=< Pr\u00e9c\u00e9dente -TipOfTheDay.nextTipText=Suivante > -TipOfTheDay.closeText=Fermer diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_it.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_it.properties deleted file mode 100644 index 45ca54a1e6..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_it.properties +++ /dev/null @@ -1,6 +0,0 @@ -TipOfTheDay.dialogTitle=Consiglio del Giorno -TipOfTheDay.didYouKnowText=Lo sapevi che... -TipOfTheDay.showOnStartupText=Visualizza Consigli all'Avvio -TipOfTheDay.previousTipText=< Precedente -TipOfTheDay.nextTipText=Successivo > -TipOfTheDay.closeText=Chiudi diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_nl.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_nl.properties deleted file mode 100644 index 2ab13942af..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_nl.properties +++ /dev/null @@ -1,6 +0,0 @@ -TipOfTheDay.dialogTitle=Tip van de dag -TipOfTheDay.didYouKnowText=Wist u dat... -TipOfTheDay.showOnStartupText=Laat tips zien bij opstarten -TipOfTheDay.previousTipText=< Vorige -TipOfTheDay.nextTipText=Volgende > -TipOfTheDay.closeText=Sluiten diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_pl.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_pl.properties deleted file mode 100644 index 8b30532abe..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_pl.properties +++ /dev/null @@ -1,6 +0,0 @@ -TipOfTheDay.dialogTitle=Porada dnia -TipOfTheDay.didYouKnowText=Czy wiesz \u017Ce... -TipOfTheDay.showOnStartupText=Pokazuj porady przy uruchomieniu -TipOfTheDay.previousTipText=< Wstecz -TipOfTheDay.nextTipText=Dalej > -TipOfTheDay.closeText=Zamknij diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_pt_BR.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_pt_BR.properties deleted file mode 100644 index 71e1c32617..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_pt_BR.properties +++ /dev/null @@ -1,6 +0,0 @@ -TipOfTheDay.dialogTitle=Dica do Dia -TipOfTheDay.didYouKnowText=Voc\u00EA sabia... -TipOfTheDay.showOnStartupText=Mostrar dicas ao iniciar -TipOfTheDay.previousTipText=< Anterior -TipOfTheDay.nextTipText=Pr\u00F3xima > -TipOfTheDay.closeText=Fechar diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_sv.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_sv.properties deleted file mode 100644 index 47d1beaae6..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/TipOfTheDay_sv.properties +++ /dev/null @@ -1,6 +0,0 @@ -TipOfTheDay.dialogTitle=Dagens tips -TipOfTheDay.didYouKnowText=Visste du... -TipOfTheDay.showOnStartupText=Visa tips vid start -TipOfTheDay.previousTipText=< Tillbaka -TipOfTheDay.nextTipText=N\u00E4sta > -TipOfTheDay.closeText=St\u00E4ng diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/clear.gif b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/clear.gif deleted file mode 100644 index 45b8481eeb901977ce6128c597dfc86a37bc16ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1026 zcmZ?wbhEHb6krfwcwWz->RYYqU$5q0ukK%~9@wB0(xegEsT10&AKGXT)?^soWE|P1 z6Fb#7rpGL%LpOe=UcyYvxOS_!cGLI?#z`|Q6Zz7|`SaGd!<@KgjH=0*nZ(e<~WzDVDwYS^W-DzKcw`0S-&W-oGHa+Ox{IF-squ#BL zXKcRIzy0aNozExjdNF0stA)F+P2c}^)}ap@j@;gM=IMo-k1yVOdg;#dYxkaAzxVw5 z{TJ8ozr6n7)eRte^ybc^S2rKOzx(9vy(h2lJbi!f>AQPC^7+U6FFrng_4&!`&rjce z{rUU%|4}f|A)xq^g^`P)fkB4>2tav)f#W^{<5vfU1q%)~bJlRk2rlE1G)&l6a55>C zOGH_wP2q;nGjDO-@D&b)hgdo%Dwv5d+-O?rC8Fs&Nk%a!#l26;h|4Z>(-R+gRqK)u z7n0jtxdd2y{|FpELiZ2e;&W=qd%3!j)ez#X6${U z*cf?aj-ix+IFF+hOV=eup9O3a9<+2_-;liDZs7uU7IFQe*Z1x|ViaiPyr=Z#p}GQt FH2`9P#s&ZY diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/clear_pressed.gif b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/clear_pressed.gif deleted file mode 100644 index acebf5e2a53fe597b0ee2cafa372ec60e9ce230d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1032 zcmZ?wbhEHb6krfwc;3jsrWeDmAIo77$7vYPWt6~eoXBI6#A}+&XO_Zmo+@CGCTx)+ zXqhf#nIUAAA#9x~Vv{9mlPzkSEpD4BW|t#wpDSUXBVnH>;gBckm@nm2AmfxP?OZ71 zTp;68Bsl=5RwD0KD(_yZ;8CXNS+3|+rQ}ti;#I2RRi@-!t?XT?>{FxSQ>Erp zuHsv(>RYYuTdCn!sp?;^;a{!gU!@+>p%+}Q5!#^<+NmGbY!KFD7~W(Y(PR|aW)j(= z6Fb!?wof;Hre4BK)A)YFgz3gfGYrxe8>TI`OrLF?IoCFSrCH%t`@&VWW!r7bc34#& zvZ*_jT0hsZR?)w*Vfv=V8Jn7BY_3~ytaa(hg&U77 z+H`dB=3`4XA6>fj__D3Xmu@??eEZ3jJ5DX%d2ZFNGi!FAS+VEx>OB|M?m4?=@5Oa{ z&#l>ib?t#`8xCCFc=*QpBX>3*zkBrj?PC}29KQ%ecTZdbqI)N=JUn&n$&a7Ee*XUb ze-sQ{2q^w!VdP@?&!EEq1fV>@!10vfe`q9wLPG;13zvjNgTlc!o&RfOe0FR|WMbu! zv1wS4+T_$GZk#v8Vc{WGab82K4GfA*C;23dG9Elw*yzR)bZkM#OeP*N0Y#S{g~*4@ zGp$ooj&L-m^Y#jA>u&jksR449* z!Qu9PDS6{h=NdL1V(XJ}y!2(}=VkuVN|r*KS(%n_NZXf8QJMHyy-)C1PJhO?q~)E@ zR7>|MTs$+aGgHmq&$w`r)Gc)O H!e9*m+pLa< diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/clear_rollover.gif b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/clear_rollover.gif deleted file mode 100644 index b9b298f8e3cb5ac361c83393ae38707389bc3842..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1025 zcmZ?wbhEHb6krfwcwWz-mpIEHd5%HqLc{dMCg}?-GUi!k&Nt0oZk09PCU3cE;TD_1 zwN3?#?F&~p6t8h9UF}}B%C%ynW91H)nw_3CTfJ(x**2baYB?9!w9B>ad|=Do;I{p) z{X1PJ?DFZp za5Zn?t^7r|3zyt2U3xos!~fRBN7@!2DOvuYeaX?%6%Wf++%H@CsC?yv@>P$cxBQRU z^1ovBld84Ps@FZQU-zhH{fpWSFY7kGs^9dwVe^~DEpMB)yldL}u4U_+=56m=wtr~d z@iF1_x3-<163%>U-}$+H*XNGiUpn`E?b`dTd+*ooecyWz{OmjUYr^3_X*d2)Jp5GX@$7k*Z~|5o+>`}Rv8TmJsPbobTe2XAjYdUNy9o2!pM z-FW=|=93S%o_)IY?907p@9#bTbpQE>yDz@nfARUw%kTGJetGcf``tHxAH4bT`0e*6 zZ+|>`|M%(pUq65U{XYr@Is_DdvM_QnG%)Be00AgZFmT*sU_7b8uwcQ#X0|krBL|*# zPuGiF7cudXOQ)c$V3x>=O|0EhH3O!}C<+~E=abeJU|1m3$~j5JsqV{xiH}{mMb)^( zBtI?okyp@fiK#sMGx2kSLqn6f_mTyl)%j-I7f8skFgP)@ zvI|`K!>C>Mriq!EMa5#XnJ+VA1*=HNfpDf-_GNeeWEwZFZ)y_LiHZ2Eex9Ab^xvM7 z6Lu`KLZ*U+5Lu!Sk^o_Z5E4Meg@_7P6crJiNL9pw)e1;Xm069{HJUZAPk55R%$-RIA z6-eL&AQ0xu!e<4=008gy@A0LT~suv4>S3ILP<0Bm`DLLvaF4FK%)Nj?Pt*r}7;7Xa9z9H|HZjR63e zC`Tj$K)V27Re@400>HumpsYY5E(E}?0f1SyGDiY{y#)Yvj#!WnKwtoXnL;eg03bL5 z07D)V%>y7z1E4U{zu>7~aD})?0RX_umCct+(lZpemCzb@^6=o|A>zVpu|i=NDG+7} zl4`aK{0#b-!z=TL9Wt0BGO&T{GJWpjryhdijfaIQ&2!o}p04JRKYg3k&Tf zVxhe-O!X z{f;To;xw^bEES6JSc$k$B2CA6xl)ltA<32E66t?3@gJ7`36pmX0IY^jz)rRYwaaY4 ze(nJRiw;=Qb^t(r^DT@T3y}a2XEZW-_W%Hszxj_qD**t_m!#tW0KDiJT&R>6OvVTR z07RgHDzHHZ48atvzz&?j9lXF70$~P3Knx_nJP<+#`N z#-MZ2bTkiLfR>_b(HgWKJ%F~Nr_oF3b#wrIijHG|(J>BYjM-sajE6;FiC7vY#};Gd zST$CUHDeuEH+B^pz@B062qXfFfD`NpUW5?BY=V%GM_5c)L#QR}BeW8_2v-S%gfYS= zB9o|3v?Y2H`NVi)In3rTB8+ej^> zQ=~r95NVuDChL%G$=>7$vVg20myx%S50Foi`^m%Pw-h?Xh~i8Mq9jtJloCocWk2Nv zrJpiFnV_ms&8eQ$2&#xWpIS+6pmtC%Q-`S&GF4Q#^mhymh7E(qNMa}%YZ-ePrx>>xFPTiH1=E+A$W$=bG8>s^ zm=Bn5Rah$aDtr}@$`X}2l~$F0mFKEdRdZE8)p@E5RI61Ft6o-prbbn>P~)iy)E2AN zsU20jsWz_8Qg>31P|s0cqrPALg8E|(vWA65poU1JRAaZs8I2(p#xiB`SVGovRs-uS zYnV-9TeA7=Om+qP8+I>yOjAR1s%ETak!GFdam@h^# z)@rS0t$wXH+Irf)+G6c;?H29p+V6F6oj{!|o%K3xI`?%6x;DB|x`n#ibhIR?(H}Q3Gzd138Ei2)WAMz7W9Vy`X}HnwgyEn!VS)>mv$8&{hQn>w4zwy3R}t;BYlZQm5)6pty=DfLrs+A-|>>;~;Q z_F?uV_HFjh9n2gO9o9Q^JA86v({H5aB!kjoO6 zc9$1ZZKsN-Zl8L~mE{`ly3)1N^`o1+o7}D0ZPeY&J;i;i`%NyJ8_8Y6J?}yE@b_5a zam?eLr<8@mESk|3$_SkmS{wQ>%qC18))9_|&j{ZT zes8AvOzF(F2#DZEY>2oYX&IRp`F#{ADl)1r>QS^)ba8a|EY_^#S^HO&t^Rgqwv=MZThqqEWH8 zxJo>d=ABlR_Bh=;eM9Tw|Ih34~oTE|= zX_mAr*D$vzw@+p(E0Yc6dFE}(8oqt`+R{gE3x4zjX+Sb3_cYE^= zgB=w+-tUy`ytONMS8KgRef4hA?t0j zufM;t32jm~jUGrkaOInTZ`zyfns>EuS}G30LFK_G-==(f<51|K&cocp&EJ`SxAh3? zNO>#LI=^+SEu(FqJ)ynt=!~PC9bO$rzPJB=?=j6w@a-(u02P7 zaQ)#(uUl{HW%tYNS3ItC^iAtK(eKlL`f9+{bJzISE?u8_z3;~C8@FyI-5j_jy7l;W z_U#vU3hqqYU3!mrul&B+{ptt$59)uk{;_4iZQ%G|z+lhASr6|H35TBkl>gI*;nGLU zN7W-nBaM%pA0HbH8olyl&XeJ%vZoWz%6?Y=dFykl=imL}`%BMQ{Mhgd`HRoLu6e2R za__6DuR6yg#~-}Tc|Gx_{H@O0eebyMy5GmWADJlpK>kqk(fVV@r_fLLKIeS?{4e)} z^ZO;zpECde00d`2O+f$vv5tKEQIh}w03c&XQcVB=dL;k=fP(-4`Tqa_faw4Lbua(` z>RI+y?e7jKeZ#YO-C0~<+1K~#9!G}BLLlXo1)@%Q(6k~U9pO<0?DuCY3$E7%U* z2AdlQs}v?4I>x}$5IywJV3TM{OxvF| zHh$#n-u=1d`%L!r;iS_%&Sq)6caOmH$k*%K?(fIBe3@#kMs#9=Xei_?dETot#{9^? zO-(nS?cc8^nbb$Al$MN9Hwbj>{CS-f(eDQabiQ0xRn_Hl=X5a?(o8I-!;Vw8;C-fJ zvA-U-wrVkv&~?z6)2FrMdHUSvr zp;p5HemIPE;|2>TUEh4m8@;^%EDQ{gzkZ#NWnqGVsxcr!BkT)Re7~xy7!ewbp$Oz( zYNE@s05HHxBEc5ms8SCCDyo!32q0|ve(D8K9|TOFI>nn;uHg7S?#Kux2Fn7Me7qU(}8ae|KH$5}Fl zjq!0jU`m7ss^3{-sy96-7J!ywkv*!+q|-dSb`1e?!^2FaQpCz-B5P|DfIKjv#s%)m zS%HMeP*VA7OO=+M9x9m(VL(LiRq=pGcQ?7&Swg_CBHUH|61ZR+fjMPrQzYIFM~beC z2h_K#Znv?%jseDjQTPqG095Q{U=^5!(dQzy29dWKAZmau;Kequ1pEU2eHa1KzzTru zyeb0h0t(Q*2hOy?dkyfi2i6+YbMTLWTfooTE&%@x0E%yq_?hqOR{#J207*qoM6N<$ Ef=!;=y8r+H diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/month-down.png b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/month-down.png deleted file mode 100644 index 2d21e24454667a2d863104da25695602871f4375..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3138 zcmV-I488M-P)KLZ*U+DPS_9i&56Iack~*0${?rBqwA zbZoLZbZI-x{_&l!W7+oidpv&MzdrMQd_LdL_w#zb-}9IOG}_oAkw5`JT)b2q;N?aS z4GX6aX@nvap#xPIusITuD~sia!7l=McMAjn+^}YgL<0P8m!>#0EF2)L0O-b$V;2Cr zQRFxXfbKG>ND2_K0CYLAY%V~g0I(7V1uy`j7=UIBIj#br8AXnN0-%}1iID=t9e}12 z&*jGhiXDKmGnXgf0BTkMxLgS*4p7Sli0-&JAs5id1z^DuiKT!>6#(nduy8v0%3K@3 z$r_-I?7g=%4JgY7jCFeNjkf>{qJgqMd+)st00Dr|tCvLE+mQ*Xy8%k~WwO`iz>qxP z^JbasW1&p;xd@;&0pANaiQ*(GZ=w$Px6kC6$nYYiJdLf^p9XtXiiWSptl zcnix36RoUmCfQE5v$vn(;52pWwCOWuInQ=+b#r65yL)(gdCl?m@%8na%bdqz`3D3B z1_g)ApC1|)9ucu%!NNt6i`h|894;?9CN`G8WT_x7UMNZsOQeZO%a$jvO!+D`ZB<6b z>dZB3*JfpZy>8w54I6WEHs$7S&fBu(KU=qL-@aq#u3fwL?A^OBzo1}$VbOsD2M--O zTwHSGNa@jI$B&;VJ9)DFn~I84rz@+ft82ckJ#+T#ci*2o|HFlg7k|9;)8(Ikxl&hm zwf@@mh8s5)pHenwsz5|E=Z0gNKhE|Nf-yY5O0~p10{j69tVsDPbya%C>eZQR)@Eg8f4y$~`VAX%Hf_q? zoR>!huu~pD{@?+Wmw!`n>U3pgRdvm`wY6vD=f5C7|1VeSuGU|>euFyyy?af~_kU|? z?R)$eov+@!d;dWOWmR=e?P0@p_4Ex4M;VPCV@x+0JI>V1%-npug{9?$2@|bsCQY)P zY-c~k!I7+lnKP+cV90AhUJT56ES5i24xv;Ui+bvzPaJ*HAPXWrV|8ZcnzcP~H<1!~ZQ4)ibzbeO>gPH@9qgnTRJBi<@Z zRPa*ZD P(Tr%3v~*g#;uOU+#X6EgiwoOV-!dKW*@7Vy59NO=p_($JuXO5?r3UwK3YjGTGcFG_WAYc8y@C(ZPv+Kx#iJT*0$Q6yLYSaN!$A- ze@S6@k^VvdL-obXlB=cu$HPywoaC3ksz^PpR=Km!jXz4or{Zf%SVUtK?wxwcFfR`~dmn(quPe(5D`{=WY+#Y$#yXechi@!ak~$@w@;^Ip|_=Yv`D;+5Me4 zCrD?0g}@??8()|(HsJ?J$zXjxnDkZ_CB64eL2F@nk$zDl)#?4MD|hzQK7Y7>w_m+p zUv%B4VRS=huXyvF{^n2bwpVu~Jqw}SU+p60-L21F9HCDGK2!5nC{1L z$N%?=6C_f5XaP{?OTB{t=mNM9E%pom;0EAMd=zsY03CoZA$3=YI#BOY|LfDR7C;6nl_ z*hs{3&>;XH2}lGVT<{=~uTO*kJS0L4Pp~0I40y1X?=Wzm5Ilky{4dPlg9~9KKSLZ> z9-Xv9m^?is*0$94sqH7w<+FWzcH|+x=Sk%5QPk(F9hDtDD;Pabr}xZ$1TkX%_8!)V z1{-1|fd>he;&+G<7s=1)T?g{N2LLE*1A80*pvI|XK%Gc=%cTIo5Q>t;{FqoN-BlzK z@aPO-oG4Ms6I;=}<2g1~bURyH2LSjB$yWB2$fH0R00004XF*Lt007wQ^&Awc0000W zV@Og>003>600482008hD004+h004H!007aO001)|0010z3vYEj0008zNkl19;`(#kW2SEh;(ooPh zB_b^?XsRNl?L)EgvkwJL&?iNaJ{V~5L5yy8H#2w6y?w}Lk~Il#F8sN3hkJkLoH=v< z31bZX{r#N1c)1hE0&}Q*guuCu{cRlpzVCxEhW6~yV`t9)XomAeqat0gih(j+KmTgl zvq!({>+8c9g9TvQ*5N(7TCl2;1fnaJNhH|4a~qaroj7~(vJKz>7{fd_LiYB)c~&@fCMB&Ym7vg_YjAMa?G8Zj@Nd=0Rs!?*H!_eB?E|Pl+q|AQ|kHi z0XmL+kN-tm*?SRP7SI`ch_qld=yy_l@nVp3=exOdy%#(WXYmq#`lW}xZQB7zR#{U6 zMKqB%F>qjDkgf|?>F&7+o=5uAm1O3)9Ne{q?G0-PRWzDN(wPouP+Ft4;mWl?In{Xq zJdb&+S5Y(D4TruEH zVWbR@N;;Sc|3+C;0u>R25smA8S{pY&Dg&tur@MaR>pjQ0(sPF(jLHQC-|AASE)7#Sf`TZ4$i=O*5U@up}~iaH)K5@DnSKG5_r-+Z~8yH5sLT3<^z z))Z}~&QKeTh?&&2`1^)A9NVUOg~!``k%{Lf0UgKA{r2qf`07*qoM6N<$f`*a!761SM diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/month-up.png b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/month-up.png deleted file mode 100644 index 2c270393b95a9605d26329cb15debec2aebaba04..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3139 zcmV-J47~G+P)KLZ*U+DPS_9i&56Iack~*0${?rBqwA zbZoLZbZI-x{_&l!W7+oidpv&MzdrMQd_LdL_w#zb-}9IOG}_oAkw5`JT)b2q;N?aS z4GX6aX@nvap#xPIusITuD~sia!7l=McMAjn+^}YgL<0P8m!>#0EF2)L0O-b$V;2Cr zQRFxXfbKG>ND2_K0CYLAY%V~g0I(7V1uy`j7=UIBIj#br8AXnN0-%}1iID=t9e}12 z&*jGhiXDKmGnXgf0BTkMxLgS*4p7Sli0-&JAs5id1z^DuiKT!>6#(nduy8v0%3K@3 z$r_-I?7g=%4JgY7jCFeNjkf>{qJgqMd+)st00Dr|tCvLE+mQ*Xy8%k~WwO`iz>qxP z^JbasW1&p;xd@;&0pANaiQ*(GZ=w$Px6kC6$nYYiJdLf^p9XtXiiWSptl zcnix36RoUmCfQE5v$vn(;52pWwCOWuInQ=+b#r65yL)(gdCl?m@%8na%bdqz`3D3B z1_g)ApC1|)9ucu%!NNt6i`h|894;?9CN`G8WT_x7UMNZsOQeZO%a$jvO!+D`ZB<6b z>dZB3*JfpZy>8w54I6WEHs$7S&fBu(KU=qL-@aq#u3fwL?A^OBzo1}$VbOsD2M--O zTwHSGNa@jI$B&;VJ9)DFn~I84rz@+ft82ckJ#+T#ci*2o|HFlg7k|9;)8(Ikxl&hm zwf@@mh8s5)pHenwsz5|E=Z0gNKhE|Nf-yY5O0~p10{j69tVsDPbya%C>eZQR)@Eg8f4y$~`VAX%Hf_q? zoR>!huu~pD{@?+Wmw!`n>U3pgRdvm`wY6vD=f5C7|1VeSuGU|>euFyyy?af~_kU|? z?R)$eov+@!d;dWOWmR=e?P0@p_4Ex4M;VPCV@x+0JI>V1%-npug{9?$2@|bsCQY)P zY-c~k!I7+lnKP+cV90AhUJT56ES5i24xv;Ui+bvzPaJ*HAPXWrV|8ZcnzcP~H<1!~ZQ4)ibzbeO>gPH@9qgnTRJBi<@Z zRPa*ZD P(Tr%3v~*g#;uOU+#X6EgiwoOV-!dKW*@7Vy59NO=p_($JuXO5?r3UwK3YjGTGcFG_WAYc8y@C(ZPv+Kx#iJT*0$Q6yLYSaN!$A- ze@S6@k^VvdL-obXlB=cu$HPywoaC3ksz^PpR=Km!jXz4or{Zf%SVUtK?wxwcFfR`~dmn(quPe(5D`{=WY+#Y$#yXechi@!ak~$@w@;^Ip|_=Yv`D;+5Me4 zCrD?0g}@??8()|(HsJ?J$zXjxnDkZ_CB64eL2F@nk$zDl)#?4MD|hzQK7Y7>w_m+p zUv%B4VRS=huXyvF{^n2bwpVu~Jqw}SU+p60-L21F9HCDGK2!5nC{1L z$N%?=6C_f5XaP{?OTB{t=mNM9E%pom;0EAMd=zsY03CoZA$3=YI#BOY|LfDR7C;6nl_ z*hs{3&>;XH2}lGVT<{=~uTO*kJS0L4Pp~0I40y1X?=Wzm5Ilky{4dPlg9~9KKSLZ> z9-Xv9m^?is*0$94sqH7w<+FWzcH|+x=Sk%5QPk(F9hDtDD;Pabr}xZ$1TkX%_8!)V z1{-1|fd>he;&+G<7s=1)T?g{N2LLE*1A80*pvI|XK%Gc=%cTIo5Q>t;{FqoN-BlzK z@aPO-oG4Ms6I;=}<2g1~bURyH2LSjB$yWB2$fH0R00004XF*Lt007wQ^&Awc0000W zV@Og>003>600482008hD004+h004H!007aO001)|0010z3vYEj0008!NklIT8bNkn}9D+lZ=lsv+SF|gLs)!qBe$uqy&w_egJZAw-$t?V!7&fBi; z-XBLsMzGf61MvHOU+wy$6JJ??w3w1*9fuv;H{I8{~U?}hpu7c4{;SC;|V$N(Y+r3^|5N(r%b zd+7Q4C&tFc^Y3Poh$%WC57b6bT9eV5-_BjeYyObm|NNVEZTsmxdYrK*<79H*3#E+| z0o_7}+E|RWZ0Y$mu@jx8>auSf9St_h~3-TIdXU>jg57AfDX7bI~1`f5tJ6n z0s*=@)-!ziTaNDAOjER(s*hJQc<&KyTf6yV@Cr(2*Zx0p9-mfk1#TML+qR45bi%uN6dYAqS*LaFcO+y0!Ez6r|S+k0C zMrR|>=1{CbM9^Zm|L`#<`Y!TJ94hLT5)KB5Evsj5`zNeg8ez&yp>+0V6ecPHT9XBe z7%HnP@am&k&_{KA*}8(2%{9amDPF#wDgszzD4d3O4vj_U97gLu;6TS(K8=Q1(Xfbk zBE|DplQU0?u`@d~)*xaLF(k#XV{MdFM$HN;+&2yAy8gK5B|_!%L%f=Z1Mqq>{@$^x zET2y@kwj_bb#DA@G7qe;4PU=;^LE#!jcqj6R+n53o|oYI@NJevYHk5(04Gl8I%mJJnDZzO!vp>kq)aSS*$Y0;~a60t@EQU`gN+F#2C(7Y^csoSZoT dr`6}b0|3Qh%H}o#G^hXo002ovPDHLkV1k;*{0RU6 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/search.gif b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/search.gif deleted file mode 100644 index 9070fda723c54e89fc9a4fb6d95b3b249f785949..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 547 zcmZ?wbhEHb6kyMHVtXR5o?TS@vfMEUl z4VyM?+Pr1U+HL!{ZriqT``&FpuzmZE9Xqz~J-m1C-h&4Z?mB$-@ZrOIj-EeqcYx@{)%&lm-G6oc;hXD^-rRWf_Tj^aHy^*f z_2k{{C+}}P{qW%Ft9vg#-GBM{{>v{9UwwJ@^3B87UtYd^`TX_!CvU$!dHem%n>SD2 zeSiM`$LkMYUw!)d`qPispMSmk^6UNA-(PYfB*ga_us#N z|LF@9f3h%gG1N2YFaQB4P8ir58tR*xnm3yCqSUG^uCFM= zuW9M(XD`^JBcQ9n#-JamDkLl>-y@}_%f-m*wx~%6;$;h#tX#Zo)v{&FmMmYjV#SK3E7z`AwFU^* zuivm~)27W^wyfQ@f9tkw8@KP>1_ax;@7S?p``*KQ_wGG#;K0Fy2X`Gld-(9-Jx9+U zIdbI8nKS3kox6DP;^`~*Zrr$W;r6o&x1V1GqB}tJ;_CfZ*Y3Z%{_xH9M{jOCdi(I< z!<&!a-g@%x_LKLwo_=`n^wqr=pYFf>eE;Q_hp)aod->+!>n|@~zI^`r{gb!fp1l43 z=FOX@@4i2O|Ks(CudhD+eEsRi>(9U5efjnN>+df={(SrS=jYF#-+%r4@%!)Z-@pI< z{rm6VzyI_Cia%KxxftphbU-lyiW3I*&W8G?=4NG14o;bt?rvUHe{UmAo}M0c#Rvxz zb1MV6sV#h-E~d6l?yjN>nz;haES>#=0wh;A@mm<#c?E}sN-gk_uo06tw)YK*bW@(H zB4MZ`E1+%d9^fd{qbI1Z#m;D0uO=)auFxZ`q0i04=CQ3=M}n0__hXA7v&0XEfB$}P O{N>)k(XhaQ!5RQ;H)&u1 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx.properties deleted file mode 100644 index 8c49b1961e..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx.properties +++ /dev/null @@ -1,59 +0,0 @@ -# default properties for swingx -# JXSearchPanel - -Search.matchCase=Case sensitive -Search.contains=contains -Search.equals=equals -Search.endsWith=ends with -Search.startsWith=begins with - -Search.wrapSearch=Wrap Search -Search.backwardsSearch=Backward -Search.match=Find -Search.close=Close -Search.findNext=Find Next -Search.findPrevious=Find Previous -Search.searchFieldLabel=Find -Search.searchFieldLabel.mnemonic=F -Search.searchTitle=Find - -Search.notFound=Value not found -Search.notFoundBackground=FF6666 -Search.notFoundForeground=000000 - -XDialog.close=Close -XDialog.cancel=Cancel -XDialog.execute=OK - -# default properties for swingx -# JXTable column control entries -# -JXTable.column.horizontalScroll=Horizontal Scroll -JXTable.column.packAll=Pack All Columns -JXTable.column.packSelected=Pack Selected Column - -# -# default actions in context menues for TextComponents -# -DefaultEditorKit.cut-to-clipboard=Cut -DefaultEditorKit.copy-to-clipboard=Copy -DefaultEditorKit.paste-from-clipboard=Paste -DefaultEditorKit.delete-next=Delete -DefaultEditorKit.select-all=Select All - -# -# default actions for context menus for ScrollBars -# -JScrollBar.vertical.minScroll=Top -JScrollBar.vertical.maxScroll=Bottom -JScrollBar.vertical.negativeUnitIncrement=Scroll Up -JScrollBar.vertical.negativeBlockIncrement=Page Up -JScrollBar.vertical.positiveUnitIncrement=Scroll Down -JScrollBar.vertical.positiveBlockIncrement=Page Down - -JScrollBar.horizontal.minScroll=Left Edge -JScrollBar.horizontal.maxScroll=Right Edge -JScrollBar.horizontal.negativeUnitIncrement=Scroll Left -JScrollBar.horizontal.negativeBlockIncrement=Page Left -JScrollBar.horizontal.positiveUnitIncrement=Scroll Right -JScrollBar.horizontal.positiveBlockIncrement=Page Right diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_cs.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_cs.properties deleted file mode 100644 index 44eed5389d..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_cs.properties +++ /dev/null @@ -1,61 +0,0 @@ -#Czech resource bundle created by Vity (vitywap@seznam.cz) 08/2007 -## default properties for swingx -# JXSearchPanel - -Search.matchCase=Nerozli\u0161ovat mal\u00e1/velk\u00e1 -Search.contains=obsahuje -Search.equals=je rovno -Search.endsWith=kon\u010d\u00ed na -Search.startsWith=za\u010d\u00edn\u00e1 na - -Search.wrapSearch=Po dosa\u017een\u00ed konce hledat znovu odshora -Search.backwardsSearch=Hledat Nahoru -Search.match=Naj\u00edt -Search.close=Zav\u0159\u00edt -Search.findNext=Naj\u00edt Dal\u0161\u00ed -Search.findPrevious=Naj\u00edt P\u0159edchoz\u00ed -Search.searchFieldLabel=Naj\u00edt -Search.searchFieldLabel.mnemonic=N -Search.searchTitle=Hledat - -Search.notFound=Hledan\u00fd v\u00fdraz nebyl nalezen -Search.notFoundBackground=FF6666 -Search.notFoundForeground=000000 - -XDialog.close=Zav\u0159\u00edt -XDialog.cancel=Storno -XDialog.execute=OK - - -# default properties for swingx -# JXTable column control entries -# -JXTable.column.horizontalScroll=Horizont\u00e1ln\u00ed Scrollbar -JXTable.column.packAll=P\u0159izp\u016fsobit V\u0161echny Sloupce -JXTable.column.packSelected=P\u0159izp\u016fsobit Aktivn\u00ed Sloupec - -# -# default actions in context menues for TextComponents -# -DefaultEditorKit.cut-to-clipboard=Vyjmout -DefaultEditorKit.copy-to-clipboard=Zkop\u00edrovat -DefaultEditorKit.paste-from-clipboard=Vlo\u017eit -DefaultEditorKit.delete-next=Smazat -DefaultEditorKit.select-all=Vybrat V\u0161e - -# -# default actions for context menus for ScrollBars -# -JScrollBar.vertical.minScroll=Nahoru -JScrollBar.vertical.maxScroll=Dolu -JScrollBar.vertical.negativeUnitIncrement=Posun Nahoru -JScrollBar.vertical.negativeBlockIncrement=Str\u00e1nku Nahoru -JScrollBar.vertical.positiveUnitIncrement=Posun Dolu -JScrollBar.vertical.positiveBlockIncrement=Str\u00e1nku Dolu - -JScrollBar.horizontal.minScroll=Lev\u00fd Kraj -JScrollBar.horizontal.maxScroll=Prav\u00fd Kraj -JScrollBar.horizontal.negativeUnitIncrement=Posun Vlevo -JScrollBar.horizontal.negativeBlockIncrement=Str\u00e1nka Vlevo -JScrollBar.horizontal.positiveUnitIncrement=Posun Vpravo -JScrollBar.horizontal.positiveBlockIncrement=Str\u00e1nka Vpravo diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_da.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_da.properties deleted file mode 100644 index 7a36d0449a..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_da.properties +++ /dev/null @@ -1,59 +0,0 @@ -# default properties for swingx -# JXSearchPanel - -Search.matchCase=Forskel p\u00e5 sm\u00e5/store bogstaver -Search.contains=indeholder -Search.equals=passer med -Search.endsWith=ender med -Search.startsWith=starter med - -Search.wrapSearch=Ombryd s\u00f8gning -Search.backwardsSearch=Tilbage -Search.match=Find -Search.close=Luk -Search.findNext=Find N\u00e6ste -Search.findPrevious=Find Forrige -Search.searchFieldLabel=Find -Search.searchFieldLabel.mnemonic=F -Search.searchTitle=Find - -Search.notFoundBackground=FF6666 -Search.notFoundForeground=000000 - -XDialog.close=Luk -XDialog.cancel=Annuller -XDialog.execute=OK - - -# default properties for swingx -# JXTable column control entries -# -JXTable.column.horizontalScroll=Vandret Scroll -JXTable.column.packAll=Tilpas alle kolonner -JXTable.column.packSelected=Tilpas markerede kolonner - -# -# default actions in context menues for TextComponents -# -DefaultEditorKit.cut-to-clipboard=Klip -DefaultEditorKit.copy-to-clipboard=Kopier -DefaultEditorKit.paste-from-clipboard=Inds\u00e6t -DefaultEditorKit.delete-next=Slet -DefaultEditorKit.select-all=Marker alt - -# -# default actions for context menus for ScrollBars -# -JScrollBar.vertical.minScroll=Top -JScrollBar.vertical.maxScroll=Bund -JScrollBar.vertical.negativeUnitIncrement=Scroll op -JScrollBar.vertical.negativeBlockIncrement=Side op -JScrollBar.vertical.positiveUnitIncrement=Scroll ned -JScrollBar.vertical.positiveBlockIncrement=Side ned - -JScrollBar.horizontal.minScroll=Venstre hj\u00f8rne -JScrollBar.horizontal.maxScroll=H\u00f8jre hj\u00f8rne -JScrollBar.horizontal.negativeUnitIncrement=Scroll til venstre -JScrollBar.horizontal.negativeBlockIncrement=Side til venstre -JScrollBar.horizontal.positiveUnitIncrement=Scroll til h\u00f8jre -JScrollBar.horizontal.positiveBlockIncrement=Side til h\u00f8jre diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_de.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_de.properties deleted file mode 100644 index bf344e5dcb..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_de.properties +++ /dev/null @@ -1,60 +0,0 @@ -# default properties for swingx -# Search - -Search.matchCase=Groß-/Kleinschreibung -Search.contains=enth\u00E4lt -Search.equals=ist gleich -Search.endsWith=endet mit -Search.startsWith=beginnt mit - -Search.wrapSearch=Umlaufend -Search.backwardsSearch=Rückwärts -Search.match=Weitersuchen -Search.close=Schließen -Search.findNext=Nächste -Search.findPrevious=Vorherige -Search.searchFieldLabel=Suchen nach -Search.searchFieldLabel.mnemonic=S -Search.searchTitle=Suchen - -Search.notFound=Ausdruck nicht gefunden -Search.notFoundBackground=FF6666 -Search.notFoundForeground=000000 - -XDialog.close=Schließen -XDialog.cancel=Abbrechen -XDialog.execute=OK - - -# default properties for swingx -# JXTable column control entries -# -JXTable.column.horizontalScroll=Horizontaler Rollbalken -JXTable.column.packAll=Alle Spalten anpassen -JXTable.column.packSelected=Ausgewählte Spalte anpassen - -# -# default actions in context menues for TextComponents -# -DefaultEditorKit.cut-to-clipboard=Ausschneiden -DefaultEditorKit.copy-to-clipboard=Kopieren -DefaultEditorKit.paste-from-clipboard=Einfügen -DefaultEditorKit.delete-next=Löschen -DefaultEditorKit.select-all=Alles markieren - -# -# default actions for context menue for ScrollBars -# -JScrollBar.vertical.minScroll=Oberer Rand -JScrollBar.vertical.maxScroll=Unterer Rand -JScrollBar.vertical.negativeUnitIncrement=Bildlauf oben -JScrollBar.vertical.negativeBlockIncrement=Nach oben -JScrollBar.vertical.positiveUnitIncrement=Bildlauf unten -JScrollBar.vertical.positiveBlockIncrement=Nach unten - -JScrollBar.horizontal.minScroll=Linker Rand -JScrollBar.horizontal.maxScroll=Rechter Rand -JScrollBar.horizontal.negativeUnitIncrement=Bildlauf links -JScrollBar.horizontal.negativeBlockIncrement=Seite links -JScrollBar.horizontal.positiveUnitIncrement=Bildlauf rechts -JScrollBar.horizontal.positiveBlockIncrement=Seite rechts diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_en.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_en.properties deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_es.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_es.properties deleted file mode 100644 index 8bbbd7707b..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_es.properties +++ /dev/null @@ -1,57 +0,0 @@ -# spanish properties for swingx -# JXSearchPanel -# @author: Diego Gil (dags in jdesktop) - -Search.matchCase=Coincidencia de may\u00fasculas/min\u00fasculas -Search.contains=contiene -Search.equals=igual -Search.endsWith=termina con -Search.startsWith=empieza con - -Search.wrapSearch=B\u00fasqueda Circular -Search.backwardsSearch=Hacia Atr\u00e1s -Search.match=Buscar -Search.close=Cerrar -Search.findNext=Buscar Siguiente -Search.findPrevious=Buscar Anterior -Search.searchFieldLabel=Buscar -Search.searchFieldLabel.mnemonic=B -Search.searchTitle=Buscar - -XDialog.close=Cerrar -XDialog.cancel=Cancelar -XDialog.execute=Aceptar - - -# default properties for swingx -# JXTable column control entries -# -JXTable.column.horizontalScroll=Desplazamiento Horizontal -JXTable.column.packAll=Compactar Todas las Columnas -JXTable.column.packSelected=Compactar la Columna Seleccionada - -# -# default actions in context menues for TextComponents -# -DefaultEditorKit.cut-to-clipboard=Cortar -DefaultEditorKit.copy-to-clipboard=Copiar -DefaultEditorKit.paste-from-clipboard=Pegar -DefaultEditorKit.delete-next=Eliminar -DefaultEditorKit.select-all=Seleccionar Todo - -# -# default actions for context menue for ScrollBars -# -JScrollBar.vertical.minScroll=Arriba -JScrollBar.vertical.maxScroll=Abajo -JScrollBar.vertical.negativeUnitIncrement=Desplazar hacia Arriba -JScrollBar.vertical.negativeBlockIncrement=P\u00e1gina Arriba -JScrollBar.vertical.positiveUnitIncrement=Desplazar hacia Abajo -JScrollBar.vertical.positiveBlockIncrement=P\u00e1gina Abajo - -JScrollBar.horizontal.minScroll=Borde Izquierdo -JScrollBar.horizontal.maxScroll=Borde Derecho -JScrollBar.horizontal.negativeUnitIncrement=Desplazar a la Izquierda -JScrollBar.horizontal.negativeBlockIncrement=P\u00e1gina Izquierda -JScrollBar.horizontal.positiveUnitIncrement=Desplazar a la Derecha -JScrollBar.horizontal.positiveBlockIncrement=P\u00e1gina Derecha diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_fr.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_fr.properties deleted file mode 100644 index 55127405de..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_fr.properties +++ /dev/null @@ -1,60 +0,0 @@ -# default properties for swingx -# JXSearchPanel -# @author: Florent Garin - -Search.matchCase=Respecter la casse -Search.contains=contenant -Search.equals=égale -Search.endsWith=se terminant par -Search.startsWith=commençant par - -Search.wrapSearch=Recherche circulaire -Search.backwardsSearch=Vers le haut -Search.match=Rechercher -Search.close=Fermer -Search.findNext=Suivant -Search.findPrevious=Précédent -Search.searchFieldLabel=Rechercher -Search.searchFieldLabel.mnemonic=R -Search.searchTitle=Rechercher - -Search.notFoundBackground=FF6666 -Search.notFoundForeground=000000 - -XDialog.close=Fermer -XDialog.cancel=Annuler -XDialog.execute=OK - - -# default properties for swingx -# JXTable column control entries -# -JXTable.column.horizontalScroll=Défilement horizontal -JXTable.column.packAll=Compacter toutes les colonnes -JXTable.column.packSelected=Compacter la colonne sélectionnée - -# -# default actions in context menues for TextComponents -# -DefaultEditorKit.cut-to-clipboard=Couper -DefaultEditorKit.copy-to-clipboard=Copier -DefaultEditorKit.paste-from-clipboard=Coller -DefaultEditorKit.delete-next=Supprimer -DefaultEditorKit.select-all=Tout sélectionner - -# -# default actions for context menue for ScrollBars -# -JScrollBar.vertical.minScroll=Haut -JScrollBar.vertical.maxScroll=Bas -JScrollBar.vertical.negativeUnitIncrement=Défilement vers le haut -JScrollBar.vertical.negativeBlockIncrement=Page précédente -JScrollBar.vertical.positiveUnitIncrement=Défilement vers le bas -JScrollBar.vertical.positiveBlockIncrement=Page suivante - -JScrollBar.horizontal.minScroll=Côté gauche -JScrollBar.horizontal.maxScroll=Côté droit -JScrollBar.horizontal.negativeUnitIncrement=Défilement vers la gauche -JScrollBar.horizontal.negativeBlockIncrement=Page vers la gauche -JScrollBar.horizontal.positiveUnitIncrement=Défilement vers la droite -JScrollBar.horizontal.positiveBlockIncrement=Page vers la droite diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_it.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_it.properties deleted file mode 100644 index 32698fd9a2..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_it.properties +++ /dev/null @@ -1,59 +0,0 @@ -# default properties for swingx -# JXSearchPanel - -Search.matchCase=Maiuscole/Minuscole -Search.contains=contiene -Search.equals=uguale -Search.endsWith=termina per -Search.startsWith=inizia con - -Search.wrapSearch=Ricomincia dall'Inizio -Search.backwardsSearch=Indietro -Search.match=Trova -Search.close=Chiudi -Search.findNext=Trova Prossimo -Search.findPrevious=Trova Precedente -Search.searchFieldLabel=Trova -Search.searchFieldLabel.mnemonic=F -Search.searchTitle=Trova - -Search.notFoundBackground=FF6666 -Search.notFoundForeground=000000 - -XDialog.close=Chiudi -XDialog.cancel=Annulla -XDialog.execute=OK - - -# default properties for swingx -# JXTable column control entries -# -JXTable.column.horizontalScroll=Muovi in Orizzontale -JXTable.column.packAll=Auto-dimensiona Colonne -JXTable.column.packSelected=Auto-dimensiona Colonna Selezionata - -# -# default actions in context menues for TextComponents -# -DefaultEditorKit.cut-to-clipboard=Taglia -DefaultEditorKit.copy-to-clipboard=Copia -DefaultEditorKit.paste-from-clipboard=Incolla -DefaultEditorKit.delete-next=Cancella -DefaultEditorKit.select-all=Seleziona Tutto - -# -# default actions for context menus for ScrollBars -# -JScrollBar.vertical.minScroll=Inizio -JScrollBar.vertical.maxScroll=Fine -JScrollBar.vertical.negativeUnitIncrement=Sposta Su -JScrollBar.vertical.negativeBlockIncrement=Pagina Su -JScrollBar.vertical.positiveUnitIncrement=Sposta Gi\u00F9 -JScrollBar.vertical.positiveBlockIncrement=Sposta Gi\u00F9 - -JScrollBar.horizontal.minScroll=Bordo Sinistro -JScrollBar.horizontal.maxScroll=Bordo Destro -JScrollBar.horizontal.negativeUnitIncrement=Sposta a Sinistra -JScrollBar.horizontal.negativeBlockIncrement=Pagina a Sinistra -JScrollBar.horizontal.positiveUnitIncrement=Sposta a Destra -JScrollBar.horizontal.positiveBlockIncrement=Pagina a Destra diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_nl.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_nl.properties deleted file mode 100644 index 0027c2bc2b..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_nl.properties +++ /dev/null @@ -1,59 +0,0 @@ -# default properties for swingx -# JXSearchPanel - -Search.matchCase=Hoofdlettergevoelig -Search.contains=bevat -Search.equals=gelijk aan -Search.endsWith=eindigd met -Search.startsWith=begint met - -Search.wrapSearch=Herhaaldelijk Doorzoeken -Search.backwardsSearch=Achterwaarts Zoeken -Search.match=Zoeken -Search.close=Sluiten -Search.findNext=Vind Volgende -Search.findPrevious=Vind Vorige -Search.searchFieldLabel=Zoek -Search.searchFieldLabel.mnemonic=F -Search.searchTitle=Zoeken - -Search.notFoundBackground=FF6666 -Search.notFoundForeground=000000 - -XDialog.close=Sluiten -XDialog.cancel=Annuleren -XDialog.execute=OK - - -# default properties for swingx -# JXTable column control entries -# -JXTable.column.horizontalScroll=Horizontaal Scrollen -JXTable.column.packAll=Pack Alle Kolommen -JXTable.column.packSelected=Pack Geselecteerde Kolom - -# -# default actions in context menues for TextComponents -# -DefaultEditorKit.cut-to-clipboard=Knippen -DefaultEditorKit.copy-to-clipboard=Kopieeren -DefaultEditorKit.paste-from-clipboard=Plakken -DefaultEditorKit.delete-next=Verwijderen -DefaultEditorKit.select-all=Selecteer alles - -# -# default actions for context menus for ScrollBars -# -JScrollBar.vertical.minScroll=Boven -JScrollBar.vertical.maxScroll=Onder -JScrollBar.vertical.negativeUnitIncrement=Scroll Up -JScrollBar.vertical.negativeBlockIncrement=Page Up -JScrollBar.vertical.positiveUnitIncrement=Scroll Down -JScrollBar.vertical.positiveBlockIncrement=Page Down - -JScrollBar.horizontal.minScroll=Linker kantlijn -JScrollBar.horizontal.maxScroll=Rechter kantlijn -JScrollBar.horizontal.negativeUnitIncrement=Scroll Links -JScrollBar.horizontal.negativeBlockIncrement=Page Links -JScrollBar.horizontal.positiveUnitIncrement=Scroll Rechts -JScrollBar.horizontal.positiveBlockIncrement=Page Rechts diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_pl.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_pl.properties deleted file mode 100644 index 7aaf58a7a5..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_pl.properties +++ /dev/null @@ -1,59 +0,0 @@ -# default properties for swingx -# JXSearchPanel - -Search.matchCase=Uwzgl\u0119dniaj wielko\u015B\u0107 liter -Search.contains=zawieraj\u0105ce -Search.equals=r\u00F3wne -Search.endsWith=ko\u0144cz\u0105ce si\u0119 na -Search.startsWith=zaczynaj\u0105ce si\u0119 od - -Search.wrapSearch=Zawi\u0144 wyszukiwanie -Search.backwardsSearch=Poszukiwanie w ty\u0142 -Search.match=Szukaj -Search.close=Zamknij -Search.findNext=Szukaj nast\u0119pnego -Search.findPrevious=Szukaj poprzedniego -Search.searchFieldLabel=Szukaj -Search.searchFieldLabel.mnemonic=S -Search.searchTitle=Szukaj - -Search.notFoundBackground=FF6666 -Search.notFoundForeground=000000 - -XDialog.close=Zamknij -XDialog.cancel=Anuluj -XDialog.execute=OK - - -# default properties for swingx -# JXTable column control entries -# -JXTable.column.horizontalScroll=Przewijanie poziome -JXTable.column.packAll=Upakuj wszystkie kolumny -JXTable.column.packSelected=Upakuj wybrane kolumny - -# -# default actions in context menues for TextComponents -# -DefaultEditorKit.cut-to-clipboard=Wytnij -DefaultEditorKit.copy-to-clipboard=Kopiuj -DefaultEditorKit.paste-from-clipboard=Wklej -DefaultEditorKit.delete-next=Usu\u0144 -DefaultEditorKit.select-all=Zaznacz wszystko - -# -# default actions for context menus for ScrollBars -# -JScrollBar.vertical.minScroll=G\u00F3ra -JScrollBar.vertical.maxScroll=D\u00F3\u0142 -JScrollBar.vertical.negativeUnitIncrement=Przewi\u0144 w g\u00F3r\u0119 -JScrollBar.vertical.negativeBlockIncrement=Strona w g\u00F3r\u0119 -JScrollBar.vertical.positiveUnitIncrement=Przewi\u0144 w d\u00F3\u0142 -JScrollBar.vertical.positiveBlockIncrement=Strona w d\u00F3\u0142 - -JScrollBar.horizontal.minScroll=Lewa kraw\u0119d\u017A -JScrollBar.horizontal.maxScroll=Prawa kraw\u0119d\u017A -JScrollBar.horizontal.negativeUnitIncrement=Przewi\u0144 w lewo -JScrollBar.horizontal.negativeBlockIncrement=Strona w lewo -JScrollBar.horizontal.positiveUnitIncrement=Przewi\u0144 w prawo -JScrollBar.horizontal.positiveBlockIncrement=Strona w prawo diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_pt_BR.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_pt_BR.properties deleted file mode 100644 index 2382819aa6..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_pt_BR.properties +++ /dev/null @@ -1,59 +0,0 @@ -# default properties for swingx -# JXSearchPanel - -Search.matchCase=Diferenciar Mai\u00FAscula/Min\u00FAscula -Search.contains=Cont\u00E9m -Search.equals=Igual -Search.endsWith=Come\u00E7a com -Search.startsWith=Termina com - -Search.wrapSearch=Circular -Search.backwardsSearch=Pesquisa para Tr\u00E1s -Search.match=Localizar -Search.close=Fechar -Search.findNext=Localizar Pr\u00F3xima -Search.findPrevious=Localizar Anterior -Search.searchFieldLabel=Localizar -Search.searchFieldLabel.mnemonic=F -Search.searchTitle=Localizar - -Search.notFoundBackground=FF6666 -Search.notFoundForeground=000000 - -XDialog.close=Fechar -XDialog.cancel=Cancelar -XDialog.execute=OK - - -# default properties for swingx -# JXTable column control entries -# -JXTable.column.horizontalScroll=Rolagem Horizontal -JXTable.column.packAll=Reduzir Todas as Colunas -JXTable.column.packSelected=Reduzir Colunas Selecionadas - -# -# default actions in context menues for TextComponents -# -DefaultEditorKit.cut-to-clipboard=Recortar -DefaultEditorKit.copy-to-clipboard=Copiar -DefaultEditorKit.paste-from-clipboard=Colar -DefaultEditorKit.delete-next=Excluir -DefaultEditorKit.select-all=Selecionar Tudo - -# -# default actions for context menus for ScrollBars -# -JScrollBar.vertical.minScroll=In\u00EDcio -JScrollBar.vertical.maxScroll=Fim -JScrollBar.vertical.negativeUnitIncrement=Rolar para Cima -JScrollBar.vertical.negativeBlockIncrement=P\u00E1gina Anterior -JScrollBar.vertical.positiveUnitIncrement=Rolar para Baixo -JScrollBar.vertical.positiveBlockIncrement=Pr\u00F3xima P\u00E1gina - -JScrollBar.horizontal.minScroll=Limite Esquerdo -JScrollBar.horizontal.maxScroll=Limite Direito -JScrollBar.horizontal.negativeUnitIncrement=Rolar para Esquerda -JScrollBar.horizontal.negativeBlockIncrement=P\u00E1gina Esquerda -JScrollBar.horizontal.positiveUnitIncrement=Rolar para Direita -JScrollBar.horizontal.positiveBlockIncrement=P\u00E1gina Direita diff --git a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_sv.properties b/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_sv.properties deleted file mode 100644 index af3c70d2e8..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/basic/resources/swingx_sv.properties +++ /dev/null @@ -1,59 +0,0 @@ -# default properties for swingx -# JXSearchPanel - -Search.matchCase=Matcha gemener/VERSALER -Search.contains=inneh\u00E5ller -Search.equals=lika med -Search.endsWith=slutar med -Search.startsWith=b\u00F6rjar med - -Search.wrapSearch=S\u00F6k runt -Search.backwardsSearch=Bak\u00E5t -Search.match=S\u00F6k -Search.close=St\u00E4ng -Search.findNext=S\u00F6k n\u00E4sta -Search.findPrevious=S\u00F6k tidigare -Search.searchFieldLabel=S\u00F6k -Search.searchFieldLabel.mnemonic=k -Search.searchTitle=S\u00F6k - -Search.notFoundBackground=FF6666 -Search.notFoundForeground=000000 - -XDialog.close=St\u00E4ng -XDialog.cancel=Avbryt -XDialog.execute=OK - - -# default properties for swingx -# JXTable column control entries -# -JXTable.column.horizontalScroll=Horizontell rullning -JXTable.column.packAll=Anpassa kolumnbredd -JXTable.column.packSelected=Anpassa valda kolumners bredd - -# -# default actions in context menues for TextComponents -# -DefaultEditorKit.cut-to-clipboard=Klipp ut -DefaultEditorKit.copy-to-clipboard=Kopiera -DefaultEditorKit.paste-from-clipboard=Klistra in -DefaultEditorKit.delete-next=Radera -DefaultEditorKit.select-all=Markera allt - -# -# default actions for context menus for ScrollBars -# -JScrollBar.vertical.minScroll=\u00D6verkant -JScrollBar.vertical.maxScroll=Nederkant -JScrollBar.vertical.negativeUnitIncrement=Rulla upp\u00E5t -JScrollBar.vertical.negativeBlockIncrement=En sida upp -JScrollBar.vertical.positiveUnitIncrement=Rulla ned\u00E5t -JScrollBar.vertical.positiveBlockIncrement=En sida ned - -JScrollBar.horizontal.minScroll=V\u00E4nsterkant -JScrollBar.horizontal.maxScroll=H\u00F6gerkant -JScrollBar.horizontal.negativeUnitIncrement=Rulla \u00E5t v\u00E4nster -JScrollBar.horizontal.negativeBlockIncrement=En sida \u00E5t v\u00E4nster -JScrollBar.horizontal.positiveUnitIncrement=Rulla \u00E5t h\u00F6ger -JScrollBar.horizontal.positiveBlockIncrement=En sida \u00E5t h\u00F6ger diff --git a/src/main/java/org/jdesktop/swingx/plaf/linux/LinuxLookAndFeelAddons.java b/src/main/java/org/jdesktop/swingx/plaf/linux/LinuxLookAndFeelAddons.java deleted file mode 100644 index 570db6471a..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/linux/LinuxLookAndFeelAddons.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * $Id: LinuxLookAndFeelAddons.java 4092 2011-11-30 18:04:36Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf.linux; - -import org.apache.commons.lang3.SystemUtils; -import org.jdesktop.swingx.plaf.LookAndFeelAddons; -import org.jdesktop.swingx.plaf.basic.BasicLookAndFeelAddons; -import org.kohsuke.MetaInfServices; - -import static javax.swing.UIManager.getLookAndFeel; -import static javax.swing.UIManager.getSystemLookAndFeelClassName; - -@MetaInfServices(LookAndFeelAddons.class) -public class LinuxLookAndFeelAddons extends BasicLookAndFeelAddons { - /** - * {@inheritDoc} - */ - @Override - protected boolean matches() { - return isSystemAddon() && getSystemLookAndFeelClassName().equals(getLookAndFeel().getClass().getName()); - } - - /** - * {@inheritDoc} - */ - @Override - protected boolean isSystemAddon() { - return SystemUtils.IS_OS_LINUX; - } - - /** - * {@inheritDoc} - */ - @Override - public void initialize() { - super.initialize(); - - //Issue 1297: added border to ensure non-null insets - // JW: moved into Table/ListAddon - // -// Border b = UIManagerExt.getSafeBorder("Table.focusSelectedCellHighlightBorder", BorderFactory.createEmptyBorder()); -// -// if (b instanceof UIResource) { -// UIManager.put("Table.focusSelectedCellHighlightBorder", new BorderUIResource(b)); -// } - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/linux/package-info.java b/src/main/java/org/jdesktop/swingx/plaf/linux/package-info.java deleted file mode 100644 index 2f63c1e20a..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/linux/package-info.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * $Id: package-info.java 4028 2011-06-03 19:32:19Z kschaefe $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -/** - * Provides linux specific implementation of pluggable look-and-feel for SwingX components together with a - * mechanism to support custom component look-and-feels. - */ -package org.jdesktop.swingx.plaf.linux; - diff --git a/src/main/java/org/jdesktop/swingx/plaf/linux/resources/combo-gtk.png b/src/main/java/org/jdesktop/swingx/plaf/linux/resources/combo-gtk.png deleted file mode 100644 index c767ea97e21116b3f377dc39fb4a4715347668e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2904 zcmV-e3#asnP)KLZ*U+5Lu!Sk^o_Z5E4Meg@_7P6crJiNL9pw)e1;Xm069{HJUZAPk55R%$-RIA z6-eL&AQ0xu!e<4=008gy@A0LT~suv4>S3ILP<0Bm`DLLvaF4FK%)Nj?Pt*r}7;7Xa9z9H|HZjR63e zC`Tj$K)V27Re@400>HumpsYY5E(E}?0f1SyGDiY{y#)Yvj#!WnKwtoXnL;eg03bL5 z07D)V%>y7z1E4U{zu>7~aD})?0RX_umCct+(lZpemCzb@^6=o|A>zVpu|i=NDG+7} zl4`aK{0#b-!z=TL9Wt0BGO&T{GJWpjryhdijfaIQ&2!o}p04JRKYg3k&Tf zVxhe-O!X z{f;To;xw^bEES6JSc$k$B2CA6xl)ltA<32E66t?3@gJ7`36pmX0IY^jz)rRYwaaY4 ze(nJRiw;=Qb^t(r^DT@T3y}a2XEZW-_W%Hszxj_qD**t_m!#tW0KDiJT&R>6OvVTR z07RgHDzHHZ48atvzz&?j9lXF70$~P3Knx_nJP<+#`N z#-MZ2bTkiLfR>_b(HgWKJ%F~Nr_oF3b#wrIijHG|(J>BYjM-sajE6;FiC7vY#};Gd zST$CUHDeuEH+B^pz@B062qXfFfD`NpUW5?BY=V%GM_5c)L#QR}BeW8_2v-S%gfYS= zB9o|3v?Y2H`NVi)In3rTB8+ej^> zQ=~r95NVuDChL%G$=>7$vVg20myx%S50Foi`^m%Pw-h?Xh~i8Mq9jtJloCocWk2Nv zrJpiFnV_ms&8eQ$2&#xWpIS+6pmtC%Q-`S&GF4Q#^mhymh7E(qNMa}%YZ-ePrx>>xFPTiH1=E+A$W$=bG8>s^ zm=Bn5Rah$aDtr}@$`X}2l~$F0mFKEdRdZE8)p@E5RI61Ft6o-prbbn>P~)iy)E2AN zsU20jsWz_8Qg>31P|s0cqrPALg8E|(vWA65poU1JRAaZs8I2(p#xiB`SVGovRs-uS zYnV-9TeA7=Om+qP8+I>yOjAR1s%ETak!GFdam@h^# z)@rS0t$wXH+Irf)+G6c;?H29p+V6F6oj{!|o%K3xI`?%6x;DB|x`n#ibhIR?(H}Q3Gzd138Ei2)WAMz7W9Vy`X}HnwgyEn!VS)>mv$8&{hQn>w4zwy3R}t;BYlZQm5)6pty=DfLrs+A-|>>;~;Q z_F?uV_HFjh9n2gO9o9Q^JA86v({H5aB!kjoO6 zc9$1ZZKsN-Zl8L~mE{`ly3)1N^`o1+o7}D0ZPeY&J;i;i`%NyJ8_8Y6J?}yE@b_5a zam?eLr<8@mESk|3$_SkmS{wQ>%qC18))9_|&j{ZT zes8AvOzF(F2#DZEY>2oYX&IRp`F#{ADl)1r>QS^)ba8a|EY_^#S^HO&t^Rgqwv=MZThqqEWH8 zxJo>d=ABlR_Bh=;eM9Tw|Ih34~oTE|= zX_mAr*D$vzw@+p(E0Yc6dFE}(8oqt`+R{gE3x4zjX+Sb3_cYE^= zgB=w+-tUy`ytONMS8KgRef4hA?t0j zufM;t32jm~jUGrkaOInTZ`zyfns>EuS}G30LFK_G-==(f<51|K&cocp&EJ`SxAh3? zNO>#LI=^+SEu(FqJ)ynt=!~PC9bO$rzPJB=?=j6w@a-(u02P7 zaQ)#(uUl{HW%tYNS3ItC^iAtK(eKlL`f9+{bJzISE?u8_z3;~C8@FyI-5j_jy7l;W z_U#vU3hqqYU3!mrul&B+{ptt$59)uk{;_4iZQ%G|z+lhASr6|H35TBkl>gI*;nGLU zN7W-nBaM%pA0HbH8olyl&XeJ%vZoWz%6?Y=dFykl=imL}`%BMQ{Mhgd`HRoLu6e2R za__6DuR6yg#~-}Tc|Gx_{H@O0eebyMy5GmWADJlpK>kqk(fVV@r_fLLKIeS?{4e)} z^ZO;zpECde00d`2O+f$vv5tKEQIh}w03c&XQcVB=dL;k=fP(-4`Tqa_faw4Lbua(` z>RI+y?e7jKeZ#YO-C0Ch=3K~#9!e9AEjgFqNW;Sq9|SZuXTyocmiTL~^~5j>4K z%7WnEx`OgjC^jJ(c-4IByB|&N%>NW&%hleIf5X0A?oHWU|S$uRFtQ zxKQ(G4s`yvvK2{EnM>6^Tb6ZE2tng}RL;55xz7NoF<&-s&Dy;H0000= fatal, it should say "Exit Application". If there is a report - * action but error < fatal, it should say "Don't Send" for ok, "Send Report" for - * the report button. If there is a report action and the error >= fatal, then - * one button should say "Exit", and the report button should say - * "Send Report and Exit". - * - * Whenever either button is clicked (ok button or report button), the "close dialog" - * procedure should occur. - * - * @author rbair - */ -public class MacOSXErrorPaneUI extends BasicErrorPaneUI { - private JLabel titleLabel; - private JEditorPane disclaimerText; // this is actually part of the details!!! - - //---------------------------------------------------------- constructor - /** Creates a new instance of BasicErrorPanelUI */ - public MacOSXErrorPaneUI() { - super(); - } - - @Override - protected void configureDetailsButton(boolean expanded) { - if (expanded) { - detailButton.setText(UIManagerExt.getString(CLASS_NAME + ".details_contract_text", detailButton.getLocale())); - detailButton.setIcon(UIManager.getIcon("Tree.expandedIcon")); - } else { - detailButton.setText(UIManagerExt.getString(CLASS_NAME + ".details_expand_text", detailButton.getLocale())); - detailButton.setIcon(UIManager.getIcon("Tree.collapsedIcon")); - } - } - - @Override - protected void configureReportAction(AbstractActionExt reportAction) { - reportAction.setName(UIManagerExt.getString(CLASS_NAME + ".report_button_text", pane.getLocale())); -// reportButton.setText("Send Report To Apple"); -// reportButton.setPreferredSize(new Dimension(100, 30)); -// reportButton.setMinimumSize(new Dimension(100, 30)); - } - - public static ComponentUI createUI(JComponent c) { - return new MacOSXErrorPaneUI(); - } - - /** - * {@inheritDoc} - */ - @Override - public JFrame getErrorFrame(Component owner) { - JFrame frame = super.getErrorFrame(owner); - frame.setTitle(" "); - return frame; - } - - /** - * {@inheritDoc} - */ - @Override - public JDialog getErrorDialog(Component owner) { - JDialog dlg = super.getErrorDialog(owner); - dlg.setTitle(" "); - return dlg; - } - - /** - * {@inheritDoc} - */ - @Override - public JInternalFrame getErrorInternalFrame(Component owner) { - JInternalFrame frame = super.getErrorInternalFrame(owner); - frame.setTitle(" "); - return frame; - } - - /** - * {@inheritDoc} - */ - @Override - protected LayoutManager createErrorPaneLayout() { - createExtraComponents(); - GridBagLayout layout = new GridBagLayout(); - try { - layout.addLayoutComponent(iconLabel, new GridBagConstraints(0, 0, 1, 2, 0.0, 0.0, GridBagConstraints.NORTH, GridBagConstraints.NONE, new Insets(0, 0, 0, 17), 0, 0)); - layout.addLayoutComponent(titleLabel, new GridBagConstraints(1, 0, 2, 1, 1.0, 0.0, GridBagConstraints.NORTH, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 12, 0), 0 ,0)); - layout.addLayoutComponent(errorScrollPane,new GridBagConstraints(1, 1, 2, 1, 1.0, 1.0, GridBagConstraints.NORTH, GridBagConstraints.BOTH, new Insets(0, 0, 10, 0), 0, 0)); - layout.addLayoutComponent(detailButton, new GridBagConstraints(0, 2, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 6, 0), 0, 0)); - layout.addLayoutComponent(detailsPanel, new GridBagConstraints(0, 3, 3, 1, 1.0, 1.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 0, 6, 0), 0 ,0)); - layout.addLayoutComponent(disclaimerText, new GridBagConstraints(0, 4, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 6, 0), 0, 0)); - layout.addLayoutComponent(closeButton, new GridBagConstraints(1, 5, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 0, 5), 0, 0)); - layout.addLayoutComponent(reportButton, new GridBagConstraints(2, 5, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); - } catch (Exception e) { - e.printStackTrace(); - } - return layout; - } - - /** - * {@inheritDoc} - */ - @Override - protected LayoutManager createDetailPanelLayout() { - GridBagLayout layout = new GridBagLayout(); - layout.addLayoutComponent(detailsScrollPane, new GridBagConstraints(0,0,1,1,1.0,1.0,GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0,0,0,0),0,0)); - copyToClipboardButton.setVisible(false); - return layout; - } - - /** - * {@inheritDoc} - */ - @Override - protected void reinit() { - super.reinit(); - ErrorInfo info = pane == null ? null : pane.getErrorInfo(); - titleLabel.setText(info == null ? "Unknown Error" : info.getTitle()); - - Object finePrint = pane.getClientProperty("fine-print"); - String text = finePrint == null ? null : finePrint.toString(); - disclaimerText.setText(text); - disclaimerText.setVisible(text != null); - - if (info != null && info.getErrorLevel() == ErrorLevel.FATAL) { - closeButton.setText(UIManagerExt.getString(CLASS_NAME + ".fatal_button_text", closeButton.getLocale())); - } else { - closeButton.setText(UIManagerExt.getString(CLASS_NAME + ".ok_button_text", closeButton.getLocale())); - } - } - - /** - * {@inheritDoc} - */ - @Override - protected int getDetailsHeight() { - return 150; - } - - private void createExtraComponents() { - titleLabel = new JLabel("Unknown Error"); - titleLabel.setFont(titleLabel.getFont().deriveFont(Font.BOLD)); - pane.add(titleLabel); - - Font f = errorMessage.getFont(); - if (f != null) { - errorMessage.setFont(f.deriveFont(f.getSize() - 2f)); - } - - disclaimerText = new JEditorPane(); - disclaimerText.setContentType("text/html"); - disclaimerText.setVisible(false); - disclaimerText.setEditable(false); - disclaimerText.setOpaque(false); - disclaimerText.putClientProperty(JXEditorPane.HONOR_DISPLAY_PROPERTIES, Boolean.TRUE); - if (f != null) { - disclaimerText.setFont(f.deriveFont(f.getSize() - 2f)); - } - pane.add(disclaimerText); - - detailButton.setBorderPainted(false); - detailButton.setContentAreaFilled(false); - detailButton.setBorder(BorderFactory.createEmptyBorder()); - detailButton.setMargin(new Insets(0, 0, 0 ,0)); - detailButton.setIcon(UIManager.getIcon("Tree.collapsedIcon")); - detailButton.setText(UIManagerExt.getString(CLASS_NAME + ".details_expand_text", detailButton.getLocale())); - - closeButton.setText(UIManagerExt.getString(CLASS_NAME + ".ok_button_text", closeButton.getLocale())); - } -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/plaf/macosx/MacOSXLookAndFeelAddons.java b/src/main/java/org/jdesktop/swingx/plaf/macosx/MacOSXLookAndFeelAddons.java deleted file mode 100644 index 7cef876040..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/macosx/MacOSXLookAndFeelAddons.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * $Id: MacOSXLookAndFeelAddons.java 4092 2011-11-30 18:04:36Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf.macosx; - -import org.apache.commons.lang3.SystemUtils; -import org.jdesktop.swingx.plaf.LookAndFeelAddons; -import org.jdesktop.swingx.plaf.basic.BasicLookAndFeelAddons; -import org.kohsuke.MetaInfServices; - -import static javax.swing.UIManager.getLookAndFeel; -import static javax.swing.UIManager.getSystemLookAndFeelClassName; - -@MetaInfServices(LookAndFeelAddons.class) -public class MacOSXLookAndFeelAddons extends BasicLookAndFeelAddons { - /** - * {@inheritDoc} - */ - @Override - protected boolean matches() { - return isSystemAddon() && getSystemLookAndFeelClassName().equals(getLookAndFeel().getClass().getName()); - } - - /** - * {@inheritDoc} - */ - @Override - protected boolean isSystemAddon() { - return SystemUtils.IS_OS_MAC_OSX; - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/macosx/MacOSXStatusBarUI.java b/src/main/java/org/jdesktop/swingx/plaf/macosx/MacOSXStatusBarUI.java deleted file mode 100644 index 4eb1bbcb26..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/macosx/MacOSXStatusBarUI.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * $Id: MacOSXStatusBarUI.java 3475 2009-08-28 08:30:47Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.plaf.macosx; - -import org.jdesktop.swingx.JXStatusBar; -import org.jdesktop.swingx.plaf.basic.BasicStatusBarUI; - -import javax.swing.*; -import javax.swing.plaf.ComponentUI; - -/** - * - * @author rbair - */ -public class MacOSXStatusBarUI extends BasicStatusBarUI { - /** - * Returns an instance of the UI delegate for the specified component. - * Each subclass must provide its own static createUI - * method that returns an instance of that UI delegate subclass. - * If the UI delegate subclass is stateless, it may return an instance - * that is shared by multiple components. If the UI delegate is - * stateful, then it should return a new instance per component. - * The default implementation of this method throws an error, as it - * should never be invoked. - */ - public static ComponentUI createUI(JComponent c) { - return new MacOSXStatusBarUI(); - } - - /** - * {@inheritDoc} - */ - @Override - protected void installDefaults(JXStatusBar sb) { - super.installDefaults(sb); - - LookAndFeel.installProperty(statusBar, "opaque", Boolean.FALSE); - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/macosx/package-info.java b/src/main/java/org/jdesktop/swingx/plaf/macosx/package-info.java deleted file mode 100644 index 8552e38246..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/macosx/package-info.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -/** - * Provides macos specific implementation of pluggable look-and-feel for SwingX components together with a - * mechanism to support custom component look-and-feels. - */ -package org.jdesktop.swingx.plaf.macosx; - diff --git a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane.properties b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane.properties deleted file mode 100644 index 8f817cf7b2..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane.properties +++ /dev/null @@ -1,7 +0,0 @@ -# -# JXErrorPanel properties -# -JXErrorPane.report_button_text=Send Report -JXErrorPane.ok_button_text=Don't Send -JXErrorPane.details_expand_text=Details -JXErrorPane.details_contract_text=Details diff --git a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_cs.properties b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_cs.properties deleted file mode 100644 index 7c9fdefba3..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_cs.properties +++ /dev/null @@ -1,7 +0,0 @@ -# -# JXErrorPanel properties -# -JXErrorPane.report_button_text=Ozn\u00e1mit Chybu -JXErrorPane.ok_button_text=Neodes\u00edlat -JXErrorPane.details_expand_text=Detaily -JXErrorPane.details_contract_text=Detaily diff --git a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_da.properties b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_da.properties deleted file mode 100644 index 5c8c06f36c..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_da.properties +++ /dev/null @@ -1,7 +0,0 @@ -# -# JXErrorPanel properties -# -JXErrorPane.report_button_text=Afsend rapport -JXErrorPane.ok_button_text=Send ikke -JXErrorPane.details_expand_text=Detaljer -JXErrorPane.details_contract_text=Detaljer diff --git a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_de.properties b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_de.properties deleted file mode 100644 index e856bcd3f9..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_de.properties +++ /dev/null @@ -1,7 +0,0 @@ -# -# JXErrorPanel properties -# -JXErrorPane.report_button_text=Bericht senden -JXErrorPane.ok_button_text=Nicht senden -JXErrorPane.details_expand_text=Details -JXErrorPane.details_contract_text=Details diff --git a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_es.properties b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_es.properties deleted file mode 100644 index 6d77238db3..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_es.properties +++ /dev/null @@ -1,3 +0,0 @@ -# -# JXErrorPanel properties -# diff --git a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_fr.properties b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_fr.properties deleted file mode 100644 index ad0a63b8c2..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_fr.properties +++ /dev/null @@ -1,7 +0,0 @@ -# -# JXErrorPanel properties -# -JXErrorPane.report_button_text=Envoyer le rapport -JXErrorPane.ok_button_text=Ne pas envoyer -JXErrorPane.details_expand_text=Détails -JXErrorPane.details_contract_text=Détails diff --git a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_it.properties b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_it.properties deleted file mode 100644 index 4a9856d2d9..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_it.properties +++ /dev/null @@ -1,7 +0,0 @@ -# -# JXErrorPanel properties -# -JXErrorPane.report_button_text=Invia Resoconto -JXErrorPane.ok_button_text=Non Inviare -JXErrorPane.details_expand_text=Dettagli -JXErrorPane.details_contract_text=Dettagli diff --git a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_nl.properties b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_nl.properties deleted file mode 100644 index c8780e5300..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_nl.properties +++ /dev/null @@ -1,7 +0,0 @@ -# -# JXErrorPanel properties -# -JXErrorPane.report_button_text=Verstuur Rapport -JXErrorPane.ok_button_text=Niet versturen -JXErrorPane.details_expand_text=Details -JXErrorPane.details_contract_text=Details diff --git a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_pt_BR.properties b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_pt_BR.properties deleted file mode 100644 index 42643d3b30..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_pt_BR.properties +++ /dev/null @@ -1,7 +0,0 @@ -# -# JXErrorPanel properties -# -JXErrorPane.report_button_text=Envia Relat\u00F3rio -JXErrorPane.ok_button_text=N\u00E3o Envia -JXErrorPane.details_expand_text=Detalhes -JXErrorPane.details_contract_text=Detalhes diff --git a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_sv.properties b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_sv.properties deleted file mode 100644 index 5a74625180..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/ErrorPane_sv.properties +++ /dev/null @@ -1,7 +0,0 @@ -# -# JXErrorPanel properties -# -JXErrorPane.report_button_text=S\u00E4nd rapport -JXErrorPane.ok_button_text=S\u00E4nd inte -JXErrorPane.details_expand_text=Detaljer -JXErrorPane.details_contract_text=Detaljer diff --git a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/clear.png b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/clear.png deleted file mode 100644 index 7bf98a52ab5bc76d592ad3b489cf3568b773670f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 322 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh1|;P@bT0xa&H|6fVg?3oVGw3ym^DWNDEP(G z#W6%<;?heSy_g*(SRTB8q{(Q>b0brE+jOPYl{udTD!Fg(>Dusqg3Y?350PUM z*I!y+O$d7+xBO+6;j#8z>!x2YIh$rR*Dme${{Fs}LXoA;XC~J~T{e+k@w!Sfd5UlD zPj?H^XDcnam7YIa(e{joRioZAw_iP{{n?wUqBj>Nyua`$GJdDh??1&S?HLM;%OXTP ROF^N<;OXk;vd$@?2>@%mg^U0I diff --git a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/clear_pressed.png b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/clear_pressed.png deleted file mode 100644 index 5fc7f8e3c8a19d0c5da280f6f8c060310db80ef9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 412 zcmV;N0b~A&P)`xJeMkTFk@hX{EDxr;a$C_%V` zICSYHty2Xl2rl=+ZLkPEbhRXaoh+Y63Yo8lB%kjx~|twh-u(l4}hX5-mCzis;b6v4n_k` zL}30ju|$?-GaJu!?zwIP^W2F&jup9sLWrbo+fQfDl@IKJD`2RU+G(x3vMfKWNJ`mh ztvg4w1zx;f0m6S)+XGL)(l$q%Mx2{J9jn~4t(JRF&Jw0E&4)L z*zyKjg~VV;C76n)wM8tVyUFFI6cR>l&d$l6o%t)@tAk5i;5=(9EbxJ4X{>6dEA((? zcdhY;IULkX*EspX1jjka2Fq5C*FzgZ2;HWmrnPf#2l^Oc6hauMlv0dwTC^DBG^Lb6 z2;-umU*R@K05Qgg!C-JVo6Vk}oQK2VV=cs@!aa`a(TW*%m|)Y|GmO1;XuGGqQDcl6 zude0P@m$C$0JMMk+9zHxFXSw3i8!?-WLK^6DkE1lF8>Vq Y7u|zw!w`0Re*gdg07*qoM6N<$f~%W}3;+NC diff --git a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/combo-osx.png b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/combo-osx.png deleted file mode 100644 index 658e48ac21bbef723b977026c77948e8c5a5f9e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2850 zcmV+-3*GdIP)KLZ*U+5Lu!Sk^o_Z5E4Meg@_7P6crJiNL9pw)e1;Xm069{HJUZAPk55R%$-RIA z6-eL&AQ0xu!e<4=008gy@A0LT~suv4>S3ILP<0Bm`DLLvaF4FK%)Nj?Pt*r}7;7Xa9z9H|HZjR63e zC`Tj$K)V27Re@400>HumpsYY5E(E}?0f1SyGDiY{y#)Yvj#!WnKwtoXnL;eg03bL5 z07D)V%>y7z1E4U{zu>7~aD})?0RX_umCct+(lZpemCzb@^6=o|A>zVpu|i=NDG+7} zl4`aK{0#b-!z=TL9Wt0BGO&T{GJWpjryhdijfaIQ&2!o}p04JRKYg3k&Tf zVxhe-O!X z{f;To;xw^bEES6JSc$k$B2CA6xl)ltA<32E66t?3@gJ7`36pmX0IY^jz)rRYwaaY4 ze(nJRiw;=Qb^t(r^DT@T3y}a2XEZW-_W%Hszxj_qD**t_m!#tW0KDiJT&R>6OvVTR z07RgHDzHHZ48atvzz&?j9lXF70$~P3Knx_nJP<+#`N z#-MZ2bTkiLfR>_b(HgWKJ%F~Nr_oF3b#wrIijHG|(J>BYjM-sajE6;FiC7vY#};Gd zST$CUHDeuEH+B^pz@B062qXfFfD`NpUW5?BY=V%GM_5c)L#QR}BeW8_2v-S%gfYS= zB9o|3v?Y2H`NVi)In3rTB8+ej^> zQ=~r95NVuDChL%G$=>7$vVg20myx%S50Foi`^m%Pw-h?Xh~i8Mq9jtJloCocWk2Nv zrJpiFnV_ms&8eQ$2&#xWpIS+6pmtC%Q-`S&GF4Q#^mhymh7E(qNMa}%YZ-ePrx>>xFPTiH1=E+A$W$=bG8>s^ zm=Bn5Rah$aDtr}@$`X}2l~$F0mFKEdRdZE8)p@E5RI61Ft6o-prbbn>P~)iy)E2AN zsU20jsWz_8Qg>31P|s0cqrPALg8E|(vWA65poU1JRAaZs8I2(p#xiB`SVGovRs-uS zYnV-9TeA7=Om+qP8+I>yOjAR1s%ETak!GFdam@h^# z)@rS0t$wXH+Irf)+G6c;?H29p+V6F6oj{!|o%K3xI`?%6x;DB|x`n#ibhIR?(H}Q3Gzd138Ei2)WAMz7W9Vy`X}HnwgyEn!VS)>mv$8&{hQn>w4zwy3R}t;BYlZQm5)6pty=DfLrs+A-|>>;~;Q z_F?uV_HFjh9n2gO9o9Q^JA86v({H5aB!kjoO6 zc9$1ZZKsN-Zl8L~mE{`ly3)1N^`o1+o7}D0ZPeY&J;i;i`%NyJ8_8Y6J?}yE@b_5a zam?eLr<8@mESk|3$_SkmS{wQ>%qC18))9_|&j{ZT zes8AvOzF(F2#DZEY>2oYX&IRp`F#{ADl)1r>QS^)ba8a|EY_^#S^HO&t^Rgqwv=MZThqqEWH8 zxJo>d=ABlR_Bh=;eM9Tw|Ih34~oTE|= zX_mAr*D$vzw@+p(E0Yc6dFE}(8oqt`+R{gE3x4zjX+Sb3_cYE^= zgB=w+-tUy`ytONMS8KgRef4hA?t0j zufM;t32jm~jUGrkaOInTZ`zyfns>EuS}G30LFK_G-==(f<51|K&cocp&EJ`SxAh3? zNO>#LI=^+SEu(FqJ)ynt=!~PC9bO$rzPJB=?=j6w@a-(u02P7 zaQ)#(uUl{HW%tYNS3ItC^iAtK(eKlL`f9+{bJzISE?u8_z3;~C8@FyI-5j_jy7l;W z_U#vU3hqqYU3!mrul&B+{ptt$59)uk{;_4iZQ%G|z+lhASr6|H35TBkl>gI*;nGLU zN7W-nBaM%pA0HbH8olyl&XeJ%vZoWz%6?Y=dFykl=imL}`%BMQ{Mhgd`HRoLu6e2R za__6DuR6yg#~-}Tc|Gx_{H@O0eebyMy5GmWADJlpK>kqk(fVV@r_fLLKIeS?{4e)} z^ZO;zpECde00d`2O+f$vv5tKEQIh}w03c&XQcVB=dL;k=fP(-4`Tqa_faw4Lbua(` z>RI+y?e7jKeZ#YO-C06$4YK~#9!Y|GmTfG`vT(1YE=HSUJ}b~TrAf%s#!NCSa9 zH*{pd-f*F?`*7lr#8b|%NqW0R!>LpE%f6e6}rVUpiQQI?O4IW=nX2cx!lrK)Enr!Lf3 z;d1a$yh@HqWcOTNYlAa;pP!p!T&@;)L&j`Fo9ms2Lg^Ku8>>{_C##=(JmaX*vcm59 zn-X}UZyXWz``D?O!~U;j@umfBW{cste|rE72nJ7AKbLh*2~7Z8x_>|b diff --git a/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/search_popup.png b/src/main/java/org/jdesktop/swingx/plaf/macosx/resources/search_popup.png deleted file mode 100644 index 0afca49c03ff9d2a876803d8bab55fcdcaba101d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 954 zcmaLVKS-2e9KiAC?e+9LlL$-Fp~ajWqCg^TNIN+X{}d%bQxY^6=-@0Z%??pGC=JF9 zLevyQK|@1}lMYf~qM3uK5n_2>-|z7XNnO9=bHDfbJ%8Wl?%}n;Tz#fF6GEuZ_hzq? zkK<^%n(qg#H{Xz|@AT!eq2y=k^~?m7+WWn?Mnb4-P!yg_o}ySYnjh$?Sx9BlZH;TG z&8H#6xqP;3Xe^2@#qahah8T^wjCQo(BR=3Q*74tn>2-l&Tuq)+A0*~-lIB5^N@IB?PVz#!8PdJY@Ea5W_g^L*OUQGNV%SEiY;Bu0E ZE&C3|YGL5n - * - */ -@MetaInfServices(LookAndFeelAddons.class) -public class MetalLookAndFeelAddons extends BasicLookAndFeelAddons { - /** - * {@inheritDoc} - */ - @Override - protected boolean matches() { - LookAndFeel laf = getLookAndFeel(); - - if (getCrossPlatformLookAndFeelClassName().equals(laf.getClass().getName())) { - //just in case someone sets Nimbus as the cross platform Look and Feel - return !laf.getID().equals("Nimbus"); - } - - return getLookAndFeel() instanceof MetalLookAndFeel; - } - - @Override - public void initialize() { - super.initialize(); - loadDefaults(getDefaults()); - } - - @Override - public void uninitialize() { - super.uninitialize(); - unloadDefaults(getDefaults()); - } - - private Object[] getDefaults() { - return new Object[] { - // "DirectoryChooserUI", - // "org.jdesktop.jdnc.swing.plaf.windows.WindowsDirectoryChooserUI", - }; - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/metal/MetalStatusBarUI.java b/src/main/java/org/jdesktop/swingx/plaf/metal/MetalStatusBarUI.java deleted file mode 100644 index ba906b8935..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/metal/MetalStatusBarUI.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * $Id: MetalStatusBarUI.java 3472 2009-08-27 13:12:42Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.plaf.metal; - -import org.jdesktop.swingx.JXStatusBar; -import org.jdesktop.swingx.plaf.basic.BasicStatusBarUI; - -import javax.swing.*; -import javax.swing.plaf.ComponentUI; -import java.awt.*; -import java.util.List; - -/** - * - * @author rbair - */ -public class MetalStatusBarUI extends BasicStatusBarUI { - - /** Creates a new instance of BasicStatusBarUI */ - public MetalStatusBarUI() { - } - - /** - * Returns an instance of the UI delegate for the specified component. - * Each subclass must provide its own static createUI - * method that returns an instance of that UI delegate subclass. - * If the UI delegate subclass is stateless, it may return an instance - * that is shared by multiple components. If the UI delegate is - * stateful, then it should return a new instance per component. - * The default implementation of this method throws an error, as it - * should never be invoked. - */ - public static ComponentUI createUI(JComponent c) { - return new MetalStatusBarUI(); - } - - @Override - protected void paintBackground(Graphics2D g, JXStatusBar bar) { - int w = bar.getWidth(); //TODO deal with insets - int h = bar.getHeight(); //TODO deal with insets - - //This list is comprised of floats and Colors, which together - //constitute the gradient. - List gradient = (List) UIManager.get("MenuBar.gradient"); - - if (gradient != null && w > 0 && 0 < h) { - float ratio1 = ((Number)gradient.get(0)).floatValue(); - float ratio2 = ((Number)gradient.get(1)).floatValue(); - Color c1 = (Color)gradient.get(2); - Color c2 = (Color)gradient.get(3); - Color c3 = (Color)gradient.get(4); - int mid = (int)(ratio1 * h); - int mid2 = (int)(ratio2 * h); - if (mid > 0) { - g.setPaint(new GradientPaint((float)0, (float)0, c1, (float)0, - (float)mid, c2)); - g.fillRect(0, 0, w, mid); - } - if (mid2 > 0) { - g.setColor(c2); - g.fillRect(0, mid, w, mid2); - } - if (mid > 0) { - g.setPaint(new GradientPaint((float)0, (float)mid + mid2, c2, - (float)0, (float)mid * 2 + mid2, c1)); - g.fillRect(0, mid + mid2, w, mid); - } - if (h - mid * 2 - mid2 > 0) { - g.setPaint(new GradientPaint((float)0, (float)mid * 2 + mid2, c1, - (float)0, (float)h, c3)); - g.fillRect(0, mid * 2 + mid2, w, h - mid * 2 - mid2); - } - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/metal/MetalTaskPaneUI.java b/src/main/java/org/jdesktop/swingx/plaf/metal/MetalTaskPaneUI.java deleted file mode 100644 index 761cefef07..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/metal/MetalTaskPaneUI.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * $Id: MetalTaskPaneUI.java 3448 2009-08-19 08:12:23Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf.metal; - -import org.jdesktop.swingx.JXTaskPane; -import org.jdesktop.swingx.plaf.basic.BasicTaskPaneUI; - -import javax.swing.*; -import javax.swing.border.Border; -import javax.swing.plaf.ComponentUI; -import java.awt.*; - -/** - * Metal implementation of the JXTaskPane UI.
                    - * - * @author Frederic Lavigne - */ -public class MetalTaskPaneUI extends BasicTaskPaneUI { - - public static ComponentUI createUI(JComponent c) { - return new MetalTaskPaneUI(); - } - - @Override - protected void installDefaults() { - super.installDefaults(); - - LookAndFeel.installProperty(group, "opaque", false); - } - - @Override - protected Border createPaneBorder() { - return new MetalPaneBorder(); - } - - /** - * The border of the task pane group paints the "text", the "icon", - * the "expanded" status and the "special" type. - * - */ - class MetalPaneBorder extends PaneBorder { - - @Override - protected void paintExpandedControls(JXTaskPane group, Graphics g, int x, - int y, int width, int height) { - ((Graphics2D)g).setRenderingHint( - RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_ON); - - g.setColor(getPaintColor(group)); - paintRectAroundControls(group, g, x, y, width, height, g.getColor(), g - .getColor()); - paintChevronControls(group, g, x, y, width, height); - - ((Graphics2D)g).setRenderingHint( - RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_OFF); - } - - @Override - protected boolean isMouseOverBorder() { - return true; - } - } - -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/plaf/metal/package-info.java b/src/main/java/org/jdesktop/swingx/plaf/metal/package-info.java deleted file mode 100644 index 59897edc1f..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/metal/package-info.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -/** - * Provides metal laf specific implementation of pluggable look-and-feel for SwingX components together with a - * mechanism to support custom component look-and-feels. - */ -package org.jdesktop.swingx.plaf.metal; - diff --git a/src/main/java/org/jdesktop/swingx/plaf/nimbus/NimbusLookAndFeelAddons.java b/src/main/java/org/jdesktop/swingx/plaf/nimbus/NimbusLookAndFeelAddons.java deleted file mode 100644 index 8393b089e9..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/nimbus/NimbusLookAndFeelAddons.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * $Id: NimbusLookAndFeelAddons.java 4092 2011-11-30 18:04:36Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf.nimbus; - -import org.jdesktop.swingx.plaf.LookAndFeelAddons; -import org.jdesktop.swingx.plaf.basic.BasicLookAndFeelAddons; -import org.kohsuke.MetaInfServices; - -import javax.swing.*; - -@MetaInfServices(LookAndFeelAddons.class) -public class NimbusLookAndFeelAddons extends BasicLookAndFeelAddons { - /** - * {@inheritDoc} - */ - @Override - protected boolean matches() { - return UIManager.getLookAndFeel().getID().equals("Nimbus"); - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/nimbus/NimbusTaskPaneUI.java b/src/main/java/org/jdesktop/swingx/plaf/nimbus/NimbusTaskPaneUI.java deleted file mode 100644 index bc0d0b73a4..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/nimbus/NimbusTaskPaneUI.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * $Id$ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.plaf.nimbus; - -import org.jdesktop.swingx.JXTaskPane; -import org.jdesktop.swingx.plaf.basic.BasicTaskPaneUI; - -import javax.swing.*; -import javax.swing.border.Border; -import javax.swing.plaf.ComponentUI; -import javax.swing.plaf.FontUIResource; -import java.awt.*; - -/** - * Nimbus implementation of the JXTaskPane UI.
                    - * - * @author Radu Dumitrescu - */ -public class NimbusTaskPaneUI extends BasicTaskPaneUI { - - public static ComponentUI createUI(JComponent c) { - return new NimbusTaskPaneUI(); - } - - @Override -protected Border createPaneBorder() { - return new NimbusPaneBorder(); - } - - /** - * Overriden to paint the background of the component but keeping the rounded - * corners. - */ - @Override -public void update(Graphics g, JComponent c) { - if (c.isOpaque()) { - g.setColor(c.getParent().getBackground()); - g.fillRect(0, 0, c.getWidth(), c.getHeight()); - g.setColor(c.getBackground()); - g.fillRect(0, getRoundHeight(), c.getWidth(), c.getHeight() - -getRoundHeight()); - } - paint(g, c); - } - - /** - * The border of the task pane group paints the "text", the "icon", the - * "expanded" status and the "special" type. - * - */ - class NimbusPaneBorder extends PaneBorder { - - @Override - protected void paintTitleBackground(JXTaskPane group, Graphics g) { - - Paint oldPaint = ((Graphics2D) g).getPaint(); - - roundHeight = 7; - - if (group.isSpecial()) { - g.setColor(specialTitleBackground); - - g.fillRoundRect(0, 0, group.getWidth(), getRoundHeight() * 2, - getRoundHeight(), getRoundHeight()); - g.fillRect(0, getRoundHeight(), group.getWidth(), - getTitleHeight(group) - getRoundHeight()); - - } else { - Color[] colors = { titleBackgroundGradientStart, - titleBackgroundGradientEnd }; - - float[] fractions = { 0.0f, 1.0f }; - - LinearGradientPaint gradient = new LinearGradientPaint(group - .getWidth() / 2, 0.0f, group.getWidth() / 2, - getTitleHeight(group), fractions, colors); - - ((Graphics2D) g).setPaint(gradient); - - ((Graphics2D) g).setRenderingHint( - RenderingHints.KEY_COLOR_RENDERING, - RenderingHints.VALUE_COLOR_RENDER_QUALITY); - ((Graphics2D) g).setRenderingHint( - RenderingHints.KEY_INTERPOLATION, - RenderingHints.VALUE_INTERPOLATION_BILINEAR); - ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_RENDERING, - RenderingHints.VALUE_RENDER_QUALITY); - ((Graphics2D) g).setRenderingHint( - RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_ON); - - g.fillRoundRect(0, 0, group.getWidth(), - getTitleHeight(group) / 2, getRoundHeight(), - getRoundHeight()); - - g.fillRect(0, getRoundHeight(), group.getWidth(), - getTitleHeight(group) - getRoundHeight()); - - } - - // draw the border around the title area - g.setColor(borderColor); - - g.drawRoundRect(0, 0, group.getWidth() - 1, getTitleHeight(group) - + getRoundHeight(), getRoundHeight(), getRoundHeight()); - g.drawLine(0, getTitleHeight(group) - 1, group.getWidth(), - getTitleHeight(group) - 1); - - ((Graphics2D) g).setPaint(oldPaint); - } - - @Override - protected void paintExpandedControls(JXTaskPane group, Graphics g, - int x, int y, int width, int height) { - ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_ON); - - g.setColor(getPaintColor(group)); - paintChevronControls(group, g, x, y, width, height); - - ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_OFF); - } - - @Override - protected void paintTitle(JXTaskPane group, Graphics g, - Color textColor, int x, int y, int width, int height) { - configureLabel(group); - // Nimbus has some issues with ColorUIResource - label.setForeground(new Color(textColor.getRGB())); - if (group.getFont() != null - && !(group.getFont() instanceof FontUIResource)) { - label.setFont(group.getFont()); - } - g.translate(x, y); - label.setBounds(0, 0, width, height); - label.paint(g); - g.translate(-x, -y); - } - - @Override - protected boolean isMouseOverBorder() { - return true; - } - } - -} - diff --git a/src/main/java/org/jdesktop/swingx/plaf/nimbus/package-info.java b/src/main/java/org/jdesktop/swingx/plaf/nimbus/package-info.java deleted file mode 100644 index b0d6b64888..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/nimbus/package-info.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -/** - * Provides nimbus laf specific implementation of pluggable look-and-feel for SwingX components together with a - * mechanism to support custom component look-and-feels. - */ -package org.jdesktop.swingx.plaf.nimbus; - diff --git a/src/main/java/org/jdesktop/swingx/plaf/package-info.java b/src/main/java/org/jdesktop/swingx/plaf/package-info.java deleted file mode 100644 index 4baeaf9891..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/package-info.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * $Id: package-info.java 4146 2012-01-25 19:40:05Z kschaefe $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -/** - * Provides pluggable look-and-feel for SwingX components together with a - * mechanism to support custom component look-and-feels. - *

                    - * The addons are loaded with {@link java.util.ServiceLoader}. As such we - * maintain, a services file for our implementations. SwingX uses the - * MetaInf/services - * generator API. This add a compile time dependency to the plaf module. - * The services generator, however, is not required at runtime. - */ -package org.jdesktop.swingx.plaf; - diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsLookAndFeelAddons.java b/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsLookAndFeelAddons.java deleted file mode 100644 index c09c489940..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsLookAndFeelAddons.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * $Id: WindowsLookAndFeelAddons.java 4092 2011-11-30 18:04:36Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf.windows; - -import org.apache.commons.lang3.SystemUtils; -import org.jdesktop.swingx.plaf.LookAndFeelAddons; -import org.jdesktop.swingx.plaf.basic.BasicLookAndFeelAddons; -import org.jdesktop.swingx.util.OS; -import org.kohsuke.MetaInfServices; - -import static javax.swing.UIManager.getLookAndFeel; -import static javax.swing.UIManager.getSystemLookAndFeelClassName; - -/** - * Adds new pluggable UI following the Windows XP look and feel. - */ -@MetaInfServices(LookAndFeelAddons.class) -public class WindowsLookAndFeelAddons extends BasicLookAndFeelAddons { - - public static final String HOMESTEAD_VISUAL_STYLE = "HomeStead"; - - public static final String SILVER_VISUAL_STYLE = "Metallic"; - - public static final String VISTA_VISUAL_STYLE = "NormalColor"; - - /** - * {@inheritDoc} - */ - @Override - protected boolean matches() { - if (isSystemAddon()) { - String laf = getLookAndFeel().getClass().getName(); - - //special-case the jgoodies to ensure that we can match it - return getSystemLookAndFeelClassName().equals(laf) - || "com.jgoodies.looks.windows.WindowsLookAndFeel".equals(laf); - } - - return false; - } - - /** - * {@inheritDoc} - */ - @Override - protected boolean isSystemAddon() { - return SystemUtils.IS_OS_WINDOWS && OS.isUsingWindowsVisualStyles(); - } - -// JW: reverting ... -// /** -// * {@inheritDoc}

                    -// * -// */ -// @Override -// public void initialize() { -// super.initialize(); -// // fix Issue #1305-swingx: wrapper for core issue #6753637 -// // set ui property to prevent eating mousePressed when closing popup -// UIManager.put("PopupMenu.consumeEventOnClose", Boolean.FALSE); -// } -// -// /** -// * {@inheritDoc}

                    -// */ -// @Override -// public void uninitialize() { -// // fix Issue #1305-swingx: wrapper for core issue #6753637 -// // remove the ui property again -// UIManager.put("PopupMenu.consumeEventOnClose", null); -// super.uninitialize(); -// } -// - - -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsStatusBarUI.java b/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsStatusBarUI.java deleted file mode 100644 index a4b501a6ca..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsStatusBarUI.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * $Id: WindowsStatusBarUI.java 3472 2009-08-27 13:12:42Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.plaf.windows; - -import org.jdesktop.swingx.JXStatusBar; -import org.jdesktop.swingx.plaf.UIManagerExt; -import org.jdesktop.swingx.plaf.basic.BasicStatusBarUI; - -import javax.imageio.ImageIO; -import javax.swing.*; -import javax.swing.plaf.ComponentUI; -import java.awt.*; -import java.awt.image.BufferedImage; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * @author rbair - */ -public class WindowsStatusBarUI extends BasicStatusBarUI { - private static final Logger log = Logger.getLogger(WindowsStatusBarUI.class.getName()); - private BufferedImage leftImage; - private BufferedImage middleImage; - private BufferedImage rightImage; - - - /** Creates a new instance of WindowsStatusBarUI */ - public WindowsStatusBarUI() { - //SwingX #827: must create these here or size is incorrect - //TODO need to determine a better way to handle these images - try { - leftImage = ImageIO.read(WindowsStatusBarUI.class.getResource(UIManagerExt.getString("StatusBar.leftImage"))); - middleImage = ImageIO.read(WindowsStatusBarUI.class.getResource(UIManagerExt.getString("StatusBar.middleImage"))); - rightImage = ImageIO.read(WindowsStatusBarUI.class.getResource(UIManagerExt.getString("StatusBar.rightImage"))); - } catch (Exception e) { - // log the message in case of init failure - log.log(Level.FINE, e.getLocalizedMessage(), e); - } - } - - /** - * Returns an instance of the UI delegate for the specified component. - * Each subclass must provide its own static createUI - * method that returns an instance of that UI delegate subclass. - * If the UI delegate subclass is stateless, it may return an instance - * that is shared by multiple components. If the UI delegate is - * stateful, then it should return a new instance per component. - * The default implementation of this method throws an error, as it - * should never be invoked. - */ - public static ComponentUI createUI(JComponent c) { - return new WindowsStatusBarUI(); - } - - @Override protected void paintBackground(Graphics2D g, JXStatusBar statusBar) { - if (leftImage == null || middleImage == null || rightImage == null) { - log.severe("Failed to initialize necessary assets. Set logging to FINE to see more details."); - return; - } - //if bidi, reverse the image painting order - //TODO need to handle vertical stretching better - g.drawImage(leftImage, 0, 0, leftImage.getWidth(), statusBar.getHeight(), null); - - if (statusBar.isResizeHandleEnabled()) { - g.drawImage(middleImage, leftImage.getWidth(), 0, statusBar.getWidth() - leftImage.getWidth() - rightImage.getWidth(), statusBar.getHeight(), null); - g.drawImage(rightImage, statusBar.getWidth() - rightImage.getWidth(), 0, rightImage.getWidth(), statusBar.getHeight(), null); - } else { - g.drawImage(middleImage, leftImage.getWidth(), 0, statusBar.getWidth() - leftImage.getWidth(), statusBar.getHeight(), null); - } - } - - @Override protected Insets getSeparatorInsets(Insets insets) { - if (insets == null) { - insets = new Insets(0, 0, 0, 0); - } - insets.top = 1; - insets.left = 4; - insets.bottom = 0; - insets.right = 4; - return insets; - } -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsTaskPaneUI.java b/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsTaskPaneUI.java deleted file mode 100644 index f359baed9d..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsTaskPaneUI.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * $Id: WindowsTaskPaneUI.java 3448 2009-08-19 08:12:23Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf.windows; - -import org.jdesktop.swingx.JXTaskPane; -import org.jdesktop.swingx.plaf.basic.BasicTaskPaneUI; - -import javax.swing.*; -import javax.swing.border.Border; -import javax.swing.plaf.ComponentUI; -import java.awt.*; - -/** - * Windows implementation of the TaskPaneUI. - * - * @author Frederic Lavigne - */ -public class WindowsTaskPaneUI extends BasicTaskPaneUI { - - public static ComponentUI createUI(JComponent c) { - return new WindowsTaskPaneUI(); - } - - @Override - protected Border createPaneBorder() { - return new XPPaneBorder(); - } - - /** - * Overriden to paint the background of the component but keeping the rounded - * corners. - */ - @Override - public void update(Graphics g, JComponent c) { - if (c.isOpaque()) { - g.setColor(c.getParent().getBackground()); - g.fillRect(0, 0, c.getWidth(), c.getHeight()); - g.setColor(c.getBackground()); - g.fillRect(0, getRoundHeight(), c.getWidth(), c.getHeight() - getRoundHeight()); - } - paint(g, c); - } - - /** - * The border of the taskpane group paints the "text", the "icon", the - * "expanded" status and the "special" type. - * - */ - class XPPaneBorder extends PaneBorder { - - @Override - protected void paintTitleBackground(JXTaskPane group, Graphics g) { - if (group.isSpecial()) { - g.setColor(specialTitleBackground); - g.fillRoundRect( - 0, - 0, - group.getWidth(), - getRoundHeight() * 2, - getRoundHeight(), - getRoundHeight()); - g.fillRect( - 0, - getRoundHeight(), - group.getWidth(), - getTitleHeight(group) - getRoundHeight()); - } else { - Paint oldPaint = ((Graphics2D)g).getPaint(); - GradientPaint gradient = new GradientPaint( - 0f, - group.getWidth() / 2, - group.getComponentOrientation().isLeftToRight()? - titleBackgroundGradientStart - :titleBackgroundGradientEnd, - group.getWidth(), - getTitleHeight(group), - group.getComponentOrientation().isLeftToRight()? - titleBackgroundGradientEnd - :titleBackgroundGradientStart); - - ((Graphics2D)g).setRenderingHint( - RenderingHints.KEY_COLOR_RENDERING, - RenderingHints.VALUE_COLOR_RENDER_QUALITY); - ((Graphics2D)g).setRenderingHint( - RenderingHints.KEY_INTERPOLATION, - RenderingHints.VALUE_INTERPOLATION_BILINEAR); - ((Graphics2D)g).setRenderingHint( - RenderingHints.KEY_RENDERING, - RenderingHints.VALUE_RENDER_QUALITY); - ((Graphics2D)g).setPaint(gradient); - g.fillRoundRect( - 0, - 0, - group.getWidth(), - getRoundHeight() * 2, - getRoundHeight(), - getRoundHeight()); - g.fillRect( - 0, - getRoundHeight(), - group.getWidth(), - getTitleHeight(group) - getRoundHeight()); - ((Graphics2D)g).setPaint(oldPaint); - } - } - - @Override - protected void paintExpandedControls(JXTaskPane group, Graphics g, int x, - int y, int width, int height) { - ((Graphics2D)g).setRenderingHint( - RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_ON); - - paintOvalAroundControls(group, g, x, y, width, height); - g.setColor(getPaintColor(group)); - paintChevronControls(group, g, x, y, width, height); - - ((Graphics2D)g).setRenderingHint( - RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_OFF); - } - - @Override - protected boolean isMouseOverBorder() { - return true; - } - - } - -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsTipOfTheDayUI.java b/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsTipOfTheDayUI.java deleted file mode 100644 index d029980c64..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/windows/WindowsTipOfTheDayUI.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * $Id: WindowsTipOfTheDayUI.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.plaf.windows; - -import org.jdesktop.swingx.JXTipOfTheDay; -import org.jdesktop.swingx.JXTipOfTheDay.ShowOnStartupChoice; -import org.jdesktop.swingx.plaf.UIManagerExt; -import org.jdesktop.swingx.plaf.basic.BasicTipOfTheDayUI; - -import javax.swing.*; -import javax.swing.border.Border; -import javax.swing.border.CompoundBorder; -import javax.swing.plaf.ComponentUI; -import java.awt.*; - -/** - * Windows implementation of the TipOfTheDayUI. - * - * @author Frederic Lavigne - */ -public class WindowsTipOfTheDayUI extends BasicTipOfTheDayUI { - - public static ComponentUI createUI(JComponent c) { - return new WindowsTipOfTheDayUI((JXTipOfTheDay)c); - } - - public WindowsTipOfTheDayUI(JXTipOfTheDay tipPane) { - super(tipPane); - } - - @Override - public JDialog createDialog(Component parentComponent, - final ShowOnStartupChoice choice) { - return createDialog(parentComponent, choice, false); - } - - @Override -protected void installComponents() { - tipPane.setLayout(new BorderLayout()); - - // tip icon - JLabel tipIcon = new JLabel(); - tipIcon.setPreferredSize(new Dimension(60, 100)); - tipIcon.setIcon(UIManager.getIcon("TipOfTheDay.icon")); - tipIcon.setHorizontalAlignment(JLabel.CENTER); - tipIcon.setVerticalAlignment(JLabel.TOP); - tipIcon.setBorder(BorderFactory.createEmptyBorder(24, 0, 0, 0)); - tipPane.add("West", tipIcon); - - // tip area - JPanel rightPane = new JPanel(new BorderLayout()); - JLabel didYouKnow = new JLabel(UIManagerExt - .getString("TipOfTheDay.didYouKnowText", tipPane.getLocale())); - didYouKnow.setPreferredSize(new Dimension(50, 32)); - didYouKnow.setOpaque(true); - didYouKnow.setBackground(UIManager.getColor("TextArea.background")); - didYouKnow.setBorder(new CompoundBorder(BorderFactory.createMatteBorder(0, - 0, 2, 0, tipPane.getBackground()), BorderFactory.createEmptyBorder(4, 4, - 4, 4))); - didYouKnow.setFont(tipPane.getFont().deriveFont(Font.BOLD, 15)); - rightPane.add("North", didYouKnow); - - tipArea = new JPanel(new BorderLayout()); - tipArea.setOpaque(true); - tipArea.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); - tipArea.setBackground(UIManager.getColor("TextArea.background")); - rightPane.add("Center", tipArea); - - tipPane.add("Center", rightPane); - } - - public static class TipAreaBorder implements Border { - @Override - public Insets getBorderInsets(Component c) { - return new Insets(2, 2, 2, 2); - } - @Override - public boolean isBorderOpaque() { - return false; - } - @Override - public void paintBorder(Component c, Graphics g, int x, int y, int width, - int height) { - g.setColor(UIManager.getColor("TipOfTheDay.background")); - g.drawLine(x, y, x + width - 1, y); - g.drawLine(x, y, x, y + height - 1); - - g.setColor(Color.black); - g.drawLine(x + 1, y + 1, x + width - 3, y + 1); - g.drawLine(x + 1, y + 1, x + 1, y + height - 3); - - g.setColor(Color.white); - g.drawLine(x, y + height - 1, x + width, y + height - 1); - g.drawLine(x + width - 1, y, x + width - 1, y + height - 1); - } - } - -} diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/package-info.java b/src/main/java/org/jdesktop/swingx/plaf/windows/package-info.java deleted file mode 100644 index 25a1494e7f..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/windows/package-info.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -/** - * Provides window laf specific implementation of pluggable look-and-feel for SwingX components together with a - * mechanism to support custom component look-and-feels. - */ -package org.jdesktop.swingx.plaf.windows; - diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay.properties b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay.properties deleted file mode 100644 index 0f04eb3e52..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay.properties +++ /dev/null @@ -1,2 +0,0 @@ -TipOfTheDay.previousTipText=Previous Tip -TipOfTheDay.nextTipText=Next Tip diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_cs.properties b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_cs.properties deleted file mode 100644 index 8ed8af4d0a..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_cs.properties +++ /dev/null @@ -1,3 +0,0 @@ -#Czech resource bundle created by Vity (vitywap@seznam.cz) 08/2007 -TipOfTheDay.previousTipText=P\u0159edchoz\u00ed Tip -TipOfTheDay.nextTipText=N\u00e1sleduj\u00edc\u00ed Tip diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_de.properties b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_de.properties deleted file mode 100644 index af1384a0c8..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_de.properties +++ /dev/null @@ -1,2 +0,0 @@ -TipOfTheDay.previousTipText=Vorheriger Tipp -TipOfTheDay.nextTipText=N\u00e4chster Tipp diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_es.properties b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_es.properties deleted file mode 100644 index e895cb6af2..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_es.properties +++ /dev/null @@ -1,2 +0,0 @@ -TipOfTheDay.previousTipText=Sugerencia Anterior -TipOfTheDay.nextTipText=Siguiente Sugerencia diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_fr.properties b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_fr.properties deleted file mode 100644 index 78eb2a5d3c..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_fr.properties +++ /dev/null @@ -1,2 +0,0 @@ -TipOfTheDay.previousTipText=Astuce Pr\u00e9c\u00e9dente -TipOfTheDay.nextTipText=Astuce Suivante diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_it.properties b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_it.properties deleted file mode 100644 index 1dbe765fec..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_it.properties +++ /dev/null @@ -1,2 +0,0 @@ -TipOfTheDay.previousTipText=Consiglio Precedente -TipOfTheDay.nextTipText=Consiglio Successivo diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_nl.properties b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_nl.properties deleted file mode 100644 index f35001c76a..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_nl.properties +++ /dev/null @@ -1,2 +0,0 @@ -TipOfTheDay.previousTipText=Vorige Tip -TipOfTheDay.nextTipText=Volgende Tip diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_pl.properties b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_pl.properties deleted file mode 100644 index 452a9ebe89..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_pl.properties +++ /dev/null @@ -1,2 +0,0 @@ -TipOfTheDay.previousTipText=Poprzednia porada -TipOfTheDay.nextTipText=Nast\u0119pna porada diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_pt_BR.properties b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_pt_BR.properties deleted file mode 100644 index dadd7e7db8..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_pt_BR.properties +++ /dev/null @@ -1,2 +0,0 @@ -TipOfTheDay.previousTipText=Dica Anterior -TipOfTheDay.nextTipText=Pr\u00F3xima Dica diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_sv.properties b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_sv.properties deleted file mode 100644 index b5105eb93f..0000000000 --- a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/TipOfTheDay_sv.properties +++ /dev/null @@ -1,2 +0,0 @@ -TipOfTheDay.previousTipText=Föreg\u00E5ende tips -TipOfTheDay.nextTipText=N\u00E4sta tips diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/clear.gif b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/clear.gif deleted file mode 100644 index a28caff081ec5d329bfb072c909b8787550f7f30..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 222 zcmZ?wbhEHblwuHJXpv%2_Nh{j>ekMfqhGovIJe)pac9lMRdv%hr7XG7IDdD`@}q6* z&v$IS*0t;QvuDpGS8$KYsl9_3PK~-@pI-`SbVh-+%x9{l@@` zKUo;L7~~mr7=Qp|Cj)Er1NFX?%y}8B*5$n3SD7IE03{=HuK)l5 diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/clear_pressed.gif b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/clear_pressed.gif deleted file mode 100644 index 250e6b50023b3abd7d18c0fa9b43ca0352a28e64..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 660 zcmZ?wbh9u|lwuHJI2OU6>{F#4)vc4%t(`GPzjRG-ZohHk&Zvs6;QE=NjkCg=W=A&7 zj%uD8+cGz{bzXeiyo~mlnH{rodl%&O&d={#P&08=-SkZ@7%Yujc$BdA;oT&8g?! zOgr~x#)Y>tFT9;|>D`=5@8?~5H~;dx1(!c8y8L$jl@E(Azg>Lw!{VzSw_bd>?E1%L z*FUYi`DxY7kK1m%+;!*u&U+tsKKQcl(U-#yKc2n!{NUrShn{>p`|!>Ahp#U_dVS-; zlgm%voqhS^*6YvL-+aIM_WP}OKkvQ!{^-N^#~*(@`SkPY=ijfs{(k-K&;J1eIt)Mn ziVFtzpAAgx0>Z6r?H!%MOiY4uS`#Ktnmk3LM?}kQ*6cZR=ebUwIdAde1xz!RtemrK z#i~_n7Oh*dVM#!EsE?aZXn4Ttg-gQ3l|y_&l*PmLGYM^+8zj#rAAC_hXpU14zpBmM zdv_hYrCAx^?INJzINENoGk;d)t?<-`;cdmtg!SF^}b-`T@pHSq@>Pk|f3k zRwZ6ti@=WNKxRP;qpUL$OC4BMET@EM2&6vdapQ5i5^=KV=>#?RnmZo~mz{F#4)vcW|N56DUaBjbG<4&9UjWrWj)lJ`&vgAVJ{M{|f zkG8Ep-?8;t*RI>kZhl#L>+9+}-`Cyyap30jjrV_Uy8mPI{U2NJf8Tch`<92lwmGz}0f1ZB+{rJn@CtvFMq3pJ3zL%@-_Ow0McRcbAvRs?}@Ot~2pkVYqqA)@|DjH?n!^@7=f8-rdzy-_+IJe!sqF zmxu283+LT9`J9ZM_&D7z>Ux~jdhqapH7_f#-BVucM_TvT+%-Oa`lzYR!~C5`Tl2Gq zdzXt1hm6Jt<_=+Q799)5N6e1=E(~@u8BA?W4C0~(3_cnxYE+bU>f(9H#K^#+=%Z9% zxaqKftE1V8Ihw(2jm#=)9TpA_Y-f2LMHo*yY|*$Vq~lg_;Xooww~(7LL*SL>rmL&N zbQ5+2G&;3%OXwslz2Gd!&zW&~RqW|7CVp3COQjQYy|=Hs%V8<KLZ*U+5Lu!Sk^o_Z5E4Meg@_7P6crJiNL9pw)e1;Xm069{HJUZAPk55R%$-RIA z6-eL&AQ0xu!e<4=008gy@A0LT~suv4>S3ILP<0Bm`DLLvaF4FK%)Nj?Pt*r}7;7Xa9z9H|HZjR63e zC`Tj$K)V27Re@400>HumpsYY5E(E}?0f1SyGDiY{y#)Yvj#!WnKwtoXnL;eg03bL5 z07D)V%>y7z1E4U{zu>7~aD})?0RX_umCct+(lZpemCzb@^6=o|A>zVpu|i=NDG+7} zl4`aK{0#b-!z=TL9Wt0BGO&T{GJWpjryhdijfaIQ&2!o}p04JRKYg3k&Tf zVxhe-O!X z{f;To;xw^bEES6JSc$k$B2CA6xl)ltA<32E66t?3@gJ7`36pmX0IY^jz)rRYwaaY4 ze(nJRiw;=Qb^t(r^DT@T3y}a2XEZW-_W%Hszxj_qD**t_m!#tW0KDiJT&R>6OvVTR z07RgHDzHHZ48atvzz&?j9lXF70$~P3Knx_nJP<+#`N z#-MZ2bTkiLfR>_b(HgWKJ%F~Nr_oF3b#wrIijHG|(J>BYjM-sajE6;FiC7vY#};Gd zST$CUHDeuEH+B^pz@B062qXfFfD`NpUW5?BY=V%GM_5c)L#QR}BeW8_2v-S%gfYS= zB9o|3v?Y2H`NVi)In3rTB8+ej^> zQ=~r95NVuDChL%G$=>7$vVg20myx%S50Foi`^m%Pw-h?Xh~i8Mq9jtJloCocWk2Nv zrJpiFnV_ms&8eQ$2&#xWpIS+6pmtC%Q-`S&GF4Q#^mhymh7E(qNMa}%YZ-ePrx>>xFPTiH1=E+A$W$=bG8>s^ zm=Bn5Rah$aDtr}@$`X}2l~$F0mFKEdRdZE8)p@E5RI61Ft6o-prbbn>P~)iy)E2AN zsU20jsWz_8Qg>31P|s0cqrPALg8E|(vWA65poU1JRAaZs8I2(p#xiB`SVGovRs-uS zYnV-9TeA7=Om+qP8+I>yOjAR1s%ETak!GFdam@h^# z)@rS0t$wXH+Irf)+G6c;?H29p+V6F6oj{!|o%K3xI`?%6x;DB|x`n#ibhIR?(H}Q3Gzd138Ei2)WAMz7W9Vy`X}HnwgyEn!VS)>mv$8&{hQn>w4zwy3R}t;BYlZQm5)6pty=DfLrs+A-|>>;~;Q z_F?uV_HFjh9n2gO9o9Q^JA86v({H5aB!kjoO6 zc9$1ZZKsN-Zl8L~mE{`ly3)1N^`o1+o7}D0ZPeY&J;i;i`%NyJ8_8Y6J?}yE@b_5a zam?eLr<8@mESk|3$_SkmS{wQ>%qC18))9_|&j{ZT zes8AvOzF(F2#DZEY>2oYX&IRp`F#{ADl)1r>QS^)ba8a|EY_^#S^HO&t^Rgqwv=MZThqqEWH8 zxJo>d=ABlR_Bh=;eM9Tw|Ih34~oTE|= zX_mAr*D$vzw@+p(E0Yc6dFE}(8oqt`+R{gE3x4zjX+Sb3_cYE^= zgB=w+-tUy`ytONMS8KgRef4hA?t0j zufM;t32jm~jUGrkaOInTZ`zyfns>EuS}G30LFK_G-==(f<51|K&cocp&EJ`SxAh3? zNO>#LI=^+SEu(FqJ)ynt=!~PC9bO$rzPJB=?=j6w@a-(u02P7 zaQ)#(uUl{HW%tYNS3ItC^iAtK(eKlL`f9+{bJzISE?u8_z3;~C8@FyI-5j_jy7l;W z_U#vU3hqqYU3!mrul&B+{ptt$59)uk{;_4iZQ%G|z+lhASr6|H35TBkl>gI*;nGLU zN7W-nBaM%pA0HbH8olyl&XeJ%vZoWz%6?Y=dFykl=imL}`%BMQ{Mhgd`HRoLu6e2R za__6DuR6yg#~-}Tc|Gx_{H@O0eebyMy5GmWADJlpK>kqk(fVV@r_fLLKIeS?{4e)} z^ZO;zpECde00d`2O+f$vv5tKEQIh}w03c&XQcVB=dL;k=fP(-4`Tqa_faw4Lbua(` z>RI+y?e7jKeZ#YO-C0FOyTK~#9!RE;qX!Y~v={UZve%ONr{AXODgI>1JTBE;0Y zpo$BSp&e1q!GMI4^s}^(db9m3!}$oQEIS|Je8hc7==y-J50J|8t&bq7EMwBa7<#=4 zBadj?^Dh@iQ`2ltO~dPA32b+o=C&+a|W(Javsl`g)7(jn0`%?6WsI=4>!8-0GOO z*|}hgTj3V-iXEOso6Q>c23PGg>pYm!xi7r`a9PLVnn_!d=A6!*d#Zlcf!sys@)n=Z zT7IE&!qgVHyyuJVQ{r#sO9=`hY z(_7JzJ2-r^ZWPjKYsl92?D>s;5QKb z`Sa)R-@pI<{rf-2fZ|UUMlObW1|0?<0L2Ld`@V+yrskH`w)T$BuI_q&KR^GTPJeG> zEp1aDze%nBHZnpBg=C#(&uQ|K5|Gx{6&H~6THb5GC*kGh;wr*t<-6QZnM=pT%gaUe z@TNJwDr}lAK0ae}4AuZ48i8a0 diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/search_popup.gif b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/search_popup.gif deleted file mode 100644 index 0d648bc0d8603d704bdc5e2d544fb484560ed924..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 273 zcmZ?wbhEHb~L|^qDhf&Ye4V{`~n%mo8noa^>2!Yfqm(eg6FU zix)3mzI^%W)vMR9U%!3(_T9U8@87@w`0?YXPoIAL`0?x4uiw9a|M~Oh@87@w{{8!p z0TlmCBD!6<41~BL_00GDm46L;Y6M9oT*%mEcw`TIf2<_w- zs@ui0?yhMK>s?TAW5pkd6H6u~@&t%nSP|mr7$|gOjaeYCkHCWs5{8bRd>IW=b} zg3Wg}%#&8q@JQUx{^7fT20u$nYa0u{1|M@zZyz(C1~1c;sneKvHF&1a;nCopGo5?> Q!fA^cgI5MHDl%9D0Nogn>;M1& diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/search_popup_pressed.gif b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/search_popup_pressed.gif deleted file mode 100644 index 7a4dbc95205a8532e46b4d524b485176ba5c10b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 228 zcmZ?wbh9u|Nd;nj<4=XYnkphaa&gBtgOyC#S<2kPg+*Jq0AOGC_ z^ylTbzi)s1d;jC#r(gfR{QCFp&%Ymk{{8y<@6W&gfB*fbI?!PN0+3%A*d{m}>~PVM z@?6~Duq4xJzSKk`mbGnD7HEkq`IEBmLBX@dUlPu}FgdpPO~kzqJC-hf67cSaP3z)2 z9{(B|g`~a2IomrrImNw1Ir9Q~vqa%Yg09QKj#sB~S diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/search_pressed.gif b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/search_pressed.gif deleted file mode 100644 index f79cb16a17e488bc206333133e1706d7481e14e6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1161 zcmdVZ`%}^f003~TTyeJMtFrT2YBjUm-E^I@rcTAEyj|z&rfKQS&CO}C&8s`TJwh`T z@QH#7_&_bmvs5g_MExRfm8ZM~gfH?CP(i`9{jk@cvCseTxpEBwOS~Hn35D3WkcgDh z^Vgpw-?&s^Gj4$0-BjqMa-=x<~=6oJbIH~LoRsIU0BTvI#O*vd0^ zaMpTQs=;~vAfT_3*l_%HB3IwT(c<}L3R~UJR<#MN?Tc0l$Joo$wJ+J~rS>6?oFo8o z%TB_QwR6e(jHe+8H}E1CLF6DW+d4$fQI4)lw9z4UQ3c=#-$W8SIwhMViK|=UAW1hT z_y)3Ut7m1KDzWs+-BZH#0fpzO((_EU!w{ROD))rkLREXFf(it*Hb85iF}PW}4M6XjHEaWhE#9hAptEufPJ!M9n0EPV zPL|0HfL)vX!D8eKBdJiw)-S@pTgnO zICfRey;aA~s>7>udi74P&gENkc@3LBt9#Gt`Tg(zl~~BX{<{D<^cMt*q6Q81F&`fa zId&#BB7>Y0csi4OH7E*Rcl6_L8E><`zHo=}KvAl|L|ti|#$5iBX1y_%`#ckolpA#X zMaZ%8yh2j|AqIUhdN?XOy&9Tag1CBXJRnq3doo(9-CuQJJd;>ciOO=U+-wNBy;02s z0zOE%hY+6o+423sv(4b*eYV4YUt|*@!zACAb{tWDD(;tX*jE3w+9Mf#7IZuH9q2bt zp-Ev#(`9PxhcIKRKSp>dKA-YA>PI8uWo+`fJ_@SLAC`~|g%jbe8T63Iyt4uT62vOf zLmyy+gJbTOM;FnVJc_L@A|^P{xPR%yD*L$Uh&qi{?Xz5RQRdTy~u-MHGv*h xP*%xZ?uk-+t&Fa>eyz&o3(ECD=13OWSIP#MeUG^{v|Q9Oc+8)l0GQH~9w zY|L;AMOq4!wjAMDaU6w0Df9qZdeHl%^n&l}`L&My7khcXyg%SwNlA)}&5nSD!gOrd zk$CLsv}dvCnzJ{XVsm@rb8&I`-{=nt?)Y#&Y)Q7_t zf2GEhj%Pn=h^d;)EcyFd)kJ<-&)u4_uV2n#>KK?N=I!Q%thU9U8ks-0vhH=VD_e#N zySNnuP6NKbinRQkwDN+=uOHyo53aS2iW(>QZBwgFba4w^)HNk-WyoGJ*2{VNmN|Jl zLxN+7I~d~Dc?D@&+CDF@UN*d1P~qnkIF`DDr6ep!U$YcFOwj;CN?26l*t*UoO($DN zWhzN56=_l0%~q3_WO%NDvLq$3mBS12E}ntN(-W4B_!Se0r=##RgjF+*EhMg*`c{oJ zu7oJC(m1*vft9#s!wZ34fu&oxMHGRPJk>udGP2OxBL;fKV4oQ1kvK>aJ4FJJ)*Vy{ zKws63h)jJlGiBY;Cv{S!PO8i`vt}HSyM`2&L4|vEePdYV8C7}aWt(Gah$S`C)E7J+V?v*8lyw|GXk*sv`$f_$?_V1a}dkH`us%%IZjShuXdCkN}F44bLy;i<(5Zhvun1T%56xs4e7SsYQU=jAU)tQ0B$Yl-2|Kl&|?50 z6X4X_z2=>?Lz|#`!|pXYycWA_)8R8YedZlqKG5T{dwdSa=Y)K&9lbuc*Z21S$H%Z8 z{(Auy;DbfQ1twA2Cd&eLpZ`6m9ZjwXin`kDq=a9}8;QnBGE?^>F~itZQGIRrbnak! zcSb>B*un7gX7v-zRX#KK#CHY>@rQl*_d=hf2i?HdoobDXI{D?yAo28#H!;cblBZYv zcOS{R`=GlzJ30e1jr-=Iy3{}G8Dgm43JX3)ou@r+OcjLmydRha8+*{nt)l&uV4D2Sk2l_(flz$8Ua{ z+}$yOD%*PqOOSUw9Wn+qq4zQ~7`sZ}h5vB^m7ss;0~w+QY>?)&kHz}=W0L(+=0d)B zbf*O|pE&;fZ21h(B?$Sxx&*dA%`cmqld|Uk3Rj0F6T&XtE{0b!B0qv56Hcb|$`cXi zoY&!@nBMG)5<2oI;=?c=w>;`-iSlwF62QPi(lK1W14XRbcH};$0(USk(>A;3vrQB| z^k#8zd3b);q<_Sx{b32ng3tnWZSrw+4ie#woc;59(kV^dHQyBH%(pp2&C6RqHfYWS J!{M+C{{p{fFv|b{ diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/silver-statusbar-left.png b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/silver-statusbar-left.png deleted file mode 100644 index 000a9b157adb933ef7fd67dd631d7bc4e91514fe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 223 zcmeAS@N?(olHy`uVBq!ia0vp^sz5Bx!2~3k{RBS&DbW(wh!W?b)Wnj^{5*w_%-mE4 z_G{dXf*cZ8`mOtc3fN0LeOrMq>1fP%4}E{-7; zx87da$lGAR;}YnwF<{q)3eJ2k*57S67N#}EE8J)Dzy9azo_DM`g=PXYh{ diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/silver-statusbar-middle.png b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/silver-statusbar-middle.png deleted file mode 100644 index 118762e2166bfceb8932d0783e8620cb47a3367d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 196 zcmeAS@N?(olHy`uVBq!ia0vp^j6f{T!2~2-e{6~XQlcfU5hcz=sfi_-`FRQ6|k3h`nrOJc(@dUl*x|L&e@ k>i^KzGcGPQaXp1PTHTV%y|(TC0yK@m)78&qol`;+00C=1oB#j- diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/silver-statusbar-right.png b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/silver-statusbar-right.png deleted file mode 100644 index 6df09e6bc67046b09d8a8fc1746278a48ecdd0b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 370 zcmV-&0ge8NP)uE_c2SE_1Fbvav zKCE@=dLmN!e0~!Fz;^(k#4kgv?Z8`zO#CBs)PEfHAO7r0h-zg3YC9I4c4|lEWyyH* zF3LFvkcVV7vTxBU%}HuJ=mNHOasyy8>;ksh=m22nq?EAQ^=iKTuzLq>0Iapq+K53j zj^>~9r9P``twpz*b*;5Zk_14w)4IGk2aqJmT5XJ>=a>G*K>;Ex7FNFa1#FUyNZ=}* Q(f|Me07*qoM6N<$f{x>(y#N3J diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/statusbar-left.png b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/statusbar-left.png deleted file mode 100644 index d344719ae513ee443b45f204d744b7db2e29c495..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3200 zcmV-`41e>9P)KLZ*U+5Lu!Sk^o_Z5E4Meg@_7P6crJiNL9pw)e1;Xm069{HJUZAPk55R%$-RIA z6-eL&AQ0xu!e<4=008gy@A0LT~suv4>S3ILP<0Bm`DLLvaF4FK%)Nj?Pt*r}7;7Xa9z9H|HZjR63e zC`Tj$K)V27Re@400>HumpsYY5E(E}?0f1SyGDiY{y#)Yvj#!WnKwtoXnL;eg03bL5 z07D)V%>y7z1E4U{zu>7~aD})?0RX_umCct+(lZpemCzb@^6=o|A>zVpu|i=NDG+7} zl4`aK{0#b-!z=TL9Wt0BGO&T{GJWpjryhdijfaIQ&2!o}p04JRKYg3k&Tf zVxhe-O!X z{f;To;xw^bEES6JSc$k$B2CA6xl)ltA<32E66t?3@gJ7`36pmX0IY^jz)rRYwaaY4 ze(nJRiw;=Qb^t(r^DT@T3y}a2XEZW-_W%Hszxj_qD**t_m!#tW0KDiJT&R>6OvVTR z07RgHDzHHZ48atvzz&?j9lXF70$~P3Knx_nJP<+#`N z#-MZ2bTkiLfR>_b(HgWKJ%F~Nr_oF3b#wrIijHG|(J>BYjM-sajE6;FiC7vY#};Gd zST$CUHDeuEH+B^pz@B062qXfFfD`NpUW5?BY=V%GM_5c)L#QR}BeW8_2v-S%gfYS= zB9o|3v?Y2H`NVi)In3rTB8+ej^> zQ=~r95NVuDChL%G$=>7$vVg20myx%S50Foi`^m%Pw-h?Xh~i8Mq9jtJloCocWk2Nv zrJpiFnV_ms&8eQ$2&#xWpIS+6pmtC%Q-`S&GF4Q#^mhymh7E(qNMa}%YZ-ePrx>>xFPTiH1=E+A$W$=bG8>s^ zm=Bn5Rah$aDtr}@$`X}2l~$F0mFKEdRdZE8)p@E5RI61Ft6o-prbbn>P~)iy)E2AN zsU20jsWz_8Qg>31P|s0cqrPALg8E|(vWA65poU1JRAaZs8I2(p#xiB`SVGovRs-uS zYnV-9TeA7=Om+qP8+I>yOjAR1s%ETak!GFdam@h^# z)@rS0t$wXH+Irf)+G6c;?H29p+V6F6oj{!|o%K3xI`?%6x;DB|x`n#ibhIR?(H}Q3Gzd138Ei2)WAMz7W9Vy`X}HnwgyEn!VS)>mv$8&{hQn>w4zwy3R}t;BYlZQm5)6pty=DfLrs+A-|>>;~;Q z_F?uV_HFjh9n2gO9o9Q^JA86v({H5aB!kjoO6 zc9$1ZZKsN-Zl8L~mE{`ly3)1N^`o1+o7}D0ZPeY&J;i;i`%NyJ8_8Y6J?}yE@b_5a zam?eLr<8@mESk|3$_SkmS{wQ>%qC18))9_|&j{ZT zes8AvOzF(F2#DZEY>2oYX&IRp`F#{ADl)1r>QS^)ba8a|EY_^#S^HO&t^Rgqwv=MZThqqEWH8 zxJo>d=ABlR_Bh=;eM9Tw|Ih34~oTE|= zX_mAr*D$vzw@+p(E0Yc6dFE}(8oqt`+R{gE3x4zjX+Sb3_cYE^= zgB=w+-tUy`ytONMS8KgRef4hA?t0j zufM;t32jm~jUGrkaOInTZ`zyfns>EuS}G30LFK_G-==(f<51|K&cocp&EJ`SxAh3? zNO>#LI=^+SEu(FqJ)ynt=!~PC9bO$rzPJB=?=j6w@a-(u02P7 zaQ)#(uUl{HW%tYNS3ItC^iAtK(eKlL`f9+{bJzISE?u8_z3;~C8@FyI-5j_jy7l;W z_U#vU3hqqYU3!mrul&B+{ptt$59)uk{;_4iZQ%G|z+lhASr6|H35TBkl>gI*;nGLU zN7W-nBaM%pA0HbH8olyl&XeJ%vZoWz%6?Y=dFykl=imL}`%BMQ{Mhgd`HRoLu6e2R za__6DuR6yg#~-}Tc|Gx_{H@O0eebyMy5GmWADJlpK>kqk(fVV@r_fLLKIeS?{4e)} z^ZO;zpECde00d`2O+f$vv5tKEQIh}w03c&XQcVB=dL;k=fP(-4`Tqa_faw4Lbua(` z>RI+y?e7jKeZ#YO-C0i8)iK~#9!>{d%s!!QheDq@;4bl@T^H~`1w1`JnBrjGSk z)J`17j>C%1P_G=>k)A$&l6-%Ca_9$)qeooc3^W5l{Ke9@_YVwx59iz@H1A4Hb?K=QS11UHJxpv(N z$72UG!@_mRb+8zvl9Bnl7P&mc%l}pNq2Nd>+^mMa3?{1Ou5$(b8V>-GV4Uq*AlU$Y z)nSxSd))m0fE!$QPw_Yz@1SLKq^Wn#?K?VNE@`t{D&{&_`N*-SVb_MmSW{e< z6x6w4$|oh|3QjqmBb3+1z9sa(qXg&1;(;%Z1C6xoHO@`Fs`>~(!gH1O%oGatup>Qa mz1}JGOK0@_e7ZSC{}}+lI|%JQe90pK0000 diff --git a/src/main/java/org/jdesktop/swingx/plaf/windows/resources/statusbar-middle.png b/src/main/java/org/jdesktop/swingx/plaf/windows/resources/statusbar-middle.png deleted file mode 100644 index 37bd99e6a3ec16241c1fb5c6d28cd341fc22ae48..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2864 zcmV-03(xe4P)KLZ*U+5Lu!Sk^o_Z5E4Meg@_7P6crJiNL9pw)e1;Xm069{HJUZAPk55R%$-RIA z6-eL&AQ0xu!e<4=008gy@A0LT~suv4>S3ILP<0Bm`DLLvaF4FK%)Nj?Pt*r}7;7Xa9z9H|HZjR63e zC`Tj$K)V27Re@400>HumpsYY5E(E}?0f1SyGDiY{y#)Yvj#!WnKwtoXnL;eg03bL5 z07D)V%>y7z1E4U{zu>7~aD})?0RX_umCct+(lZpemCzb@^6=o|A>zVpu|i=NDG+7} zl4`aK{0#b-!z=TL9Wt0BGO&T{GJWpjryhdijfaIQ&2!o}p04JRKYg3k&Tf zVxhe-O!X z{f;To;xw^bEES6JSc$k$B2CA6xl)ltA<32E66t?3@gJ7`36pmX0IY^jz)rRYwaaY4 ze(nJRiw;=Qb^t(r^DT@T3y}a2XEZW-_W%Hszxj_qD**t_m!#tW0KDiJT&R>6OvVTR z07RgHDzHHZ48atvzz&?j9lXF70$~P3Knx_nJP<+#`N z#-MZ2bTkiLfR>_b(HgWKJ%F~Nr_oF3b#wrIijHG|(J>BYjM-sajE6;FiC7vY#};Gd zST$CUHDeuEH+B^pz@B062qXfFfD`NpUW5?BY=V%GM_5c)L#QR}BeW8_2v-S%gfYS= zB9o|3v?Y2H`NVi)In3rTB8+ej^> zQ=~r95NVuDChL%G$=>7$vVg20myx%S50Foi`^m%Pw-h?Xh~i8Mq9jtJloCocWk2Nv zrJpiFnV_ms&8eQ$2&#xWpIS+6pmtC%Q-`S&GF4Q#^mhymh7E(qNMa}%YZ-ePrx>>xFPTiH1=E+A$W$=bG8>s^ zm=Bn5Rah$aDtr}@$`X}2l~$F0mFKEdRdZE8)p@E5RI61Ft6o-prbbn>P~)iy)E2AN zsU20jsWz_8Qg>31P|s0cqrPALg8E|(vWA65poU1JRAaZs8I2(p#xiB`SVGovRs-uS zYnV-9TeA7=Om+qP8+I>yOjAR1s%ETak!GFdam@h^# z)@rS0t$wXH+Irf)+G6c;?H29p+V6F6oj{!|o%K3xI`?%6x;DB|x`n#ibhIR?(H}Q3Gzd138Ei2)WAMz7W9Vy`X}HnwgyEn!VS)>mv$8&{hQn>w4zwy3R}t;BYlZQm5)6pty=DfLrs+A-|>>;~;Q z_F?uV_HFjh9n2gO9o9Q^JA86v({H5aB!kjoO6 zc9$1ZZKsN-Zl8L~mE{`ly3)1N^`o1+o7}D0ZPeY&J;i;i`%NyJ8_8Y6J?}yE@b_5a zam?eLr<8@mESk|3$_SkmS{wQ>%qC18))9_|&j{ZT zes8AvOzF(F2#DZEY>2oYX&IRp`F#{ADl)1r>QS^)ba8a|EY_^#S^HO&t^Rgqwv=MZThqqEWH8 zxJo>d=ABlR_Bh=;eM9Tw|Ih34~oTE|= zX_mAr*D$vzw@+p(E0Yc6dFE}(8oqt`+R{gE3x4zjX+Sb3_cYE^= zgB=w+-tUy`ytONMS8KgRef4hA?t0j zufM;t32jm~jUGrkaOInTZ`zyfns>EuS}G30LFK_G-==(f<51|K&cocp&EJ`SxAh3? zNO>#LI=^+SEu(FqJ)ynt=!~PC9bO$rzPJB=?=j6w@a-(u02P7 zaQ)#(uUl{HW%tYNS3ItC^iAtK(eKlL`f9+{bJzISE?u8_z3;~C8@FyI-5j_jy7l;W z_U#vU3hqqYU3!mrul&B+{ptt$59)uk{;_4iZQ%G|z+lhASr6|H35TBkl>gI*;nGLU zN7W-nBaM%pA0HbH8olyl&XeJ%vZoWz%6?Y=dFykl=imL}`%BMQ{Mhgd`HRoLu6e2R za__6DuR6yg#~-}Tc|Gx_{H@O0eebyMy5GmWADJlpK>kqk(fVV@r_fLLKIeS?{4e)} z^ZO;zpECde00d`2O+f$vv5tKEQIh}w03c&XQcVB=dL;k=fP(-4`Tqa_faw4Lbua(` z>RI+y?e7jKeZ#YO-C08L3mK~#9!e8jO0fG`jQ!PNyX0!t=B?!f_7QURh^u=%3{ zCK#^suzKH7Ya6O-MG=8)Bj6|dMUV)CoQX0M%{kC`ZZzgV5eq7%@C^XFz#7+S7$;%? O0000KLZ*U+5Lu!Sk^o_Z5E4Meg@_7P6crJiNL9pw)e1;Xm069{HJUZAPk55R%$-RIA z6-eL&AQ0xu!e<4=008gy@A0LT~suv4>S3ILP<0Bm`DLLvaF4FK%)Nj?Pt*r}7;7Xa9z9H|HZjR63e zC`Tj$K)V27Re@400>HumpsYY5E(E}?0f1SyGDiY{y#)Yvj#!WnKwtoXnL;eg03bL5 z07D)V%>y7z1E4U{zu>7~aD})?0RX_umCct+(lZpemCzb@^6=o|A>zVpu|i=NDG+7} zl4`aK{0#b-!z=TL9Wt0BGO&T{GJWpjryhdijfaIQ&2!o}p04JRKYg3k&Tf zVxhe-O!X z{f;To;xw^bEES6JSc$k$B2CA6xl)ltA<32E66t?3@gJ7`36pmX0IY^jz)rRYwaaY4 ze(nJRiw;=Qb^t(r^DT@T3y}a2XEZW-_W%Hszxj_qD**t_m!#tW0KDiJT&R>6OvVTR z07RgHDzHHZ48atvzz&?j9lXF70$~P3Knx_nJP<+#`N z#-MZ2bTkiLfR>_b(HgWKJ%F~Nr_oF3b#wrIijHG|(J>BYjM-sajE6;FiC7vY#};Gd zST$CUHDeuEH+B^pz@B062qXfFfD`NpUW5?BY=V%GM_5c)L#QR}BeW8_2v-S%gfYS= zB9o|3v?Y2H`NVi)In3rTB8+ej^> zQ=~r95NVuDChL%G$=>7$vVg20myx%S50Foi`^m%Pw-h?Xh~i8Mq9jtJloCocWk2Nv zrJpiFnV_ms&8eQ$2&#xWpIS+6pmtC%Q-`S&GF4Q#^mhymh7E(qNMa}%YZ-ePrx>>xFPTiH1=E+A$W$=bG8>s^ zm=Bn5Rah$aDtr}@$`X}2l~$F0mFKEdRdZE8)p@E5RI61Ft6o-prbbn>P~)iy)E2AN zsU20jsWz_8Qg>31P|s0cqrPALg8E|(vWA65poU1JRAaZs8I2(p#xiB`SVGovRs-uS zYnV-9TeA7=Om+qP8+I>yOjAR1s%ETak!GFdam@h^# z)@rS0t$wXH+Irf)+G6c;?H29p+V6F6oj{!|o%K3xI`?%6x;DB|x`n#ibhIR?(H}Q3Gzd138Ei2)WAMz7W9Vy`X}HnwgyEn!VS)>mv$8&{hQn>w4zwy3R}t;BYlZQm5)6pty=DfLrs+A-|>>;~;Q z_F?uV_HFjh9n2gO9o9Q^JA86v({H5aB!kjoO6 zc9$1ZZKsN-Zl8L~mE{`ly3)1N^`o1+o7}D0ZPeY&J;i;i`%NyJ8_8Y6J?}yE@b_5a zam?eLr<8@mESk|3$_SkmS{wQ>%qC18))9_|&j{ZT zes8AvOzF(F2#DZEY>2oYX&IRp`F#{ADl)1r>QS^)ba8a|EY_^#S^HO&t^Rgqwv=MZThqqEWH8 zxJo>d=ABlR_Bh=;eM9Tw|Ih34~oTE|= zX_mAr*D$vzw@+p(E0Yc6dFE}(8oqt`+R{gE3x4zjX+Sb3_cYE^= zgB=w+-tUy`ytONMS8KgRef4hA?t0j zufM;t32jm~jUGrkaOInTZ`zyfns>EuS}G30LFK_G-==(f<51|K&cocp&EJ`SxAh3? zNO>#LI=^+SEu(FqJ)ynt=!~PC9bO$rzPJB=?=j6w@a-(u02P7 zaQ)#(uUl{HW%tYNS3ItC^iAtK(eKlL`f9+{bJzISE?u8_z3;~C8@FyI-5j_jy7l;W z_U#vU3hqqYU3!mrul&B+{ptt$59)uk{;_4iZQ%G|z+lhASr6|H35TBkl>gI*;nGLU zN7W-nBaM%pA0HbH8olyl&XeJ%vZoWz%6?Y=dFykl=imL}`%BMQ{Mhgd`HRoLu6e2R za__6DuR6yg#~-}Tc|Gx_{H@O0eebyMy5GmWADJlpK>kqk(fVV@r_fLLKIeS?{4e)} z^ZO;zpECde00d`2O+f$vv5tKEQIh}w03c&XQcVB=dL;k=fP(-4`Tqa_faw4Lbua(` z>RI+y?e7jKeZ#YO-C0hdWcK~#9!)RWIjTTu|ke{=51jR>{@lSZsiS4va05~K+h znu6$?^$AMf$1H3U7gBXAbW^OTFW|j5XI$KK|HTwUSI%m_GiSbU=A0S%@%@|f9}@&I zh9EHcS*=#1UJp6g-@|q~ol1gg!6gF5NHy77>T`7f>{7e=P*DU`m-}KQj|E1sL69al z8jVbIs)Ed$q$QMF@z{o8IHTK1$+w=`dDeo+7CpKtg4vabWcLCL&#)JeJUl#NczEQCiZxxG3^eZZKKa*>z@hr^#zZXMDT5!(DQh1NX;O+!}mvoqP9nd|-8otZm3J1FtM-MROk zd(L<7Io~~Z6+#F?Bve@uB}^Gvg=7EPK%hv7fg{%3uONlbknJDjV{$u;p z21N0NNE(>wgD{u-py*dX)~rX}ka+=I=PSR%7}*D{yhxxg^4)7l2Jg+n%L${=<$3roF~Qx=#9W772g@7x?PnX04> zv49+~_${3S3ctZJFVdKe!$^<98199&SmJlUbq>e*O^5Cj#%b57w(jl#fR2M8lX;kn z6lCcv-*Lm{%a|M<0#Uz#M&f8tyJ*{q=<;ojbMy4+&>Tb{ZXDssyuJu?Y!KRx6lLdK zFk?1aQa@piGBWX2w_`N%;wzGq4bf(dP6=u@-?7W9Qlt#_eB-fnK>-^BGvnVJ>=Y~n ztW2|0SW3ZV$CqXNmhd4v4nytSjxsW^^WqNV2J`$|tvjCG*F2d)Cy)}(PGaYVMGmYB zHO_szuaO8sL}}Q~^U yQ@7yJka?fex_^Mf03?twHis$I)foQ^J^ld`!qce0#nc-B0000 - * - */ -public class BuddyButton extends JButton { - public BuddyButton() { - this(null); - } - - public BuddyButton(String text) { - super(text); - setFocusable(false); - setMargin(SearchFieldUI.NO_INSETS); - - // Windows UI will add 1 pixel for width and height, if this is true - setFocusPainted(false); - - setBorderPainted(false); - setContentAreaFilled(false); - setIconTextGap(0); - - setBorder(null); - - setOpaque(false); - - setCursor(Cursor.getDefaultCursor()); - } - - // Windows UI overrides Insets. - // Who knows what other UIs are doing... - @Override - public Insets getInsets() { - return SearchFieldUI.NO_INSETS; - } - - @Override - public Insets getInsets(Insets insets) { - return getInsets(); - } - - @Override - public Insets getMargin() { - return getInsets(); - } - - @Override - public void setBorder(Border border) { - // Don't let Motif overwrite my Border - super.setBorder(BorderFactory.createEmptyBorder()); - } -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/prompt/BuddySupport.java b/src/main/java/org/jdesktop/swingx/prompt/BuddySupport.java deleted file mode 100644 index a92c300044..0000000000 --- a/src/main/java/org/jdesktop/swingx/prompt/BuddySupport.java +++ /dev/null @@ -1,151 +0,0 @@ -package org.jdesktop.swingx.prompt; - -import org.jdesktop.swingx.plaf.TextUIWrapper; - -import javax.swing.*; -import javax.swing.plaf.basic.BasicTextUI; -import java.awt.*; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -public class BuddySupport { - public enum Position { - LEFT, RIGHT - }; - - public static final String OUTER_MARGIN = "outerMargin"; - - public static void addLeft(Component c, JTextField textField) { - add(c, Position.LEFT, textField); - } - - public static void addRight(Component c, JTextField textField) { - add(c, Position.RIGHT, textField); - } - - public static void add(Component c, Position pos, JTextField textField) { - TextUIWrapper.getDefaultWrapper().install(textField, true); - - List leftBuddies = buddies(Position.LEFT, textField); - List rightBuddies = buddies(Position.RIGHT, textField); - - // ensure buddies are added - setLeft(textField, leftBuddies); - setRight(textField, rightBuddies); - - // check if component is already here - if (isBuddy(c, textField)) { - throw new IllegalStateException("Component already added."); - } - - if (Position.LEFT == pos) { - leftBuddies.add(c); - } else { - rightBuddies.add(0, c); - } - - addToComponentHierarchy(c, pos, textField); - } - - public static void addGap(int width, Position pos, JTextField textField) { - add(createGap(width), pos, textField); - } - - public static void setRight(JTextField textField, List rightBuddies) { - set(rightBuddies, Position.RIGHT, textField); - } - - public static void setLeft(JTextField textField, List leftBuddies) { - set(leftBuddies, Position.LEFT, textField); - } - - public static void set(List buddies, Position pos, JTextField textField) { - textField.putClientProperty(pos, buddies); - } - - private static void addToComponentHierarchy(Component c, Position pos, JTextField textField) { - textField.add(c, pos.toString()); - } - - public static List getLeft(JTextField textField) { - return getBuddies(Position.LEFT, textField); - } - - public static List getRight(JTextField textField) { - return getBuddies(Position.RIGHT, textField); - } - - public static List getBuddies(Position pos, JTextField textField) { - return Collections.unmodifiableList(buddies(pos, textField)); - } - - @SuppressWarnings("unchecked") - private static List buddies(Position pos, JTextField textField) { - List buddies = (List) textField.getClientProperty(pos); - - if (buddies != null) { - return buddies; - } - return new ArrayList(); - } - - public static boolean isBuddy(Component c, JTextField textField) { - return buddies(Position.LEFT, textField).contains(c) || buddies(Position.RIGHT, textField).contains(c); - } - - /** - * Because {@link BasicTextUI} removes all components when uninstalled and - * therefore all buddies are removed when the LnF changes. - * - * @param c - * @param textField - */ - public static void remove(JComponent c, JTextField textField) { - buddies(Position.LEFT, textField).remove(c); - buddies(Position.RIGHT, textField).remove(c); - - textField.remove(c); - } - - public static void removeAll(JTextField textField) { - List left = buddies(Position.LEFT, textField); - for (Component c : left) { - textField.remove(c); - } - left.clear(); - List right = buddies(Position.RIGHT, textField); - for (Component c : right) { - textField.remove(c); - } - right.clear(); - - } - - public static void setOuterMargin(JTextField buddyField, Insets margin) { - buddyField.putClientProperty(OUTER_MARGIN, margin); - } - - public static Insets getOuterMargin(JTextField buddyField) { - return (Insets) buddyField.getClientProperty(OUTER_MARGIN); - } - - public static void ensureBuddiesAreInComponentHierarchy(JTextField textField) { - for (Component c : BuddySupport.getLeft(textField)) { - addToComponentHierarchy(c, Position.LEFT, textField); - } - for (Component c : BuddySupport.getRight(textField)) { - addToComponentHierarchy(c, Position.RIGHT, textField); - } - } - - /** - * Create a gap to insert between to buddies. - * - * @param width - * @return - */ - public static Component createGap(int width) { - return Box.createHorizontalStrut(width); - } -} diff --git a/src/main/java/org/jdesktop/swingx/prompt/PromptSupport.java b/src/main/java/org/jdesktop/swingx/prompt/PromptSupport.java deleted file mode 100644 index f672a8bb45..0000000000 --- a/src/main/java/org/jdesktop/swingx/prompt/PromptSupport.java +++ /dev/null @@ -1,327 +0,0 @@ -package org.jdesktop.swingx.prompt; - -import org.jdesktop.swingx.JXFormattedTextField; -import org.jdesktop.swingx.JXTextArea; -import org.jdesktop.swingx.JXTextField; -import org.jdesktop.swingx.painter.Painter; -import org.jdesktop.swingx.painter.Painters; -import org.jdesktop.swingx.plaf.PromptTextUI; -import org.jdesktop.swingx.plaf.TextUIWrapper; - -import javax.swing.text.JTextComponent; -import java.awt.*; - -/** - *

                    - * Sets prompt text, foreground, background and {@link FocusBehavior} properties - * on a JTextComponent by calling - * {@link JTextComponent#putClientProperty(Object, Object)}. These properties - * are used by {@link PromptTextUI} instances to render the prompt of a text - * component. - *

                    - * - *

                    - * This class is used by {@link JXTextField}, {@link JXFormattedTextField} and - * {@link JXTextArea} to get and set prompt properties. {@link PromptTextUI} - * retrieves these properties using PromptSupport. - *

                    - * - * @see JXTextField - * @see JXFormattedTextField - * @see JXTextArea - * @see PromptTextUI - * - * @author Peter Weishapl - * @author Karl Schaefer - */ -public final class PromptSupport { - /** - * The prompt text property. - */ - public static final String PROMPT = "promptText"; - - /** - * The color of the prompt text property. - */ - public static final String FOREGROUND = "promptForeground"; - - /** - * The prompt background property. - */ - public static final String BACKGROUND = "promptBackground"; - - /** - * The prompt background property. - */ - public static final String BACKGROUND_PAINTER = "promptBackgroundPainter"; - - /** - * The focus behavior property. - */ - public static final String FOCUS_BEHAVIOR = "focusBehavior"; - - /** - * The font style property, if different from the components font. - */ - public static final String FONT_STYLE = "promptFontStyle"; - - /** - *

                    - * Determines how the {@link JTextComponent} is rendered when focused and no - * text is present. - *

                    - */ - public static enum FocusBehavior { - /** - * Keep the prompt text visible. - */ - SHOW_PROMPT, - /** - * Highlight the prompt text as it would be selected. - */ - HIGHLIGHT_PROMPT, - /** - * Hide the prompt text. - */ - HIDE_PROMPT - }; - - private PromptSupport() { - //prevent instantiation - } - - /** - *

                    - * Convenience method to set the promptText and - * promptTextColor on a {@link JTextComponent}. - *

                    - *

                    - * If stayOnUIChange is true, The prompt support will stay - * installed, even when the text components UI changes. See - * {@link #install(JTextComponent, boolean)}. - *

                    - * - * @param promptText - * @param promptForeground - * @param promptBackground - * @param textComponent - */ - public static void init(String promptText, Color promptForeground, Color promptBackground, - final JTextComponent textComponent) { - if (promptText != null && promptText.length() > 0) { - setPrompt(promptText, textComponent); - } - if (promptForeground != null) { - setForeground(promptForeground, textComponent); - } - if (promptBackground != null) { - setBackground(promptBackground, textComponent); - } - } - - /** - * Get the {@link FocusBehavior} of textComponent. - * - * @param textComponent - * @return the {@link FocusBehavior} or {@link FocusBehavior#HIDE_PROMPT} if - * none is set - */ - public static FocusBehavior getFocusBehavior(JTextComponent textComponent) { - FocusBehavior fb = (FocusBehavior) textComponent.getClientProperty(FOCUS_BEHAVIOR); - if (fb == null) { - fb = FocusBehavior.HIDE_PROMPT; - } - return fb; - } - - /** - * Sets the {@link FocusBehavior} on textComponent and - * repaints the component to reflect the changes, if it is the focus owner. - * - * @param focusBehavior - * @param textComponent - */ - public static void setFocusBehavior(FocusBehavior focusBehavior, JTextComponent textComponent) { - textComponent.putClientProperty(FOCUS_BEHAVIOR, focusBehavior); - if (textComponent.isFocusOwner()) { - textComponent.repaint(); - } - } - - /** - * Get the prompt text of textComponent. - * - * @param textComponent - * @return the prompt text - */ - public static String getPrompt(JTextComponent textComponent) { - return (String) textComponent.getClientProperty(PROMPT); - } - - /** - *

                    - * Sets the prompt text on textComponent. Also sets the - * tooltip text to the prompt text if textComponent has no - * tooltip text or the current tooltip text is the same as the current - * prompt text. - *

                    - *

                    - * Calls {@link #install(JTextComponent)} to ensure that the - * textComponents UI is wrapped by the appropriate - * {@link PromptTextUI}. - *

                    - * - * @param promptText - * @param textComponent - */ - public static void setPrompt(String promptText, JTextComponent textComponent) { - TextUIWrapper.getDefaultWrapper().install(textComponent, true); - - // display prompt as tooltip by default - if (textComponent.getToolTipText() == null || textComponent.getToolTipText().equals(getPrompt(textComponent))) { - textComponent.setToolTipText(promptText); - } - - textComponent.putClientProperty(PROMPT, promptText); - textComponent.repaint(); - } - - /** - * Get the foreground color of the prompt text. If no color has been set, - * the textComponents disabled text color will be returned. - * - * @param textComponent - * @return the color of the prompt text or - * {@link JTextComponent#getDisabledTextColor()} if none is set - */ - public static Color getForeground(JTextComponent textComponent) { - if (textComponent.getClientProperty(FOREGROUND) == null) { - return textComponent.getDisabledTextColor(); - } - return (Color) textComponent.getClientProperty(FOREGROUND); - } - - /** - * Sets the foreground color of the prompt on textComponent - * and repaints the component to reflect the changes. This color will be - * used when no text is present. - * - * @param promptTextColor - * @param textComponent - */ - public static void setForeground(Color promptTextColor, JTextComponent textComponent) { - textComponent.putClientProperty(FOREGROUND, promptTextColor); - textComponent.repaint(); - } - - /** - * Get the background color of the textComponent, when no - * text is present. If no color has been set, the textComponents - * background color color will be returned. - * - * @param textComponent - * @return the the background color of the text component, when no text is - * present - */ - public static Color getBackground(JTextComponent textComponent) { - if (textComponent.getClientProperty(BACKGROUND) == null) { - return textComponent.getBackground(); - } - return (Color) textComponent.getClientProperty(BACKGROUND); - } - - /** - *

                    - * Sets the prompts background color on textComponent and - * repaints the component to reflect the changes. This background color will - * only be used when no text is present. - *

                    - *

                    - * Calls {@link #install(JTextComponent)} to ensure that the - * textComponents UI is wrapped by the appropriate - * {@link PromptTextUI}. - *

                    - * - * @param background - * @param textComponent - */ - public static void setBackground(Color background, JTextComponent textComponent) { - TextUIWrapper.getDefaultWrapper().install(textComponent, true); - - textComponent.putClientProperty(BACKGROUND, background); - textComponent.repaint(); - } - - /** - * Get the background painter of the textComponent, when no - * text is present. If no painter has been set, then {@code null} will be returned. - * - * @param textComponent - * @return the background painter of the text component - */ - @SuppressWarnings("unchecked") - public static Painter getBackgroundPainter(T textComponent) { - Painter painter = (Painter) textComponent.getClientProperty(BACKGROUND_PAINTER); - - if (painter == null) { - painter = Painters.EMPTY_PAINTER; - } - - return painter; - } - - /** - *

                    - * Sets the prompts background painter on textComponent and - * repaints the component to reflect the changes. This background painter will - * only be used when no text is present. - *

                    - *

                    - * Calls {@link #install(JTextComponent)} to ensure that the - * textComponents UI is wrapped by the appropriate - * {@link PromptTextUI}. - *

                    - * - * @param background - * @param textComponent - */ - public static void setBackgroundPainter(Painter background, T textComponent) { - TextUIWrapper.getDefaultWrapper().install(textComponent, true); - - textComponent.putClientProperty(BACKGROUND_PAINTER, background); - textComponent.repaint(); - } - - /** - *

                    - * Set the style of the prompt font, if different from the - * textComponents font. - *

                    - *

                    - * Allowed values are {@link Font#PLAIN}, {@link Font#ITALIC}, - * {@link Font#BOLD}, a combination of {@link Font#BOLD} and - * {@link Font#ITALIC} or null if the prompt font should be - * the same as the textComponents font. - *

                    - * - * @param fontStyle - * @param textComponent - */ - public static void setFontStyle(Integer fontStyle, JTextComponent textComponent) { - textComponent.putClientProperty(FONT_STYLE, fontStyle); - textComponent.revalidate(); - textComponent.repaint(); - } - - /** - * Returns the font style of the prompt text, or null if the - * prompt's font style should not differ from the textComponents - * font. - * - * @param textComponent - * @return font style of the prompt text - */ - public static Integer getFontStyle(JTextComponent textComponent) { - return (Integer) textComponent.getClientProperty(FONT_STYLE); - } -} diff --git a/src/main/java/org/jdesktop/swingx/renderer/AbstractRenderer.java b/src/main/java/org/jdesktop/swingx/renderer/AbstractRenderer.java deleted file mode 100644 index 8ec8d78fb1..0000000000 --- a/src/main/java/org/jdesktop/swingx/renderer/AbstractRenderer.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * $Id: AbstractRenderer.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.renderer; - -import org.jdesktop.swingx.plaf.UIDependent; -import org.jdesktop.swingx.rollover.RolloverRenderer; - -import java.awt.*; -import java.io.Serializable; - -/** - * Convenience common ancestor for SwingX renderers. Concrete subclasses - * should - * - *
                      - *
                    • provide a bunch of convenience constructors as appropriate for the type of - * collection component - *
                    • create a reasonable default ComponentProvider if none is given - *
                    • implement the getXXCellRenderer by delegating to the ComponentProvider - *
                    - * - * @author Jeanette Winzenburg - */ -public abstract class AbstractRenderer - implements RolloverRenderer, StringValue, Serializable, UIDependent { - - protected ComponentProvider componentController; - - public AbstractRenderer(ComponentProvider provider) { - if (provider == null) { - provider = createDefaultComponentProvider(); - } - this.componentController = provider; - } - - /** - * Returns the ComponentProvider used by this renderer. - * - * @return the ComponentProvider used by this renderer - */ - public ComponentProvider getComponentProvider() { - return componentController; - } - - /** - * The default ComponentProvider to use if no special. - * - * @return the default ComponentProvider - */ - protected abstract ComponentProvider createDefaultComponentProvider(); - -// --------------- implement StringValue - - /** - * {@inheritDoc} - */ - @Override - public String getString(Object value) { - return componentController.getString(value); - } - - // ------------ implement RolloverRenderer - - /** - * {@inheritDoc} - */ - @Override - public void doClick() { - if (isEnabled()) { - ((RolloverRenderer) componentController).doClick(); - } - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isEnabled() { - return (componentController instanceof RolloverRenderer) - && ((RolloverRenderer) componentController).isEnabled(); - } - - /** - * {@inheritDoc} - */ - @Override - public void updateUI() { - componentController.updateUI(); - } - -//-------------------- legacy: configure arbitrary visuals - /** - * @param background - */ - public void setBackground(Color background) { - componentController.getDefaultVisuals().setBackground(background); - - } - - /** - * @param foreground - */ - public void setForeground(Color foreground) { - componentController.getDefaultVisuals().setForeground(foreground); - } - - -} diff --git a/src/main/java/org/jdesktop/swingx/renderer/BooleanValue.java b/src/main/java/org/jdesktop/swingx/renderer/BooleanValue.java deleted file mode 100644 index c3ee11c462..0000000000 --- a/src/main/java/org/jdesktop/swingx/renderer/BooleanValue.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * $Id: BooleanValue.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.renderer; - -/** - * A simple converter to return a Boolean value from an Object. - * - * @author Jeanette Winzenburg - */ -public interface BooleanValue { - - boolean getBoolean(Object value); - -} diff --git a/src/main/java/org/jdesktop/swingx/renderer/CellContext.java b/src/main/java/org/jdesktop/swingx/renderer/CellContext.java deleted file mode 100644 index 42eb2b2ee4..0000000000 --- a/src/main/java/org/jdesktop/swingx/renderer/CellContext.java +++ /dev/null @@ -1,436 +0,0 @@ -/* - * $Id: CellContext.java 3778 2010-09-07 10:10:49Z kleopatra $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.renderer; - -import javax.swing.*; -import javax.swing.border.Border; -import javax.swing.border.EmptyBorder; -import java.awt.*; -import java.io.Serializable; - -/** - * Encapsulates a snapshop of cell content and default display context - * for usage by a ComponentProvider. - *

                    - * - * One part is the super-set of properties that's traditionally passed into the - * core renderers' (Table-, List-, Tree-) getXXCellRendererComponent. Raw - * properties which define the context are - * - *

                      - *
                    • selected - *
                    • focused - *
                    • expanded - *
                    • leaf - *
                    - * - * Similarl to a ComponentAdapter, the properties are a super-set of those for - * a concrete component type. It's up to sub-classes (once the generics will be removed, until - * then the DefaultXXRenderers - PENDING JW: undecided - even after the generics removal, the - * param list in the subclasses are the same) fill any reasonable - * defaults for those not applicable to the specific component context. - * - * With those raw properties given, a CellContext looks up and returns dependent visual - * properties as appropriate for the concrete component. Typically, they are taken - * from the component if supported, or requested from the UIManager. - * Dependent properties are - * - *
                      - *
                    • foreground and background color - *
                    • border - *
                    • icon (relevant for trees only) - *
                    • editable - *
                    - * - * For a backdoor, the cell location (in horizontal and vertical view coordinates) - * and the originating component is accessible as well. Note that they are not necessarily - * valid for the "life" component. It's not recommended to actually use them. If needed, - * that's probably a sign the api is lacking :-) - *

                    - * - * - *

                      - * - *
                    • PENDING: still incomplete? how about Font? - *
                    • PENDING: protected methods? Probably need to open up - derived - * properties should be accessible in client code. - *
                    - * - * @author Jeanette Winzenburg - */ -public class CellContext implements Serializable { - - /** the default border for unfocused cells. */ - protected static Border noFocusBorder = new EmptyBorder(1, 1, 1, 1); - - /** ?? the default border for unfocused cells. ?? */ - private static final Border SAFE_NO_FOCUS_BORDER = new EmptyBorder(1, 1, 1, - 1); - - /** - * Returns the shared border for unfocused cells. - *

                    - * PENDING: ?? copied from default renderers - why is it done like this? - * - * @return the border for unfocused cells. - */ - private static Border getNoFocusBorder() { - if (System.getSecurityManager() != null) { - return SAFE_NO_FOCUS_BORDER; - } else { - return noFocusBorder; - } - } - - /** PENDING JW: maybe make this a WeakReference? Would be a more robust fix for Issue #1040-swingx. */ - protected transient JComponent component; - - /** PENDING JW: maybe make this a WeakReference? Would be a more robust fix for Issue #1040-swingx. */ - protected transient Object value; - - protected transient int row; - - protected transient int column; - - protected transient boolean selected; - - protected transient boolean focused; - - protected transient boolean expanded; - - protected transient boolean leaf; - - protected transient boolean dropOn; - - // --------------------------- install context - - - /** - * Sets the state of the cell's context. Convenience method for subclasses. - * - * @param value the content value of the cell - * @param row the cell's row index in view coordinates - * @param column the cell's column index in view coordinates - * @param selected the cell's selected state - * @param focused the cell's focused state - * @param expanded the cell's expanded state - * @param leaf the cell's leaf state - */ - protected void installState(Object value, int row, int column, - boolean selected, boolean focused, boolean expanded, boolean leaf) { - this.value = value; - this.row = row; - this.column = column; - this.selected = selected; - this.focused = focused; - this.expanded = expanded; - this.leaf = leaf; - } - - /** - * Replaces the value of this cell context with the given parameter and returns - * the replaced value. - * - * @param value the new value of the cell context - * @return the replaced value of the cell context - */ - public Object replaceValue(Object value) { - Object old = getValue(); - this.value = value; - return old; - } - - // -------------------- accessors of installed state - - /** - * Returns the component the cell resides on, may be null. Subclasses are - * expected to override and return the component type they are handling. - * - * @return the component the cell resides on, may be null. - */ - public JComponent getComponent() { - return component; - } - - /** - * Returns the value of the cell as set in the install. - * - * @return the content value of the cell. - */ - public Object getValue() { - return value; - } - - /** - * Returns the cell's row index in view coordinates as set in the install. - * - * @return the cell's row index. - */ - public int getRow() { - return row; - } - - /** - * Returns the cell's column index in view coordinates as set in the - * install. - * - * @return the cell's column index. - */ - public int getColumn() { - return column; - } - - /** - * Returns the selected state as set in the install. - * - * @return the cell's selected state. - */ - public boolean isSelected() { - return selected; - } - - /** - * Returns the focused state as set in the install. - * - * @return the cell's focused state. - */ - public boolean isFocused() { - return focused; - } - - /** - * Returns the expanded state as set in the install. - * - * @return the cell's expanded state. - */ - public boolean isExpanded() { - return expanded; - } - - /** - * Returns the leaf state as set in the install. - * - * @return the cell's leaf state. - */ - public boolean isLeaf() { - return leaf; - } - - // -------------------- accessors for derived state - /** - * Returns the cell's editability. Subclasses should override to return a - * reasonable cell-related state. - *

                    - * - * Here: false. - * - * @return the cell's editable property. - */ - public boolean isEditable() { - return false; - } - - /** - * Returns the icon. Subclasses should override to return a reasonable - * cell-related state. - *

                    - * - * Here: null. - * - * @return the cell's icon. - */ - public Icon getIcon() { - return null; - } - - /** - * Returns a boolean indicating if the cell is a drop location with any of the dropOn - * modes. It's up to subclasses to implement. - *

                    - * - * Here: false. - * - * @return true if the current cell is a drop location with any of the dropOn modes, - * false otherwise - */ - protected boolean isDropOn() { - return dropOn; - } - - /** - * Returns the foreground color of the renderered component or null if the - * component is null - *

                    - * - * PENDING: fallback to UI properties if comp == null? - * - * @return the foreground color of the rendered component. - */ - protected Color getForeground() { - if (isDropOn()) { - return getSelectionForeground(); - } - return getComponent() != null ? getComponent().getForeground() : null; - } - - /** - * Returns the background color of the renderered component or null if the - * component is null - *

                    - * - * PENDING: fallback to UI properties if comp == null? - * - * @return the background color of the rendered component. - */ - protected Color getBackground() { - if (isDropOn()) { - return getSelectionBackground(); - } - return getComponent() != null ? getComponent().getBackground() : null; - } - - /** - * Returns the default selection background color of the renderered - * component. Typically, the color is LF specific. It's up to subclasses to - * look it up. Here: returns null. - *

                    - * - * PENDING: return UI properties here? - * - * @return the selection background color of the rendered component. - */ - protected Color getSelectionBackground() { - return null; - } - - /** - * Returns the default selection foreground color of the renderered - * component. Typically, the color is LF specific. It's up to subclasses to - * look it up. Here: returns null. - *

                    - * - * PENDING: return UI properties here? - * - * @return the selection foreground color of the rendered component. - */ - protected Color getSelectionForeground() { - return null; - } - - /** - * Returns the default focus border of the renderered component. Typically, - * the border is LF specific. - * - * @return the focus border of the rendered component. - */ - protected Border getFocusBorder() { - Border border = null; - if (isSelected()) { - border = UIManager - .getBorder(getUIKey("focusSelectedCellHighlightBorder")); - } - if (border == null) { - border = UIManager.getBorder(getUIKey("focusCellHighlightBorder")); - } - return border; - } - - /** - * Returns the default border of the renderered component depending on cell - * state. Typically, the border is LF specific. - *

                    - * - * Here: returns the focus border if the cell is focused, the context - * defined no focus border otherwise. - * - * @return the default border of the rendered component. - */ - protected Border getBorder() { - if (isFocused()) { - return getFocusBorder(); - } - Border border = UIManager.getBorder(getUIKey("cellNoFocusBorder")); - return border != null ? border : getNoFocusBorder(); - } - - /** - * Returns the default focused foreground color of the renderered component. - * Typically, the color is LF specific. - * - * @return the focused foreground color of the rendered component. - */ - protected Color getFocusForeground() { - return UIManager.getColor(getUIKey("focusCellForeground")); - } - - /** - * Returns the default focused background color of the renderered component. - * Typically, the color is LF specific. - * - * @return the focused background color of the rendered component. - */ - protected Color getFocusBackground() { - return UIManager.getColor(getUIKey("focusCellBackground")); - } - - protected Color getDropCellForeground() { - return UIManager.getColor(getUIKey("dropCellForeground")); - } - - protected Color getDropCellBackground() { - return UIManager.getColor(getUIKey("dropCellBackground")); - } - // ----------------------- convenience - - /** - * Convenience method to build a component type specific lookup key for the - * UIManager. - * - * @param key the general part of the key - * @return a composed key build of a component type prefix and the input. - */ - protected String getUIKey(String key) { - return getUIPrefix() + key; - } - - /** - * Returns the component type specific prefix of keys for lookup in the - * UIManager. Subclasses must override, here: returns the empty String. - * - * @return the component type specific prefix. - */ - protected String getUIPrefix() { - return ""; - } - - /** - * Returns the Font of the target component or null if no component installed. - * @return - */ - protected Font getFont() { - return getComponent() != null ? getComponent().getFont() : null; - } - - public String getCellRendererName() { - return getUIPrefix() + "cellRenderer"; - } - -} diff --git a/src/main/java/org/jdesktop/swingx/renderer/CheckBoxProvider.java b/src/main/java/org/jdesktop/swingx/renderer/CheckBoxProvider.java deleted file mode 100644 index 93a22bc981..0000000000 --- a/src/main/java/org/jdesktop/swingx/renderer/CheckBoxProvider.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * $Id: CheckBoxProvider.java 3152 2008-12-23 18:12:39Z kschaefe $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.renderer; - -import javax.swing.*; - -/** - * A component provider which uses a JCheckBox. - *

                    - * - * This implementation respects a BooleanValue and a StringValue to configure - * the button's selected and text property. By default, the selected is mapped - * to a Boolean-type value and the text is empty. - *

                    - * - * To allow mapping to different types, client code can supply a custom - * StringValue which also implements BooleanValue. F.i. to render a cell value - * of type TableColumnExt with the column's visibility mapped to the selected - * and the column's title to the text: - * - *

                    
                    - *            
                    - *     BooleanValue bv = new BooleanValue(){
                    - *        public boolean getBoolean(Object value) {
                    - *           if (value instanceof TableColumnExt) 
                    - *               return ((TableColumnExt) value).isVisible();
                    - *           return false;
                    - *        }
                    - *     };
                    - *     StringValue sv = new StringValue() {
                    - *         public String getString(Object value) {
                    - *           if (value instanceof TableColumnExt) 
                    - *               return ((TableColumnExt) value).getTitle();
                    - *           return "";
                    - *         }
                    - *     };
                    - *     list.setCellRenderer(new DefaultListRenderer(
                    - *           new CheckBoxProvider(new MappedValue(sv, null, bv), JLabel.LEADING))); 
                    - * 
                    - * - * - * @see BooleanValue - * @see StringValue - * @see MappedValue - * - * @author Jeanette Winzenburg - */ -public class CheckBoxProvider extends ComponentProvider { - - private boolean borderPainted; - - /** - * Instantiates a CheckBoxProvider with default properties.

                    - * - */ - public CheckBoxProvider() { - this(null); - } - - /** - * Instantiates a CheckBoxProvider with the given StringValue and default - * alignment. - * - * @param stringValue the StringValue to use for formatting. - */ - public CheckBoxProvider(StringValue stringValue) { - this(stringValue, JLabel.CENTER); - } - - /** - * Instantiates a CheckBoxProvider with the given StringValue and - * alignment. - * - * @param stringValue the StringValue to use for formatting. - * @param alignment the horizontalAlignment. - */ - public CheckBoxProvider(StringValue stringValue, int alignment) { - super(stringValue == null ? StringValues.EMPTY : stringValue, alignment); - setBorderPainted(true); - } - - - /** - * Returns the border painted flag. - * @return the borderpainted flag to use on the checkbox. - * @see #setBorderPainted(boolean) - */ - public boolean isBorderPainted() { - return borderPainted; - } - - /** - * Sets the border painted flag. The underlying checkbox - * is configured with this value on every request.

                    - * - * The default value is true. - * - * @param borderPainted the borderPainted property to configure - * the underlying checkbox with. - * - * @see #isBorderPainted() - */ - public void setBorderPainted(boolean borderPainted) { - this.borderPainted = borderPainted; - } - - /** - * {@inheritDoc}

                    - * Overridden to set the button's selected state and text.

                    - * - * PENDING: set icon? - * - * @see #getValueAsBoolean(CellContext) - * @see #getValueAsString(CellContext) - */ - @Override - protected void format(CellContext context) { - rendererComponent.setSelected(getValueAsBoolean(context)); - rendererComponent.setText(getValueAsString(context)); - } - - /** - * Returns a boolean representation of the content.

                    - * - * This method messages the - * BooleanValue to get the boolean rep. If none available, - * checks for Boolean type directly and returns its value. Returns - * false otherwise.

                    - * - * PENDING: fallback to check for boolean is convenient .. could cleanup - * to use a default BooleanValue instead. - * - * @param context the cell context, must not be null. - * @return the boolean representation of the cell's content, - * or false if none if available. - */ - protected boolean getValueAsBoolean(CellContext context) { - if (formatter instanceof BooleanValue) { - return ((BooleanValue) formatter).getBoolean(context.getValue()); - } - return Boolean.TRUE.equals(context.getValue()); - } - - /** - * {@inheritDoc}

                    - * - * Here: set's the buttons horizontal alignment and borderpainted properties - * to this provider's properties. - */ - @Override - protected void configureState(CellContext context) { - rendererComponent.setBorderPainted(isBorderPainted()); - rendererComponent.setHorizontalAlignment(getHorizontalAlignment()); - } - - /** - * {@inheritDoc}

                    - * Here: returns a JCheckBox as rendering component.

                    - * - */ - @Override - protected AbstractButton createRendererComponent() { - return new JRendererCheckBox(); - } - -} diff --git a/src/main/java/org/jdesktop/swingx/renderer/ComponentProvider.java b/src/main/java/org/jdesktop/swingx/renderer/ComponentProvider.java deleted file mode 100644 index 0a7dbd739e..0000000000 --- a/src/main/java/org/jdesktop/swingx/renderer/ComponentProvider.java +++ /dev/null @@ -1,383 +0,0 @@ -/* - * $Id: ComponentProvider.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.renderer; - -import org.jdesktop.swingx.plaf.UIDependent; - -import javax.swing.*; -import java.io.Serializable; - -/** - * Abstract base class of a provider for a cell rendering component. Configures - * the component's content and default visuals depending on the renderee's state - * as captured in a CellContext. It's basically re-usable across - * all types of renderees (JTable, JList, JTree). - *

                    - * - *

                    Content

                    - * - * A provider guarantees to configure the "content" properties completely - * for any given object. The most frequent mappings are to text and/or icon - * properties of the rendering components. The former is controlled by a - * StringValue (see below), the latter by an IconValue. Subclasses which - * hand out component of type IconAware guarantee to reset its icon property - * always.

                    - * - * To ease content configuration, it supports a pluggable - * StringValue which purpose is to create and return a string - * representation of a given object. Implemenations of a ComponentProvider can - * use it to configure their rendering component as appropriate.

                    - * - * F.i. to show a Contributor cell object as "Busywoman, Herta" implement a - * custom StringValue and use it in a text rendering provider. (Note that SwingX - * default implementations of Table/List/TreeCellRenderer have convenience - * constructors to take the converter and create a default LabelProvider which - * uses it). - * - *

                    
                    - * StringValue stringValue = new StringValue() {
                    - * 
                    - *     public String getString(Object value) {
                    - *         if (!(value instanceof Contributor))
                    - *             return TO_STRING.getString(value);
                    - *         Contributor contributor = (Contributor) value;
                    - *         return contributor.lastName + ", " + contributor.firstName;
                    - *     }
                    - * 
                    - * };
                    - * table.setDefaultRenderer(Contributor.class, new DefaultTableRenderer(
                    - *         stringValue));
                    - * list.setCellRenderer(new DefaultListRenderer(stringValue));
                    - * tree.setCellRenderer(new DefaultTreeRenderer(stringValue));
                    - * 
                    - * 
                    - * - * To ease handling of formatted localizable content, there's a - * FormatStringValue which is pluggable with a - * Format.

                    - * - * F.i. to show a Date's time in the default Locale's SHORT - * version and right align the cell - * - *

                    
                    - *   StringValue stringValue = new FormatStringValue(
                    - *       DateFormat.getTimeInstance(DateFormat.SHORT));
                    - *   table.getColumnExt("timeID").setCellRenderer(
                    - *       new DefaultTableRenderer(stringValue, JLabel.RIGHT);  
                    - * 
                    - * - * - *

                    - * - * - *

                    Default Visuals

                    - * - * Guarantees to completely configure the visual properties listed below. As a - * consequence, client code (f.i. in Highlighters) can safely - * change them without long-lasting visual artefacts. - * - *
                      - *
                    • foreground and background, depending on selected and focused state - *
                    • border - *
                    • font - *
                    • Painter (if applicable) - *
                    • enabled - *
                    • componentOrientation - *
                    • tooltipText - *
                    • minimum-, maximum-, preferredSize - *
                    • horizontal alignment (if applicable) - *
                    - * - * As this internally delegates default visual configuration to a - * DefaultVisuals (which handles the first eight items) - * subclasses have to guarantee the alignment only. - *

                    - * - * - * @see StringValue - * @see FormatStringValue - * @see IconValue - * @see BooleanValue - * @see CellContext - * @see DefaultTableRenderer - * @see DefaultListRenderer - * @see DefaultTreeRenderer - * @see DefaultVisuals - */ -public abstract class ComponentProvider - implements Serializable, UIDependent { - /** component to render with. */ - protected T rendererComponent; - /** configurator of default visuals. */ - protected DefaultVisuals defaultVisuals; - /** horizontal (text) alignment of component. - * PENDING: useful only for labels, buttons? */ - protected int alignment; - /** the converter to use for string representation. - * PENDING: IconValue? */ - protected StringValue formatter; - - /** - * Instantiates a component provider with LEADING - * horizontal alignment and default to-String converter.

                    - * - */ - public ComponentProvider() { - this(null, JLabel.LEADING); - } - - /** - * Instantiates a component provider with LEADING - * horizontal alignment and the given converter.

                    - * - * @param converter the converter to use for mapping the cell value to a - * String representation. - */ - public ComponentProvider(StringValue converter) { - this(converter, JLabel.LEADING); - } - - /** - * Instantiates a LabelProvider with given to-String converter and given - * horizontal alignment. If the converter is null, the default TO_STRING is - * used. - * - * @param converter the converter to use for mapping the cell value to a - * String representation. - * @param alignment the horizontal alignment. - */ - public ComponentProvider(StringValue converter, int alignment) { - setHorizontalAlignment(alignment); - setStringValue(converter); - rendererComponent = createRendererComponent(); - defaultVisuals = createDefaultVisuals(); - } - - /** - * Configures and returns an appropriate component to render a cell - * in the given context. If the context is null, returns the - * component in its current state. - * - * @param context the cell context to configure from - * @return a component to render a cell in the given context. - */ - public T getRendererComponent(CellContext context) { - if (context != null) { - configureVisuals(context); - configureContent(context); - } - return rendererComponent; - } - - /** - * Sets the horizontal alignment property to configure the component with. - * Allowed values are those accepted by corresponding JLabel setter. The - * default value is JLabel.LEADING. This controller guarantees to apply the - * alignment on each request for a configured rendering component, if - * possible. Note that not all components have a horizontal alignment - * property. - * - * @param alignment the horizontal alignment to use when configuring the - * rendering component. - */ - public void setHorizontalAlignment(int alignment) { - this.alignment = alignment; - } - - /** - * Returns the horizontal alignment. - * - * @return the horizontal alignment of the rendering component. - * - * @see #setHorizontalAlignment(int) - * - */ - public int getHorizontalAlignment() { - return alignment; - } - - /** - * Sets the StringValue to use. If the given StringValue is null, - * defaults to StringValue.TO_STRING.

                    - * - * @param formatter the format to use. - */ - public void setStringValue(StringValue formatter) { - if (formatter == null) { - formatter = StringValues.TO_STRING; - } - this.formatter = formatter; - } - - /** - * Returns the StringValue to use for obtaining - * the String representation.

                    - * - * @return the StringValue used by this provider, guaranteed to - * be not null. - */ - public StringValue getStringValue() { - return formatter; - } - - /** - * Returns a string representation of the content. - *

                    - * - * This method guarantees to return the same string representation as would - * appear in the renderer, given that the corresponding cellContext has the - * same value as the parameter passed-in here. That is (assuming that the - * rendering component has a getText()) - * - *

                    
                    -     * if (equals(value, context.getValue()) {
                    -     *     assertEquals(provider.getString(value), 
                    -     *     provider.getRenderingComponent(context).getText());
                    -     * }
                    -     * 
                    - * - * This implementation simply delegates to its StringValue. Subclasses might - * need to override to comply. - *

                    - * - * This is a second attempt - the driving force is the need for a consistent - * string representation across all (new and old) themes: rendering, - * (pattern) filtering/highlighting, searching, auto-complete ... - *

                    - * - * @param value the object to represent as string. - * @return a appropriate string representation of the cell's content. - */ - public String getString(Object value) { - return formatter.getString(value); - } - - /** - * Returns a String representation of the content.

                    - * - * This method messages the - * StringValue to get the String rep. Meant as - * a convenience for subclasses. - * - * @param context the cell context, must not be null. - * @return a appropriate string representation of the cell's content. - */ - protected String getValueAsString(CellContext context) { - Object value = context.getValue(); - return formatter.getString(value); - } - - /** - * Returns a Icon representation of the content.

                    - * - * This method messages the - * IconValue to get the Icon rep. Meant as - * a convenience for subclasses. - * - * @param context the cell context, must not be null. - * @return a appropriate icon representation of the cell's content, - * or null if non if available. - */ - protected Icon getValueAsIcon(CellContext context) { - Object value = context.getValue(); - if (formatter instanceof IconValue) { - return ((IconValue) formatter).getIcon(value); - } - return null; - } - - /** - * Configures the rendering component's default visuals frome - * the given cell context. Here: delegates to the renderer - * controller. - * - * @param context the cell context to configure from, must not be null. - * @see DefaultVisuals - */ - protected void configureVisuals(CellContext context) { - defaultVisuals.configureVisuals(rendererComponent, context); - } - - /** - * Configures the renderering component's content and state from the - * given cell context. - * - * @param context the cell context to configure from, must not be null. - * - * @see #configureState(CellContext) - * @see #format(CellContext) - */ - protected void configureContent(CellContext context) { - configureState(context); - format(context); - } - - /** - * Formats the renderering component's content from the - * given cell context. - * - * @param context the cell context to configure from, must not be null. - */ - protected abstract void format(CellContext context); - - /** - * Configures the rendering component's state from the - * given cell context. - * @param context the cell context to configure from, must not be null. - */ - protected abstract void configureState(CellContext context); - - /** - * Factory method to create and return the component to use for rendering.

                    - * - * @return the component to use for rendering. - */ - protected abstract T createRendererComponent(); - - /** - * Factory method to create and return the DefaultVisuals used by this - * to configure the default visuals. Here: creates the default controller - * parameterized to the same type as this. - * - * @return the controller used to configure the default visuals of - * the rendering component. - */ - protected DefaultVisuals createDefaultVisuals() { - return new DefaultVisuals(); - } - - /** - * Intermediate exposure during refactoring... - * - * @return the default visual configurator used by this. - */ - protected DefaultVisuals getDefaultVisuals() { - return defaultVisuals; - } - - /** - * {@inheritDoc} - */ - @Override - public void updateUI() { - SwingUtilities.updateComponentTreeUI(rendererComponent); - } -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/renderer/DefaultListRenderer.java b/src/main/java/org/jdesktop/swingx/renderer/DefaultListRenderer.java deleted file mode 100644 index 7853fa2578..0000000000 --- a/src/main/java/org/jdesktop/swingx/renderer/DefaultListRenderer.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * $Id: DefaultListRenderer.java 3779 2010-09-07 18:01:55Z kschaefe $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.renderer; - -import javax.swing.*; -import java.awt.*; - -/** - * Adapter to glue SwingX renderer support to core API. It has convenience - * constructors to create a LabelProvider, optionally configured with a - * StringValue and horizontal alignment. Typically, client code does not - * interact with this class except at instantiation time. - *

                    - * - * Note: core DefaultListCellRenderer shows either an icon or the element's - * toString representation, depending on whether or not the given value - * is of type icon or implementors. This renderer's empty/null provider - * constructor takes care of configuring the default provider with a converter - * which mimics that behaviour. When instantiating this renderer with - * any of the constructors which have converters as parameters, - * it's up to the client code to supply the appropriate converter, if needed: - * - * - *

                    
                    - * StringValue sv = new StringValue() {
                    - * 
                    - *     public String getString(Object value) {
                    - *         if (value instanceof Icon) {
                    - *             return "";
                    - *         }
                    - *         return StringValue.TO_STRING.getString(value);
                    - *     }
                    - * 
                    - * };
                    - * StringValue lv = new MappedValue(sv, IconValue.ICON);
                    - * listRenderer = new DefaultListRenderer(lv, alignment);
                    - * 
                    - * 
                    - * - *

                    - * - * - * @author Jeanette Winzenburg - * - * @see ComponentProvider - * @see StringValue - * @see IconValue - * @see MappedValue - * - * - */ -public class DefaultListRenderer extends AbstractRenderer - implements ListCellRenderer { - - protected ListCellContext cellContext; - - /** - * Instantiates a default list renderer with the default component - * provider. - * - */ - public DefaultListRenderer() { - this((ComponentProvider) null); - } - - /** - * Instantiates a ListCellRenderer with the given ComponentProvider. - * If the provider is null, creates and uses a default. The default - * provider is of type LabelProvider

                    - * - * Note: the default provider is configured with a custom StringValue - * which behaves exactly as core DefaultListCellRenderer: depending on - * whether or not given value is of type icon or implementors, it shows - * either the icon or the element's toString. - * - * @param componentProvider the provider of the configured component to - * use for cell rendering - */ - public DefaultListRenderer(ComponentProvider componentProvider) { - super(componentProvider); - this.cellContext = new ListCellContext(); - } - - /** - * Instantiates a default table renderer with a default component controller - * using the given converter.

                    - * - * PENDING JW: how to guarantee core consistent icon handling? Leave to - * client code? - * - * @param converter the converter to use for mapping the content value to a - * String representation. - * - */ - public DefaultListRenderer(StringValue converter) { - this(new LabelProvider(converter)); - } - - /** - * Instantiates a default list renderer with a default component - * controller using the given converter and horizontal - * alignment. - * - * PENDING JW: how to guarantee core consistent icon handling? Leave to - * client code? - * - * - * @param converter the converter to use for mapping the - * content value to a String representation. - * @param alignment the horizontal alignment. - */ - public DefaultListRenderer(StringValue converter, int alignment) { - this(new LabelProvider(converter, alignment)); - } - - - /** - * Instantiates a default list renderer with default component provider - * using both converters. - * - * @param stringValue the converter to use for the string representation - * @param iconValue the converter to use for the icon representation - */ - public DefaultListRenderer(StringValue stringValue, IconValue iconValue) { - this(new MappedValue(stringValue, iconValue)); - } - - /** - * Instantiates a default list renderer with default component provider - * using both converters and the given alignment. - * - * @param stringValue the converter to use for the string representation - * @param iconValue the converter to use for the icon representation - * @param alignment the rendering component's horizontal alignment - */ - public DefaultListRenderer(StringValue stringValue, IconValue iconValue, - int alignment) { - this(new MappedValue(stringValue, iconValue), alignment); - } - - // -------------- implements javax.swing.table.ListCellRenderer - /** - * - * Returns a configured component, appropriate to render the given - * list cell.

                    - * - * Note: The component's name is set to "List.cellRenderer" for the sake - * of Synth-based LAFs. - * - * @param list the JList to render on - * @param value the value to assign to the cell - * @param isSelected true if cell is selected - * @param cellHasFocus true if cell has focus - * @param index the row index (in view coordinates) of the cell to render - * @return a component to render the given list cell. - */ - @Override - public Component getListCellRendererComponent(JList list, Object value, - int index, boolean isSelected, boolean cellHasFocus) { - cellContext.installContext(list, value, index, 0, isSelected, - cellHasFocus, true, true); - Component comp = componentController.getRendererComponent(cellContext); - // fix issue #1040-swingx: memory leak if value not released - cellContext.replaceValue(null); - return comp; - } - - /** - * {@inheritDoc} - */ - @Override - protected ComponentProvider createDefaultComponentProvider() { - return new LabelProvider(createDefaultStringValue()); - } - - /** - * Creates and returns the default StringValue for a JList.

                    - * This is added to keep consistent with core list rendering which - * shows either the Icon (for Icon value types) or the default - * to-string for non-icon types. - * - * @return the StringValue to use by default. - */ - private StringValue createDefaultStringValue() { - return MappedValues.STRING_OR_ICON_ONLY; - } -} diff --git a/src/main/java/org/jdesktop/swingx/renderer/DefaultTableRenderer.java b/src/main/java/org/jdesktop/swingx/renderer/DefaultTableRenderer.java deleted file mode 100644 index 57cd3d7823..0000000000 --- a/src/main/java/org/jdesktop/swingx/renderer/DefaultTableRenderer.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * $Id: DefaultTableRenderer.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.renderer; - - -import javax.swing.*; -import javax.swing.table.TableCellRenderer; -import java.awt.*; - - -/** - * Adapter to glue SwingX renderer support to core api. It has convenience - * constructors to create a LabelProvider, optionally configured with a - * StringValue and horizontal alignment. Typically, client code does not - * interact with this class except at instantiation time. - *

                    - * - * JXTable uses instances of this as per-class default renderers. - * - *

                    
                    - * setDefaultRenderer(Object.class, new DefaultTableRenderer());
                    - * setDefaultRenderer(Number.class, new DefaultTableRenderer(
                    - *         FormatStringValues.NUMBER_TO_STRING, JLabel.RIGHT));
                    - * setDefaultRenderer(Date.class, new DefaultTableRenderer(
                    - *         FormatStringValues.DATE_TO_STRING));
                    - * // use the same center aligned default for Image/Icon
                    - * TableCellRenderer renderer = new DefaultTableRenderer(new MappedValue(
                    - *         StringValues.EMPTY, IconValues.ICON), JLabel.CENTER);
                    - * setDefaultRenderer(Icon.class, renderer);
                    - * setDefaultRenderer(ImageIcon.class, renderer);
                    - * // use a CheckBoxProvider for booleans
                    - * setDefaultRenderer(Boolean.class,
                    - *         new DefaultTableRenderer(new CheckBoxProvider()));
                    - * 
                    - * - * - * - * @author Jeanette Winzenburg - * - * @see ComponentProvider - * @see LabelProvider - * @see StringValue - * @see IconValue - * @see MappedValue - * @see CellContext - * - */ -public class DefaultTableRenderer extends AbstractRenderer - implements TableCellRenderer { - - private TableCellContext cellContext; - - - /** - * Instantiates a default table renderer with the default component - * provider. - * - * @see #DefaultTableRenderer(ComponentProvider) - */ - public DefaultTableRenderer() { - this((ComponentProvider) null); - } - - /** - * Instantiates a default table renderer with the given component provider. - * If the controller is null, creates and uses a default. The default - * provider is of type LabelProvider. - * - * @param componentProvider the provider of the configured component to - * use for cell rendering - */ - public DefaultTableRenderer(ComponentProvider componentProvider) { - super(componentProvider); - this.cellContext = new TableCellContext(); - } - - /** - * Instantiates a default table renderer with a default component - * provider using the given converter. - * - * @param converter the converter to use for mapping the - * content value to a String representation. - * - * @see #DefaultTableRenderer(ComponentProvider) - */ - public DefaultTableRenderer(StringValue converter) { - this(new LabelProvider(converter)); - } - - /** - * Instantiates a default table renderer with a default component - * provider using the given converter and horizontal - * alignment. - * - * @param converter the converter to use for mapping the - * content value to a String representation. - * - * @see #DefaultTableRenderer(ComponentProvider) - */ - public DefaultTableRenderer(StringValue converter, int alignment) { - this(new LabelProvider(converter, alignment)); - } - - /** - * Intantiates a default table renderer with default component provider - * using both converters. - * - * @param stringValue the converter to use for the string representation - * @param iconValue the converter to use for the icon representation - */ - public DefaultTableRenderer(StringValue stringValue, IconValue iconValue) { - this(new MappedValue(stringValue, iconValue)); - } - - /** - * Intantiates a default table renderer with default component provider - * using both converters and the given alignment. - * - * @param stringValue the converter to use for the string representation - * @param iconValue the converter to use for the icon representation - * @param alignment the rendering component's horizontal alignment - */ - public DefaultTableRenderer(StringValue stringValue, IconValue iconValue, - int alignment) { - this(new MappedValue(stringValue, iconValue), alignment); - } - - // -------------- implements javax.swing.table.TableCellRenderer - /** - * - * Returns a configured component, appropriate to render the given - * list cell.

                    - * - * Note: The component's name is set to "Table.cellRenderer" for the sake - * of Synth-based LAFs. - * - * @param table the JTable - * @param value the value to assign to the cell at - * [row, column] - * @param isSelected true if cell is selected - * @param hasFocus true if cell has focus - * @param row the row of the cell to render - * @param column the column of the cell to render - * @return the default table cell renderer - */ - @Override - public Component getTableCellRendererComponent(JTable table, Object value, - boolean isSelected, boolean hasFocus, int row, int column) { - cellContext.installContext(table, value, row, column, isSelected, hasFocus, - true, true); - Component comp = componentController.getRendererComponent(cellContext); - // fix issue #1040-swingx: memory leak if value not released - cellContext.replaceValue(null); - return comp; - } - - /** - * {@inheritDoc} - */ - @Override - protected ComponentProvider createDefaultComponentProvider() { - return new LabelProvider(); - } - - -} - - diff --git a/src/main/java/org/jdesktop/swingx/renderer/DefaultTreeRenderer.java b/src/main/java/org/jdesktop/swingx/renderer/DefaultTreeRenderer.java deleted file mode 100644 index 50a6af4cf7..0000000000 --- a/src/main/java/org/jdesktop/swingx/renderer/DefaultTreeRenderer.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * $Id: DefaultTreeRenderer.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.renderer; - - -import javax.swing.*; -import javax.swing.tree.TreeCellRenderer; -import java.awt.*; - - -/** - * Adapter to glue SwingX renderer support to core api. - *

                    - * - * - * @author Jeanette Winzenburg - * - * - */ -public class DefaultTreeRenderer extends AbstractRenderer - implements TreeCellRenderer { - - private TreeCellContext cellContext; - - /** - * Instantiates a default tree renderer with the default component - * provider. - * - */ - public DefaultTreeRenderer() { - this((ComponentProvider)null); - } - - - /** - * Instantiates a default tree renderer with the given component provider. - * If the controller is null, creates and uses a default. The default - * controller is of type WrappingProvider. - * - * @param componentProvider the provider of the configured component to - * use for cell rendering - */ - public DefaultTreeRenderer(ComponentProvider componentProvider) { - super(componentProvider); - this.cellContext = new TreeCellContext(); - } - - /** - * Instantiates a default tree renderer with the default - * wrapping provider, using the given IconValue for - * customizing the icons. - * - * @param iv the IconValue to use for mapping a custom icon - * for a given value - * - */ - public DefaultTreeRenderer(IconValue iv) { - this(new WrappingProvider(iv)); - } - - /** - * Instantiates a default tree renderer with a default component - * provider using the given converter. - * - * @param sv the converter to use for mapping the - * content value to a String representation. - * - */ - public DefaultTreeRenderer(StringValue sv) { - this(new WrappingProvider(sv)); - } - - - /** - * Instantiates a default tree renderer with the default - * wrapping provider, using the given IconValue for - * customizing the icons and the given StringValue for - * node content. - * - * @param iv the IconValue to use for mapping a custom icon - * for a given value - * @param sv the converter to use for mapping the - * content value to a String representation. - * - */ - public DefaultTreeRenderer(IconValue iv, StringValue sv) { - this(new WrappingProvider(iv, sv)); - } - - /** - * Instantiates a default tree renderer with the default - * wrapping provider, using the given IconValue for - * customizing the icons and the given StringValue for - * node content. - * - * @param iv the IconValue to use for mapping a custom icon - * for a given value - * @param sv the converter to use for mapping the - * content value to a String representation. - * @param unwrapUserObject a flag indicating whether this provider - * should auto-unwrap the userObject from the context value. - * - */ - public DefaultTreeRenderer(IconValue iv, StringValue sv, boolean unwrapUserObject) { - this(new WrappingProvider(iv, sv, unwrapUserObject)); - } - - // -------------- implements javax.swing.table.TableCellRenderer - /** - * - * Returns a configured component, appropriate to render the given tree - * cell.

                    - * - * @param tree the JTree - * @param value the value to assign to the cell - * @param selected true if cell is selected - * @param expanded true if the cell is expanded - * @param leaf true if the cell is a leaf - * @param hasFocus true if cell has focus - * @param row the row of the cell to render - * @return a component to render the given list cell. - */ - @Override - public Component getTreeCellRendererComponent(JTree tree, Object value, - boolean selected, boolean expanded, boolean leaf, int row, - boolean hasFocus) { - cellContext.installContext(tree, value, row, 0, selected, hasFocus, - expanded, leaf); - Component comp = componentController.getRendererComponent(cellContext); - // fix issue #1040-swingx: memory leak if value not released - cellContext.replaceValue(null); - return comp; - } - - - /** - * {@inheritDoc} - */ - @Override - protected ComponentProvider createDefaultComponentProvider() { - return new WrappingProvider(); - } - - -} - - diff --git a/src/main/java/org/jdesktop/swingx/renderer/DefaultVisuals.java b/src/main/java/org/jdesktop/swingx/renderer/DefaultVisuals.java deleted file mode 100644 index 1c96e5e940..0000000000 --- a/src/main/java/org/jdesktop/swingx/renderer/DefaultVisuals.java +++ /dev/null @@ -1,264 +0,0 @@ -/* - * $Id: DefaultVisuals.java 3778 2010-09-07 10:10:49Z kleopatra $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.renderer; - -import javax.swing.*; -import java.awt.*; -import java.io.Serializable; - -/** - * Encapsulates the default visual configuration of renderering components, - * respecting the state of the passed-in CellContext. It's - * basically re-usable across all types of renderees (JTable, JList, JTree). - *

                    - * - * Guarantees to completely configure the default visual properties (listed - * below) of a given component. As a consequence, client code (f.i. in - * Highlighters) can safely change them without long-lasting - * visual artefacts. - * - *

                      - *
                    • foreground and background, depending on selected and focused state - *
                    • border - *
                    • font - *
                    • Painter (if applicable) - *
                    • enabled - *
                    • componentOrientation - *
                    • toolTipText - *
                    • minimum-, maximum-, preferredSize - *
                    • name - *
                    - * - * Client code will rarely need to be aware of this class. It's the single - * place to change on introduction of new properties considered as belonging - * to the "default visuals" of rendering components.

                    - * - * PENDING: allow mutators for overruling the CellContexts - * defaults? Would prefer not to, as in the context of SwingX visual config on - * the renderer level is discouraged (the way to go are Highlighters.

                    - * - * PENDING: not yet quite decided whether the toolTipText property belongs - * into the visual default config. Doing so gives client code the choice to - * set it either in a Highlighter or a custom ComponentProvider. - * - * @author Jeanette Winzenburg - * - * @see CellContext - */ -public class DefaultVisuals implements Serializable { - - - private Color unselectedForeground; - - private Color unselectedBackground; - - /** - * Sets the renderer's unselected-foreground color to the specified color. - * If not null this color will overrule the default color of - * the CellContext. - * - * @param c set the foreground color to this value - */ - public void setForeground(Color c) { - unselectedForeground = c; - } - - /** - * Sets the renderer's unselected-background color to the specified color. - * If not null this color will overrule the default color of - * the CellContext. - * - * @param c set the background color to this value - */ - public void setBackground(Color c) { - unselectedBackground = c; - } - - - //---------------- subclass configuration - /** - * Configures all default visual state of the rendering component from the - * given cell context. - * - * @param renderingComponent the component to configure, must not be null - * @param context the cell context to configure from, must not be null - * @throws NullPointerException if either renderingComponent or cellContext - * is null - */ - public void configureVisuals(T renderingComponent, CellContext context) { - configureState(renderingComponent, context); - configureColors(renderingComponent, context); - configureBorder(renderingComponent, context); - configurePainter(renderingComponent, context); - } - - /** - * Configures the default Painter if applicable. Here: set's to null. - * - * @param renderingComponent the component to configure, must not be null - * @param context the cell context to configure from, must not be null - */ - protected void configurePainter(T renderingComponent, CellContext context) { - if (renderingComponent instanceof PainterAware) { - ((PainterAware) renderingComponent).setPainter(null); - } - - } - - /** - * Configure "divers" visual state of the rendering component from the given - * cell context. - *

                    - * - * Here: synch Font, ComponentOrientation and - * enabled to context's component. Resets toolTipText to null. - * Calls configureSizes to reset xxSize if appropriate. Resets the component's - * name property. - *

                    - * - * PENDING: not fully defined - "divers" means everything that's not - * Colors - * nor Border nor Painter. - * - * @param renderingComponent the component to configure, must not be null - * @param context the cell context to configure from, must not be null - */ - protected void configureState(T renderingComponent, CellContext context) { - renderingComponent.setName(context.getCellRendererName()); - renderingComponent.setToolTipText(null); - configureSizes(renderingComponent, context); - // PENDING JW: as of Issue #1269 this was changed to query the - // CellContext for the font - should move out off the else? - // context takes care of null component - renderingComponent.setFont(context.getFont()); - if (context.getComponent() == null) { - // what to do? - // we guarantee to cleanup completely - what are the defaults? - // leave the decistion to the context? - } else { - renderingComponent.setEnabled(context.getComponent().isEnabled()); - renderingComponent.applyComponentOrientation(context.getComponent() - .getComponentOrientation()); - } - } - - /** - * Configures min-, max, preferredSize properties of the renderingComponent. - * - * Here: set all to null. - * - * @param renderingComponent the component to configure, must not be null - * @param context the cell context to configure from, must not be null - */ - protected void configureSizes(T renderingComponent, CellContext context) { - renderingComponent.setPreferredSize(null); - renderingComponent.setMinimumSize(null); - renderingComponent.setMaximumSize(null); - } - - /** - * Configures colors of rendering component from the given cell context. - * - * @param renderingComponent the component to configure, must not be null - * @param context the cell context to configure from, must not be null - */ - protected void configureColors(T renderingComponent, CellContext context) { - if (context.isSelected()) { - renderingComponent.setForeground(context.getSelectionForeground()); - renderingComponent.setBackground(context.getSelectionBackground()); - } else { - renderingComponent.setForeground(getForeground(context)); - renderingComponent.setBackground(getBackground(context)); - } - if (context.isFocused()) { - configureFocusColors(renderingComponent, context); - } - } - /** - * Configures focus-related colors form given cell context.

                    - * - * PENDING: move to context as well? - it's the only comp - * with focus specifics? Problem is the parameter type... - * - * @param renderingComponent the component to configure, must not be null - * @param context the cell context to configure from, must not be null - */ - protected void configureFocusColors(T renderingComponent, CellContext context) { - if (!context.isSelected() && context.isEditable()) { - Color col = context.getFocusForeground(); - if (col != null) { - renderingComponent.setForeground(col); - } - col = context.getFocusBackground(); - if (col != null) { - renderingComponent.setBackground(col); - } - } - } - - - /** - * Configures the rendering component's border from the given cell context.

                    - * - * @param renderingComponent the component to configure, must not be null - * @param context the cell context to configure from, must not be null - */ - protected void configureBorder(T renderingComponent, CellContext context) { - renderingComponent.setBorder(context.getBorder()); - } - - /** - * Returns the unselected foreground to use for the rendering - * component.

                    - * - * Here: returns this renderer's unselected foreground is not null, - * returns the foreground from the given context. In other words: - * the renderer's foreground takes precedence if set. - * - * @param context the cell context. - * @return the unselected foreground. - */ - protected Color getForeground(CellContext context) { - if (unselectedForeground != null) - return unselectedForeground; - return context.getForeground(); - } - - /** - * Returns the unselected background to use for the rendering - * component.

                    - * - * Here: returns this renderer's unselected background is not null, - * returns the background from the given context. In other words: - * the renderer's background takes precedence if set. - * - * @param context the cell context. - * @return the unselected background. - */ - protected Color getBackground(CellContext context) { - if (unselectedBackground != null) - return unselectedBackground; - return context.getBackground(); - } - - - -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/renderer/FormatStringValue.java b/src/main/java/org/jdesktop/swingx/renderer/FormatStringValue.java deleted file mode 100644 index 72ddc9bfde..0000000000 --- a/src/main/java/org/jdesktop/swingx/renderer/FormatStringValue.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * $Id: FormatStringValue.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.renderer; - -import java.text.Format; - -/** - * Base type for Format-backed StringValue. Has - * static defaults for Date and Number which use the locale-dependent default - * Formats as returned from xxFormat.getInstance(). - *

                    - * - * This class is intended to ease the handling of formatted cell content. - * F.i. to show a list of Dates in the default - * Locale's FULL version and right align the text: - * - *

                    
                    - *    StringValue stringValue = new FormatStringValue(
                    - *        DateFormat.getInstance(DateFormat.FULL));
                    - *    list.setCellRenderer(
                    - *        new DefaultListRenderer(stringValue, JLabel.RIGHT);  
                    - * 
                    - * - * - * PENDING: need to update on Locale change? How to detect? When? - * - * @author Jeanette Winzenburg - */ -public class FormatStringValue implements StringValue { - - /** the format used in creating the String representation. */ - protected Format format; - - /** - * Instantiates a formatted converter with null format. - * - */ - public FormatStringValue() { - this(null); - } - - /** - * Instantiates a formatted converter with the given Format. - * - * @param format the format to use in creating the String representation. - */ - public FormatStringValue(Format format) { - this.format = format; - } - - /** - * - * @return the format used in creating the String representation. - */ - public Format getFormat() { - return format; - } - - /** - * {@inheritDoc} - */ - @Override - public String getString(Object value) { - if (value == null) return ""; - if (format != null) { - try { - return format.format(value); - } catch (IllegalArgumentException e) { - // didn't work, nothing we can do - } - } - return value.toString(); - } - -} diff --git a/src/main/java/org/jdesktop/swingx/renderer/HyperlinkProvider.java b/src/main/java/org/jdesktop/swingx/renderer/HyperlinkProvider.java deleted file mode 100644 index 8742034f85..0000000000 --- a/src/main/java/org/jdesktop/swingx/renderer/HyperlinkProvider.java +++ /dev/null @@ -1,285 +0,0 @@ -/* - * $Id: HyperlinkProvider.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.renderer; - -import org.jdesktop.swingx.JXHyperlink; -import org.jdesktop.swingx.hyperlink.AbstractHyperlinkAction; -import org.jdesktop.swingx.rollover.RolloverProducer; -import org.jdesktop.swingx.rollover.RolloverRenderer; - -import java.awt.*; -import java.awt.event.ActionEvent; - -/** - * Renderer for hyperlinks".

                    - * - * The renderer is configured with a LinkAction. - * It's mostly up to the developer to guarantee that the all - * values which are passed into the getXXRendererComponent(...) are - * compatible with T: she can provide a runtime class to check against. - * If it isn't the renderer will configure the - * action with a null target.

                    - * - * It's recommended to not use the given Action anywhere else in code, - * as it is updated on each getXXRendererComponent() call which might - * lead to undesirable side-effects.

                    - * - * Internally uses JXHyperlink as rendering component.

                    - * - * PENDING: can go from ButtonProvider?

                    - * - * PENDING: make renderer respect selected cell state.

                    - * - * PENDING: TreeCellRenderer has several issues

                    - *

                      - *
                    1. no icons - *
                    2. usual background highlighter issues - *
                    - * - * @author Jeanette Winzenburg - */ -public class HyperlinkProvider - extends ComponentProvider implements - RolloverRenderer { - - - private AbstractHyperlinkAction linkAction; - protected Class targetClass; - - /** - * Instantiate a LinkRenderer with null LinkAction and null - * targetClass. - * - */ - public HyperlinkProvider() { - this(null, null); - } - - /** - * Instantiate a LinkRenderer with the LinkAction to use with - * target values. - * - * @param linkAction the action that acts on values. - */ - public HyperlinkProvider(AbstractHyperlinkAction linkAction) { - this(linkAction, null); - } - - /** - * Instantiate a LinkRenderer with a LinkAction to use with - * target values and the type of values the action can cope with.

                    - * - * It's up to developers to take care of matching types. - * - * @param linkAction the action that acts on values. - * @param targetClass the type of values the action can handle. - */ - public HyperlinkProvider(AbstractHyperlinkAction linkAction, Class targetClass) { - super(); -// rendererComponent.addActionListener(createEditorActionListener()); - setLinkAction(linkAction, targetClass); - } - - /** - * Sets the class the action is supposed to handle.

                    - * - * PENDING: make sense to set independently of LinkAction? - * - * @param targetClass the type of values the action can handle. - */ - public void setTargetClass(Class targetClass) { - this.targetClass = targetClass; - } - - /** - * Sets the LinkAction for handling the values.

                    - * - * The action is assumed to be able to cope with any type, that is - * this method is equivalent to setLinkAction(linkAction, null). - * - * @param linkAction - */ - public void setLinkAction(AbstractHyperlinkAction linkAction) { - setLinkAction(linkAction, null); - } - - /** - * Sets the LinkAction for handling the values and the - * class the action can handle.

                    - * - * PENDING: in the general case this is not independent of the - * targetClass. Need api to set them combined? - * - * @param linkAction - */ - public void setLinkAction(AbstractHyperlinkAction linkAction, Class targetClass) { - if (linkAction == null) { - linkAction = createDefaultLinkAction(); - } - setTargetClass(targetClass); - this.linkAction = linkAction; - rendererComponent.setAction(linkAction); - - } - /** - * decides if the given target is acceptable for setTarget. - *

                    - * - * target == null is acceptable for all types. - * targetClass == null is the same as Object.class - * - * @param target the target to set. - * @return true if setTarget can cope with the object, - * false otherwise. - * - */ - public boolean isTargetable(Object target) { - // we accept everything - if (targetClass == null) return true; - if (target == null) return true; - return targetClass.isAssignableFrom(target.getClass()); - } - - - - /** - * default action - does nothing... except showing the target. - * - * @return a default LinkAction for showing the target. - */ - protected AbstractHyperlinkAction createDefaultLinkAction() { - return new AbstractHyperlinkAction(null) { - - @Override - public void actionPerformed(ActionEvent e) { - // TODO Auto-generated method stub - - } - - }; - } - -//----------------------- Implement RolloverRenderer - - @Override - public boolean isEnabled() { - return true; - } - - @Override - public void doClick() { - rendererComponent.doClick(); - } - -//------------------------ ComponentProvider - - /** - * {@inheritDoc}

                    - * - * PENDING JW: Needs to be overridden - doesn't comply to contract!. Not sure - * how to do it without disturbing the hyperlinks current setting? - * All hyperlink properties are defined by the LinkAction configured - * with the target ... - */ - @Override - public String getString(Object value) { - if (isTargetable(value)) { - Object oldTarget = linkAction.getTarget(); - linkAction.setTarget(value); - String text = linkAction.getName(); - linkAction.setTarget(oldTarget); - return text; - } - return super.getString(value); - } - - - - /** - * {@inheritDoc}

                    - * - * Overridden to set the hyperlink's rollover state. - */ - @Override - protected void configureState(CellContext context) { -// rendererComponent.setHorizontalAlignment(getHorizontalAlignment()); - if (context.getComponent() != null) { - Point p = (Point) context.getComponent() - .getClientProperty(RolloverProducer.ROLLOVER_KEY); - if (/*hasFocus || */(p != null && (p.x >= 0) && - (p.x == context.getColumn()) && (p.y == context.getRow()))) { - if (!rendererComponent.getModel().isRollover()) - rendererComponent.getModel().setRollover(true); - } else { - if (rendererComponent.getModel().isRollover()) - rendererComponent.getModel().setRollover(false); - } - } - } - - /** - * {@inheritDoc} - * - * Overridden to set the LinkAction's target to the context's value, if - * targetable.

                    - * - * Forces foreground color to the one defined by hyperlink for unselected - * cells, doesn't change the foreground for selected (as darkish text on dark selection - * background might be unreadable, Issue #840-swingx). Not entirely safe because - * the unselected background might be dark as well. Need to find a better way in - * the long run. Until then, client code can use Highlighters to repair - * (which is nasty!).

                    - * - * PENDING JW: by-passes XXValues - state currently is completely defined by - * the action. Hmm ... - * - */ - @Override - protected void format(CellContext context) { - Object value = context.getValue(); - if (isTargetable(value)) { - linkAction.setTarget(value); - } else { - linkAction.setTarget(null); - } - // hmm... the hyperlink should do this automatically.. - // Issue #840-swingx: hyperlink unreadable if selected (for dark selection colors) - // so we only force clicked/unclicked if unselected - if (!context.isSelected()) { - rendererComponent.setForeground(linkAction.isVisited() ? - rendererComponent.getClickedColor() : rendererComponent.getUnclickedColor()); - } else { - // JW: workaround #845-swingx which was introduced by fixing #840 - // if we interfere with the colors, need to do always. Not quite understood - rendererComponent.setForeground(context.getSelectionForeground()); - } - } - - /** - * {@inheritDoc} - */ - @Override - protected JXHyperlink createRendererComponent() { - return new JXRendererHyperlink(); - } - - -} diff --git a/src/main/java/org/jdesktop/swingx/renderer/IconAware.java b/src/main/java/org/jdesktop/swingx/renderer/IconAware.java deleted file mode 100644 index a4be33152d..0000000000 --- a/src/main/java/org/jdesktop/swingx/renderer/IconAware.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Created on 05.11.2010 - * - */ -package org.jdesktop.swingx.renderer; - -import javax.swing.*; - -/** - * Interface for tagging rendering components to allow Highlighters to treat - * the Icon (Issue #1311-swingx) as a visual decoration. ComponentProviders - * which hand out IconAware rendering components must guarantee to reset its - * Icon property in each configuration round. - * - * @author Jeanette Winzenburg, Berlin - */ -public interface IconAware { - - /** - * Sets the icon property. - * - * @param icon - */ - public void setIcon(Icon icon); - - /** - * Returns the icon property. - * - * @return - */ - public Icon getIcon(); - -} diff --git a/src/main/java/org/jdesktop/swingx/renderer/IconValue.java b/src/main/java/org/jdesktop/swingx/renderer/IconValue.java deleted file mode 100644 index 80cf05abe6..0000000000 --- a/src/main/java/org/jdesktop/swingx/renderer/IconValue.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * $Id: IconValue.java 3298 2009-03-11 13:51:25Z kleopatra $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.renderer; - -import org.jdesktop.swingx.icon.EmptyIcon; - -import javax.swing.*; -import java.io.Serializable; - -/** - * A simple converter to return a Icon representation of an Object. - *

                    - * - * This class is intended to be the "small coin" to configure/format icon cell - * content of concrete subclasses of ComponentProvider. - *

                    - * - * - * NOTE: this is experimental, most probably will change. A (near) future - * version with change the signature of the getIcon method to - * - *

                    
                    - * Icon getIcon(Object value, IconType type);
                    - * 
                    - * - * That will allow a more fine-grained control of custom icons in tree rendering. - * - * @author Jeanette Winzenburg - */ -public interface IconValue extends Serializable { - - /** - * The cell type the icon is used for. - */ - public enum IconType { - - LEAF, - - OPEN_FOLDER, - - CLOSED_FOLDER - - } - - /** - * A marker icon used to indicate a null. - * - */ - public final static Icon NULL_ICON = new EmptyIcon(); - - - /** - * Returns a icon representation of the given value. - * - * @param value the object to present as Icon - * @return a Icon representation of the given value, - * may be null if none available. - * - */ - Icon getIcon(Object value); - -} diff --git a/src/main/java/org/jdesktop/swingx/renderer/IconValues.java b/src/main/java/org/jdesktop/swingx/renderer/IconValues.java deleted file mode 100644 index 359c5fa423..0000000000 --- a/src/main/java/org/jdesktop/swingx/renderer/IconValues.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * $Id: IconValues.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.renderer; - -import javax.swing.*; -import javax.swing.filechooser.FileSystemView; -import java.io.File; - -/** - * A collection of common {@code IconValue} implementations. - * - * @author Karl George Schaefer - * @author Jeanette Winzenburg - */ -public final class IconValues { - /** - * Always NULL_ICON. This is useful to indicate that we really want - * no icon instead of f.i. a default provided by the CellContext. - */ - @SuppressWarnings("serial") - public static final IconValue NONE = new IconValue() { - - @Override - public Icon getIcon(Object value) { - return IconValue.NULL_ICON; - } - - }; - - /** - * Returns the value as Icon if possible or null. - */ - @SuppressWarnings("serial") - public static final IconValue ICON = new IconValue() { - - @Override - public Icon getIcon(Object value) { - if (value instanceof Icon) { - return (Icon) value; - } - return null; - } - }; - - /** - * An {@code IconValue} that presents the current L&F icon for a given file. - * If the value passed to {@code FILE_ICON} is not a {@link File}, this has - * the same effect as {@link IconValues#NONE}. - */ - @SuppressWarnings("serial") - public static final IconValue FILE_ICON = new IconValue() { - @Override - public Icon getIcon(Object value) { - if (value instanceof File) { - FileSystemView fsv = FileSystemView.getFileSystemView(); - - return fsv.getSystemIcon((File) value); - } - - return IconValues.NONE.getIcon(value); - } - }; - - private IconValues() { - // does nothing - } -} diff --git a/src/main/java/org/jdesktop/swingx/renderer/JRendererCheckBox.java b/src/main/java/org/jdesktop/swingx/renderer/JRendererCheckBox.java deleted file mode 100644 index 1f89360fb1..0000000000 --- a/src/main/java/org/jdesktop/swingx/renderer/JRendererCheckBox.java +++ /dev/null @@ -1,311 +0,0 @@ -/* - * $Id: JRendererCheckBox.java 4282 2013-02-22 11:55:40Z Kleopatra $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.renderer; - -import org.jdesktop.swingx.painter.Painter; - -import javax.swing.*; -import java.awt.*; - -/** - * A JCheckBox optimized for usage in renderers and - * with a minimal background painter support.

                    - * - * Note: As of revision #4223, there's a complete overhaul (aka: changed the tricksery) to - * fix Issue swingx-1513 (allow client code to set renderer transparent) while keeping - * fix Issue swingx-897 (striping/background lost when painter installed) - *

                    - * - * Note: The change of logic _did_ introduce a regression (swingx-1546) - * which was fixed by forcing the box's opacity to true (for regression release - * 1.6.5-1). Further improvements (like f.i. the option to delegate to the ui's - * update - to allow LAF installed painters - instead of paint) are deferred - * to a later normal release, more discussions needed. - *

                    - * - * @author Jeanette Winzenburg - * - * @see #paintComponent(Graphics) - */ -@SuppressWarnings({ "rawtypes", "unchecked" }) -public class JRendererCheckBox extends JCheckBox implements PainterAware { - /** the swingx painter */ - protected Painter painter; - /** a flag to prevent ui painting from filling the background. */ - private boolean fakeTransparency; - - /** - * Instantiates a JRendererCheckBox with opacity true. - */ - public JRendererCheckBox() { - super(); - // fix # 1546-swingx: striping lost in synth-based lafs - // forcing opaque to enable painting the background - setOpaque(true); - } - /** - * {@inheritDoc} - */ - @Override - public Painter getPainter() { - return painter; - } - - /** - * {@inheritDoc} - */ - @Override - public void setPainter(Painter painter) { - Painter old = getPainter(); - this.painter = painter; - firePropertyChange("painter", old, getPainter()); - } - - /** - * {@inheritDoc}

                    - * - * Overridden to return false if painting flag is true.

                    - * - */ - @Override - public boolean isOpaque() { - if (fakeTransparency) { - return false; - } - return super.isOpaque(); - } - - /** - * {@inheritDoc}

                    - * - * Overridden to return false if painting flag is true.

                    - * - */ - @Override - public boolean isContentAreaFilled() { - if (fakeTransparency) { - return false; - } - return super.isContentAreaFilled(); - } - - /** - * {@inheritDoc}

                    - * - * Overridden to not automatically de/register itself from/to the ToolTipManager. - * As rendering component it is not considered to be active in any way, so the - * manager must not listen. - */ - @Override - public void setToolTipText(String text) { - putClientProperty(TOOL_TIP_TEXT_KEY, text); - } - - /** - * Overridden to snatch painting from super if a painter installed or Nimbus - * detected.

                    - * - * The overall logic currently (since 1.6.5) is to simply call super without SwingX - * painter. Otherwise, that is with SwingX painter: - *

                      - *
                    1. if opaque - *
                        - *
                      1. set a flag which fakes transparency, that is both - * contentAreaFilled and - * opaque return false - *
                      2. fill background with the component's background color - *
                      3. apply swingx painter - *
                      4. hook into ui.paint(...) - *
                      5. reset the flag - *
                      - *
                    2. else - *
                        apply swingx painter - *
                          call super - *
                        1. - *
                            - *
                          - * - * Note that Nimbus is special cased (mainly due to its bug of - * even row striping instead of odd) - * and handled as if a SwingX painter were set. - * - */ - @Override - protected void paintComponent(Graphics g) { - // JW: hack around for #1178-swingx (core issue) - // grab painting if Nimbus detected - if ((painter != null) || isNimbus()) { - // we have a custom (background) painter - // try to inject if possible - // there's no guarantee - some LFs have their own background - // handling elsewhere - if (isOpaque()) { - // replace the paintComponent completely - fakeTransparency = true; - paintComponentWithPainter((Graphics2D) g); - fakeTransparency = false; - } else { - // transparent apply the background painter before calling super - paintPainter(g); - super.paintComponent(g); - } - } else { - // nothing to worry about - delegate to super - super.paintComponent(g); - } - } - - /** - * Hack around Nimbus not respecting background colors if UIResource. - * So by-pass ... - * - * @return - */ - private boolean isNimbus() { - return UIManager.getLookAndFeel().getName().contains("Nimbus"); - } - - - /** - * - * Hack around AbstractPainter.paint bug which disposes the Graphics. - * So here we give it a scratch to paint on.

                          - * TODO - remove again, the issue is fixed? - * - * @param g the graphics to paint on - */ - private void paintPainter(Graphics g) { - if (painter == null) return; - // fail fast: we assume that g must not be null - // which throws an NPE here instead deeper down the bowels - // this differs from corresponding core implementation! - Graphics2D scratch = (Graphics2D) g.create(); - try { - painter.paint(scratch, this, getWidth(), getHeight()); - } - finally { - scratch.dispose(); - } - } - - /** - * - * @param g - */ - protected void paintComponentWithPainter(Graphics2D g) { - // 1. be sure to fill the background - // 2. paint the painter - // by-pass ui.update and hook into ui.paint directly - if (ui != null) { - // fail fast: we assume that g must not be null - // which throws an NPE here instead deeper down the bowels - // this differs from corresponding core implementation! - Graphics scratchGraphics = g.create(); - try { - scratchGraphics.setColor(getBackground()); - scratchGraphics.fillRect(0, 0, getWidth(), getHeight()); - paintPainter(g); - ui.paint(scratchGraphics, this); -// super.paintComponent(g); - } finally { - scratchGraphics.dispose(); - } - } - - } - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - * - * @since 1.5 - */ - @Override - public void invalidate() {} - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void validate() {} - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void revalidate() {} - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void repaint(long tm, int x, int y, int width, int height) {} - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void repaint(Rectangle r) { } - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - * - * @since 1.5 - */ - @Override - public void repaint() { - } - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { - // Strings get interned... - if ("text".equals(propertyName)) { - super.firePropertyChange(propertyName, oldValue, newValue); - } - } - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { } - - - -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/renderer/JRendererLabel.java b/src/main/java/org/jdesktop/swingx/renderer/JRendererLabel.java deleted file mode 100644 index ea409602c8..0000000000 --- a/src/main/java/org/jdesktop/swingx/renderer/JRendererLabel.java +++ /dev/null @@ -1,299 +0,0 @@ -/* - * $Id: JRendererLabel.java 4222 2012-08-07 10:23:08Z kleopatra $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.renderer; - -import org.jdesktop.swingx.painter.Painter; - -import javax.swing.*; -import java.awt.*; - -/** - * A JLabel optimized for usage in renderers and - * with a minimal background painter support.

                          - * - * Note: the painter support will be switched to painter_work as - * soon it enters main. - * - * The reasoning for the performance-overrides is copied from core:

                          - * - * The standard JLabel component was not - * designed to be used this way and we want to avoid - * triggering a revalidate each time the - * cell is drawn. This would greatly decrease performance because the - * revalidate message would be - * passed up the hierarchy of the container to determine whether any other - * components would be affected. - * As the renderer is only parented for the lifetime of a painting operation - * we similarly want to avoid the overhead associated with walking the - * hierarchy for painting operations. - * So this class - * overrides the validate, invalidate, - * revalidate, repaint, and - * firePropertyChange methods to be - * no-ops and override the isOpaque method solely to improve - * performance. If you write your own renderer component, - * please keep this performance consideration in mind. - *

                          - * - * @author Jeanette Winzenburg - */ -public class JRendererLabel extends JLabel implements PainterAware, IconAware { - - protected Painter painter; - - /** - * - */ - public JRendererLabel() { - super(); - setOpaque(true); - } - -// /** -// * Overridden for performance reasons.

                          -// * PENDING: Think about Painters and opaqueness? -// * -// */ -// @Override -// public boolean isOpaque() { -// Color back = getBackground(); -// Component p = getParent(); -// if (p != null) { -// p = p.getParent(); -// } -// // p should now be the JTable. -// boolean colorMatch = (back != null) && (p != null) && -// back.equals(p.getBackground()) && -// p.isOpaque(); -// return !colorMatch && super.isOpaque(); -// // PENDING JW: Issue #1188-swingx: problems with background in Synth -// // basically a core issue - nevertheless, evaluate implications of -// // a simple straight-forward implemenation - return the property -// // no tricks -//// return super.isOpaque(); -// } - - /** - * {@inheritDoc} - */ - public void setPainter(Painter painter) { - Painter old = getPainter(); - this.painter = painter; - firePropertyChange("painter", old, getPainter()); - } - - /** - * {@inheritDoc} - */ - public Painter getPainter() { - return painter; - } - /** - * {@inheritDoc}

                          - * - * Overridden to inject Painter's painting.

                          - * TODO: cleanup logic - see JRendererCheckBox. - * - */ - @Override - protected void paintComponent(Graphics g) { - // JW: hack around for #1178-swingx (core issue) - // grab painting if Nimbus detected - if ((painter != null) || isNimbus()) { - // we have a custom (background) painter - // try to inject if possible - // there's no guarantee - some LFs have their own background - // handling elsewhere - if (isOpaque()) { - // replace the paintComponent completely - paintComponentWithPainter((Graphics2D) g); - } else { - // transparent apply the background painter before calling super - paintPainter(g); - super.paintComponent(g); - } - } else { - // nothing to worry about - delegate to super - super.paintComponent(g); - } - } - - /** - * Hack around Nimbus not respecting background colors if UIResource. - * So by-pass ... - * - * @return - */ - private boolean isNimbus() { - return UIManager.getLookAndFeel().getName().contains("Nimbus"); - } - - - /** - * - * Hack around AbstractPainter.paint bug which disposes the Graphics. - * So here we give it a scratch to paint on.

                          - * TODO - remove again, the issue is fixed? - * - * @param g the graphics to paint on - */ - private void paintPainter(Graphics g) { - if (painter == null) return; - // fail fast: we assume that g must not be null - // which throws an NPE here instead deeper down the bowels - // this differs from corresponding core implementation! - Graphics2D scratch = (Graphics2D) g.create(); - try { - painter.paint(scratch, this, getWidth(), getHeight()); - } - finally { - scratch.dispose(); - } - } - -// public void setStrictWidth(boolean strict) { -// this.strict = strict; -// } -// -// @Override -// public Dimension getMaximumSize() { -// if (strict) { -// return super.getMaximumSize(); -// } -// Dimension max = super.getMaximumSize(); -// max.width = Integer.MAX_VALUE - 1; -// return max; -// } - - /** - * PRE: painter != null, isOpaque() - * @param g - */ - protected void paintComponentWithPainter(Graphics2D g) { - // 1. be sure to fill the background - // 2. paint the painter - // by-pass ui.update and hook into ui.paint directly - if (ui != null) { - // fail fast: we assume that g must not be null - // which throws an NPE here instead deeper down the bowels - // this differs from corresponding core implementation! - Graphics2D scratchGraphics = (Graphics2D) g.create(); - try { - scratchGraphics.setColor(getBackground()); - scratchGraphics.fillRect(0, 0, getWidth(), getHeight()); - paintPainter(g); - ui.paint(scratchGraphics, this); - } - finally { - scratchGraphics.dispose(); - } - } - } - - - /** - * {@inheritDoc}

                          - * - * Overridden to not automatically de/register itself from/to the ToolTipManager. - * As rendering component it is not considered to be active in any way, so the - * manager must not listen. - */ - @Override - public void setToolTipText(String text) { - putClientProperty(TOOL_TIP_TEXT_KEY, text); - } - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - * - * @since 1.5 - */ - @Override - public void invalidate() {} - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void validate() {} - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void revalidate() {} - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void repaint(long tm, int x, int y, int width, int height) {} - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void repaint(Rectangle r) { } - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - * - * @since 1.5 - */ - @Override - public void repaint() { - } - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { - // Strings get interned... - if ("text".equals(propertyName)) { - super.firePropertyChange(propertyName, oldValue, newValue); - } - } - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { } - - -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/renderer/JRendererPanel.java b/src/main/java/org/jdesktop/swingx/renderer/JRendererPanel.java deleted file mode 100644 index 0107ea8f11..0000000000 --- a/src/main/java/org/jdesktop/swingx/renderer/JRendererPanel.java +++ /dev/null @@ -1,85 +0,0 @@ -package org.jdesktop.swingx.renderer; - -import javax.swing.*; -import java.awt.*; - -/** - * An implementation of JPanel used for rendering. It overrides methods for performance reasons. - * - * @author kschaefer - */ -public class JRendererPanel extends JPanel { - public JRendererPanel() { - super(); - } - - /** - * @param layout - */ - public JRendererPanel(LayoutManager layout) { - super(layout); - } - - /** - * {@inheritDoc}

                          - * - * Overridden to not automatically de/register itself from/to the ToolTipManager. - * As rendering component it is not considered to be active in any way, so the - * manager must not listen. - */ - @Override - public void setToolTipText(String text) { - putClientProperty(TOOL_TIP_TEXT_KEY, text); - } - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void revalidate() {} - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void repaint(long tm, int x, int y, int width, int height) {} - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void repaint(Rectangle r) { } - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - * - * @since 1.5 - */ - @Override - public void repaint() { - } - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { } - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { } -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/renderer/JXRendererHyperlink.java b/src/main/java/org/jdesktop/swingx/renderer/JXRendererHyperlink.java deleted file mode 100644 index b018f07819..0000000000 --- a/src/main/java/org/jdesktop/swingx/renderer/JXRendererHyperlink.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * $Id: JXRendererHyperlink.java 3235 2009-02-01 15:01:07Z rah003 $ - * - * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.renderer; - -import org.jdesktop.swingx.JXHyperlink; -import org.jdesktop.swingx.painter.Painter; - -import java.awt.*; - -/** - * A JXHyperlink optimized for usage in renderers and - * with a minimal background painter support.

                          - * - * Note: the painter support will be switched to painter_work as - * soon it enters main. - * - * @author Jeanette Winzenburg - */ -public class JXRendererHyperlink extends JXHyperlink implements PainterAware { - protected Painter painter; - - /** - * {@inheritDoc} - */ - public void setPainter(Painter painter) { - Painter old = getPainter(); - this.painter = painter; - if (painter != null) { - // ui maps to !opaque - // Note: this is incomplete - need to keep track of the - // "real" contentfilled property - setContentAreaFilled(false); - } - firePropertyChange("painter", old, getPainter()); - } - - /** - * {@inheritDoc} - */ - public Painter getPainter() { - return painter; - } - - @Override - protected void paintComponent(Graphics g) { - if (painter != null) { - // we have a custom (background) painter - // try to inject if possible - // there's no guarantee - some LFs have their own background - // handling elsewhere - paintComponentWithPainter((Graphics2D) g); - } else { - // no painter - delegate to super - super.paintComponent(g); - } - } - - /** - * - * Hack around AbstractPainter.paint bug which disposes the Graphics. - * So here we give it a scratch to paint on.

                          - * TODO - remove again, the issue is fixed? - * - * @param g the graphics to paint on - */ - private void paintPainter(Graphics g) { - // fail fast: we assume that g must not be null - // which throws an NPE here instead deeper down the bowels - // this differs from corresponding core implementation! - Graphics2D scratch = (Graphics2D) g.create(); - try { - painter.paint(scratch, this, getWidth(), getHeight()); - } - finally { - scratch.dispose(); - } - } - - /** - * PRE: painter != null - * @param g - */ - protected void paintComponentWithPainter(Graphics2D g) { - // 1. be sure to fill the background - // 2. paint the painter - // by-pass ui.update and hook into ui.paint directly - if (ui != null) { - // fail fast: we assume that g must not be null - // which throws an NPE here instead deeper down the bowels - // this differs from corresponding core implementation! - Graphics scratchGraphics = g.create(); - try { - scratchGraphics.setColor(getBackground()); - scratchGraphics.fillRect(0, 0, getWidth(), getHeight()); - paintPainter(g); - ui.paint(scratchGraphics, this); - } finally { - scratchGraphics.dispose(); - } - } - - } - - @Override - public void updateUI() { - super.updateUI(); - setBorderPainted(true); - setOpaque(true); - } - /** - * {@inheritDoc}

                          - * - * Overridden to not automatically de/register itself from/to the ToolTipManager. - * As rendering component it is not considered to be active in any way, so the - * manager must not listen. - */ - @Override - public void setToolTipText(String text) { - putClientProperty(TOOL_TIP_TEXT_KEY, text); - } - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - * - * @since 1.5 - */ - @Override - public void invalidate() {} - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void validate() {} - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void revalidate() {} - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void repaint(long tm, int x, int y, int width, int height) {} - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void repaint(Rectangle r) { } - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - * - * @since 1.5 - */ - @Override - public void repaint() { - } - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { - // Strings get interned... - if ("text".equals(propertyName)) { - super.firePropertyChange(propertyName, oldValue, newValue); - } - } - - /** - * Overridden for performance reasons. - * See the Implementation Note - * for more information. - */ - @Override - public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { } - -} diff --git a/src/main/java/org/jdesktop/swingx/renderer/LabelProvider.java b/src/main/java/org/jdesktop/swingx/renderer/LabelProvider.java deleted file mode 100644 index f84c019f64..0000000000 --- a/src/main/java/org/jdesktop/swingx/renderer/LabelProvider.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * $Id: LabelProvider.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.renderer; - -import javax.swing.*; - -/** - * A component provider which uses a JLabel as rendering - * component.

                          - * - * It configures the Label's text and icon property from the - * StringValue. - * - * @author Jeanette Winzenburg - * - * @see StringValue - * @see FormatStringValue - * @see IconValue - */ -public class LabelProvider extends ComponentProvider { - - /** - * Instantiates a LabelProvider with default to-String converter and LEADING - * horizontal alignment . - *

                          - * - */ - public LabelProvider() { - this(null); - } - - /** - * Instantiates a LabelProvider with the given to-String converter and LEADING - * horizontal alignment. If the converter is null, the default TO_STRING is - * used. - *

                          - * - * @param converter the converter to use for mapping the cell value to a - * String representation. - */ - public LabelProvider(StringValue converter) { - this(converter, JLabel.LEADING); - } - - /** - * Instantiates a LabelProvider with default to-String converter and given - * horizontal alignment. - * - * @param alignment the horizontal alignment. - */ - public LabelProvider(int alignment) { - this(null, alignment); - } - - /** - * Instantiates a LabelProvider with given to-String converter and given - * horizontal alignment. If the converter is null, the default TO_STRING is - * used. - * - * @param converter the converter to use for mapping the cell value to a - * String representation. - * @param alignment the horizontal alignment. - */ - public LabelProvider(StringValue converter, int alignment) { - super(converter, alignment); - } - - /** - * {@inheritDoc} - */ - @Override - protected JLabel createRendererComponent() { - return new JRendererLabel(); - } - - /** - * {@inheritDoc} - * Here: sets the Label's horizontal alignment to the alignment as configured - * in the controller. - */ - @Override - protected void configureState(CellContext context) { - rendererComponent.setHorizontalAlignment(getHorizontalAlignment()); - } - - /** - * {@inheritDoc} - * Here: sets the labels's text and icon property to the value as - * returned by getValueAsString/Icon, respectively. - * - * @param context the cellContext to use - * - * @see #getValueAsString(CellContext) - * @see #getValueAsIcon(CellContext) - */ - @Override - protected void format(CellContext context) { - rendererComponent.setIcon(getValueAsIcon(context)); - rendererComponent.setText(getValueAsString(context)); - } - - -} diff --git a/src/main/java/org/jdesktop/swingx/renderer/ListCellContext.java b/src/main/java/org/jdesktop/swingx/renderer/ListCellContext.java deleted file mode 100644 index 30442cdfa3..0000000000 --- a/src/main/java/org/jdesktop/swingx/renderer/ListCellContext.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * $Id: ListCellContext.java 3424 2009-07-30 10:53:39Z kleopatra $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.renderer; - -import javax.swing.*; -import java.awt.*; - -/** - * List specific CellContext. - */ -public class ListCellContext extends CellContext { - - /** - * Sets state of the cell's context. Note that the component might be null - * to indicate a cell without a concrete context. All accessors must cope - * with. - * - * @param component the component the cell resides on, might be null - * @param value the content value of the cell - * @param row the cell's row index in view coordinates - * @param column the cell's column index in view coordinates - * @param selected the cell's selected state - * @param focused the cell's focused state - * @param expanded the cell's expanded state - * @param leaf the cell's leaf state - */ - public void installContext(JList component, Object value, int row, int column, - boolean selected, boolean focused, boolean expanded, boolean leaf) { - this.component = component; - installState(value, row, column, selected, focused, expanded, leaf); - this.dropOn = checkDropOnState(); - } - - /** - * - */ - private boolean checkDropOnState() { - if ((getComponent() == null)) { - return false; - } - JList.DropLocation dropLocation = getComponent().getDropLocation(); - if (dropLocation != null - && !dropLocation.isInsert() - && dropLocation.getIndex() == row) { - return true; - } - return false; - } - - - @Override - public JList getComponent() { - return (JList) super.getComponent(); - } - - /** - * {@inheritDoc} - */ - @Override - protected Color getSelectionBackground() { - Color selection = null; - if (isDropOn()) { - selection = getDropCellBackground(); - if (selection != null) return selection; - } - return getComponent() != null ? getComponent().getSelectionBackground() : null; - } - - /** - * {@inheritDoc} - */ - @Override - protected Color getSelectionForeground() { - Color selection = null; - if (isDropOn()) { - selection = getDropCellForeground(); - if (selection != null) return selection; - } - return getComponent() != null ? getComponent().getSelectionForeground() : null; - } - - /** - * {@inheritDoc} - */ - @Override - protected String getUIPrefix() { - return "List."; - } - - - -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/renderer/LocalizableStringValue.java b/src/main/java/org/jdesktop/swingx/renderer/LocalizableStringValue.java deleted file mode 100644 index ec849b84a6..0000000000 --- a/src/main/java/org/jdesktop/swingx/renderer/LocalizableStringValue.java +++ /dev/null @@ -1,119 +0,0 @@ -package org.jdesktop.swingx.renderer; - -import org.jdesktop.swingx.plaf.UIManagerExt; -import org.jdesktop.swingx.util.Contract; - -import java.util.Locale; -import java.util.Map; - -/** - * A StringValue which looks up localized String representations for objects. - */ -public class LocalizableStringValue implements StringValue { - - private Map lookup; - - private Locale locale; - - private String prefix; - - /** - * Instantiates a LocaleStringValue which looks up localized String - * representations for items in the map using the JComponent defaultLocale. - * - * @param lookup a map containing Entries of objects and a string key to - * look up its string representation in the UIManager - */ - public LocalizableStringValue(Map lookup) { - this(lookup, null, null); - } - - /** - * Instantiates a LocaleStringValue which looks up localized String - * representations for items in the map using the given Locale. - * - * @param lookup a map containing Entries of objects and a string key to - * look up its string representation in the UIManager - * @param locale the locale to lookup the localized strings, may be null to - * denote using JComponent.defaultLocale - */ - public LocalizableStringValue(Map lookup, Locale locale) { - this(lookup, null, locale); - } - - /** - * Instantiates a LocaleStringValue which looks up localized String - * representations for items in the map using the JComponent defaultLocale. - * - * @param lookup a map containing Entries of objects and a string key to - * look up its string representation in the UIManager - * @param prefix a common prefix for all string keys in the map, may be null - * to denote that the keys should be use as are - */ - public LocalizableStringValue(Map lookup, String prefix) { - this(lookup, prefix, null); - } - - /** - * Instantiates a LocaleStringValue which looks up localized String - * representations for items in the map using the given Locale. - * - * @param lookup a map containing Entries of objects and a string key to - * look up its string representation in the UIManager - * @param prefix a common prefix for all string keys in the map, may be null - * to denote that the keys should be use as are - * @param locale the locale to lookup the localized strings, may be null to - * denote using JComponent.defaultLocale - */ - public LocalizableStringValue(Map lookup, String prefix, - Locale locale) { - this.lookup = Contract.asNotNull(lookup, "map must not be null"); - this.prefix = prefix; - setLocale(locale); - } - - /** - * - * @inherited

                          - * - * Implemented to lookup the value's localized string - * representation, if contained in the lookup map. Returns - * toString if not. - * - */ - @Override - public String getString(Object value) { - String key = lookup.get(value); - if (key != null) { - if (prefix != null) { - key = prefix + key; - } - String text = UIManagerExt.getString(key, getLocale()); - if (text != null) - return text; - } - return StringValues.TO_STRING_UI.getString(value); - } - - // -------------------- implement Localizable - - /** - * Sets the Locale to use for lookup of localized string representation. - * - * @param locale the locale to lookup the localized strings, may be null to - * denote using Locale's default. - */ - public final void setLocale(Locale locale) { - this.locale = locale; - } - - /** - * Returns the Locale to use for lookup, guaranteed to be not null. If - * the initial setting had been null, returns current Locale's default. - * - * @return the Locale used for lookup. - */ - public Locale getLocale() { - return locale != null ? locale : Locale.getDefault(); - } -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/renderer/MappedValue.java b/src/main/java/org/jdesktop/swingx/renderer/MappedValue.java deleted file mode 100644 index 8287decb71..0000000000 --- a/src/main/java/org/jdesktop/swingx/renderer/MappedValue.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * $Id: MappedValue.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.renderer; - -import javax.swing.*; - -/** - * Compound implementation of XXValue. Currently, XX stands for String, - * Icon, Boolean.

                          - * - * Quick hack around #590-swingx: LabelProvider should respect StringValue - * when formatting (instead of going clever with icons). - * - * Note: this will change! - * - * @see CheckBoxProvider - */ -public class MappedValue implements StringValue, IconValue, BooleanValue { - - private StringValue stringDelegate; - private IconValue iconDelegate; - private BooleanValue booleanDelegate; - - public MappedValue(StringValue stringDelegate, IconValue iconDelegate) { - this(stringDelegate, iconDelegate, null); - } - - public MappedValue(StringValue stringDelegate, IconValue iconDelegate, - BooleanValue booleanDelegate) { - this.stringDelegate = stringDelegate; - this.iconDelegate = iconDelegate; - this.booleanDelegate = booleanDelegate; - } - - /** - * {@inheritDoc}

                          - * - * This implementation delegates to the contained StringValue if available or - * returns an empty String, if not. - * - */ - @Override - public String getString(Object value) { - if (stringDelegate != null) { - return stringDelegate.getString(value); - } - return ""; - } - - /** - * {@inheritDoc}

                          - * - * This implementation delegates to the contained IconValue if available or - * returns null, if not. - * - */ - @Override - public Icon getIcon(Object value) { - if (iconDelegate != null) { - return iconDelegate.getIcon(value); - } - return null; - } - - /** - * {@inheritDoc}

                          - * - * This implementation delegates to the contained BooleanValue if available or - * returns false, if not. - * - */ - @Override - public boolean getBoolean(Object value) { - if (booleanDelegate != null) { - return booleanDelegate.getBoolean(value); - } - return false; - } - -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/renderer/MappedValues.java b/src/main/java/org/jdesktop/swingx/renderer/MappedValues.java deleted file mode 100644 index 659edd608c..0000000000 --- a/src/main/java/org/jdesktop/swingx/renderer/MappedValues.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * $Id: MappedValues.java 3882 2010-11-05 09:24:37Z kleopatra $ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.renderer; - -import javax.swing.*; -import javax.swing.plaf.UIResource; - -/** - * A collection of common {@code MappedValue} implementations. - * - * @author kschaefer - */ -public final class MappedValues { - /** - * A {@code MappedValue} that returns either a {@code String} or {@code Icon}, but not both. - */ - @SuppressWarnings("serial") - public static final MappedValue STRING_OR_ICON_ONLY = new MappedValue(new StringValue() { - @Override - public String getString(Object value) { - if (value instanceof Icon) { - return StringValues.EMPTY.getString(value); - } - - return StringValues.TO_STRING.getString(value); - } - }, IconValues.ICON); - - /** - * MappedValue wrapper of type UIResource to tag LAF installed converters. - * - * @author (Jeanette Winzenburg, Berlin - */ - public static class MappedValueUIResource extends MappedValue implements UIResource { - - public MappedValueUIResource(MappedValue delegate) { - this(delegate, delegate, delegate); - } - - /** - * @param stringDelegate - * @param iconDelegate - * @param booleanDelegate - */ - public MappedValueUIResource(StringValue stringDelegate, - IconValue iconDelegate, BooleanValue booleanDelegate) { - super(stringDelegate, iconDelegate, booleanDelegate); - } - - /** - * @param stringDelegate - * @param iconDelegate - */ - public MappedValueUIResource(StringValue stringDelegate, - IconValue iconDelegate) { - super(stringDelegate, iconDelegate); - } - - } - - private MappedValues() { - //prevent instantiation - } -} diff --git a/src/main/java/org/jdesktop/swingx/renderer/PainterAware.java b/src/main/java/org/jdesktop/swingx/renderer/PainterAware.java deleted file mode 100644 index d36f7cff37..0000000000 --- a/src/main/java/org/jdesktop/swingx/renderer/PainterAware.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * $Id: PainterAware.java 3472 2009-08-27 13:12:42Z kleopatra $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.renderer; - -import org.jdesktop.swingx.painter.Painter; - -/** - * Temporary hook to allow painters in rendering.

                          - * - * NOTE: this will be removed as soon as the painter_work enters - * main. - * - * @author Jeanette Winzenburg - */ -public interface PainterAware { - void setPainter(Painter painter); - Painter getPainter(); -} diff --git a/src/main/java/org/jdesktop/swingx/renderer/StringValue.java b/src/main/java/org/jdesktop/swingx/renderer/StringValue.java deleted file mode 100644 index 3656bc27bb..0000000000 --- a/src/main/java/org/jdesktop/swingx/renderer/StringValue.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * $Id: StringValue.java 3297 2009-03-11 13:45:10Z kleopatra $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.renderer; - -import java.io.Serializable; - - -/** - * A simple converter to return a String representation of an object. - * - * This class is intended to be the "small coin" to configure/format textual - * cell content of concrete subclasses of ComponentProvider. - *

                          - * - * F.i. to show a Contributor cell object as "Busywoman, Herta" implement a - * custom StringValue and use it in a text rendering provider. - * - *

                          
                          - * StringValue stringValue = new StringValue() {
                          - * 
                          - *     public String getString(Object value) {
                          - *         if (!(value instanceof Contributor))
                          - *             return TO_STRING.getString(value);
                          - *         Contributor contributor = (Contributor) value;
                          - *         return contributor.lastName + ", " + contributor.firstName;
                          - *     }
                          - * 
                          - * };
                          - * 
                          - * ComponentProvider provider = new LabelProvider(stringValue);
                          - * table.setDefaultRenderer(Contributor.class, 
                          - *   new DefaultTableRenderer(provider));
                          - * 
                          - * - *

                          - * - * PENDING: use a full-fledged Format instead? - * Would impose a higher burden onto implementors but could be re-used in - * editors. - * - * @author Jeanette Winzenburg - * - * @see ComponentProvider - * @see LabelProvider - * @see DefaultTableRenderer - * @see DefaultListRenderer - * @see DefaultTreeRenderer - */ -public interface StringValue extends Serializable { - - /** - * Returns a string representation of the given value.

                          - * - * PENDING JW: forgot - why not null return guaranteed? - * - * @param value the object to present as a string - * @return a string representation of the given value, - * guaranteed to be not null - */ - String getString(Object value); -} diff --git a/src/main/java/org/jdesktop/swingx/renderer/StringValues.java b/src/main/java/org/jdesktop/swingx/renderer/StringValues.java deleted file mode 100644 index 4806f9d8cb..0000000000 --- a/src/main/java/org/jdesktop/swingx/renderer/StringValues.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * $Id: StringValues.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.renderer; - -import org.jdesktop.swingx.util.Contract; - -import javax.swing.filechooser.FileSystemView; -import javax.swing.plaf.UIResource; -import java.io.File; -import java.text.DateFormat; -import java.text.NumberFormat; -import java.util.Locale; - - -/** - * A collection of common {@code StringValue} implementations. - * - * @author Karl George Schaefer - * @author Jeanette Winzenburg - */ -public final class StringValues { - /** - * A {@code StringValue} that always presents an empty string. - */ - @SuppressWarnings("serial") - public final static StringValue EMPTY = new StringValue() { - @Override - public String getString(Object value) { - return ""; - } - }; - - /** - * A {@code StringValue} that presents a {@link Object#toString() toString} - * value for the given object. If the value passed is {@code null}, this has - * the same effect as {@link StringValues#EMPTY}. - */ - @SuppressWarnings("serial") - public final static StringValue TO_STRING = new StringValue() { - @Override - public String getString(Object value) { - return (value != null) ? value.toString() : StringValues.EMPTY.getString(value); - } - }; - - /** - * A {@code StringValue} that presents the current L&F display name for a - * given file. If the value passed to {@code FILE_NAME} is not a - * {@link File}, this has the same effect as {@link StringValues#TO_STRING}. - */ - @SuppressWarnings("serial") - public static final StringValue FILE_NAME = new StringValue() { - @Override - public String getString(Object value) { - if (value instanceof File) { - FileSystemView fsv = FileSystemView.getFileSystemView(); - - return fsv.getSystemDisplayName((File) value); - } - - return StringValues.TO_STRING.getString(value); - } - }; - - /** - * A {@code StringValue} that presents the current L&F type name for a - * given file. If the value passed to {@code FILE_TYPE} is not a - * {@link File}, this has the same effect as {@link StringValues#TO_STRING}. - */ - @SuppressWarnings("serial") - public static final StringValue FILE_TYPE = new StringValue() { - @Override - public String getString(Object value) { - if (value instanceof File) { - FileSystemView fsv = FileSystemView.getFileSystemView(); - - return fsv.getSystemTypeDescription((File) value); - } - - return StringValues.TO_STRING.getString(value); - } - }; - - - /** keep track of default locale. */ - private static Locale defaultLocale; - - /** - * Returns a boolean to indicate if the default Locale has changed. - * Updates internal state to keep track of the default Locale. - * - * @return true if the default Locale has changed. - */ - private static boolean localeChanged() { - boolean changed = !Locale.getDefault().equals(defaultLocale); - if (changed) { - defaultLocale = Locale.getDefault(); - } - return changed; - } - - /** - * Default converter for Date types. Uses the default format - * as returned from DateFormat. - */ - @SuppressWarnings("serial") - public final static FormatStringValue DATE_TO_STRING = new FormatStringValue() { - - /** - * {@inheritDoc} - */ - @Override - public String getString(Object value) { - if (format == null || localeChanged()) { - format = DateFormat.getDateInstance(); - } - return super.getString(value); - } - - }; - - /** - * Default converter for Number types. Uses the default format - * as returned from NumberFormat. - */ - @SuppressWarnings("serial") - public final static FormatStringValue NUMBER_TO_STRING = new FormatStringValue() { - - /** - * {@inheritDoc} - */ - @Override - public String getString(Object value) { - if (format == null || localeChanged()) { - format = NumberFormat.getNumberInstance(); - } - return super.getString(value); - } - - }; - - - - public static final StringValue TO_STRING_UI = new StringValueUIResource(StringValues.TO_STRING); - public static final StringValue EMPTY_UI = new StringValueUIResource(StringValues.EMPTY); - - /** - * StringValue wrapper of type UIResource to tag LAF installed converters. - * - * @author Jeanette Winzenburg, Berlin - */ - public static class StringValueUIResource implements StringValue, UIResource { - - private StringValue delegate; - - public StringValueUIResource(StringValue toString) { - Contract.asNotNull(toString, "delegate StringValue must not be null"); - this.delegate = toString; - } - - @Override - public String getString(Object value) { - return delegate.getString(value); - } - - } - - private StringValues() { - // does nothing - } -} diff --git a/src/main/java/org/jdesktop/swingx/renderer/TableCellContext.java b/src/main/java/org/jdesktop/swingx/renderer/TableCellContext.java deleted file mode 100644 index 7bd71ef7aa..0000000000 --- a/src/main/java/org/jdesktop/swingx/renderer/TableCellContext.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * $Id: TableCellContext.java 3783 2010-09-15 13:13:23Z kleopatra $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.renderer; - -import org.jdesktop.swingx.plaf.UIManagerExt; - -import javax.swing.*; -import java.awt.*; - -/** - * Table specific CellContext. - * - * This implementation optionally can handle LAF provide alternateRowColor. The default - * is not doing it. To enable, client code must set a UI-Property with key - * HANDLE_ALTERNATE_ROW_BACKGROUND to Boolean.TRUE. - */ -public class TableCellContext extends CellContext { - - public static final String HANDLE_ALTERNATE_ROW_BACKGROUND = "TableCellContext.handleAlternateRowBackground"; - /** - * Sets state of the cell's context. Note that the component might be null - * to indicate a cell without a concrete context. All accessors must cope - * with. - * - * @param component the component the cell resides on, might be null - * @param value the content value of the cell - * @param row the cell's row index in view coordinates - * @param column the cell's column index in view coordinates - * @param selected the cell's selected state - * @param focused the cell's focused state - * @param expanded the cell's expanded state - * @param leaf the cell's leaf state - */ - public void installContext(JTable component, Object value, int row, int column, - boolean selected, boolean focused, boolean expanded, boolean leaf) { - this.component = component; - installState(value, row, column, selected, focused, expanded, leaf); - this.dropOn = checkDropOnState(); - } - - - /** - * - */ - private boolean checkDropOnState() { - if ((getComponent() == null) || !isValidRow() || !isValidColumn()) { - return false; - } - JTable.DropLocation dropLocation = getComponent().getDropLocation(); - if (dropLocation != null - && !dropLocation.isInsertRow() - && !dropLocation.isInsertColumn() - && dropLocation.getRow() == row - && dropLocation.getColumn() == column) { - return true; - } - return false; - } - - - @Override - public JTable getComponent() { - return (JTable) super.getComponent(); - } - - /** - * Returns the cell's editable property as returned by table.isCellEditable - * or false if the table is null. - * - * @return the cell's editable property. - */ - @Override - public boolean isEditable() { - if ((getComponent() == null) || !isValidRow() || !isValidColumn()) { - return false; - } - return getComponent().isCellEditable(getRow(), getColumn()); - } - - /** - * @inherited

                          - * Overridden to respect UI alternating row colors. - * - */ - @Override - protected Color getBackground() { - if (isDropOn()) { - return getSelectionBackground(); - } - if (getComponent() == null) return null; - Color color = getAlternateRowColor(); - // JW: this is fixing a core bug - alternate color (aka: different - // from default table background) should be the odd row - if ((color != null) && getRow() >= 0 && getRow() % 2 == 1) { - return color; - } - return getComponent().getBackground(); - } - - /** - * Returns a Color to for odd row background if this context should handle the - * alternating row color AND the UIManager has the alternateRowColor property set. - * Returns null otherwise. - * - * @return the color to use for odd row background, or null if either this context - * does not handle or no alternate row color is set. - */ - protected Color getAlternateRowColor() { - if (!Boolean.TRUE.equals(UIManager.get(HANDLE_ALTERNATE_ROW_BACKGROUND))) return null; - return UIManagerExt.getColor(getUIPrefix() + "alternateRowColor"); - - } - - /** - * {@inheritDoc} - */ - @Override - protected Color getSelectionBackground() { - Color selection = null; - if (isDropOn()) { - selection = getDropCellBackground(); - if (selection != null) return selection; - } - return getComponent() != null ? getComponent() - .getSelectionBackground() : null; - } - - /** - * {@inheritDoc} - */ - @Override - protected Color getSelectionForeground() { - Color selection = null; - if (isDropOn()) { - selection = getDropCellForeground(); - if (selection != null) return selection; - } - return getComponent() != null ? getComponent() - .getSelectionForeground() : null; - } - - /** - * {@inheritDoc} - */ - @Override - protected String getUIPrefix() { - return "Table."; - } - - /** - * PRE getComponent != null - * - * @return whether the column coordinate is valid in this context - */ - protected boolean isValidColumn() { - return getColumn() >= 0 && getColumn() < getComponent().getColumnCount() ; - } - - /** - * PRE getComponent != null - * - * @return whether the row coordinate is valid in this context - */ - protected boolean isValidRow() { - return getRow() >= 0 && getRow() < getComponent().getRowCount() ; - } - - -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/renderer/TreeCellContext.java b/src/main/java/org/jdesktop/swingx/renderer/TreeCellContext.java deleted file mode 100644 index ec2eb32bf3..0000000000 --- a/src/main/java/org/jdesktop/swingx/renderer/TreeCellContext.java +++ /dev/null @@ -1,278 +0,0 @@ -/* - * $Id: TreeCellContext.java 3424 2009-07-30 10:53:39Z kleopatra $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.renderer; - -import org.jdesktop.swingx.JXTree; - -import javax.swing.*; -import javax.swing.border.Border; -import javax.swing.border.LineBorder; -import javax.swing.plaf.basic.BasicGraphicsUtils; -import javax.swing.tree.TreePath; -import java.awt.*; - -/** - * Tree specific CellContext. - * - *

                            - *
                          • PENDING: use focus border as returned from list or table instead of - * rolling its own? The missing ui-border probably is a consequence of the - * border hacking as implemented in core default renderer. SwingX has a - * composite default which should use the "normal" border. - *
                          • PENDING: selection colors couple explicitly to SwingX - should we go JXTree as - * generic type? - *
                          • PENDING: for a JXTree use the icons as returned by the xtree api? - *
                          - */ -public class TreeCellContext extends CellContext { - /** the icon to use for a leaf node. */ - protected Icon leafIcon; - - /** the default icon to use for a closed folder. */ - protected Icon closedIcon; - - /** the default icon to use for a open folder. */ - protected Icon openIcon; - - /** the border around a focused node. */ - private Border treeFocusBorder; - - /** - * Sets state of the cell's context. Note that the component might be null - * to indicate a cell without a concrete context. All accessors must cope - * with. - * - * @param component the component the cell resides on, might be null - * @param value the content value of the cell - * @param row the cell's row index in view coordinates - * @param column the cell's column index in view coordinates - * @param selected the cell's selected state - * @param focused the cell's focused state - * @param expanded the cell's expanded state - * @param leaf the cell's leaf state - */ - public void installContext(JTree component, Object value, int row, int column, - boolean selected, boolean focused, boolean expanded, boolean leaf) { - this.component = component; - installState(value, row, column, selected, focused, expanded, leaf); - this.dropOn = checkDropOnState(); - } - - private boolean checkDropOnState() { - if ((getComponent() == null)) { - return false; - } - JTree.DropLocation dropLocation = getComponent().getDropLocation(); - if (dropLocation != null - && dropLocation.getChildIndex() == -1 - && getComponent().getRowForPath(dropLocation.getPath()) == row) { - return true; - } - return false; - } - - @Override - public JTree getComponent() { - return (JTree) super.getComponent(); - } - -//------------------- accessors for derived state - - /** - * Returns the treePath for the row or null if invalid. - * - */ - public TreePath getTreePath() { - if (getComponent() == null) return null; - if ((row < 0) || (row >= getComponent().getRowCount())) return null; - return getComponent().getPathForRow(row); - } - /** - * {@inheritDoc} - *

                          - * PENDING: implement to return the tree cell editability! - */ - @Override - public boolean isEditable() { - return false; - // return getComponent() != null ? getComponent().isCellEditable( - // getRow(), getColumn()) : false; - } - - /** - * {@inheritDoc} - */ - @Override - protected Color getSelectionBackground() { - Color selection = null; - if (isDropOn()) { - selection = getDropCellBackground(); - if (selection != null) return selection; - } - if (getComponent() instanceof JXTree) { - return ((JXTree) getComponent()).getSelectionBackground(); - } - return UIManager.getColor("Tree.selectionBackground"); - } - - /** - * {@inheritDoc} - */ - @Override - protected Color getSelectionForeground() { - Color selection = null; - if (isDropOn()) { - selection = getDropCellForeground(); - if (selection != null) return selection; - } - if (getComponent() instanceof JXTree) { - return ((JXTree) getComponent()).getSelectionForeground(); - } - return UIManager.getColor("Tree.selectionForeground"); - } - - /** - * {@inheritDoc} - */ - @Override - protected String getUIPrefix() { - return "Tree."; - } - - /** - * Returns the default icon to use for leaf cell. - * - * @return the icon to use for leaf cell. - */ - protected Icon getLeafIcon() { - return leafIcon != null ? leafIcon : UIManager - .getIcon(getUIKey("leafIcon")); - } - - /** - * Returns the default icon to use for open cell. - * - * @return the icon to use for open cell. - */ - protected Icon getOpenIcon() { - return openIcon != null ? openIcon : UIManager - .getIcon(getUIKey("openIcon")); - } - - /** - * Returns the default icon to use for closed cell. - * - * @return the icon to use for closed cell. - */ - protected Icon getClosedIcon() { - return closedIcon != null ? closedIcon : UIManager - .getIcon(getUIKey("closedIcon")); - } - - /** - * {@inheritDoc} - *

                          - * - * Overridden to return a default depending for the leaf/open cell state. - */ - @Override - public Icon getIcon() { - if (isLeaf()) { - return getLeafIcon(); - } - if (isExpanded()) { - return getOpenIcon(); - } - return getClosedIcon(); - } - - @Override - protected Border getFocusBorder() { - if (treeFocusBorder == null) { - treeFocusBorder = new TreeFocusBorder(); - } - return treeFocusBorder; - } - - /** - * Border used to draw around the content of the node.

                          - * PENDING: isn't that the same as around a list or table cell, but - * without a tree-specific key/value pair in UIManager? - */ - public class TreeFocusBorder extends LineBorder { - - private Color treeBackground; - - private Color focusColor; - - public TreeFocusBorder() { - super(Color.BLACK); - treeBackground = getBackground(); - if (treeBackground != null) { - focusColor = new Color(~treeBackground.getRGB()); - } - } - - @Override - public void paintBorder(Component c, Graphics g, int x, int y, - int width, int height) { - Color color = UIManager.getColor("Tree.selectionBorderColor"); - if (color != null) { - lineColor = color; - } - if (isDashed()) { - if (treeBackground != c.getBackground()) { - treeBackground = c.getBackground(); - focusColor = new Color(~treeBackground.getRGB()); - } - - Color old = g.getColor(); - g.setColor(focusColor); - BasicGraphicsUtils.drawDashedRect(g, x, y, width, height); - g.setColor(old); - - } else { - super.paintBorder(c, g, x, y, width, height); - } - - } - - /** - * @return a boolean indicating whether the focus border - * should be painted dashed style. - */ - private boolean isDashed() { - return Boolean.TRUE.equals(UIManager - .get("Tree.drawDashedFocusIndicator")); - - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isBorderOpaque() { - return false; - } - - } - -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/renderer/WrappingIconPanel.java b/src/main/java/org/jdesktop/swingx/renderer/WrappingIconPanel.java deleted file mode 100644 index f4e1487a90..0000000000 --- a/src/main/java/org/jdesktop/swingx/renderer/WrappingIconPanel.java +++ /dev/null @@ -1,304 +0,0 @@ -/* - * $Id: WrappingIconPanel.java 4252 2012-11-13 18:37:17Z kschaefe $ - * - * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.renderer; - -import org.jdesktop.swingx.JXPanel; -import org.jdesktop.swingx.painter.Painter; - -import javax.swing.*; -import javax.swing.border.Border; -import java.awt.*; - -/** - * Compound component for usage in tree renderer.

                          - * - * Supports setting an icon for the node and a delegate component - * which is used to show the text/content of the node. The delegate - * component can be shared across renderers.

                          - * - * This implements the PainterAware by delegating to the delegate component if that - * is of type PainterAware. Does nothing if not. - */ -public class WrappingIconPanel extends JXPanel implements PainterAware, IconAware { - protected JComponent delegate; - JLabel iconLabel; - String labelPosition = BorderLayout.CENTER; //2; - int iconLabelGap; - private Border ltorBorder; - private Border rtolBorder; - private boolean dropHackEnabled; - private boolean extendsComponentOpacity; - - - /** - * Instantiates and configures a WrappingIconPanel with the dropHack - * enabled. - * - */ - public WrappingIconPanel() { - this(true); - } - /** - * Instantiates and configures a WrappingIconPanel with the dropHack - * property set as indicated by the boolean. - * - * @param dropHackEnabled a boolean indicating whether the drop hack should - * be enabled. - * - * @see #isVisible() - */ - public WrappingIconPanel(boolean dropHackEnabled) { - setOpaque(false); - iconLabel = new JRendererLabel(); - iconLabelGap = iconLabel.getIconTextGap(); - iconLabel.setOpaque(false); - updateIconBorder(); - setBorder(null); - setLayout(new BorderLayout()); - add(iconLabel, BorderLayout.LINE_START); - setDropHackEnabled(dropHackEnabled); - } - - /** - * {@inheritDoc}

                          - * - * Overridden to update the icon position. - */ - @Override - public void setComponentOrientation(ComponentOrientation o) { - super.setComponentOrientation(o); - updateIconBorder(); - } - - /** - * Updates the icon position according to ComponentOrientation. - */ - private void updateIconBorder() { - if (ltorBorder == null) { - ltorBorder = BorderFactory.createEmptyBorder(0, 0, 0, iconLabelGap); - rtolBorder = BorderFactory.createEmptyBorder(0, iconLabelGap, 0, 0); - } - if (getComponentOrientation().isLeftToRight()) { - iconLabel.setBorder(ltorBorder); - } else { - iconLabel.setBorder(rtolBorder); - } - } - - /** - * Sets the icon. - * - * @param icon the icon to use. - */ - @Override - public void setIcon(Icon icon) { - iconLabel.setIcon(icon); - iconLabel.setText(null); - validate(); - } - - /** - * Returns the icon used in this panel, may be null. - * - * @return the icon used in this panel, may be null. - */ - @Override - public Icon getIcon() { - return iconLabel.getIcon(); - } - - - /** - * Sets the delegate component. - * - * @param comp the component to add as delegate. - */ - public void setComponent(JComponent comp) { - JComponent old = getComponent(); - if (delegate != null) { - remove(delegate); - } - delegate = comp; - if (extendsComponentOpacity) { - iconLabel.setOpaque(comp.isOpaque()); - } else { - iconLabel.setOpaque(false); - } - add(delegate, labelPosition); - validate(); - firePropertyChange("component", old, getComponent()); - } - - /** - * Returns the delegate component. - * - * @return the delegate component. - */ - public JComponent getComponent() { - return delegate; - } - - /** - * {@inheritDoc}

                          - * - * Overridden to set the background of the delegate and icon label as well. - */ - @Override - public void setBackground(Color bg) { - super.setBackground(bg); - if (iconLabel != null) { - iconLabel.setBackground(bg); - } - if (delegate != null) { - delegate.setBackground(bg); - } - } - - /** - * {@inheritDoc}

                          - * - * Overridden to set the foreground of the delegate and icon label as well. - */ - @Override - public void setForeground(Color bg) { - super.setForeground(bg); - if (iconLabel != null) { - iconLabel.setForeground(bg); - } - if (delegate != null) { - delegate.setForeground(bg); - } - } - - - - - /** - * {@inheritDoc}

                          - * - * Overridden to set the Font of the delegate as well. - */ - @Override - public void setFont(Font font) { - if (delegate != null) { - delegate.setFont(font); - } - super.setFont(font); - } - - - /** - * {@inheritDoc} - *

                          - * - * Overridden to hack around #766-swingx: cursor flickering in DnD when - * dragging over tree column. This is a core bug (#6700748) related to - * painting the rendering component on a CellRendererPane. A trick around is - * to let this return false. - *

                          - * - * Some LayoutManagers don't layout an invisible component, so need to make - * the hack-enabled configurable. This implementation will return false - * if isDropHackEnabled, super.isVisible otherwise. - */ - @Override - public boolean isVisible() { - return dropHackEnabled ? false : super.isVisible(); - } - - - /** - * {@inheritDoc} - *

                          - * - * Returns the delegate's Painter if it is of type PainterAware or null - * otherwise. - * - * @return the delegate's Painter or null. - */ - @Override - public Painter getPainter() { - if (delegate instanceof PainterAware) { - return ((PainterAware) delegate).getPainter(); - } - return null; - } - - - /** - * Sets the delegate's Painter if it is of type PainterAware. Does nothing otherwise. - * - * @param painter the Painter to apply to the delegate. - */ - @Override - public void setPainter(Painter painter) { - if (delegate instanceof PainterAware) { - ((PainterAware) delegate).setPainter(painter); - } - - } - - /** - * - * Returns the bounds of the delegate component or null if the delegate is null. - * - * PENDING JW: where do we use it? Maybe it was for testing only? - * - * @return the bounds of the delegate, or null if the delegate is null. - */ - public Rectangle getDelegateBounds() { - if (delegate == null) return null; - return delegate.getBounds(); - } - - - /** - * Sets the dropHackEnabled property.

                          - * - * The default value is true. - * - * @param dropHackEnabled - * - * @see #isVisible() - */ - public void setDropHackEnabled(boolean dropHackEnabled) { - this.dropHackEnabled = dropHackEnabled; - } - - /** - * Sets a boolean indicating whether or not the main component's opacity - * should be applied to the Icon region.

                          - * - * The default value is false. This covers the main use case in a JTree. - * - * @param extendsComponentOpacity - */ - public void setExtendsComponentOpacity(boolean extendsComponentOpacity) { - this.extendsComponentOpacity = extendsComponentOpacity; - - } - /** - * @return the extendsComponentOpacity - */ - public boolean getExtendsComponentOpacity() { - return extendsComponentOpacity; - } -} diff --git a/src/main/java/org/jdesktop/swingx/renderer/WrappingProvider.java b/src/main/java/org/jdesktop/swingx/renderer/WrappingProvider.java deleted file mode 100644 index 815429a725..0000000000 --- a/src/main/java/org/jdesktop/swingx/renderer/WrappingProvider.java +++ /dev/null @@ -1,398 +0,0 @@ -/* - * $Id: WrappingProvider.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.renderer; - -import org.jdesktop.swingx.rollover.RolloverRenderer; -import org.jdesktop.swingx.treetable.TreeTableNode; - -import javax.swing.*; -import javax.swing.tree.DefaultMutableTreeNode; - - -/** - * Wrapping ComponentProvider for usage in tree rendering. Handles the icon - * itself, delegates the node content to the wrappee. Value-based icon and - * content mapping can be configured by custom IconValues and - * StringValue, respectively. - *

                          - * - * An example of how to configure a file tree by using the system icons and - * display names - * - *

                          
                          - * TreeCellRenderer r = new DefaultTreeRenderer(
                          - *         IconValues.FILE_ICON, StringValues.FILE_NAME);
                          - * tree.setCellRenderer(r);
                          - * treeTable.setTreeCellRenderer(r);
                          - * 
                          - * - * PENDING: ui specific focus rect variation (draw rect around icon) missing - *

                          - */ -public class WrappingProvider extends - ComponentProvider implements RolloverRenderer { - - protected ComponentProvider wrappee; - private boolean unwrapUserObject; - - /** - * Instantiates a WrappingProvider with default delegate provider. - * - */ - public WrappingProvider() { - this((ComponentProvider) null); - } - - /** - * Instantiates a WrappingProvider with default wrappee, configured - * to use the wrappeeStringValue. Uses the - * given IconValue to configure the icon. - * - * @param iconValue the IconValue to use for configuring the icon. - * @param wrappeeStringValue the StringValue to use in the wrappee. - */ - public WrappingProvider(IconValue iconValue, StringValue wrappeeStringValue) { - this(iconValue, wrappeeStringValue, true); - } - - /** - * Instantiates a WrappingProvider with default wrappee. Uses the - * given IconValue to configure the icon. - * - * @param iconValue the IconValue to use for configuring the icon. - */ - public WrappingProvider(IconValue iconValue) { - this(iconValue, null); - } - - /** - * Instantiates a WrappingProvider with default wrappee configured - * with the given StringValue. - * - * PENDING: we have a slight semantic glitch compared to super because - * the given StringValue is not for use in this provider but for use - * in the wrappee! - * - * @param wrappeeStringValue the StringValue to use in the wrappee. - */ - public WrappingProvider(StringValue wrappeeStringValue) { - this(null, wrappeeStringValue); - } - - /** - * Instantiates a WrappingProvider with the given delegate - * provider for the node content. If null, a default - * LabelProvider will be used. - * - * @param delegate the provider to use as delegate - */ - public WrappingProvider(ComponentProvider delegate) { - this(delegate, true); - } - - /** - * Instantiates a WrappingProvider with the given delegate - * provider for the node content and unwrapUserObject property. - * If the delegate is null, a default LabelProvider will be used. - * - * @param delegate the provider to use as delegate - * @param unwrapUserObject a flag indicating whether this provider - * should auto-unwrap the userObject from the context value. - */ - public WrappingProvider(ComponentProvider delegate, boolean unwrapUserObject) { - this(null, delegate, unwrapUserObject); - } - - /** - * Instantiates a WrappingProvider with the given delegate - * provider for the node content and unwrapUserObject property. - * If the delegate is null, a default LabelProvider will be used. - * - * @param iv the icon converter to use for this provider - * @param delegate the provider to use as delegate - * @param unwrapUserObject a flag indicating whether this provider - * should auto-unwrap the userObject from the context value. - */ - public WrappingProvider(IconValue iv, ComponentProvider delegate, boolean unwrapUserObject) { - super(iv != null ? (new MappedValue(null, iv)) : StringValues.EMPTY); - setWrappee(delegate); - setUnwrapUserObject(unwrapUserObject); - } - - /** - * Instantiates a WrappingProvider with the given delegate - * provider for the node content and unwrapUserObject property. - * If the delegate is null, a default LabelProvider will be used. - * - * @param iv the icon converter to use for this provider - * @param delegateStringValue the StringValue to use in the wrappee. - * @param unwrapUserObject a flag indicating whether this provider - * should auto-unwrap the userObject from the context value. - */ - public WrappingProvider(IconValue iv, StringValue delegateStringValue, boolean unwrapUserObject) { - this(iv, (ComponentProvider) null, unwrapUserObject); - getWrappee().setStringValue(delegateStringValue); - } - - /** - * Sets the given provider as delegate for the node content. - * If the delegate is null, a default LabelProvider is set.

                          - * - * PENDING: rename to setDelegate? - * - * @param delegate the provider to use as delegate. - */ - public void setWrappee(ComponentProvider delegate) { - if (delegate == null) { - delegate = new LabelProvider(); - } - this.wrappee = delegate; - } - - /** - * Returns the delegate provider used to render the node content. - * - * @return the provider used for rendering the node content. - */ - public ComponentProvider getWrappee() { - return wrappee; - } - - /** - * Sets the unwrapUserObject property. If true, this provider - * replaces a context value of type XXNode with its user object before - * delegating to the wrappee. Otherwise the value is passed as-is always.

                          - * - * The default value is true. - * - * @param unwrap - * @see #getUnwrapUserObject() - */ - public void setUnwrapUserObject(boolean unwrap) { - this.unwrapUserObject = unwrap; - } - - /** - * Returns a boolean indicating whether this provider tries to unwrap - * a userObject from a tree/table/node type value before delegating the - * context. - * - * @return a flag indicating the auto-unwrap property. - * - * @see #setUnwrapUserObject(boolean) - */ - public boolean getUnwrapUserObject() { - return unwrapUserObject; - } - - /** - * {@inheritDoc}

                          - * - * Overridden to comply to contract: returns the string representation as - * provided by the wrappee (as this level has no string rep). Must do the - * same unwrapping magic as in configuring the rendering component if the - * unwrapUserObject property is true.

                          - * - * - * @param value the Object to get a String representation for. - * - * @see #setUnwrapUserObject(boolean) - * @see #getUnwrappedValue(Object) - */ - @Override - public String getString(Object value) { - value = getUnwrappedValue(value); - return wrappee.getString(value); - } - - /** - * Sets a boolean indicating whether or not the main component's opacity - * should be applied to the Icon region.

                          - * - * The default value is false. This covers the main use case in a JTree. - * - * @param extendsComponentOpacity - */ - public void setExtendsComponentOpacity(boolean extendsComponentOpacity) { - rendererComponent.setExtendsComponentOpacity(extendsComponentOpacity); - - } - /** - * @return the extendsComponentOpacity - */ - public boolean getExtendsComponentOpacity() { - return rendererComponent.getExtendsComponentOpacity(); - } - - - /** - * Returns the value as it should be passed to the delegate. If the unwrapUserObject - * property is true, tries return a userObject as appropriate for the value type. - * Returns the given value itself, ff the property is false or the type does - * not support the notion of userObject

                          - * - * Here: unwraps userObject of DefaultMutableTreeNode and TreeTableNode.

                          - * - * @param value the value to possibly unwrap - * @return the userObject if the value has an appropriate type and the - * unwrapUserObject property is true, otherwise returns the value unchanged. - * - * @see #setUnwrapUserObject(boolean) - * @see #getString(Object) - * @see #getRendererComponent(CellContext) - */ - protected Object getUnwrappedValue(Object value) { - if (!getUnwrapUserObject()) return value; - if (value instanceof DefaultMutableTreeNode) { - value = ((DefaultMutableTreeNode) value).getUserObject(); - } else if (value instanceof TreeTableNode) { - TreeTableNode node = (TreeTableNode) value; - value = node.getUserObject(); - } - return value; - } - - /** - * {@inheritDoc} - */ - @Override - public WrappingIconPanel getRendererComponent(CellContext context) { - if (context != null) { - rendererComponent.setComponent(wrappee.rendererComponent); - Object oldValue = adjustContextValue(context); - // PENDING JW: sequence of config? - // A - first wrappee, then this allows to override configure/format methods - // of this class and overrule the wrappee - // B - first this, then wrappee allows overrule by overriding getRendererComp - // would take control from wrappee (f.i. Hyperlink foreground) - super.getRendererComponent(context); - wrappee.getRendererComponent(context); - restoreContextValue(context, oldValue); - return rendererComponent; - } - // PENDING JW: Findbugs barking [NP] Load of known null value - // probably can move the return rendererComponent from the if - // to here (the contract is to return the comp as-is if the - // context is null) - so we can do it here instead of delegating - // to super? - return super.getRendererComponent(context); - } - - /** - * Restores the context value to the old value. - * - * @param context the CellContext to restore. - * @param oldValue the value to restore the context to. - */ - protected void restoreContextValue(CellContext context, Object oldValue) { - context.replaceValue(oldValue); - } - - /** - * Replace the context's value with the userobject if the value is a type - * supporting the notion of userObject and this provider's unwrapUserObject - * property is true. Otherwise does nothing.

                          - * - * Subclasses may override but must guarantee to return the original - * value for restoring. - * - * @param context the context to adjust - * @return the old context value - * - * @see #setUnwrapUserObject(boolean) - * @see #getString(Object) - */ - protected Object adjustContextValue(CellContext context) { - Object oldValue = context.getValue(); - if (getUnwrapUserObject()) { - context.replaceValue(getUnwrappedValue(oldValue)); - } - return oldValue; - } - - @Override - protected void configureState(CellContext context) { - rendererComponent.setBorder(BorderFactory.createEmptyBorder()); - } - -// /** -// * @return -// */ -// private boolean isBorderAroundIcon() { -// return Boolean.TRUE.equals(UIManager.get("Tree.drawsFocusBorderAroundIcon")); -// } - - @Override - protected WrappingIconPanel createRendererComponent() { - return new WrappingIconPanel(); - } - - /** - * {@inheritDoc}

                          - * - * Here: implemented to set the icon. - */ - @Override - protected void format(CellContext context) { - rendererComponent.setIcon(getValueAsIcon(context)); - } - - /** - * {@inheritDoc}

                          - * - * Overridden to fallback to the default icons supplied by the - * context if super returns null. - * - */ - @Override - protected Icon getValueAsIcon(CellContext context) { - Icon icon = super.getValueAsIcon(context); - if (icon == null) { - return context.getIcon(); - } - return IconValue.NULL_ICON == icon ? null : icon; - } - - //----------------- implement RolloverController - - - /** - * {@inheritDoc} - */ - @Override - public void doClick() { - if (isEnabled()) { - ((RolloverRenderer) wrappee).doClick(); - } - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isEnabled() { - return (wrappee instanceof RolloverRenderer) && - ((RolloverRenderer) wrappee).isEnabled(); - } - - - -} diff --git a/src/main/java/org/jdesktop/swingx/renderer/package-info.java b/src/main/java/org/jdesktop/swingx/renderer/package-info.java deleted file mode 100644 index 17548705ea..0000000000 --- a/src/main/java/org/jdesktop/swingx/renderer/package-info.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -/** - * Contains implementation of renderers used by JXTable, JXTreeTable and related classes. - */ -package org.jdesktop.swingx.renderer; - diff --git a/src/main/java/org/jdesktop/swingx/rollover/ListRolloverController.java b/src/main/java/org/jdesktop/swingx/rollover/ListRolloverController.java deleted file mode 100644 index 544d93b496..0000000000 --- a/src/main/java/org/jdesktop/swingx/rollover/ListRolloverController.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * $Id: ListRolloverController.java 3707 2010-07-08 19:19:25Z kschaefe $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.rollover; - -import javax.swing.*; -import java.awt.*; - - -/** - * listens to rollover properties. Repaints effected component regions. - * Updates link cursor. - * - * @author Jeanette Winzenburg - */ -public class ListRolloverController extends RolloverController { - - private Cursor oldCursor; - - // --------------------------------- JList rollover - - @Override - protected void rollover(Point oldLocation, Point newLocation) { - // PENDING JW - track down the -1 in location.y - if (oldLocation != null) { - Rectangle r = component.getCellBounds(oldLocation.y, oldLocation.y); - // LOG.info("old index/cellbounds: " + index + "/" + r); - if (r != null) { - component.repaint(r); - } - } - if (newLocation != null) { - Rectangle r = component.getCellBounds(newLocation.y, newLocation.y); - // LOG.info("new index/cellbounds: " + index + "/" + r); - if (r != null) { - component.repaint(r); - } - } - setRolloverCursor(newLocation); - } - - /** - * something weird: cursor in JList behaves different from JTable? - * Hmm .. no: using the table code snippets seems to fix #503-swingx - * @param location - */ - private void setRolloverCursor(Point location) { - if (hasRollover(location)) { - if (oldCursor == null) { - oldCursor = component.getCursor(); - component.setCursor(Cursor - .getPredefinedCursor(Cursor.HAND_CURSOR)); - } - } else { - if (oldCursor != null) { - component.setCursor(oldCursor); - oldCursor = null; - } - } -// if (hasRollover(location)) { -// oldCursor = component.getCursor(); -// component.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); -// } else { -// component.setCursor(oldCursor); -// oldCursor = null; -// } - - } - - @Override - protected RolloverRenderer getRolloverRenderer(Point location, - boolean prepare) { - ListCellRenderer renderer = component.getCellRenderer(); - RolloverRenderer rollover = renderer instanceof RolloverRenderer - ? (RolloverRenderer) renderer : null; - if ((rollover != null) && !rollover.isEnabled()) { - rollover = null; - } - if ((rollover != null) && prepare) { - Object element = component.getModel().getElementAt(location.y); - renderer.getListCellRendererComponent(component, element, - location.y, false, true); - } - return rollover; - } - - @Override - protected Point getFocusedCell() { - int leadRow = component.getLeadSelectionIndex(); - if (leadRow < 0) - return null; - return new Point(0, leadRow); - } - - } \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/rollover/ListRolloverProducer.java b/src/main/java/org/jdesktop/swingx/rollover/ListRolloverProducer.java deleted file mode 100644 index a7353388a8..0000000000 --- a/src/main/java/org/jdesktop/swingx/rollover/ListRolloverProducer.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * $Id: ListRolloverProducer.java 3296 2009-03-11 12:06:01Z kleopatra $ - * - * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.rollover; - -import javax.swing.*; -import java.awt.*; - -/** - * List-specific implementation of RolloverProducer. - * - * @author Jeanette Winzenburg - */ -public class ListRolloverProducer extends RolloverProducer { - - @Override - protected void updateRolloverPoint(JComponent component, Point mousePoint) { - JList list = (JList) component; - int row = list.locationToIndex(mousePoint); - if (row >= 0) { - Rectangle cellBounds = list.getCellBounds(row, row); - if (!cellBounds.contains(mousePoint)) { - row = -1; - } - } - int col = row < 0 ? -1 : 0; - rollover.x = col; - rollover.y = row; - } - -} diff --git a/src/main/java/org/jdesktop/swingx/rollover/RolloverController.java b/src/main/java/org/jdesktop/swingx/rollover/RolloverController.java deleted file mode 100644 index 4f8cbfd739..0000000000 --- a/src/main/java/org/jdesktop/swingx/rollover/RolloverController.java +++ /dev/null @@ -1,235 +0,0 @@ -/* - * $Id: RolloverController.java 3967 2011-03-17 19:18:47Z kschaefe $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.rollover; - -import org.jdesktop.swingx.plaf.UIAction; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.logging.Logger; - -/** - * Controller for "live" behaviour of XXRenderers. - * - * Once installed on a component, it updates renderer's rollover - * state based on the component's rollover properties. Rollover - * client properties are Points with cell coordinates - * in the view coordinate - * system as appropriate for the concrete component - * (Point.x == column, Point.y == row). - * - * Repaints effected component regions. Updates - * link cursor. Installs a click-action bound to space-released in the target's - * actionMap/inputMap. - * - * - * @author Jeanette Winzenburg, Berlin - */ -public abstract class RolloverController implements - PropertyChangeListener { - @SuppressWarnings("unused") - private static final Logger LOG = Logger.getLogger(RolloverController.class - .getName()); - /** - * the key of the rollover click action which is installed in the - * component's actionMap. - */ - public static final String EXECUTE_BUTTON_ACTIONCOMMAND = "executeButtonAction"; - - protected T component; - - @Override - public void propertyChange(PropertyChangeEvent evt) { - // JW: should not happen ... being paranoid. - if ((component == null) || (component != evt.getSource())) - return; - if (RolloverProducer.ROLLOVER_KEY.equals(evt.getPropertyName())) { - rollover((Point) evt.getOldValue(), (Point) evt.getNewValue()); - } else if (RolloverProducer.CLICKED_KEY.equals(evt.getPropertyName())) { - click((Point) evt.getNewValue()); - } - } - - /** - * Install this as controller for the given component. - * - * @param table the component which has renderers to control. - */ - public void install(T table) { - release(); - this.component = table; - table.addPropertyChangeListener(RolloverProducer.CLICKED_KEY, this); - table.addPropertyChangeListener(RolloverProducer.ROLLOVER_KEY, this); - registerExecuteButtonAction(); - } - - /** - * Uninstall this as controller from the component, if any. - * - */ - public void release() { - if (component == null) - return; - component.removePropertyChangeListener(RolloverProducer.CLICKED_KEY, this); - component.removePropertyChangeListener(RolloverProducer.ROLLOVER_KEY, this); - unregisterExecuteButtonAction(); - component = null; - } - - /** - * called on change of client property Rollover_Key. - * - * @param oldLocation the old value of the rollover location. - * @param newLocation the new value of the rollover location. - */ - protected abstract void rollover(Point oldLocation, Point newLocation); - - /** - * called on change of client property Clicked_key. - * @param location the new value of the clicked location. - */ - protected void click(Point location) { - if (!isClickable(location)) - return; - RolloverRenderer rollover = getRolloverRenderer(location, true); - if (rollover != null) { - rollover.doClick(); - component.repaint(); - } - } - - /** - * Returns the rolloverRenderer at the given location.

                          - * - * The result - * may be null if there is none or if rollover is not enabled. - * - * If the prepare flag is true, the renderer will be prepared - * with value and state as appropriate for the given location. - * - * Note: PRE - the location must be valid in cell coordinate space. - * - * @param location a valid location in cell coordinates, p.x == column, p.y == row. - * @param prepare - * @return RolloverRenderer at the given location - */ - protected abstract RolloverRenderer getRolloverRenderer(Point location, - boolean prepare); - - /** - * Returns a boolean indicating whether or not the cell at the given - * location is clickable.

                          - * - * This implementation returns true if the target is enabled and the - * cell has a rollover renderer. - * - * @param location in cell coordinates, p.x == column, p.y == row. - * @return true if the cell at the given location is clickable - * - * @see #hasRollover(Point) - */ - protected boolean isClickable(Point location) { - return component.isEnabled() && hasRollover(location); - } - - /** - * Returns a boolean indicating whether the or not the cell at the - * given has a rollover renderer. Always returns false if the location - * is not valid. - * - * @param location in cell coordinates, p.x == column, p.y == row. - * @return true if the location is valid and has rollover effects, false - * otherwise. - * - */ - protected boolean hasRollover(Point location) { - if (location == null || location.x < 0 || location.y < 0) - return false; - return getRolloverRenderer(location, false) != null; - } - - /** - * The coordinates of the focused cell in view coordinates. - * - * This method is called if the click action is invoked by a keyStroke. - * The returned cell coordinates should be related to - * what is typically interpreted as "focused" in the context of the - * component. - * - * p.x == focused column, p.y == focused row. - * A null return value or any coordinate value of < 0 - * is interpreted as "outside". - * - * @return the location of the focused cell. - */ - protected abstract Point getFocusedCell(); - - /** - * uninstalls and deregisters the click action from the component's - * actionMap/inputMap. - * - */ - protected void unregisterExecuteButtonAction() { - component.getActionMap().put(EXECUTE_BUTTON_ACTIONCOMMAND, null); - KeyStroke space = KeyStroke.getKeyStroke("released SPACE"); - component.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put( - space, null); - } - - /** - * installs and registers the click action in the component's - * actionMap/inputMap. - * - */ - protected void registerExecuteButtonAction() { - component.getActionMap().put(EXECUTE_BUTTON_ACTIONCOMMAND, - createExecuteButtonAction()); - KeyStroke space = KeyStroke.getKeyStroke("released SPACE"); - component.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put( - space, EXECUTE_BUTTON_ACTIONCOMMAND); - - } - - /** - * creates and returns the click action to install in the - * component's actionMap. - * - */ - protected Action createExecuteButtonAction() { - return new UIAction(null) { - @Override - public void actionPerformed(ActionEvent e) { - click(getFocusedCell()); - } - - @Override - public boolean isEnabled(Object sender) { - if (component == null || !component.isEnabled() || !component.hasFocus()) - return false; - return isClickable(getFocusedCell()); - } - }; - } - -} diff --git a/src/main/java/org/jdesktop/swingx/rollover/RolloverProducer.java b/src/main/java/org/jdesktop/swingx/rollover/RolloverProducer.java deleted file mode 100644 index c4c1a04e64..0000000000 --- a/src/main/java/org/jdesktop/swingx/rollover/RolloverProducer.java +++ /dev/null @@ -1,281 +0,0 @@ -/* - * $Id: RolloverProducer.java 3982 2011-03-30 12:27:31Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.rollover; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.*; -import java.util.logging.Logger; - -/** - * Mouse/Motion/Listener which maps mouse coordinates to client coordinates - * and stores these as client properties in the target JComponent. The exact - * mapping process is left to subclasses. Typically, they will map to "cell" - * coordinates.

                          - * - * Note: this class assumes that the target component is of type JComponent.

                          - * Note: this implementation is stateful, it can't be shared across different - * instances of a target component.

                          - * - * - * @author Jeanette Winzenburg - */ -public abstract class RolloverProducer implements MouseListener, MouseMotionListener, - ComponentListener { - - @SuppressWarnings("unused") - private static final Logger LOG = Logger.getLogger(RolloverProducer.class - .getName()); - - /** - * Key for client property mapped from mouse-triggered action. - * Note that the actual mouse-event which results in setting the property - * depends on the implementation of the concrete RolloverProducer. - */ - public static final String CLICKED_KEY = "swingx.clicked"; - - /** Key for client property mapped from rollover events */ - public static final String ROLLOVER_KEY = "swingx.rollover"; - - // public static final String PRESSED_KEY = "swingx.pressed"; - - private boolean isDragging; - - /** - * Installs all listeners, as required. - * - * @param component target to install required listeners on, must - * not be null. - */ - public void install(JComponent component) { - component.addMouseListener(this); - component.addMouseMotionListener(this); - component.addComponentListener(this); - } - - /** - * Removes all listeners. - * - * @param component target component to uninstall required listeners from, - * must not be null - */ - public void release(JComponent component) { - component.removeMouseListener(this); - component.removeMouseMotionListener(this); - component.removeComponentListener(this); - } - - //----------------- mouseListener - - /** - * Implemented to map to Rollover properties as needed. This implemenation calls - * updateRollover with both ROLLOVER_KEY and CLICKED_KEY properties. - */ - @Override - public void mouseReleased(MouseEvent e) { - Point oldCell = new Point(rollover); - // JW: fix for #456-swingx - rollover not updated after end of dragging - updateRollover(e, ROLLOVER_KEY, false); - // Fix Issue 1387-swingx - no click on release-after-drag - if (isClick(e, oldCell, isDragging)) { - updateRollover(e, CLICKED_KEY, true); - } - isDragging = false; - } - - /** - * Returns a boolean indicating whether or not the given mouse event should - * be interpreted as a click. This method is called from mouseReleased - * after the cell coordiates were updated. While the ID of mouse event - * is not formally enforced, it is assumed to be a MOUSE_RELEASED. Calling - * for other types might or might not work as expected.

                          - * - * This implementation returns true if the current rollover point is the same - * cell as the given oldRollover, that is ending a drag inside the same cell - * triggers the action while ending a drag somewhere does not.

                          - * - * PENDING JW: open to more complex logic in case it clashes with existing code, - * see Issue #1387. - * - * @param e the mouseEvent which triggered calling this, assumed to be - * a mouseReleased, must not be null - * @param oldRollover the cell before the mouseEvent was mapped, must not be null - * @param wasDragging true if the release happened - * @return a boolean indicating whether or not the given mouseEvent should - * be interpreted as a click. - */ - protected boolean isClick(MouseEvent e, Point oldRollover, boolean wasDragging) { - return oldRollover.equals(rollover); - } - - /** - * Implemented to map to client property rollover and fire only if client - * coordinate changed. - */ - @Override - public void mouseEntered(MouseEvent e) { -// LOG.info("" + e); - isDragging = false; - updateRollover(e, ROLLOVER_KEY, false); - } - - /** - * Implemented to remove client properties rollover and clicked. if the - * source is a JComponent. Does nothing otherwise. - */ - @Override - public void mouseExited(MouseEvent e) { - isDragging = false; -// screenLocation = null; -// LOG.info("" + e); -// if (((JComponent) e.getComponent()).getMousePosition(true) != null) { -// updateRollover(e, ROLLOVER_KEY, false); -// } else { -// } - ((JComponent) e.getSource()).putClientProperty(ROLLOVER_KEY, null); - ((JComponent) e.getSource()).putClientProperty(CLICKED_KEY, null); - - } - - /** - * Implemented to do nothing. - */ - @Override - public void mouseClicked(MouseEvent e) { - } - - /** - * Implemented to do nothing. - */ - @Override - public void mousePressed(MouseEvent e) { - } - - // ---------------- MouseMotionListener - /** - * Implemented to set a dragging flag to true. - */ - @Override - public void mouseDragged(MouseEvent e) { - isDragging = true; - } - - /** - * Implemented to map to client property rollover and fire only if client - * coordinate changed. - */ - @Override - public void mouseMoved(MouseEvent e) { - updateRollover(e, ROLLOVER_KEY, false); - } - - //---------------- ComponentListener - - - @Override - public void componentShown(ComponentEvent e) { - } - - @Override - public void componentResized(ComponentEvent e) { - updateRollover(e); - } - - @Override - public void componentMoved(ComponentEvent e) { - updateRollover(e); - } - - /** - * @param e - */ - private void updateRollover(ComponentEvent e) { - Point componentLocation = e.getComponent().getMousePosition(); - if (componentLocation == null) { - componentLocation = new Point(-1, -1); - } -// LOG.info("" + componentLocation + " / " + e); - updateRolloverPoint((JComponent) e.getComponent(), componentLocation); - updateClientProperty((JComponent) e.getComponent(), ROLLOVER_KEY, true); - } - - @Override - public void componentHidden(ComponentEvent e) { - } - - //---------------- mapping methods - - /** - * Controls the mapping of the given mouse event to a client property. This - * implementation first calls updateRolloverPoint to convert the mouse coordinates. - * Then calls updateClientProperty to actually set the client property in the - * - * @param e the MouseEvent to map to client coordinates - * @param property the client property to map to - * @param fireAlways a flag indicating whether a client event should be fired if unchanged. - * - * @see #updateRolloverPoint(JComponent, Point) - * @see #updateClientProperty(JComponent, String, boolean) - */ - protected void updateRollover(MouseEvent e, String property, - boolean fireAlways) { - updateRolloverPoint((JComponent) e.getComponent(), e.getPoint()); - updateClientProperty((JComponent) e.getComponent(), property, fireAlways); - } - - /** Current mouse location in client coordinates. */ - protected Point rollover = new Point(-1, -1); - - /** - * Sets the given client property to the value of current mouse location in - * client coordinates. If fireAlways, the property is force to fire a change. - * - * @param component the target component - * @param property the client property to set - * @param fireAlways a flag indicating whether a client property - * should be forced to fire an event. - */ - protected void updateClientProperty(JComponent component, String property, - boolean fireAlways) { - if (fireAlways) { - // fix Issue #864-swingx: force propertyChangeEvent - component.putClientProperty(property, null); - component.putClientProperty(property, new Point(rollover)); - } else { - Point p = (Point) component.getClientProperty(property); - if (p == null || (rollover.x != p.x) || (rollover.y != p.y)) { - component.putClientProperty(property, new Point(rollover)); - } - } - } - - /** - * Subclasses must implement to map the given mouse coordinates into - * appropriate client coordinates. The result must be stored in the - * rollover field. - * - * @param component the target component which received a mouse event - * @param mousePoint the mouse position of the event, coordinates are - * component pixels - */ - protected abstract void updateRolloverPoint(JComponent component, Point mousePoint); - -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/rollover/RolloverRenderer.java b/src/main/java/org/jdesktop/swingx/rollover/RolloverRenderer.java deleted file mode 100644 index efbbbd09b4..0000000000 --- a/src/main/java/org/jdesktop/swingx/rollover/RolloverRenderer.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * $Id: RolloverRenderer.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.rollover; - -/** - * Interface to mark renderers as "live".

                          - * - * PENDING: probably need methods to enabled/click taking a similar - * set of parameters as getXXComponent because the actual - * outcome might depend on the given value. If so, we'll need - * to extend the XXRenderer interfaces. - * - * @author Jeanette Winzenburg - */ -public interface RolloverRenderer { - /** - * - * @return true if rollover effects are on and clickable. - */ - boolean isEnabled(); - - /** - * Same as AbstractButton.doClick(). It's up to client - * code to prepare the renderer's component before calling - * this method. - * - */ - void doClick(); -} diff --git a/src/main/java/org/jdesktop/swingx/rollover/TableRolloverController.java b/src/main/java/org/jdesktop/swingx/rollover/TableRolloverController.java deleted file mode 100644 index e8cff8042a..0000000000 --- a/src/main/java/org/jdesktop/swingx/rollover/TableRolloverController.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * $Id: TableRolloverController.java 3641 2010-04-01 10:48:43Z kleopatra $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.rollover; - -import javax.swing.*; -import javax.swing.table.TableCellRenderer; -import java.awt.*; -import java.util.ArrayList; -import java.util.List; - -/** - * listens to rollover properties. Repaints effected component regions. Updates - * link cursor. - * - * @author Jeanette Winzenburg - */ -public class TableRolloverController extends - RolloverController { - - private Cursor oldCursor; - - // --------------------------- JTable rollover - - @Override - protected void rollover(Point oldLocation, Point newLocation) { - // check which rows are effected and need repaint - boolean paintOldRow = hasRow(oldLocation); - boolean paintNewRow = hasRow(newLocation); - if (paintOldRow && paintNewRow) { - if (oldLocation.y == newLocation.y) { - // row unchanged, no need for repaint - paintOldRow = false; - paintNewRow = false; - } - } - // check which columns are effected and need repaint - boolean paintOldColumn = hasColumn(oldLocation); - boolean paintNewColumn = hasColumn(newLocation); - if (paintOldColumn && paintNewColumn) { - if (oldLocation.x == newLocation.x) { - // column unchanged, no need for repaint - paintOldColumn = false; - paintNewColumn = false; - } - } - - List rectangles = getPaintRectangles(null, oldLocation, paintOldRow, paintOldColumn); - rectangles = getPaintRectangles(rectangles, newLocation, paintNewRow, paintNewColumn); - if (rectangles != null) { - for (Rectangle rectangle : rectangles) { - component.repaint(rectangle); - } - } - setRolloverCursor(newLocation); - } - - /** - * @param rectangles List of rectangles to paint, maybe null - * @param cellLocation the location of the cell, guaranteed to be not null - * @param paintRow boolean indicating whether the row should be painted - * @param paintColumn boolean indicating whether the column should be painted - * @return list of rectangles to paint, maybe null - */ - private List getPaintRectangles(List rectangles, Point cellLocation, - boolean paintRow, boolean paintColumn) { - if (!paintRow && !paintColumn) return rectangles; - if (rectangles == null) { - rectangles = new ArrayList(); - } - Rectangle r = component.getCellRect(cellLocation.y, cellLocation.x, - false); - if (paintRow) { - rectangles.add(new Rectangle(0, r.y, component.getWidth(), - r.height)); - } - if (paintColumn) { - rectangles.add(new Rectangle(r.x, 0, r.width, component - .getHeight())); - } - return rectangles; - } - - /** - * @param cellLocation the cell location to check, may be null - * @return a boolean indicating whether the given cellLocation has a column - * to paint - */ - private boolean hasColumn(Point cellLocation) { - return cellLocation != null && cellLocation.x >= 0; - } - - /** - * @param cellLocation the cell location to check, may be null - * @return a boolean indicating whether the given cellLocation has a row - * to paint - */ - private boolean hasRow(Point cellLocation) { - return cellLocation != null && cellLocation.y >= 0; - } - - /** - * overridden to return false if cell editable. - */ - @Override - protected boolean isClickable(Point location) { - return super.isClickable(location) - && !component.isCellEditable(location.y, location.x); - } - - @Override - protected RolloverRenderer getRolloverRenderer(Point location, - boolean prepare) { - TableCellRenderer renderer = component.getCellRenderer(location.y, - location.x); - RolloverRenderer rollover = renderer instanceof RolloverRenderer ? (RolloverRenderer) renderer - : null; - if ((rollover != null) && !rollover.isEnabled()) { - rollover = null; - } - if ((rollover != null) && prepare) { - component.prepareRenderer(renderer, location.y, location.x); - } - return rollover; - } - - private void setRolloverCursor(Point location) { - if (hasRollover(location)) { - if (oldCursor == null) { - oldCursor = component.getCursor(); - component.setCursor(Cursor - .getPredefinedCursor(Cursor.HAND_CURSOR)); - } - } else { - if (oldCursor != null) { - component.setCursor(oldCursor); - oldCursor = null; - } - } - - } - - @Override - protected Point getFocusedCell() { - int leadRow = component.getSelectionModel().getLeadSelectionIndex(); - int leadColumn = component.getColumnModel().getSelectionModel() - .getLeadSelectionIndex(); - return new Point(leadColumn, leadRow); - } - -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/rollover/TableRolloverProducer.java b/src/main/java/org/jdesktop/swingx/rollover/TableRolloverProducer.java deleted file mode 100644 index a412767f1d..0000000000 --- a/src/main/java/org/jdesktop/swingx/rollover/TableRolloverProducer.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * $Id: TableRolloverProducer.java 3296 2009-03-11 12:06:01Z kleopatra $ - * - * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.rollover; - -import javax.swing.*; -import java.awt.*; - -/** - * Table-specific implementation of RolloverProducer. - * - * @author Jeanette Winzenburg - */ -public class TableRolloverProducer extends RolloverProducer { - - @Override - protected void updateRolloverPoint(JComponent component, Point mousePoint) { - JTable table = (JTable) component; - int col = table.columnAtPoint(mousePoint); - int row = table.rowAtPoint(mousePoint); - if ((col < 0) || (row < 0)) { - row = -1; - col = -1; - } - rollover.x = col; - rollover.y = row; - } - -} diff --git a/src/main/java/org/jdesktop/swingx/rollover/TreeRolloverController.java b/src/main/java/org/jdesktop/swingx/rollover/TreeRolloverController.java deleted file mode 100644 index 2340e5b311..0000000000 --- a/src/main/java/org/jdesktop/swingx/rollover/TreeRolloverController.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * $Id: TreeRolloverController.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.rollover; - -import javax.swing.*; -import javax.swing.tree.TreeCellRenderer; -import javax.swing.tree.TreePath; -import java.awt.*; - - -/** - * listens to rollover properties. - * Repaints effected component regions. - * Updates link cursor. - * - * @author Jeanette Winzenburg - */ - public class TreeRolloverController extends RolloverController { - - private Cursor oldCursor; - -// -------------------------------------JTree rollover - - @Override - protected void rollover(Point oldLocation, Point newLocation) { - // JW: conditional repaint not working? -// component.repaint(); - if (oldLocation != null) { - Rectangle r = component.getRowBounds(oldLocation.y); - if (r != null) { - r.x = 0; - r.width = component.getWidth(); - component.repaint(r); - } - } - if (newLocation != null) { - Rectangle r = component.getRowBounds(newLocation.y); - if (r != null) { - r.x = 0; - r.width = component.getWidth(); - component.repaint(r); - } - } - setRolloverCursor(newLocation); - } - - - private void setRolloverCursor(Point location) { - if (hasRollover(location)) { - if (oldCursor == null) { - oldCursor = component.getCursor(); - component.setCursor(Cursor - .getPredefinedCursor(Cursor.HAND_CURSOR)); - } - } else { - if (oldCursor != null) { - component.setCursor(oldCursor); - oldCursor = null; - } - } - - } - - - @Override - protected RolloverRenderer getRolloverRenderer(Point location, boolean prepare) { - TreeCellRenderer renderer = component.getCellRenderer(); - RolloverRenderer rollover = renderer instanceof RolloverRenderer - ? (RolloverRenderer) renderer : null; - if ((rollover != null) && !rollover.isEnabled()) { - rollover = null; - } - if ((rollover != null) && prepare) { - TreePath path = component.getPathForRow(location.y); - Object element = path != null ? path.getLastPathComponent() : null; - renderer.getTreeCellRendererComponent(component, element, false, - false, false, - location.y, false); - } - return rollover; - } - - - @Override - protected Point getFocusedCell() { - // TODO Auto-generated method stub - return null; - } - - } \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/rollover/TreeRolloverProducer.java b/src/main/java/org/jdesktop/swingx/rollover/TreeRolloverProducer.java deleted file mode 100644 index 973bb0a80b..0000000000 --- a/src/main/java/org/jdesktop/swingx/rollover/TreeRolloverProducer.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * $Id: TreeRolloverProducer.java 4190 2012-06-27 19:01:06Z kschaefe $ - * - * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.rollover; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.MouseEvent; - -/** - * Tree-specific implementation of RolloverProducer. - *

                          - * This implementation assumes a "hit" for rollover if the mouse is anywhere in - * the total width of the tree. Additionally, a pressed to the right (but - * outside of the label bounds) is re-dispatched as a pressed just inside the - * label bounds. This is a first go for #166-swingx. - *

                          - * - * PENDING JW: bidi-compliance of pressed? - * - * @author Jeanette Winzenburg - */ -public class TreeRolloverProducer extends RolloverProducer { - - @Override - public void mousePressed(MouseEvent e) { - JTree tree = (JTree) e.getComponent(); - Point mousePoint = e.getPoint(); - int labelRow = tree.getRowForLocation(mousePoint.x, mousePoint.y); - // default selection - if (labelRow >= 0) - return; - int row = tree.getClosestRowForLocation(mousePoint.x, mousePoint.y); - Rectangle bounds = tree.getRowBounds(row); - if (bounds == null) { - row = -1; - } else { - if ((bounds.y + bounds.height < mousePoint.y) - || bounds.x > mousePoint.x) { - row = -1; - } - } - // no hit - if (row < 0) - return; - tree.dispatchEvent(new MouseEvent(tree, e.getID(), e.getWhen(), e - .getModifiers(), bounds.x + bounds.width - 2, mousePoint.y, e - .getClickCount(), e.isPopupTrigger(), e.getButton())); - } - - @Override - protected void updateRolloverPoint(JComponent component, Point mousePoint) { - JTree tree = (JTree) component; - int row = tree.getClosestRowForLocation(mousePoint.x, mousePoint.y); - Rectangle bounds = tree.getRowBounds(row); - if (bounds == null) { - row = -1; - } else { - if ((bounds.y + bounds.height < mousePoint.y) - || bounds.x > mousePoint.x) { - row = -1; - } - } - int col = row < 0 ? -1 : 0; - rollover.x = col; - rollover.y = row; - } - -} diff --git a/src/main/java/org/jdesktop/swingx/search/AbstractSearchable.java b/src/main/java/org/jdesktop/swingx/search/AbstractSearchable.java deleted file mode 100644 index f5dc73c7a0..0000000000 --- a/src/main/java/org/jdesktop/swingx/search/AbstractSearchable.java +++ /dev/null @@ -1,661 +0,0 @@ -/* - * $Id: AbstractSearchable.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.search; - -import org.jdesktop.swingx.decorator.*; - -import javax.swing.*; -import java.awt.*; -import java.util.regex.MatchResult; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * An abstract implementation of Searchable supporting - * incremental search. - * - * Keeps internal state to represent the previous search result. - * For all methods taking a String as parameter: compiles the String - * to a Pattern as-is and routes to the central method taking a Pattern. - * - * - * @author Jeanette Winzenburg - */ -public abstract class AbstractSearchable implements Searchable { - - /** - * stores the result of the previous search. - */ - protected final SearchResult lastSearchResult = new SearchResult(); - - private AbstractHighlighter matchHighlighter; - - - /** key for client property to use SearchHighlighter as match marker. */ - public static final String MATCH_HIGHLIGHTER = "match.highlighter"; - - /** - * Performs a forward search starting at the beginning - * across the Searchable using String that represents a - * regex pattern; {@link Pattern}. - * - * @param searchString String that we will try to locate - * @return the position of the match in appropriate coordinates or -1 if - * no match found. - */ - @Override - public int search(String searchString) { - return search(searchString, -1); - } - - /** - * Performs a forward search starting at the given startIndex - * using String that represents a regex - * pattern; {@link Pattern}. - * - * @param searchString String that we will try to locate - * @param startIndex position in the document in the appropriate coordinates - * from which we will start search or -1 to start from the beginning - * @return the position of the match in appropriate coordinates or -1 if - * no match found. - */ - @Override - public int search(String searchString, int startIndex) { - return search(searchString, startIndex, false); - } - - /** - * Performs a search starting at the given startIndex - * using String that represents a regex - * pattern; {@link Pattern}. The search direction - * depends on the boolean parameter: forward/backward if false/true, respectively. - * - * @param searchString String that we will try to locate - * @param startIndex position in the document in the appropriate coordinates - * from which we will start search or -1 to start from the beginning - * @param backward true if we should perform search towards the beginning - * @return the position of the match in appropriate coordinates or -1 if - * no match found. - */ - @Override - public int search(String searchString, int startIndex, boolean backward) { - Pattern pattern = null; - if (!isEmpty(searchString)) { - pattern = Pattern.compile(searchString, 0); - } - return search(pattern, startIndex, backward); - } - - /** - * Performs a forward search starting at the beginning - * across the Searchable using the pattern; {@link Pattern}. - * - * @param pattern Pattern that we will try to locate - * @return the position of the match in appropriate coordinates or -1 if - * no match found. - */ - @Override - public int search(Pattern pattern) { - return search(pattern, -1); - } - - /** - * Performs a forward search starting at the given startIndex - * using the Pattern; {@link Pattern}. - * - * @param pattern Pattern that we will try to locate - * @param startIndex position in the document in the appropriate coordinates - * from which we will start search or -1 to start from the beginning - * @return the position of the match in appropriate coordinates or -1 if - * no match found. - */ - @Override - public int search(Pattern pattern, int startIndex) { - return search(pattern, startIndex, false); - } - - /** - * Performs a search starting at the given startIndex - * using the pattern; {@link Pattern}. - * The search direction depends on the boolean parameter: - * forward/backward if false/true, respectively.

                          - * - * Updates visible and internal search state. - * - * @param pattern Pattern that we will try to locate - * @param startIndex position in the document in the appropriate coordinates - * from which we will start search or -1 to start from the beginning - * @param backwards true if we should perform search towards the beginning - * @return the position of the match in appropriate coordinates or -1 if - * no match found. - */ - @Override - public int search(Pattern pattern, int startIndex, boolean backwards) { - int matchingRow = doSearch(pattern, startIndex, backwards); - moveMatchMarker(); - return matchingRow; - } - - /** - * Performs a search starting at the given startIndex - * using the pattern; {@link Pattern}. - * The search direction depends on the boolean parameter: - * forward/backward if false/true, respectively.

                          - * - * Updates internal search state. - * - * @param pattern Pattern that we will try to locate - * @param startIndex position in the document in the appropriate coordinates - * from which we will start search or -1 to start from the beginning - * @param backwards true if we should perform search towards the beginning - * @return the position of the match in appropriate coordinates or -1 if - * no match found. - */ - protected int doSearch(Pattern pattern, final int startIndex, boolean backwards) { - if (isTrivialNoMatch(pattern, startIndex)) { - updateState(null); - return lastSearchResult.foundRow; - } - - int startRow; - if (isEqualStartIndex(startIndex)) { // implies: the last found coordinates are valid - if (!isEqualPattern(pattern)) { - SearchResult searchResult = findExtendedMatch(pattern, startIndex); - if (searchResult != null) { - updateState(searchResult); - return lastSearchResult.foundRow; - } - - } - // didn't find a match, make sure to move the startPosition - // for looking for the next/previous match - startRow = moveStartPosition(startIndex, backwards); - - } else { - // startIndex is different from last search, reset the column to -1 - // and make sure a -1 startIndex is mapped to first/last row, respectively. - startRow = adjustStartPosition(startIndex, backwards); - } - findMatchAndUpdateState(pattern, startRow, backwards); - return lastSearchResult.foundRow; - } - - /** - * Loops through the searchable until a match is found or the - * end is reached. Updates internal search state. - * - * @param pattern Pattern that we will try to locate - * @param startRow position in the document in the appropriate coordinates - * from which we will start search or -1 to start from the beginning - * @param backwards true if we should perform search towards the beginning - */ - protected abstract void findMatchAndUpdateState(Pattern pattern, int startRow, boolean backwards); - - /** - * Returns a boolean indicating if it can be trivially decided to not match. - *

                          - * - * This implementation returns true if pattern is null or startIndex - * exceeds the upper size limit.

                          - * - * @param pattern Pattern that we will try to locate - * @param startIndex position in the document in the appropriate coordinates - * from which we will start search or -1 to start from the beginning - * @return true if we can say ahead that no match will be found with given search criteria - */ - protected boolean isTrivialNoMatch(Pattern pattern, final int startIndex) { - return (pattern == null) || (startIndex >= getSize()); - } - - /** - * Called if startIndex is different from last search - * and make sure a backwards/forwards search starts at last/first row, - * respectively.

                          - * - * @param startIndex position in the document in the appropriate coordinates - * from which we will start search or -1 to start from the beginning - * @param backwards true if we should perform search from towards the beginning - * @return adjusted startIndex - */ - protected int adjustStartPosition(int startIndex, boolean backwards) { - if (startIndex < 0) { - if (backwards) { - return getSize() - 1; - } else { - return 0; - } - } - return startIndex; - } - - /** - * Moves the internal start position for matching as appropriate and returns - * the new startIndex to use. Called if search was messaged with the same - * startIndex as previously. - *

                          - * - * This implementation returns a by 1 decremented/incremented startIndex - * depending on backwards true/false, respectively. - * - * @param startIndex position in the document in the appropriate coordinates - * from which we will start search or -1 to start from the beginning - * @param backwards true if we should perform search towards the beginning - * @return adjusted startIndex - */ - protected int moveStartPosition(int startIndex, boolean backwards) { - if (backwards) { - startIndex--; - } else { - startIndex++; - } - return startIndex; - } - - - /** - * Checks if the given Pattern should be considered as the same as - * in a previous search. - *

                          - * This implementation compares the patterns' regex. - * - * @param pattern Pattern that we will compare with last request - * @return if provided Pattern is the same as the stored from - * the previous search attempt - */ - protected boolean isEqualPattern(Pattern pattern) { - return pattern.pattern().equals(lastSearchResult.getRegEx()); - } - - /** - * Checks if the startIndex should be considered as the same as in - * the previous search. - * - * @param startIndex startIndex that we will compare with the index - * stored by the previous search request - * @return true if the startIndex should be re-matched, false if not. - */ - protected boolean isEqualStartIndex(final int startIndex) { - return isValidIndex(startIndex) && (startIndex == lastSearchResult.foundRow); - } - - /** - * Checks if the searchString should be interpreted as empty. - *

                          - * This implementation returns true if string is null or has zero length. - * - * @param searchString String that we should evaluate - * @return true if the provided String should be interpreted as empty - */ - protected boolean isEmpty(String searchString) { - return (searchString == null) || searchString.length() == 0; - } - - - /** - * Matches the cell at row/lastFoundColumn against the pattern. - * Called if sameRowIndex && !hasEqualRegEx. - * PRE: lastFoundColumn valid. - * - * @param pattern Pattern that we will try to match - * @param row position at which we will get the value to match with the provided Pattern - * @return result of the match; {@link SearchResult} - */ - protected abstract SearchResult findExtendedMatch(Pattern pattern, int row); - - /** - * Factory method to create a SearchResult from the given parameters. - * - * @param matcher the matcher after a successful find. Must not be null. - * @param row the found index - * @param column the found column - * @return newly created SearchResult - */ - protected SearchResult createSearchResult(Matcher matcher, int row, int column) { - return new SearchResult(matcher.pattern(), - matcher.toMatchResult(), row, column); - } - - /** - * Checks if index is in range: 0 <= index < getSize(). - * - * @param index possible start position that we will check for validity - * @return true if given parameter is valid index - */ - protected boolean isValidIndex(int index) { - return index >= 0 && index < getSize(); - } - - /** - * Returns the size of this searchable. - * - * @return size of this searchable - */ - protected abstract int getSize(); - - /** - * Updates inner searchable state based on provided search result - * - * @param searchResult SearchResult that represents the new state - * of this AbstractSearchable - */ - protected void updateState(SearchResult searchResult) { - lastSearchResult.updateFrom(searchResult); - } - - /** - * Moves the match marker according to current found state. - */ - protected abstract void moveMatchMarker(); - - /** - * It's the responsibility of subclasses to covariant override. - * - * @return the target component - */ - public abstract JComponent getTarget(); - - /** - * Removes the highlighter. - * - * @param searchHighlighter the Highlighter to remove. - */ - protected abstract void removeHighlighter(Highlighter searchHighlighter); - - /** - * Returns the highlighters registered on the search target. - * - * @return all registered highlighters - */ - protected abstract Highlighter[] getHighlighters(); - - /** - * Adds the highlighter to the target. - * - * @param highlighter the Highlighter to add. - */ - protected abstract void addHighlighter(Highlighter highlighter); - - /** - * Ensure that the given Highlighter is the last in the list of - * the highlighters registered on the target. - * - * @param highlighter the Highlighter to be inserted as last. - */ - protected void ensureInsertedSearchHighlighters(Highlighter highlighter) { - if (!isInPipeline(highlighter)) { - addHighlighter(highlighter); - } - } - - /** - * Returns a flag indicating if the given highlighter is last in the - * list of highlighters registered on the target. If so returns true. - * If not, it has the side-effect of removing the highlighter and returns false. - * - * @param searchHighlighter the highlighter to check for being last - * @return a boolean indicating whether the highlighter is last. - */ - private boolean isInPipeline(Highlighter searchHighlighter) { - Highlighter[] inPipeline = getHighlighters(); - if ((inPipeline.length > 0) && - (searchHighlighter.equals(inPipeline[inPipeline.length -1]))) { - return true; - } - removeHighlighter(searchHighlighter); - return false; - } - - /** - * Converts and returns the given column index from view coordinates to model - * coordinates. - *

                          - * This implementation returns the view coordinate, that is assumes - * that both coordinate systems are the same. - * - * @param viewColumn the column index in view coordinates, must be a valid index - * in that system. - * @return the column index in model coordinates. - */ - protected int convertColumnIndexToModel(int viewColumn) { - return viewColumn; - } - - /** - * - * @param result - * @return {@code true} if the {@code result} contains a match; - * {@code false} otherwise - */ - private boolean hasMatch(SearchResult result) { - boolean noMatch = (result.getFoundRow() < 0) || (result.getFoundColumn() < 0); - return !noMatch; - } - - /** - * Returns a boolean indicating whether the current search result is a match. - *

                          - * PENDING JW: move to SearchResult? - * @return a boolean indicating whether the current search result is a match. - */ - protected boolean hasMatch() { - return hasMatch(lastSearchResult); - } - - /** - * Returns a boolean indicating whether a match should be marked with a - * Highlighter. Typically, if true, the match highlighter is used, otherwise - * a match is indicated by selection. - *

                          - * - * This implementation returns true if the target component has a client - * property for key MATCH_HIGHLIGHTER with value Boolean.TRUE, false - * otherwise. The SearchFactory sets that client property in incremental - * search mode, that is when triggering a search via the JXFindBar as - * installed by the factory. - * - * @return a boolean indicating whether a match should be marked by a using - * a Highlighter. - * - * @see SearchFactory - */ - protected boolean markByHighlighter() { - return Boolean.TRUE.equals(getTarget().getClientProperty( - MATCH_HIGHLIGHTER)); - } - - /** - * Sets the AbstractHighlighter to use as match marker, if enabled. A null value - * will re-install the default. - * - * @param hl the Highlighter to use as match marker. - */ - public void setMatchHighlighter(AbstractHighlighter hl) { - removeHighlighter(matchHighlighter); - matchHighlighter = hl; - if (markByHighlighter()) { - moveMatchMarker(); - } - } - - /** - * Returns the Hihglighter to use as match marker, lazyly created if null. - * - * @return a highlighter used for matching, guaranteed to be not null. - */ - protected AbstractHighlighter getMatchHighlighter() { - if (matchHighlighter == null) { - matchHighlighter = createMatchHighlighter(); - } - return matchHighlighter; - } - - /** - * Creates and returns the Highlighter used as match marker. - * - * @return a highlighter used for matching - */ - protected AbstractHighlighter createMatchHighlighter() { - return new ColorHighlighter(HighlightPredicate.NEVER, Color.YELLOW.brighter(), - null, Color.YELLOW.brighter(), - null); - } - - - /** - * Configures and returns the match highlighter for the current match. - * - * @return a highlighter configured for matching - */ - protected AbstractHighlighter getConfiguredMatchHighlighter() { - AbstractHighlighter searchHL = getMatchHighlighter(); - searchHL.setHighlightPredicate(createMatchPredicate()); - return searchHL; - } - - /** - * Creates and returns a HighlightPredicate appropriate for the current - * search result. - * - * @return a HighlightPredicate appropriate for the current search result. - */ - protected HighlightPredicate createMatchPredicate() { - return hasMatch() ? - new SearchPredicate(lastSearchResult.pattern, lastSearchResult.foundRow, - convertColumnIndexToModel(lastSearchResult.foundColumn)) - : HighlightPredicate.NEVER; - } - - /** - * A convenience class to hold search state.

                          - * - * NOTE: this is still in-flow, probably will take more responsibility/ - * or even change altogether on further factoring - */ - public static class SearchResult { - int foundRow; - int foundColumn; - MatchResult matchResult; - Pattern pattern; - - /** - * Instantiates an empty SearchResult. - */ - public SearchResult() { - reset(); - } - - /** - * Instantiates a SearchResult with the given state. - * - * @param ex the Pattern used for matching - * @param result the current MatchResult - * @param row the row index of the current match - * @param column the column index of the current match - */ - public SearchResult(Pattern ex, MatchResult result, int row, int column) { - pattern = ex; - matchResult = result; - foundRow = row; - foundColumn = column; - } - - /** - * Sets internal state to the same as the given SearchResult. Resets internals - * if the param is null. - * - * @param searchResult the SearchResult to copy internal state from. - */ - public void updateFrom(SearchResult searchResult) { - if (searchResult == null) { - reset(); - return; - } - foundRow = searchResult.foundRow; - foundColumn = searchResult.foundColumn; - matchResult = searchResult.matchResult; - pattern = searchResult.pattern; - } - - /** - * Returns the regex of the Pattern used for matching. - * - * @return the regex of the Pattern used for matching. - */ - public String getRegEx() { - return pattern != null ? pattern.pattern() : null; - } - - /** - * Resets all internal state to no-match. - */ - public void reset() { - foundRow= -1; - foundColumn = -1; - matchResult = null; - pattern = null; - } - - /** - * Resets the column to OFF. - */ - public void resetFoundColumn() { - foundColumn = -1; - } - - /** - * Returns the column index of the match position. - * - * @return the column index of the match position. - */ - public int getFoundColumn() { - return foundColumn; - } - - /** - * Returns the row index of the match position. - * - * @return the row index of the match position. - */ - public int getFoundRow() { - return foundRow; - } - - /** - * Returns the MatchResult representing the current match. - * - * @return the MatchResult representing the current match. - */ - public MatchResult getMatchResult() { - return matchResult; - } - - /** - * Returns the Pattern used for matching. - * - * @return the Pattern used for the matching. - */ - public Pattern getPattern() { - return pattern; - } - - } - -} diff --git a/src/main/java/org/jdesktop/swingx/search/ListSearchable.java b/src/main/java/org/jdesktop/swingx/search/ListSearchable.java deleted file mode 100644 index 44c6debf2c..0000000000 --- a/src/main/java/org/jdesktop/swingx/search/ListSearchable.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * $Id: ListSearchable.java 3166 2009-01-02 13:27:18Z rah003 $ - * - * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.search; - -import org.jdesktop.swingx.JXList; -import org.jdesktop.swingx.decorator.AbstractHighlighter; -import org.jdesktop.swingx.decorator.Highlighter; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class ListSearchable extends AbstractSearchable { - - protected JXList list; - - public ListSearchable(JXList list) { - this.list = list; - } - - @Override - protected void findMatchAndUpdateState(Pattern pattern, int startRow, boolean backwards) { - SearchResult searchResult = null; - if (backwards) { - for (int index = startRow; index >= 0 && searchResult == null; index--) { - searchResult = findMatchAt(pattern, index); - } - } else { - for (int index = startRow; index < getSize() && searchResult == null; index++) { - searchResult = findMatchAt(pattern, index); - } - } - updateState(searchResult); - } - - @Override - protected SearchResult findExtendedMatch(Pattern pattern, int row) { - - return findMatchAt(pattern, row); - } - /** - * Matches the cell content at row/col against the given Pattern. - * Returns an appropriate SearchResult if matching or null if no - * matching - * - * @param pattern - * @param row a valid row index in view coordinates - * @return SearchResult if matched otherwise null - */ - protected SearchResult findMatchAt(Pattern pattern, int row) { - String text = list.getStringAt(row); - if ((text != null) && (text.length() > 0 )) { - Matcher matcher = pattern.matcher(text); - if (matcher.find()) { - return createSearchResult(matcher, row, 0); - } - } - return null; - } - - /** - * {@inheritDoc} - */ - @Override - protected int getSize() { - return list.getElementCount(); - } - - - /** - * {@inheritDoc} - */ - @Override - public JXList getTarget() { - return list; - } - - /** - * {@inheritDoc} - */ - @Override - protected void moveMatchMarker() { - if (markByHighlighter()) { - moveMatchByHighlighter(); - } else { // use selection - moveMatchBySelection(); - } - } - - protected void moveMatchBySelection() { - // PENDING JW: #718-swingx - don't move selection on not found - // complying here is accidental, defaultListSelectionModel doesn't - // clear on -1 but silently does nothing - // isn't doc'ed anywhere - so we back out - if (!hasMatch()) { - return; - } - list.setSelectedIndex(lastSearchResult.foundRow); - list.ensureIndexIsVisible(lastSearchResult.foundRow); - } - - - /** - * use and move the match highlighter. - * PRE: markByHighlighter - * - */ - protected void moveMatchByHighlighter() { - AbstractHighlighter searchHL = getConfiguredMatchHighlighter(); - // no match - if (!hasMatch()) { - return; - } else { - ensureInsertedSearchHighlighters(searchHL); - list.ensureIndexIsVisible(lastSearchResult.foundRow); - } - } - - - - /** - * @param searchHighlighter - */ - @Override - protected void removeHighlighter(Highlighter searchHighlighter) { - list.removeHighlighter(searchHighlighter); - } - - /** - * @return all registered highlighters - */ - @Override - protected Highlighter[] getHighlighters() { - return list.getHighlighters(); - } - - /** - * @param highlighter - */ - @Override - protected void addHighlighter(Highlighter highlighter) { - list.addHighlighter(highlighter); - } - - } \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/search/NativeSearchFieldSupport.java b/src/main/java/org/jdesktop/swingx/search/NativeSearchFieldSupport.java deleted file mode 100644 index ff8552715d..0000000000 --- a/src/main/java/org/jdesktop/swingx/search/NativeSearchFieldSupport.java +++ /dev/null @@ -1,111 +0,0 @@ -package org.jdesktop.swingx.search; - -import org.apache.commons.lang3.SystemUtils; -import org.jdesktop.swingx.plaf.AbstractUIChangeHandler; - -import javax.swing.*; -import java.awt.event.ActionListener; -import java.beans.PropertyChangeEvent; - -/** - * TODO: comment - * - * @author Peter Weishapl - */ -public class NativeSearchFieldSupport { - public static final String FIND_POPUP_PROPERTY = "JTextField.Search.FindPopup"; - public static final String FIND_ACTION_PROPERTY = "JTextField.Search.FindAction"; - public static final String MAC_SEARCH_VARIANT = "search"; - public static final String MAC_TEXT_FIELD_VARIANT_PROPERTY = "JTextField.variant"; - public static final String CANCEL_ACTION_PROPERTY = "JTextField.Search.CancelAction"; - - /** - * @return true if we run Leopard and the Mac Look And Feel. - */ - public static boolean isNativeSearchFieldSupported() { - try { - String versionString = System.getProperty("os.version"); - // Mac versions have the format 10.x or 10.x.x - if (versionString.length() < 4) { - return false; - } - // only the part 10.x is important - versionString = versionString.substring(0, 4); - - return SystemUtils.IS_OS_MAC_OSX && Float.parseFloat(versionString) >= 10.5 - && UIManager.getLookAndFeel().getName().equals("Mac OS X"); - } catch (Exception e) { - // in case the os.version cannot be parsed, we are surely not - // running mac os x. - return false; - } - } - - public static void setSearchField(JTextField txt, boolean isSearchField) { - // Leopard Hack: ensure property change event is triggered, if nothing - // changes. - if (isSearchField == isSearchField(txt)) { - txt.putClientProperty(MAC_TEXT_FIELD_VARIANT_PROPERTY, "_triggerevent_"); - } else if (isSearchField) { - // if we have a search field here, register listener for ui changes - // (leopard hack) - uiChangeHandler.install(txt); - } else { - // if we don't have a search field, we don't need to listen anymore. - uiChangeHandler.uninstall(txt); - } - - if (isSearchField) { - txt.putClientProperty(MAC_TEXT_FIELD_VARIANT_PROPERTY, MAC_SEARCH_VARIANT); - txt.putClientProperty("Quaqua.TextField.style", MAC_SEARCH_VARIANT); - - } else { - txt.putClientProperty(MAC_TEXT_FIELD_VARIANT_PROPERTY, "default"); - txt.putClientProperty("Quaqua.TextField.style", "default"); - } - } - - public static boolean isSearchField(JTextField txt) { - return MAC_SEARCH_VARIANT.equals(txt.getClientProperty(MAC_TEXT_FIELD_VARIANT_PROPERTY)); - } - - public static boolean isNativeSearchField(JTextField txt) { - return isSearchField(txt) && isNativeSearchFieldSupported(); - } - - public static void setFindPopupMenu(JTextField txt, JPopupMenu popupMenu) { - txt.putClientProperty(FIND_POPUP_PROPERTY, popupMenu); - } - - public static JPopupMenu getFindPopupMenu(JTextField txt) { - return (JPopupMenu) txt.getClientProperty(FIND_POPUP_PROPERTY); - } - - public static void setFindAction(JTextField txt, ActionListener findAction) { - txt.putClientProperty(FIND_ACTION_PROPERTY, findAction); - } - - public static ActionListener getFindAction(JTextField txt) { - return (ActionListener) txt.getClientProperty(FIND_ACTION_PROPERTY); - } - - public static void setCancelAction(JTextField txt, ActionListener cancelAction) { - txt.putClientProperty(CANCEL_ACTION_PROPERTY, cancelAction); - } - - public static ActionListener getCancelAction(JTextField txt) { - return (ActionListener) txt.getClientProperty(CANCEL_ACTION_PROPERTY); - } - - private static final SearchFieldUIChangeHandler uiChangeHandler = new SearchFieldUIChangeHandler(); - - private static final class SearchFieldUIChangeHandler extends AbstractUIChangeHandler { - @Override - public void propertyChange(PropertyChangeEvent evt) { - JTextField txt = (JTextField) evt.getSource(); - // Leopard hack to make appear correctly in search variant when - // changing LnF. - setSearchField(txt, isSearchField(txt)); - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/search/PatternMatcher.java b/src/main/java/org/jdesktop/swingx/search/PatternMatcher.java deleted file mode 100644 index 11ed7c6f1e..0000000000 --- a/src/main/java/org/jdesktop/swingx/search/PatternMatcher.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * $Id$ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.search; - -import java.util.regex.Pattern; - -/** - * Implemented by classes that work with {@link Pattern} objects. - - * @author Ramesh Gupta - */ -public interface PatternMatcher { - public Pattern getPattern(); - public void setPattern(Pattern pattern); -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/search/PatternModel.java b/src/main/java/org/jdesktop/swingx/search/PatternModel.java deleted file mode 100644 index dca374874a..0000000000 --- a/src/main/java/org/jdesktop/swingx/search/PatternModel.java +++ /dev/null @@ -1,620 +0,0 @@ -/* - * $Id: PatternModel.java 3472 2009-08-27 13:12:42Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.search; - -import java.beans.PropertyChangeListener; -import java.beans.PropertyChangeSupport; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.regex.Pattern; - -/** - * Presentation Model for Find/Filter Widgets. - *

                          - * - * Compiles and holds a Pattern from rawText. There are different - * predefined strategies to control the compilation: - * - *

                            - *
                          • TODO: list and explain - *
                          - * - * Holds state for controlling the match process - * for both find and filter (TODO - explain). - * Relevant in all - * - *
                            - *
                          • caseSensitive - - *
                          • empty - true if there's no searchString - *
                          • incremental - a hint to clients to react immediately - * to pattern changes. - * - *
                          - * - * Relevant in find contexts: - *
                            - *
                          • backwards - search direction if used in a find context - *
                          • wrapping - wrap over the end/start if not found - *
                          • foundIndex - storage for last found index - *
                          • autoAdjustFoundIndex - flag to indicate auto-incr/decr of foundIndex on setting. - * Here the property correlates to !isIncremental() - to simplify batch vs. - * incremental search ui. - *
                          - * - * - * JW: Work-in-progress - Anchors will be factored into AnchoredSearchMode - * Anchors By default, the scope of the pattern relative to strings - * being tested are unanchored, ie, the pattern will match any part of the - * tested string. Traditionally, special characters ('^' and '$') are used to - * describe patterns that match the beginning (or end) of a string. If those - * characters are included in the pattern, the regular expression will honor - * them. However, for ease of use, two properties are included in this model - * that will determine how the pattern will be evaluated when these characters - * are omitted. - *

                          - * The StartAnchored property determines if the pattern must match from - * the beginning of tested strings, or if the pattern can appear anywhere in the - * tested string. Likewise, the EndAnchored property determines if the - * pattern must match to the end of the tested string, or if the end of the - * pattern can appear anywhere in the tested string. The default values (false - * in both cases) correspond to the common database 'LIKE' operation, where the - * pattern is considered to be a match if any part of the tested string matches - * the pattern. - * - * @author Jeanette Winzenburg - * @author David Hall - */ -public class PatternModel { - - /** - * The prefix marker to find component related properties in the - * resourcebundle. - */ - public static final String SEARCH_PREFIX = "Search."; - - /* - * TODO: use Enum for strategy. - */ - public static final String REGEX_UNCHANGED = "regex"; - - public static final String REGEX_ANCHORED = "anchored"; - - public static final String REGEX_WILDCARD = "wildcard"; - - public static final String REGEX_MATCH_RULES = "explicit"; - - /* - * TODO: use Enum for rules. - */ - public static final String MATCH_RULE_CONTAINS = "contains"; - - public static final String MATCH_RULE_EQUALS = "equals"; - - public static final String MATCH_RULE_ENDSWITH = "endsWith"; - - public static final String MATCH_RULE_STARTSWITH = "startsWith"; - - public static final String MATCH_BACKWARDS_ACTION_COMMAND = "backwardsSearch"; - - public static final String MATCH_WRAP_ACTION_COMMAND = "wrapSearch"; - - public static final String MATCH_CASE_ACTION_COMMAND = "matchCase"; - - public static final String MATCH_INCREMENTAL_ACTION_COMMAND = "matchIncremental"; - - - private String rawText; - - private boolean backwards; - - private Pattern pattern; - - private int foundIndex = -1; - - private boolean caseSensitive; - - private PropertyChangeSupport propertySupport; - - private String regexCreatorKey; - - private RegexCreator regexCreator; - - private boolean wrapping; - - private boolean incremental; - - -//---------------------- misc. properties not directly related to Pattern. - - public int getFoundIndex() { - return foundIndex; - } - - public void setFoundIndex(int foundIndex) { - int old = getFoundIndex(); - updateFoundIndex(foundIndex); - firePropertyChange("foundIndex", old, getFoundIndex()); - } - - /** - * - * @param newFoundIndex - */ - protected void updateFoundIndex(int newFoundIndex) { - if (newFoundIndex < 0) { - this.foundIndex = newFoundIndex; - return; - } - if (isAutoAdjustFoundIndex()) { - foundIndex = backwards ? newFoundIndex -1 : newFoundIndex + 1; - } else { - foundIndex = newFoundIndex; - } - - } - - public boolean isAutoAdjustFoundIndex() { - return !isIncremental(); - } - - public boolean isBackwards() { - return backwards; - } - - public void setBackwards(boolean backwards) { - boolean old = isBackwards(); - this.backwards = backwards; - firePropertyChange("backwards", old, isBackwards()); - setFoundIndex(getFoundIndex()); - } - - public boolean isWrapping() { - return wrapping; - } - - public void setWrapping(boolean wrapping) { - boolean old = isWrapping(); - this.wrapping = wrapping; - firePropertyChange("wrapping", old, isWrapping()); - } - - public void setIncremental(boolean incremental) { - boolean old = isIncremental(); - this.incremental = incremental; - firePropertyChange("incremental", old, isIncremental()); - } - - public boolean isIncremental() { - return incremental; - } - - - public boolean isCaseSensitive() { - return caseSensitive; - } - - public void setCaseSensitive(boolean caseSensitive) { - boolean old = isCaseSensitive(); - this.caseSensitive = caseSensitive; - updatePattern(caseSensitive); - firePropertyChange("caseSensitive", old, isCaseSensitive()); - } - - public Pattern getPattern() { - return pattern; - } - - public String getRawText() { - return rawText; - } - - public void setRawText(String findText) { - String old = getRawText(); - boolean oldEmpty = isEmpty(); - this.rawText = findText; - updatePattern(createRegEx(findText)); - firePropertyChange("rawText", old, getRawText()); - firePropertyChange("empty", oldEmpty, isEmpty()); - } - - public boolean isEmpty() { - return isEmpty(getRawText()); - } - - /** - * returns a regEx for compilation into a pattern. Here: either a "contains" - * (== partial find) or null if the input was empty. - * - * @param searchString - * @return null if the input was empty, or a regex according to the internal - * rules - */ - private String createRegEx(String searchString) { - if (isEmpty(searchString)) - return null; //".*"; - return getRegexCreator().createRegEx(searchString); - } - - /** - * - * @param s - * @return - */ - - private boolean isEmpty(String text) { - return (text == null) || (text.length() == 0); - } - - private void updatePattern(String regEx) { - Pattern old = getPattern(); - if (isEmpty(regEx)) { - pattern = null; - } else if ((old == null) || (!old.pattern().equals(regEx))) { - pattern = Pattern.compile(regEx, getFlags()); - } - firePropertyChange("pattern", old, getPattern()); - } - - private int getFlags() { - return isCaseSensitive() ? 0 : getCaseInsensitiveFlag(); - } - - private int getCaseInsensitiveFlag() { - return Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE; - } - - private void updatePattern(boolean caseSensitive) { - if (pattern == null) - return; - Pattern old = getPattern(); - int flags = old.flags(); - int flag = getCaseInsensitiveFlag(); - if ((caseSensitive) && ((flags & flag) != 0)) { - pattern = Pattern.compile(pattern.pattern(), 0); - } else if (!caseSensitive && ((flags & flag) == 0)) { - pattern = Pattern.compile(pattern.pattern(), flag); - } - firePropertyChange("pattern", old, getPattern()); - } - - public void addPropertyChangeListener(PropertyChangeListener l) { - if (propertySupport == null) { - propertySupport = new PropertyChangeSupport(this); - } - propertySupport.addPropertyChangeListener(l); - } - - public void removePropertyChangeListener(PropertyChangeListener l) { - if (propertySupport == null) - return; - propertySupport.removePropertyChangeListener(l); - } - - protected void firePropertyChange(String name, Object oldValue, - Object newValue) { - if (propertySupport == null) - return; - propertySupport.firePropertyChange(name, oldValue, newValue); - } - - /** - * Responsible for converting a "raw text" into a valid - * regular expression in the context of a set of rules. - * - */ - public static class RegexCreator { - protected String matchRule; - private List rules; - - public String getMatchRule() { - if (matchRule == null) { - matchRule = getDefaultMatchRule(); - } - return matchRule; - } - - public boolean isAutoDetect() { - return false; - } - - public String createRegEx(String searchString) { - if (MATCH_RULE_CONTAINS.equals(getMatchRule())) { - return createContainedRegEx(searchString); - } - if (MATCH_RULE_EQUALS.equals(getMatchRule())) { - return createEqualsRegEx(searchString); - } - if (MATCH_RULE_STARTSWITH.equals(getMatchRule())){ - return createStartsAnchoredRegEx(searchString); - } - if (MATCH_RULE_ENDSWITH.equals(getMatchRule())) { - return createEndAnchoredRegEx(searchString); - } - return searchString; - } - - protected String createEndAnchoredRegEx(String searchString) { - return Pattern.quote(searchString) + "$"; - } - - protected String createStartsAnchoredRegEx(String searchString) { - return "^" + Pattern.quote(searchString); - } - - protected String createEqualsRegEx(String searchString) { - return "^" + Pattern.quote(searchString) + "$"; - } - - protected String createContainedRegEx(String searchString) { - return Pattern.quote(searchString); - } - - public void setMatchRule(String category) { - this.matchRule = category; - } - - protected String getDefaultMatchRule() { - return MATCH_RULE_CONTAINS; - } - - public List getMatchRules() { - if (rules == null) { - rules = createAndInitRules(); - } - return rules; - } - - private List createAndInitRules() { - if (!supportsRules()) return Collections.emptyList(); - List list = new ArrayList(); - list.add(MATCH_RULE_CONTAINS); - list.add(MATCH_RULE_EQUALS); - list.add(MATCH_RULE_STARTSWITH); - list.add(MATCH_RULE_ENDSWITH); - return list; - } - - private boolean supportsRules() { - return true; - } - } - - - /** - * Support for anchored input. - * - * PENDING: NOT TESTED - simply moved! - * Need to define requirements... - * - */ - public static class AnchoredSearchMode extends RegexCreator { - - @Override - public boolean isAutoDetect() { - return true; - } - - @Override - public String createRegEx(String searchExp) { - if (isAutoDetect()) { - StringBuffer buf = new StringBuffer(searchExp.length() + 4); - if (!hasStartAnchor(searchExp)) { - if (isStartAnchored()) { - buf.append("^"); - } - } - - //PENDING: doesn't escape contained regex metacharacters... - buf.append(searchExp); - - if (!hasEndAnchor(searchExp)) { - if (isEndAnchored()) { - buf.append("$"); - } - } - - return buf.toString(); - } - return super.createRegEx(searchExp); - } - - private boolean hasStartAnchor(String str) { - return str.startsWith("^"); - } - - private boolean hasEndAnchor(String str) { - int len = str.length(); - if ((str.charAt(len - 1)) != '$') - return false; - - // the string "$" is anchored - if (len == 1) - return true; - - // scan backwards along the string: if there's an odd number - // of backslashes, then the last escapes the dollar and the - // pattern is not anchored. if there's an even number, then - // the dollar is unescaped and the pattern is anchored. - for (int n = len - 2; n >= 0; --n) - if (str.charAt(n) != '\\') - return (len - n) % 2 == 0; - - // The string is of the form "\+$". If the length is an odd - // number (ie, an even number of '\' and a '$') the pattern is - // anchored - return len % 2 != 0; - } - - - /** - * returns true if the pattern must match from the beginning of the string, - * or false if the pattern can match anywhere in a string. - */ - public boolean isStartAnchored() { - return MATCH_RULE_EQUALS.equals(getMatchRule()) || - MATCH_RULE_STARTSWITH.equals(getMatchRule()); - } - // -// /** -// * sets the default interpretation of the pattern for strings it will later -// * be given. Setting this value to true will force the pattern to match from -// * the beginning of tested strings. Setting this value to false will allow -// * the pattern to match any part of a tested string. -// */ -// public void setStartAnchored(boolean startAnchored) { -// boolean old = isStartAnchored(); -// this.startAnchored = startAnchored; -// updatePattern(createRegEx(getRawText())); -// firePropertyChange("startAnchored", old, isStartAnchored()); -// } - // - /** - * returns true if the pattern must match from the beginning of the string, - * or false if the pattern can match anywhere in a string. - */ - public boolean isEndAnchored() { - return MATCH_RULE_EQUALS.equals(getMatchRule()) || - MATCH_RULE_ENDSWITH.equals(getMatchRule()); - } - // -// /** -// * sets the default interpretation of the pattern for strings it will later -// * be given. Setting this value to true will force the pattern to match the -// * end of tested strings. Setting this value to false will allow the pattern -// * to match any part of a tested string. -// */ -// public void setEndAnchored(boolean endAnchored) { -// boolean old = isEndAnchored(); -// this.endAnchored = endAnchored; -// updatePattern(createRegEx(getRawText())); -// firePropertyChange("endAnchored", old, isEndAnchored()); -// } - // -// public boolean isStartEndAnchored() { -// return isEndAnchored() && isStartAnchored(); -// } -// -// /** -// * sets the default interpretation of the pattern for strings it will later -// * be given. Setting this value to true will force the pattern to match the -// * end of tested strings. Setting this value to false will allow the pattern -// * to match any part of a tested string. -// */ -// public void setStartEndAnchored(boolean endAnchored) { -// boolean old = isStartEndAnchored(); -// this.endAnchored = endAnchored; -// this.startAnchored = endAnchored; -// updatePattern(createRegEx(getRawText())); -// firePropertyChange("StartEndAnchored", old, isStartEndAnchored()); -// } - } - /** - * Set the strategy to use for compiling a pattern from - * rawtext. - * - * NOTE: This is imcomplete (in fact it wasn't implemented at - * all) - only recognizes REGEX_ANCHORED, every other value - * results in REGEX_MATCH_RULES. - * - * @param mode the String key of the match strategy to use. - */ - public void setRegexCreatorKey(String mode) { - if (getRegexCreatorKey().equals(mode)) return; - String old = getRegexCreatorKey(); - regexCreatorKey = mode; - createRegexCreator(getRegexCreatorKey()); - firePropertyChange("regexCreatorKey", old, getRegexCreatorKey()); - - } - - /** - * Creates and sets the strategy to use for compiling a pattern from - * rawtext. - * - * NOTE: This is imcomplete (in fact it wasn't implemented at - * all) - only recognizes REGEX_ANCHORED, every other value - * results in REGEX_MATCH_RULES. - * - * @param mode the String key of the match strategy to use. - */ - protected void createRegexCreator(String mode) { - if (REGEX_ANCHORED.equals(mode)) { - setRegexCreator(new AnchoredSearchMode()); - } else { - setRegexCreator(new RegexCreator()); - } - - } - - public String getRegexCreatorKey() { - if (regexCreatorKey == null) { - regexCreatorKey = getDefaultRegexCreatorKey(); - } - return regexCreatorKey; - } - - private String getDefaultRegexCreatorKey() { - return REGEX_MATCH_RULES; - } - - private RegexCreator getRegexCreator() { - if (regexCreator == null) { - regexCreator = new RegexCreator(); - } - return regexCreator; - } - - /** - * This is a quick-fix to allow custom strategies for compiling - * rawtext to patterns. - * - * @param regexCreator the strategy to use for compiling text - * into pattern. - */ - public void setRegexCreator(RegexCreator regexCreator) { - Object old = this.regexCreator; - this.regexCreator = regexCreator; - firePropertyChange("regexCreator", old, regexCreator); - } - - public void setMatchRule(String category) { - if (getMatchRule().equals(category)) { - return; - } - String old = getMatchRule(); - getRegexCreator().setMatchRule(category); - updatePattern(createRegEx(getRawText())); - firePropertyChange("matchRule", old, getMatchRule()); - } - - public String getMatchRule() { - return getRegexCreator().getMatchRule(); - } - - public List getMatchRules() { - return getRegexCreator().getMatchRules(); - } - - - - -} diff --git a/src/main/java/org/jdesktop/swingx/search/RecentSearches.java b/src/main/java/org/jdesktop/swingx/search/RecentSearches.java deleted file mode 100644 index ad6b06785e..0000000000 --- a/src/main/java/org/jdesktop/swingx/search/RecentSearches.java +++ /dev/null @@ -1,364 +0,0 @@ -package org.jdesktop.swingx.search; - -import org.jdesktop.swingx.JXSearchField; -import org.jdesktop.swingx.plaf.UIManagerExt; - -import javax.swing.*; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.security.AccessControlException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.logging.Logger; -import java.util.prefs.BackingStoreException; -import java.util.prefs.Preferences; - -/** - * Maintains a list of recent searches and persists this list automatically - * using {@link Preferences}. A recent searches popup menu can be installed on - * a {@link JXSearchField} using {@link #install(JXSearchField)}. - * - * @author Peter Weishapl - * - */ -public class RecentSearches implements ActionListener { - private Preferences prefsNode; - - private int maxRecents = 5; - - private List recentSearches = new ArrayList(); - - private List listeners = new ArrayList(); - - /** - * Creates a list of recent searches and uses saveName to - * persist this list under the {@link Preferences} user root node. Existing - * entries will be loaded automatically. - * - * @param saveName - * a unique name for saving this list of recent searches - */ - public RecentSearches(String saveName) { - this(null, saveName); - } - - /** - * Creates a list of recent searches and uses saveName to - * persist this list under the prefs node. Existing entries - * will be loaded automatically. - * - * @param prefsNode - * the preferences node under which this list will be persisted. - * If prefsNode is null the preferences node will - * be set to the user root node - * @param saveName - * a unique name for saving this list of recent searches. If - * saveName is null, the list will not be - * persisted - */ - public RecentSearches(Preferences prefs, String saveName) { - if (prefs == null) { - try { - prefs = Preferences.userRoot(); - } catch (AccessControlException ace) { - // disable persistency, if we aren't allowed to access - // preferences. - Logger.getLogger(getClass().getName()).warning("cannot acces preferences. persistency disabled."); - } - } - - if (prefs != null && saveName != null) { - this.prefsNode = prefs.node(saveName); - load(); - } - } - - private void load() { - // load persisted entries - try { - String[] recent = new String[prefsNode.keys().length]; - for (String key : prefsNode.keys()) { - recent[prefsNode.getInt(key, -1)] = key; - } - recentSearches.addAll(Arrays.asList(recent)); - } catch (Exception ex) { - // ignore - } - } - - private void save() { - if (prefsNode == null) { - return; - } - - try { - prefsNode.clear(); - } catch (BackingStoreException e) { - // ignore - } - - int i = 0; - for (String search : recentSearches) { - prefsNode.putInt(search, i++); - } - } - - /** - * Add a search string as the first element. If the search string is - * null or empty nothing will be added. If the search string - * already exists, the old element will be removed. The modified list will - * automatically be persisted. - * - * If the number of elements exceeds the maximum number of entries, the last - * entry will be removed. - * - * @see #getMaxRecents() - * @param searchString - * the search string to add - */ - public void put(String searchString) { - if (searchString == null || searchString.trim().length() == 0) { - return; - } - - int lastIndex = recentSearches.indexOf(searchString); - if (lastIndex != -1) { - recentSearches.remove(lastIndex); - } - recentSearches.add(0, searchString); - if (getLength() > getMaxRecents()) { - recentSearches.remove(recentSearches.size() - 1); - } - save(); - fireChangeEvent(); - } - - /** - * Returns all recent searches in this list. - * - * @return the recent searches - */ - public String[] getRecentSearches() { - return recentSearches.toArray(new String[] {}); - } - - /** - * The number of recent searches. - * - * @return number of recent searches - */ - public int getLength() { - return recentSearches.size(); - } - - /** - * Remove all recent searches. - */ - public void removeAll() { - recentSearches.clear(); - save(); - fireChangeEvent(); - } - - /** - * Returns the maximum number of recent searches. - * - * @see #put(String) - * @return the maximum number of recent searches - */ - public int getMaxRecents() { - return maxRecents; - } - - /** - * Set the maximum number of recent searches. - * - * @see #put(String) - * @param maxRecents - * maximum number of recent searches - */ - public void setMaxRecents(int maxRecents) { - this.maxRecents = maxRecents; - } - - /** - * Add a change listener. A {@link ChangeEvent} will be fired whenever a - * search is added or removed. - * - * @param l - * the {@link ChangeListener} - */ - public void addChangeListener(ChangeListener l) { - listeners.add(l); - } - - /** - * Remove a change listener. - * - * @param l - * a registered {@link ChangeListener} - */ - public void removeChangeListener(ChangeListener l) { - listeners.remove(l); - } - - /** - * Returns all registered {@link ChangeListener}s. - * - * @return all registered {@link ChangeListener}s - */ - public ChangeListener[] getChangeListeners() { - return listeners.toArray(new ChangeListener[] {}); - } - - private void fireChangeEvent() { - ChangeEvent e = new ChangeEvent(this); - - for (ChangeListener l : listeners) { - l.stateChanged(e); - } - } - - /** - * Creates the recent searches popup menu which will be used by - * {@link #install(JXSearchField)} to set a search popup menu on - * searchField. - * - * Override to return a custom popup menu. - * - * @param searchField - * the search field the returned popup menu will be installed on - * @return the recent searches popup menu - */ - protected JPopupMenu createPopupMenu(JTextField searchField) { - return new RecentSearchesPopup(this, searchField); - } - - /** - * Install a recent the searches popup menu returned by - * {@link #createPopupMenu(JXSearchField)} on searchField. - * Also registers an {@link ActionListener} on searchField - * and adds the search string to the list of recent searches whenever a - * {@link ActionEvent} is received. - * - * Uses {@link NativeSearchFieldSupport} to achieve compatibility with the native - * search field support provided by the Mac Look And Feel since Mac OS 10.5. - * - * @param searchField - * the search field to install a recent searches popup menu on - */ - public void install(JTextField searchField) { - searchField.addActionListener(this); - NativeSearchFieldSupport.setFindPopupMenu(searchField, createPopupMenu(searchField)); - } - - /** - * Remove the recent searches popup from searchField when - * installed and stop listening for {@link ActionEvent}s fired by the - * search field. - * - * @param searchField - * uninstall recent searches popup menu - */ - public void uninstall(JXSearchField searchField) { - searchField.removeActionListener(this); - if (searchField.getFindPopupMenu() instanceof RecentSearchesPopup) { - removeChangeListener((ChangeListener) searchField.getFindPopupMenu()); - searchField.setFindPopupMenu(null); - } - } - - /** - * Calls {@link #put(String)} with the {@link ActionEvent}s action command - * as the search string. - */ - @Override - public void actionPerformed(ActionEvent e) { - put(e.getActionCommand()); - } - - /** - * The popup menu returned by - * {@link RecentSearches#createPopupMenu(JXSearchField)}. - */ - public static class RecentSearchesPopup extends JPopupMenu implements ActionListener, ChangeListener { - private RecentSearches recentSearches; - - private JTextField searchField; - - private JMenuItem clear; - - /** - * Creates a new popup menu based on the given {@link RecentSearches} - * and {@link JXSearchField}. - * - * @param recentSearches - * @param searchField - */ - public RecentSearchesPopup(RecentSearches recentSearches, JTextField searchField) { - this.searchField = searchField; - this.recentSearches = recentSearches; - - recentSearches.addChangeListener(this); - buildMenu(); - } - - /** - * Rebuilds the menu according to the recent searches. - */ - private void buildMenu() { - setVisible(false); - removeAll(); - - if (recentSearches.getLength() == 0) { - JMenuItem noRecent = new JMenuItem(UIManagerExt.getString("SearchField.noRecentsText")); - noRecent.setEnabled(false); - add(noRecent); - } else { - JMenuItem recent = new JMenuItem(UIManagerExt.getString("SearchField.recentsMenuTitle")); - recent.setEnabled(false); - add(recent); - - for (String searchString : recentSearches.getRecentSearches()) { - JMenuItem mi = new JMenuItem(searchString); - mi.addActionListener(this); - add(mi); - } - - addSeparator(); - clear = new JMenuItem(UIManagerExt.getString("SearchField.clearRecentsText")); - clear.addActionListener(this); - add(clear); - } - } - - /** - * Sets {@link #searchField}s text to the {@link ActionEvent}s action - * command and call {@link JXSearchField#postActionEvent()} to fire an - * {@link ActionEvent}, if es source is not the clear - * menu item. If the source is the clear menu item, all recent searches - * will be removed. - */ - @Override - public void actionPerformed(ActionEvent e) { - if (e.getSource() == clear) { - recentSearches.removeAll(); - } else { - searchField.setText(e.getActionCommand()); - searchField.postActionEvent(); - } - } - - /** - * Every time the recent searches fires a {@link ChangeEvent} call - * {@link #buildMenu()} to rebuild the whole menu. - */ - @Override - public void stateChanged(ChangeEvent e) { - buildMenu(); - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/search/SearchFactory.java b/src/main/java/org/jdesktop/swingx/search/SearchFactory.java deleted file mode 100644 index e02c7dc104..0000000000 --- a/src/main/java/org/jdesktop/swingx/search/SearchFactory.java +++ /dev/null @@ -1,549 +0,0 @@ -/* - * $Id: SearchFactory.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.search; - -import org.jdesktop.swingx.*; -import org.jdesktop.swingx.plaf.LookAndFeelAddons; -import org.jdesktop.swingx.plaf.UIDependent; -import org.jdesktop.swingx.util.Utilities; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.lang.ref.WeakReference; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; - -/** - * Factory to create, configure and show application consistent - * search and find widgets. - * - * Typically a shared JXFindBar is used for incremental search, while - * a shared JXFindPanel is used for batch search. This implementation - * - *

                            - *
                          • JXFindBar - adds and shows it in the target's toplevel container's - * toolbar (assuming a JXRootPane) - *
                          • JXFindPanel - creates a JXDialog, adds and shows the findPanel in the - * Dialog - *
                          - * - * - * PENDING: JW - update (?) views/wiring on focus change. Started brute force - - * stop searching. This looks extreme confusing for findBars added to ToolBars - * which are empty except for the findbar. Weird problem if triggered from - * menu - find widget disappears after having been shown for an instance. - * Where's the focus? - * - * - * PENDING: add methods to return JXSearchPanels (for use by PatternMatchers). - * - * @author Jeanette Winzenburg - */ -public class SearchFactory implements UIDependent { - private static class LaFListener implements PropertyChangeListener { - private final WeakReference ref; - - public LaFListener(SearchFactory sf) { - this.ref = new WeakReference(sf); - } - - /** - * {@inheritDoc} - */ - @Override - public void propertyChange(PropertyChangeEvent evt) { - SearchFactory sf = ref.get(); - - if (sf == null) { - UIManager.removePropertyChangeListener(this); - } else if ("lookAndFeel".equals(evt.getPropertyName())) { - sf.updateUI(); - } - } - } - - // PENDING: rename methods to batch/incremental instead of dialog/toolbar - - static { - // Hack to enforce loading of SwingX framework ResourceBundle - LookAndFeelAddons.getAddon(); - } - - private static SearchFactory searchFactory; - - - /** the shared find widget for batch-find. */ - protected JXFindPanel findPanel; - - /** the shared find widget for incremental-find. */ - protected JXFindBar findBar; - /** this is a temporary hack: need to remove the useSearchHighlighter property. */ - protected JComponent lastFindBarTarget; - - private boolean useFindBar; - - private Point lastFindDialogLocation; - - private FindRemover findRemover; - - /** - * Returns the shared SearchFactory. - * - * @return the shared SearchFactory - */ - public static SearchFactory getInstance() { - if (searchFactory == null) { - searchFactory = new SearchFactory(); - } - return searchFactory; - } - - /** - * Sets the shared SearchFactory. - * - * @param factory - */ - public static void setInstance(SearchFactory factory) { - searchFactory = factory; - } - - public SearchFactory() { - UIManager.addPropertyChangeListener(new LaFListener(this)); - } - - /** - * Returns a common Keystroke for triggering - * a search. Tries to be OS-specific.

                          - * - * PENDING: this should be done in the LF and the - * keyStroke looked up in the UIManager. - * - * @return the keyStroke to register with a findAction. - */ - public KeyStroke getSearchAccelerator() { - // JW: this should be handled by the LF! - // get the accelerator mnemonic from the UIManager - String findMnemonic = "F"; - KeyStroke findStroke = Utilities.stringToKey("D-" + findMnemonic); - // fallback for sandbox (this should be handled in Utilities instead!) - if (findStroke == null) { - findStroke = KeyStroke.getKeyStroke("control F"); - } - return findStroke; - - } - /** - * Returns decision about using a batch- vs. incremental-find for the - * searchable. This implementation returns the useFindBar property directly. - * - * @param target - the component associated with the searchable - * @param searchable - the object to search. - * @return true if a incremental-find should be used, false otherwise. - */ - public boolean isUseFindBar(JComponent target, Searchable searchable) { - return useFindBar; - } - - /** - * Sets the default search type to incremental or batch, for a - * true/false boolean. The default value is false (== batch). - * - * @param incremental a boolean to indicate the default search - * type, true for incremental and false for batch. - */ - public void setUseFindBar(boolean incremental) { - if (incremental == useFindBar) return; - this.useFindBar = incremental; - getFindRemover().endSearching(); - } - - - /** - * Shows an appropriate find widget targeted at the searchable. - * Opens a batch-find or incremental-find - * widget based on the return value of isUseFindBar. - * - * @param target - the component associated with the searchable - * @param searchable - the object to search. - * - * @see #isUseFindBar(JComponent, Searchable) - * @see #setUseFindBar(boolean) - */ - public void showFindInput(JComponent target, Searchable searchable) { - if (isUseFindBar(target, searchable)) { - showFindBar(target, searchable); - } else { - showFindDialog(target, searchable); - } - } - -//------------------------- incremental search - - /** - * Show a incremental-find widget targeted at the searchable. - * - * This implementation uses a JXFindBar and inserts it into the - * target's toplevel container toolbar. - * - * PENDING: Nothing shown if there is no toolbar found. - * - * @param target - the component associated with the searchable - * @param searchable - the object to search. - */ - public void showFindBar(JComponent target, Searchable searchable) { - if (target == null) return; - if (findBar == null) { - findBar = getSharedFindBar(); - } else { - releaseFindBar(); - } - Window topLevel = SwingUtilities.getWindowAncestor(target); - if (topLevel instanceof JXFrame) { - JXRootPane rootPane = ((JXFrame) topLevel).getRootPaneExt(); - JToolBar toolBar = rootPane.getToolBar(); - if (toolBar == null) { - toolBar = new JToolBar(); - rootPane.setToolBar(toolBar); - } - toolBar.add(findBar, 0); - rootPane.revalidate(); - KeyboardFocusManager.getCurrentKeyboardFocusManager().focusNextComponent(findBar); - - } - lastFindBarTarget = target; - findBar.setLocale(target.getLocale()); - target.putClientProperty(AbstractSearchable.MATCH_HIGHLIGHTER, Boolean.TRUE); - getSharedFindBar().setSearchable(searchable); - installFindRemover(target, findBar); - } - - /** - * Returns the shared JXFindBar. Creates and configures on - * first call. - * - * @return the shared JXFindBar - */ - public JXFindBar getSharedFindBar() { - if (findBar == null) { - findBar = createFindBar(); - configureSharedFindBar(); - } - return findBar; - } - - /** - * Factory method to create a JXFindBar. - * - * @return the JXFindBar - */ - public JXFindBar createFindBar() { - return new JXFindBar(); - } - - - protected void installFindRemover(Container target, Container findWidget) { - if (target != null) { - getFindRemover().addTarget(target); - } - getFindRemover().addTarget(findWidget); - } - - private FindRemover getFindRemover() { - if (findRemover == null) { - findRemover = new FindRemover(); - } - return findRemover; - } - - /** - * convenience method to remove a component from its parent - * and revalidate the parent - */ - protected void removeFromParent(JComponent component) { - Container oldParent = component.getParent(); - if (oldParent != null) { - oldParent.remove(component); - if (oldParent instanceof JComponent) { - ((JComponent) oldParent).revalidate(); - } else { - // not sure... never have non-j comps - oldParent.invalidate(); - oldParent.validate(); - } - } - } - - protected void stopSearching() { - if (findPanel != null) { - lastFindDialogLocation = hideSharedFindPanel(false); - findPanel.setSearchable(null); - } - if (findBar != null) { - releaseFindBar(); - } - } - - /** - * Pre: findbar != null. - */ - protected void releaseFindBar() { - findBar.setSearchable(null); - if (lastFindBarTarget != null) { - lastFindBarTarget.putClientProperty(AbstractSearchable.MATCH_HIGHLIGHTER, Boolean.FALSE); - lastFindBarTarget = null; - } - removeFromParent(findBar); - } - - - /** - * Configures the shared FindBar. This method is - * called once after creation of the shared FindBar. - * Subclasses can override to add configuration code.

                          - * - * Here: registers a custom action to remove the - * findbar from its ancestor container. - * - * PRE: findBar != null. - * - */ - protected void configureSharedFindBar() { - Action removeAction = new AbstractAction() { - - @Override - public void actionPerformed(ActionEvent e) { - removeFromParent(findBar); -// stopSearching(); -// releaseFindBar(); - - } - - }; - findBar.getActionMap().put(JXDialog.CLOSE_ACTION_COMMAND, removeAction); - } - -//------------------------ batch search - - /** - * Show a batch-find widget targeted at the given Searchable. - * - * This implementation uses a shared JXFindPanel contained - * JXDialog. - * - * @param target - - * the component associated with the searchable - * @param searchable - - * the object to search. - */ - public void showFindDialog(JComponent target, Searchable searchable) { - Window frame = null; //JOptionPane.getRootFrame(); - if (target != null) { - target.putClientProperty(AbstractSearchable.MATCH_HIGHLIGHTER, Boolean.FALSE); - frame = SwingUtilities.getWindowAncestor(target); -// if (window instanceof Frame) { -// frame = (Frame) window; -// } - } - JXDialog topLevel = getDialogForSharedFindPanel(); - JXDialog findDialog; - if ((topLevel != null) && (topLevel.getOwner().equals(frame))) { - findDialog = topLevel; - // JW: #635-swingx - quick hack to update title to current locale ... -// findDialog.setTitle(getSharedFindPanel().getName()); - KeyboardFocusManager.getCurrentKeyboardFocusManager().focusNextComponent(findDialog); - } else { - Point location = hideSharedFindPanel(true); - if (frame instanceof Frame) { - findDialog = new JXDialog((Frame) frame, getSharedFindPanel()); - } else if (frame instanceof Dialog) { - // fix #215-swingx: had problems with secondary modal dialogs. - findDialog = new JXDialog((Dialog) frame, getSharedFindPanel()); - } else { - findDialog = new JXDialog(JOptionPane.getRootFrame(), getSharedFindPanel()); - } - // RJO: shouldn't we avoid overloaded useage like this in a JSR296 world? swap getName() for getTitle() here? -// findDialog.setTitle(getSharedFindPanel().getName()); - // JW: don't - this will stay on top of all applications! - // findDialog.setAlwaysOnTop(true); - findDialog.pack(); - if (location == null) { - findDialog.setLocationRelativeTo(frame); - } else { - findDialog.setLocation(location); - } - } - if (target != null) { - findDialog.setLocale(target.getLocale()); - } - getSharedFindPanel().setSearchable(searchable); - installFindRemover(target, findDialog); - findDialog.setVisible(true); - } - - - /** - * Returns the shared JXFindPanel. Lazyly creates and configures on - * first call. - * - * @return the shared JXFindPanel - */ - public JXFindPanel getSharedFindPanel() { - if (findPanel == null) { - findPanel = createFindPanel(); - configureSharedFindPanel(); - } else { - // JW: temporary hack around #718-swingx - // no longer needed with cleanup of hideSharedFindPanel -// if (findPanel.getParent() == null) { -// SwingUtilities.updateComponentTreeUI(findPanel); -// } - } - return findPanel; - } - - /** - * Factory method to create a JXFindPanel. - * - * @return JXFindPanel - */ - public JXFindPanel createFindPanel() { - return new JXFindPanel(); - } - - - /** - * Configures the shared FindPanel. This method is - * called once after creation of the shared FindPanel. - * Subclasses can override to add configuration code.

                          - * - * Here: no-op - * PRE: findPanel != null. - * - */ - protected void configureSharedFindPanel() { - } - - - - private JXDialog getDialogForSharedFindPanel() { - if (findPanel == null) return null; - Window window = SwingUtilities.getWindowAncestor(findPanel); - return (window instanceof JXDialog) ? (JXDialog) window : null; - } - - - /** - * Hides the findPanel's toplevel window and returns its location. - * If the dispose is true, the findPanel is removed from its parent - * and the toplevel window is disposed. - * - * @param dispose boolean to indicate whether the findPanels toplevel - * window should be disposed. - * @return the location of the window if visible, or the last known - * location. - */ - protected Point hideSharedFindPanel(boolean dispose) { - if (findPanel == null) return null; - Window window = SwingUtilities.getWindowAncestor(findPanel); - Point location = lastFindDialogLocation; - if (window != null) { - // PENDING JW: can't remember why it it removed always? - if (window.isVisible()) { - location = window.getLocationOnScreen(); - window.setVisible(false); - } - if (dispose) { - findPanel.getParent().remove(findPanel); - window.dispose(); - } - } - return location; - } - - public class FindRemover implements PropertyChangeListener { - KeyboardFocusManager focusManager; - Set targets; - - public FindRemover() { - updateManager(); - } - - public void addTarget(Container target) { - getTargets().add(target); - } - - public void removeTarget(Container target) { - getTargets().remove(target); - } - - private Set getTargets() { - if (targets == null) { - targets = new HashSet(); - } - return targets; - } - - private void updateManager() { - if (focusManager != null) { - focusManager.removePropertyChangeListener("permanentFocusOwner", this); - } - this.focusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); - focusManager.addPropertyChangeListener("permanentFocusOwner", this); - } - - @Override - public void propertyChange(PropertyChangeEvent ev) { - - Component c = focusManager.getPermanentFocusOwner(); - if (c == null) return; - for (Iterator iter = getTargets().iterator(); iter.hasNext();) { - Container element = iter.next(); - if ((element == c) || (SwingUtilities.isDescendingFrom(c, element))) { - return; - } - } - endSearching(); - } - - public void endSearching() { - getTargets().clear(); - stopSearching(); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void updateUI() { - if (findBar != null) { - SwingUtilities.updateComponentTreeUI(findBar); - } - - if (findPanel != null) { - SwingUtilities.updateComponentTreeUI(findPanel); - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/search/Searchable.java b/src/main/java/org/jdesktop/swingx/search/Searchable.java deleted file mode 100644 index bc624f3a62..0000000000 --- a/src/main/java/org/jdesktop/swingx/search/Searchable.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * $Id: Searchable.java 2948 2008-06-16 15:02:14Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.search; - -import java.util.regex.Pattern; - -/** - * Interface that used to implement search logic in all the search capable - * components. - * - * @author Ramesh Gupta - */ -public interface Searchable { - - /** - * Search searchString from the beginning of a document. - * - * @param searchString String we should find in a document. - * - * @return index of matched String or -1 if a match cannot be found. - */ - public int search(String searchString); - - /** - * Search searchString from the given position in a document. - * - * @param searchString String we should find in a document. - * @param startIndex Start position in a document or -1 if we want to search from the beginning. - * - * @return index of matched String or -1 if a match cannot be found. - */ - public int search(String searchString, int startIndex); - - /** - * Search searchString in the given direction from the some position in a document. - * - * @param searchString String we should find in a document. - * @param startIndex Start position in a document or -1 if we want to search from the beginning. - * @param backward Indicates search direction, will search from the given position towards the - * beginning of a document if this parameter is true. - * - * @return index of matched String or -1 if a match cannot be found. - */ - public int search(String searchString, int startIndex, boolean backward); - - /** - * Search for the pattern from the beginning of the document. - * - * @param pattern Pattern for search - * - * @return index of matched Pattern or -1 if a match cannot be found. - */ - public int search(Pattern pattern); - - /** - * Search for the pattern from the start index. - * @param pattern Pattern for search - * @param startIndex starting index of search. If -1 then start from the beginning - * @return index of matched pattern or -1 if a match cannot be found. - */ - public int search(Pattern pattern, int startIndex); - - /** - * Search for the pattern from the start index. - * @param pattern Pattern for search - * @param startIndex starting index of search. If -1 then start from the beginning - * @param backward indicates the direction if true then search is backwards - * @return index of matched pattern or -1 if a match cannot be found. - */ - public int search(Pattern pattern, int startIndex, boolean backward); -} diff --git a/src/main/java/org/jdesktop/swingx/search/TableSearchable.java b/src/main/java/org/jdesktop/swingx/search/TableSearchable.java deleted file mode 100644 index 25cba6c544..0000000000 --- a/src/main/java/org/jdesktop/swingx/search/TableSearchable.java +++ /dev/null @@ -1,335 +0,0 @@ -/* - * $Id: TableSearchable.java 3194 2009-01-21 11:39:19Z kleopatra $ - * - * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.search; - -import org.jdesktop.swingx.JXTable; -import org.jdesktop.swingx.decorator.AbstractHighlighter; -import org.jdesktop.swingx.decorator.Highlighter; - -import java.awt.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * An Searchable implementation for use in JXTable. - * - * @author Jeanette Winzenburg - */ -public class TableSearchable extends AbstractSearchable { - - /** The target JXTable. */ - protected JXTable table; - - /** - * Instantiates a TableSearchable with the given table as target. - * - * @param table the JXTable to search. - */ - public TableSearchable(JXTable table) { - this.table = table; - } - - /** - * {@inheritDoc} - *

                          - * - * This implementation loops through the cells in a row to find a match. - */ - @Override - protected void findMatchAndUpdateState(Pattern pattern, int startRow, - boolean backwards) { - SearchResult matchRow = null; - if (backwards) { - // CHECK: off-one end still needed? - // Probably not - the findXX don't have side-effects any longer - // hmmm... still needed: even without side-effects we need to - // guarantee calling the notfound update at the very end of the - // loop. - for (int r = startRow; r >= -1 && matchRow == null; r--) { - matchRow = findMatchBackwardsInRow(pattern, r); - updateState(matchRow); - } - } else { - for (int r = startRow; r <= getSize() && matchRow == null; r++) { - matchRow = findMatchForwardInRow(pattern, r); - updateState(matchRow); - } - } - // KEEP - JW: Needed to update if loop wasn't entered! - // the alternative is to go one off in the loop. Hmm - which is - // preferable? - // updateState(matchRow); - - } - - /** - * {@inheritDoc} - *

                          - * - * Implemented to search for an extension in the cell given by row and - * foundColumn. - */ - @Override - protected SearchResult findExtendedMatch(Pattern pattern, int row) { - return findMatchAt(pattern, row, lastSearchResult.foundColumn); - } - - /** - * Searches forward through columns of the given row. Starts at - * lastFoundColumn or first column if lastFoundColumn < 0. returns an - * appropriate SearchResult if a matching cell is found in this row or null - * if no match is found. A row index out off range results in a no-match. - * - * @param pattern Pattern that we will try to locate - * @param row the row to search - * @return an appropriate SearchResult if a matching cell is - * found in this row or null if no match is found - */ - private SearchResult findMatchForwardInRow(Pattern pattern, int row) { - int startColumn = (lastSearchResult.foundColumn < 0) ? 0 - : lastSearchResult.foundColumn; - if (isValidIndex(row)) { - for (int column = startColumn; column < table.getColumnCount(); column++) { - SearchResult result = findMatchAt(pattern, row, column); - if (result != null) - return result; - } - } - return null; - } - - /** - * Searches forward through columns of the given row. Starts at - * lastFoundColumn or first column if lastFoundColumn < 0. returns an - * appropriate SearchResult if a matching cell is found in this row or null - * if no match is found. A row index out off range results in a no-match. - * - * @param pattern Pattern that we will try to locate - * @param row the row to search - * @return an appropriate SearchResult if a matching cell is - * found in this row or null if no match is found - */ - private SearchResult findMatchBackwardsInRow(Pattern pattern, int row) { - int startColumn = (lastSearchResult.foundColumn < 0) ? table - .getColumnCount() - 1 : lastSearchResult.foundColumn; - if (isValidIndex(row)) { - for (int column = startColumn; column >= 0; column--) { - SearchResult result = findMatchAt(pattern, row, column); - if (result != null) - return result; - } - } - return null; - } - - /** - * Matches the cell content at row/col against the given Pattern. Returns an - * appropriate SearchResult if matching or null if no matching - * - * @param pattern Pattern that we will try to locate - * @param row a valid row index in view coordinates - * @param column a valid column index in view coordinates - * @return an appropriate SearchResult if matching or null - */ - protected SearchResult findMatchAt(Pattern pattern, int row, int column) { - String text = table.getStringAt(row, column); - if ((text != null) && (text.length() > 0)) { - Matcher matcher = pattern.matcher(text); - if (matcher.find()) { - return createSearchResult(matcher, row, column); - } - } - return null; - } - - /** - * - * {@inheritDoc} - *

                          - * - * Overridden to adjust the column index to -1. - */ - @Override - protected int adjustStartPosition(int startIndex, boolean backwards) { - lastSearchResult.foundColumn = -1; - return super.adjustStartPosition(startIndex, backwards); - } - - /** - * {@inheritDoc} - *

                          - * - * Overridden to loop through all columns in a row. - */ - @Override - protected int moveStartPosition(int startRow, boolean backwards) { - if (backwards) { - lastSearchResult.foundColumn--; - if (lastSearchResult.foundColumn < 0) { - startRow--; - } - } else { - lastSearchResult.foundColumn++; - if (lastSearchResult.foundColumn >= table.getColumnCount()) { - lastSearchResult.foundColumn = -1; - startRow++; - } - } - return startRow; - } - - /** - * {@inheritDoc} - *

                          - * - * Overridden to check the column index of last find. - */ - @Override - protected boolean isEqualStartIndex(final int startIndex) { - return super.isEqualStartIndex(startIndex) - && isValidColumn(lastSearchResult.foundColumn); - } - - /** - * Checks if row is in range: 0 <= row < getRowCount(). - * - * @param column the column index to check in view coordinates. - * @return true if the column is in range, false otherwise - */ - private boolean isValidColumn(int column) { - return column >= 0 && column < table.getColumnCount(); - } - - /** - * {@inheritDoc} - */ - @Override - protected int getSize() { - return table.getRowCount(); - } - - /** - * {@inheritDoc} - */ - @Override - public JXTable getTarget() { - return table; - } - - /** - * Configures the match highlighter to the current match. Ensures that the - * matched cell is visible, if there is a match. - * - * PRE: markByHighlighter - * - */ - protected void moveMatchByHighlighter() { - AbstractHighlighter searchHL = getConfiguredMatchHighlighter(); - // no match - if (!hasMatch()) { - return; - } else { - ensureInsertedSearchHighlighters(searchHL); - table.scrollCellToVisible(lastSearchResult.foundRow, - lastSearchResult.foundColumn); - } - } - - /** - * {@inheritDoc} - *

                          - * - * Overridden to convert the column index in the table's view coordinate - * system to model coordinate. - *

                          - * - * PENDING JW: this is only necessary because the SearchPredicate wants its - * highlight column in model coordinates. But code comments in the - * SearchPredicate seem to indicate that we probably want to revise that - * (legacy?). - */ - @Override - protected int convertColumnIndexToModel(int viewColumn) { - return getTarget().convertColumnIndexToModel(viewColumn); - } - - /** - * Moves the row selection to the matching cell and ensures its visibility, - * if any. Does nothing if there is no match. - * - */ - protected void moveMatchBySelection() { - if (!hasMatch()) { - return; - } - int row = lastSearchResult.foundRow; - int column = lastSearchResult.foundColumn; - table.changeSelection(row, column, false, false); - if (!table.getAutoscrolls()) { - // scrolling not handled by moving selection - Rectangle cellRect = table.getCellRect(row, column, true); - if (cellRect != null) { - table.scrollRectToVisible(cellRect); - } - } - } - - /** - * {@inheritDoc} - *

                          - */ - @Override - protected void moveMatchMarker() { - if (markByHighlighter()) { - moveMatchByHighlighter(); - } else { // use selection - moveMatchBySelection(); - } - } - - /** - * {@inheritDoc} - *

                          - */ - @Override - protected void removeHighlighter(Highlighter searchHighlighter) { - table.removeHighlighter(searchHighlighter); - } - - /** - * {@inheritDoc} - *

                          - */ - @Override - protected Highlighter[] getHighlighters() { - return table.getHighlighters(); - } - - /** - * {@inheritDoc} - *

                          - */ - @Override - protected void addHighlighter(Highlighter highlighter) { - table.addHighlighter(highlighter); - } - -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/search/TreeSearchable.java b/src/main/java/org/jdesktop/swingx/search/TreeSearchable.java deleted file mode 100644 index fa4bd0c5a1..0000000000 --- a/src/main/java/org/jdesktop/swingx/search/TreeSearchable.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * $Id: TreeSearchable.java 4178 2012-06-20 08:52:11Z kleopatra $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.search; - -import org.jdesktop.swingx.JXTree; -import org.jdesktop.swingx.decorator.AbstractHighlighter; -import org.jdesktop.swingx.decorator.Highlighter; -import org.jdesktop.swingx.util.Contract; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * A searchable targetting the visible rows of a JXTree. - * - * - */ -public class TreeSearchable extends AbstractSearchable { - - protected JXTree tree; - - /** - * Instantiates a Searchable for the given JTree. - * - * @param tree the JTree to search, must not be null. - */ - public TreeSearchable(JXTree tree) { - this.tree = Contract.asNotNull(tree, "tree must not be null"); - } - - @Override - protected void findMatchAndUpdateState(Pattern pattern, int startRow, - boolean backwards) { - SearchResult searchResult = null; - if (backwards) { - for (int index = startRow; index >= 0 && searchResult == null; index--) { - searchResult = findMatchAt(pattern, index); - } - } else { - for (int index = startRow; index < getSize() - && searchResult == null; index++) { - searchResult = findMatchAt(pattern, index); - } - } - updateState(searchResult); - - } - - @Override - protected SearchResult findExtendedMatch(Pattern pattern, int row) { - return findMatchAt(pattern, row); - } - - /** - * Matches the cell content at row/col against the given Pattern. Returns an - * appropriate SearchResult if matching or null if no matching - * - * @param pattern - * @param row a valid row index in view coordinates a valid column index in - * view coordinates - * @return an appropriate SearchResult if matching or null if - * no matching - */ - protected SearchResult findMatchAt(Pattern pattern, int row) { - String text = tree.getStringAt(row); - if ((text != null) && (text.length() > 0)) { - Matcher matcher = pattern.matcher(text); - if (matcher.find()) { - return createSearchResult(matcher, row, 0); - } - } - return null; - } - - @Override - protected int getSize() { - return tree.getRowCount(); - } - - /** - * {@inheritDoc} - */ - @Override - public JXTree getTarget() { - return tree; - } - - /** - * {@inheritDoc} - */ - @Override - protected void moveMatchMarker() { - if (markByHighlighter()) { - moveMatchByHighlighter(); - } else { // use selection - moveMatchBySelection(); - } - } - - protected void moveMatchBySelection() { - // // the common behaviour (JXList, JXTable) is to not - // // move the selection if not found - if (!hasMatch()) { - return; - } - tree.setSelectionRow(lastSearchResult.foundRow); - tree.scrollRowToVisible(lastSearchResult.foundRow); - } - - /** - * use and move the match highlighter. PRE: markByHighlighter - * - */ - protected void moveMatchByHighlighter() { - AbstractHighlighter searchHL = getConfiguredMatchHighlighter(); - // no match - if (!hasMatch()) { - return; - } else { - ensureInsertedSearchHighlighters(searchHL); - tree.scrollRowToVisible(lastSearchResult.foundRow); - } - } - - /** - * @param searchHighlighter - */ - @Override - protected void removeHighlighter(Highlighter searchHighlighter) { - tree.removeHighlighter(searchHighlighter); - } - - /** - * @return all registered highlighters - */ - @Override - protected Highlighter[] getHighlighters() { - return tree.getHighlighters(); - } - - /** - * @param highlighter - */ - @Override - protected void addHighlighter(Highlighter highlighter) { - tree.addHighlighter(highlighter); - } - -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/sort/DefaultSortController.java b/src/main/java/org/jdesktop/swingx/sort/DefaultSortController.java deleted file mode 100644 index 888c4b2751..0000000000 --- a/src/main/java/org/jdesktop/swingx/sort/DefaultSortController.java +++ /dev/null @@ -1,405 +0,0 @@ -/* - * $Id$ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.sort; - -import org.jdesktop.swingx.renderer.StringValue; -import org.jdesktop.swingx.renderer.StringValues; -import org.jdesktop.swingx.util.Contract; - -import javax.swing.*; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; - -/** - * A default SortController implementation used as parent class for concrete - * SortControllers in SwingX.

                          - * - * Additionally, this implementation contains a fix for core - * Issue 6894632. - * It guarantees to only touch the underlying model during sort/filter and during - * processing the notification methods. This implies that the conversion and size query - * methods are valid at all times outside the internal updates, including the critical - * period (in core with undefined behaviour) after the underlying model has changed and - * before this sorter has been notified. - * - * @author Jeanette Winzenburg - */ -public abstract class DefaultSortController extends DefaultRowSorter implements - SortController { - - /** - * Comparator that uses compareTo on the contents. - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static final Comparator COMPARABLE_COMPARATOR = - new ComparableComparator(); - - private final static SortOrder[] DEFAULT_CYCLE = new SortOrder[] {SortOrder.ASCENDING, SortOrder.DESCENDING}; - - private List sortCycle; - - private boolean sortable; - - private StringValueProvider stringValueProvider; - - protected int cachedModelRowCount; - - public DefaultSortController() { - super(); - setSortable(true); - setSortOrderCycle(DEFAULT_CYCLE); - setSortsOnUpdates(true); - } - /** - * {@inheritDoc}

                          - * - */ - @Override - public void setSortable(boolean sortable) { - this.sortable = sortable; - } - - /** - * {@inheritDoc}

                          - * - */ - @Override - public boolean isSortable() { - return sortable; - } - - /** - * {@inheritDoc}

                          - * - */ - @Override - public void setSortable(int column, boolean sortable) { - super.setSortable(column, sortable); - } - - /** - * {@inheritDoc}

                          - * - */ - @Override - public boolean isSortable(int column) { - if (!isSortable()) return false; - return super.isSortable(column); - } - - /** - * {@inheritDoc} - *

                          - * - * Overridden - that is completely new implementation - to get first/next SortOrder - * from sort order cycle. Does nothing if the cycle is empty. - */ - @Override - public void toggleSortOrder(int column) { - checkColumn(column); - if (!isSortable(column)) - return; - SortOrder firstInCycle = getFirstInCycle(); - // nothing to toggle through - if (firstInCycle == null) - return; - List keys = new ArrayList(getSortKeys()); - SortKey sortKey = SortUtils.getFirstSortKeyForColumn(keys, column); - if (keys.indexOf(sortKey) == 0) { - // primary key: in this case we'll use next sortorder in cylce - keys.set(0, new SortKey(column, getNextInCycle(sortKey.getSortOrder()))); - } else { - // all others: make primary with first sortOrder in cycle - keys.remove(sortKey); - keys.add(0, new SortKey(column, getFirstInCycle())); - } - if (keys.size() > getMaxSortKeys()) { - keys = keys.subList(0, getMaxSortKeys()); - } - setSortKeys(keys); - } - - - /** - * Returns the next SortOrder relative to the current, or null - * if the sort order cycle is empty. - * - * @param current the current SortOrder - * @return the next SortOrder to use, may be null if the cycle is empty. - */ - private SortOrder getNextInCycle(SortOrder current) { - int pos = sortCycle.indexOf(current); - if (pos < 0) { - // not in cycle ... what to do? - return getFirstInCycle(); - } - pos++; - if (pos >= sortCycle.size()) { - pos = 0; - } - return sortCycle.get(pos); - } - - /** - * Returns the first SortOrder in the sort order cycle, or null if empty. - * - * @return the first SortOrder in the sort order cycle or null if empty. - */ - private SortOrder getFirstInCycle() { - return sortCycle.size() > 0 ? sortCycle.get(0) : null; - } - - private void checkColumn(int column) { - if (column < 0 || column >= getModelWrapper().getColumnCount()) { - throw new IndexOutOfBoundsException( - "column beyond range of TableModel"); - } - } - - /** - * {@inheritDoc}

                          - * - * PENDING JW: toggle has two effects: makes the column the primary sort column, - * and cycle through. So here we something similar. Should we? - * - */ - @Override - public void setSortOrder(int column, SortOrder sortOrder) { - if (!isSortable(column)) return; - SortKey replace = new SortKey(column, sortOrder); - List keys = new ArrayList(getSortKeys()); - SortUtils.removeFirstSortKeyForColumn(keys, column); - keys.add(0, replace); - // PENDING max sort keys, respect here? - setSortKeys(keys); - } - - /** - * {@inheritDoc}

                          - * - */ - @Override - public SortOrder getSortOrder(int column) { - SortKey key = SortUtils.getFirstSortKeyForColumn(getSortKeys(), column); - return key != null ? key.getSortOrder() : SortOrder.UNSORTED; - } - - /** - * {@inheritDoc}

                          - * - */ - @Override - public void resetSortOrders() { - if (!isSortable()) return; - List keys = new ArrayList(getSortKeys()); - for (int i = keys.size() -1; i >= 0; i--) { - SortKey sortKey = keys.get(i); - if (isSortable(sortKey.getColumn())) { - keys.remove(sortKey); - } - - } - setSortKeys(keys); - - } - - - /** - * {@inheritDoc}

                          - */ - @Override - public SortOrder[] getSortOrderCycle() { - return sortCycle.toArray(new SortOrder[0]); - } - - /** - * {@inheritDoc}

                          - */ - @Override - public void setSortOrderCycle(SortOrder... cycle) { - Contract.asNotNull(cycle, "Elements of SortOrderCycle must not be null"); - // JW: not safe enough? - sortCycle = Arrays.asList(cycle); - } - - /** - * Sets the registry of string values. If null, the default provider is used. - * - * @param registry the registry to get StringValues for conversion. - */ - @Override - public void setStringValueProvider(StringValueProvider registry) { - this.stringValueProvider = registry; -// updateStringConverter(); - } - - /** - * Returns the registry of string values. - * - * @return the registry of string converters, guaranteed to never be null. - */ - @Override - public StringValueProvider getStringValueProvider() { - if (stringValueProvider == null) { - stringValueProvider = DEFAULT_PROVIDER; - } - return stringValueProvider; - } - - /** - * Returns the default cycle. - * - * @return default sort order cycle. - */ - public static SortOrder[] getDefaultSortOrderCycle() { - return Arrays.copyOf(DEFAULT_CYCLE, DEFAULT_CYCLE.length); - } - - private static final StringValueProvider DEFAULT_PROVIDER = new StringValueProvider() { - - @Override - public StringValue getStringValue(int row, int column) { - return StringValues.TO_STRING; - } - - }; - - - @SuppressWarnings({ "unchecked", "rawtypes" }) - private static class ComparableComparator implements Comparator { - @Override - public int compare(Object o1, Object o2) { - return ((Comparable)o1).compareTo(o2); - } - } - -//-------------------------- replacing super for more consistent conversion/rowCount behaviour - - /** - * {@inheritDoc}

                          - * - * Overridden to use check against getViewRowCount for validity. - * - * @see #getViewRowCount() - */ - @Override - public int convertRowIndexToModel(int viewIndex) { - if ((viewIndex < 0) || viewIndex >= getViewRowCount()) - throw new IndexOutOfBoundsException("valid viewIndex: 0 <= index < " - + getViewRowCount() - + " but was: " + viewIndex); - try { - return super.convertRowIndexToModel(viewIndex); - } catch (Exception e) { - // this will happen only if unsorted/-filtered and super - // incorrectly access the model while it had been changed - // under its feet - } - return viewIndex; - } - - - /** - * {@inheritDoc}

                          - * - * Overridden to use check against getModelRowCount for validity. - * - * @see #getModelRowCount() - */ - @Override - public int convertRowIndexToView(int modelIndex) { - if ((modelIndex < 0) || modelIndex >= getModelRowCount()) - throw new IndexOutOfBoundsException("valid modelIndex: 0 <= index < " - + getModelRowCount() - + " but was: " + modelIndex); - try { - return super.convertRowIndexToView(modelIndex); - } catch (Exception e) { - // this will happen only if unsorted/-filtered and super - // incorrectly access the model while it had been changed - // under its feet - } - return modelIndex; - } - - /** - * {@inheritDoc}

                          - * - * Overridden to return the model row count which corresponds to the currently - * mapped model instead of accessing the model directly (as super does). - * This may differ from the "real" current model row count if the model has changed - * but this sorter not yet notified. - * - */ - @Override - public int getModelRowCount() { - return cachedModelRowCount; - } - - /** - * {@inheritDoc}

                          - * - * Overridden to return the model row count if no filters installed, otherwise - * return super. - * - * @see #getModelRowCount() - * - */ - @Override - public int getViewRowCount() { - if (hasRowFilter()) - return super.getViewRowCount(); - return getModelRowCount(); - } - - /** - * @return - */ - private boolean hasRowFilter() { - return getRowFilter() != null; - } - -//------------------ overridden notification methods: cache model row count - @Override - public void allRowsChanged() { - cachedModelRowCount = getModelWrapper().getRowCount(); - super.allRowsChanged(); - } - @Override - public void modelStructureChanged() { - super.modelStructureChanged(); - cachedModelRowCount = getModelWrapper().getRowCount(); - } - @Override - public void rowsDeleted(int firstRow, int endRow) { - cachedModelRowCount = getModelWrapper().getRowCount(); - super.rowsDeleted(firstRow, endRow); - } - @Override - public void rowsInserted(int firstRow, int endRow) { - cachedModelRowCount = getModelWrapper().getRowCount(); - super.rowsInserted(firstRow, endRow); - } - -} diff --git a/src/main/java/org/jdesktop/swingx/sort/ListSortController.java b/src/main/java/org/jdesktop/swingx/sort/ListSortController.java deleted file mode 100644 index d2f1b916ed..0000000000 --- a/src/main/java/org/jdesktop/swingx/sort/ListSortController.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * $Id$ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.sort; - -import javax.swing.*; - -/** - * A SortController to use with JXList. - * - * @author Jeanette Winzenburg - */ -public class ListSortController extends DefaultSortController { - - /** underlying model */ - private M listModel; - /** - * @param model - */ - public ListSortController(M model) { - setModel(model); - } - - /** - * Sets the TableModel to use as the underlying model - * for this TableRowSorter. A value of null - * can be used to set an empty model. - * - * @param model the underlying model to use, or null - */ - public void setModel(M model) { - listModel = model; - if (model != null) - cachedModelRowCount = model.getSize(); - setModelWrapper(new ListRowSorterModelWrapper()); - } - - /** - * Implementation of DefaultRowSorter.ModelWrapper that delegates to a - * TableModel. - */ - private class ListRowSorterModelWrapper extends ModelWrapper { - @Override - public M getModel() { - return listModel; - } - - @Override - public int getColumnCount() { - return (listModel == null) ? 0 : 1; - } - - @Override - public int getRowCount() { - return (listModel == null) ? 0 : listModel.getSize(); - } - - @Override - public Object getValueAt(int row, int column) { - return listModel.getElementAt(row); - } - - @Override - public String getStringValueAt(int row, int column) { - return getStringValueProvider().getStringValue(row, column) - .getString(getValueAt(row, column)); - } - - @Override - public Integer getIdentifier(int index) { - return index; - } - } - -} diff --git a/src/main/java/org/jdesktop/swingx/sort/RowFilters.java b/src/main/java/org/jdesktop/swingx/sort/RowFilters.java deleted file mode 100644 index 1503be582f..0000000000 --- a/src/main/java/org/jdesktop/swingx/sort/RowFilters.java +++ /dev/null @@ -1,216 +0,0 @@ -/* - * $Id$ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.sort; - -import org.jdesktop.swingx.util.Contract; - -import javax.swing.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; - -/** - * Factory of additional RowFilters.

                          - * - * Trigger is the missing of Pattern/Regex+matchflags factory method in core. - * Can't do much other than c&p core as both abstract base class GeneralFilter and - * concrete RowFilter are private. Expose the base as public for custom subclasses - * - * @author Jeanette Winzenburg - */ -@SuppressWarnings("unchecked") -public class RowFilters { - - /** - * Returns a RowFilter that uses a regular - * expression to determine which entries to include. Only entries - * with at least one matching value are included. For - * example, the following creates a RowFilter that - * includes entries with at least one value starting with - * "a": - *

                          -     *   RowFilter.regexFilter("^a");
                          -     * 
                          - *

                          - * The returned filter uses {@link Matcher#find} - * to test for inclusion. To test for exact matches use the - * characters '^' and '$' to match the beginning and end of the - * string respectively. For example, "^foo$" includes only rows whose - * string is exactly "foo" and not, for example, "food". See - * {@link Pattern} for a complete description of - * the supported regular-expression constructs. - * - * @param regex the regular expression to filter on - * @param indices the indices of the values to check. If not supplied all - * values are evaluated - * @return a RowFilter implementing the specified criteria - * @throws NullPointerException if regex is - * null - * @throws IllegalArgumentException if any of the indices - * are < 0 - * @throws PatternSyntaxException if regex is - * not a valid regular expression. - * @see Pattern - */ - public static RowFilter regexFilter(String regex, - int... indices) { - return regexFilter(0, regex, indices); - } - - /** - * Returns a RowFilter that uses a regular - * expression to determine which entries to include. Only entries - * with at least one matching value are included. For - * example, the following creates a RowFilter that - * includes entries with at least one value starting with - * "a" ignoring case: - *

                          -     *   RowFilter.regexFilter(Pattern.CASE_INSENSITIVE, "^a");
                          -     * 
                          - *

                          - * The returned filter uses {@link Matcher#find} - * to test for inclusion. To test for exact matches use the - * characters '^' and '$' to match the beginning and end of the - * string respectively. For example, "^foo$" includes only rows whose - * string is exactly "foo" and not, for example, "food". See - * {@link Pattern} for a complete description of - * the supported regular-expression constructs. - * - * @param matchFlags - * Match flags, a bit mask that may include - * {@link Pattern#CASE_INSENSITIVE}, {@link Pattern#MULTILINE}, {@link Pattern#DOTALL}, - * {@link Pattern#UNICODE_CASE}, {@link Pattern#CANON_EQ}, {@link Pattern#UNIX_LINES}, - * {@link Pattern#LITERAL} and {@link Pattern#COMMENTS} - * - * @param regex the regular expression to filter on - * @param indices the indices of the values to check. If not supplied all - * values are evaluated - * @return a RowFilter implementing the specified criteria - * @throws NullPointerException if regex is - * null - * @throws IllegalArgumentException if any of the indices - * are < 0 - * @throws IllegalArgumentException - * If bit values other than those corresponding to the defined - * match flags are set in flags - * @throws PatternSyntaxException if regex is - * not a valid regular expression. - * @see Pattern - */ - public static RowFilter regexFilter(int matchFlags, String regex, - int... indices) { - return regexFilter(Pattern.compile(regex, matchFlags), indices); - } - - /** - * Returns a RowFilter that uses a regular - * expression to determine which entries to include. - * - * @param pattern the Pattern to use for matching - * @param indices the indices of the values to check. If not supplied all - * values are evaluated - * @return a RowFilter implementing the specified criteria - * @throws NullPointerException if pattern is - * null - * @see Pattern - */ - public static RowFilter regexFilter(Pattern pattern, - int... indices) { - return (RowFilter)new RegexFilter(pattern, indices); - } - - /** - * C&P from core Swing to allow subclassing. - */ - public static abstract class GeneralFilter extends RowFilter { - private int[] columns; - - protected GeneralFilter(int... columns) { - checkIndices(columns); - this.columns = columns; - } - - @Override - public boolean include(Entry value){ - int count = value.getValueCount(); - if (columns.length > 0) { - for (int i = columns.length - 1; i >= 0; i--) { - int index = columns[i]; - if (index < count) { - if (include(value, index)) { - return true; - } - } - } - } - else { - while (--count >= 0) { - if (include(value, count)) { - return true; - } - } - } - return false; - } - - protected abstract boolean include( - Entry value, int index); - /** - * Throws an IllegalArgumentException if any of the values in - * columns are < 0. - */ - protected void checkIndices(int[] columns) { - for (int i = columns.length - 1; i >= 0; i--) { - if (columns[i] < 0) { - throw new IllegalArgumentException("Index must be >= 0"); - } - } - } - } - - /** - * C&P from core to allow richer factory methods. - */ - private static class RegexFilter extends GeneralFilter { - private Matcher matcher; - - RegexFilter(Pattern regex, int[] columns) { - super(columns); - if (regex == null) { - // JW: Exception type changed to comply with swingx convention - Contract.asNotNull(regex, "Pattern must be non-null"); -// throw new IllegalArgumentException("Pattern must be non-null"); - } - matcher = regex.matcher(""); - } - - @Override - protected boolean include( - Entry value, int index) { - matcher.reset(value.getStringValue(index)); - return matcher.find(); - } - } - - private RowFilters() {}; - -} diff --git a/src/main/java/org/jdesktop/swingx/sort/SortController.java b/src/main/java/org/jdesktop/swingx/sort/SortController.java deleted file mode 100644 index 61d2faa1a9..0000000000 --- a/src/main/java/org/jdesktop/swingx/sort/SortController.java +++ /dev/null @@ -1,279 +0,0 @@ -/* - * $Id: SortController.java 3498 2009-09-10 09:35:40Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.sort; - -import javax.swing.*; -import java.util.Comparator; - -/** - * Defines the interactive sort control for sortable collection components (like - * JXList, JXTable). All sort gesture requests from their sort api - * are routed through the SortController. - *

                          - * - * This is very-much work-in-progress: while moving from ol' SwingX sorting to - * core jdk6 sorting we need a hook for sorting api on the view. So in terms of - * jdk6 classes, this is something like:

                          - * - *

                          - * SortController == DefaultRowSorter - RowSorter + XX
                          - * 
                          - * All methods which change sort state must respect per-controller and per-column - * sortable property, as follows - *
                            - *
                          1. if per-controller sortable is false, do nothing - *
                          2. if per-controller sortable is true, if per-column sortable is false, do nothing - *
                          3. if both are true toggle the SortOrder of the given column - *
                          - * - * - * @author Jesse Wilson - * @author Jeanette Winzenburg - * - */ -public interface SortController { - -//----------------- configuration - - /** - * Sets whether or not this controller is sortable.

                          - * - * The default is true.

                          - * - * PENDING JW: define behaviour if sortable is disabled while has sortOrders. - * In this case JXTable resets all sorts. - * - * @param sortable whether or not this controller is sortable - * @see #isSortable() - */ - void setSortable(boolean sortable); - - /** - * Returns true if this controller is sortable; otherwise, false. - * - * @return true if this controller is sortable - * - * @see #isSortable() - */ - boolean isSortable(); - - /** - * Sets whether or not the specified column is sortable.

                          - * - * The default is true.

                          - * - * PENDING JW: define behaviour if sortable is disabled while has sortOrders. - * In this case JXTable removes the sort of the column.

                          - * - * PENDING JW: decide whether or not this method should trigger a resort - * DefaultRowSorter explicitly doesn't, JXTable does. - * - * @param column the column to enable or disable sorting on, in terms - * of the underlying model - * @param sortable whether or not the specified column is sortable - * @throws IndexOutOfBoundsException if column is outside - * the range of the model - * @see #isSortable(int) - * @see #toggleSortOrder(int) - * @see #setSortOrder(int, SortOrder) - */ - void setSortable(int column, boolean sortable); - - /** - * Returns true if the specified column is sortable.

                          - * This returns true if both the controller's sortable property and - * the column's sortable property is true. Returns false if any of - * them is false. - * - * @param column the column to check sorting for, in terms of the - * underlying model - * @return true if the column is sortable - * @throws IndexOutOfBoundsException if column is outside - * the range of the underlying model - * - * @see #isSortable(int) - */ - boolean isSortable(int column); - - /** - * Sets the Comparator to use when sorting the specified - * column. This does not trigger a sort. If you want to sort after - * setting the comparator you need to explicitly invoke sort. - * - * @param column the index of the column the Comparator is - * to be used for, in terms of the underlying model - * @param comparator the Comparator to use - * @throws IndexOutOfBoundsException if column is outside - * the range of the underlying model - */ - public void setComparator(int column, Comparator comparator); - - /** - * Returns the Comparator for the specified - * column. This will return null if a Comparator - * has not been specified for the column. - * - * @param column the column to fetch the Comparator for, in - * terms of the underlying model - * @return the Comparator for the specified column - * @throws IndexOutOfBoundsException if column is outside - * the range of the underlying model - */ - public Comparator getComparator(int column); - - /** - * Sets the cycle of sort ordes to toggle through. Zero or more SortOrders which - * must not be null. - * - * @param cycle the SortOrders to cycle through, may be empty - * @throws NullPointerException if the array or any of its elements is null - */ - void setSortOrderCycle(SortOrder... cycle); - - /** - * Returns the cycle of sort orders to cycle through. - * - * @return - */ - SortOrder[] getSortOrderCycle(); - - /** - * If true, specifies that a sort should happen when the underlying - * model is updated (rowsUpdated is invoked). For - * example, if this is true and the user edits an entry the - * location of that item in the view may change. The default is - * true. - * - * @param sortsOnUpdates whether or not to sort on update events - */ - void setSortsOnUpdates(boolean sortsOnUpdates); - - /** - * Returns true if a sort should happen when the underlying - * model is updated; otherwise, returns false. - * - * @return whether or not to sort when the model is updated - */ - boolean getSortsOnUpdates(); - - /** - * Sets the StringValueProvider to look up the StringValues. If the value - * is not-null, it guarantees to use it exclusively for string conversion.

                          - * - * PENDING JW: this is more or less parallel to TableStringConverter. Need to think - * about merging somehow. - * - * @param provider the look up for StringValues, may be null. - */ - void setStringValueProvider(StringValueProvider provider); - - /** - * Returns the StringValueProvider used to look up StringValues. - * - * @return StringValueProvider used to look up StringValues, guaranteed to - * be not null. - */ - StringValueProvider getStringValueProvider(); - -//------------------------ sort - - /** - * Reverses the sort order of the specified column. The exact behaviour is - * up to implementations.

                          - * - * Implementations must respect the per-controller and per-column-sortable - * property. - * - * @param column the model index of the column to toggle - * @see #isSortable(int) - * @see #isSortable() - */ - void toggleSortOrder(int column); - - /** - * Sets the sort order of the specified column.

                          - * - * Implementations must respect the per-controller and per-column-sortable - * property. - * - * @param column the model index of the column to set - * @param sortOrder the SortOrder to set for the column - * - * @see #isSortable(int) - * @see #isSortable() - */ - void setSortOrder(int column, SortOrder sortOrder); - - /** - * Returns the sort order of the specified column. - * - * - * @return one of {@link SortOrder#ASCENDING}, - * {@link SortOrder#DESCENDING} or {@link SortOrder#UNSORTED}. - */ - SortOrder getSortOrder(int column); - - - /** - * Resets all interactive sorting.

                          - * - * Implementations must respect the per-controller and per-column-sortable - * property. - * - */ - void resetSortOrders(); - -//-------------------- filter - - /** - * Sets the filter that determines which rows, if any, should be - * hidden from the view. The filter is applied before sorting. A value - * of null indicates all values from the model should be - * included. - *

                          - * RowFilter's include method is passed an - * Entry that wraps the underlying model. The number - * of columns in the Entry corresponds to the - * number of columns in the underlying model. The identifier - * comes from the underlying model as well. - *

                          - * This method triggers a sort. - * - * PENDING JW: the "underlying model" is the ModelWrapper ... want to - * expose here as well? Otherwise, the second paramter doesn't make much sense. - * - * @param filter the filter used to determine what entries should be - * included - */ - void setRowFilter(RowFilter filter); - - /** - * Returns the filter that determines which rows, if any, should - * be hidden from view. - * - * @return the filter - */ - RowFilter getRowFilter(); - - - -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/sort/SortUtils.java b/src/main/java/org/jdesktop/swingx/sort/SortUtils.java deleted file mode 100644 index c9f18c9544..0000000000 --- a/src/main/java/org/jdesktop/swingx/sort/SortUtils.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Created on 15.03.2006 - * - */ -package org.jdesktop.swingx.sort; - -import javax.swing.*; -import java.util.List; - -/** - * Collection of convenience methods. - */ -public class SortUtils { - -//---------------------- static utility methods - - /** - * Returns the first SortKey in the list which is sorted. - * If none is sorted, null is returned. - * - * @param keys a list of SortKeys to search - * @return the first SortKey which is sorted or null, if no - * is found. - */ - public static RowSorter.SortKey getFirstSortingKey(List keys) { - for (RowSorter.SortKey key : keys) { - if (isSorted(key.getSortOrder())) { - return key; - } - } - return null; - } - - /** - * Returns the first SortKey in the list for the given column, - * or null if the column has no SortKey. - * - * @param keys a list of SortKeys to search - * @param modelColumn the column index in model coordinates - * @return the first SortKey for the given column or null if none is - * found. - */ - public static RowSorter.SortKey getFirstSortKeyForColumn(List keys, int modelColumn) { - for (RowSorter.SortKey key : keys) { - if (key.getColumn() == modelColumn) { - return key; - } - } - return null; - } - - /** - * Removes and returns the first SortKey in the list for the given column, - * or null if the column has no SortKey. - * - * @param keys a list of SortKeys to search - * @param modelColumn the column index in model coordinates - * @return the first SortKey for the given column or null if none is - * found. - */ - public static RowSorter.SortKey removeFirstSortKeyForColumn(List keys, int modelColumn) { - for (RowSorter.SortKey key : keys) { - if (key.getColumn() == modelColumn) { - keys.remove(key); - return key; - } - } - return null; - } - public static boolean isSorted(SortOrder sortOrder) { - return sortOrder != null && (SortOrder.UNSORTED != sortOrder); - } - - /** - * Convenience to check for ascending sort order. - * PENDING: is this helpful at all? - * - * @return true if ascendingly sorted, false for unsorted/descending. - */ - public static boolean isAscending(SortOrder sortOrder) { - return sortOrder == SortOrder.ASCENDING; - } - - public static boolean isSorted(SortOrder sortOrder, boolean ascending) { - return isSorted(sortOrder) && (ascending == isAscending(sortOrder)); - } - - - private SortUtils() {}; -} diff --git a/src/main/java/org/jdesktop/swingx/sort/StringValueProvider.java b/src/main/java/org/jdesktop/swingx/sort/StringValueProvider.java deleted file mode 100644 index 0df7542edb..0000000000 --- a/src/main/java/org/jdesktop/swingx/sort/StringValueProvider.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * $Id$ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.sort; - -import org.jdesktop.swingx.renderer.StringValue; - -/** - * Read-only repository for StringValues. This is meant to be shared by collection views - * (in rendering a cell) and RowSorters/SortControllers/ComponentAdapters.

                          - * - * Note: this is work-in-progress, related to re-enable WYSIWYM in sorting/filtering. - * It's location and api is expected to change. - * - * @author Jeanette Winzenburg - */ -public interface StringValueProvider { - - /** - * Returns a StringValue to use for conversion of the cell content at row and column. - * The converter is guaranteed to be not null, so implemenations are responsible for - * a reasonable fall-back value always, f.i. if they have no converters registered of - * if any or both of the row/column coordinate is "invalid" (f.i. -1)

                          - * - * @param row the row of the cell in model coordinates - * @param column the column of the cell in model coordinates - * - * @return a StringValue to use for conversion, guaranteed to not null. - */ - StringValue getStringValue(int row, int column); - -} diff --git a/src/main/java/org/jdesktop/swingx/sort/StringValueRegistry.java b/src/main/java/org/jdesktop/swingx/sort/StringValueRegistry.java deleted file mode 100644 index 0a2b79cace..0000000000 --- a/src/main/java/org/jdesktop/swingx/sort/StringValueRegistry.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * $Id$ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.sort; - -import org.jdesktop.swingx.renderer.StringValue; -import org.jdesktop.swingx.renderer.StringValues; - -import java.util.HashMap; -import java.util.Map; -import java.util.logging.Logger; - -/** - * A writable implemenation of StringValueProvider. Typically, this is created and - * maintained by a collection view and then passed over to interested parties. It is - * modeled/implemented after the default renderer maintenance in a JTable.

                          - * - * PENDING JW: for safety - better not implement but return a provider. We probably don't want - * readers to frickle around here?. - * - * @author Jeanette Winzenburg - */ -public final class StringValueRegistry implements StringValueProvider { - - @SuppressWarnings("unused") - private static final Logger LOG = Logger - .getLogger(StringValueRegistry.class.getName()); - - private Map, StringValue> perClass; - private HashMap perColumn; - private HashMap> classPerColumn; - - /** - * {@inheritDoc}

                          - */ - @Override - public StringValue getStringValue(int row, int column) { - StringValue sv = getPerColumnMap().get(column); - if (sv == null) { - sv = getStringValueByClass(getClass(row, column)); - } - if (sv == null) { - sv = getStringValueByClass(Object.class); - } - return sv != null ? sv : StringValues.TO_STRING; - } - -//-------------------- manage - /** - * Sets a StringValue to use for the given column. If the converter is null, - * the mapping is removed. - * - * @param sv the StringValue to use for the given column. - * @param column the column index in model coordinates. - * - */ - public void setStringValue(StringValue sv, int column) { - // PENDING really remove mapping if sv null - getPerColumnMap().put(column, sv); - } - - /** - * Removes all per-column mappings of StringValues. - * - */ - public void clearColumnStringValues() { - getPerColumnMap().clear(); - } - - /** - * Sets the StringValue to use for the given class. If the converter is null, - * the mapping is removed. - * - * @param sv the StringValue to use for the given column. - * @param clazz the class - */ - public void setStringValue(StringValue sv, Class clazz) { - // PENDING really remove mapping if sv null - getPerClassMap().put(clazz, sv); - } - - /** - * Returns the StringValue registered for the given class.

                          - * - * This is temporarily exposed for testing only - do not use, it will - * be removed very soon! - * - * @param clazz the class to find the registered StringValue for - * @return the StringValue registered for the class, or null if not directly - * registered. - */ - public StringValue getStringValue(Class clazz) { - return getPerClassMap().get(clazz); - } - /** - * Sets the column class. - * - * @param clazz - * @param column index in model coordinates - */ - public void setColumnClass(Class clazz, int column) { - getColumnClassMap().put(column, clazz); - } - - /** - * @param classPerColumn - */ - public void setColumnClasses(Map> classPerColumn) { - this.classPerColumn = classPerColumn != null ? - new HashMap>(classPerColumn) : null; - } - - /** - * - * @param clazz - * @return - */ - private StringValue getStringValueByClass(Class clazz) { - if (clazz == null) return null; - StringValue sv = getPerClassMap().get(clazz); - if (sv != null) return sv; - return getStringValueByClass(clazz.getSuperclass()); - } - - /** - * Returns the Class of the column. - * - * @param row - * @param column - * @return - */ - private Class getClass(int row, int column) { - Class clazz = getColumnClassMap().get(column); - return clazz != null ? clazz : Object.class; - } - - /** - * Returns the Map which stores the per-column Class, lazily - * creates one if null. - * - * @return the per-column storage map of Class - */ - private Map> getColumnClassMap() { - if (classPerColumn == null) { - classPerColumn = new HashMap>(); - } - return classPerColumn; - } - - /** - * Returns the Map which stores the per-class StringValues, lazily - * creates one if null. - * - * @return the per-class storage map of StringValues - */ - private Map, StringValue> getPerClassMap() { - if (perClass == null) { - perClass = new HashMap, StringValue>(); - } - return perClass; - } - - /** - * Returns the Map which stores the per-column StringValues, lazily - * creates one if null. - * - * @return the per-column storage map of StringValues - */ - private Map getPerColumnMap() { - if (perColumn == null) { - perColumn = new HashMap(); - } - return perColumn; - } -} diff --git a/src/main/java/org/jdesktop/swingx/sort/TableSortController.java b/src/main/java/org/jdesktop/swingx/sort/TableSortController.java deleted file mode 100644 index b84569bf4c..0000000000 --- a/src/main/java/org/jdesktop/swingx/sort/TableSortController.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * $Id$ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.sort; - -import javax.swing.table.TableModel; -import java.text.Collator; -import java.util.Comparator; - -/** - * A SortController to use for a JXTable.

                          - * - * @author Jeanette Winzenburg - */ -public class TableSortController extends DefaultSortController { - /** - * Underlying model. - */ - private M tableModel; - - public TableSortController() { - this(null); - } - - /** - * @param model - */ - public TableSortController(M model) { - super(); - setModel(model); - } - - /** - * Sets the TableModel to use as the underlying model - * for this TableRowSorter. A value of null - * can be used to set an empty model. - * - * @param model the underlying model to use, or null - */ - public void setModel(M model) { - tableModel = model; - if (model != null) - cachedModelRowCount = model.getRowCount(); - setModelWrapper(new TableRowSorterModelWrapper()); - } - - - /** - * Returns the Comparator for the specified - * column. If a Comparator has not been specified using - * the setComparator method a Comparator - * will be returned based on the column class - * (TableModel.getColumnClass) of the specified column. - * If the column class is String, - * Collator.getInstance is returned. If the - * column class implements Comparable a private - * Comparator is returned that invokes the - * compareTo method. Otherwise - * Collator.getInstance is returned.

                          - * - * PENDING JW: think about implications to string value lookup! - * - * @throws IndexOutOfBoundsException {@inheritDoc} - */ - @Override - public Comparator getComparator(int column) { - Comparator comparator = super.getComparator(column); - if (comparator != null) { - return comparator; - } - Class columnClass = getModel().getColumnClass(column); - if (columnClass == String.class) { - return Collator.getInstance(); - } - if (Comparable.class.isAssignableFrom(columnClass)) { - return COMPARABLE_COMPARATOR; - } - return Collator.getInstance(); - } - - /** - * {@inheritDoc}

                          - * Note: must implement same logic as the overridden comparator - * lookup, otherwise will throw ClassCastException because - * here the comparator is never null.

                          - * - * PENDING JW: think about implications to string value lookup! - * - * @throws IndexOutOfBoundsException {@inheritDoc} - */ - @Override - protected boolean useToString(int column) { - Comparator comparator = super.getComparator(column); - if (comparator != null) { - return false; - } - Class columnClass = getModel().getColumnClass(column); - if (columnClass == String.class) { - return false; - } - if (Comparable.class.isAssignableFrom(columnClass)) { - return false; - } - return true; - } - - - /** - * Implementation of DefaultRowSorter.ModelWrapper that delegates to a - * TableModel. - */ - private class TableRowSorterModelWrapper extends ModelWrapper { - @Override - public M getModel() { - return tableModel; - } - - @Override - public int getColumnCount() { - return (tableModel == null) ? 0 : tableModel.getColumnCount(); - } - - @Override - public int getRowCount() { - return (tableModel == null) ? 0 : tableModel.getRowCount(); - } - - @Override - public Object getValueAt(int row, int column) { - return tableModel.getValueAt(row, column); - } - - @Override - public String getStringValueAt(int row, int column) { - return getStringValueProvider().getStringValue(row, column) - .getString(getValueAt(row, column)); - } - - @Override - public Integer getIdentifier(int index) { - return index; - } - } - - -} diff --git a/src/main/java/org/jdesktop/swingx/table/ColumnControlButton.java b/src/main/java/org/jdesktop/swingx/table/ColumnControlButton.java deleted file mode 100644 index 80fdd1245d..0000000000 --- a/src/main/java/org/jdesktop/swingx/table/ColumnControlButton.java +++ /dev/null @@ -1,1040 +0,0 @@ -/* - * $Id: ColumnControlButton.java 4065 2011-08-19 13:28:26Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.table; - -import org.jdesktop.swingx.JXTable; -import org.jdesktop.swingx.action.AbstractActionExt; -import org.jdesktop.swingx.action.ActionContainerFactory; -import org.jdesktop.swingx.plaf.ColumnControlButtonAddon; -import org.jdesktop.swingx.plaf.LookAndFeelAddons; -import org.jdesktop.swingx.table.ColumnControlPopup.ActionGroupable; -import org.jdesktop.swingx.table.ColumnControlPopup.ActionGrouper; - -import javax.swing.*; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.TableColumnModelEvent; -import javax.swing.event.TableColumnModelListener; -import javax.swing.plaf.UIResource; -import javax.swing.table.TableColumn; -import javax.swing.table.TableColumnModel; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ItemEvent; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * A component to allow interactive customization of JXTable's - * columns. - * It's main purpose is to allow toggling of table columns' visibility. - * Additionally, arbitrary configuration actions can be exposed. - *

                          - * - * This component is installed in the JXTable's - * trailing corner, if enabled: - * - *

                          
                          - * table.setColumnControlVisible(true);
                          - * 
                          - * - * From the perspective of a JXTable, the component's behaviour is - * opaque. Typically, the button's action is to popup a component for user - * interaction.

                          - * - * This class is responsible for handling/providing/updating the lists of - * actions and to keep each Action's state in synch with Table-/Column state. - * The visible behaviour of the popup is delegated to a - * ColumnControlPopup.

                          - * - * Default support for adding table (configuration or other) Actions is - * informal, driven by convention: - *

                            - *
                          • the JXTable's actionMap is scanned for candidate actions, the default marker - * is a key of type String which starts with {@link ColumnControlButton.COLUMN_CONTROL_MARKER} - *
                          • the actions are sorted by that key and then handed over to the ColumnControlPopup - * for binding and addition of appropriate menu items - *
                          • the addition as such is control by additionalActionsVisible property, its - * default value is true - *
                          - * - * - * - * @see TableColumnExt - * @see TableColumnModelExt - * @see JXTable#setColumnControl - * - */ -public class ColumnControlButton extends JButton { - - // JW: really want to extend? for builders? - /** Marker to auto-recognize actions which should be added to the popup. */ - public static final String COLUMN_CONTROL_MARKER = "column."; - - /** the key for looking up the control's icon in the UIManager. Typically, it's LAF dependent. */ - public static final String COLUMN_CONTROL_BUTTON_ICON_KEY = "ColumnControlButton.actionIcon"; - - /** the key for looking up the control's margin in the UIManager. Typically, it's LAF dependent. */ - public static final String COLUMN_CONTROL_BUTTON_MARGIN_KEY = "ColumnControlButton.margin"; - - static { - LookAndFeelAddons.contribute(new ColumnControlButtonAddon()); - } - - /** exposed for testing. */ - protected ColumnControlPopup popup; - // TODO: the table reference is a potential leak? - /** The table which is controlled by this. */ - private JXTable table; - /** Listener for table property changes. */ - private PropertyChangeListener tablePropertyChangeListener; - /** Listener for table's columnModel. */ - TableColumnModelListener columnModelListener; - /** the list of actions for column menuitems.*/ - private List columnVisibilityActions; - - private boolean additionalActionsVisible; - - - /** - * Creates a column control button for the table. Uses the default - * icon as provided by the addon. - * - * @param table the JXTable controlled by this component - */ - public ColumnControlButton(JXTable table) { - this(table, null); - } - - /** - * Creates a column control button for the table. The button - * uses the given icon and has no text. - * @param table the JXTable controlled by this component - * @param icon the Icon to show - */ - public ColumnControlButton(JXTable table, Icon icon) { - super(); - init(); - // JW: icon LF dependent? - setAction(createControlAction(icon)); - updateActionUI(); - updateButtonUI(); - installTable(table); - } - - - @Override - public void updateUI() { - super.updateUI(); - // JW: icon may be LF dependent - updateActionUI(); - updateButtonUI(); - getColumnControlPopup().updateUI(); - } - - /** - * Updates this button's properties provided by the LAF. - * Here: overwrites the action's small_icon with the icon from the ui if the current - * icon is null or a UIResource. - */ - protected void updateButtonUI() { - if ((getMargin() == null) || (getMargin() instanceof UIResource)) { - Insets insets = UIManager.getInsets(COLUMN_CONTROL_BUTTON_MARGIN_KEY); - setMargin(insets); - } - } - - /** - * Updates the action properties provided by the LAF. - * Here: overwrites the action's small_icon with the icon from the ui if the current - * icon is null or a UIResource. - */ - protected void updateActionUI() { - if (getAction() == null) return; - Icon icon = (Icon) getAction().getValue(Action.SMALL_ICON); - if ((icon == null) || (icon instanceof UIResource)) { - icon = UIManager.getIcon(COLUMN_CONTROL_BUTTON_ICON_KEY); - getAction().putValue(Action.SMALL_ICON, icon); - } - } - - /** - * Toggles the popup component's visibility. This method is - * called by this control's default action.

                          - * - * Here: delegates to getControlPopup(). - */ - public void togglePopup() { - getColumnControlPopup().toggleVisibility(this); - } - - /** - * Returns the actionsVisible property which controls whether or not - * additional table Actions should be included into the popup. - * - * @return a boolean indicating whether or not additional table Actions - * are visible - */ - public boolean getAdditionalActionsVisible() { - return additionalActionsVisible; - } - - - /** - * Sets the additonalActionsVisible property. It controls whether or - * not additional table actions should be included into the popup.

                          - * - * The default value is true. - * - * @param additionalActionsVisible the additionalActionsVisible to set - */ - public void setAdditionalActionsVisible(boolean additionalActionsVisible) { - if (additionalActionsVisible == getAdditionalActionsVisible()) return; - boolean old = getAdditionalActionsVisible(); - this.additionalActionsVisible = additionalActionsVisible; - populatePopup(); - firePropertyChange("additionalActionsVisible", old, getAdditionalActionsVisible()); - } - - /** - * Sets the grouper to use for grouping the additional actions. Maybe null to - * have no additional grouping. Has no effect - * if the ColumnControlPopup doesn't implement Groupable. The default - * ColumnControlPopup supports Groupable, but is instantiated without a Grouper. - * - * @param grouper - */ - public void setActionGrouper(ActionGrouper grouper) { - if (!(getColumnControlPopup() instanceof ActionGroupable)) return; - ((ActionGroupable) getColumnControlPopup()).setActionGrouper(grouper); - populatePopup(); - } - - @Override - public void applyComponentOrientation(ComponentOrientation o) { - super.applyComponentOrientation(o); - getColumnControlPopup().applyComponentOrientation(o); - } - - -//-------------------------- Action in synch with column properties - /** - * A specialized Action which takes care of keeping in synch with - * TableColumn state. - * - * NOTE: client must call releaseColumn if this action is no longer needed! - * - */ - public class ColumnVisibilityAction extends AbstractActionExt { - - private TableColumn column; - - private PropertyChangeListener columnListener; - - /** flag to distinguish selection changes triggered by - * column's property change from those triggered by - * user interaction. Hack around #212-swingx. - */ - private boolean fromColumn; - - /** - * Creates a action synched to the table column. - * - * @param column the TableColumn to keep synched to. - */ - public ColumnVisibilityAction(TableColumn column) { - super((String) null); - setStateAction(); - installColumn(column); - } - - /** - * Releases all references to the synched TableColumn. - * Client code must call this method if the - * action is no longer needed. After calling this action must not be - * used any longer. - */ - public void releaseColumn() { - column.removePropertyChangeListener(columnListener); - column = null; - } - - /** - * Returns true if the action is enabled. Returns - * true only if the action is enabled and the table - * column can be controlled. - * - * @return true if the action is enabled, false otherwise - * @see #canControlColumn() - */ - @Override - public boolean isEnabled() { - return super.isEnabled() && canControlColumn(); - } - - /** - * Returns flag to indicate if column's visibility can - * be controlled. Minimal requirement is that column is of type - * TableColumnExt. - * - * @return boolean to indicate if columns's visibility can be controlled. - */ - protected boolean canControlColumn() { - // JW: should have direction? control is from action to column, the - // other way round should be guaranteed always - return (column instanceof TableColumnExt); - } - - @Override - public void itemStateChanged(final ItemEvent e) { - if (canControlColumn()) { - if ((e.getStateChange() == ItemEvent.DESELECTED) - //JW: guarding against 1 leads to #212-swingx: setting - // column visibility programatically fails if - // the current column is the second last visible - // guarding against 0 leads to hiding all columns - // by deselecting the menu item. - && (table.getColumnCount() <= 1) - // JW Fixed #212: basically implemented Rob's idea to distinguish - // event sources instead of unconditionally reselect - // not entirely sure if the state transitions are completely - // defined but all related tests are passing now. - && !fromColumn) { - reselect(); - } else { - setSelected(e.getStateChange() == ItemEvent.SELECTED); - } - } - } - - - @Override - public synchronized void setSelected(boolean newValue) { - super.setSelected(newValue); - if (canControlColumn()) { - if (!fromColumn) - ((TableColumnExt) column).setVisible(newValue); - } - } - - /** - * Does nothing. Synch from action state to TableColumn state - * is done in itemStateChanged. - */ - @Override - public void actionPerformed(ActionEvent e) { - - } - - /** - * Synchs selected property to visible. This - * is called on change of tablecolumn's visible property. - * - * @param visible column visible state to synch to. - */ - private void updateFromColumnVisible(boolean visible) { -// /*boolean*/ visible = true; -// if (canControlColumn()) { -// visible = ((TableColumnExt) column).isVisible(); -// } - fromColumn = true; - setSelected(visible); - fromColumn = false; - } - - - protected void updateFromColumnHideable(boolean hideable) { - setEnabled(hideable); - } - - /** - * Synchs name property to value. This is called on change of - * tableColumn's headerValue property. - * - * @param value - */ - private void updateFromColumnHeader(Object value) { - setName(String.valueOf(value)); - } - - /** - * Enforces selected to true. Called if user interaction - * tried to de-select the last single visible column. - * - */ - private void reselect() { - firePropertyChange("selected", null, Boolean.TRUE); - } - - // -------------- init - private void installColumn(TableColumn column) { - this.column = column; - column.addPropertyChangeListener(getColumnListener()); - updateFromColumnHeader(column.getHeaderValue()); - // #429-swing: actionCommand must be string - if (column.getIdentifier() != null) { - setActionCommand(column.getIdentifier().toString()); - } - boolean visible = (column instanceof TableColumnExt) ? - ((TableColumnExt) column).isVisible() : true; - updateFromColumnVisible(visible); - } - - /** - * Returns the listener to column's property changes. The listener - * is created lazily if necessary. - * - * @return the PropertyChangeListener listening to - * TableColumn's property changes, guaranteed to be - * not null. - */ - protected PropertyChangeListener getColumnListener() { - if (columnListener == null) { - columnListener = createPropertyChangeListener(); - } - return columnListener; - } - - /** - * Creates and returns the listener to column's property changes. - * Subclasses are free to roll their own. - *

                          - * Implementation note: this listener reacts to column's - * visible and headerValue properties and - * calls the respective updateFromXX methodes. - * - * @return the PropertyChangeListener to use with the - * column - */ - protected PropertyChangeListener createPropertyChangeListener() { - return new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - if ("visible".equals(evt.getPropertyName())) { - updateFromColumnVisible((Boolean) evt.getNewValue()); - } else if ("headerValue".equals(evt.getPropertyName())) { - updateFromColumnHeader(evt.getNewValue()); - } else if ("hideable".equals(evt.getPropertyName())) { - updateFromColumnHideable((Boolean) evt.getNewValue()); - } - } - }; - } - } - - // ---------------------- the popup - - /** - * A default implementation of ColumnControlPopup. - * It uses a JPopupMenu with - * MenuItems corresponding to the Actions as - * provided by the ColumnControlButton. - * - * - */ - public class DefaultColumnControlPopup implements ColumnControlPopup, ActionGroupable { - private JPopupMenu popupMenu; - private ActionGrouper grouper; - - public DefaultColumnControlPopup() { - this(null); - } - - //------------------ public methods to control visibility status - - public DefaultColumnControlPopup(ActionGrouper grouper) { - this.grouper = grouper; - } - - /** - * @inheritDoc - * - */ - @Override - public void updateUI() { - SwingUtilities.updateComponentTreeUI(getPopupMenu()); - } - - /** - * @inheritDoc - * - */ - @Override - public void toggleVisibility(JComponent owner) { - JPopupMenu popupMenu = getPopupMenu(); - if (popupMenu.isVisible()) { - popupMenu.setVisible(false); - } else if (popupMenu.getComponentCount() > 0) { - Dimension buttonSize = owner.getSize(); - int xPos = owner.getComponentOrientation().isLeftToRight() ? buttonSize.width - - popupMenu.getPreferredSize().width - : 0; - popupMenu.show(owner, - // JW: trying to allow popup without CCB showing - // weird behaviour -// owner.isShowing()? owner : null, - xPos, buttonSize.height); - } - - } - - /** - * @inheritDoc - * - */ - @Override - public void applyComponentOrientation(ComponentOrientation o) { - getPopupMenu().applyComponentOrientation(o); - - } - - //-------------------- public methods to manipulate popup contents. - - /** - * @inheritDoc - * - */ - @Override - public void removeAll() { - getPopupMenu().removeAll(); - } - - - /** - * @inheritDoc - * - */ - @Override - public void addVisibilityActionItems( - List actions) { - addItems(new ArrayList(actions)); - - } - - - /** - * @inheritDoc - * - */ - @Override - public void addAdditionalActionItems(List actions) { - if (actions.size() == 0) - return; - // JW: this is a reference to the enclosing class - // prevents to make this implementation static - // Hmmm...any way around? - if (canControl()) { - addSeparator(); - } - - if (getGrouper() == null) { - addItems(actions); - return; - } - List> groups = grouper.group(actions); - for (List group : groups) { - addItems(group); - if (group != groups.get(groups.size()- 1)) - addSeparator(); - - } - - } - - //--------------------------- internal helpers to manipulate popups content - - /** - * Here: creates and adds a menuItem to the popup for every - * Action in the list. Does nothing if - * if the list is empty. - * - * PRE: actions != null. - * - * @param actions a list containing the actions to add to the popup. - * Must not be null. - * - */ - protected void addItems(List actions) { - ActionContainerFactory factory = new ActionContainerFactory(null); - for (Action action : actions) { - addItem(factory.createMenuItem(action)); - } - } - - /** - * adds a separator to the popup. - * - */ - protected void addSeparator() { - getPopupMenu().addSeparator(); - } - - /** - * - * @param item the menuItem to add to the popup. - */ - protected void addItem(JMenuItem item) { - getPopupMenu().add(item); - } - - /** - * - * @return the popup to add menuitems. Guaranteed to be != null. - */ - protected JPopupMenu getPopupMenu() { - if (popupMenu == null) { - popupMenu = new JPopupMenu(); - } - return popupMenu; - } - - // --------------- implement Groupable - - @Override - public void setActionGrouper(ActionGrouper grouper) { - this.grouper = grouper; - } - - protected ActionGrouper getGrouper() { - return grouper; - } - - } - /** - * Returns to popup component for user interaction. Lazily - * creates the component if necessary. - * - * @return the ColumnControlPopup for showing the items, guaranteed - * to be not null. - * @see #createColumnControlPopup() - */ - protected ColumnControlPopup getColumnControlPopup() { - if (popup == null) { - popup = createColumnControlPopup(); - } - return popup; - } - - /** - * Factory method to return a ColumnControlPopup. - * Subclasses can override to hook custom implementations. - * - * @return the ColumnControlPopup used. - */ - protected ColumnControlPopup createColumnControlPopup() { - return new DefaultColumnControlPopup(); - } - - -//-------------------------- updates from table propertyChangelistnere - - /** - * Adjusts internal state after table's column model property has changed. - * Handles cleanup of listeners to the old/new columnModel (Note, that - * it listens to the column model only if it can control column visibility). - * Updates content of popup. - * - * @param oldModel the old TableColumnModel we had been listening to. - */ - protected void updateFromColumnModelChange(TableColumnModel oldModel) { - if (oldModel != null) { - oldModel.removeColumnModelListener(columnModelListener); - } - populatePopup(); - if (canControl()) { - table.getColumnModel().addColumnModelListener(getColumnModelListener()); - } - } - - /** - * Synchs this button's enabled with table's enabled. - * - */ - protected void updateFromTableEnabledChanged() { - getAction().setEnabled(table.isEnabled()); - - } - /** - * Method to check if we can control column visibility POST: if true we can - * be sure to have an extended TableColumnModel - * - * @return boolean to indicate if controlling the visibility state is - * possible. - */ - protected boolean canControl() { - return table.getColumnModel() instanceof TableColumnModelExt; - } - -// ------------------------ updating the popup - /** - * Populates the popup from scratch. - * - * If applicable, creates and adds column visibility actions. Always adds - * additional actions. - */ - protected void populatePopup() { - clearAll(); - if (canControl()) { - createVisibilityActions(); - addVisibilityActionItems(); - } - addAdditionalActionItems(); - } - - /** - * - * removes all components from the popup, making sure to release all - * columnVisibility actions. - * - */ - protected void clearAll() { - clearColumnVisibilityActions(); - getColumnControlPopup().removeAll(); - } - - - /** - * Releases actions and clears list of actions. - * - */ - protected void clearColumnVisibilityActions() { - if (columnVisibilityActions == null) - return; - for (ColumnVisibilityAction action : columnVisibilityActions) { - action.releaseColumn(); - } - columnVisibilityActions.clear(); - } - - - /** - * Adds visibility actions into the popup view. - * - * Here: delegates the list of actions to the DefaultColumnControlPopup. - *

                          - * PRE: columnVisibilityActions populated before calling this. - * - */ - protected void addVisibilityActionItems() { - getColumnControlPopup().addVisibilityActionItems( - Collections.unmodifiableList(getColumnVisibilityActions())); - } - - /** - * Adds additional actions to the popup, if additionalActionsVisible is true, - * does nothing otherwise.

                          - * - * Here: delegates the list of actions as returned by #getAdditionalActions() - * to the DefaultColumnControlPopup. - * Does nothing if #getColumnActions() is empty. - * - */ - protected void addAdditionalActionItems() { - if (!getAdditionalActionsVisible()) return; - getColumnControlPopup().addAdditionalActionItems( - Collections.unmodifiableList(getAdditionalActions())); - } - - - /** - * Creates and adds a ColumnVisiblityAction for every column that should be - * togglable via the column control.

                          - * - * Here: all table columns contained in the TableColumnModel - - * visible and invisible columns - to createColumnVisibilityAction and - * adds all not null return values. - * - *

                          - * PRE: canControl() - * - * @see #createColumnVisibilityAction - */ - protected void createVisibilityActions() { - List columns = table.getColumns(true); - for (TableColumn column : columns) { - ColumnVisibilityAction action = createColumnVisibilityAction(column); - if (action != null) { - getColumnVisibilityActions().add(action); - } - } - - } - - /** - * Creates and returns a ColumnVisibilityAction for the given - * TableColumn. The return value might be null, f.i. if the - * column should not be allowed to be toggled. - * - * @param column the TableColumn to use for the action - * @return a ColumnVisibilityAction to use for the given column, - * may be null. - */ - protected ColumnVisibilityAction createColumnVisibilityAction(TableColumn column) { - return new ColumnVisibilityAction(column); - } - - /** - * Lazyly creates and returns the List of visibility actions. - * - * @return the list of visibility actions, guaranteed to be != null. - */ - protected List getColumnVisibilityActions() { - if (columnVisibilityActions == null) { - columnVisibilityActions = new ArrayList(); - } - return columnVisibilityActions; - } - - - /** - * creates and returns a list of additional Actions to add to the popup. - * Here: the actions are looked up in the table's actionMap according - * to the keys as returned from #getColumnControlActionKeys(); - * - * @return a list containing all additional actions to include into the popup. - */ - protected List getAdditionalActions() { - List actionKeys = getColumnControlActionKeys(); - List actions = new ArrayList(); - for (Object key : actionKeys) { - actions.add(table.getActionMap().get(key)); - } - return actions; - } - - /** - * Looks up and returns action keys to access actions in the - * table's actionMap which should be included into the popup. - * - * Here: all keys with isColumnControlActionKey(key). The list - * is sorted by those keys. - * - * @return the action keys of table's actionMap entries whose - * action should be included into the popup. - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - protected List getColumnControlActionKeys() { - Object[] allKeys = table.getActionMap().allKeys(); - List columnKeys = new ArrayList(); - for (int i = 0; i < allKeys.length; i++) { - if (isColumnControlActionKey(allKeys[i])) { - columnKeys.add(allKeys[i]); - } - } - // JW: this will blow for non-String keys! - // so this method is less decoupled from the - // decision method isControl than expected. - Collections.sort(columnKeys); - return columnKeys; - } - - /** - * Here: true if a String key starts with #COLUMN_CONTROL_MARKER. - * - * @param actionKey a key in the table's actionMap. - * @return a boolean to indicate whether the given actionKey maps to - * an action which should be included into the popup. - * - */ - protected boolean isColumnControlActionKey(Object actionKey) { - return (actionKey instanceof String) && - ((String) actionKey).startsWith(COLUMN_CONTROL_MARKER); - } - - - //--------------------------- init - - private void installTable(JXTable table) { - this.table = table; - table.addPropertyChangeListener(getTablePropertyChangeListener()); - updateFromColumnModelChange(null); - updateFromTableEnabledChanged(); - } - - - /** - * Initialize the column control button's gui - */ - private void init() { - setFocusPainted(false); - setFocusable(false); - // this is a trick to get hold of the client prop which - // prevents closing of the popup - JComboBox box = new JComboBox(); - Object preventHide = box.getClientProperty("doNotCancelPopup"); - putClientProperty("doNotCancelPopup", preventHide); - additionalActionsVisible = true; - } - - - /** - * Creates and returns the default action for this button. - * @param icon - * - * @param icon the Icon to use in the action. - * @return the default action. - */ - private Action createControlAction(Icon icon) { - - Action control = new AbstractAction() { - - @Override - public void actionPerformed(ActionEvent e) { - togglePopup(); - } - - }; - control.putValue(Action.SMALL_ICON, icon); - return control; - } - - // -------------------------------- listeners - - /** - * Returns the listener to table's property changes. The listener is - * lazily created if necessary. - * @return the PropertyChangeListener for use with the - * table, guaranteed to be not null. - */ - protected PropertyChangeListener getTablePropertyChangeListener() { - if (tablePropertyChangeListener == null) { - tablePropertyChangeListener = createTablePropertyChangeListener(); - } - return tablePropertyChangeListener; - } - - /** - * Creates the listener to table's property changes. Subclasses are free - * to roll their own.

                          - * Implementation note: this listener reacts to table's enabled and - * columnModel properties and calls the respective - * updateFromXX methodes. - * - * @return the PropertyChangeListener for use with the table. - */ - protected PropertyChangeListener createTablePropertyChangeListener() { - return new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - if ("columnModel".equals(evt.getPropertyName())) { - updateFromColumnModelChange((TableColumnModel) evt - .getOldValue()); - } else if ("enabled".equals(evt.getPropertyName())) { - updateFromTableEnabledChanged(); - } - } - }; - } - - /** - * Returns the listener to table's column model. The listener is - * lazily created if necessary. - * @return the TableColumnModelListener for use with the - * table's column model, guaranteed to be not null. - */ - protected TableColumnModelListener getColumnModelListener() { - if (columnModelListener == null) { - columnModelListener = createColumnModelListener(); - } - return columnModelListener; - } - - /** - * Creates the listener to columnModel. Subclasses are free to roll their - * own. - *

                          - * Implementation note: this listener reacts to "real" columnRemoved/-Added by - * populating the popups content from scratch. - * - * @return the TableColumnModelListener for use with the - * table's columnModel. - */ - protected TableColumnModelListener createColumnModelListener() { - return new TableColumnModelListener() { - /** Tells listeners that a column was added to the model. */ - @Override - public void columnAdded(TableColumnModelEvent e) { - // quickfix for #192 - if (!isVisibilityChange(e, true)) { - populatePopup(); - } - } - - /** Tells listeners that a column was removed from the model. */ - @Override - public void columnRemoved(TableColumnModelEvent e) { - if (!isVisibilityChange(e, false)) { - populatePopup(); - } - } - - /** - * Check if the add/remove event is triggered by a move to/from the - * invisible columns. - * - * PRE: the event must be received in columnAdded/Removed. - * - * @param e the received event - * @param added if true the event is assumed to be received via - * columnAdded, otherwise via columnRemoved. - * @return boolean indicating whether the removed/added is a side-effect - * of hiding/showing the column. - */ - private boolean isVisibilityChange(TableColumnModelEvent e, - boolean added) { - // can't tell - if (!(e.getSource() instanceof DefaultTableColumnModelExt)) - return false; - DefaultTableColumnModelExt model = (DefaultTableColumnModelExt) e - .getSource(); - if (added) { - return model.isAddedFromInvisibleEvent(e.getToIndex()); - } else { - return model.isRemovedToInvisibleEvent(e.getFromIndex()); - } - } - - /** Tells listeners that a column was repositioned. */ - @Override - public void columnMoved(TableColumnModelEvent e) { - } - - /** Tells listeners that a column was moved due to a margin change. */ - @Override - public void columnMarginChanged(ChangeEvent e) { - } - - /** - * Tells listeners that the selection model of the TableColumnModel - * changed. - */ - @Override - public void columnSelectionChanged(ListSelectionEvent e) { - } - }; - } - - -} // end class ColumnControlButton diff --git a/src/main/java/org/jdesktop/swingx/table/ColumnControlPopup.java b/src/main/java/org/jdesktop/swingx/table/ColumnControlPopup.java deleted file mode 100644 index 976b4c494b..0000000000 --- a/src/main/java/org/jdesktop/swingx/table/ColumnControlPopup.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * $Id: ColumnControlPopup.java 4065 2011-08-19 13:28:26Z kleopatra $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.table; - -import org.jdesktop.swingx.action.AbstractActionExt; -import org.jdesktop.swingx.plaf.UIDependent; - -import javax.swing.*; -import java.awt.*; -import java.util.List; - -/** - * Encapsulates the popup component which is the delegate for - * all popup visuals, used by a ColumnControlButton. - *

                          - * For now, this class a simple extraction of what a ColumnControl needs. - * Usage will drive further evolution. - * - */ -public interface ColumnControlPopup extends UIDependent { - /** - * Toggles the popup's visibility. This method is responsible for - * placing itself relative to the given owner if toggled to visible. - * - * @param owner the JComponent which triggered the visibility change, typically - * a ColumnControlButton. - */ - void toggleVisibility(JComponent owner); - - /** - * Applies the specified component orientation to all internal widgets. - * This method must be called by the owner if its component orientation - * changes. - * - * @param o the componentOrientation to apply to all internal widgets. - * @see JComponent#applyComponentOrientation(ComponentOrientation). - */ - void applyComponentOrientation(ComponentOrientation o); - - /** - * Removes all items from the popup. - */ - void removeAll(); - - /** - * Adds items corresponding to the column's visibility actions. - *

                          - * Each Action in the list is a stateAction, - * its selected property bound to a column's - * visible property, that is toggling the selected will - * toggle the column's visibility (if the action is enabled). - * - * The Actions name property is bound to - * the column's title. - * - * @param actions List of AbstractActionExt to add. - */ - void addVisibilityActionItems(List actions); - // JW: dooohhh ... what a winding description ... - // sure need to have a better abstraction! - // - - /** - * Adds additional actions to the popup. - * - * @param actions List of Actions to add to the popup. - */ - void addAdditionalActionItems(List actions); - - /** - * Splits and returns a List of actions into sub-lists. - */ - public interface ActionGrouper { - List> group(List actions); - } - - /** - * Interface indicating support for grouping of menu actions. - * Implementations of ColumnControlPopup may implement this - * if they support grouping of additional action. - */ - public interface ActionGroupable { - public void setActionGrouper(ActionGrouper grouper); - } - - -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/table/ColumnFactory.java b/src/main/java/org/jdesktop/swingx/table/ColumnFactory.java deleted file mode 100644 index c08d405f50..0000000000 --- a/src/main/java/org/jdesktop/swingx/table/ColumnFactory.java +++ /dev/null @@ -1,465 +0,0 @@ -/* - * $Id: ColumnFactory.java 3554 2009-11-06 09:07:55Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.table; - -import org.jdesktop.swingx.JXTable; - -import javax.swing.table.JTableHeader; -import javax.swing.table.TableCellRenderer; -import javax.swing.table.TableModel; -import java.awt.*; - -/** - * Creates and configures TableColumnExts. - *

                          - * TODO JW: explain types of configuration - initial from tableModel, initial - * from table context, user triggered at runtime. - *

                          - * - * JXTable delegates all TableColumn creation and - * configuration to a ColumnFactory. Enhanced column - * configuration should be implemented in a custom factory subclass. The example - * beautifies the column titles to always start with a capital letter: - * - *

                          - * 
                          - *    MyColumnFactory extends ColumnFactory {
                          - *        //@Override
                          - *        public void configureTableColumn(TableModel model, 
                          - *            TableColumnExt columnExt) {
                          - *            super.configureTableColumn(model, columnExt);
                          - *            String title = columnExt.getTitle();
                          - *            title = title.substring(0,1).toUpperCase() + title.substring(1).toLowerCase();
                          - *            columnExt.setTitle(title);
                          - *        }
                          - *    };
                          - * 
                          - * 
                          - * - * By default a single instance is shared across all tables of an application. - * This instance can be replaced by a custom implementation, preferably "early" - * in the application's lifetime. - * - *
                          
                          - * ColumnFactory.setInstance(new MyColumnFactory());
                          - * 
                          - * - * Alternatively, any instance of JXTable can be configured - * individually with its own ColumnFactory. - * - *
                          - *  
                          - * JXTable table = new JXTable();
                          - * table.setColumnFactory(new MyColumnFactory());
                          - * table.setModel(myTableModel);
                          - * 
                          - *  
                          - * - *

                          - * - * @see JXTable#setColumnFactory(ColumnFactory) - * - * @author Jeanette Winzenburg - * @author M.Hillary (the pack code) - */ -public class ColumnFactory { - - /** the shared instance. */ - private static ColumnFactory columnFactory; - /** the default margin to use in pack. */ - private int packMargin = 4; - - /** - * Returns the shared default factory. - * - * @return the shared instance of ColumnFactory - * @see #setInstance(ColumnFactory) - */ - public static synchronized ColumnFactory getInstance() { - if (columnFactory == null) { - columnFactory = new ColumnFactory(); - } - return columnFactory; - } - - /** - * Sets the shared default factory. The shared instance is used - * by JXTable if none has been set individually. - * - * @param factory the default column factory. - * @see #getInstance() - * @see JXTable#getColumnFactory() - */ - public static synchronized void setInstance(ColumnFactory factory) { - columnFactory = factory; - } - - /** - * Creates and configures a TableColumnExt. JXTable calls - * this method for each column in the TableModel. - * - * @param model the TableModel to read configuration properties from - * @param modelIndex column index in model coordinates - * @return a TableColumnExt to use for the modelIndex - * @throws NPE if model == null - * @throws IllegalStateException if the modelIndex is invalid - * (in coordinate space of the tablemodel) - * - * @see #createTableColumn(int) - * @see #configureTableColumn(TableModel, TableColumnExt) - * @see JXTable#createDefaultColumnsFromModel() - */ - public TableColumnExt createAndConfigureTableColumn(TableModel model, int modelIndex) { - TableColumnExt column = createTableColumn(modelIndex); - if (column != null) { - configureTableColumn(model, column); - } - return column; - } - - /** - * Creates a table column with modelIndex. - *

                          - * The factory's column creation is passed through this method, so - * subclasses can override to return custom column types. - * - * @param modelIndex column index in model coordinates - * @return a TableColumnExt with modelIndex - * - * @see #createAndConfigureTableColumn(TableModel, int) - * - */ - public TableColumnExt createTableColumn(int modelIndex) { - return new TableColumnExt(modelIndex); - } - - /** - * Configure column properties from TableModel. This implementation - * sets the column's headerValue property from the - * model's columnName. - *

                          - * - * The factory's initial column configuration is passed through this method, so - * subclasses can override to customize. - *

                          - * - * @param model the TableModel to read configuration properties from - * @param columnExt the TableColumnExt to configure. - * @throws NullPointerException if model or column == null - * @throws IllegalStateException if column does not have valid modelIndex - * (in coordinate space of the tablemodel) - * - * @see #createAndConfigureTableColumn(TableModel, int) - */ - public void configureTableColumn(TableModel model, TableColumnExt columnExt) { - if ((columnExt.getModelIndex() < 0) - || (columnExt.getModelIndex() >= model.getColumnCount())) - throw new IllegalStateException("column must have valid modelIndex"); - columnExt.setHeaderValue(model.getColumnName(columnExt.getModelIndex())); - } - - - /** - * Configures column initial widths properties from JXTable. - * This implementation sets the column's - * preferredWidth with the strategy: - *

                            if the column has a prototype, measure the rendering - * component with the prototype as value and use that as - * pref width - *
                              if the column has no prototype, use the standard magic - * pref width (= 75) - *
                                try to measure the column's header and use it's preferred - * width if it exceeds the former. - *
                              - * - * TODO JW - rename method to better convey what's happening, maybe - * initializeColumnWidths like the old method in JXTable.

                              - * - * TODO JW - how to handle default settings which are different from - * standard 75? - * - * @param table the context the column will live in. - * @param columnExt the Tablecolumn to configure. - * - * @see JXTable#getPreferredScrollableViewportSize() - */ - public void configureColumnWidths(JXTable table, TableColumnExt columnExt) { - /* - * PENDING JW: really only called once in a table's lifetime? - * unfortunately: yes - should be called always after structureChanged. - * - */ - // magic value: default in TableColumn - int prefWidth = 75 - table.getColumnMargin(); - int prototypeWidth = calcPrototypeWidth(table, columnExt); - if (prototypeWidth > 0) { - prefWidth = prototypeWidth; - } - int headerWidth = calcHeaderWidth(table, columnExt); - prefWidth = Math.max(prefWidth, headerWidth); - prefWidth += table.getColumnModel().getColumnMargin(); - columnExt.setPreferredWidth(prefWidth); - } - - /** - * Calculates and returns the preferred scrollable viewport - * width of the given table. Subclasses are free to override - * and implement a custom strategy.

                              - * - * This implementation sums the pref widths of the first - * visibleColumnCount contained visible tableColumns. If - * the table contains less columns, the standard preferred - * width per column is added to the result. - * - * @param table the table containing the columns - */ - public int getPreferredScrollableViewportWidth(JXTable table) { - int w = 0; - int count; - if (table.getVisibleColumnCount() < 0) { - count = table.getColumnCount(); - } else { - count = Math.min(table.getColumnCount(), table.getVisibleColumnCount()); - } - for (int i = 0; i < count; i++) { - // sum up column's pref size, until maximal the - // visibleColumnCount - w += table.getColumn(i).getPreferredWidth(); - } - if (count < table.getVisibleColumnCount()) { - w += (table.getVisibleColumnCount() - count) * 75; - } - return w; - - } - /** - * Measures and returns the preferred width of the header. Returns -1 if not - * applicable. - * - * @param table the component the renderer lives in - * @param columnExt the TableColumn to configure - * @return the preferred width of the header or -1 if none. - */ - protected int calcHeaderWidth(JXTable table, TableColumnExt columnExt) { - int prototypeWidth = -1; - // now calculate how much room the column header wants - TableCellRenderer renderer = getHeaderRenderer(table, columnExt); - if (renderer != null) { - Component comp = renderer.getTableCellRendererComponent(table, - columnExt.getHeaderValue(), false, false, -1, -1); - - prototypeWidth = comp.getPreferredSize().width; - } - return prototypeWidth; - } - - /** - * Measures and returns the preferred width of the rendering component - * configured with the prototype value, if any. Returns -1 if not - * applicable. - * - * @param table the component the renderer lives in - * @param columnExt the TableColumn to configure - * @return the preferred width of the prototype or -1 if none. - */ - protected int calcPrototypeWidth(JXTable table, TableColumnExt columnExt) { - int prototypeWidth = -1; - Object prototypeValue = columnExt.getPrototypeValue(); - if (prototypeValue != null) { - // calculate how much room the prototypeValue requires - TableCellRenderer cellRenderer = getCellRenderer(table, columnExt); - Component comp = cellRenderer.getTableCellRendererComponent(table, - prototypeValue, false, false, 0, -1); - prototypeWidth = comp.getPreferredSize().width; - } - return prototypeWidth; - } - - /** - * Returns the cell renderer to use for measuring. Delegates to - * JXTable for visible columns, duplicates table logic for hidden - * columns.

                              - * - * @param table the table which provides the renderer - * @param columnExt the TableColumn to configure - * - * @return returns a cell renderer for measuring. - */ - protected TableCellRenderer getCellRenderer(JXTable table, TableColumnExt columnExt) { - int viewIndex = table.convertColumnIndexToView(columnExt - .getModelIndex()); - if (viewIndex >= 0) { - // JW: ok to not guard against rowCount < 0? - // technically, the index should be a valid coordinate - return table.getCellRenderer(0, viewIndex); - } - // hidden column - need api on JXTable to access renderer for hidden? - // here we duplicate JXTable api ... maybe by-passing the strategy - // implemented there - TableCellRenderer renderer = columnExt.getCellRenderer(); - if (renderer == null) { - renderer = table.getDefaultRenderer(table.getModel().getColumnClass(columnExt.getModelIndex())); - } - return renderer; - } - - /** - * Looks up and returns the renderer used for the column's header.

                              - * - * @param table the table which contains the column - * @param columnExt the column to lookup the header renderer for - * @return the renderer for the columns header, may be null. - */ - protected TableCellRenderer getHeaderRenderer(JXTable table, TableColumnExt columnExt) { - TableCellRenderer renderer = columnExt.getHeaderRenderer(); - if (renderer == null) { - JTableHeader header = table.getTableHeader(); - if (header != null) { - renderer = header.getDefaultRenderer(); - } - } - // JW: default to something if null? - // if so, could be table's default object/string header? - return renderer; - } - - - /** - * Configures the column's preferredWidth to fit the content. - * It respects the table context, a margin to add and a maximum width. This - * is typically called in response to a user gesture to adjust the column's - * width to the "widest" cell content of a column. - *

                              - * - * This implementation loops through all rows of the given column and - * measures the renderers pref width (it's a potential performance sink). - * Subclasses can override to implement a different strategy. - *

                              - * - * Note: though 2 * margin is added as spacing, this does not imply - * a left/right symmetry - it's up to the table to place the renderer and/or - * the renderer/highlighter to configure a border.

                              - * - * PENDING: support pack for hidden column? - * This implementation can't handle it! For now, added doc and - * fail-fast. - * - * @param table the context the column will live in. - * @param columnExt the column to configure. - * @param margin the extra spacing to add twice, if -1 uses this factories - * default - * @param max an upper limit to preferredWidth, -1 is interpreted as no - * limit - * @throws IllegalStateException if column is not visible - * - * @see #setDefaultPackMargin(int) - * @see JXTable#packTable(int) - * @see JXTable#packColumn(int, int) - * - */ - public void packColumn(JXTable table, TableColumnExt columnExt, int margin, - int max) { - if (!columnExt.isVisible()) - throw new IllegalStateException("column must be visible to pack"); - - int column = table.convertColumnIndexToView(columnExt.getModelIndex()); - int width = 0; - TableCellRenderer headerRenderer = getHeaderRenderer(table, columnExt); - if (headerRenderer != null) { - Component comp = headerRenderer.getTableCellRendererComponent(table, - columnExt.getHeaderValue(), false, false, 0, column); - width = comp.getPreferredSize().width; - } - // PENDING JW: slightly inconsistent - the getCellRenderer here - // returns a (guessed) renderer for invisible columns which must not - // be used in the loop. For now that's okay, as we back out early anyway - TableCellRenderer renderer = getCellRenderer(table, columnExt); - for (int r = 0; r < getRowCount(table); r++) { - // JW: fix for #1215-swing as suggested by the reporter adrienclerc - Component comp = table.prepareRenderer(renderer, r, column); -// Component comp = renderer.getTableCellRendererComponent(table, table -// .getValueAt(r, column), false, false, r, column); - width = Math.max(width, comp.getPreferredSize().width); - } - if (margin < 0) { - margin = getDefaultPackMargin(); - } - width += 2 * margin; - - /* Check if the width exceeds the max */ - if (max != -1 && width > max) - width = max; - - columnExt.setPreferredWidth(width); - - } - - /** - * Returns the number of table view rows accessible during row-related - * config. All row-related access is bounded by the value returned from this - * method. - * - * Here: delegates to table.getRowCount(). - *

                              - * - * Subclasses can override to reduce the number (for performance) or support - * restrictions due to lazy loading, f.i. Implementors must guarantee that - * view row access with 0 <= row < getRowCount(JXTable) - * succeeds. - * - * @param table the table to access - * @return valid rowCount - */ - protected int getRowCount(JXTable table) { - return table.getRowCount(); - } - -// ------------------------ default state - - /** - * Returns the default pack margin. - * - * @return the default pack margin to use in packColumn. - * - * @see #setDefaultPackMargin(int) - */ - public int getDefaultPackMargin() { - return packMargin; - } - - /** - * Sets the default pack margin.

                              - * - * Note: this is not really a margin in the sense of symmetrically - * adding white space to the left/right of a cell's content. It's simply an - * amount of space which is added twice to the measured widths in packColumn. - * - * @param margin the default marging to use in packColumn. - * - * @see #getDefaultPackMargin() - * @see #packColumn(JXTable, TableColumnExt, int, int) - */ - public void setDefaultPackMargin(int margin) { - this.packMargin = margin; - } - - -} diff --git a/src/main/java/org/jdesktop/swingx/table/DatePickerCellEditor.java b/src/main/java/org/jdesktop/swingx/table/DatePickerCellEditor.java deleted file mode 100644 index 3bb251a775..0000000000 --- a/src/main/java/org/jdesktop/swingx/table/DatePickerCellEditor.java +++ /dev/null @@ -1,334 +0,0 @@ -/* - * $Id: DatePickerCellEditor.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.table; - -import org.jdesktop.swingx.JXDatePicker; -import org.jdesktop.swingx.treetable.AbstractMutableTreeTableNode; - -import javax.swing.*; -import javax.swing.table.TableCellEditor; -import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.TreeCellEditor; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseEvent; -import java.text.DateFormat; -import java.text.ParseException; -import java.util.Date; -import java.util.EventObject; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * A CellEditor using a JXDatePicker as editor component.

                              - * - * NOTE: this class will be moved! - * - * @author Richard Osbald - * @author Jeanette Winzenburg - */ -public class DatePickerCellEditor extends AbstractCellEditor implements - TableCellEditor, TreeCellEditor { - - protected JXDatePicker datePicker; - - protected DateFormat dateFormat; - - protected int clickCountToStart = 2; - - private ActionListener pickerActionListener; - - protected boolean ignoreAction; - - private static Logger logger = Logger.getLogger(DatePickerCellEditor.class - .getName()); - - private static final long serialVersionUID = -1L; - - /** - * Instantiates a editor with the default dateFormat. - * - * PENDING: always override default from DatePicker? - * - */ - public DatePickerCellEditor() { - this(null); - } - - /** - * Instantiates an editor with the given dateFormat. If - * null, the datePickers default is used. - * - * @param dateFormat - */ - public DatePickerCellEditor(DateFormat dateFormat) { - // JW: the copy is used to synchronize .. can - // we use something else? - this.dateFormat = dateFormat != null ? dateFormat : - DateFormat.getDateInstance(); - datePicker = new JXDatePicker(); - // default border crushes the editor/combo - datePicker.getEditor().setBorder( - BorderFactory.createEmptyBorder(0, 1, 0, 1)); - // should be fixed by j2se 6.0 - datePicker.setFont(UIManager.getDefaults().getFont("TextField.font")); - if (dateFormat != null) { - datePicker.setFormats(dateFormat); - } - datePicker.addActionListener(getPickerActionListener()); - } - -//-------------------- CellEditor - - /** - * Returns the pickers date. - * - * Note: the date is only meaningful after a stopEditing and - * before the next call to getTableCellEditorComponent. - */ - @Override - public Date getCellEditorValue() { - return datePicker.getDate(); - } - - @Override - public boolean isCellEditable(EventObject anEvent) { - if (anEvent instanceof MouseEvent) { - return ((MouseEvent) anEvent).getClickCount() >= getClickCountToStart(); - } - return super.isCellEditable(anEvent); - } - - /** - * {@inheritDoc} - *

                              - * - * Overridden to commit pending edits. If commit successful, returns super, - * else returns false. - * - * - */ - @Override - public boolean stopCellEditing() { - ignoreAction = true; - boolean canCommit = commitChange(); - ignoreAction = false; - if (canCommit) { - return super.stopCellEditing(); - } - return false; - } - - /** - * Specifies the number of clicks needed to start editing. - * - * @param count an int specifying the number of clicks needed to start - * editing - * @see #getClickCountToStart - */ - public void setClickCountToStart(int count) { - clickCountToStart = count; - } - - /** - * Returns the number of clicks needed to start editing. - * - * @return the number of clicks needed to start editing - */ - public int getClickCountToStart() { - return clickCountToStart; - } - - -//------------------------ TableCellEditor - - @Override - public Component getTableCellEditorComponent(JTable table, Object value, - boolean isSelected, int row, int column) { - // PENDING JW: can remove the ignore flags here? - // the picker learnde to behave ... - ignoreAction = true; - datePicker.setDate(getValueAsDate(value)); - // todo how to.. - // SwingUtilities.invokeLater(new Runnable() { - // public void run() { - // datePicker.getEditor().selectAll(); - // } - // }); - ignoreAction = false; - return datePicker; - } - - //------------------------- TreeCellEditor - - @Override - public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row) { - // PENDING JW: can remove the ignore flags here? - // the picker learnde to behave ... - ignoreAction = true; - datePicker.setDate(getValueAsDate(value)); - // todo how to.. - // SwingUtilities.invokeLater(new Runnable() { - // public void run() { - // datePicker.getEditor().selectAll(); - // } - // }); - ignoreAction = false; - return datePicker; - } - -//-------------------- helpers - - /** - * Returns the given value as Date. - * - * PENDING: abstract into something pluggable (like StringValue - * in ComponentProvider?) - * - * @param value the value to map as Date - * @return the value as Date or null, if not successful. - * - */ - protected Date getValueAsDate(Object value) { - if (isEmpty(value)) return null; - if (value instanceof Date) { - return (Date) value; - } - if (value instanceof Long) { - return new Date((Long) value); - } - if (value instanceof String) { - try { - // JW: why was the parsing synchronized? -// synchronized (dateFormat) { -// datePicker.setDate(dateFormat.parse((String) value)); -// } - return dateFormat.parse((String) value); - } catch (ParseException e) { - handleParseException(e); - } - } - if (value instanceof DefaultMutableTreeNode) { - return getValueAsDate(((DefaultMutableTreeNode) value).getUserObject()); - } - if (value instanceof AbstractMutableTreeTableNode) { - return getValueAsDate(((AbstractMutableTreeTableNode) value).getUserObject()); - } - return null; - } - - /** - * @param e - */ - protected void handleParseException(ParseException e) { - logger.log(Level.SEVERE, e.getMessage(), e.getMessage()); - } - - protected boolean isEmpty(Object value) { - return value == null || value instanceof String - && ((String) value).length() == 0; - } - -//--------------- picker specifics - /** - * Commits any pending edits and returns a boolean indicating whether the - * commit was successful. - * - * @return true if the edit was valid, false otherwise. - */ - protected boolean commitChange() { - try { - datePicker.commitEdit(); - return true; - } catch (ParseException e) { - } - return false; - } - - /** - * - * @return the DatePicker's formats. - * - * @see JXDatePicker#getFormats(). - */ - public DateFormat[] getFormats() { - return datePicker.getFormats(); - } - - /** - * - * @param formats the formats to use in the datepicker. - * - * @see JXDatePicker#setFormats(DateFormat...) - * - */ - public void setFormats(DateFormat... formats) { - datePicker.setFormats(formats); - } - /** - * Returns the ActionListener to add to the datePicker. - * - * @return the action listener to listen for datePicker's - * action events. - */ - protected ActionListener getPickerActionListener() { - if (pickerActionListener == null) { - pickerActionListener = createPickerActionListener(); - } - return pickerActionListener; - } - - /** - * Creates and returns the ActionListener for the Picker. - * @return the ActionListener to listen for Picker's action events. - */ - protected ActionListener createPickerActionListener() { - ActionListener l = new ActionListener() { - @Override - public void actionPerformed(final ActionEvent e) { - // avoid duplicate trigger from - // commit in stopCellEditing - if (ignoreAction) - return; - // still need to invoke .. hmm - // no ... with the table cooperating the - // invoke is contra-productive! - terminateEdit(e); - } - - /** - * @param e - */ - private void terminateEdit(final ActionEvent e) { - if ((e != null) - && (JXDatePicker.COMMIT_KEY.equals(e.getActionCommand()))) { - stopCellEditing(); - } else { - cancelCellEditing(); - } - } - }; - return l; - } - - -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/table/DefaultTableColumnModelExt.java b/src/main/java/org/jdesktop/swingx/table/DefaultTableColumnModelExt.java deleted file mode 100644 index d64182a010..0000000000 --- a/src/main/java/org/jdesktop/swingx/table/DefaultTableColumnModelExt.java +++ /dev/null @@ -1,388 +0,0 @@ -/* - * $Id: DefaultTableColumnModelExt.java 3975 2011-03-28 14:05:29Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.table; - -import org.jdesktop.swingx.event.TableColumnModelExtListener; - -import javax.swing.event.EventListenerList; -import javax.swing.event.TableColumnModelListener; -import javax.swing.table.DefaultTableColumnModel; -import javax.swing.table.TableColumn; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - - -/** - * A default implementation of TableColumnModelExt. - *

                              - * - * TODO: explain sub-optimal notification on showing/hiding columns. - * (hot fixed issues #156, #157. To really do it - * need enhanced TableColumnModelEvent and -Listeners that are - * aware of the event.) - * - * - * @author Richard Bair - * @author Jeanette Winzenburg - */ -public class DefaultTableColumnModelExt extends DefaultTableColumnModel - implements TableColumnModelExt { - /** flag to distinguish a shown/hidden column from really added/removed - * columns during notification. This is brittle! - */ -// private static final String IGNORE_EVENT = "TableColumnModelExt.ignoreEvent"; - private boolean isVisibilityChange; - /** - * contains a list of all columns, in the order in which were - * added to the model. - */ - private List initialColumns = new ArrayList(); - - /** - * contains a list of all column, in the order they would appear if - * all were visible. - */ - private List currentColumns = new ArrayList(); - - /** - * Listener attached to TableColumnExt instances to listen for changes - * to their visibility status, and to hide/show the column as oppropriate - */ - private VisibilityListener visibilityListener = new VisibilityListener(); - - /** - * Creates a an empty DefaultTableColumnModelExt. - */ - public DefaultTableColumnModelExt() { - super(); - } - -//----------------------- implement TableColumnModelExt - - /** - * {@inheritDoc} - */ - @Override - public List getColumns(boolean includeHidden) { - if (includeHidden) { - return new ArrayList(initialColumns); - } - return Collections.list(getColumns()); - } - - /** - * {@inheritDoc} - */ - @Override - public int getColumnCount(boolean includeHidden) { - if (includeHidden) { - return initialColumns.size(); - } - return getColumnCount(); - } - - /** - * {@inheritDoc} - */ - @Override - public TableColumnExt getColumnExt(Object identifier) { - for (Iterator iter = initialColumns.iterator(); iter.hasNext();) { - TableColumn column = iter.next(); - if ((column instanceof TableColumnExt) && (identifier.equals(column.getIdentifier()))) { - return (TableColumnExt) column; - } - } - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public TableColumnExt getColumnExt(int columnIndex) { - TableColumn column = getColumn(columnIndex); - if (column instanceof TableColumnExt) { - return (TableColumnExt) column; - } - return null; - } - - /** - * hot fix for #157: listeners that are aware of - * the possible existence of invisible columns - * should check if the received columnRemoved originated - * from moving a column from visible to invisible. - * - * @param oldIndex the fromIndex of the columnEvent - * @return true if the column was moved to invisible - */ - public boolean isRemovedToInvisibleEvent(int oldIndex) { - return isVisibilityChange; - } - - /** - * hot fix for #157: listeners that are aware of - * the possible existence of invisible columns - * should check if the received columnAdded originated - * from moving a column from invisible to visible. - * - * @param newIndex the toIndex of the columnEvent - * @return true if the column was moved to visible - */ - public boolean isAddedFromInvisibleEvent(int newIndex) { - return isVisibilityChange; - } - -//------------------------ TableColumnModel - - /** - * {@inheritDoc}

                              - * - * Overridden to update internals related to column visibility. - */ - @Override - public void removeColumn(TableColumn column) { - boolean oldVisible = true; - //remove the visibility listener if appropriate - if (column instanceof TableColumnExt) { - oldVisible = ((TableColumnExt) column).isVisible(); - ((TableColumnExt) column).setVisible(true); - ((TableColumnExt)column).removePropertyChangeListener(visibilityListener); - } - currentColumns.remove(column); - initialColumns.remove(column); - //let the superclass handle notification etc - super.removeColumn(column); - if (column instanceof TableColumnExt) { - ((TableColumnExt) column).setVisible(oldVisible); - } - } - - /** - * {@inheritDoc}

                              - * - * Overridden to update internals related to column visibility. - */ - @Override - public void addColumn(TableColumn aColumn) { - // hacking to guarantee correct events - // two step: add as visible, setVisible - boolean oldVisible = true; - //add the visibility listener if appropriate - if (aColumn instanceof TableColumnExt) { - TableColumnExt xColumn = (TableColumnExt) aColumn; - oldVisible = xColumn.isVisible(); - xColumn.setVisible(true); - xColumn.addPropertyChangeListener(visibilityListener); - } - // append the column to the end of both initial- and currentColumns. - currentColumns.add(aColumn); - initialColumns.add(aColumn); - // let super handle the event notification, super.book-keeping - super.addColumn(aColumn); - if (aColumn instanceof TableColumnExt) { - // reset original visibility - ((TableColumnExt) aColumn).setVisible(oldVisible); - } - - } - - /** - * {@inheritDoc}

                              - * - * Overridden to update internals related to column visibility. - */ - @Override - public void moveColumn(int columnIndex, int newIndex) { - if (columnIndex != newIndex) { - updateCurrentColumns(columnIndex, newIndex); - } - super.moveColumn(columnIndex, newIndex); - } - - /** - * Adjusts the current column sequence when a visible column is moved. - * - * @param oldIndex the old visible position. - * @param newIndex the new visible position. - */ - private void updateCurrentColumns(int oldIndex, int newIndex) { - TableColumn movedColumn = tableColumns.elementAt(oldIndex); - int oldPosition = currentColumns.indexOf(movedColumn); - TableColumn targetColumn = tableColumns.elementAt(newIndex); - int newPosition = currentColumns.indexOf(targetColumn); - currentColumns.remove(oldPosition); - currentColumns.add(newPosition, movedColumn); - - } - - /** - * Update internal state after the visibility of the column - * was changed to invisible. The given column is assumed to - * be contained in this model. - * - * @param col the column which was hidden. - */ - protected void moveToInvisible(TableColumnExt col) { - isVisibilityChange = true; - super.removeColumn(col); - isVisibilityChange = false; - } - - - /** - * Update internal state after the visibility of the column - * was changed to visible. The given column is assumed to - * be contained in this model. - * - * @param col the column which was made visible. - */ - protected void moveToVisible(TableColumnExt col) { - isVisibilityChange = true; - // two step process: first add at end of columns - // then move to "best" position relative to where it - // was before hiding. - super.addColumn(col); - // this is analogous to the proposed fix in #253-swingx - // but uses the currentColumns as reference. - Integer addIndex = currentColumns.indexOf(col); - for (int i = 0; i < (getColumnCount() - 1); i++) { - TableColumn tableCol = getColumn(i); - int actualPosition = currentColumns.indexOf(tableCol); - if (actualPosition > addIndex) { - super.moveColumn(getColumnCount() - 1, i); - break; - } - } - isVisibilityChange = false; - } - - - /** - * TODO JW: move into propertyChanged! No need for a dedicated listener. - * Changed evaluation JW: may still be required as super removes itself as - * propertyChangeListener if column is hidden - */ - private class VisibilityListener implements PropertyChangeListener, Serializable { - @Override - public void propertyChange(PropertyChangeEvent evt) { - if ("visible".equals(evt.getPropertyName())) { - TableColumnExt columnExt = (TableColumnExt)evt.getSource(); - - if (columnExt.isVisible()) { - moveToVisible(columnExt); - fireColumnPropertyChange(evt); - } else { - moveToInvisible(columnExt); - } - } else if (!((TableColumnExt) evt.getSource()).isVisible()) { - fireColumnPropertyChange(evt); - } - } - } - - // enhanced listener notification - - - /** - * Exposed for testing only - don't use! Will be removed again! - * @return super's listener list - */ - protected EventListenerList getEventListenerList() { - return listenerList; - } - - - - /** - * {@inheritDoc} - */ - @Override - public void propertyChange(PropertyChangeEvent evt) { - super.propertyChange(evt); - fireColumnPropertyChange(evt); - } - - /** - * Notifies TableColumnModelExtListeners about property - * changes of contained columns. The event instance - * is the original as fired by the TableColumn. - * @param evt the event received - * @see EventListenerList - */ - protected void fireColumnPropertyChange(PropertyChangeEvent evt) { -// if (IGNORE_EVENT.equals(evt.getPropertyName())) return; - // Guaranteed to return a non-null array - Object[] listeners = listenerList.getListenerList(); - // Process the listeners last to first, notifying - // those that are interested in this event - for (int i = listeners.length-2; i>=0; i-=2) { - if (listeners[i]==TableColumnModelExtListener.class) { - ((TableColumnModelExtListener)listeners[i+1]). - columnPropertyChange(evt); - } - } - } - - - /** - * {@inheritDoc}

                              - * - * - * Overridden to install enhanced notification of listeners of type. - * TableColumnModelListenerExt about property changes of contained columns. - * - */ - @Override - public void addColumnModelListener(TableColumnModelListener x) { - super.addColumnModelListener(x); - if (x instanceof TableColumnModelExtListener) { - listenerList.add(TableColumnModelExtListener.class, (TableColumnModelExtListener) x); - } - } - - /** - * {@inheritDoc}

                              - * - * Overridden to uninstall enhanced notification of listeners of type. - * TableColumnModelListenerExt about property changes of contained columns. - */ - @Override - public void removeColumnModelListener(TableColumnModelListener x) { - super.removeColumnModelListener(x); - if (x instanceof TableColumnModelExtListener) { - listenerList.remove(TableColumnModelExtListener.class, (TableColumnModelExtListener) x); - } - } - - /** - * @return array of all registered listeners - */ - public TableColumnModelExtListener[] getTableColumnModelExtListeners() { - return listenerList.getListeners(TableColumnModelExtListener.class); - } -} diff --git a/src/main/java/org/jdesktop/swingx/table/NumberEditorExt.java b/src/main/java/org/jdesktop/swingx/table/NumberEditorExt.java deleted file mode 100644 index 4402a40c1c..0000000000 --- a/src/main/java/org/jdesktop/swingx/table/NumberEditorExt.java +++ /dev/null @@ -1,335 +0,0 @@ -/* - * $Id: NumberEditorExt.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.table; - -import org.jdesktop.swingx.text.NumberFormatExt; -import org.jdesktop.swingx.text.StrictNumberFormatter; - -import javax.swing.*; -import javax.swing.border.LineBorder; -import javax.swing.text.NumberFormatter; -import java.awt.*; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.text.NumberFormat; -import java.text.ParseException; - - -/** - * - * Issue #393-swingx: localized NumberEditor. Added feature to use StrictNumberFormatter. - * - * @author Noel Grandin - * @author Jeanette Winzenburg - */ -public class NumberEditorExt extends DefaultCellEditor { - - private static Class[] argTypes = new Class[]{String.class}; - java.lang.reflect.Constructor constructor; - private boolean useStrictFormatter; - - /** - * Instantiates an editor with default NumberFormat and default NumberFormatter. - */ - public NumberEditorExt() { - this(null); - } - - /** - * Instantiates an editor with the given NumberFormat and default NumberFormatter. - * - * @param format the NumberFormat to use for conversion, may be null to indicate - * usage of default NumberFormat. - */ - public NumberEditorExt(NumberFormat format) { - this(format, false); - } - - /** - * Instantiates an editor with default NumberFormat and NumberFormatter depending - * on useStrictFormatter. - * - * @param useStrictFormatter if true, uses a StrictNumberFormatter, else uses - * default NumberFormatter - */ - public NumberEditorExt(boolean useStrictFormatter) { - this(null, useStrictFormatter); - } - - /** - * Instantiates an editor with the given NumberFormat and NumberFormatter depending on - * useStrictFormatter. - * - * @param format the NumberFormat to use for conversion, may be null to indicate - * usage of default NumberFormat - * @param useStrictFormatter if true, uses a StrictNumberFormatter, else uses - * default NumberFormatter - */ - public NumberEditorExt(NumberFormat format, boolean useStrictFormatter) { - - super(useStrictFormatter ? createFormattedTextFieldX(format) : createFormattedTextField(format)); - this.useStrictFormatter = useStrictFormatter; - final JFormattedTextField textField = getComponent(); - - textField.setName("Table.editor"); - textField.setHorizontalAlignment(JTextField.RIGHT); - - // remove action listener added in DefaultCellEditor - textField.removeActionListener(delegate); - // replace the delegate created in DefaultCellEditor - delegate = new EditorDelegate() { - @Override - public void setValue(Object value) { - getComponent().setValue(value); - } - - @Override - public Object getCellEditorValue() { - try { - getComponent().commitEdit(); - return getComponent().getValue(); - } catch (ParseException ex) { - return null; - } - } - }; - textField.addActionListener(delegate); - } - - @Override - public boolean stopCellEditing() { - if (!isValid()) return false; - return super.stopCellEditing(); - } - - /** - * Returns a boolean indicating whether the current text is valid for - * instantiating the expected Number type. - * - * @return true if text is valid, false otherwise. - */ - protected boolean isValid() { - if (!getComponent().isEditValid()) return false; - try { - if (!hasStrictFormatter()) - getNumber(); - return true; - } catch (Exception ex) { - - } - return false; - } - - /** - * Returns the editor value as number. May fail for a variety of reasons, - * as it forces parsing of the current text as well as reflective construction - * of the target type. - * - * @return the editor value or null - * @throws Exception if creation of the expected type fails in some way. - */ - protected Number getNumber() throws Exception { - Number number = (Number) super.getCellEditorValue(); - if (number==null) return null; - return hasStrictFormatter() ? number : - (Number) constructor.newInstance(new Object[]{number.toString()}); - } - - /** - * @return - */ - protected boolean hasStrictFormatter() { - return useStrictFormatter; - } - - /** Override and set the border back to normal in case there was an error previously */ - @Override - public Component getTableCellEditorComponent(JTable table, Object value, - boolean isSelected, - int row, int column) { - ((JComponent)getComponent()).setBorder(new LineBorder(Color.black)); - try { - final Class type = table.getColumnClass(column); - if (hasStrictFormatter()) { - // delegate to formatter which decides at parsing time - // then either handles or throws - ((NumberFormatter) getComponent().getFormatter()).setValueClass(type); - } else { - // Assume that the Number object we are dealing with has a constructor which takes - // a single string parameter. - if (!Number.class.isAssignableFrom(type)) { - throw new IllegalStateException("NumberEditor can only handle subclasses of java.lang.Number"); - } - constructor = type.getConstructor(argTypes); - } - // JW: in strict mode this may fail in setting the value in the formatter - return super.getTableCellEditorComponent(table, value, isSelected, row, column); - } catch (Exception ex) { - // PENDING JW: super generic editor swallows all failures and returns null - // should we do so as well? - throw new IllegalStateException("value/type not compatible with Number", ex); - } - } - - /** - * {@inheritDoc}

                              - * - * Overridden to instantiate a Number of the expected type. Note that this - * may throw a IllegalStateException if invoked without querying - * for a valid value with stopCellEditing. This should not happen during - * normal usage. - * - * @throws IllegalStateException if current value invalid - * - */ - @Override - public Number getCellEditorValue() throws IllegalStateException { - try { - return getNumber(); - } catch (Exception ex) { - throw new IllegalStateException("Number conversion not possible from " + - "current string " + getComponent().getText()); - } - } - - - /** - * {@inheritDoc}

                              - * - * Convenience override with type cast. - */ - @Override - public JFormattedTextField getComponent() { - return (JFormattedTextField) super.getComponent(); - } - - /** - * Creates and returns a JFormattedTextField configured with SwingX extended - * NumberFormat and StrictNumberFormatter. This method is called if - * the constructor parameter useStrictFormatter is true. - * - * Use a static method so that we can do some stuff before calling the - * superclass. - */ - private static JFormattedTextField createFormattedTextFieldX( - NumberFormat format) { - StrictNumberFormatter formatter = new StrictNumberFormatter( - new NumberFormatExt(format)); - final JFormattedTextField textField = new JFormattedTextField( - formatter); - /* - * FIXME: I am sure there is a better way to do this, but I don't know - * what it is. JTable sets up a binding for the ESCAPE key, but - * JFormattedTextField overrides that binding with it's own. Remove the - * JFormattedTextField binding. - */ - InputMap map = textField.getInputMap(); - map.put(KeyStroke.getKeyStroke("ESCAPE"), "none"); -// while (map != null) { -// map.remove(KeyStroke.getKeyStroke("pressed ESCAPE")); -// map = map.getParent(); -// } - /* - * Set an input verifier to prevent the cell losing focus when the value - * is invalid - */ - textField.setInputVerifier(new InputVerifier() { - @Override - public boolean verify(JComponent input) { - JFormattedTextField ftf = (JFormattedTextField) input; - return ftf.isEditValid(); - } - }); - /* - * The formatted text field will not call stopCellEditing() until the - * value is valid. So do the red border thing here. - */ - textField.addPropertyChangeListener("editValid", - new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - if (evt.getNewValue() == Boolean.TRUE) { - ((JFormattedTextField) evt.getSource()) - .setBorder(new LineBorder(Color.black)); - } else { - ((JFormattedTextField) evt.getSource()) - .setBorder(new LineBorder(Color.red)); - } - } - }); - return textField; - } - - - /** - * Creates and returns a JFormattedTextField configured with defaults. This - * method is called if the contructor useStrictFormatter is false.

                              - * - * Use a static method so that we can do some stuff before calling the - * superclass. - */ - private static JFormattedTextField createFormattedTextField( - NumberFormat formatter) { - final JFormattedTextField textField = new JFormattedTextField( - new NumberFormatExt(formatter)); - /* - * FIXME: I am sure there is a better way to do this, but I don't know - * what it is. JTable sets up a binding for the ESCAPE key, but - * JFormattedTextField overrides that binding with it's own. Remove the - * JFormattedTextField binding. - */ - InputMap map = textField.getInputMap(); - map.put(KeyStroke.getKeyStroke("ESCAPE"), "none"); -// while (map != null) { -// map.remove(KeyStroke.getKeyStroke("pressed ESCAPE")); -// map = map.getParent(); -// } - /* - * Set an input verifier to prevent the cell losing focus when the value - * is invalid - */ - textField.setInputVerifier(new InputVerifier() { - @Override - public boolean verify(JComponent input) { - JFormattedTextField ftf = (JFormattedTextField) input; - return ftf.isEditValid(); - } - }); - /* - * The formatted text field will not call stopCellEditing() until the - * value is valid. So do the red border thing here. - */ - textField.addPropertyChangeListener("editValid", - new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - if (evt.getNewValue() == Boolean.TRUE) { - ((JFormattedTextField) evt.getSource()) - .setBorder(new LineBorder(Color.black)); - } else { - ((JFormattedTextField) evt.getSource()) - .setBorder(new LineBorder(Color.red)); - } - } - }); - return textField; - } -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/table/NumberEditorNumberFormat.java b/src/main/java/org/jdesktop/swingx/table/NumberEditorNumberFormat.java deleted file mode 100644 index 166068b882..0000000000 --- a/src/main/java/org/jdesktop/swingx/table/NumberEditorNumberFormat.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * $Id: NumberEditorNumberFormat.java 3781 2010-09-15 08:33:52Z kleopatra $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.table; - -import java.text.*; - -/** - * A specialised Format for the NumberEditor that returns a null for empty - * strings. - * - * @author Noel Grandin - * - * @deprecated (pre-1.6.2) replaced by NumberEditorExt, no longer used internally - */ -@Deprecated -class NumberEditorNumberFormat extends Format { - private final NumberFormat childFormat; - - public NumberEditorNumberFormat(NumberFormat childFormat) { - if (childFormat == null) { - childFormat = NumberFormat.getInstance(); - } - this.childFormat = childFormat; - } - - @Override - public AttributedCharacterIterator formatToCharacterIterator(Object obj) { - if (obj == null) - return new AttributedString("").getIterator(); - return childFormat.formatToCharacterIterator(obj); - } - - @Override - public StringBuffer format(Object obj, StringBuffer toAppendTo, - FieldPosition pos) { - if (obj == null) - return new StringBuffer(""); - return childFormat.format(obj, toAppendTo, pos); - } - - @Override - public Object parseObject(String source, ParsePosition pos) { - if (source == null) { - pos.setIndex(1); // otherwise Format thinks parse failed - return null; - } - if (source.trim().equals("")) { - pos.setIndex(1); // otherwise Format thinks parse failed - return null; - } - Object val = childFormat.parseObject(source, pos); - /* - * The default behaviour of Format objects is to keep parsing as long as - * they encounter valid data. By for table editing we don't want - * trailing bad data to be considered a "valid value". So set the index - * to 0 so that the parse(Object) method knows that we had an error. - */ - if (pos.getIndex() != source.length()) { - pos.setErrorIndex(pos.getIndex()); - pos.setIndex(0); - } - return val; - } -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/table/NumberFormatExt.java b/src/main/java/org/jdesktop/swingx/table/NumberFormatExt.java deleted file mode 100644 index ff7b0cd87e..0000000000 --- a/src/main/java/org/jdesktop/swingx/table/NumberFormatExt.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * $Id$ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.table; - -import java.text.*; - -/** - * A specialised NumberFormat which handles null values and empty Strings. - * This is useful in cell editors and used in StrictNumberFormatter. - * - * @author Noel Grandin - * @author Jeanette Winzenburg - * - * @deprecated (pre-1.6.2) moved to org.jdesktop.swingx.text - */ -@Deprecated -class NumberFormatExt extends NumberFormat { - - private NumberFormat childFormat; - - public NumberFormatExt() { - this(null); - } - - public NumberFormatExt(NumberFormat childFormat) { - if (childFormat == null) { - childFormat = NumberFormat.getInstance(); - } - this.childFormat = childFormat; - } - - @Override - public AttributedCharacterIterator formatToCharacterIterator(Object obj) { - if (obj == null) - return new AttributedString("").getIterator(); - return childFormat.formatToCharacterIterator(obj); - } - - @Override - public StringBuffer format(Object obj, StringBuffer toAppendTo, - FieldPosition pos) { - if (obj == null) - return new StringBuffer(""); - return childFormat.format(obj, toAppendTo, pos); - } - - @Override - public Number parse(String source, ParsePosition pos) { - if (source == null) { - pos.setIndex(1); // otherwise Format thinks parse failed - return null; - } - if (source.trim().equals("")) { - pos.setIndex(1); // otherwise Format thinks parse failed - return null; - } - Number val = childFormat.parse(source, pos); - /* - * The default behaviour of Format objects is to keep parsing as long as - * they encounter valid data. By for table editing we don't want - * trailing bad data to be considered a "valid value". So set the index - * to 0 so that the parse(Object) method knows that we had an error. - */ - if (pos.getIndex() != source.length()) { - pos.setErrorIndex(pos.getIndex()); - pos.setIndex(0); - } - return val; - } - - @Override - public StringBuffer format(double number, StringBuffer toAppendTo, - FieldPosition pos) { - return childFormat.format(number, toAppendTo, pos); - } - - @Override - public StringBuffer format(long number, StringBuffer toAppendTo, - FieldPosition pos) { - return childFormat.format(number, toAppendTo, pos); - } - -} diff --git a/src/main/java/org/jdesktop/swingx/table/StrictNumberFormatter.java b/src/main/java/org/jdesktop/swingx/table/StrictNumberFormatter.java deleted file mode 100644 index 3e922133a5..0000000000 --- a/src/main/java/org/jdesktop/swingx/table/StrictNumberFormatter.java +++ /dev/null @@ -1,227 +0,0 @@ -/* - * $Id$ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.table; - -import javax.swing.text.NumberFormatter; -import java.math.BigDecimal; -import java.text.Format; -import java.text.NumberFormat; -import java.text.ParseException; - -/** - * Experiment to work around Issue #1183-swingx: NumberEditorExt throws exception - * on getCellValue. Remaining issue: no visual error feedback if the expected - * number type exceeds its range. - * - * @author Jeanette Winzenburg - * - * @deprecated (pre-1.6.2) moved to text package - */ -@Deprecated -class StrictNumberFormatter extends NumberFormatter { - - - private BigDecimal maxAsBig; - private BigDecimal minAsBig; - - /** - * @param format - */ - public StrictNumberFormatter(NumberFormat format) { - super(format); - } - - /** - * {@inheritDoc}

                              - * - * Overridden to automatically set the minimum/maximum to the boundaries of - * the Number type if it corresponds to a raw type, or null if not. - */ - @Override - public void setValueClass(Class valueClass) { - super.setValueClass(valueClass); - updateMinMax(); - } - - - /** - * - */ - @SuppressWarnings("unchecked") - private void updateMinMax() { - Comparable min = null; - Comparable max = null; - if (getValueClass() == Integer.class) { - max = Integer.MAX_VALUE; - min = Integer.MIN_VALUE; - } else if (getValueClass() == Long.class) { - max = Long.MAX_VALUE; - min = Long.MIN_VALUE; - } else if (getValueClass() == Short.class) { - max = Short.MAX_VALUE; - min = Short.MIN_VALUE; - } else if (getValueClass() == Byte.class) { - max = Byte.MAX_VALUE; - min = Byte.MIN_VALUE; - } else if (getValueClass() == Float.class) { - max = Float.MAX_VALUE; - min = Float.MIN_VALUE; - } else if (getValueClass() == Double.class) { - // don*t understand what's happening here, naive compare with bigDecimal - // fails - so don't do anything for now - // JW: revisit! - } - setMaximum(max); - setMinimum(min); - } - - - @SuppressWarnings("unchecked") - @Override - public void setMaximum(Comparable max) { - super.setMaximum(max); - this.maxAsBig = max != null ? new BigDecimal(max.toString()) : null; - } - - @SuppressWarnings("unchecked") - @Override - public void setMinimum(Comparable minimum) { - super.setMinimum(minimum); - this.minAsBig = minimum != null ? new BigDecimal(minimum.toString()) : null; - } - - - /** - * Returns the Object representation of the - * String text, may be null. - * - * @param text String to convert - * @return Object representation of text - * @throws ParseException if there is an error in the conversion - */ - @Override - public Object stringToValue(String text) throws ParseException { - Object value = getParsedValue(text, getFormat()); - try { - if (!isValueInRange(value, true)) { - throw new ParseException("Value not within min/max range", 0); - } - } catch (ClassCastException cce) { - throw new ParseException("Class cast exception comparing values: " - + cce, 0); - } - return convertValueToValueClass(value, getValueClass()); - } - - /** - * Converts the passed in value to the passed in class. This only - * works if valueClass is one of Integer, - * Long, Float, Double, - * Byte or Short and value - * is an instanceof Number. - */ - private Object convertValueToValueClass(Object value, Class valueClass) { - if (valueClass != null && (value instanceof Number)) { - if (valueClass == Integer.class) { - return ((Number) value).intValue(); - } - else if (valueClass == Long.class) { - return ((Number) value).longValue(); - } - else if (valueClass == Float.class) { - return ((Number) value).floatValue(); - } - else if (valueClass == Double.class) { - return ((Number) value).doubleValue(); - } - else if (valueClass == Byte.class) { - return ((Number) value).byteValue(); - } - else if (valueClass == Short.class) { - return ((Number) value).shortValue(); - } - } - return value; - } - - /** - * Invokes parseObject on f, returning - * its value. - */ - private Object getParsedValue(String text, Format f) throws ParseException { - if (f == null) { - return text; - } - return f.parseObject(text); - } - - - /** - * Returns true if value is between the min/max. - * - * @param wantsCCE If false, and a ClassCastException is thrown in - * comparing the values, the exception is consumed and - * false is returned. - */ - private boolean isValueInRange(Object orgValue, boolean wantsCCE) { - if (orgValue == null) return true; - if ((getMinimum() == null) && getMaximum() == null) return true; - - BigDecimal value = new BigDecimal(orgValue.toString()); - Comparable min = getMinimumAsBig(); - - try { - if (min != null && min.compareTo(value) > 0) { - return false; - } - } catch (ClassCastException cce) { - if (wantsCCE) { - throw cce; - } - return false; - } - - Comparable max = getMaximumAsBig(); - try { - if (max != null && max.compareTo(value) < 0) { - return false; - } - } catch (ClassCastException cce) { - if (wantsCCE) { - throw cce; - } - return false; - } - return true; - } - - - private Comparable getMinimumAsBig() { - return minAsBig; - } - - private Comparable getMaximumAsBig() { - return maxAsBig; - } - - -} diff --git a/src/main/java/org/jdesktop/swingx/table/TableColumnExt.java b/src/main/java/org/jdesktop/swingx/table/TableColumnExt.java deleted file mode 100644 index 498e3511f4..0000000000 --- a/src/main/java/org/jdesktop/swingx/table/TableColumnExt.java +++ /dev/null @@ -1,761 +0,0 @@ -/* - * $Id: TableColumnExt.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.table; - -import org.jdesktop.swingx.decorator.CompoundHighlighter; -import org.jdesktop.swingx.decorator.Highlighter; -import org.jdesktop.swingx.plaf.UIDependent; -import org.jdesktop.swingx.renderer.AbstractRenderer; - -import javax.swing.*; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import javax.swing.table.TableCellEditor; -import javax.swing.table.TableCellRenderer; -import javax.swing.table.TableColumn; -import java.awt.*; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.Comparator; -import java.util.Hashtable; - -/** - * TableColumn extension for enhanced view column configuration. - * The general drift is to strengthen the TableColumn abstraction as the - * place to configure and dynamically update view column properties, covering a - * broad range of customization requirements. Using collaborators are expected - * to listen to property changes and update themselves accordingly. - *

                              - * - * A functionality enhancement is the notion of column visibility: - * TableColumnModelExt manages sets of visible/hidden - * TableColumnExts controlled by the columns' - * visible property. Typically, users can toggle column - * visibility at runtime, f.i. through a dedicated control in the upper trailing - * corner of a JScrollPane. - *

                              - * - * A prominent group of properties allows fine-grained, per-column control of - * corresponding Table/-Header features. - * - *

                                - *
                              • Sorting: sortable controls whether this column - * should be sortable by user's sort gestures; Comparator can - * hold a column specific type. - * - *
                              • Editing: editable controls whether cells of this - * column should be accessible to in-table editing. - * - *
                              • Tooltip: toolTipText holds the column tooltip - * which is shown when hovering over the column's header. - * - *
                              • Highlighter: highlighters holds the column - * highlighters; these are applied to the renderer after the table highlighters. - * Any modification of the list of contained Highlighters - * (setting them, adding one or removing one) will result in a - * {@code PropertyChangeEvent} being fired for "highlighters". State changes on - * contained Highlighters will result in a PropertyChangeEvent - * for "highlighterStateChanged". - *
                              - * - * - * Analogous to JComponent, this class supports per-instance - * "client" properties. They are meant as a small-scale extension mechanism. - * They are similar to regular bean properties in that registered - * PropertyChangeListeners are notified about changes. TODO: - * example? - *

                              - * - * A TableColumnExt implements UIDependent, that is it takes over - * responsibility to update LAF dependent properties of contained elements when - * messaged with updateUI. This implementation updates its Highlighters, - * Cell-/HeaderRenderer and CellEditor.

                              - * - * TODO: explain prototype (sizing, collaborator-used-by ColumnFactory (?)) - *

                              - * - * @author Ramesh Gupta - * @author Amy Fowler - * @author Jeanette Winzenburg - * @author Karl Schaefer - * - * @see TableColumnModelExt - * @see ColumnFactory - * @see UIDependent - * @see JComponent#putClientProperty - */ -public class TableColumnExt extends TableColumn implements UIDependent { - - /** visible property. Initialized to true.*/ - protected boolean visible = true; - /** hideable property. Initialized to true.*/ - protected boolean hideable = true; - - /** prototype property. */ - protected Object prototypeValue; - - - /** per-column comparator */ - protected Comparator comparator; - /** per-column sortable property. Initialized to true. */ - protected boolean sortable = true; - /** per-column editable property. Initialized to true.*/ - protected boolean editable = true; - /** per-column tool tip text. */ - private String toolTipText; - - /** storage for client properties. */ - protected Hashtable clientProperties; - - /** - * The compound highlighter for the column. - */ - protected CompoundHighlighter compoundHighlighter; - - private ChangeListener highlighterChangeListener; - - private boolean ignoreHighlighterStateChange; - - - /** - * Creates new table view column with a model index = 0. - */ - public TableColumnExt() { - this(0); - } - - /** - * Creates new table view column with the specified model index. - * @param modelIndex index of table model column to which this view column - * is bound. - */ - public TableColumnExt(int modelIndex) { - this(modelIndex, 75); // default width taken from javax.swing.table.TableColumn - } - - /** - * Creates new table view column with the specified model index and column width. - * @param modelIndex index of table model column to which this view column - * is bound. - * @param width pixel width of view column - */ - public TableColumnExt(int modelIndex, int width) { - this(modelIndex, width, null, null); - } - - /** - * Creates new table view column with the specified model index, column - * width, cell renderer and cell editor. - * @param modelIndex index of table model column to which this view column - * is bound. - * @param width pixel width of view column - * @param cellRenderer the cell renderer which will render all cells in this - * view column - * @param cellEditor the cell editor which will edit cells in this view column - */ - public TableColumnExt(int modelIndex, int width, - TableCellRenderer cellRenderer, TableCellEditor cellEditor) { - super(modelIndex, width, cellRenderer, cellEditor); - } - - /** - * Instantiates a new table view column with all properties copied from the - * given original. - * - * @param columnExt the column to copy properties from - * @see #copyFrom(TableColumnExt) - */ - public TableColumnExt(TableColumnExt columnExt) { - this(columnExt.getModelIndex(), columnExt.getWidth(), columnExt - .getCellRenderer(), columnExt.getCellEditor()); - copyFrom(columnExt); - } - - - /** - * Sets the Highlighters to the table, replacing any old settings. - * None of the given Highlighters must be null.

                              - * - * This is a bound property.

                              - * - * Note: as of version #1.257 the null constraint is enforced strictly. To remove - * all highlighters use this method without param. - * - * @param highlighters zero or more not null highlighters to use for renderer decoration. - * @throws NullPointerException if array is null or array contains null values. - * - * @see #getHighlighters() - * @see #addHighlighter(Highlighter) - * @see #removeHighlighter(Highlighter) - * - */ - public void setHighlighters(Highlighter... highlighters) { - ignoreHighlighterStateChange = true; - Highlighter[] old = getHighlighters(); - getCompoundHighlighter().setHighlighters(highlighters); - firePropertyChange("highlighters", old, getHighlighters()); - ignoreHighlighterStateChange = false; - } - - /** - * Returns the Highlighters used by this table. - * Maybe empty, but guarantees to be never null. - * - * @return the Highlighters used by this table, guaranteed to never null. - * @see #setHighlighters(Highlighter[]) - */ - public Highlighter[] getHighlighters() { - return getCompoundHighlighter().getHighlighters(); - } - /** - * Appends a Highlighter to the end of the list of used - * Highlighters. The argument must not be null. - *

                              - * - * @param highlighter the Highlighter to add, must not be null. - * @throws NullPointerException if Highlighter is null. - * - * @see #removeHighlighter(Highlighter) - * @see #setHighlighters(Highlighter[]) - */ - public void addHighlighter(Highlighter highlighter) { - ignoreHighlighterStateChange = true; - Highlighter[] old = getHighlighters(); - getCompoundHighlighter().addHighlighter(highlighter); - firePropertyChange("highlighters", old, getHighlighters()); - ignoreHighlighterStateChange = false; - } - - /** - * Removes the given Highlighter.

                              - * - * Does nothing if the Highlighter is not contained. - * - * @param highlighter the Highlighter to remove. - * @see #addHighlighter(Highlighter) - * @see #setHighlighters(Highlighter...) - */ - public void removeHighlighter(Highlighter highlighter) { - ignoreHighlighterStateChange = true; - Highlighter[] old = getHighlighters(); - getCompoundHighlighter().removeHighlighter(highlighter); - firePropertyChange("highlighters", old, getHighlighters()); - ignoreHighlighterStateChange = false; - } - - /** - * Returns the CompoundHighlighter assigned to the table, null if none. - * PENDING: open up for subclasses again?. - * - * @return the CompoundHighlighter assigned to the table. - */ - protected CompoundHighlighter getCompoundHighlighter() { - if (compoundHighlighter == null) { - compoundHighlighter = new CompoundHighlighter(); - compoundHighlighter.addChangeListener(getHighlighterChangeListener()); - } - return compoundHighlighter; - } - - /** - * Returns the ChangeListener to use with highlighters. Lazily - * creates the listener. - * - * @return the ChangeListener for observing changes of highlighters, - * guaranteed to be not-null - */ - protected ChangeListener getHighlighterChangeListener() { - if (highlighterChangeListener == null) { - highlighterChangeListener = createHighlighterChangeListener(); - } - return highlighterChangeListener; - } - - /** - * Creates and returns the ChangeListener observing Highlighters. - *

                              - * Here: repaints the table on receiving a stateChanged. - * - * @return the ChangeListener defining the reaction to changes of - * highlighters. - */ - protected ChangeListener createHighlighterChangeListener() { - return new ChangeListener() { - @Override - public void stateChanged(ChangeEvent e) { - if (ignoreHighlighterStateChange) return; - firePropertyChange("highlighterStateChanged", false, true); - } - }; - } - - /** - * Returns true if the user can resize the TableColumn's width, - * false otherwise. This is a usability override: it takes into account - * the case where it's principally allowed to resize the column - * but not possible because the column has fixed size. - * - * @return a boolean indicating whether the user can resize this column. - */ - @Override - public boolean getResizable() { - // TODO JW: resizable is a bound property, so to be strict - // we'll need to override setMin/MaxWidth to fire resizable - // property change. - return super.getResizable() && (getMinWidth() < getMaxWidth()); - } - - /** - * Sets the editable property. This property allows to mark all cells in a - * column as read-only, independent of the per-cell editability as returned - * by the TableModel.isCellEditable. If the cell is - * read-only in the model layer, this property will have no effect. - * - * @param editable boolean indicating whether or not the user may edit cell - * values in this view column - * @see #isEditable - * @see org.jdesktop.swingx.JXTable#isCellEditable(int, int) - * @see javax.swing.table.TableModel#isCellEditable - */ - public void setEditable(boolean editable) { - boolean oldEditable = this.editable; - this.editable = editable; - firePropertyChange("editable", - Boolean.valueOf(oldEditable), - Boolean.valueOf(editable)); - } - - /** - * Returns the per-column editable property. - * The default is true. - * - * @return boolean indicating whether or not the user may edit cell - * values in this view column - * @see #setEditable - */ - public boolean isEditable() { - return editable; - } - - /** - * Sets the prototypeValue property. The value should be of a type - * which corresponds to the column's class as defined by the table model. - * If non-null, the JXTable instance will use this property to calculate - * and set the initial preferredWidth of the column. Note that this - * initial preferredWidth will be overridden if the user resizes columns - * directly. - * - * @param value Object containing the value of the prototype to be used - * to calculate the initial preferred width of the column - * @see #getPrototypeValue - * @see org.jdesktop.swingx.JXTable#getPreferredScrollableViewportSize - */ - public void setPrototypeValue(Object value) { - Object oldPrototypeValue = this.prototypeValue; - this.prototypeValue = value; - firePropertyChange("prototypeValue", - oldPrototypeValue, - value); - - } - - /** - * Returns the prototypeValue property. - * The default is null. - * - * @return Object containing the value of the prototype to be used - * to calculate the initial preferred width of the column - * @see #setPrototypeValue - */ - public Object getPrototypeValue() { - return prototypeValue; - } - - - /** - * Sets the comparator to use for this column. - * JXTable sorting api respects this property by passing it on - * to the SortController. - * - * @param comparator a custom comparator to use in interactive - * sorting. - * @see #getComparator - * @see org.jdesktop.swingx.sort.SortController - * @see org.jdesktop.swingx.decorator.SortKey - */ - public void setComparator(Comparator comparator) { - Comparator old = getComparator(); - this.comparator = comparator; - firePropertyChange("comparator", old, getComparator()); - } - - /** - * Returns the comparator to use for the column. - * The default is null. - * - * @return Comparator to use for this column - * @see #setComparator - */ - public Comparator getComparator() { - return comparator; - } - - /** - * Sets the sortable property. JXTable sorting api respects this - * property by disabling interactive sorting on this column if false. - * - * @param sortable boolean indicating whether or not this column can - * be sorted in the table - * @see #isSortable - */ - public void setSortable(boolean sortable) { - boolean old = isSortable(); - this.sortable = sortable; - firePropertyChange("sortable", old, isSortable()); - } - - /** - * Returns the sortable property. - * The default value is true. - * - * @return boolean indicating whether this view column is sortable - * @see #setSortable - */ - public boolean isSortable() { - return sortable; - } - - /** - * Registers the text to display in the column's tool tip. - * Typically, this is used by JXTableHeader to - * display when the mouse cursor lingers over the column's - * header cell. - * - * @param toolTipText text to show. - * @see #setToolTipText(String) - */ - public void setToolTipText(String toolTipText) { - String old = getToolTipText(); - this.toolTipText = toolTipText; - firePropertyChange("toolTipText", old, getToolTipText()); - } - - /** - * Returns the text of to display in the column's tool tip. - * The default is null. - * - * @return the text of the column ToolTip. - * @see #setToolTipText(String) - */ - public String getToolTipText() { - return toolTipText; - } - - - /** - * Sets the title of this view column. This is a convenience - * wrapper for setHeaderValue. - * @param title String containing the title of this view column - */ - public void setTitle(String title) { - setHeaderValue(title); // simple wrapper - } - - /** - * Convenience method which returns the headerValue property after - * converting it to a string. - * @return String containing the title of this view column or null if - * no headerValue is set. - */ - public String getTitle() { - Object header = getHeaderValue(); - return header != null ? header.toString() : null; // simple wrapper - } - - /** - * Sets the visible property. This property controls whether or not - * this view column is currently visible in the table. - * - * @param visible boolean indicating whether or not this view column is - * visible in the table - * @see #setVisible - */ - public void setVisible(boolean visible) { - boolean oldVisible = isVisible(); - this.visible = visible; - firePropertyChange("visible", oldVisible, isVisible()); - } - - /** - * Returns a boolean indicating whether or not this column is visible. - * The bare property value is constrained by this column's hideable setting, - * that is a not hideable column is always visible, irrespective of the - * property setting. - *

                              - * The default is true. - * - * @return boolean indicating whether or not this view column is - * visible in the table - * @see #setVisible - */ - public boolean isVisible() { - if (!isHideable()) return true; - return visible; - } - - /** - * Sets the hideable property. This property controls whether the column can - * be hidden. This is a bound property. If the column's visibilty is affected, - * listeners are notified about that change as well.. - *

                              - * - * The default value is true. - * - * @param hideable - */ - public void setHideable(boolean hideable) { - boolean old = isHideable(); - boolean oldVisible = isVisible(); - this.hideable = hideable; - firePropertyChange("visible", oldVisible, isVisible()); - firePropertyChange("hideable", old, isHideable()); - } - - /** - * Returns the hideable property. - * - * @return the hideable property. - * - * @see #setHideable(boolean) - */ - public boolean isHideable() { - return hideable; - } - - /** - * Sets the client property "key" to value. - * If value is null this method will remove the property. - * Changes to - * client properties are reported with PropertyChange events. - * The name of the property (for the sake of PropertyChange events) is - * key.toString(). - *

                              - * The get/putClientProperty methods provide access to a - * per-instance hashtable, which is intended for small scale extensions of - * TableColumn. - *

                              - * - * @param key Object which is used as key to retrieve value - * @param value Object containing value of client property - * @throws IllegalArgumentException if key is null - * @see #getClientProperty - * @see JComponent#putClientProperty - */ - public void putClientProperty(Object key, Object value) { - if (key == null) - throw new IllegalArgumentException("null key"); - - if ((value == null) && (getClientProperty(key) == null)) { - return; - } - - Object old = getClientProperty(key); - if (value == null) { - getClientProperties().remove(key); - } - else { - getClientProperties().put(key, value); - } - - firePropertyChange(key.toString(), old, value); - /* Make all fireXXX methods in TableColumn protected instead of private */ - } - - /** - * Returns the value of the property with the specified key. Only properties - * added with putClientProperty will return a non-null - * value. - * - * @param key Object which is used as key to retrieve value - * @return Object containing value of client property or null - * - * @see #putClientProperty - */ - public Object getClientProperty(Object key) { - return ((key == null) || (clientProperties == null)) ? - null : clientProperties.get(key); - } - - private Hashtable getClientProperties() { - if (clientProperties == null) { - clientProperties = new Hashtable(); - } - return clientProperties; - } - - - /** - * Copies properties from original. Handles all properties except - * modelIndex, width, cellRenderer, cellEditor. Called from copy - * constructor. - * - * @param original the tableColumn to copy from - * - * @see #TableColumnExt(TableColumnExt) - */ - protected void copyFrom(TableColumnExt original) { - setEditable(original.isEditable()); - setHeaderValue(original.getHeaderValue()); // no need to copy setTitle(); - setToolTipText(original.getToolTipText()); - setIdentifier(original.getIdentifier()); - setMaxWidth(original.getMaxWidth()); - setMinWidth(original.getMinWidth()); - setPreferredWidth(original.getPreferredWidth()); - setPrototypeValue(original.getPrototypeValue()); - // JW: isResizable is overridden to return a calculated property! - setResizable(original.isResizable); - setVisible(original.isVisible()); - setSortable(original.isSortable()); - setComparator(original.getComparator()); - copyClientPropertiesFrom(original); - - if (original.compoundHighlighter != null) { - setHighlighters(original.getHighlighters()); - } - - } - - /** - * Copies all clientProperties of this TableColumnExt - * to the target column. - * - * @param original the target column. - */ - protected void copyClientPropertiesFrom(TableColumnExt original) { - if (original.clientProperties == null) return; - for(Object key: original.clientProperties.keySet()) { - putClientProperty(key, original.getClientProperty(key)); - } - } - - - /** - * Notifies registered PropertyChangeListeners - * about property changes. This method must be invoked internally - * whe any of the enhanced properties changed. - *

                              - * Implementation note: needed to replicate super - * functionality because super's field propertyChangeSupport - * and method fireXX are both private. - * - * @param propertyName name of changed property - * @param oldValue old value of changed property - * @param newValue new value of changed property - */ - protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { - if ((oldValue != null && !oldValue.equals(newValue)) || - oldValue == null && newValue != null) { - PropertyChangeListener pcl[] = getPropertyChangeListeners(); - if (pcl != null && pcl.length != 0) { - PropertyChangeEvent pce = new PropertyChangeEvent(this, - propertyName, - oldValue, newValue); - - for (int i = 0; i < pcl.length; i++) { - pcl[i].propertyChange(pce); - } - } - } - } - -//---------------- implement UIDependent - - /** - * Update ui of owned ui-dependent parts. This implementation - * updates the contained highlighters. - * - */ - @Override - public void updateUI() { - updateHighlighterUI(); - updateRendererUI(getCellRenderer()); - updateRendererUI(getHeaderRenderer()); - updateEditorUI(getCellEditor()); - } - - /** - * @param editor - * - */ - private void updateEditorUI(TableCellEditor editor) { - if (editor == null) return; - // internal knowledge of core table - already updated - if ((editor instanceof JComponent) - || (editor instanceof DefaultCellEditor)) - return; - try { - Component comp = editor - .getTableCellEditorComponent(null, null, false, -1, -1); - if (comp != null) { - SwingUtilities.updateComponentTreeUI(comp); - } - } catch (Exception e) { - // can't do anything - renderer can't cope with off-range cells - } - } - - /** - * @param tableCellRenderer - * - */ - private void updateRendererUI(TableCellRenderer renderer) { - if (renderer == null) return; - // internal knowledge of core table - already updated - if (renderer instanceof JComponent) { - return; - } - Component comp = null; - if (renderer instanceof AbstractRenderer) { - comp = ((AbstractRenderer) renderer).getComponentProvider().getRendererComponent(null); - } else { - try { - comp = renderer - .getTableCellRendererComponent(null, null, false, false, - -1, -1); - - } catch (Exception e) { - // can't do anything - renderer can't cope with off-range cells - } - } - if (comp != null) { - SwingUtilities.updateComponentTreeUI(comp); - } - } - - /** - * - */ - private void updateHighlighterUI() { - if (compoundHighlighter == null) return; - compoundHighlighter.updateUI(); - } -} diff --git a/src/main/java/org/jdesktop/swingx/table/TableColumnModelExt.java b/src/main/java/org/jdesktop/swingx/table/TableColumnModelExt.java deleted file mode 100644 index 06a16edf47..0000000000 --- a/src/main/java/org/jdesktop/swingx/table/TableColumnModelExt.java +++ /dev/null @@ -1,232 +0,0 @@ -/* - * $Id: TableColumnModelExt.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.table; - -import org.jdesktop.swingx.JXTable; -import org.jdesktop.swingx.event.TableColumnModelExtListener; - -import javax.swing.event.TableColumnModelListener; -import javax.swing.table.TableColumn; -import javax.swing.table.TableColumnModel; -import java.util.List; - -/** - * An extension of TableColumnModel suitable for use with - * JXTable. It extends the notion of columns considered as part - * of the view realm to include invisible columns. Conceptually, there are - * several sets of "columns": - * - *

                                - *
                              1. model columns: all columns of a TableModel. They are but - * a virtual concept, characterizable f.i. by (model) column index, (model) - * column name. - *
                              2. view columns: all TableColumnExt objects added to the - * TableColumnModelExt, each typically created and configured in - * relation to a model column. These can be regarded as a kind of subset of the - * model columns (not literally, obviously). Each view column belongs to exactly - * one of the following (real) subsets: - *
                                  - *
                                • visible columns: all view columns with the visibility property enabled - *
                                • hidden columns: all view columns with the visibility property disabled - *
                                - *
                              - * - * This class manages the view columns and automatically takes care of keeping - * track of their location in the visible/hidden subset, triggering the - * corresponding changes in interested views. Typically, a view column's - * visibility can be toggled by user interaction, f.i. via a ColumnControlButton. - *

                              - * An example to programmatically hide - * the first visible column in the column model: - * - *

                              
                              - * TableColumnExt columnExt = columnModel.getColumnExt(0);
                              - * if (columnExt != null) {
                              - *     columnExt.setVisible(false);
                              - * }
                              - * 
                              - * - * Note that it is principally allowed to add standard TableColumns. - * Practically, it doesn't make much sense to do so - they will always be - * visible. - *

                              - * - * While individual visible columns can be requested by both column identifier - * and column index, the latter is not available for hidden columns. An example - * to programmatically guarantee that the view column which corresponds to the - * first column in the associated TableModel. - * - *

                              
                              - * List<TableColumn> columns = colModel.getColumns(true);
                              - * for (TableColumn column : columns) {
                              - *     if (column.getModelIndex() == 0) {
                              - *         if (column instanceof TableColumnExt) {
                              - *             ((TableColumnExt) column).setVisible(false);
                              - *         }
                              - *         return;
                              - *     }
                              - * }
                              - * 
                              - * - * Alternatively, the column could be requested directly by identifier. By - * default the column's headerValue is returned as identifier, if none is set. - * - *
                              
                              - * Object identifier = tableModel.getColumnName(0);
                              - * TableColumnExt columnExt = columnModel.getColumnExt(identifier);
                              - * if (columnExt != null) {
                              - *     columnExt.setVisible(false);
                              - * }
                              - * 
                              - * - * Relying on default identifiers is inherently brittle (headerValues might - * change f.i. with Locales), so explicit configuration of columns with - * identifiers is strongly recommended. A custom ColumnFactory - * helps to automate column configuration. - *

                              - * - * - * This class guarantees to notify registered - * TableColumnModelListeners of type - * TableColumnModelExtListener about propertyChanges fired by - * contained TableColumns. - * An example of a client which adjusts itself based on - * headerValue property of visible columns: - *

                              
                              - * TableColumnModelExtListener l = new TableColumnModelExtListener() {
                              - * 
                              - *     public void columnPropertyChange(PropertyChangeEvent event) {
                              - *         if ("headerValue".equals(event.getPropertyName())) {
                              - *             TableColumn column = (TableColumn) event.getSource();
                              - *             if ((column instanceof TableColumnExt)
                              - *                     && !((TableColumnExt) column).isVisible()) {
                              - *                 return;
                              - *             }
                              - *             resizeAndRepaint();
                              - *         }
                              - *     }
                              - * 
                              - *     public void columnAdded(TableColumnModelEvent e) {
                              - *     }
                              - * 
                              - *     public void columnMarginChanged(ChangeEvent e) {
                              - *     }
                              - * 
                              - *     public void columnMoved(TableColumnModelEvent e) {
                              - *     }
                              - * 
                              - *     public void columnRemoved(TableColumnModelEvent e) {
                              - *     }
                              - * 
                              - *     public void columnSelectionChanged(ListSelectionEvent e) {
                              - *     }
                              - * 
                              - * };
                              - * columnModel.addColumnModelListener(l);
                              - * 
                              - * - * - * @author Richard Bair - * @author Jeanette Winzenburg - * - * @see DefaultTableColumnModelExt - * @see TableColumnExt - * @see TableColumnModelExtListener - * @see ColumnControlButton - * @see JXTable#setColumnControlVisible - * @see ColumnFactory - * - */ -public interface TableColumnModelExt extends TableColumnModel { - - /** - * Returns the number of contained columns. The count includes or excludes invisible - * columns, depending on whether the includeHidden is true or - * false, respectively. If false, this method returns the same count as - * getColumnCount(). - * - * @param includeHidden a boolean to indicate whether invisible columns - * should be included - * @return the number of contained columns, including or excluding the - * invisible as specified. - */ - public int getColumnCount(boolean includeHidden); - - /** - * Returns a List of contained TableColumns. - * Includes or excludes invisible columns, depending on whether the - * includeHidden is true or false, respectively. If false, an - * Iterator over the List is equivalent to the - * Enumeration returned by getColumns(). - *

                              - * - * NOTE: the order of columns in the List depends on whether or not the - * invisible columns are included, in the former case it's the insertion - * order in the latter it's the current order of the visible columns. - * - * @param includeHidden a boolean to indicate whether invisible columns - * should be included - * @return a List of contained columns. - */ - public List getColumns(boolean includeHidden); - - /** - * Returns the first TableColumnExt with the given - * identifier. The return value is null if there is no contained - * column with identifier or if the column with identifier is not - * of type TableColumnExt. The returned column - * may be visible or hidden. - * - * @param identifier the object used as column identifier - * @return first TableColumnExt with the given identifier or - * null if none is found - */ - public TableColumnExt getColumnExt(Object identifier); - - /** - * Returns the TableColumnExt at view position - * columnIndex. The return value is null, if the - * column at position columnIndex is not of type - * TableColumnExt. - * The returned column is visible. - * - * @param columnIndex the index of the column desired - * @return the TableColumnExt object that matches the column - * index - * @throws ArrayIndexOutOfBoundsException if columnIndex out of allowed - * range, that is if - * (columnIndex < 0) || (columnIndex >= getColumnCount()). - */ - public TableColumnExt getColumnExt(int columnIndex); - - /** - * Adds a listener for table column model events. This enhances super's - * behaviour in that it guarantees to notify listeners of type - * TableColumnModelListenerExt about property changes of contained columns. - * - * @param x a TableColumnModelListener object - */ - @Override - public void addColumnModelListener(TableColumnModelListener x); - - -} diff --git a/src/main/java/org/jdesktop/swingx/table/TableRowHeightController.java b/src/main/java/org/jdesktop/swingx/table/TableRowHeightController.java deleted file mode 100644 index a41e8417f1..0000000000 --- a/src/main/java/org/jdesktop/swingx/table/TableRowHeightController.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * $Id$ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.table; - -import javax.swing.*; -import javax.swing.event.TableModelEvent; -import javax.swing.event.TableModelListener; -import javax.swing.table.TableModel; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.logging.Logger; - -import static org.jdesktop.swingx.table.TableUtilities.*; - -/** - * A controller to adjust JTable rowHeight based on sizing requirements of its renderers. - * - * @author Jeanette Winzenburg, Berlin - */ -public class TableRowHeightController { - - private JTable table; - private TableModelListener tableModelListener; - private PropertyChangeListener tablePropertyListener; - - /** - * Instantiates an unbound TableRowHeightController. - */ - public TableRowHeightController() { - this(null); - } - - /** - * Instantiates a TableRowHeightController and installs itself to the given table. - * The row heights of all visible rows are automatically adjusted on model changes. - * - * @param table the table to control. - */ - public TableRowHeightController(JTable table) { - install(table); - } - - /** - * Installs this controller on the given table. Releases control from previously - * installed table, if any. - * @param table the table to install upon. - */ - public void install(JTable table) { - release(); - if (table != null) { - this.table = table; - installListeners(); - updatePreferredRowHeights(); - } - } - - /** - * Release this controller from its table. Does nothing if no table installed. - * - */ - public void release() { - if (table == null) - return; - uninstallListeners(); - table = null; - } - - /** - * Sets the row heights of the rows in the range of first- to lastRow, inclusive. - * The coordinates are model indices. - * - * @param firstRow the first row in model coordinates - * @param lastRow the last row in model coordinates - */ - protected void updatePreferredRowHeights(int firstRow, int lastRow) { - for (int row = firstRow; row <= lastRow; row++) { - int viewRow = table.convertRowIndexToView(row); - if (viewRow >= 0) { -// int oldHeight = table.getRowHeight(viewRow); -// LOG.info("in viewRow/old/new: " + viewRow + " / " + oldHeight + " / " + table.getRowHeight(viewRow)); - setPreferredRowHeight(table, viewRow); - } - } - } - - /** - * Sets the row heights of all rows. - */ - protected void updatePreferredRowHeights() { - if (table.getRowCount() == 0) return; - updatePreferredRowHeights(0, table.getModel().getRowCount() - 1); - } - - /** - * @param oldValue - */ - protected void updateModel(TableModel oldValue) { - if (oldValue != null) { - oldValue.removeTableModelListener(getTableModelListener()); - } - table.getModel().addTableModelListener(getTableModelListener()); - updatePreferredRowHeights(); - } - - - /** - * @return - */ - protected PropertyChangeListener createTablePropertyListener() { - PropertyChangeListener l = new PropertyChangeListener() { - - @Override - public void propertyChange(PropertyChangeEvent evt) { - invokedPropertyChanged(evt); - } - - /** - * @param evt - */ - private void invokedPropertyChanged(final PropertyChangeEvent evt) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - if ("model".equals(evt.getPropertyName())) { - updateModel((TableModel) evt.getOldValue()); - } - - } - }); - } - }; - return l; - } - - protected TableModelListener createTableModelListener() { - TableModelListener l = new TableModelListener() { - @Override - public void tableChanged(final TableModelEvent e) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - invokedTableChanged(e); - } - }); - } - - private void invokedTableChanged(TableModelEvent e) { - if (isStructureChanged(e) || isDataChanged(e)) { - updatePreferredRowHeights(); - } else if (isUpdate(e) || isInsert(e)) { - updatePreferredRowHeights(e.getFirstRow(), e.getLastRow()); - } - // do nothing on delete - } - }; - return l; - } - /** - * - */ - private void uninstallListeners() { - table.removePropertyChangeListener(getPropertyChangeListener()); - table.getModel().removeTableModelListener(getTableModelListener()); - // whatever else turns out to be needed - } - - private void installListeners() { - table.addPropertyChangeListener(getPropertyChangeListener()); - table.getModel().addTableModelListener(getTableModelListener()); - // whatever else turns out to be needed - } - - /** - * @return - */ - protected TableModelListener getTableModelListener() { - if (tableModelListener == null) { - tableModelListener = createTableModelListener(); - } - return tableModelListener; - } - - /** - * @return - */ - protected PropertyChangeListener getPropertyChangeListener() { - if (tablePropertyListener == null) { - tablePropertyListener = createTablePropertyListener(); - } - return tablePropertyListener; - } - - @SuppressWarnings("unused") - private static final Logger LOG = Logger - .getLogger(TableRowHeightController.class.getName()); -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/table/TableUtilities.java b/src/main/java/org/jdesktop/swingx/table/TableUtilities.java deleted file mode 100644 index 4063789950..0000000000 --- a/src/main/java/org/jdesktop/swingx/table/TableUtilities.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Created on 25.02.2011 - * - */ -package org.jdesktop.swingx.table; - -import javax.swing.*; -import javax.swing.event.TableModelEvent; -import javax.swing.table.TableCellRenderer; -import javax.swing.table.TableColumn; -import javax.swing.table.TableColumnModel; -import java.awt.*; -import java.util.Collections; -import java.util.List; - -/** - * Collection of utility methods for J/X/Table. - * - * @author Jeanette Winzenburg, Berlin - */ -public class TableUtilities { - - - /** - * Returns a boolean indication whether the event represents a - * dataChanged type. - * - * @param e the event to examine. - * @return true if the event is of type dataChanged, false else. - */ - public static boolean isDataChanged(TableModelEvent e) { - if (e == null) - return false; - return e.getType() == TableModelEvent.UPDATE && e.getFirstRow() == 0 - && e.getLastRow() == Integer.MAX_VALUE; - } - - /** - * Returns a boolean indication whether the event represents a - * update type. - * - * @param e the event to examine. - * @return true if the event is a true update, false - * otherwise. - */ - public static boolean isUpdate(TableModelEvent e) { - if (isStructureChanged(e)) - return false; - return e.getType() == TableModelEvent.UPDATE - && e.getLastRow() < Integer.MAX_VALUE; - } - - /** - * Returns a boolean indication whether the event represents a - * insert type. - * - * @param e the event to examine - * @return true if the event is of type insert, false otherwise. - */ - public static boolean isInsert(TableModelEvent e) { - if (e == null) return false; - return TableModelEvent.INSERT == e.getType(); - } - - - /** - * Returns a boolean indication whether the event represents a - * structureChanged type. - * - * @param e the event to examine. - * @return true if the event is of type structureChanged or null, false - * else. - */ - public static boolean isStructureChanged(TableModelEvent e) { - return e == null || e.getFirstRow() == TableModelEvent.HEADER_ROW; - } - - - - /** - * Returns the preferred height for the given row. It loops - * across all visible columns and returns the maximal pref height of - * the rendering component. Falls back to the table's base rowheight, i - * f there are no columns or the renderers - * max is zeor.

                              - * - * @param table the table which provides the renderers, must not be null - * @param row the index of the row in view coordinates - * @return the preferred row height of - * @throws NullPointerException if table is null. - * @throws IndexOutOfBoundsException if the row is not a valid row index - */ - public static int getPreferredRowHeight(JTable table, int row) { - int pref = 0; - for (int column = 0; column < table.getColumnCount(); column++) { - TableCellRenderer renderer = table.getCellRenderer(row, column); - Component comp = table.prepareRenderer(renderer, row, column); - pref = Math.max(pref, comp.getPreferredSize().height); - } - return pref > 0 ? pref : table.getRowHeight(); - } - - /** - * - * @param table the table which provides the renderers, must not be null - * @param row the index of the row in view coordinates - * @throws NullPointerException if table is null. - * @throws IndexOutOfBoundsException if the row is not a valid row index - */ - public static void setPreferredRowHeight(JTable table, int row) { - int prefHeight = getPreferredRowHeight(table, row); - table.setRowHeight(row, prefHeight); - } - - /** - * Sets preferred row heights for all visible rows. - * - * @param table the table to set row heights to - * @throws NullPointerException if no table installed. - */ - public static void setPreferredRowHeights(JTable table) { - // care about visible rows only - for (int row = 0; row < table.getRowCount(); row++) { - setPreferredRowHeight(table, row); - } - } - - /** - * Returns an array containing the ordinals of the given values of an Enum.

                              - * - * Convience for clients which define TableColumns as Enums (Issue #1304-swingx). - * - * @param values the enums to map to its ordinals - * @return an array of ordinals, guaranteed to be not null - */ - public static int[] ordinalsOf(Enum... values) { - int[] cols = new int[values.length]; - for (int i = 0; i < values.length; i++) { - cols[i] = values[i].ordinal(); - } - return cols; - } - - - /** - * Removes all columns of the given column model. Includes hidden - * columns as indicated by the includesHidden flag, the flag has no - * effect if the model is not of type TableColumnModelExt.

                              - * - * @param model the column model to remove all columns from. - * @param includeHidden indicates whether hidden columns should be - * removed as well, has no effect if model is not of type TableColumnModelExt. - */ - /* - * Stand-in instead of fixing of issue http://java.net/jira/browse/SWINGX-1407 - */ - public static void clear(TableColumnModel model, boolean includeHidden) { - if (model instanceof TableColumnModelExt) { - clear(model, ((TableColumnModelExt) model).getColumns(includeHidden)); - } else { - clear(model, Collections.list(model.getColumns())); - } - } - - private static void clear(TableColumnModel model, List columns) { - for (TableColumn tableColumn : columns) { - model.removeColumn(tableColumn); - } - } - - private TableUtilities() {} -} diff --git a/src/main/java/org/jdesktop/swingx/table/package-info.java b/src/main/java/org/jdesktop/swingx/table/package-info.java deleted file mode 100644 index 7f48e8d2d1..0000000000 --- a/src/main/java/org/jdesktop/swingx/table/package-info.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * $Id: package-info.java 3167 2009-01-02 13:28:04Z rah003 $ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -/**Contains API required by the extended JTable component, JXTable. -

                              Package Specification

                              - -
                              - -

                              Related Documentation

                              - - - -*/ -package org.jdesktop.swingx.table; \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/text/NumberFormatExt.java b/src/main/java/org/jdesktop/swingx/text/NumberFormatExt.java deleted file mode 100644 index 0c685e17cb..0000000000 --- a/src/main/java/org/jdesktop/swingx/text/NumberFormatExt.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * $Id$ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.text; - -import java.text.*; - -/** - * A specialised NumberFormat which handles null values and empty Strings. - * This is useful in cell editors and used in StrictNumberFormatter. - * - * @author Noel Grandin - * @author Jeanette Winzenburg - */ -public class NumberFormatExt extends NumberFormat { - - private NumberFormat childFormat; - - public NumberFormatExt() { - this(null); - } - - public NumberFormatExt(NumberFormat childFormat) { - if (childFormat == null) { - childFormat = NumberFormat.getInstance(); - } - this.childFormat = childFormat; - } - - @Override - public AttributedCharacterIterator formatToCharacterIterator(Object obj) { - if (obj == null) - return new AttributedString("").getIterator(); - return childFormat.formatToCharacterIterator(obj); - } - - @Override - public StringBuffer format(Object obj, StringBuffer toAppendTo, - FieldPosition pos) { - if (obj == null) - return new StringBuffer(""); - return childFormat.format(obj, toAppendTo, pos); - } - - @Override - public Number parse(String source, ParsePosition pos) { - if (source == null) { - pos.setIndex(1); // otherwise Format thinks parse failed - return null; - } - if (source.trim().equals("")) { - pos.setIndex(1); // otherwise Format thinks parse failed - return null; - } - Number val = childFormat.parse(source, pos); - /* - * The default behaviour of Format objects is to keep parsing as long as - * they encounter valid data. By for table editing we don't want - * trailing bad data to be considered a "valid value". So set the index - * to 0 so that the parse(Object) method knows that we had an error. - */ - if (pos.getIndex() != source.length()) { - pos.setErrorIndex(pos.getIndex()); - pos.setIndex(0); - } - return val; - } - - @Override - public StringBuffer format(double number, StringBuffer toAppendTo, - FieldPosition pos) { - return childFormat.format(number, toAppendTo, pos); - } - - @Override - public StringBuffer format(long number, StringBuffer toAppendTo, - FieldPosition pos) { - return childFormat.format(number, toAppendTo, pos); - } - -} diff --git a/src/main/java/org/jdesktop/swingx/text/StrictNumberFormatter.java b/src/main/java/org/jdesktop/swingx/text/StrictNumberFormatter.java deleted file mode 100644 index 9e576ba1eb..0000000000 --- a/src/main/java/org/jdesktop/swingx/text/StrictNumberFormatter.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * $Id$ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.text; - -import javax.swing.text.NumberFormatter; -import java.math.BigDecimal; -import java.text.Format; -import java.text.NumberFormat; -import java.text.ParseException; - -/** - * Experiment to work around Issue #1183-swingx: NumberEditorExt throws exception - * on getCellValue. Remaining issue: no visual error feedback if the expected - * number type exceeds its range. - * - * @author Jeanette Winzenburg - */ -public class StrictNumberFormatter extends NumberFormatter { - - - private BigDecimal maxAsBig; - private BigDecimal minAsBig; - - /** - * @param format - */ - public StrictNumberFormatter(NumberFormat format) { - super(format); - } - - /** - * {@inheritDoc}

                              - * - * Overridden to automatically set the minimum/maximum to the boundaries of - * the Number type if it corresponds to a raw type, or null if not. - */ - @Override - public void setValueClass(Class valueClass) { - super.setValueClass(valueClass); - updateMinMax(); - } - - - /** - * - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - private void updateMinMax() { - Comparable min = null; - Comparable max = null; - if (getValueClass() == Integer.class) { - max = Integer.MAX_VALUE; - min = Integer.MIN_VALUE; - } else if (getValueClass() == Long.class) { - max = Long.MAX_VALUE; - min = Long.MIN_VALUE; - } else if (getValueClass() == Short.class) { - max = Short.MAX_VALUE; - min = Short.MIN_VALUE; - } else if (getValueClass() == Byte.class) { - max = Byte.MAX_VALUE; - min = Byte.MIN_VALUE; - } else if (getValueClass() == Float.class) { - // Issue #1528-swingx: float doesn't take negative/zero numbers - // was: crude error - Float.MIN_VALUE is the smallest _positive_ - max = Float.MAX_VALUE; - min = - Float.MAX_VALUE; - } else if (getValueClass() == Double.class) { - // don*t understand what's happening here, naive compare with bigDecimal - // fails - so don't do anything for now - // JW: revisit! - } - setMaximum(max); - setMinimum(min); - } - - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Override - public void setMaximum(Comparable max) { - super.setMaximum(max); - this.maxAsBig = max != null ? new BigDecimal(max.toString()) : null; - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Override - public void setMinimum(Comparable minimum) { - super.setMinimum(minimum); - this.minAsBig = minimum != null ? new BigDecimal(minimum.toString()) : null; - } - - - /** - * Returns the Object representation of the - * String text, may be null. - * - * @param text String to convert - * @return Object representation of text - * @throws ParseException if there is an error in the conversion - */ - @Override - public Object stringToValue(String text) throws ParseException { - Object value = getParsedValue(text, getFormat()); - try { - if (!isValueInRange(value, true)) { - throw new ParseException("Value not within min/max range", 0); - } - } catch (ClassCastException cce) { - throw new ParseException("Class cast exception comparing values: " - + cce, 0); - } - return convertValueToValueClass(value, getValueClass()); - } - - /** - * Converts the passed in value to the passed in class. This only - * works if valueClass is one of Integer, - * Long, Float, Double, - * Byte or Short and value - * is an instanceof Number. - */ - private Object convertValueToValueClass(Object value, Class valueClass) { - if (valueClass != null && (value instanceof Number)) { - if (valueClass == Integer.class) { - return ((Number) value).intValue(); - } - else if (valueClass == Long.class) { - return ((Number) value).longValue(); - } - else if (valueClass == Float.class) { - return ((Number) value).floatValue(); - } - else if (valueClass == Double.class) { - return ((Number) value).doubleValue(); - } - else if (valueClass == Byte.class) { - return ((Number) value).byteValue(); - } - else if (valueClass == Short.class) { - return ((Number) value).shortValue(); - } - } - return value; - } - - /** - * Invokes parseObject on f, returning - * its value. - */ - private Object getParsedValue(String text, Format f) throws ParseException { - if (f == null) { - return text; - } - return f.parseObject(text); - } - - - /** - * Returns true if value is between the min/max. - * - * @param wantsCCE If false, and a ClassCastException is thrown in - * comparing the values, the exception is consumed and - * false is returned. - */ - private boolean isValueInRange(Object orgValue, boolean wantsCCE) { - if (orgValue == null) return true; - if ((getMinimum() == null) && getMaximum() == null) return true; - - BigDecimal value = new BigDecimal(orgValue.toString()); - Comparable min = getMinimumAsBig(); - - try { - if (min != null && min.compareTo(value) > 0) { - return false; - } - } catch (ClassCastException cce) { - if (wantsCCE) { - throw cce; - } - return false; - } - - Comparable max = getMaximumAsBig(); - try { - if (max != null && max.compareTo(value) < 0) { - return false; - } - } catch (ClassCastException cce) { - if (wantsCCE) { - throw cce; - } - return false; - } - return true; - } - - - private Comparable getMinimumAsBig() { - return minAsBig; - } - - private Comparable getMaximumAsBig() { - return maxAsBig; - } - - -} diff --git a/src/main/java/org/jdesktop/swingx/tips/DefaultTip.java b/src/main/java/org/jdesktop/swingx/tips/DefaultTip.java deleted file mode 100644 index 22d1c53850..0000000000 --- a/src/main/java/org/jdesktop/swingx/tips/DefaultTip.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * $Id: DefaultTip.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.tips; - -import org.jdesktop.swingx.tips.TipOfTheDayModel.Tip; - -/** - * Default {@link Tip} implementation.
                              - * - * @author Frederic Lavigne - */ -public class DefaultTip implements Tip { - - private String name; - - private Object tip; - - public DefaultTip() { - } - - public DefaultTip(String name, Object tip) { - this.name = name; - this.tip = tip; - } - - @Override - public Object getTip() { - return tip; - } - - public void setTip(Object tip) { - this.tip = tip; - } - - @Override - public String getTipName() { - return name; - } - - public void setTipName(String name) { - this.name = name; - } - - @Override - public String toString() { - return getTipName(); - } - -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/tips/DefaultTipOfTheDayModel.java b/src/main/java/org/jdesktop/swingx/tips/DefaultTipOfTheDayModel.java deleted file mode 100644 index c5662f3b77..0000000000 --- a/src/main/java/org/jdesktop/swingx/tips/DefaultTipOfTheDayModel.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * $Id: DefaultTipOfTheDayModel.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.tips; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -/** - * Default {@link TipOfTheDayModel} implementation.
                              - * - * @author Frederic Lavigne - */ -public class DefaultTipOfTheDayModel implements TipOfTheDayModel { - - private List tips = new ArrayList(); - - public DefaultTipOfTheDayModel() { - } - - public DefaultTipOfTheDayModel(Tip[] tips) { - this(Arrays.asList(tips)); - } - - public DefaultTipOfTheDayModel(Collection tips) { - this.tips.addAll(tips); - } - - @Override - public Tip getTipAt(int index) { - return tips.get(index); - } - - @Override - public int getTipCount() { - return tips.size(); - } - - public void add(Tip tip) { - tips.add(tip); - } - - public void remove(Tip tip) { - tips.remove(tip); - } - - public Tip[] getTips() { - return tips.toArray(new Tip[tips.size()]); - } - - public void setTips(Tip[] tips) { - this.tips.clear(); - this.tips.addAll(Arrays.asList(tips)); - } - -} diff --git a/src/main/java/org/jdesktop/swingx/tips/TipLoader.java b/src/main/java/org/jdesktop/swingx/tips/TipLoader.java deleted file mode 100644 index 4d2f010400..0000000000 --- a/src/main/java/org/jdesktop/swingx/tips/TipLoader.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * $Id: TipLoader.java 542 2005-10-10 18:03:15Z rbair $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.tips; - -import java.util.ArrayList; -import java.util.List; -import java.util.Properties; - -/** - * Loads tips from Properties.
                              - * - * @author Frederic Lavigne - */ -public class TipLoader { - - private TipLoader() { } - - /** - * Initializes a TipOfTheDayModel from properties. Each tip is defined by two - * properties, its name and its description: - * - *

                              -   * 
                              -   * tip.1.name=First Tip
                              -   * tip.1.description=This is the description
                              -   *  
                              -   * tip.2.name=Second Tip
                              -   * tip.2.description=<html>This is an html description
                              -   * 
                              -   * ...
                              -   * 
                              -   * tip.10.description=No name for this tip, name is optional
                              -   * 
                              -   * 
                              - * - * @param props - * @return a TipOfTheDayModel - * @throws IllegalArgumentException - * if a name is found without description - */ - public static TipOfTheDayModel load(Properties props) { - List tips = new ArrayList(); - - int count = 1; - while (true) { - String nameKey = "tip." + count + ".name"; - String nameValue = props.getProperty(nameKey); - - String descriptionKey = "tip." + count + ".description"; - String descriptionValue = props.getProperty(descriptionKey); - - if (nameValue != null && descriptionValue == null) { throw new IllegalArgumentException( - "No description for name " + nameValue); } - - if (descriptionValue == null) { - break; - } - - DefaultTip tip = new DefaultTip(nameValue, descriptionValue); - tips.add(tip); - - count++; - } - - return new DefaultTipOfTheDayModel(tips); - } - -} diff --git a/src/main/java/org/jdesktop/swingx/tips/TipOfTheDayModel.java b/src/main/java/org/jdesktop/swingx/tips/TipOfTheDayModel.java deleted file mode 100644 index 1e608e125e..0000000000 --- a/src/main/java/org/jdesktop/swingx/tips/TipOfTheDayModel.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * $Id: TipOfTheDayModel.java 542 2005-10-10 18:03:15Z rbair $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.tips; - -import org.jdesktop.swingx.JXTipOfTheDay; - -/** - * A model for {@link JXTipOfTheDay}.
                              - * - * @author Frederic Lavigne - */ -public interface TipOfTheDayModel { - - /** - * @return the number of tips in this model - */ - int getTipCount(); - - /** - * @param index - * @return the tip at index - * @throws IndexOutOfBoundsException - * if the index is out of range (index < 0 || index >= - * getTipCount()). - */ - Tip getTipAt(int index); - - /** - * A tip.
                              - */ - interface Tip { - - /** - * @return very short (optional) text describing the tip - */ - String getTipName(); - - /** - * The tip object to show. See {@link JXTipOfTheDay} for supported object - * types. - * - * @return the tip to display - */ - Object getTip(); - } - -} diff --git a/src/main/java/org/jdesktop/swingx/tips/package-info.java b/src/main/java/org/jdesktop/swingx/tips/package-info.java deleted file mode 100644 index 04408783ed..0000000000 --- a/src/main/java/org/jdesktop/swingx/tips/package-info.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * $Id: package-info.java 3167 2009-01-02 13:28:04Z rah003 $ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -/** Provides classes and interfaces for dealing with - org.jdesktop.swingx.JXTipOfTheDay. - -

                              Related Documentation

                              -*/ -package org.jdesktop.swingx.tips; \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/tree/DefaultXTreeCellEditor.java b/src/main/java/org/jdesktop/swingx/tree/DefaultXTreeCellEditor.java deleted file mode 100644 index 04063e1a47..0000000000 --- a/src/main/java/org/jdesktop/swingx/tree/DefaultXTreeCellEditor.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * $Id: DefaultXTreeCellEditor.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.tree; - -import org.jdesktop.swingx.plaf.UIDependent; - -import javax.swing.*; -import javax.swing.tree.DefaultTreeCellEditor; -import javax.swing.tree.DefaultTreeCellRenderer; -import javax.swing.tree.TreeCellEditor; -import java.awt.*; - -/** - * Subclassed to hack around core bug with RtoL editing (#4980473). - * - * The price to pay is currently is to guarantee a minimum size of the - * editing field (is only one char wide if the node value is null). - * - * PENDING: any possibility to position the editorContainer? - * BasicTreeUI adds it to the tree and positions at the node location. - * That's not a problem in LToR, only - * in RToL - * - * - * @author Jeanette Winzenburg - */ -public class DefaultXTreeCellEditor extends DefaultTreeCellEditor implements UIDependent { - - public DefaultXTreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer) { - super(tree, renderer); - } - - public DefaultXTreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer, - TreeCellEditor editor) { - super(tree, renderer, editor); - } - - public void setRenderer(DefaultTreeCellRenderer renderer) { - this.renderer = renderer; - } - - public DefaultTreeCellRenderer getRenderer() { - return renderer; - } - - public class XEditorContainer extends EditorContainer { - - @Override - public Dimension getPreferredSize() { - if (isRightToLeft()) { - if(editingComponent != null) { - Dimension pSize = editingComponent.getPreferredSize(); - - pSize.width += offset + 5; - - Dimension rSize = (renderer != null) ? - renderer.getPreferredSize() : null; - - if(rSize != null) - pSize.height = Math.max(pSize.height, rSize.height); - if(editingIcon != null) - pSize.height = Math.max(pSize.height, - editingIcon.getIconHeight()); - - // trying to enforce a minimum size leads to field being painted over the icon - // Make sure width is at least 100. - // pSize.width = Math.max(pSize.width, 100); - return pSize; - } - return new Dimension(0, 0); - } - return super.getPreferredSize(); - - } - - @Override - public void doLayout() { - if (isRightToLeft()) { - Dimension cSize = getSize(); - - editingComponent.getPreferredSize(); - editingComponent.setLocation(0, 0); - editingComponent.setBounds(0, 0, - cSize.width - offset, - cSize.height); - } else { - - super.doLayout(); - } - } - - - @Override - public void paint(Graphics g) { - if (isRightToLeft()) { - Dimension size = getSize(); - - // Then the icon. - if (editingIcon != null) { - int yLoc = Math.max(0, (size.height - editingIcon - .getIconHeight()) / 2); - int xLoc = Math.max(0, size.width - offset); - editingIcon.paintIcon(this, g, xLoc, yLoc); - } - // need to prevent super from painting the icon - Icon rememberIcon = editingIcon; - editingIcon = null; - super.paint(g); - editingIcon = rememberIcon; - - } else { - super.paint(g); - } - } - - } - - - @Override - protected Container createContainer() { - return new XEditorContainer(); - } - - @Override - protected void prepareForEditing() { - super.prepareForEditing(); - applyComponentOrientation(); - } - - protected void applyComponentOrientation() { - if (tree != null) { - editingContainer.applyComponentOrientation(tree.getComponentOrientation()); - } - - } - - /** - * @return - */ - private boolean isRightToLeft() { - return (tree != null) && (!tree.getComponentOrientation().isLeftToRight()); - } - - /** - * Implement UIDependent. Quick hack for #1060-swingx: icons lost on laf toggle. - */ - @Override - public void updateUI() { - if (getRenderer() != null) { - SwingUtilities.updateComponentTreeUI(getRenderer()); - } - if (realEditor instanceof JComponent) { - SwingUtilities.updateComponentTreeUI((JComponent) realEditor); - } else if (realEditor instanceof UIDependent) { - ((UIDependent) realEditor).updateUI(); - } - - } - -} diff --git a/src/main/java/org/jdesktop/swingx/tree/DefaultXTreeCellRenderer.java b/src/main/java/org/jdesktop/swingx/tree/DefaultXTreeCellRenderer.java deleted file mode 100644 index e8a2eeb157..0000000000 --- a/src/main/java/org/jdesktop/swingx/tree/DefaultXTreeCellRenderer.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * $Id: DefaultXTreeCellRenderer.java 3304 2009-03-20 15:11:25Z kleopatra $ - * - * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -package org.jdesktop.swingx.tree; - -import org.jdesktop.swingx.SwingXUtilities; - -import javax.swing.*; -import javax.swing.tree.DefaultTreeCellRenderer; - -/** - * Quick fix for #1061-swingx (which actually is a core issue): - * tree icons lost on toggle laf. Updates colors as well - - * but beware: this is incomplete as some of super's properties are private! - * - * Will not do more because in the longer run (as soon as we've fixed the editor issues) - * the JXTree's default renderer will be changed to SwingX DefaultTreeRenderer. - * - * @author Jeanette Winzenburg - */ -public class DefaultXTreeCellRenderer extends DefaultTreeCellRenderer { - - /** - * {@inheritDoc}

                              - * - * Overridden to update icons and colors. - */ - @Override - public void updateUI() { - super.updateUI(); - updateIcons(); - updateColors(); - } - - /** - * - */ - protected void updateColors() { - if (SwingXUtilities.isUIInstallable(getTextSelectionColor())) { - setTextSelectionColor(UIManager.getColor("Tree.selectionForeground")); - } - if (SwingXUtilities.isUIInstallable(getTextNonSelectionColor())) { - setTextNonSelectionColor(UIManager.getColor("Tree.textForeground")); - } - if (SwingXUtilities.isUIInstallable(getBackgroundSelectionColor())) { - setBackgroundSelectionColor(UIManager.getColor("Tree.selectionBackground")); - } - if (SwingXUtilities.isUIInstallable(getBackgroundNonSelectionColor())) { - setBackgroundNonSelectionColor(UIManager.getColor("Tree.textBackground")); - } - if (SwingXUtilities.isUIInstallable(getBorderSelectionColor())) { - setBorderSelectionColor(UIManager.getColor("Tree.selectionBorderColor")); - } -// Object value = UIManager.get("Tree.drawsFocusBorderAroundIcon"); -// drawsFocusBorderAroundIcon = (value != null && ((Boolean)value). -// booleanValue()); -// value = UIManager.get("Tree.drawDashedFocusIndicator"); -// drawDashedFocusIndicator = (value != null && ((Boolean)value). -// booleanValue()); - } - - /** - * - */ - protected void updateIcons() { - if (SwingXUtilities.isUIInstallable(getLeafIcon())) { - setLeafIcon(UIManager.getIcon("Tree.leafIcon")); - } - if (SwingXUtilities.isUIInstallable(getClosedIcon())) { - setClosedIcon(UIManager.getIcon("Tree.closedIcon")); - } - if (SwingXUtilities.isUIInstallable(getOpenIcon())) { - setOpenIcon(UIManager.getIcon("Tree.openIcon")); - } - - } - - - -} diff --git a/src/main/java/org/jdesktop/swingx/tree/TreeModelSupport.java b/src/main/java/org/jdesktop/swingx/tree/TreeModelSupport.java deleted file mode 100644 index f6e8742a1b..0000000000 --- a/src/main/java/org/jdesktop/swingx/tree/TreeModelSupport.java +++ /dev/null @@ -1,338 +0,0 @@ -/* - * $Id: TreeModelSupport.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.tree; - -import org.jdesktop.swingx.util.Contract; - -import javax.swing.event.EventListenerList; -import javax.swing.event.TreeModelEvent; -import javax.swing.event.TreeModelListener; -import javax.swing.tree.TreeModel; -import javax.swing.tree.TreePath; - -/** - * Support for change notification, usable by {@code TreeModel}s. - * - * The changed/inserted/removed is expressed in terms of a {@code TreePath}, - * it's up to the client model to build it as appropriate. - * - * This is inspired by {@code AbstractTreeModel} from Christian Kaufhold, - * www.chka.de. - * - * TODO - implement and test precondition failure of added/removed notification - * - * @author JW - */ -public final class TreeModelSupport { - protected EventListenerList listeners; - - private TreeModel treeModel; - - /** - * Creates the support class for the given {@code TreeModel}. - * - * @param model the model to support - * @throws NullPointerException if {@code model} is {@code null} - */ - public TreeModelSupport(TreeModel model) { - if (model == null) - throw new NullPointerException("model must not be null"); - listeners = new EventListenerList(); - this.treeModel = model; - } - -//---------------------- structural changes on subtree - - /** - * Notifies registered TreeModelListeners that the tree's root has - * been replaced. Can cope with a null root. - */ - public void fireNewRoot() { - - Object root = treeModel.getRoot(); - - /* - * Undocumented. I think it is the only reasonable/possible solution to - * use use null as path if there is no root. TreeModels without root - * aren't important anyway, since JTree doesn't support them (yet). - */ - TreePath path = (root != null) ? new TreePath(root) : null; - fireTreeStructureChanged(path); - } - - /** - * Call when a node has changed its leaf state.

                              - * - * PENDING: rename? Do we need it? - * @param path the path to the node with changed leaf state. - */ - public void firePathLeafStateChanged(TreePath path) { - fireTreeStructureChanged(path); - } - - /** - * Notifies registered TreeModelListeners that the structure - * below the node identified by the given path has been - * completely changed. - *

                              - * NOTE: the subtree path maybe null if the root is null. - * If not null, it must contain at least one element (the root). - * - * @param subTreePath the path to the root of the subtree - * whose structure was changed. - * @throws NullPointerException if the path is not null but empty - * or contains null elements. - */ - public void fireTreeStructureChanged(TreePath subTreePath) { - if (subTreePath != null) { - Contract.asNotNull(subTreePath.getPath(), - "path must not contain null elements"); - } - Object[] pairs = listeners.getListenerList(); - - TreeModelEvent e = null; - - for (int i = pairs.length - 2; i >= 0; i -= 2) { - if (pairs[i] == TreeModelListener.class) { - if (e == null) - e = createStructureChangedEvent(subTreePath); - - ((TreeModelListener) pairs[i + 1]).treeStructureChanged(e); - } - } - } - -//----------------------- node modifications, no mutations - - /** - * Notifies registered TreeModelListeners that the - * the node identified by the given path has been modified. - * - * @param path the path to the node that has been modified, - * must not be null and must not contain null path elements. - * - */ - public void firePathChanged(TreePath path) { - Object node = path.getLastPathComponent(); - TreePath parentPath = path.getParentPath(); - - if (parentPath == null) - fireChildrenChanged(path, null, null); - else { - Object parent = parentPath.getLastPathComponent(); - - fireChildChanged(parentPath, treeModel - .getIndexOfChild(parent, node), node); - } - } - - /** - * Notifies registered TreeModelListeners that the given child of - * the node identified by the given parent path has been modified. - * The parent path must not be null, nor empty nor contain null - * elements. - * - * @param parentPath the path to the parent of the modified children. - * @param index the position of the child - * @param child child node that has been modified, must not be null - */ - public void fireChildChanged(TreePath parentPath, int index, Object child) { - fireChildrenChanged(parentPath, new int[] { index }, - new Object[] { child }); - } - - /** - * Notifies registered TreeModelListeners that the given children of - * the node identified by the given parent path have been modified. - * The parent path must not be null, nor empty nor contain null - * elements. Note that the index array must contain the position of the - * corresponding child in the the children array. The indices must be in - * ascending order.

                              - * - * The exception to these rules is if the root itself has been - * modified (which has no parent by definition). In this case - * the path must be the path to the root and both indices and children - * arrays must be null. - * - * @param parentPath the path to the parent of the modified children. - * @param indices the positions of the modified children - * @param children the modified children - */ - public void fireChildrenChanged(TreePath parentPath, int[] indices, - Object[] children) { - Contract.asNotNull(parentPath.getPath(), - "path must not be null and must not contain null elements"); - Object[] pairs = listeners.getListenerList(); - - TreeModelEvent e = null; - - for (int i = pairs.length - 2; i >= 0; i -= 2) { - if (pairs[i] == TreeModelListener.class) { - if (e == null) - e = createTreeModelEvent(parentPath, indices, children); - - ((TreeModelListener) pairs[i + 1]).treeNodesChanged(e); - } - } - } - - -//------------------------ mutations (insert/remove nodes) - - - /** - * Notifies registered TreeModelListeners that the child has been added to - * the the node identified by the given parent path at the given position. - * The parent path must not be null, nor empty nor contain null elements. - * - * @param parentPath the path to the parent of added child. - * @param index the position of the added children - * @param child the added child - */ - public void fireChildAdded(TreePath parentPath, int index, Object child) { - fireChildrenAdded(parentPath, new int[] { index }, - new Object[] { child }); - } - - /** - * Notifies registered TreeModelListeners that the child has been removed - * from the node identified by the given parent path from the given position. - * The parent path must not be null, nor empty nor contain null elements. - * - * @param parentPath the path to the parent of removed child. - * @param index the position of the removed children before the removal - * @param child the removed child - */ - public void fireChildRemoved(TreePath parentPath, int index, Object child) { - fireChildrenRemoved(parentPath, new int[] { index }, - new Object[] { child }); - } - - /** - * Notifies registered TreeModelListeners that the given children have been - * added to the the node identified by the given parent path at the given - * locations. The parent path and the child array must not be null, nor - * empty nor contain null elements. Note that the index array must contain - * the position of the corresponding child in the the children array. The - * indices must be in ascending order. - *

                              - * - * @param parentPath the path to the parent of the added children. - * @param indices the positions of the added children. - * @param children the added children. - */ - public void fireChildrenAdded(TreePath parentPath, int[] indices, - Object[] children) { - Object[] pairs = listeners.getListenerList(); - - TreeModelEvent e = null; - - for (int i = pairs.length - 2; i >= 0; i -= 2) { - if (pairs[i] == TreeModelListener.class) { - if (e == null) - e = createTreeModelEvent(parentPath, indices, children); - - ((TreeModelListener) pairs[i + 1]).treeNodesInserted(e); - } - } - } - - /** - * Notifies registered TreeModelListeners that the given children have been - * removed to the the node identified by the given parent path from the - * given locations. The parent path and the child array must not be null, - * nor empty nor contain null elements. Note that the index array must - * contain the position of the corresponding child in the the children - * array. The indices must be in ascending order. - *

                              - * - * @param parentPath the path to the parent of the removed children. - * @param indices the positions of the removed children before the removal - * @param children the removed children - */ - public void fireChildrenRemoved(TreePath parentPath, int[] indices, - Object[] children) { - Object[] pairs = listeners.getListenerList(); - - TreeModelEvent e = null; - - for (int i = pairs.length - 2; i >= 0; i -= 2) { - if (pairs[i] == TreeModelListener.class) { - if (e == null) - e = createTreeModelEvent(parentPath, indices, children); - ((TreeModelListener) pairs[i + 1]).treeNodesRemoved(e); - } - } - } - -//------------------- factory methods of TreeModelEvents - - /** - * Creates and returns a TreeModelEvent for structureChanged - * event notification. The given path may be null to indicate - * setting a null root. In all other cases, the first path element - * must contain the root and the last path element the rootNode of the - * structural change. Specifically, a TreePath with a single element - * (which is the root) denotes a structural change of the complete tree. - * - * @param parentPath the path to the root of the changed structure, - * may be null to indicate setting a null root. - * @return a TreeModelEvent for structureChanged notification. - * - * @see TreeModelEvent - * @see TreeModelListener - */ - private TreeModelEvent createStructureChangedEvent(TreePath parentPath) { - return createTreeModelEvent(parentPath, null, null); - } - - /** - * Creates and returns a TreeModelEvent for changed/inserted/removed - * event notification. - * - * @param parentPath path to parent of modified node - * @param indices the indices of the modified children (before the change) - * @param children the array of modified children - * @return a TreeModelEvent for changed/inserted/removed notification - * - * @see TreeModelEvent - * @see TreeModelListener - */ - private TreeModelEvent createTreeModelEvent(TreePath parentPath, - int[] indices, Object[] children) { - return new TreeModelEvent(treeModel, parentPath, indices, children); - } - - -//------------------------ handling listeners - - public void addTreeModelListener(TreeModelListener l) { - listeners.add(TreeModelListener.class, l); - } - - public TreeModelListener[] getTreeModelListeners() { - return listeners.getListeners(TreeModelListener.class); - } - - public void removeTreeModelListener(TreeModelListener l) { - listeners.remove(TreeModelListener.class, l); - } -} diff --git a/src/main/java/org/jdesktop/swingx/tree/TreeUtilities.java b/src/main/java/org/jdesktop/swingx/tree/TreeUtilities.java deleted file mode 100644 index 88dfa6b2be..0000000000 --- a/src/main/java/org/jdesktop/swingx/tree/TreeUtilities.java +++ /dev/null @@ -1,423 +0,0 @@ -package org.jdesktop.swingx.tree; - -import javax.swing.tree.TreeModel; -import javax.swing.tree.TreeNode; -import javax.swing.tree.TreePath; -import java.util.*; -import java.util.logging.Logger; - -/** - * Contains convenience classes/methods for handling hierarchical Swing structures. - * - * @author Jeanette Winzenburg, Berlin - */ -@SuppressWarnings({ "unchecked", "rawtypes" }) -public class TreeUtilities { - - /** - * An enumeration that is always empty. - */ - public static final Enumeration EMPTY_ENUMERATION - = new Enumeration() { - @Override - public boolean hasMoreElements() { return false; } - @Override - public Object nextElement() { - throw new NoSuchElementException("No more elements"); - } - }; - - /** - * Implementation of a preorder traversal of a TreeModel. - */ - public static class PreorderModelEnumeration implements Enumeration { - protected Deque stack; - protected TreeModel model; - // the last component is the current subtree to travers - private TreePath path; - - /** - * Instantiates a preorder traversal starting from the root of the - * TreeModel. - * - * @param model the TreeModel to travers. - */ - public PreorderModelEnumeration(TreeModel model) { - this(model, model.getRoot()); - } - - /** - * Instantiates a preorder traversal of the TreeModel which - * starts at the given node. It iterates over all nodes of the - * subtree, only. - * - * @param model the TreeModel to travers. - * @param node the node to start - */ - public PreorderModelEnumeration(TreeModel model, Object node) { - this.model = model; - stack = new ArrayDeque(); - pushNodeAsEnumeration(node); - } - - /** - * Instantiates a preorder traversal of the TreeModel which starts at the - * last path component of the given TreePath. It iterates over all nodes - * of the subtree and all of its siblings, with the same end as a traversal - * starting at the model's roolt would have. - * - * @param model the TreeModel to travers. - * @param path the TreePath to start from - */ - public PreorderModelEnumeration(TreeModel model, TreePath path) { - this(model, path.getLastPathComponent()); - this.path = path; - } - - @Override - public boolean hasMoreElements() { - return (!stack.isEmpty() && stack.peek().hasMoreElements()); - } - - @Override - public Object nextElement() { - Enumeration enumer = stack.peek(); - Object node = enumer.nextElement(); - Enumeration children = children(model, node); - - if (!enumer.hasMoreElements()) { - stack.pop(); - } - if (children.hasMoreElements()) { - stack.push(children); - } - if (!hasMoreElements()) { - // check if there are more subtrees to travers - // and update internal state accordingly - updateSubtree(); - } - return node; - } - - /** - * - */ - private void updateSubtree() { - if (path == null) return; - TreePath parentPath = path.getParentPath(); - if (parentPath == null) { - // root - path = null; - return; - } - Object parent = parentPath.getLastPathComponent(); - Object currentNode = path.getLastPathComponent(); - int currentIndex = model.getIndexOfChild(parent, currentNode); - if (currentIndex +1 < model.getChildCount(parent)) { - // use sibling - Object child = model.getChild(parent, currentIndex + 1); - path = parentPath.pathByAddingChild(child); - pushNodeAsEnumeration(child); - } else { - path = parentPath; - // up one level - updateSubtree(); - } - } - - private void pushNodeAsEnumeration(Object node) { - // single element enum - Vector v = new Vector(1); - v.add(node); - stack.push(v.elements()); //children(model)); - } - - - } // End of class PreorderEnumeration - - - /** - * Implementation of a breadthFirst traversal of a subtree in a TreeModel. - */ - public static class BreadthFirstModelEnumeration implements Enumeration { - protected Queue queue; - private TreeModel model; - - public BreadthFirstModelEnumeration(TreeModel model) { - this(model, model.getRoot()); - } - - public BreadthFirstModelEnumeration(TreeModel model, Object node) { - this.model = model; - // Vector is just used for getting an Enumeration easily - Vector v = new Vector(1); - v.addElement(node); - queue = new ArrayDeque(); - queue.offer(v.elements()); - } - - @Override - public boolean hasMoreElements() { - return !queue.isEmpty() && - queue.peek().hasMoreElements(); - } - - @Override - public Object nextElement() { - // look at head - Enumeration enumer = queue.peek(); - Object node = enumer.nextElement(); - Enumeration children = children(model, node); - - if (!enumer.hasMoreElements()) { - // remove head - queue.poll(); - } - if (children.hasMoreElements()) { - // add at tail - queue.offer(children); - } - return node; - } - - } // End of class BreadthFirstEnumeration - - - /** - * Implementation of a postorder traversal of a subtree in a TreeModel. - */ - public static class PostorderModelEnumeration implements Enumeration { - protected TreeModel model; - protected Object root; - protected Enumeration children; - protected Enumeration subtree; - - public PostorderModelEnumeration(TreeModel model) { - this(model, model.getRoot()); - } - - public PostorderModelEnumeration(TreeModel model, Object node) { - this.model = model; - root = node; - children = children(model, root); - subtree = EMPTY_ENUMERATION; - } - - @Override - public boolean hasMoreElements() { - return root != null; - } - - @Override - public Object nextElement() { - Object retval; - - if (subtree.hasMoreElements()) { - retval = subtree.nextElement(); - } else if (children.hasMoreElements()) { - subtree = new PostorderModelEnumeration(model, - children.nextElement()); - retval = subtree.nextElement(); - } else { - retval = root; - root = null; - } - - return retval; - } - - } // End of class PostorderEnumeration - - /** - * Implementation of a preorder traversal of a subtree with nodes of type TreeNode. - */ - public static class PreorderNodeEnumeration implements Enumeration { - protected Deque> stack; - - public PreorderNodeEnumeration(M rootNode) { - // Vector is just used for getting an Enumeration easily - Vector v = new Vector(1); - v.addElement(rootNode); - stack = new ArrayDeque>(); - stack.push(v.elements()); - } - - @Override - public boolean hasMoreElements() { - return (!stack.isEmpty() && - stack.peek().hasMoreElements()); - } - - @Override - public M nextElement() { - Enumeration enumer = stack.peek(); - M node = enumer.nextElement(); - Enumeration children = getChildren(node); - - if (!enumer.hasMoreElements()) { - stack.pop(); - } - if (children.hasMoreElements()) { - stack.push(children); - } - return node; - } - - protected Enumeration getChildren(M node) { - return (Enumeration) node.children(); - } - - } // End of class PreorderEnumeration - - /** - * Implementation of a postorder traversal of a subtree with nodes of type TreeNode. - */ - public static class PostorderNodeEnumeration implements Enumeration { - protected M root; - protected Enumeration children; - protected Enumeration subtree; - - public PostorderNodeEnumeration(M rootNode) { - super(); - root = rootNode; - children = getChildren(rootNode); - subtree = EMPTY_ENUMERATION; - } - @Override - public boolean hasMoreElements() { - return root != null; - } - - @Override - public M nextElement() { - M retval; - - if (subtree.hasMoreElements()) { - retval = subtree.nextElement(); - } else if (children.hasMoreElements()) { - subtree = createSubTree(children.nextElement()); - retval = subtree.nextElement(); - } else { - retval = root; - root = null; - } - - return retval; - } - - /** - * Creates and returns a PostorderEnumeration on the given node. - * - * @param node the node to create the PostorderEnumeration for - * @return the PostorderEnumeration on the given node - */ - protected PostorderNodeEnumeration createSubTree(M node) { - return new PostorderNodeEnumeration(node); - } - - /** - * Returns an enumeration on the children of the root node. - * @param node - * @return - */ - protected Enumeration getChildren(M node) { - return (Enumeration) node.children(); - } - - - } // End of class PostorderEnumeration - - - /** - * Implementation of a breadthFirst traversal of a subtree with nodes of type TreeNode. - */ - public static class BreadthFirstNodeEnumeration implements Enumeration { - protected Queue> queue; - - public BreadthFirstNodeEnumeration(M rootNode) { - // Vector is just used for getting an Enumeration easily - Vector v = new Vector(1); - v.addElement(rootNode); - queue = new ArrayDeque>(); - queue.offer(v.elements()); - } - - @Override - public boolean hasMoreElements() { - return !queue.isEmpty() && - queue.peek().hasMoreElements(); - } - - @Override - public M nextElement() { - // look at head - Enumeration enumer = queue.peek(); - M node = enumer.nextElement(); - Enumeration children = getChildren(node); - - if (!enumer.hasMoreElements()) { - // remove head - queue.poll(); - } - if (children.hasMoreElements()) { - // add at tail - queue.offer(children); - } - return node; - } - - protected Enumeration getChildren(M node) { - return (Enumeration) node.children(); - } - - - } // End of class BreadthFirstEnumeration - - /** - * Creates and returns an Enumeration across the direct children of the - * rootNode in the given TreeModel. - * - * @param model the TreeModel which contains parent, must not be null - * @return an Enumeration across the direct children of the model's root, the enumeration - * is empty if the root is null or contains no children - */ - public static Enumeration children(TreeModel model) { - return children(model, model.getRoot()); - } - - /** - * Creates and returns an Enumeration across the direct children of parentNode - * in the given TreeModel. - * - * @param model the TreeModel which contains parent, must not be null - * @param parent the parent of the enumerated children - * @return an Enumeration across the direct children of parent, the enumeration - * is empty if the parent is null or contains no children - */ - public static Enumeration children(final TreeModel model, final Object parent) { - if (parent == null || model.isLeaf(parent)) { - return EMPTY_ENUMERATION; - } - Enumeration e = new Enumeration() { - - int currentIndex = 0; - @Override - public boolean hasMoreElements() { - return model.getChildCount(parent) > currentIndex; - } - - @Override - public Object nextElement() { - return model.getChild(parent, currentIndex++); - } - - }; - return e; - } - - private TreeUtilities() {} - - @SuppressWarnings("unused") - private static final Logger LOG = Logger.getLogger(TreeUtilities.class - .getName()); -} diff --git a/src/main/java/org/jdesktop/swingx/tree/package-info.java b/src/main/java/org/jdesktop/swingx/tree/package-info.java deleted file mode 100644 index 95fef0416d..0000000000 --- a/src/main/java/org/jdesktop/swingx/tree/package-info.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * $Id: package-info.java 3100 2008-10-14 22:33:10Z rah003 $ - * - * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -/** - * Contains Tree specific classes and interfaces. - */ -package org.jdesktop.swingx.tree; - diff --git a/src/main/java/org/jdesktop/swingx/treetable/AbstractMutableTreeTableNode.java b/src/main/java/org/jdesktop/swingx/treetable/AbstractMutableTreeTableNode.java deleted file mode 100644 index 72e35b58f7..0000000000 --- a/src/main/java/org/jdesktop/swingx/treetable/AbstractMutableTreeTableNode.java +++ /dev/null @@ -1,282 +0,0 @@ -/* - * $Id: AbstractMutableTreeTableNode.java 3780 2010-09-09 16:17:41Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.treetable; - -import javax.swing.tree.TreeNode; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Enumeration; -import java.util.List; - -/** - * {@code AbstractMutableTreeTableNode} provides an implementation of most of - * the {@code MutableTreeTableNode} features. - * - * @author Karl Schaefer - */ -public abstract class AbstractMutableTreeTableNode implements - MutableTreeTableNode { - /** this node's parent, or null if this node has no parent */ - protected MutableTreeTableNode parent; - - /** - * List of children, if this node has no children the list will be empty. - * This list will never be null. - */ - protected final List children; - - /** optional user object */ - protected transient Object userObject; - - protected boolean allowsChildren; - - public AbstractMutableTreeTableNode() { - this(null); - } - - public AbstractMutableTreeTableNode(Object userObject) { - this(userObject, true); - } - - public AbstractMutableTreeTableNode(Object userObject, - boolean allowsChildren) { - this.userObject = userObject; - this.allowsChildren = allowsChildren; - children = createChildrenList(); - } - - /** - * Creates the list used to manage the children of this node. - *

                              - * This method is called by the constructor. - * - * @return a list; this list is guaranteed to be non-{@code null} - */ - protected List createChildrenList() { - return new ArrayList(); - } - - public void add(MutableTreeTableNode child) { - insert(child, getChildCount()); - } - - /** - * {@inheritDoc} - */ - @Override - public void insert(MutableTreeTableNode child, int index) { - if (!allowsChildren) { - throw new IllegalStateException("this node cannot accept children"); - } - - if (children.contains(child)) { - children.remove(child); - index--; - } - - children.add(index, child); - - if (child.getParent() != this) { - child.setParent(this); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void remove(int index) { - children.remove(index).setParent(null); - } - - /** - * {@inheritDoc} - */ - @Override - public void remove(MutableTreeTableNode node) { - children.remove(node); - node.setParent(null); - } - - /** - * {@inheritDoc} - */ - @Override - public void removeFromParent() { - parent.remove(this); - } - - /** - * {@inheritDoc} - */ - @Override - public void setParent(MutableTreeTableNode newParent) { - if (newParent == null || newParent.getAllowsChildren()) { - if (parent != null && parent.getIndex(this) != -1) { - parent.remove(this); - } - } else { - throw new IllegalArgumentException( - "newParent does not allow children"); - } - - parent = newParent; - - if (parent != null && parent.getIndex(this) == -1) { - parent.insert(this, parent.getChildCount()); - } - } - - /** - * Returns this node's user object. - * - * @return the Object stored at this node by the user - * @see #setUserObject - * @see #toString - */ - @Override - public Object getUserObject() { - return userObject; - } - - /** - * {@inheritDoc} - */ - @Override - public void setUserObject(Object object) { - userObject = object; - } - - /** - * {@inheritDoc} - */ - @Override - public TreeTableNode getChildAt(int childIndex) { - return children.get(childIndex); - } - - /** - * {@inheritDoc} - */ - @Override - public int getIndex(TreeNode node) { - return children.indexOf(node); - } - - /** - * {@inheritDoc} - */ - @Override - public TreeTableNode getParent() { - return parent; - } - - /** - * {@inheritDoc} - */ - @Override - public Enumeration children() { - return Collections.enumeration(children); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean getAllowsChildren() { - return allowsChildren; - } - - /** - * Determines whether or not this node is allowed to have children. If - * {@code allowsChildren} is {@code false}, all of this node's children are - * removed. - *

                              - * Note: By default, a node allows children. - * - * @param allowsChildren - * {@code true} if this node is allowed to have children - */ - public void setAllowsChildren(boolean allowsChildren) { - this.allowsChildren = allowsChildren; - - if (!this.allowsChildren) { - children.clear(); - } - } - - /** - * {@inheritDoc} - */ - @Override - public int getChildCount() { - return children.size(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isLeaf() { - return getChildCount() == 0; - } - - /** - * Determines whether the specified column is editable. - * - * @param column - * the column to query - * @return always returns {@code false} - */ - @Override - public boolean isEditable(int column) { - return false; - } - - /** - * Sets the value for the given {@code column}. - * - * @impl does nothing. It is provided for convenience. - * @param aValue - * the value to set - * @param column - * the column to set the value on - */ - @Override - public void setValueAt(Object aValue, int column) { - // does nothing - } - - /** - * Returns the result of sending toString() to this node's - * user object, or null if this node has no user object. - * - * @see #getUserObject - */ - @Override - public String toString() { - if (userObject == null) { - return ""; - } else { - return userObject.toString(); - } - } -} diff --git a/src/main/java/org/jdesktop/swingx/treetable/AbstractTreeTableModel.java b/src/main/java/org/jdesktop/swingx/treetable/AbstractTreeTableModel.java deleted file mode 100644 index aa5a0cc821..0000000000 --- a/src/main/java/org/jdesktop/swingx/treetable/AbstractTreeTableModel.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * $Id: AbstractTreeTableModel.java 3780 2010-09-09 16:17:41Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.treetable; - -import org.jdesktop.swingx.tree.TreeModelSupport; - -import javax.swing.event.TreeModelListener; -import javax.swing.tree.TreePath; - -// There is no javax.swing.tree.AbstractTreeModel; There ought to be one. - -/** - * AbstractTreeTableModel provides an implementation of - * {@link TreeTableModel} as a convenient starting - * point in defining custom data models for - * {@link org.jdesktop.swingx.JXTreeTable}. It takes care of listener - * management and contains convenience methods for creating and dispatching - * {@code TreeModelEvent}s. To create a concreate instance of - * {@code TreeTableModel} you need only to provide implementations for the - * following methods: - * - *

                              - * public int getColumnCount();
                              - * public Object getValueAt(Object node, int column);
                              - * public Object getChild(Object parent, int index);
                              - * public int getChildCount(Object parent);
                              - * public int getIndexOfChild(Object parent, Object child);
                              - * public boolean isLeaf(Object node);
                              - * 
                              - * - * @author Ramesh Gupta - * @author Karl Schaefer - */ -public abstract class AbstractTreeTableModel implements TreeTableModel { - - /** - * Root node of the model - */ - protected Object root; - - /** - * Provides support for event dispatching. - */ - protected TreeModelSupport modelSupport; - - /** - * Constructs an {@code AbstractTreeTableModel} with a {@code null} root - * node. - */ - public AbstractTreeTableModel() { - this(null); - } - - /** - * Constructs an {@code AbstractTreeTableModel} with the specified root - * node. - * - * @param root - * root node - */ - public AbstractTreeTableModel(Object root) { - this.root = root; - this.modelSupport = new TreeModelSupport(this); - } - - /** - * {@inheritDoc} - */ - @Override - public Class getColumnClass(int column) { - return Object.class; - } - - /** - * {@inheritDoc} - */ - @Override - public String getColumnName(int column) { - //Copied from AbstractTableModel. - //Should use same defaults when possible. - String result = ""; - - for (; column >= 0; column = column / 26 - 1) { - result = (char) ((char) (column % 26) + 'A') + result; - } - - return result; - } - - /** - * {@inheritDoc} - */ - @Override - public int getHierarchicalColumn() { - if (getColumnCount() == 0) { - return -1; - } - - return 0; - } - - /** - * {@inheritDoc} - */ - @Override - public Object getRoot() { - return root; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isCellEditable(Object node, int column) { - // RG: Fix Issue 49 -- Cell not editable, by default. - // Subclasses might override this to return true. - return false; - } - - /** - * Returns true if node is a leaf. - * - * @impl {@code true} if {@code getChildCount(node) == 0} - * @param node a node in the tree, obtained from this data source - * @return true if node is a leaf - */ - @Override - public boolean isLeaf(Object node) { - return getChildCount(node) == 0; - } - - /** - * Sets the value for the {@code node} at {@code columnIndex} to - * {@code value}. - * - * @impl is no-op; provided for convenience for uneditable models - * @param value - * the new value - * @param node - * the node whose value is to be changed - * @param column - * the column whose value is to be changed - * @see #getValueAt - * @see #isCellEditable - * @see javax.swing.table.TableModel#setValueAt(Object, int, int) - */ - @Override - public void setValueAt(Object value, Object node, int column) { - //does nothing - } - - /** - * Called when value for the item identified by path has been changed. If - * newValue signifies a truly new value the model should post a - * {@code treeNodesChanged} event. - *

                              - * - * @impl is no-op. A {@code JXTreeTable} does not usually edit the node directly. - * @param path - * path to the node that has changed - * @param newValue - * the new value from the TreeCellEditor - */ - @Override - public void valueForPathChanged(TreePath path, Object newValue) { - //does nothing - } - - /** - * {@inheritDoc} - */ - @Override - public void addTreeModelListener(TreeModelListener l) { - modelSupport.addTreeModelListener(l); - } - - /** - * {@inheritDoc} - */ - @Override - public void removeTreeModelListener(TreeModelListener l) { - modelSupport.removeTreeModelListener(l); - } - - /** - * Returns an array of all the TreeModelListeners added - * to this JXTreeTable with addTreeModelListener(). - * - * @return all of the TreeModelListeners added or an empty - * array if no listeners have been added - */ - public TreeModelListener[] getTreeModelListeners() { - return modelSupport.getTreeModelListeners(); - } -} diff --git a/src/main/java/org/jdesktop/swingx/treetable/DefaultMutableTreeTableNode.java b/src/main/java/org/jdesktop/swingx/treetable/DefaultMutableTreeTableNode.java deleted file mode 100644 index 2eefb6c910..0000000000 --- a/src/main/java/org/jdesktop/swingx/treetable/DefaultMutableTreeTableNode.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * $Id: DefaultMutableTreeTableNode.java 3780 2010-09-09 16:17:41Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.treetable; - -/** - * A default implementation of an {@code AbstractMutableTreeTableNode} that - * returns {@code getUserObject().toString()} for all value queries. This - * implementation is designed mainly for testing. It is NOT recommended to use - * this implementation. Any user that needs to create {@code TreeTableNode}s - * should consider directly extending {@code AbstractMutableTreeTableNode} or - * directly implementing the interface. - * - * @author Karl Schaefer - */ -public class DefaultMutableTreeTableNode extends AbstractMutableTreeTableNode { - - /** - * - */ - public DefaultMutableTreeTableNode() { - super(); - } - - /** - * @param userObject - */ - public DefaultMutableTreeTableNode(Object userObject) { - super(userObject); - } - - /** - * @param userObject - * @param allowsChildren - */ - public DefaultMutableTreeTableNode(Object userObject, boolean allowsChildren) { - super(userObject, allowsChildren); - } - - /** - * {@inheritDoc} - */ - @Override - public Object getValueAt(int column) { - return getUserObject(); - } - - /** - * {@inheritDoc} - */ - @Override - public int getColumnCount() { - return 1; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isEditable(int column) { - return true; - } - - /** - * {@inheritDoc} - */ - @Override - public void setValueAt(Object aValue, int column) { - setUserObject(aValue); - } -} diff --git a/src/main/java/org/jdesktop/swingx/treetable/DefaultTreeTableModel.java b/src/main/java/org/jdesktop/swingx/treetable/DefaultTreeTableModel.java deleted file mode 100644 index fe75a7c611..0000000000 --- a/src/main/java/org/jdesktop/swingx/treetable/DefaultTreeTableModel.java +++ /dev/null @@ -1,453 +0,0 @@ -/* - * $Id: DefaultTreeTableModel.java 3780 2010-09-09 16:17:41Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.treetable; - -import javax.swing.tree.TreePath; -import java.util.ArrayList; -import java.util.List; - -/** - * {@code DefaultTreeTableModel} is a concrete implementation of - * {@code AbstractTreeTableModel} and is provided purely as a convenience for - * use with {@code TreeTableNode}s. Applications that use {@code JXTreeTable} - * without {@code TreeTableNode}s are expected to provide their own - * implementation of a {@code TreeTableModel}. - *

                              - * The {@code DefaultTreeTableModel} is designed to be used with - * {@code TreeTableNode}s. Specifically, users should extend - * {@code AbstractMutableTreeTableNode} to provide custom implementations for - * data display. - *

                              - * Users who do not provide a list of column identifiers must provide a root - * that contains at least one column. Without specified identifiers the model - * will attempt to calculate the columns required for display by querying the - * root node. Normally, the root node can be little more than a shell (in - * displays that hide it), but without identifiers, the model relies on the root - * node metadata for display. - * - * @author Ramesh Gupta - * @author Karl Schaefer - */ -public class DefaultTreeTableModel extends AbstractTreeTableModel { - /** The List of column identifiers. */ - protected List columnIdentifiers; - - private boolean useAutoCalculatedIdentifiers; - - /** - * Creates a new {@code DefaultTreeTableModel} with a {@code null} root. - */ - public DefaultTreeTableModel() { - this(null); - } - - /** - * Creates a new {@code DefaultTreeTableModel} with the specified - * {@code root}. - * - * @param root - * the root node of the tree - */ - public DefaultTreeTableModel(TreeTableNode root) { - this(root, null); - } - - /** - * Creates a new {@code DefaultTreeTableModel} with the specified {@code - * root} and column names. - * - * @param root - * the root node of the tree - * @param columnNames - * the names of the columns used by this model - * @see #setColumnIdentifiers(List) - */ - public DefaultTreeTableModel(TreeTableNode root, List columnNames) { - super(root); - - setColumnIdentifiers(columnNames); - } - - private boolean isValidTreeTableNode(Object node) { - boolean result = false; - - if (node instanceof TreeTableNode) { - TreeTableNode ttn = (TreeTableNode) node; - - while (!result && ttn != null) { - result = ttn == root; - - ttn = ttn.getParent(); - } - } - - return result; - } - - /** - * Replaces the column identifiers in the model. If the number of - * newIdentifiers is greater than the current number of - * columns, new columns are added to the end of each row in the model. If - * the number of newIdentifiers is less than the current - * number of columns, all the extra columns at the end of a row are - * discarded. - *

                              - * - * @param columnIdentifiers - * vector of column identifiers. If null, set the - * model to zero columns - */ - // from DefaultTableModel - public void setColumnIdentifiers(List columnIdentifiers) { - useAutoCalculatedIdentifiers = columnIdentifiers == null; - - this.columnIdentifiers = useAutoCalculatedIdentifiers - ? getAutoCalculatedIdentifiers(getRoot()) - : columnIdentifiers; - - modelSupport.fireNewRoot(); - } - - private static List getAutoCalculatedIdentifiers( - TreeTableNode exemplar) { - List autoCalculatedIndentifiers = new ArrayList(); - - if (exemplar != null) { - for (int i = 0, len = exemplar.getColumnCount(); i < len; i++) { - // forces getColumnName to use super.getColumnName - autoCalculatedIndentifiers.add(null); - } - } - - return autoCalculatedIndentifiers; - } - - /** - * Returns the root of the tree. Returns {@code null} only if the tree has - * no nodes. - * - * @return the root of the tree - * - * @throws ClassCastException - * if {@code root} is not a {@code TreeTableNode}. Even though - * subclasses have direct access to {@code root}, they should - * avoid accessing it directly. - * @see AbstractTreeTableModel#root - * @see #setRoot(TreeTableNode) - */ - @Override - public TreeTableNode getRoot() { - return (TreeTableNode) root; - } - - /** - * Gets the value for the {@code node} at {@code column}. - * - * @impl delegates to {@code TreeTableNode.getValueAt(int)} - * @param node - * the node whose value is to be queried - * @param column - * the column whose value is to be queried - * @return the value Object at the specified cell - * @throws IllegalArgumentException - * if {@code node} is not an instance of {@code TreeTableNode} - * or is not managed by this model, or {@code column} is not a - * valid column index - */ - @Override - public Object getValueAt(Object node, int column) { - if (!isValidTreeTableNode(node)) { - throw new IllegalArgumentException( - "node must be a valid node managed by this model"); - } - - if (column < 0 || column >= getColumnCount()) { - throw new IllegalArgumentException("column must be a valid index"); - } - - TreeTableNode ttn = (TreeTableNode) node; - - if (column >= ttn.getColumnCount()) { - return null; - } - - return ttn.getValueAt(column); - } - - /** - * {@inheritDoc} - */ - @Override - public void setValueAt(Object value, Object node, int column) { - if (!isValidTreeTableNode(node)) { - throw new IllegalArgumentException( - "node must be a valid node managed by this model"); - } - - if (column < 0 || column >= getColumnCount()) { - throw new IllegalArgumentException("column must be a valid index"); - } - - TreeTableNode ttn = (TreeTableNode) node; - - if (column < ttn.getColumnCount()) { - ttn.setValueAt(value, column); - - modelSupport.firePathChanged(new TreePath(getPathToRoot(ttn))); - } - } - - /** - * {@inheritDoc} - */ - @Override - public int getColumnCount() { - return columnIdentifiers.size(); - } - - /** - * {@inheritDoc} - */ - // Can we make getColumnClass final and avoid the complex DTM copy? -- kgs - @Override - public String getColumnName(int column) { - // Copied from DefaultTableModel. - Object id = null; - - // This test is to cover the case when - // getColumnCount has been subclassed by mistake ... - if (column < columnIdentifiers.size() && (column >= 0)) { - id = columnIdentifiers.get(column); - } - - return (id == null) ? super.getColumnName(column) : id.toString(); - } - - /** - * {@inheritDoc} - */ - @Override - public Object getChild(Object parent, int index) { - if (!isValidTreeTableNode(parent)) { - throw new IllegalArgumentException( - "parent must be a TreeTableNode managed by this model"); - } - - return ((TreeTableNode) parent).getChildAt(index); - } - - /** - * {@inheritDoc} - */ - @Override - public int getChildCount(Object parent) { - if (!isValidTreeTableNode(parent)) { - throw new IllegalArgumentException( - "parent must be a TreeTableNode managed by this model"); - } - - return ((TreeTableNode) parent).getChildCount(); - } - - /** - * {@inheritDoc} - */ - @Override - public int getIndexOfChild(Object parent, Object child) { - if (!isValidTreeTableNode(parent) || !isValidTreeTableNode(child)) { - return -1; - } - - return ((TreeTableNode) parent).getIndex((TreeTableNode) child); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isCellEditable(Object node, int column) { - if (!isValidTreeTableNode(node)) { - throw new IllegalArgumentException( - "node must be a valid node managed by this model"); - } - - if (column < 0 || column >= getColumnCount()) { - throw new IllegalArgumentException("column must be a valid index"); - } - - TreeTableNode ttn = (TreeTableNode) node; - - if (column >= ttn.getColumnCount()) { - return false; - } - - return ttn.isEditable(column); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isLeaf(Object node) { - if (!isValidTreeTableNode(node)) { - throw new IllegalArgumentException( - "node must be a TreeTableNode managed by this model"); - } - - return ((TreeTableNode) node).isLeaf(); - } - - /** - * Gets the path from the root to the specified node. - * - * @param aNode - * the node to query - * @return an array of {@code TreeTableNode}s, where - * {@code arr[0].equals(getRoot())} and - * {@code arr[arr.length - 1].equals(aNode)}, or an empty array if - * the node is not found. - * @throws NullPointerException - * if {@code aNode} is {@code null} - */ - public TreeTableNode[] getPathToRoot(TreeTableNode aNode) { - List path = new ArrayList(); - TreeTableNode node = aNode; - - while (node != root) { - path.add(0, node); - - node = node.getParent(); - } - - if (node == root) { - path.add(0, node); - } - - return path.toArray(new TreeTableNode[0]); - } - - /** - * Sets the root for this table model. If no column identifiers have been - * specified, this will rebuild the identifier list, using {@code root} as - * an examplar of the table. - * - * @param root - * the node to set as root - */ - public void setRoot(TreeTableNode root) { - this.root = root; - - if (useAutoCalculatedIdentifiers) { - // rebuild the list - //this already fires an event don't duplicate - setColumnIdentifiers(null); - } else { - modelSupport.fireNewRoot(); - } - } - - /** - * Invoked this to insert newChild at location index in parents children. - * This will then message nodesWereInserted to create the appropriate event. - * This is the preferred way to add children as it will create the - * appropriate event. - */ - public void insertNodeInto(MutableTreeTableNode newChild, - MutableTreeTableNode parent, int index) { - parent.insert(newChild, index); - - modelSupport.fireChildAdded(new TreePath(getPathToRoot(parent)), index, - newChild); - } - - /** - * Message this to remove node from its parent. This will message - * nodesWereRemoved to create the appropriate event. This is the preferred - * way to remove a node as it handles the event creation for you. - */ - public void removeNodeFromParent(MutableTreeTableNode node) { - MutableTreeTableNode parent = (MutableTreeTableNode) node.getParent(); - - if (parent == null) { - throw new IllegalArgumentException("node does not have a parent."); - } - - int index = parent.getIndex(node); - node.removeFromParent(); - - modelSupport.fireChildRemoved(new TreePath(getPathToRoot(parent)), - index, node); - } - - /** - * Called when value for the item identified by path has been changed. If - * newValue signifies a truly new value the model should post a {@code - * treeNodesChanged} event. - *

                              - * This changes the object backing the {@code TreeTableNode} described by - * the path. This change does not alter a nodes children in any way. If you - * need to change structure of the node, use one of the provided mutator - * methods. - * - * @param path - * path to the node that has changed - * @param newValue - * the new value - * @throws NullPointerException - * if {@code path} is {@code null} - * @throws IllegalArgumentException - * if {@code path} is not a path managed by this model - * @throws ClassCastException - * if {@code path.getLastPathComponent()} is not a {@code - * TreeTableNode} - */ - @Override - public void valueForPathChanged(TreePath path, Object newValue) { - if (path.getPathComponent(0) != root) { - throw new IllegalArgumentException("invalid path"); - } - - TreeTableNode node = (TreeTableNode) path.getLastPathComponent(); - node.setUserObject(newValue); - - modelSupport.firePathChanged(path); - } - - /** - * Sets the user object for a node. Client code must use this method, so - * that the model can notify listeners that a change has occurred. - *

                              - * This method is a convenient cover for - * {@link #valueForPathChanged(TreePath, Object)}. - * - * @param node - * the node to modify - * @param userObject - * the new user object to set - * @throws NullPointerException - * if {@code node} is {@code null} - * @throws IllegalArgumentException - * if {@code node} is not a node managed by this model - */ - public void setUserObject(TreeTableNode node, Object userObject) { - valueForPathChanged(new TreePath(getPathToRoot(node)), userObject); - } -} diff --git a/src/main/java/org/jdesktop/swingx/treetable/FileSystemModel.java b/src/main/java/org/jdesktop/swingx/treetable/FileSystemModel.java deleted file mode 100644 index ab191d601d..0000000000 --- a/src/main/java/org/jdesktop/swingx/treetable/FileSystemModel.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * $Id: FileSystemModel.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.treetable; - -import java.io.File; -import java.util.Arrays; -import java.util.Date; - -/** - * A tree table model to simulate a file system. - *

                              - * This tree table model implementation extends {@code AbstractTreeTableModel}. - * The file system metaphor demonstrates that it is often easier to directly - * implement tree structures directly instead of using intermediaries, such as - * {@code TreeTableNode}. - *

                              - * A comparison of this class with {@code SimpleFileSystemModel}, shows that - * extending {@code AbstractTreeTableModel} is often easier than creating a model - * from scratch. - *

                              - * A "full" version of this model might allow editing of file names, the - * deletion of files, and the movement of files. This simple implementation does - * not intend to tackle such problems, but this implementation may be extended - * to handle such details. - * - * @author Ramesh Gupta - * @author Karl Schaefer - */ -public class FileSystemModel extends AbstractTreeTableModel { - // The the returned file length for directories. - private static final Long DIRECTORY = 0L; - - /** - * Creates a file system model using the root directory as the model root. - */ - public FileSystemModel() { - this(new File(File.separator)); - } - - /** - * Creates a file system model using the specified {@code root}. - * - * @param root - * the root for this model; this may be different than the root - * directory for a file system. - */ - public FileSystemModel(File root) { - super(root); - } - - private boolean isValidFileNode(Object file) { - boolean result = false; - - if (file instanceof File) { - File f = (File) file; - - while (!result && f != null) { - result = f.equals(root); - - f = f.getParentFile(); - } - } - - return result; - } - - /** - * {@inheritDoc} - */ - @Override - public File getChild(Object parent, int index) { - if (!isValidFileNode(parent)) { - throw new IllegalArgumentException("parent is not a file governed by this model"); - } - - File parentFile = (File) parent; - String[] children = parentFile.list(); - - if (children != null) { - return new File(parentFile, children[index]); - } - - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public int getChildCount(Object parent) { - if (parent instanceof File) { - String[] children = ((File) parent).list(); - - if (children != null) { - return children.length; - } - } - - return 0; - } - - /** - * {@inheritDoc} - */ - @Override - public Class getColumnClass(int column) { - switch (column) { - case 0: - return String.class; - case 1: - return Long.class; - case 2: - return Boolean.class; - case 3: - return Date.class; - default: - return super.getColumnClass(column); - } - } - - @Override - public int getColumnCount() { - return 4; - } - - @Override - public String getColumnName(int column) { - switch (column) { - case 0: - return "Name"; - case 1: - return "Size"; - case 2: - return "Directory"; - case 3: - return "Modification Date"; - default: - return super.getColumnName(column); - } - } - - @Override - public Object getValueAt(Object node, int column) { - if (node instanceof File) { - File file = (File) node; - switch (column) { - case 0: - return file.getName(); - case 1: - return isLeaf(node) ? file.length() : DIRECTORY; - case 2: - return file.isDirectory(); - case 3: - return new Date(file.lastModified()); - } - } - - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public int getIndexOfChild(Object parent, Object child) { - if (parent instanceof File && child instanceof File) { - File parentFile = (File) parent; - File[] files = parentFile.listFiles(); - - Arrays.sort(files); - - for (int i = 0, len = files.length; i < len; i++) { - if (files[i].equals(child)) { - return i; - } - } - } - - return -1; - } - - /** - * {@inheritDoc} - */ - @Override - public File getRoot() { - return (File) root; - } - - /** - * Sets the root for this tree table model. This method will notify - * listeners that a change has taken place. - * - * @param root - * the new root node to set - */ - public void setRoot(File root) { - this.root = root; - - modelSupport.fireNewRoot(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isLeaf(Object node) { - if (node instanceof File) { - //do not use isFile(); some system files return false - return ((File) node).list() == null; - } - - return true; - } -} diff --git a/src/main/java/org/jdesktop/swingx/treetable/MutableTreeTableNode.java b/src/main/java/org/jdesktop/swingx/treetable/MutableTreeTableNode.java deleted file mode 100644 index 6fa36d13c1..0000000000 --- a/src/main/java/org/jdesktop/swingx/treetable/MutableTreeTableNode.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * $Id: MutableTreeTableNode.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.treetable; - -import java.util.Enumeration; - -/** - * Defines the requirements for a tree table node object that can change -- by - * adding or removing child nodes, or by changing the contents of a user object - * stored in the node. - *

                              - * Note this does not extend {@code MutableTreeNode} to minimize the contract - * breakage, cf. {@link TreeTableNode#getIndex(javax.swing.tree.TreeNode)}. - * - * @see javax.swing.tree.MutableTreeNode - * - * @author Karl Schaefer - */ -public interface MutableTreeTableNode extends TreeTableNode { - /** - * Returns an enumeration this node's children. - * - * @return an enumeration of {@code TreeTableNode}s - */ - @Override - Enumeration children(); - - /** - * Adds the {@code child} to this node at the specified {@code index}. This - * method calls {@code setParent} on {@code child} with {@code this} as the - * parameter. - * - * @param child - * the node to add as a child - * @param index - * the index of the child - * @throws IndexOutOfBoundsException - * if {@code index} is not a valid index - */ - void insert(MutableTreeTableNode child, int index); - - /** - * Removes the child node at the specified {@code index} from this node. - * This method calls {@code setParent} on {@code child} with a {@code null} - * parameter. - * - * @param index - * the index of the child - * @throws IndexOutOfBoundsException - * if {@code index} is not a valid index - */ - void remove(int index); - - /** - * Removes the specified child {@code node} from this node. - * This method calls {@code setParent} on {@code child} with a {@code null} - * parameter. - * - * @param node - * the index of the child - */ - void remove(MutableTreeTableNode node); - - /** - * Removes this node from it's parent. Most implementations will use - * {@code getParent().remove(this)}. - * - * @throws NullPointerException - * if {@code getParent()} returns {@code null} - */ - void removeFromParent(); - - /** - * Sets the parent of this node to {@code newParent}. This methods remove - * the node from its old parent. - * - * @param newParent - * the new parent for this node - */ - void setParent(MutableTreeTableNode newParent); -} diff --git a/src/main/java/org/jdesktop/swingx/treetable/SimpleFileSystemModel.java b/src/main/java/org/jdesktop/swingx/treetable/SimpleFileSystemModel.java deleted file mode 100644 index 8cbdd187a1..0000000000 --- a/src/main/java/org/jdesktop/swingx/treetable/SimpleFileSystemModel.java +++ /dev/null @@ -1,272 +0,0 @@ -/* - * $Id: SimpleFileSystemModel.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.treetable; - -import javax.swing.event.EventListenerList; -import javax.swing.event.TreeModelListener; -import javax.swing.tree.TreePath; -import java.io.File; -import java.util.Date; - -/** - * A tree table model to simulate a file system. - *

                              - * This tree table model implementation does not extends - * {@code AbstractTreeTableModel}. The file system metaphor demonstrates that - * it is often easier to directly implement tree structures directly instead of - * using intermediaries, such as {@code TreeNode}. - *

                              - * It would be possible to create this same class by extending - * {@code AbstractTreeTableModel}, however the number of methods that you would - * need to override almost precludes that means of implementation. - *

                              - * A "full" version of this model might allow editing of file names, the - * deletion of files, and the movement of files. This simple implementation does - * not intend to tackle such problems, but this implementation may be extended - * to handle such details. - * - * @author Ramesh Gupta - * @author Karl Schaefer - */ -public class SimpleFileSystemModel implements TreeTableModel { - protected EventListenerList listenerList; - - // the returned file length for directories - private static final Long ZERO = Long.valueOf(0); - - private File root; - - /** - * Creates a file system model, using the root directory as the model root. - */ - public SimpleFileSystemModel() { - this(new File(File.separator)); - } - - /** - * Creates a file system model, using the specified {@code root} as the - * model root. - */ - public SimpleFileSystemModel(File root) { - this.root = root; - this.listenerList = new EventListenerList(); - } - - /** - * {@inheritDoc} - */ - @Override - public File getChild(Object parent, int index) { - if (parent instanceof File) { - File parentFile = (File) parent; - File[] files = parentFile.listFiles(); - - if (files != null) { - return files[index]; - } - } - - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public int getChildCount(Object parent) { - if (parent instanceof File) { - String[] children = ((File) parent).list(); - - if (children != null) { - return children.length; - } - } - - return 0; - } - - /** - * {@inheritDoc} - */ - @Override - public Class getColumnClass(int column) { - switch(column) { - case 0: - return String.class; - case 1: - return Long.class; - case 2: - return Boolean.class; - case 3: - return Date.class; - default: - return Object.class; - } - } - - /** - * {@inheritDoc} - */ - @Override - public int getColumnCount() { - return 4; - } - - /** - * {@inheritDoc} - */ - @Override - public String getColumnName(int column) { - switch (column) { - case 0: - return "Name"; - case 1: - return "Size"; - case 2: - return "Directory"; - case 3: - return "Modification Date"; - default: - return "Column " + column; - } - } - - /** - * {@inheritDoc} - */ - @Override - public Object getValueAt(Object node, int column) { - if (node instanceof File) { - File file = (File) node; - switch (column) { - case 0: - return file.getName(); - case 1: - return file.isFile() ? file.length() : ZERO; - case 2: - return file.isDirectory(); - case 3: - return new Date(file.lastModified()); - } - } - - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public int getHierarchicalColumn() { - return 0; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isCellEditable(Object node, int column) { - return false; - } - - /** - * {@inheritDoc} - */ - @Override - public void setValueAt(Object value, Object node, int column) { - //does nothing - } - - /** - * {@inheritDoc} - */ - @Override - public void addTreeModelListener(TreeModelListener l) { - listenerList.add(TreeModelListener.class, l); - } - - /** - * {@inheritDoc} - */ - @Override - public int getIndexOfChild(Object parent, Object child) { - if (parent instanceof File && child instanceof File) { - File parentFile = (File) parent; - File[] files = parentFile.listFiles(); - - for (int i = 0, len = files.length; i < len; i++) { - if (files[i].equals(child)) { - return i; - } - } - } - - return -1; - } - - /** - * {@inheritDoc} - */ - @Override - public File getRoot() { - return root; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isLeaf(Object node) { - if (node instanceof File) { - //do not use isFile(); some system files return false - return ((File) node).list() == null; - } - - return true; - } - - /** - * {@inheritDoc} - */ - @Override - public void removeTreeModelListener(TreeModelListener l) { - listenerList.remove(TreeModelListener.class, l); - } - - /** - * {@inheritDoc} - */ - @Override - public void valueForPathChanged(TreePath path, Object newValue) { - //does nothing - } - - /** - * Gets a an array of all the listeners attached to this model. - * - * @return an array of listeners; this array is guaranteed to be - * non-{@code null} - */ - public TreeModelListener[] getTreeModelListeners() { - return listenerList.getListeners(TreeModelListener.class); - } -} diff --git a/src/main/java/org/jdesktop/swingx/treetable/TreeTableCellEditor.java b/src/main/java/org/jdesktop/swingx/treetable/TreeTableCellEditor.java deleted file mode 100644 index f448f8ac57..0000000000 --- a/src/main/java/org/jdesktop/swingx/treetable/TreeTableCellEditor.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * $Id: TreeTableCellEditor.java 3990 2011-03-31 13:41:08Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.treetable; - -import org.jdesktop.swingx.JXTable.GenericEditor; - -import javax.swing.*; -import javax.swing.tree.TreeCellRenderer; -import java.awt.*; -import java.awt.event.MouseEvent; -import java.util.EventObject; -import java.util.logging.Logger; - -/** - * An editor that can be used to edit the tree column. This extends - * DefaultCellEditor and uses a JTextField (actually, TreeTableTextField) - * to perform the actual editing. - *

                              To support editing of the tree column we can not make the tree - * editable. The reason this doesn't work is that you can not use - * the same component for editing and rendering. The table may have - * the need to paint cells, while a cell is being edited. If the same - * component were used for the rendering and editing the component would - * be moved around, and the contents would change. When editing, this - * is undesirable, the contents of the text field must stay the same, - * including the caret blinking, and selections persisting. For this - * reason the editing is done via a TableCellEditor. - *

                              Another interesting thing to be aware of is how tree positions - * its render and editor. The render/editor is responsible for drawing the - * icon indicating the type of node (leaf, branch...). The tree is - * responsible for drawing any other indicators, perhaps an additional - * +/- sign, or lines connecting the various nodes. So, the renderer - * is positioned based on depth. On the other hand, table always makes - * its editor fill the contents of the cell. To get the allusion - * that the table cell editor is part of the tree, we don't want the - * table cell editor to fill the cell bounds. We want it to be placed - * in the same manner as tree places it editor, and have table message - * the tree to paint any decorations the tree wants. Then, we would - * only have to worry about the editing part. The approach taken - * here is to determine where tree would place the editor, and to override - * the reshape method in the JTextField component to - * nudge the textfield to the location tree would place it. Since - * JXTreeTable will paint the tree behind the editor everything should - * just work. So, that is what we are doing here. Determining of - * the icon position will only work if the TreeCellRenderer is - * an instance of DefaultTreeCellRenderer. If you need custom - * TreeCellRenderers, that don't descend from DefaultTreeCellRenderer, - * and you want to support editing in JXTreeTable, you will have - * to do something similar. - * - * @author Scott Violet - * @author Ramesh Gupta - */ -public class TreeTableCellEditor extends GenericEditor { - //DefaultCellEditor { -// JW: changed to extends GenericEditor to fix #1365-swingx - -// borders different in hierarchical column vs. table column -// - @SuppressWarnings("unused") - private static final Logger LOG = Logger - .getLogger(TreeTableCellEditor.class.getName()); - - public TreeTableCellEditor(JTree tree) { - super(new TreeTableTextField()); - if (tree == null) { - throw new IllegalArgumentException("null tree"); - } - // JW: no need to... - this.tree = tree; // immutable - } - - /** - * Overriden to determine an offset that tree would place the editor at. The - * offset is determined from the getRowBounds JTree method, - * and additionaly from the icon DefaultTreeCellRenderer will use. - *

                              - * The offset is then set on the TreeTableTextField component created in the - * constructor, and returned. - */ - @Override - public Component getTableCellEditorComponent(JTable table, Object value, - boolean isSelected, int row, int column) { - Component component = super.getTableCellEditorComponent(table, value, - isSelected, row, column); - // JW: this implementation is not bidi-compliant, need to do better - initEditorOffset(table, row, column, isSelected); - return component; - } - - - /** - * @param row - * @param isSelected - */ - protected void initEditorOffset(JTable table, int row, int column, - boolean isSelected) { - if (tree == null) - return; -// Rectangle bounds = tree.getRowBounds(row); -// int offset = bounds.x; - Object node = tree.getPathForRow(row).getLastPathComponent(); - boolean leaf = tree.getModel().isLeaf(node); - boolean expanded = tree.isExpanded(row); - TreeCellRenderer tcr = tree.getCellRenderer(); - Component editorComponent = tcr.getTreeCellRendererComponent(tree, node, - isSelected, expanded, leaf, row, false); - - ((TreeTableTextField) getComponent()).init(row, - column, table, tree, editorComponent); - } - - /** - * This is overriden to forward the event to the tree. This will - * return true if the click count >= clickCountToStart, or the event is null. - */ - @Override - public boolean isCellEditable(EventObject e) { - // JW: quick fix for #592-swingx - - // editing not started on keyEvent in hierarchical column (1.6) - if (e instanceof MouseEvent) { - return (((MouseEvent) e).getClickCount() >= clickCountToStart); - } - return true; - } - - /** - * Component used by TreeTableCellEditor. The only thing this does - * is to override the reshape method, and to ALWAYS - * make the x location be offset. - */ - static class TreeTableTextField extends JTextField { - private int iconWidth; - - void init(int row, int column, JTable table, JTree tree, Component editorComponent) { - this.column = column; - this.row = row; - this.table = table; - this.tree = tree; - updateIconWidth(editorComponent); - setComponentOrientation(table.getComponentOrientation()); - } - - /** - * @param treeComponent - */ - private void updateIconWidth(Component treeComponent) { - iconWidth = 0; - if (!(treeComponent instanceof JLabel)) return; - Icon icon = ((JLabel) treeComponent).getIcon(); - if (icon != null) { - iconWidth = icon.getIconWidth() + ((JLabel) treeComponent).getIconTextGap(); - } - - } - - private int column; - private int row; - private JTable table; - private JTree tree; - - /** - * {@inheritDoc}

                              - * - * Overridden to place the textfield in the node content boundaries, - * leaving the icon to the renderer.

                              - * - * PENDING JW: insets? - * - */ - @SuppressWarnings("deprecation") - @Override - public void reshape(int x, int y, int width, int height) { - // Allows precise positioning of text field in the tree cell. - // following three lines didn't work out - //Border border = this.getBorder(); // get this text field's border - //Insets insets = border == null ? null : border.getBorderInsets(this); - //int newOffset = offset - (insets == null ? 0 : insets.left); - - Rectangle cellRect = table.getCellRect(0, column, false); - Rectangle nodeRect = tree.getRowBounds(row); - nodeRect.width -= iconWidth; - if(table.getComponentOrientation().isLeftToRight()) { - int nodeStart = cellRect.x + nodeRect.x + iconWidth; - int nodeEnd = cellRect.x + cellRect.width; - super.reshape(nodeStart, y, nodeEnd - nodeStart, height); -// int newOffset = nodeLeftX - getInsets().left; -// super.reshape(x + newOffset, y, width - newOffset, height); - } else { - int nodeRightX = nodeRect.x + nodeRect.width; - nodeRect.x = 0; //Math.max(0, nodeRect.x); - // ignore the parameter - width = nodeRightX - nodeRect.x; - super.reshape(cellRect.x + nodeRect.x, y, width, height); - } - } - - } - - private final JTree tree; // immutable -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/treetable/TreeTableModel.java b/src/main/java/org/jdesktop/swingx/treetable/TreeTableModel.java deleted file mode 100644 index 482960042d..0000000000 --- a/src/main/java/org/jdesktop/swingx/treetable/TreeTableModel.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * $Id: TreeTableModel.java 2476 2007-11-25 15:52:59Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.treetable; - - -import javax.swing.tree.TreeModel; - -/** - * The model used by {@code JXTreeTable}. - *

                              - * This model is a combination of {@link TreeModel} and - * {@link javax.swing.table.TableModel} for use with the tree table. It does not - * actually extends {@code TableModel}, but instead copies method signature as - * appropriate and alters other to work with the underlying {@code TreeModel}. - *

                              - * - * @see TreeModel - * @see javax.swing.table.TableModel - */ -public interface TreeTableModel extends TreeModel { - /** - * Returns the most specific superclass for all the cell values in the - * column. This is used by the {@code JXTreeTable} to set up a default - * renderer and editor for the column. - * - * @param columnIndex - * the index of the column - * @return the common ancestor class of the object values in the model. - * @see javax.swing.table.TableModel#getColumnClass(int) - */ - public Class getColumnClass(int columnIndex); - - /** - * Returns the number of columns in the model. A {@code JXTreeTable} uses - * this method to determine how many columns it should create and display by - * default. - * - * @return the number of columns in the model - * @see javax.swing.table.TableModel#getColumnCount() - */ - public int getColumnCount(); - - /** - * Returns the name of the column at {@code columnIndex}. This is used to - * initialize the table's column header name. Note: this name does not need - * to be unique; two columns in a table can have the same name. - * - * @param column - * the index of the column - * @return the name of the column - * @see javax.swing.table.TableModel#getColumnName(int) - */ - public String getColumnName(int column); - - /** - * Returns the column that is the "tree" column. While it is not required, - * most implementations will default the first column to be the hierarchical - * one. - * - * @return the index of the hierarchical column or -1 if no column is the - * hierarchical column. - */ - public int getHierarchicalColumn(); - - /** - * Returns the value for the {@code node} at {@code columnIndex}. The - * {@code node} must be managed by this model. Unamanaged nodes should throw - * an {@code IllegalArgumentException}. - * - * @param node - * the node whose value is to be queried - * @param column - * the column whose value is to be queried - * @return the value Object at the specified cell - * @throws IllegalArgumentException - * if {@code node} is not managed by this model. - * @see #setValueAt - * @see javax.swing.table.TableModel#getValueAt(int, int) - */ - public Object getValueAt(Object node, int column); - - /** - * Returns true if the cell for the {@code node} at {@code columnIndex} is - * editable. Otherwise, {@code setValueAt} on the cell will not change the - * value of that cell. The {@code node} must be managed by this model. - * Unamanaged nodes should throw an {@code IllegalArgumentException}. - * - * @param node - * the node whose value to be queried - * @param column - * the column whose value to be queried - * @return true if the cell is editable - * @throws IllegalArgumentException - * if {@code node} is not managed by this model. - * @see #setValueAt - * @see javax.swing.table.TableModel#isCellEditable(int, int) - */ - public boolean isCellEditable(Object node, int column); - - /** - * Sets the value for the {@code node} at {@code columnIndex} to - * {@code value}. The {@code node} must be managed by this model. - * Unamanaged nodes should throw an {@code IllegalArgumentException}. - * - * - * @param value - * the new value - * @param node - * the node whose value is to be changed - * @param column - * the column whose value is to be changed - * @throws IllegalArgumentException - * if {@code node} is not managed by this model. - * @see #getValueAt - * @see #isCellEditable - * @see javax.swing.table.TableModel#setValueAt(Object, int, int) - */ - public void setValueAt(Object value, Object node, int column); -} \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/treetable/TreeTableModelProvider.java b/src/main/java/org/jdesktop/swingx/treetable/TreeTableModelProvider.java deleted file mode 100644 index 25ba8ffce9..0000000000 --- a/src/main/java/org/jdesktop/swingx/treetable/TreeTableModelProvider.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Created on 11.01.2011 - * - */ -package org.jdesktop.swingx.treetable; - - -/** - * Interface which guarantees access to a TreeTableModel. It is implemented by - * the internal TreeTableModelAdapter of JXTreeTable to allow direct access to - * the underlying TreeTableModel from the adapter. - *

                              - * - * That's useful f.i. when trying to configure TableColumnExt in a - * ColumnFactory, like in - * - *

                              - * 
                              - * JXTreeTable table = new JXTreeTable();
                              - * ColumnFactory factory = new ColumnFactory() {
                              - * 
                              - *     @Override
                              - *     public void configureTableColumn(TableModel model,
                              - *             TableColumnExt columnExt) {
                              - *         super.configureTableColumn(model, columnExt);
                              - *         if (model instanceof TreeTableModelProvider) {
                              - *             TreeTableModel treeTableModel = ((TreeTableModelProvider) model).getTreeTableModel();
                              - *             if (treeTableModel.getHierarchicalColumn() == columnExt.getModelIndex()) {
                              - *                 columnExt.setTitle("Hierarchical: " + columnExt.getTitle());
                              - *             }
                              - *         }
                              - *     }
                              - * };
                              - * table.setColumnFactory(factory);
                              - * table.setTreeTableModel(new FileSystemModel());
                              - * 
                              - * 
                              - * 
                              - * - * @author Jeanette Winzenburg, Berlin - */ -public interface TreeTableModelProvider { - - /** - * Returns a TreeTableModel, guaranteed to be not null. - * - * @return a TreeTableModel, guaranteed to be not null. - */ - TreeTableModel getTreeTableModel(); - -} diff --git a/src/main/java/org/jdesktop/swingx/treetable/TreeTableNode.java b/src/main/java/org/jdesktop/swingx/treetable/TreeTableNode.java deleted file mode 100644 index f1c69a9dc9..0000000000 --- a/src/main/java/org/jdesktop/swingx/treetable/TreeTableNode.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * $Id: TreeTableNode.java 3927 2011-02-22 16:34:11Z kleopatra $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.treetable; - -import javax.swing.tree.TreeNode; -import java.util.Enumeration; - -/** - * Defines the requirements for an object that can be used as a tree node in a - * {@code JXTreeTable}. - * - * @author Karl Schaefer - */ -public interface TreeTableNode extends TreeNode { - /** - * Returns an enumeration this node's children. - * - * @return an enumeration of {@code TreeTableNode}s - */ - @Override - Enumeration children(); - - /** - * Gets the value for this node that corresponds to a particular tabular - * column. - * - * @param column - * the column to query - * @return the value for the queried column - * @throws IndexOutOfBoundsException - * if {@code column} is not a valid column index - */ - Object getValueAt(int column); - - /** - * Overridden to specify the return type. Returns the child {@code TreeNode} - * at index {@code childIndex}. Models that utilize this node should verify - * the column count before querying this node, since nodes may return - * differing sizes even for the same model. - * - * @param childIndex - * the index of the child - * @return the {@code TreeTableNode} corresponding to the specified index - */ - @Override - TreeTableNode getChildAt(int childIndex); - - /** - * Returns the number of columns supported by this {@code TreeTableNode}. - * - * @return the number of columns this node supports - */ - int getColumnCount(); - - /** - * Overridden to specify the return type. Returns the parent - * {@code TreeTableNode} of the receiver. - * - * @return the parent {@code TreeTableNode} or {@code null} if this node has - * no parent (such nodes are usually root nodes). - */ - @Override - TreeTableNode getParent(); - - /** - * Determines whether the specified column is editable. - * - * @param column - * the column to query - * @return {@code true} if the column is editable, {@code false} otherwise - */ - boolean isEditable(int column); - - /** - * Sets the value for the given {@code column}. - * - * @param aValue - * the value to set - * @param column - * the column to set the value on - */ - void setValueAt(Object aValue, int column); - - /** - * Returns this node's user object. - * - * @return the Object stored at this node by the user - */ - Object getUserObject(); - - /** - * Sets the user object stored in this node. - * - * @param userObject - * the object to store - */ - void setUserObject(Object userObject); -} diff --git a/src/main/java/org/jdesktop/swingx/treetable/package-info.java b/src/main/java/org/jdesktop/swingx/treetable/package-info.java deleted file mode 100644 index 5b2dcd423a..0000000000 --- a/src/main/java/org/jdesktop/swingx/treetable/package-info.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * $Id: package-info.java 3167 2009-01-02 13:28:04Z rah003 $ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -/**Contains API required by the JXTreeTable component. -

                              Package Specification

                              - - - -

                              Related Documentation

                              - - - -*/ -package org.jdesktop.swingx.treetable; \ No newline at end of file diff --git a/src/main/java/org/jdesktop/swingx/util/Contract.java b/src/main/java/org/jdesktop/swingx/util/Contract.java deleted file mode 100644 index d56e7a1b42..0000000000 --- a/src/main/java/org/jdesktop/swingx/util/Contract.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * $Id: Contract.java 4028 2011-06-03 19:32:19Z kschaefe $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.util; - - -/** - * Utility class for checking contracts. - * - * @author Jeanette Winzenburg - */ -public class Contract { - - private Contract() { - - } - - /** - * Tests the input parameter against null. If the input is - * an array, checks all of its elements as well. Returns the - * unchanged parameter if not null, throws a NullPointerException - * otherwise.

                              - * - * PENDING: type of exception? there are raging debates, some - * favour an IllegalArgument?

                              - * - * PENDING: the implementation uses a unchecked type cast to an array. - * can we do better, how? - * - * - * @param the type of the input parameter - * @param input the argument to check against null. - * @param message the text of the exception if the argument is null - * @return the input if not null - * @throws NullPointerException if input is null - */ - @SuppressWarnings("unchecked") - public static T asNotNull(T input, String message) { - if (input == null) - throw new NullPointerException(message); - - if (input.getClass().isArray()) { - if (!input.getClass().getComponentType().isPrimitive()) { - T[] array = (T[]) input; - for (int i = 0; i < array.length; i++) { - asNotNull(array[i], message); - } - } - } - - return input; - } -} diff --git a/src/main/java/org/jdesktop/swingx/util/GraphicsUtilities.java b/src/main/java/org/jdesktop/swingx/util/GraphicsUtilities.java deleted file mode 100644 index e068eb3f78..0000000000 --- a/src/main/java/org/jdesktop/swingx/util/GraphicsUtilities.java +++ /dev/null @@ -1,834 +0,0 @@ -/* - * $Id: GraphicsUtilities.java 4193 2012-06-27 19:42:05Z kschaefe $ - * - * Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy). - * - * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * Copyright (c) 2006 Romain Guy - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.jdesktop.swingx.util; - -import javax.imageio.ImageIO; -import javax.swing.*; -import java.awt.*; -import java.awt.image.BufferedImage; -import java.awt.image.ColorModel; -import java.awt.image.Raster; -import java.awt.image.WritableRaster; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; - -/** - *

                              GraphicsUtilities contains a set of tools to perform - * common graphics operations easily. These operations are divided into - * several themes, listed below.

                              - * - *

                              Compatible Images

                              - * - *

                              Compatible images can, and should, be used to increase drawing - * performance. This class provides a number of methods to load compatible - * images directly from files or to convert existing images to compatibles - * images.

                              - * - *

                              Creating Thumbnails

                              - * - *

                              This class provides a number of methods to easily scale down images. - * Some of these methods offer a trade-off between speed and result quality and - * should be used all the time. They also offer the advantage of producing - * compatible images, thus automatically resulting into better runtime - * performance.

                              - * - *

                              All these methods are both faster than - * {@link Image#getScaledInstance(int, int, int)} and produce - * better-looking results than the various drawImage() methods - * in {@link Graphics}, which can be used for image scaling.

                              - * - *

                              Image Manipulation

                              - * - *

                              This class provides two methods to get and set pixels in a buffered image. - * These methods try to avoid unmanaging the image in order to keep good - * performance.

                              - * - * @author Romain Guy - * @author rbair - * @author Karl Schaefer - */ -@SuppressWarnings("nls") -public class GraphicsUtilities { - private GraphicsUtilities() { - } - - // Returns the graphics configuration for the primary screen - private static GraphicsConfiguration getGraphicsConfiguration() { - return GraphicsEnvironment.getLocalGraphicsEnvironment(). - getDefaultScreenDevice().getDefaultConfiguration(); - } - - private static boolean isHeadless() { - return GraphicsEnvironment.isHeadless(); - } - - /** - * Converts the specified image into a compatible buffered image. - * - * @param img - * the image to convert - * @return a compatible buffered image of the input - */ - public static BufferedImage convertToBufferedImage(Image img) { - BufferedImage buff = createCompatibleTranslucentImage( - img.getWidth(null), img.getHeight(null)); - Graphics2D g2 = buff.createGraphics(); - - try { - g2.drawImage(img, 0, 0, null); - } finally { - g2.dispose(); - } - - return buff; - } - - /** - *

                              Returns a new BufferedImage using the same color model - * as the image passed as a parameter. The returned image is only compatible - * with the image passed as a parameter. This does not mean the returned - * image is compatible with the hardware.

                              - * - * @param image the reference image from which the color model of the new - * image is obtained - * @return a new BufferedImage, compatible with the color model - * of image - */ - public static BufferedImage createColorModelCompatibleImage(BufferedImage image) { - ColorModel cm = image.getColorModel(); - return new BufferedImage(cm, - cm.createCompatibleWritableRaster(image.getWidth(), - image.getHeight()), - cm.isAlphaPremultiplied(), null); - } - - /** - *

                              Returns a new compatible image with the same width, height and - * transparency as the image specified as a parameter. That is, the - * returned BufferedImage will be compatible with the graphics hardware. - * If this method is called in a headless environment, then - * the returned BufferedImage will be compatible with the source - * image.

                              - * - * @see Transparency - * @see #createCompatibleImage(int, int) - * @see #createCompatibleImage(BufferedImage, int, int) - * @see #createCompatibleTranslucentImage(int, int) - * @see #loadCompatibleImage(URL) - * @see #toCompatibleImage(BufferedImage) - * @param image the reference image from which the dimension and the - * transparency of the new image are obtained - * @return a new compatible BufferedImage with the same - * dimension and transparency as image - */ - public static BufferedImage createCompatibleImage(BufferedImage image) { - return createCompatibleImage(image, image.getWidth(), image.getHeight()); - } - - /** - *

                              Returns a new compatible image of the specified width and height, and - * the same transparency setting as the image specified as a parameter. - * That is, the returned BufferedImage is compatible with - * the graphics hardware. If the method is called in a headless - * environment, then the returned BufferedImage will be compatible with - * the source image.

                              - * - * @see Transparency - * @see #createCompatibleImage(BufferedImage) - * @see #createCompatibleImage(int, int) - * @see #createCompatibleTranslucentImage(int, int) - * @see #loadCompatibleImage(URL) - * @see #toCompatibleImage(BufferedImage) - * @param width the width of the new image - * @param height the height of the new image - * @param image the reference image from which the transparency of the new - * image is obtained - * @return a new compatible BufferedImage with the same - * transparency as image and the specified dimension - */ - public static BufferedImage createCompatibleImage(BufferedImage image, - int width, int height) { - return isHeadless() ? - new BufferedImage(width, height, image.getType()) : - getGraphicsConfiguration().createCompatibleImage(width, height, - image.getTransparency()); - } - - /** - *

                              Returns a new opaque compatible image of the specified width and - * height. That is, the returned BufferedImage is compatible with - * the graphics hardware. If the method is called in a headless - * environment, then the returned BufferedImage will be compatible with - * the source image.

                              - * - * @see #createCompatibleImage(BufferedImage) - * @see #createCompatibleImage(BufferedImage, int, int) - * @see #createCompatibleTranslucentImage(int, int) - * @see #loadCompatibleImage(URL) - * @see #toCompatibleImage(BufferedImage) - * @param width the width of the new image - * @param height the height of the new image - * @return a new opaque compatible BufferedImage of the - * specified width and height - */ - public static BufferedImage createCompatibleImage(int width, int height) { - return isHeadless() ? - new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB) : - getGraphicsConfiguration().createCompatibleImage(width, height); - } - - /** - *

                              Returns a new translucent compatible image of the specified width and - * height. That is, the returned BufferedImage is compatible with - * the graphics hardware. If the method is called in a headless - * environment, then the returned BufferedImage will be compatible with - * the source image.

                              - * - * @see #createCompatibleImage(BufferedImage) - * @see #createCompatibleImage(BufferedImage, int, int) - * @see #createCompatibleImage(int, int) - * @see #loadCompatibleImage(URL) - * @see #toCompatibleImage(BufferedImage) - * @param width the width of the new image - * @param height the height of the new image - * @return a new translucent compatible BufferedImage of the - * specified width and height - */ - public static BufferedImage createCompatibleTranslucentImage(int width, - int height) { - return isHeadless() ? - new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB) : - getGraphicsConfiguration().createCompatibleImage(width, height, - Transparency.TRANSLUCENT); - } - - /** - *

                              - * Returns a new compatible image from a stream. The image is loaded from - * the specified stream and then turned, if necessary into a compatible - * image. - *

                              - * - * @see #createCompatibleImage(BufferedImage) - * @see #createCompatibleImage(BufferedImage, int, int) - * @see #createCompatibleImage(int, int) - * @see #createCompatibleTranslucentImage(int, int) - * @see #toCompatibleImage(BufferedImage) - * @param in - * the stream of the picture to load as a compatible image - * @return a new translucent compatible BufferedImage of the - * specified width and height - * @throws IOException - * if the image cannot be read or loaded - */ - public static BufferedImage loadCompatibleImage(InputStream in) throws IOException { - BufferedImage image = ImageIO.read(in); - if(image == null) return null; - return toCompatibleImage(image); - } - - /** - *

                              Returns a new compatible image from a URL. The image is loaded from the - * specified location and then turned, if necessary into a compatible - * image.

                              - * - * @see #createCompatibleImage(BufferedImage) - * @see #createCompatibleImage(BufferedImage, int, int) - * @see #createCompatibleImage(int, int) - * @see #createCompatibleTranslucentImage(int, int) - * @see #toCompatibleImage(BufferedImage) - * @param resource the URL of the picture to load as a compatible image - * @return a new translucent compatible BufferedImage of the - * specified width and height - * @throws IOException if the image cannot be read or loaded - */ - public static BufferedImage loadCompatibleImage(URL resource) - throws IOException { - BufferedImage image = ImageIO.read(resource); - return toCompatibleImage(image); - } - - /** - *

                              Return a new compatible image that contains a copy of the specified - * image. This method ensures an image is compatible with the hardware, - * and therefore optimized for fast blitting operations.

                              - * - *

                              If the method is called in a headless environment, then the returned - * BufferedImage will be the source image.

                              - * - * @see #createCompatibleImage(BufferedImage) - * @see #createCompatibleImage(BufferedImage, int, int) - * @see #createCompatibleImage(int, int) - * @see #createCompatibleTranslucentImage(int, int) - * @see #loadCompatibleImage(URL) - * @param image the image to copy into a new compatible image - * @return a new compatible copy, with the - * same width and height and transparency and content, of image - */ - public static BufferedImage toCompatibleImage(BufferedImage image) { - if (isHeadless()) { - return image; - } - - if (image.getColorModel().equals( - getGraphicsConfiguration().getColorModel())) { - return image; - } - - BufferedImage compatibleImage = - getGraphicsConfiguration().createCompatibleImage( - image.getWidth(), image.getHeight(), - image.getTransparency()); - Graphics g = compatibleImage.getGraphics(); - - try { - g.drawImage(image, 0, 0, null); - } finally { - g.dispose(); - } - - return compatibleImage; - } - - /** - *

                              Returns a thumbnail of a source image. newSize defines - * the length of the longest dimension of the thumbnail. The other - * dimension is then computed according to the dimensions ratio of the - * original picture.

                              - *

                              This method favors speed over quality. When the new size is less than - * half the longest dimension of the source image, - * {@link #createThumbnail(BufferedImage, int)} or - * {@link #createThumbnail(BufferedImage, int, int)} should be used instead - * to ensure the quality of the result without sacrificing too much - * performance.

                              - * - * @see #createThumbnailFast(BufferedImage, int, int) - * @see #createThumbnail(BufferedImage, int) - * @see #createThumbnail(BufferedImage, int, int) - * @param image the source image - * @param newSize the length of the largest dimension of the thumbnail - * @return a new compatible BufferedImage containing a - * thumbnail of image - * @throws IllegalArgumentException if newSize is larger than - * the largest dimension of image or <= 0 - */ - public static BufferedImage createThumbnailFast(BufferedImage image, - int newSize) { - float ratio; - int width = image.getWidth(); - int height = image.getHeight(); - - if (width > height) { - if (newSize >= width) { - throw new IllegalArgumentException("newSize must be lower than" + - " the image width"); - } else if (newSize <= 0) { - throw new IllegalArgumentException("newSize must" + - " be greater than 0"); - } - - ratio = (float) width / (float) height; - width = newSize; - height = (int) (newSize / ratio); - } else { - if (newSize >= height) { - throw new IllegalArgumentException("newSize must be lower than" + - " the image height"); - } else if (newSize <= 0) { - throw new IllegalArgumentException("newSize must" + - " be greater than 0"); - } - - ratio = (float) height / (float) width; - height = newSize; - width = (int) (newSize / ratio); - } - - BufferedImage temp = createCompatibleImage(image, width, height); - Graphics2D g2 = temp.createGraphics(); - - try { - g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, - RenderingHints.VALUE_INTERPOLATION_BILINEAR); - g2.drawImage(image, 0, 0, temp.getWidth(), temp.getHeight(), null); - } finally { - g2.dispose(); - } - - return temp; - } - - /** - *

                              Returns a thumbnail of a source image.

                              - *

                              This method favors speed over quality. When the new size is less than - * half the longest dimension of the source image, - * {@link #createThumbnail(BufferedImage, int)} or - * {@link #createThumbnail(BufferedImage, int, int)} should be used instead - * to ensure the quality of the result without sacrificing too much - * performance.

                              - * - * @see #createThumbnailFast(BufferedImage, int) - * @see #createThumbnail(BufferedImage, int) - * @see #createThumbnail(BufferedImage, int, int) - * @param image the source image - * @param newWidth the width of the thumbnail - * @param newHeight the height of the thumbnail - * @return a new compatible BufferedImage containing a - * thumbnail of image - * @throws IllegalArgumentException if newWidth is larger than - * the width of image or if code>newHeight is larger - * than the height of image or if one of the dimensions - * is <= 0 - */ - public static BufferedImage createThumbnailFast(BufferedImage image, - int newWidth, int newHeight) { - if (newWidth >= image.getWidth() || - newHeight >= image.getHeight()) { - throw new IllegalArgumentException("newWidth and newHeight cannot" + - " be greater than the image" + - " dimensions"); - } else if (newWidth <= 0 || newHeight <= 0) { - throw new IllegalArgumentException("newWidth and newHeight must" + - " be greater than 0"); - } - - BufferedImage temp = createCompatibleImage(image, newWidth, newHeight); - Graphics2D g2 = temp.createGraphics(); - - try { - g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, - RenderingHints.VALUE_INTERPOLATION_BILINEAR); - g2.drawImage(image, 0, 0, temp.getWidth(), temp.getHeight(), null); - } finally { - g2.dispose(); - } - - return temp; - } - - /** - *

                              Returns a thumbnail of a source image. newSize defines - * the length of the longest dimension of the thumbnail. The other - * dimension is then computed according to the dimensions ratio of the - * original picture.

                              - *

                              This method offers a good trade-off between speed and quality. - * The result looks better than - * {@link #createThumbnailFast(BufferedImage, int)} when - * the new size is less than half the longest dimension of the source - * image, yet the rendering speed is almost similar.

                              - * - * @see #createThumbnailFast(BufferedImage, int, int) - * @see #createThumbnailFast(BufferedImage, int) - * @see #createThumbnail(BufferedImage, int, int) - * @param image the source image - * @param newSize the length of the largest dimension of the thumbnail - * @return a new compatible BufferedImage containing a - * thumbnail of image - * @throws IllegalArgumentException if newSize is larger than - * the largest dimension of image or <= 0 - */ - public static BufferedImage createThumbnail(BufferedImage image, - int newSize) { - int width = image.getWidth(); - int height = image.getHeight(); - - boolean isTranslucent = image.getTransparency() != Transparency.OPAQUE; - boolean isWidthGreater = width > height; - - if (isWidthGreater) { - if (newSize >= width) { - throw new IllegalArgumentException("newSize must be lower than" + - " the image width"); - } - } else if (newSize >= height) { - throw new IllegalArgumentException("newSize must be lower than" + - " the image height"); - } - - if (newSize <= 0) { - throw new IllegalArgumentException("newSize must" + - " be greater than 0"); - } - - float ratioWH = (float) width / (float) height; - float ratioHW = (float) height / (float) width; - - BufferedImage thumb = image; - BufferedImage temp = null; - - Graphics2D g2 = null; - - try { - int previousWidth = width; - int previousHeight = height; - - do { - if (isWidthGreater) { - width /= 2; - if (width < newSize) { - width = newSize; - } - height = (int) (width / ratioWH); - } else { - height /= 2; - if (height < newSize) { - height = newSize; - } - width = (int) (height / ratioHW); - } - - if (temp == null || isTranslucent) { - if (g2 != null) { - //do not need to wrap with finally - //outer finally block will ensure - //that resources are properly reclaimed - g2.dispose(); - } - temp = createCompatibleImage(image, width, height); - g2 = temp.createGraphics(); - g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, - RenderingHints.VALUE_INTERPOLATION_BILINEAR); - } - g2.drawImage(thumb, 0, 0, width, height, - 0, 0, previousWidth, previousHeight, null); - - previousWidth = width; - previousHeight = height; - - thumb = temp; - } while (newSize != (isWidthGreater ? width : height)); - } finally { - if (g2 != null) { - g2.dispose(); - } - } - - if (width != thumb.getWidth() || height != thumb.getHeight()) { - temp = createCompatibleImage(image, width, height); - g2 = temp.createGraphics(); - - try { - g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, - RenderingHints.VALUE_INTERPOLATION_BILINEAR); - g2.drawImage(thumb, 0, 0, width, height, 0, 0, width, height, null); - } finally { - g2.dispose(); - } - - thumb = temp; - } - - return thumb; - } - - /** - *

                              Returns a thumbnail of a source image.

                              - *

                              This method offers a good trade-off between speed and quality. - * The result looks better than - * {@link #createThumbnailFast(BufferedImage, int)} when - * the new size is less than half the longest dimension of the source - * image, yet the rendering speed is almost similar.

                              - * - * @see #createThumbnailFast(BufferedImage, int) - * @see #createThumbnailFast(BufferedImage, int, int) - * @see #createThumbnail(BufferedImage, int) - * @param image the source image - * @param newWidth the width of the thumbnail - * @param newHeight the height of the thumbnail - * @return a new compatible BufferedImage containing a - * thumbnail of image - * @throws IllegalArgumentException if newWidth is larger than - * the width of image or if code>newHeight is larger - * than the height of image or if one the dimensions is not > 0 - */ - public static BufferedImage createThumbnail(BufferedImage image, - int newWidth, int newHeight) { - int width = image.getWidth(); - int height = image.getHeight(); - - boolean isTranslucent = image.getTransparency() != Transparency.OPAQUE; - - if (newWidth >= width || newHeight >= height) { - throw new IllegalArgumentException("newWidth and newHeight cannot" + - " be greater than the image" + - " dimensions"); - } else if (newWidth <= 0 || newHeight <= 0) { - throw new IllegalArgumentException("newWidth and newHeight must" + - " be greater than 0"); - } - - BufferedImage thumb = image; - BufferedImage temp = null; - - Graphics2D g2 = null; - - try { - int previousWidth = width; - int previousHeight = height; - - do { - if (width > newWidth) { - width /= 2; - if (width < newWidth) { - width = newWidth; - } - } - - if (height > newHeight) { - height /= 2; - if (height < newHeight) { - height = newHeight; - } - } - - if (temp == null || isTranslucent) { - if (g2 != null) { - //do not need to wrap with finally - //outer finally block will ensure - //that resources are properly reclaimed - g2.dispose(); - } - temp = createCompatibleImage(image, width, height); - g2 = temp.createGraphics(); - g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, - RenderingHints.VALUE_INTERPOLATION_BILINEAR); - } - g2.drawImage(thumb, 0, 0, width, height, - 0, 0, previousWidth, previousHeight, null); - - previousWidth = width; - previousHeight = height; - - thumb = temp; - } while (width != newWidth || height != newHeight); - } finally { - if (g2 != null) { - g2.dispose(); - } - } - - if (width != thumb.getWidth() || height != thumb.getHeight()) { - temp = createCompatibleImage(image, width, height); - g2 = temp.createGraphics(); - - try { - g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, - RenderingHints.VALUE_INTERPOLATION_BILINEAR); - g2.drawImage(thumb, 0, 0, width, height, 0, 0, width, height, null); - } finally { - g2.dispose(); - } - - thumb = temp; - } - - return thumb; - } - - /** - *

                              Returns an array of pixels, stored as integers, from a - * BufferedImage. The pixels are grabbed from a rectangular - * area defined by a location and two dimensions. Calling this method on - * an image of type different from BufferedImage.TYPE_INT_ARGB - * and BufferedImage.TYPE_INT_RGB will unmanage the image.

                              - * - * @param img the source image - * @param x the x location at which to start grabbing pixels - * @param y the y location at which to start grabbing pixels - * @param w the width of the rectangle of pixels to grab - * @param h the height of the rectangle of pixels to grab - * @param pixels a pre-allocated array of pixels of size w*h; can be null - * @return pixels if non-null, a new array of integers - * otherwise - * @throws IllegalArgumentException is pixels is non-null and - * of length < w*h - */ - public static int[] getPixels(BufferedImage img, - int x, int y, int w, int h, int[] pixels) { - if (w == 0 || h == 0) { - return new int[0]; - } - - if (pixels == null) { - pixels = new int[w * h]; - } else if (pixels.length < w * h) { - throw new IllegalArgumentException("pixels array must have a length" + - " >= w*h"); - } - - int imageType = img.getType(); - if (imageType == BufferedImage.TYPE_INT_ARGB || - imageType == BufferedImage.TYPE_INT_RGB) { - Raster raster = img.getRaster(); - return (int[]) raster.getDataElements(x, y, w, h, pixels); - } - - // Unmanages the image - return img.getRGB(x, y, w, h, pixels, 0, w); - } - - /** - *

                              Writes a rectangular area of pixels in the destination - * BufferedImage. Calling this method on - * an image of type different from BufferedImage.TYPE_INT_ARGB - * and BufferedImage.TYPE_INT_RGB will unmanage the image.

                              - * - * @param img the destination image - * @param x the x location at which to start storing pixels - * @param y the y location at which to start storing pixels - * @param w the width of the rectangle of pixels to store - * @param h the height of the rectangle of pixels to store - * @param pixels an array of pixels, stored as integers - * @throws IllegalArgumentException is pixels is non-null and - * of length < w*h - */ - public static void setPixels(BufferedImage img, - int x, int y, int w, int h, int[] pixels) { - if (pixels == null || w == 0 || h == 0) { - return; - } else if (pixels.length < w * h) { - throw new IllegalArgumentException("pixels array must have a length" + - " >= w*h"); - } - - int imageType = img.getType(); - if (imageType == BufferedImage.TYPE_INT_ARGB || - imageType == BufferedImage.TYPE_INT_RGB) { - WritableRaster raster = img.getRaster(); - raster.setDataElements(x, y, w, h, pixels); - } else { - // Unmanages the image - img.setRGB(x, y, w, h, pixels, 0, w); - } - } - - /** - * Clears the data from the image. - * - * @param img - * the image to erase - */ - public static void clear(Image img) { - Graphics g = img.getGraphics(); - - try { - if (g instanceof Graphics2D) { - ((Graphics2D) g).setComposite(AlphaComposite.Clear); - } else { - g.setColor(new Color(0, 0, 0, 0)); - } - - g.fillRect(0, 0, img.getWidth(null), img.getHeight(null)); - } finally { - g.dispose(); - } - } - - /** - * Draws an image on top of a component by doing a 3x3 grid stretch of the image - * using the specified insets. - */ - public static void tileStretchPaint(Graphics g, - JComponent comp, - BufferedImage img, - Insets ins) { - - int left = ins.left; - int right = ins.right; - int top = ins.top; - int bottom = ins.bottom; - - // top - g.drawImage(img, - 0,0,left,top, - 0,0,left,top, - null); - g.drawImage(img, - left, 0, - comp.getWidth() - right, top, - left, 0, - img.getWidth() - right, top, - null); - g.drawImage(img, - comp.getWidth() - right, 0, - comp.getWidth(), top, - img.getWidth() - right, 0, - img.getWidth(), top, - null); - - // middle - g.drawImage(img, - 0, top, - left, comp.getHeight()-bottom, - 0, top, - left, img.getHeight()-bottom, - null); - - g.drawImage(img, - left, top, - comp.getWidth()-right, comp.getHeight()-bottom, - left, top, - img.getWidth()-right, img.getHeight()-bottom, - null); - - g.drawImage(img, - comp.getWidth()-right, top, - comp.getWidth(), comp.getHeight()-bottom, - img.getWidth()-right, top, - img.getWidth(), img.getHeight()-bottom, - null); - - // bottom - g.drawImage(img, - 0,comp.getHeight()-bottom, - left, comp.getHeight(), - 0,img.getHeight()-bottom, - left,img.getHeight(), - null); - g.drawImage(img, - left, comp.getHeight()-bottom, - comp.getWidth()-right, comp.getHeight(), - left, img.getHeight()-bottom, - img.getWidth()-right, img.getHeight(), - null); - g.drawImage(img, - comp.getWidth()-right, comp.getHeight()-bottom, - comp.getWidth(), comp.getHeight(), - img.getWidth()-right, img.getHeight()-bottom, - img.getWidth(), img.getHeight(), - null); - } -} diff --git a/src/main/java/org/jdesktop/swingx/util/OS.java b/src/main/java/org/jdesktop/swingx/util/OS.java deleted file mode 100644 index 1d21d57e8d..0000000000 --- a/src/main/java/org/jdesktop/swingx/util/OS.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * $Id: OS.java 4088 2011-11-17 19:53:49Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.util; - -import org.apache.commons.lang3.SystemUtils; - -import javax.swing.*; -import java.awt.*; - -/** - * Provides methods related to the runtime environment. - */ -@SuppressWarnings("nls") -public class OS { - - /** - * @return true if the VM is running Windows and the Java - * application is rendered using XP Visual Styles. - */ - public static boolean isUsingWindowsVisualStyles() { - if (!SystemUtils.IS_OS_WINDOWS) { - return false; - } - - boolean xpthemeActive = Boolean.TRUE.equals(Toolkit.getDefaultToolkit() - .getDesktopProperty("win.xpstyle.themeActive")); - if (!xpthemeActive) { - return false; - } else { - try { - return System.getProperty("swing.noxp") == null; - } catch (RuntimeException e) { - return true; - } - } - } - - /** - * Returns the name of the current Windows visual style. - *
                                - *
                              • it looks for a property name "win.xpstyle.name" in UIManager and if not found - *
                              • it queries the win.xpstyle.colorName desktop property ({@link Toolkit#getDesktopProperty(String)}) - *
                              - * - * @return the name of the current Windows visual style if any. - */ - public static String getWindowsVisualStyle() { - String style = UIManager.getString("win.xpstyle.name"); - if (style == null) { - // guess the name of the current XPStyle - // (win.xpstyle.colorName property found in awt_DesktopProperties.cpp in - // JDK source) - style = (String)Toolkit.getDefaultToolkit().getDesktopProperty( - "win.xpstyle.colorName"); - } - return style; - } - -} diff --git a/src/main/java/org/jdesktop/swingx/util/PaintUtils.java b/src/main/java/org/jdesktop/swingx/util/PaintUtils.java deleted file mode 100644 index 8b2e9bbfcd..0000000000 --- a/src/main/java/org/jdesktop/swingx/util/PaintUtils.java +++ /dev/null @@ -1,538 +0,0 @@ -/* - * $Id: PaintUtils.java 4193 2012-06-27 19:42:05Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.util; - -import java.awt.*; -import java.awt.geom.Point2D; -import java.awt.image.BufferedImage; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -/** - * A collection of utilities for working with Paints and Colors. - * - * @author Mark Davidson - * @author joshua.marinacci@sun.com - * @author Karl George Schaefer - */ -@SuppressWarnings("nls") -public class PaintUtils { - public static final GradientPaint BLUE_EXPERIENCE = new GradientPaint( - new Point2D.Double(0, 0), - new Color(168, 204, 241), - new Point2D.Double(0, 1), - new Color(44, 61, 146)); - public static final GradientPaint MAC_OSX_SELECTED = new GradientPaint( - new Point2D.Double(0, 0), - new Color(81, 141, 236), - new Point2D.Double(0, 1), - new Color(36, 96, 192)); - public static final GradientPaint MAC_OSX = new GradientPaint( - new Point2D.Double(0, 0), - new Color(167, 210, 250), - new Point2D.Double(0, 1), - new Color(99, 147, 206)); - public static final GradientPaint AERITH = new GradientPaint( - new Point2D.Double(0, 0), - Color.WHITE, - new Point2D.Double(0, 1), - new Color(64, 110, 161)); - public static final GradientPaint GRAY = new GradientPaint( - new Point2D.Double(0, 0), - new Color(226, 226, 226), - new Point2D.Double(0, 1), - new Color(250, 248, 248)); - public static final GradientPaint RED_XP = new GradientPaint( - new Point2D.Double(0, 0), - new Color(236, 81, 81), - new Point2D.Double(0, 1), - new Color(192, 36, 36)); - public static final GradientPaint NIGHT_GRAY = new GradientPaint( - new Point2D.Double(0, 0), - new Color(102, 111, 127), - new Point2D.Double(0, 1), - new Color(38, 45, 61)); - public static final GradientPaint NIGHT_GRAY_LIGHT = new GradientPaint( - new Point2D.Double(0, 0), - new Color(129, 138, 155), - new Point2D.Double(0, 1), - new Color(58, 66, 82)); - - - //originally included in LinearGradientPainter - public static final Paint ORANGE_DELIGHT = new LinearGradientPaint( - new Point2D.Double(0, 0), - new Point2D.Double(1, 0), - new float[] {0f, .5f, .51f, 1f}, - new Color[] { - new Color(248, 192, 75), - new Color(253, 152, 6), - new Color(243, 133, 0), - new Color(254, 124, 0)}); - - //originally included in LinearGradientPainter - public static final Paint BLACK_STAR = new LinearGradientPaint( - new Point2D.Double(0, 0), - new Point2D.Double(1, 0), - new float[] {0f, .5f, .51f, 1f}, - new Color[] { - new Color(54, 62, 78), - new Color(32, 39, 55), - new Color(74, 82, 96), - new Color(123, 132, 145)}); - - private PaintUtils() { - } - - /** Resizes a gradient to fill the width and height available. If the - * gradient is left to right it will be resized to fill the entire width. - * If the gradient is top to bottom it will be resized to fill the entire - * height. If the gradient is on an angle it will be resized to go from - * one corner to the other of the rectangle formed by (0,0 -> width,height). - * - * This method can resize java.awt.GradientPaint, java.awt.LinearGradientPaint, - * and the LinearGradientPaint implementation from Apache's Batik project. Note, - * this method does not require the MultipleGradientPaint.jar from Apache to - * compile or to run. MultipleGradientPaint.jar *is* required if you want - * to resize the LinearGradientPaint from that jar. - * - * Any paint passed into this method which is not a kind of gradient paint (like - * a Color or TexturePaint) will be returned unmodified. It will not throw - * an exception. If the gradient cannot be resized due to other errors the - * original paint will be returned unmodified. It will not throw an - * exception. - * - */ - public static Paint resizeGradient(Paint p, int width, int height) { - if(p == null) return p; - - if(p instanceof GradientPaint) { - GradientPaint gp = (GradientPaint)p; - Point2D[] pts = new Point2D[2]; - pts[0] = gp.getPoint1(); - pts[1] = gp.getPoint2(); - pts = adjustPoints(pts, width, height); - return new GradientPaint(pts[0], gp.getColor1(), pts[1], gp.getColor2(), gp.isCyclic()); - } - - if("java.awt.LinearGradientPaint".equals(p.getClass().getName()) || - "org.apache.batik.ext.awt.LinearGradientPaint".equals(p.getClass().getName())) { - return resizeLinearGradient(p,width,height); - } - return p; - } - - - private static Paint resizeLinearGradient(Paint p, int width, int height) { - try { - Point2D[] pts = new Point2D[2]; - pts[0] = (Point2D) invokeMethod(p,"getStartPoint"); - pts[1] = (Point2D) invokeMethod(p,"getEndPoint"); - pts = adjustPoints(pts, width, height); - float[] fractions = (float[]) invokeMethod(p,"getFractions"); - Color[] colors = (Color[]) invokeMethod(p,"getColors"); - - Constructor con = p.getClass().getDeclaredConstructor( - Point2D.class, Point2D.class, - new float[0].getClass(), - new Color[0].getClass()); - return (Paint) con.newInstance(pts[0],pts[1],fractions, colors); - } catch (Exception ex) { - ex.printStackTrace(); - } - return p; - } - - private static Object invokeMethod(final Object p, final String methodName) - throws NoSuchMethodException, InvocationTargetException, IllegalArgumentException, SecurityException, IllegalAccessException { - Method meth = p.getClass().getMethod(methodName); - return meth.invoke(p); - } - - - private static Point2D[] adjustPoints(Point2D[] pts, int width, int height) { - Point2D start = pts[0]; - Point2D end = pts[1]; - - double angle = calcAngle(start,end); - double a2 = Math.toDegrees(angle); - double e = 1; - - // if it is near 0 degrees - if(Math.abs(angle) < Math.toRadians(e) || - Math.abs(angle) > Math.toRadians(360 - e)) { - start = new Point2D.Float(0, 0); - end = new Point2D.Float(normalize(end.getX(), width), 0); - } - - // near 45 - if (isNear(a2, 45, e)) { - start = new Point2D.Float(0, 0); - end = new Point2D.Float(normalize(end.getX(), width), normalize(end.getY(), height)); - } - - // near 90 - if (isNear(a2, 90, e)) { - start = new Point2D.Float(0, 0); - end = new Point2D.Float(0, normalize(end.getY(), height)); - } - - // near 135 - if (isNear(a2, 135, e)) { - start = new Point2D.Float(normalize(start.getX(), width), 0); - end = new Point2D.Float(0, normalize(end.getY(), height)); - } - - // near 180 - if (isNear(a2, 180, e)) { - start = new Point2D.Float(normalize(start.getX(), width), 0); - end = new Point2D.Float(0, 0); - } - - // near 225 - if (isNear(a2, 225, e)) { - start = new Point2D.Float(normalize(start.getX(), width), normalize(start.getY(), height)); - end = new Point2D.Float(0, 0); - } - - // near 270 - if (isNear(a2, 270, e)) { - start = new Point2D.Float(0, normalize(start.getY(), height)); - end = new Point2D.Float(0, 0); - } - - // near 315 - if (isNear(a2, 315, e)) { - start = new Point2D.Float(0, normalize(start.getY(), height)); - end = new Point2D.Float(normalize(end.getX(), width), 0); - } - - return new Point2D[] { start, end }; - } - - private static boolean isNear(double angle, double target, double error) { - return Math.abs(target - Math.abs(angle)) < error; - } - - private static float normalize(double original, float target) { - if (original < 1f) { - return target * (float) original; - } - - return target; - } - - private static double calcAngle(Point2D p1, Point2D p2) { - double x_off = p2.getX() - p1.getX(); - double y_off = p2.getY() - p1.getY(); - double angle = Math.atan(y_off / x_off); - if (x_off < 0) { - angle = angle + Math.PI; - } - - if(angle < 0) { angle+= 2*Math.PI; } - if(angle > 2*Math.PI) { angle -= 2*Math.PI; } - return angle; - } -/* - public static void main(String ... args) { - LinearGradientPaint in = new LinearGradientPaint( - new Point(0,0), new Point(10,0), - new float[] {0f, 0.5f, 1f}, - new Color[] {Color.RED, Color.GREEN, Color.BLUE}); - log.fine("in = " + toString(in)); - Paint out = resizeGradient(in,100,100); - log.fine(("out = " + toString((MultipleGradientPaint) out)); - }*/ - /* - private static String toString(MultipleGradientPaint paint) { - StringBuffer buffer = new StringBuffer(); - buffer.append(paint.getClass().getName()); - Color[] colors = paint.getColors(); - float[] values = paint.getFractions(); - buffer.append("["); - for(int i=0; i"); - buffer.append(""+lgp.getEndPoint().getX() + ", " + lgp.getEndPoint().getY()); - } - - return buffer.toString(); - }*/ - - /** - * Creates a new {@code Paint} that is a checkered effect using the colors {@link Color#GRAY - * gray} and {@link Color#WHITE}. - * - * @return a the checkered paint - */ - public static Paint getCheckerPaint() { - return getCheckerPaint(Color.WHITE, Color.GRAY, 20); - } - - /** - * Creates a new {@code Paint} that is a checkered effect using the specified colors. - *

                              - * While this method supports transparent colors, this implementation performs painting - * operations using the second color after it performs operations using the first color. This - * means that to create a checkered paint with a fully-transparent color, you MUST specify that - * color first. - * - * @param c1 - * the first color - * @param c2 - * the second color - * @param size - * the size of the paint - * @return a new {@code Paint} checkering the supplied colors - */ - public static Paint getCheckerPaint(Paint c1, Paint c2, int size) { - BufferedImage img = GraphicsUtilities.createCompatibleTranslucentImage(size, size); - Graphics2D g = img.createGraphics(); - - try { - g.setPaint(c1); - g.fillRect(0, 0, size, size); - g.setPaint(c2); - g.fillRect(0, 0, size / 2, size / 2); - g.fillRect(size / 2, size / 2, size / 2, size / 2); - } finally { - g.dispose(); - } - - return new TexturePaint(img,new Rectangle(0,0,size,size)); - } - - /** - * Creates a {@code String} that represents the supplied color as a - * hex-value RGB triplet, including the "#". The return value is suitable - * for use in HTML. The alpha (transparency) channel is neither include nor - * used in producing the string. - * - * @param color - * the color to convert - * @return the hex {@code String} - */ - public static String toHexString(Color color) { - return "#" + Integer.toHexString(color.getRGB() | 0xFF000000).substring(2); - } - - /** - * Returns a new color equal to the old one, except that there is no alpha - * (transparency) channel. - *

                              - * This method is a convenience and has the same effect as {@code - * setAlpha(color, 255)}. - * - * @param color - * the color to remove the alpha (transparency) from - * @return a new non-transparent {@code Color} - * @throws NullPointerException - * if {@code color} is {@code null} - */ - public static Color removeAlpha(Color color) { - return setAlpha(color, 255); - } - - /** - * Returns a new color equal to the old one, except alpha (transparency) - * channel is set to the new value. - * - * @param color - * the color to modify - * @param alpha - * the new alpha (transparency) level. Must be an int between 0 - * and 255 - * @return a new alpha-applied {@code Color} - * @throws IllegalArgumentException - * if {@code alpha} is not between 0 and 255 inclusive - * @throws NullPointerException - * if {@code color} is {@code null} - */ - public static Color setAlpha(Color color, int alpha) { - if (alpha < 0 || alpha > 255) { - throw new IllegalArgumentException("invalid alpha value"); - } - - return new Color( - color.getRed(), color.getGreen(), color.getBlue(), alpha); - } - - /** - * Returns a new color equal to the old one, except the saturation is set to - * the new value. The new color will have the same alpha (transparency) as - * the original color. - *

                              - * The color is modified using HSB calculations. The saturation must be a - * float between 0 and 1. If 0 the resulting color will be gray. If 1 the - * resulting color will be the most saturated possible form of the passed in - * color. - * - * @param color - * the color to modify - * @param saturation - * the saturation to use in the new color - * @return a new saturation-applied {@code Color} - * @throws IllegalArgumentException - * if {@code saturation} is not between 0 and 1 inclusive - * @throws NullPointerException - * if {@code color} is {@code null} - */ - public static Color setSaturation(Color color, float saturation) { - if (saturation < 0f || saturation > 1f) { - throw new IllegalArgumentException("invalid saturation value"); - } - - int alpha = color.getAlpha(); - - float[] hsb = Color.RGBtoHSB( - color.getRed(), color.getGreen(), color.getBlue(), null); - Color c = Color.getHSBColor(hsb[0], saturation, hsb[2]); - - return setAlpha(c, alpha); - } - - /** - * Returns a new color equal to the old one, except the brightness is set to - * the new value. The new color will have the same alpha (transparency) as - * the original color. - *

                              - * The color is modified using HSB calculations. The brightness must be a - * float between 0 and 1. If 0 the resulting color will be black. If 1 the - * resulting color will be the brightest possible form of the passed in - * color. - * - * @param color - * the color to modify - * @param brightness - * the brightness to use in the new color - * @return a new brightness-applied {@code Color} - * @throws IllegalArgumentException - * if {@code brightness} is not between 0 and 1 inclusive - * @throws NullPointerException - * if {@code color} is {@code null} - */ - public static Color setBrightness(Color color, float brightness) { - if (brightness < 0f || brightness > 1f) { - throw new IllegalArgumentException("invalid brightness value"); - } - - int alpha = color.getAlpha(); - - float[] hsb = Color.RGBtoHSB( - color.getRed(), color.getGreen(), color.getBlue(), null); - Color c = Color.getHSBColor(hsb[0], hsb[1], brightness); - - return setAlpha(c, alpha); - } - - /** - * Blends two colors to create a new color. The {@code origin} color is the - * base for the new color and regardless of its alpha component, it is - * treated as fully opaque (alpha 255). - * - * @param origin - * the base of the new color - * @param over - * the alpha-enabled color to add to the {@code origin} color - * @return a new color comprised of the {@code origin} and {@code over} - * colors - */ - public static Color blend(Color origin, Color over) { - if (over == null) { - return origin; - } - - if (origin == null) { - return over; - } - - int a = over.getAlpha(); - - int rb = (((over.getRGB() & 0x00ff00ff) * (a + 1)) - + ((origin.getRGB() & 0x00ff00ff) * (0xff - a))) & 0xff00ff00; - int g = (((over.getRGB() & 0x0000ff00) * (a + 1)) - + ((origin.getRGB() & 0x0000ff00) * (0xff - a))) & 0x00ff0000; - - return new Color((over.getRGB() & 0xff000000) | ((rb | g) >> 8)); - } - - /** - * Interpolates a color. - * - * @param b - * the first color - * @param a - * the second color - * @param t - * the amount to interpolate - * @return a new color - */ - public static Color interpolate(Color b, Color a, float t) { - float[] acomp = a.getRGBComponents(null); - float[] bcomp = b.getRGBComponents(null); - float[] ccomp = new float[4]; - - // log.fine(("a comp "); - // for(float f : acomp) { - // log.fine((f); - // } - // for(float f : bcomp) { - // log.fine((f); - // } - for(int i=0; i<4; i++) { - ccomp[i] = acomp[i] + (bcomp[i]-acomp[i])*t; - } - // for(float f : ccomp) { - // log.fine((f); - // } - - return new Color(ccomp[0],ccomp[1],ccomp[2],ccomp[3]); - } - - /** - * Computes an appropriate foreground color (either white or black) for the - * given background color. - * - * @param bg - * the background color - * @return {@code Color.WHITE} or {@code Color.BLACK} - * @throws NullPointerException - * if {@code bg} is {@code null} - */ - public static Color computeForeground(Color bg) { - float[] rgb = bg.getRGBColorComponents(null); - float y = .3f * rgb[0] + .59f * rgb[1] + .11f * rgb[2]; - - return y > .5f ? Color.BLACK : Color.WHITE; - } -} diff --git a/src/main/java/org/jdesktop/swingx/util/Separator.java b/src/main/java/org/jdesktop/swingx/util/Separator.java deleted file mode 100644 index 5da93c99ef..0000000000 --- a/src/main/java/org/jdesktop/swingx/util/Separator.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.jdesktop.swingx.util; - -/** - * A simple separator for adding in between each element in a list. - *

                              - *

                              - * for (String s : strings) {
                              - *   stringBuilder.append(separator.get().append(s);
                              - * }
                              - * 
                              - * - * @author Karl Schaefer - * @author Bruce Chapman (original idea) - * - * @param - * the type of separator - */ -public class Separator { - private T next; - private T separator; - - /** - * Constructs a separator with the specified initial value and remaining separator. - * - * @param initial - * the value to use for the first call - * @param separator - * the value to use after the first call - */ - public Separator(T initial, T separator) { - this.next = initial; - this.separator = separator; - } - - /** - * Returns the current value of the separator. - * - * @return the separator value - */ - public T get() { - T result = next; - next = separator; - - return result; - } -} diff --git a/src/main/java/org/jdesktop/swingx/util/ShapeUtils.java b/src/main/java/org/jdesktop/swingx/util/ShapeUtils.java deleted file mode 100644 index 0070771a14..0000000000 --- a/src/main/java/org/jdesktop/swingx/util/ShapeUtils.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * $Id: ShapeUtils.java 4082 2011-11-15 18:39:43Z kschaefe $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.util; - -import java.awt.*; -import java.awt.font.GlyphVector; -import java.awt.geom.*; -import java.awt.image.BufferedImage; - -/** - * - * @author joshy - */ -public final class ShapeUtils { - - /** Creates a new instance of ShapeUtils */ - private ShapeUtils() { - } - - public static Shape generatePolygon(int sides, int outsideRadius, boolean normalize) { - return generatePolygon(sides, outsideRadius, 0, normalize); - } - - public static Shape generatePolygon(int sides, int outsideRadius, int insideRadius, - boolean normalize) { - Shape shape = generatePolygon(sides, outsideRadius, insideRadius); - if (normalize) { - Rectangle2D bounds = shape.getBounds2D(); - GeneralPath path = new GeneralPath(shape); - shape = path.createTransformedShape(AffineTransform.getTranslateInstance( - -bounds.getX(), -bounds.getY())); - } - return shape; - } - - public static Shape generatePolygon(int sides, int outsideRadius, int insideRadius) { - if (sides < 3) { - return new Ellipse2D.Float(0, 0, 10, 10); - } - - AffineTransform trans = new AffineTransform(); - Polygon poly = new Polygon(); - for (int i = 0; i < sides; i++) { - trans.rotate(Math.PI * 2 / sides / 2); - Point2D out = trans.transform(new Point2D.Float(0, outsideRadius), null); - poly.addPoint((int) out.getX(), (int) out.getY()); - trans.rotate(Math.PI * 2 / sides / 2); - if (insideRadius > 0) { - Point2D in = trans.transform(new Point2D.Float(0, insideRadius), null); - poly.addPoint((int) in.getX(), (int) in.getY()); - } - } - - return poly; - } - - public static Shape generateShapeFromText(Font font, char ch) { - return generateShapeFromText(font, String.valueOf(ch)); - } - - public static Shape generateShapeFromText(Font font, String string) { - BufferedImage img = GraphicsUtilities.createCompatibleTranslucentImage(1, 1); - Graphics2D g2 = img.createGraphics(); - - try { - GlyphVector vect = font.createGlyphVector(g2.getFontRenderContext(), string); - Shape shape = vect.getOutline(0f, (float) -vect.getVisualBounds().getY()); - - return shape; - } finally { - g2.dispose(); - } - } - - /** - * Sets the clip on a graphics object by merging a supplied clip with the existing one. The new - * clip will be an intersection of the old clip and the supplied clip. The old clip shape will - * be returned. This is useful for resetting the old clip after an operation is performed. - * - * @param g - * the graphics object to update - * @param clip - * a new clipping region to add to the graphics clip. - * @return the current clipping region of the supplied graphics object. This may return - * {@code null} if the current clip is {@code null}. - * @throws NullPointerException - * if any parameter is {@code null} - */ - public static Shape mergeClip(Graphics g, Shape clip) { - Shape oldClip = g.getClip(); - if (oldClip == null) { - g.setClip(clip); - return null; - } - Area area = new Area(oldClip); - area.intersect(new Area(clip));// new Rectangle(0,0,width,height))); - g.setClip(area); - return oldClip; - } -} diff --git a/src/main/java/org/jdesktop/swingx/util/Utilities.java b/src/main/java/org/jdesktop/swingx/util/Utilities.java deleted file mode 100644 index 917e26fa50..0000000000 --- a/src/main/java/org/jdesktop/swingx/util/Utilities.java +++ /dev/null @@ -1,964 +0,0 @@ -/* - * $Id: Utilities.java 4084 2011-11-15 18:53:49Z kschaefe $ - * - * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.jdesktop.swingx.util; - - -import javax.swing.*; -import java.awt.*; -import java.awt.event.KeyEvent; -import java.lang.ref.Reference; -import java.lang.ref.SoftReference; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.text.BreakIterator; -import java.util.*; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Contribution from NetBeans: Issue #319-swingx.

                              - * - * PENDING: need to reconcile with OS, JVM... added as-is - * because needed the shortcut handling to fix # - * - * @author apple - */ -public class Utilities { - private Utilities() { - } - - private static final int CTRL_WILDCARD_MASK = 32768; - private static final int ALT_WILDCARD_MASK = CTRL_WILDCARD_MASK * 2; - - /** Operating system is Windows NT. */ - public static final int OS_WINNT = 1; - - /** Operating system is Windows 95. */ - public static final int OS_WIN95 = OS_WINNT << 1; - - /** Operating system is Windows 98. */ - public static final int OS_WIN98 = OS_WIN95 << 1; - - /** Operating system is Solaris. */ - public static final int OS_SOLARIS = OS_WIN98 << 1; - - /** Operating system is Linux. */ - public static final int OS_LINUX = OS_SOLARIS << 1; - - /** Operating system is HP-UX. */ - public static final int OS_HP = OS_LINUX << 1; - - /** Operating system is IBM AIX. */ - public static final int OS_AIX = OS_HP << 1; - - /** Operating system is SGI IRIX. */ - public static final int OS_IRIX = OS_AIX << 1; - - /** Operating system is Sun OS. */ - public static final int OS_SUNOS = OS_IRIX << 1; - - /** Operating system is Compaq TRU64 Unix */ - public static final int OS_TRU64 = OS_SUNOS << 1; - - /** Operating system is OS/2. */ - public static final int OS_OS2 = OS_TRU64 << 2; - - /** Operating system is Mac. */ - public static final int OS_MAC = OS_OS2 << 1; - - /** Operating system is Windows 2000. */ - public static final int OS_WIN2000 = OS_MAC << 1; - - /** Operating system is Compaq OpenVMS */ - public static final int OS_VMS = OS_WIN2000 << 1; - - /** - *Operating system is one of the Windows variants but we don't know which - *one it is - */ - public static final int OS_WIN_OTHER = OS_VMS << 1; - - /** Operating system is unknown. */ - public static final int OS_OTHER = OS_WIN_OTHER << 1; - - /** Operating system is FreeBSD - * @since 4.50 - */ - public static final int OS_FREEBSD = OS_OTHER << 1; - - /** A mask for Windows platforms. */ - public static final int OS_WINDOWS_MASK = OS_WINNT | OS_WIN95 | OS_WIN98 | OS_WIN2000 | OS_WIN_OTHER; - - /** A mask for Unix platforms. */ - public static final int OS_UNIX_MASK = OS_SOLARIS | OS_LINUX | OS_HP | OS_AIX | OS_IRIX | OS_SUNOS | OS_TRU64 | - OS_MAC | OS_FREEBSD; - - /** A height of the windows's taskbar */ - public static final int TYPICAL_WINDOWS_TASKBAR_HEIGHT = 27; - - /** A height of the Mac OS X's menu */ - private static final int TYPICAL_MACOSX_MENU_HEIGHT = 24; - - private static int operatingSystem = -1; - - /** reference to map that maps allowed key names to their values (String, Integer) - and reference to map for mapping of values to their names */ - private static Reference namesAndValues; - - /** Get the operating system on which NetBeans is running. - * @return one of the OS_* constants (such as {@link #OS_WINNT}) - */ - public static int getOperatingSystem() { - if (operatingSystem == -1) { - String osName = System.getProperty("os.name"); - - if ("Windows NT".equals(osName)) { // NOI18N - operatingSystem = OS_WINNT; - } else if ("Windows 95".equals(osName)) { // NOI18N - operatingSystem = OS_WIN95; - } else if ("Windows 98".equals(osName)) { // NOI18N - operatingSystem = OS_WIN98; - } else if ("Windows 2000".equals(osName)) { // NOI18N - operatingSystem = OS_WIN2000; - } else if (osName.startsWith("Windows ")) { // NOI18N - operatingSystem = OS_WIN_OTHER; - } else if ("Solaris".equals(osName)) { // NOI18N - operatingSystem = OS_SOLARIS; - } else if (osName.startsWith("SunOS")) { // NOI18N - operatingSystem = OS_SOLARIS; - } - // JDK 1.4 b2 defines os.name for me as "Redhat Linux" -jglick - else if (osName.endsWith("Linux")) { // NOI18N - operatingSystem = OS_LINUX; - } else if ("HP-UX".equals(osName)) { // NOI18N - operatingSystem = OS_HP; - } else if ("AIX".equals(osName)) { // NOI18N - operatingSystem = OS_AIX; - } else if ("Irix".equals(osName)) { // NOI18N - operatingSystem = OS_IRIX; - } else if ("SunOS".equals(osName)) { // NOI18N - operatingSystem = OS_SUNOS; - } else if ("Digital UNIX".equals(osName)) { // NOI18N - operatingSystem = OS_TRU64; - } else if ("OS/2".equals(osName)) { // NOI18N - operatingSystem = OS_OS2; - } else if ("OpenVMS".equals(osName)) { // NOI18N - operatingSystem = OS_VMS; - } else if (osName.equals("Mac OS X")) { // NOI18N - operatingSystem = OS_MAC; - } else if (osName.startsWith("Darwin")) { // NOI18N - operatingSystem = OS_MAC; - } else if (osName.toLowerCase(Locale.US).startsWith("freebsd")) { // NOI18N - operatingSystem = OS_FREEBSD; - } else { - operatingSystem = OS_OTHER; - } - } - return operatingSystem; - } - - /** Test whether NetBeans is running on some variant of Windows. - * @return true if Windows, false if some other manner of operating system - */ - public static boolean isWindows() { - return (getOperatingSystem() & OS_WINDOWS_MASK) != 0; - } - - /** Test whether NetBeans is running on some variant of Unix. - * Linux is included as well as the commercial vendors, and Mac OS X. - * @return true some sort of Unix, false if some other manner of operating system - */ - public static boolean isUnix() { - return (getOperatingSystem() & OS_UNIX_MASK) != 0; - } - - /** Test whether the operating system supports icons on frames (windows). - * @return true if it does not - * - */ - public static boolean isLargeFrameIcons() { - return (getOperatingSystem() == OS_SOLARIS) || (getOperatingSystem() == OS_HP); - } - - /** - * Finds out the monitor where the user currently has the input focus. - * This method is usually used to help the client code to figure out on - * which monitor it should place newly created windows/frames/dialogs. - * - * @return the GraphicsConfiguration of the monitor which currently has the - * input focus - */ - private static GraphicsConfiguration getCurrentGraphicsConfiguration() { - Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); - if (focusOwner != null) { - Window w = SwingUtilities.getWindowAncestor(focusOwner); - if (w != null) { - return w.getGraphicsConfiguration(); - } - } - - return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration(); - } - - /** - * Returns the usable area of the screen where applications can place its - * windows. The method subtracts from the screen the area of taskbars, - * system menus and the like. The screen this method applies to is the one - * which is considered current, ussually the one where the current input - * focus is. - * - * @return the rectangle of the screen where one can place windows - * - * @since 2.5 - */ - public static Rectangle getUsableScreenBounds() { - return getUsableScreenBounds(getCurrentGraphicsConfiguration()); - } - - /** - * Returns the usable area of the screen where applications can place its - * windows. The method subtracts from the screen the area of taskbars, - * system menus and the like. - * - * @param gconf the GraphicsConfiguration of the monitor - * @return the rectangle of the screen where one can place windows - * - * @since 2.5 - */ - public static Rectangle getUsableScreenBounds(GraphicsConfiguration gconf) { - if (gconf == null) { - gconf = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration(); - } - - Rectangle bounds = new Rectangle(gconf.getBounds()); - - String str; - - str = System.getProperty("netbeans.screen.insets"); // NOI18N - - if (str != null) { - StringTokenizer st = new StringTokenizer(str, ", "); // NOI18N - - if (st.countTokens() == 4) { - try { - bounds.y = Integer.parseInt(st.nextToken()); - bounds.x = Integer.parseInt(st.nextToken()); - bounds.height -= (bounds.y + Integer.parseInt(st.nextToken())); - bounds.width -= (bounds.x + Integer.parseInt(st.nextToken())); - } catch (NumberFormatException ex) { - Logger.getAnonymousLogger().log(Level.WARNING, null, ex); - } - } - - return bounds; - } - - str = System.getProperty("netbeans.taskbar.height"); // NOI18N - - if (str != null) { - bounds.height -= Integer.getInteger(str, 0); - - return bounds; - } - - try { - Toolkit toolkit = Toolkit.getDefaultToolkit(); - Insets insets = toolkit.getScreenInsets(gconf); - bounds.y += insets.top; - bounds.x += insets.left; - bounds.height -= (insets.top + insets.bottom); - bounds.width -= (insets.left + insets.right); - } catch (Exception ex) { - Logger.getAnonymousLogger().log(Level.WARNING, null, ex); - } - - return bounds; - } - - - /** Initialization of the names and values - * @return array of two hashmaps first maps - * allowed key names to their values (String, Integer) - * and second - * hashtable for mapping of values to their names (Integer, String) - */ - private static synchronized HashMap[] initNameAndValues() { - if (namesAndValues != null) { - HashMap[] arr = (HashMap[]) namesAndValues.get(); - - if (arr != null) { - return arr; - } - } - - Field[] fields; - // JW - fix Issue #353-swingx: play nicer inside sandbox. - try { - fields = KeyEvent.class.getDeclaredFields(); -// fields = KeyEvent.class.getFields(); - } catch (SecurityException e) { - // JW: need to do better? What are the use-cases where we don't have - // any access to the fields? - fields = new Field[0]; - } - - HashMap names = new HashMap(((fields.length * 4) / 3) + 5, 0.75f); - HashMap values = new HashMap(((fields.length * 4) / 3) + 5, 0.75f); - - for (int i = 0; i < fields.length; i++) { - if (Modifier.isStatic(fields[i].getModifiers())) { - String name = fields[i].getName(); - - if (name.startsWith("VK_")) { // NOI18N - - // exclude VK - name = name.substring(3); - - try { - int numb = fields[i].getInt(null); - Integer value = numb; - names.put(name, value); - values.put(value, name); - } catch (IllegalArgumentException ex) { - } catch (IllegalAccessException ex) { - } - } - } - } - - if (names.get("CONTEXT_MENU") == null) { // NOI18N - - Integer n = 0x20C; - names.put("CONTEXT_MENU", n); // NOI18N - values.put(n, "CONTEXT_MENU"); // NOI18N - - n = 0x20D; - names.put("WINDOWS", n); // NOI18N - values.put(n, "WINDOWS"); // NOI18N - } - - HashMap[] arr = { names, values }; - - namesAndValues = new SoftReference(arr); - - return arr; - } - - /** Converts a Swing key stroke descriptor to a familiar Emacs-like name. - * @param stroke key description - * @return name of the key (e.g. CS-F1 for control-shift-function key one) - * @see #stringToKey - */ - public static String keyToString(KeyStroke stroke) { - StringBuffer sb = new StringBuffer(); - - // add modifiers that must be pressed - if (addModifiers(sb, stroke.getModifiers())) { - sb.append('-'); - } - - HashMap[] namesAndValues = initNameAndValues(); - - String c = (String) namesAndValues[1].get(stroke.getKeyCode()); - - if (c == null) { - sb.append(stroke.getKeyChar()); - } else { - sb.append(c); - } - - return sb.toString(); - } - - /** Construct a new key description from a given universal string - * description. - * Provides mapping between Emacs-like textual key descriptions and the - * KeyStroke object used in Swing. - *

                              - * This format has following form: - *

                              [C][A][S][M]-identifier - *

                              Where: - *

                                - *
                              • C stands for the Control key - *
                              • A stands for the Alt key - *
                              • S stands for the Shift key - *
                              • M stands for the Meta key - *
                              - * The format also supports two wildcard codes, to support differences in - * platforms. These are the preferred choices for registering keystrokes, - * since platform conflicts will automatically be handled: - *
                                - *
                              • D stands for the default menu accelerator - the Control - * key on most platforms, the Command (meta) key on Macintosh
                              • - *
                              • O stands for the alternate accelerator - the Alt key on - * most platforms, the Ctrl key on Macintosh (Macintosh uses Alt as a - * secondary shift key for composing international characters - if you bind - * Alt-8 to an action, a mac user with a French keyboard will not be able - * to type the [ character, which is a significant handicap
                              • - *
                              - * If you use the wildcard characters, and specify a key which will conflict - * with keys the operating system consumes, it will be mapped to whichever - * choice can work - for example, on Macintosh, Command-Q is always consumed - * by the operating system, so D-Q will always map to Control-Q. - *

                              - * Every modifier before the hyphen must be pressed. - * identifier can be any text constant from {@link KeyEvent} but - * without the leading VK_ characters. So {@link KeyEvent#VK_ENTER} is described as - * ENTER. - * - * @param s the string with the description of the key - * @return key description object, or null if the string does not represent any valid key - */ - public static KeyStroke stringToKey(String s) { - StringTokenizer st = new StringTokenizer(s.toUpperCase(Locale.ENGLISH), "-", true); // NOI18N - - int needed = 0; - - HashMap names = initNameAndValues()[0]; - - int lastModif = -1; - - try { - for (;;) { - String el = st.nextToken(); - - // required key - if (el.equals("-")) { // NOI18N - - if (lastModif != -1) { - needed |= lastModif; - lastModif = -1; - } - - continue; - } - - // if there is more elements - if (st.hasMoreElements()) { - // the text should describe modifiers - lastModif = readModifiers(el); - } else { - // last text must be the key code - Integer i = (Integer) names.get(el); - boolean wildcard = (needed & CTRL_WILDCARD_MASK) != 0; - - //Strip out the explicit mask - KeyStroke won't know - //what to do with it - needed = needed & ~CTRL_WILDCARD_MASK; - - boolean macAlt = (needed & ALT_WILDCARD_MASK) != 0; - needed = needed & ~ALT_WILDCARD_MASK; - - if (i != null) { - //#26854 - Default accelerator should be Command on mac - if (wildcard) { - needed |= getMenuShortCutKeyMask(); - - if ((getOperatingSystem() & OS_MAC) != 0) { - if (!usableKeyOnMac(i.intValue(), needed)) { - needed &= ~getMenuShortCutKeyMask(); - needed |= KeyEvent.CTRL_MASK; - } - } - } - - if (macAlt) { - if (getOperatingSystem() == OS_MAC) { - needed |= KeyEvent.CTRL_MASK; - } else { - needed |= KeyEvent.ALT_MASK; - } - } - - return KeyStroke.getKeyStroke(i.intValue(), needed); - } else { - return null; - } - } - } - } catch (NoSuchElementException ex) { - return null; - } - } - /** - * need to guard against headlessExceptions when testing. - * @return the acceletor mask for shortcuts. - */ - private static int getMenuShortCutKeyMask() { - if (GraphicsEnvironment.isHeadless()) { - return ((getOperatingSystem() & OS_MAC) != 0) ? - KeyEvent.META_MASK : KeyEvent.CTRL_MASK; - } - - return Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); - } - - private static boolean usableKeyOnMac(int key, int mask) { - //All permutations fail for Q except ctrl - if (key == KeyEvent.VK_Q) { - return false; - } - - boolean isMeta = ((mask & KeyEvent.META_MASK) != 0) || ((mask & KeyEvent.CTRL_DOWN_MASK) != 0); - - boolean isAlt = ((mask & KeyEvent.ALT_MASK) != 0) || ((mask & KeyEvent.ALT_DOWN_MASK) != 0); - - boolean isOnlyMeta = isMeta && ((mask & ~(KeyEvent.META_DOWN_MASK | KeyEvent.META_MASK)) == 0); - - //Mac OS consumes keys Command+ these keys - the app will never see - //them, so CTRL should not be remapped for these - if (isOnlyMeta) { - return (key != KeyEvent.VK_H) && (key != KeyEvent.VK_SPACE) && (key != KeyEvent.VK_TAB); - } else return !((key == KeyEvent.VK_D) && isMeta && isAlt); - } - - /** Convert a space-separated list of Emacs-like key binding names to a list of Swing key strokes. - * @param s the string with keys - * @return array of key strokes, or null if the string description is not valid - * @see #stringToKey - */ - public static KeyStroke[] stringToKeys(String s) { - StringTokenizer st = new StringTokenizer(s.toUpperCase(Locale.ENGLISH), " "); // NOI18N - ArrayList arr = new ArrayList(); - - while (st.hasMoreElements()) { - s = st.nextToken(); - - KeyStroke k = stringToKey(s); - - if (k == null) { - return null; - } - - arr.add(k); - } - - return arr.toArray(new KeyStroke[arr.size()]); - } - - /** Adds characters for modifiers to the buffer. - * @param buf buffer to add to - * @param modif modifiers to add (KeyEvent.XXX_MASK) - * @return true if something has been added - */ - private static boolean addModifiers(StringBuffer buf, int modif) { - boolean b = false; - - if ((modif & KeyEvent.CTRL_MASK) != 0) { - buf.append("C"); // NOI18N - b = true; - } - - if ((modif & KeyEvent.ALT_MASK) != 0) { - buf.append("A"); // NOI18N - b = true; - } - - if ((modif & KeyEvent.SHIFT_MASK) != 0) { - buf.append("S"); // NOI18N - b = true; - } - - if ((modif & KeyEvent.META_MASK) != 0) { - buf.append("M"); // NOI18N - b = true; - } - - if ((modif & CTRL_WILDCARD_MASK) != 0) { - buf.append("D"); - b = true; - } - - if ((modif & ALT_WILDCARD_MASK) != 0) { - buf.append("O"); - b = true; - } - - return b; - } - - /** Reads for modifiers and creates integer with required mask. - * @param s string with modifiers - * @return integer with mask - * @exception NoSuchElementException if some letter is not modifier - */ - private static int readModifiers(String s) throws NoSuchElementException { - int m = 0; - - for (int i = 0; i < s.length(); i++) { - switch (s.charAt(i)) { - case 'C': - m |= KeyEvent.CTRL_MASK; - break; - - case 'A': - m |= KeyEvent.ALT_MASK; - break; - - case 'M': - m |= KeyEvent.META_MASK; - break; - - case 'S': - m |= KeyEvent.SHIFT_MASK; - break; - - case 'D': - m |= CTRL_WILDCARD_MASK; - break; - - case 'O': - m |= ALT_WILDCARD_MASK; - break; - - default: - throw new NoSuchElementException(s); - } - } - - return m; - } - - /** - * Convert an array of objects to an array of primitive types. - * E.g. an Integer[] would be changed to an int[]. - * @param array the wrapper array - * @return a primitive array - * @throws IllegalArgumentException if the array element type is not a primitive wrapper - */ - public static Object toPrimitiveArray(Object[] array) { - if (array instanceof Integer[]) { - int[] r = new int[array.length]; - int i; - int k = array.length; - - for (i = 0; i < k; i++) - r[i] = (array[i] == null) ? 0 : ((Integer) array[i]).intValue(); - - return r; - } - - if (array instanceof Boolean[]) { - boolean[] r = new boolean[array.length]; - int i; - int k = array.length; - - for (i = 0; i < k; i++) - r[i] = (array[i] != null) && ((Boolean) array[i]).booleanValue(); - - return r; - } - - if (array instanceof Byte[]) { - byte[] r = new byte[array.length]; - int i; - int k = array.length; - - for (i = 0; i < k; i++) - r[i] = (array[i] == null) ? 0 : ((Byte) array[i]).byteValue(); - - return r; - } - - if (array instanceof Character[]) { - char[] r = new char[array.length]; - int i; - int k = array.length; - - for (i = 0; i < k; i++) - r[i] = (array[i] == null) ? 0 : ((Character) array[i]).charValue(); - - return r; - } - - if (array instanceof Double[]) { - double[] r = new double[array.length]; - int i; - int k = array.length; - - for (i = 0; i < k; i++) - r[i] = (array[i] == null) ? 0 : ((Double) array[i]).doubleValue(); - - return r; - } - - if (array instanceof Float[]) { - float[] r = new float[array.length]; - int i; - int k = array.length; - - for (i = 0; i < k; i++) - r[i] = (array[i] == null) ? 0 : ((Float) array[i]).floatValue(); - - return r; - } - - if (array instanceof Long[]) { - long[] r = new long[array.length]; - int i; - int k = array.length; - - for (i = 0; i < k; i++) - r[i] = (array[i] == null) ? 0 : ((Long) array[i]).longValue(); - - return r; - } - - if (array instanceof Short[]) { - short[] r = new short[array.length]; - int i; - int k = array.length; - - for (i = 0; i < k; i++) - r[i] = (array[i] == null) ? 0 : ((Short) array[i]).shortValue(); - - return r; - } - - throw new IllegalArgumentException(); - } - - /** - * Convert an array of primitive types to an array of objects. - * E.g. an int[] would be turned into an Integer[]. - * @param array the primitive array - * @return a wrapper array - * @throws IllegalArgumentException if the array element type is not primitive - */ - public static Object[] toObjectArray(Object array) { - if (array instanceof Object[]) { - return (Object[]) array; - } - - if (array instanceof int[]) { - int i; - int k = ((int[]) array).length; - Integer[] r = new Integer[k]; - - for (i = 0; i < k; i++) - r[i] = ((int[]) array)[i]; - - return r; - } - - if (array instanceof boolean[]) { - int i; - int k = ((boolean[]) array).length; - Boolean[] r = new Boolean[k]; - - for (i = 0; i < k; i++) - r[i] = ((boolean[]) array)[i] ? Boolean.TRUE : Boolean.FALSE; - - return r; - } - - if (array instanceof byte[]) { - int i; - int k = ((byte[]) array).length; - Byte[] r = new Byte[k]; - - for (i = 0; i < k; i++) - r[i] = ((byte[]) array)[i]; - - return r; - } - - if (array instanceof char[]) { - int i; - int k = ((char[]) array).length; - Character[] r = new Character[k]; - - for (i = 0; i < k; i++) - r[i] = ((char[]) array)[i]; - - return r; - } - - if (array instanceof double[]) { - int i; - int k = ((double[]) array).length; - Double[] r = new Double[k]; - - for (i = 0; i < k; i++) - r[i] = ((double[]) array)[i]; - - return r; - } - - if (array instanceof float[]) { - int i; - int k = ((float[]) array).length; - Float[] r = new Float[k]; - - for (i = 0; i < k; i++) - r[i] = ((float[]) array)[i]; - - return r; - } - - if (array instanceof long[]) { - int i; - int k = ((long[]) array).length; - Long[] r = new Long[k]; - - for (i = 0; i < k; i++) - r[i] = ((long[]) array)[i]; - - return r; - } - - if (array instanceof short[]) { - int i; - int k = ((short[]) array).length; - Short[] r = new Short[k]; - - for (i = 0; i < k; i++) - r[i] = ((short[]) array)[i]; - - return r; - } - - throw new IllegalArgumentException(); - } - - /** Wrap multi-line strings (and get the individual lines). - * @param original the original string to wrap - * @param width the maximum width of lines - * @param breakIterator breaks original to chars, words, sentences, depending on what instance you provide. - * @param removeNewLines if true, any newlines in the original string are ignored - * @return the lines after wrapping - */ - public static String[] wrapStringToArray( - String original, int width, BreakIterator breakIterator, boolean removeNewLines - ) { - if (original.length() == 0) { - return new String[] { original }; - } - - String[] workingSet; - - // substitute original newlines with spaces, - // remove newlines from head and tail - if (removeNewLines) { - original = trimString(original); - original = original.replace('\n', ' '); - workingSet = new String[] { original }; - } else { - StringTokenizer tokens = new StringTokenizer(original, "\n"); // NOI18N - int len = tokens.countTokens(); - workingSet = new String[len]; - - for (int i = 0; i < len; i++) { - workingSet[i] = tokens.nextToken(); - } - } - - if (width < 1) { - width = 1; - } - - if (original.length() <= width) { - return workingSet; - } - -widthcheck: { - boolean ok = true; - - for (int i = 0; i < workingSet.length; i++) { - ok = ok && (workingSet[i].length() < width); - - if (!ok) { - break widthcheck; - } - } - - return workingSet; - } - - ArrayList lines = new ArrayList(); - - int lineStart = 0; // the position of start of currently processed line in the original string - - for (int i = 0; i < workingSet.length; i++) { - if (workingSet[i].length() < width) { - lines.add(workingSet[i]); - } else { - breakIterator.setText(workingSet[i]); - - int nextStart = breakIterator.next(); - int prevStart = 0; - - do { - while (((nextStart - lineStart) < width) && (nextStart != BreakIterator.DONE)) { - prevStart = nextStart; - nextStart = breakIterator.next(); - } - - if (nextStart == BreakIterator.DONE) { - nextStart = prevStart = workingSet[i].length(); - } - - if (prevStart == 0) { - prevStart = nextStart; - } - - lines.add(workingSet[i].substring(lineStart, prevStart)); - - lineStart = prevStart; - prevStart = 0; - } while (lineStart < workingSet[i].length()); - - lineStart = 0; - } - } - - String[] s = new String[lines.size()]; - - return (String[]) lines.toArray(s); - } - - private static String trimString(String s) { - int idx = 0; - char c; - final int slen = s.length(); - - if (slen == 0) { - return s; - } - - do { - c = s.charAt(idx++); - } while (((c == '\n') || (c == '\r')) && (idx < slen)); - - s = s.substring(--idx); - idx = s.length() - 1; - - if (idx < 0) { - return s; - } - - do { - c = s.charAt(idx--); - } while (((c == '\n') || (c == '\r')) && (idx >= 0)); - - return s.substring(0, idx + 2); - } -} diff --git a/src/main/java/org/jdesktop/swingx/util/WindowUtils.java b/src/main/java/org/jdesktop/swingx/util/WindowUtils.java deleted file mode 100644 index a1deee7ee3..0000000000 --- a/src/main/java/org/jdesktop/swingx/util/WindowUtils.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * $Id: WindowUtils.java 4084 2011-11-15 18:53:49Z kschaefe $ - * - * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package org.jdesktop.swingx.util; - -import javax.swing.*; -import java.awt.*; -import java.util.ArrayList; -import java.util.List; - -import static java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment; - -/** - * Encapsulates various utilities for windows (ie: Frame and - * Dialog objects and descendants, in particular). - * - * @author Richard Bair - */ -public final class WindowUtils { - /** - * Hide the constructor - don't wan't anybody creating an instance of this - */ - private WindowUtils() { - } - - private static GraphicsConfiguration getDefaultGraphicsConfiguration() { - return getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration(); - } - - private static boolean isUnowned(Window window) { - return window.getOwner() == null || (window instanceof JDialog && JOptionPane.getRootFrame().equals(window.getOwner())); - } - - private static Rectangle getUsableDeviceBounds(GraphicsConfiguration gc) { - Rectangle bounds = gc.getBounds(); - Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(gc); - - bounds.x += insets.left; - bounds.y += insets.top; - bounds.width -= (insets.left + insets.right); - bounds.height -= (insets.top + insets.bottom); - - return bounds; - } - - /** - *

                              - * Returns the Point at which a window should be placed to - * center that window on the screen. - *

                              - *

                              - * Some thought was taken as to whether to implement a method such as this, - * or to simply make a method that, given a window, will center it. It was - * decided that it is better to not alter an object within a method. - *

                              - * - * @param window The window to calculate the center point for. This object - * can not be null. - * - * @return the Point at which the window should be placed to - * center that window on the screen. - */ - public static Point getPointForCentering(Window window) { - Window w = window.isShowing() || isUnowned(window) ? window : window.getOwner(); - GraphicsConfiguration gc = w.getGraphicsConfiguration(); - - Rectangle usableBounds = getUsableDeviceBounds(gc); - int screenWidth = usableBounds.width; - int screenHeight = usableBounds.height; - int width = window.getWidth(); - int height = window.getHeight(); - - return new Point(((screenWidth - width) / 2) + usableBounds.x, - ((screenHeight - height) / 2) + usableBounds.y); - } - - /** - *

                              - * Returns the Point at which a window should be placed to - * center that window on the given desktop. - *

                              - *

                              - * Some thought was taken as to whether to implement a method such as this, - * or to simply make a method that, given a window, will center it. It was - * decided that it is better to not alter an object within a method. - *

                              - * - * @param window The window (JInternalFrame) to calculate the center point - * for. This object can not be null. - * - * @return the Point at which the window should be placed to - * center that window on the given desktop - */ - public static Point getPointForCentering(JInternalFrame window) { - Window w = SwingUtilities.getWindowAncestor(window); - GraphicsConfiguration gc = w == null ? getDefaultGraphicsConfiguration() - : w.getGraphicsConfiguration(); - - Rectangle usableBounds = getUsableDeviceBounds(gc); - int screenWidth = usableBounds.width; - int screenHeight = usableBounds.height; - int width = window.getWidth(); - int height = window.getHeight(); - - return new Point(((screenWidth - width) / 2) + usableBounds.x, - ((screenHeight - height) / 2) + usableBounds.y); - } - - /** - *

                              - * Returns the Point at which a window should be placed in - * order to be staggered slightly from another "origin" window to - * ensure that the title areas of both windows remain visible to the user. - *

                              - * - * @param originWindow Window from which the staggered location will be calculated - * - * @return location staggered from the upper left location of the origin - * window - */ - public static Point getPointForStaggering(Window originWindow) { - Point origin = originWindow.getLocation(); - Insets insets = originWindow.getInsets(); - origin.x += insets.top; - origin.y += insets.top; - return origin; - } - - public static Window findWindow(Component c) { - if (c == null) { - return JOptionPane.getRootFrame(); - } else if (c instanceof Window) { - return (Window) c; - } else { - return findWindow(c.getParent()); - } - } - - public static List getAllComponents(final Container c) { - Component[] comps = c.getComponents(); - List compList = new ArrayList(); - for (Component comp : comps) { - compList.add(comp); - if (comp instanceof Container) { - compList.addAll(getAllComponents((Container) comp)); - } - } - return compList; - } -} diff --git a/src/main/java/org/jdesktop/swingx/util/package-info.java b/src/main/java/org/jdesktop/swingx/util/package-info.java deleted file mode 100644 index 28f75777ee..0000000000 --- a/src/main/java/org/jdesktop/swingx/util/package-info.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * $Id: package-info.java 4084 2011-11-15 18:53:49Z kschaefe $ - * - * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, - * Santa Clara, California 95054, U.S.A. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -/**Contains utility API required by JDNC's Swing Extensions. - -

                              Package Specification

                              - - - -

                              Related Documentation

                              - - - -*/ -package org.jdesktop.swingx.util; \ No newline at end of file From b384cccea3b0ec90b7ba9b5e01fa8218fda12c6d Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 7 Jul 2022 16:57:48 +0200 Subject: [PATCH 140/422] - code cleanup Former-commit-id: 86bc2486d525d2fd189f281dd73d1fe3e7cda706 --- .../mediathek/gui/tabs/tab_film/FilmDescriptionPanel.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/mediathek/gui/tabs/tab_film/FilmDescriptionPanel.java b/src/main/java/mediathek/gui/tabs/tab_film/FilmDescriptionPanel.java index 4ec9b776e8..ea0ee1974d 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/FilmDescriptionPanel.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/FilmDescriptionPanel.java @@ -19,6 +19,7 @@ import java.net.URL; public class FilmDescriptionPanel extends JPanel { + private static final Dimension ICON_DIMENSION = new Dimension(96,96); private final AGuiTabPanel currentTab; private final JScrollPane scrollPane1 = new JScrollPane(); private final JPopupMenu popupMenu = new JPopupMenu(); @@ -27,7 +28,6 @@ public class FilmDescriptionPanel extends JPanel { private final JLabel lblTitel = new JLabel(); private final JTextArea textArea = new JTextArea(); private final JXHyperlink hyperlink = new JXHyperlink(); - private DatenFilm currentFilm; public FilmDescriptionPanel(@NotNull AGuiTabPanel currentTab) { @@ -151,8 +151,7 @@ private void showFilmDescription(@NotNull DatenFilm film) { SwingUtilities.invokeLater(() -> scrollPane1.getVerticalScrollBar().setValue(0)); MVSenderIconCache.get(film.getSender()).ifPresentOrElse(icon -> { var imageDim = new Dimension(icon.getIconWidth(), icon.getIconHeight()); - var boundary = new Dimension(96, 96); - var destDim = GuiFunktionen.calculateFittedDimension(imageDim, boundary); + var destDim = GuiFunktionen.calculateFittedDimension(imageDim, ICON_DIMENSION); lblIcon.setIcon(new ScaledImageIcon(icon, destDim.width, destDim.height)); }, () -> lblIcon.setIcon(null)); } From d8ef01e36acf60ae6179ca1bf19804a85861bbff Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 7 Jul 2022 22:01:28 +0200 Subject: [PATCH 141/422] - fix incorrect sized icon ONLY using Linux... Former-commit-id: 3151a5a45703740c653505e311c9236ae1bfb74c --- .../java/mediathek/gui/tabs/tab_film/FilmDescriptionPanel.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/mediathek/gui/tabs/tab_film/FilmDescriptionPanel.java b/src/main/java/mediathek/gui/tabs/tab_film/FilmDescriptionPanel.java index ea0ee1974d..e4ba14dda5 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/FilmDescriptionPanel.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/FilmDescriptionPanel.java @@ -81,7 +81,6 @@ private void initComponents() { .gap() )); - lblIcon.setMaximumSize(new Dimension(96, 96)); lblIcon.setPreferredSize(new Dimension(96, 96)); lblIcon.setVerticalAlignment(SwingConstants.TOP); add(lblIcon, new CC().cell(0, 0, 1, 3).alignX("center").alignY("top").grow(0, 0)); //NON-NLS From 8a1496ab9ea2310a11012be10e75ab675a0c3334 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 7 Jul 2022 22:08:29 +0200 Subject: [PATCH 142/422] - doc changes Former-commit-id: f9a7bc88a895248dd790fb231fe1a6f1eb4b1dd1 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 681d87a8bd..72169ee5e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,9 @@ - Das Programm überprüft beim Start ob die JVM-Parameter korrekt sind und gibt ansonsten eine Warnung aus. Dies kann über die Paramter `-nj` oder `--no-jvm-param-checks` deaktiviert werden. - **BUGFIX:** *"Filme/Blacklist öffnen..."* Eintrag wurde in *"Filme/Blacklist bearbeiten..."* umbenannt und die Icons vereinheitlicht. - **BUGFIX:** Manchmal inkorrekte Darstellung des Bookmark-Status im Film-Tab wurde korrigiert. +- **BUGFIX:** Sendericons werden nun vollständig und hochauflösend gerendert. - **FEATURE:** Die Toolbars können nun zum Schweben in allen Tabs abgetrennt werden. +- **FEATURE:** Tab Filmbeschreibung wurde neu designed und stellt nun auch das Sendericon dar. - **FEATURE:** Icons wurden durch hochauflösende Vektorvarianten ersetzt. - **FEATURE:** Neues Suchfeld inklusive Suchhistorie. - **FEATURE:** Geoinformationen werden nun als *gesperrt/entsperrt* Icons dargestellt. Detailinformationen werden über Tooltip-Text eingeblendet. From 993dadd47f5f86d4bb76ac8dd2609f98627a0275 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 7 Jul 2022 22:25:17 +0200 Subject: [PATCH 143/422] convert to swing Former-commit-id: c6d0a69b1fd8ca51c914478145c90a36ede54b1f --- .../gui/actions/ChangeGlobalFontSetting.java | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/main/java/mediathek/gui/actions/ChangeGlobalFontSetting.java b/src/main/java/mediathek/gui/actions/ChangeGlobalFontSetting.java index efbd8bbd1a..2ab52240b1 100644 --- a/src/main/java/mediathek/gui/actions/ChangeGlobalFontSetting.java +++ b/src/main/java/mediathek/gui/actions/ChangeGlobalFontSetting.java @@ -8,6 +8,7 @@ import javafx.scene.control.*; import javafx.scene.layout.FlowPane; import javafx.stage.Stage; +import mediathek.config.Konstanten; import mediathek.mainwindow.MediathekGui; import mediathek.tool.ApplicationConfiguration; import mediathek.tool.swing.LookAndFeelType; @@ -106,18 +107,6 @@ private void resetAction() { window.close(); } - private void showAppTerminationAlert() { - Alert alert = new Alert(Alert.AlertType.CONFIRMATION, """ - Die Änderung der Schriftgröße erfordert einen Neustart. - Möchten Sie MediathekView nun beenden? - """, ButtonType.YES, ButtonType.NO); - alert.setHeaderText("Schriftgröße wurde geändert"); - alert.showAndWait(); - if (alert.getResult() == ButtonType.YES) { - SwingUtilities.invokeLater(() -> MediathekGui.ui().beenden(false, false)); - } - } - private void createWindow() { window = new Stage(); window.setTitle("Globale Schriftgröße ändern"); @@ -126,7 +115,13 @@ private void createWindow() { window.setOnHidden(evt -> SwingUtilities.invokeLater(() -> { menuItem.setEnabled(true); if (sizeChanged) { - Platform.runLater(this::showAppTerminationAlert); + var result = JOptionPane.showConfirmDialog(MediathekGui.ui(), + """ + Die Änderung der Schriftgröße erfordert einen Neustart. + Möchten Sie MediathekView nun beenden? + """, Konstanten.PROGRAMMNAME, JOptionPane.YES_NO_OPTION); + if (result == JOptionPane.YES_OPTION) + SwingUtilities.invokeLater(() -> MediathekGui.ui().beenden(false, false)); } })); } From 3c9e6e201d8a359fb3b19cb6ccdc43cf87753c54 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 7 Jul 2022 23:15:37 +0200 Subject: [PATCH 144/422] - check for correct uiScale java2d property use on Linux Former-commit-id: b9b2666a9719e83ad3b847dcb24df10917cbda65 --- CHANGELOG.md | 3 ++- src/main/java/mediathek/Main.java | 40 +++++++++++++++++++++++++++---- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72169ee5e6..07ecbbee14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,8 @@ **14.0.0** -- User Interface für macOS überarbeitet +- User Interface wurde primär für neue macOS-Versionen überarbeitet. - Das Programm überprüft beim Start ob die JVM-Parameter korrekt sind und gibt ansonsten eine Warnung aus. Dies kann über die Paramter `-nj` oder `--no-jvm-param-checks` deaktiviert werden. +- Das Programm prüft unter Linux ob der `sun.java2d.uiScale`-Parameter korrekt angegeben wurde. Im Fehlerfall wird ein Warndialog angezeigt. - **BUGFIX:** *"Filme/Blacklist öffnen..."* Eintrag wurde in *"Filme/Blacklist bearbeiten..."* umbenannt und die Icons vereinheitlicht. - **BUGFIX:** Manchmal inkorrekte Darstellung des Bookmark-Status im Film-Tab wurde korrigiert. - **BUGFIX:** Sendericons werden nun vollständig und hochauflösend gerendert. diff --git a/src/main/java/mediathek/Main.java b/src/main/java/mediathek/Main.java index 3e7857864f..5adb09deb4 100644 --- a/src/main/java/mediathek/Main.java +++ b/src/main/java/mediathek/Main.java @@ -330,18 +330,45 @@ private static void checkJVMSettings() { } } + /** + * Check if a non-floating point scale factor is set on Linux. + * Java 18 VM does not support fractional scaling. + */ + private static void checkUiScaleSetting() { + var strScale = System.getProperty("sun.java2d.uiScale"); + if (strScale != null) { + try { + Integer.parseInt(strScale); + } + catch (NumberFormatException ex) { + // not an int -> show warning + // fractional scale is NOT supported under Linux, must use integer only. + var scaleFactor = Float.parseFloat(strScale); + System.out.println("uiScale factor: " + scaleFactor); + var newScale = Math.round(scaleFactor); + System.out.println("new scale: " + newScale); + JOptionPane.showMessageDialog(null, + "" + + "Sie verwenden den Parameter -Dsun.java2d.uiScale=" + strScale + ".
                              " + + "Java unter Linux unterstützt nur ganzzahlige Skalierung!

                              " + + "Sie sollten -Dsun.java2d.uiScale=" + newScale + " oder größer verwenden falls die Schriftgröße zu klein ist.", + Konstanten.PROGRAMMNAME, JOptionPane.WARNING_MESSAGE); + } + } + } + /** * @param args the command line arguments */ public static void main(final String... args) { + if (GraphicsEnvironment.isHeadless()) { + System.err.println("Diese Version von MediathekView unterstützt keine Kommandozeilenausführung."); + System.exit(1); + } + EventQueue.invokeLater(() -> { setupEnvironmentProperties(); - if (GraphicsEnvironment.isHeadless()) { - System.err.println("Diese Version von MediathekView unterstützt keine Kommandozeilenausführung."); - System.exit(1); - } - CommandLine cmd = new CommandLine(Config.class); try { var parseResult = cmd.parseArgs(args); @@ -361,6 +388,9 @@ public static void main(final String... args) { setupDockIcon(); setupFlatLaf(); + if (SystemUtils.IS_OS_LINUX) + checkUiScaleSetting(); + if (!Config.isDisableJvmParameterChecks()) checkJVMSettings(); From 9a15a4c5e82ddc5cc1b02d977a9b32c7abc56ec6 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 7 Jul 2022 23:23:43 +0200 Subject: [PATCH 145/422] remove log message Former-commit-id: c3db83aaa0c40c269ecf63029e57f4a6a97fdc68 --- src/main/java/mediathek/Main.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/mediathek/Main.java b/src/main/java/mediathek/Main.java index 5adb09deb4..6867a3be33 100644 --- a/src/main/java/mediathek/Main.java +++ b/src/main/java/mediathek/Main.java @@ -467,8 +467,7 @@ private static void changeGlobalFontSize() { logger.info("Custom font size found, changing global UI settings"); SwingUIFontChanger fc = new SwingUIFontChanger(); fc.changeFontSize(size); - } catch (Exception e) { - logger.info("No custom font size found."); + } catch (Exception ignored) { } } From 8141f5790b5756c91581b8c325852449d6e14e45 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 7 Jul 2022 23:25:57 +0200 Subject: [PATCH 146/422] - deprecate global font change on Linux as it will work only on Nimbus L&F which is not used anymore. Remove in future commit Former-commit-id: 8a8d518b4f9d3a6023521efb84f284621037f4ec --- src/main/java/mediathek/Main.java | 2 +- src/main/java/mediathek/tool/swing/SwingUIFontChanger.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/mediathek/Main.java b/src/main/java/mediathek/Main.java index 6867a3be33..1d38e413f6 100644 --- a/src/main/java/mediathek/Main.java +++ b/src/main/java/mediathek/Main.java @@ -461,7 +461,7 @@ private static boolean isDebuggerAttached() { return ManagementFactory.getRuntimeMXBean().getInputArguments().toString().indexOf("-agentlib:jdwp") > 0; } - private static void changeGlobalFontSize() { + @Deprecated private static void changeGlobalFontSize() { try { var size = ApplicationConfiguration.getConfiguration().getFloat(ApplicationConfiguration.APPLICATION_UI_FONT_SIZE); logger.info("Custom font size found, changing global UI settings"); diff --git a/src/main/java/mediathek/tool/swing/SwingUIFontChanger.java b/src/main/java/mediathek/tool/swing/SwingUIFontChanger.java index 1367318f0d..a0863b9e2e 100644 --- a/src/main/java/mediathek/tool/swing/SwingUIFontChanger.java +++ b/src/main/java/mediathek/tool/swing/SwingUIFontChanger.java @@ -14,7 +14,7 @@ * Utility class which changes the font size for each swing font key. * Useful for enhancing the font size of an application. */ -public class SwingUIFontChanger { +@Deprecated public class SwingUIFontChanger { private static final Logger logger = LogManager.getLogger(); public static final String NIMBUS_DEFAULT_FONT = "defaultFont"; public static final String WINDOWS_DEFAULT_FONT = "Table.font"; From 1fbe75a231eeef5744db99672d80927bae2d49c7 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 7 Jul 2022 23:39:33 +0200 Subject: [PATCH 147/422] - code cleanup Former-commit-id: 4df5e6665fc48c1b2a9630a63a6c79af18391e30 --- src/main/java/mediathek/Main.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/mediathek/Main.java b/src/main/java/mediathek/Main.java index 1d38e413f6..87029d7c3a 100644 --- a/src/main/java/mediathek/Main.java +++ b/src/main/java/mediathek/Main.java @@ -215,14 +215,13 @@ private static void printVersionInformation() { */ private static void migrateOldConfigSettings() { var settingsDir = StandardLocations.getSettingsDirectory().toString(); - if (settingsDir != null && !settingsDir.isEmpty()) { + if (!settingsDir.isEmpty()) { Path pSettingsDir = Paths.get(settingsDir); if (Files.exists(pSettingsDir)) { //convert existing settings Path settingsFile = pSettingsDir.resolve(Konstanten.CONFIG_FILE); if (Files.exists(settingsFile)) { - logger.trace("{} exists", Konstanten.CONFIG_FILE); - logger.trace("migrating old config settings"); + logger.trace("migrating old config settings {}", settingsFile.toAbsolutePath().toString()); try { SettingsMigrator migrator = new SettingsMigrator(settingsFile); migrator.migrate(); @@ -525,6 +524,7 @@ private static void loadConfigurationData() { } } + @SuppressWarnings("ResultOfMethodCallIgnored") private static void deleteSettingsDirectory() { try (var walk = Files.walk(StandardLocations.getSettingsDirectory())) { walk.sorted(Comparator.reverseOrder()) From c369d5d3258ab540d0ddb33209c8590b11c858c0 Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 8 Jul 2022 09:48:27 +0200 Subject: [PATCH 148/422] - code refactor Former-commit-id: 01913e7025c13e13ee5e32cb6aebf07e3a812126 --- src/main/java/mediathek/config/Daten.java | 11 +---------- src/main/java/mediathek/config/StandardLocations.kt | 10 ++++++++++ .../javafx/bookmark/BookmarkWindowController.java | 3 ++- src/main/java/mediathek/mainwindow/MediathekGui.java | 2 +- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/main/java/mediathek/config/Daten.java b/src/main/java/mediathek/config/Daten.java index 980cb12221..771d429ea9 100644 --- a/src/main/java/mediathek/config/Daten.java +++ b/src/main/java/mediathek/config/Daten.java @@ -123,15 +123,6 @@ private static List getMediathekXmlCopyFilePath() { return xmlFilePath; } - /** - * Return the path to "bookmarks.json" - * - * @return Path object of bookmark file - */ - public static Path getBookmarkFilePath() { - return StandardLocations.getSettingsDirectory().resolve(Konstanten.BOOKMARK_FILE); - } - public StarterClass getStarterClass() { return starterClass; } @@ -141,7 +132,7 @@ public StarterClass getStarterClass() { * into memory */ public void loadBookMarkData() { - listeBookmarkList.loadFromFile(getBookmarkFilePath()); + listeBookmarkList.loadFromFile(StandardLocations.getBookmarkFilePath()); } /** diff --git a/src/main/java/mediathek/config/StandardLocations.kt b/src/main/java/mediathek/config/StandardLocations.kt index cda2f5eddc..4d7d8973d6 100644 --- a/src/main/java/mediathek/config/StandardLocations.kt +++ b/src/main/java/mediathek/config/StandardLocations.kt @@ -47,6 +47,16 @@ object StandardLocations { return baseDirectoryPath } + /** + * Return the path to "bookmarks.json" + * + * @return Path object of bookmark file + */ + @JvmStatic + fun getBookmarkFilePath(): Path { + return getSettingsDirectory().resolve(Konstanten.BOOKMARK_FILE) + } + /** * Return the path to "mediathek.xml" * diff --git a/src/main/java/mediathek/javafx/bookmark/BookmarkWindowController.java b/src/main/java/mediathek/javafx/bookmark/BookmarkWindowController.java index 0c292e982f..0545a7e216 100644 --- a/src/main/java/mediathek/javafx/bookmark/BookmarkWindowController.java +++ b/src/main/java/mediathek/javafx/bookmark/BookmarkWindowController.java @@ -32,6 +32,7 @@ import jiconfont.javafx.IconNode; import mediathek.config.Daten; import mediathek.config.MVConfig; +import mediathek.config.StandardLocations; import mediathek.controller.history.SeenHistoryController; import mediathek.daten.DatenDownload; import mediathek.daten.DatenFilm; @@ -654,7 +655,7 @@ private void updateDisplay() { */ private void saveBookMarkList() { if (listUpdated) { - listeBookmarkList.saveToFile(Daten.getBookmarkFilePath()); + listeBookmarkList.saveToFile(StandardLocations.getBookmarkFilePath()); btnSaveList.setDisable(true); JavaFxUtils.invokeInFxThreadAndWait(() -> lblMessage.setText("Merkliste ist gesichert")); } diff --git a/src/main/java/mediathek/mainwindow/MediathekGui.java b/src/main/java/mediathek/mainwindow/MediathekGui.java index b53ad8c61a..9923ee2bbd 100644 --- a/src/main/java/mediathek/mainwindow/MediathekGui.java +++ b/src/main/java/mediathek/mainwindow/MediathekGui.java @@ -992,7 +992,7 @@ public boolean beenden(boolean showOptionTerminate, boolean shutDown) { stopDownloads(); dialog.setStatusText(ShutdownState.SAVE_BOOKMARKS); - daten.getListeBookmarkList().saveToFile(Daten.getBookmarkFilePath()); + daten.getListeBookmarkList().saveToFile(StandardLocations.getBookmarkFilePath()); dialog.setStatusText(ShutdownState.SAVE_APP_DATA); daten.allesSpeichern(); From 38167d7b89d80bdf7f727cabd61ffe6002e82dbd Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 8 Jul 2022 09:54:44 +0200 Subject: [PATCH 149/422] - remove linux global font change option as it is ineffective now Former-commit-id: 79f4ff5c36f97d20a9cccb706cb3f977f3da0af8 --- CHANGELOG.md | 1 + src/main/java/mediathek/Main.java | 14 -- .../gui/actions/ChangeGlobalFontSetting.java | 128 ------------------ .../mediathek/mainwindow/MediathekGui.java | 6 - .../tool/swing/SwingUIFontChanger.java | 65 --------- .../java/mediathek/x11/MediathekGuiX11.java | 9 -- 6 files changed, 1 insertion(+), 222 deletions(-) delete mode 100644 src/main/java/mediathek/gui/actions/ChangeGlobalFontSetting.java delete mode 100644 src/main/java/mediathek/tool/swing/SwingUIFontChanger.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 07ecbbee14..fa52658432 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ - User Interface wurde primär für neue macOS-Versionen überarbeitet. - Das Programm überprüft beim Start ob die JVM-Parameter korrekt sind und gibt ansonsten eine Warnung aus. Dies kann über die Paramter `-nj` oder `--no-jvm-param-checks` deaktiviert werden. - Das Programm prüft unter Linux ob der `sun.java2d.uiScale`-Parameter korrekt angegeben wurde. Im Fehlerfall wird ein Warndialog angezeigt. +- *Globale Schriftgröße ändern...*-Befehl wurde unter Linux entfernt da es keine Auswirkungen mehr hat. - **BUGFIX:** *"Filme/Blacklist öffnen..."* Eintrag wurde in *"Filme/Blacklist bearbeiten..."* umbenannt und die Icons vereinheitlicht. - **BUGFIX:** Manchmal inkorrekte Darstellung des Bookmark-Status im Film-Tab wurde korrigiert. - **BUGFIX:** Sendericons werden nun vollständig und hochauflösend gerendert. diff --git a/src/main/java/mediathek/Main.java b/src/main/java/mediathek/Main.java index 87029d7c3a..a354e16bae 100644 --- a/src/main/java/mediathek/Main.java +++ b/src/main/java/mediathek/Main.java @@ -16,7 +16,6 @@ import mediathek.tool.affinity.Affinity; import mediathek.tool.javafx.FXErrorDialog; import mediathek.tool.migrator.SettingsMigrator; -import mediathek.tool.swing.SwingUIFontChanger; import mediathek.tool.swing.ThreadCheckingRepaintManager; import mediathek.windows.MediathekGuiWindows; import mediathek.x11.MediathekGuiX11; @@ -444,9 +443,6 @@ public static void main(final String... args) { Daten.getInstance().loadBookMarkData(); - if (SystemUtils.IS_OS_LINUX) - changeGlobalFontSize(); - startGuiMode(); }); } @@ -460,16 +456,6 @@ private static boolean isDebuggerAttached() { return ManagementFactory.getRuntimeMXBean().getInputArguments().toString().indexOf("-agentlib:jdwp") > 0; } - @Deprecated private static void changeGlobalFontSize() { - try { - var size = ApplicationConfiguration.getConfiguration().getFloat(ApplicationConfiguration.APPLICATION_UI_FONT_SIZE); - logger.info("Custom font size found, changing global UI settings"); - SwingUIFontChanger fc = new SwingUIFontChanger(); - fc.changeFontSize(size); - } catch (Exception ignored) { - } - } - /** * Migrate the old text file history to new database format */ diff --git a/src/main/java/mediathek/gui/actions/ChangeGlobalFontSetting.java b/src/main/java/mediathek/gui/actions/ChangeGlobalFontSetting.java deleted file mode 100644 index 2ab52240b1..0000000000 --- a/src/main/java/mediathek/gui/actions/ChangeGlobalFontSetting.java +++ /dev/null @@ -1,128 +0,0 @@ -package mediathek.gui.actions; - -import javafx.application.Platform; -import javafx.geometry.Insets; -import javafx.scene.Scene; -import javafx.scene.control.Button; -import javafx.scene.control.Label; -import javafx.scene.control.*; -import javafx.scene.layout.FlowPane; -import javafx.stage.Stage; -import mediathek.config.Konstanten; -import mediathek.mainwindow.MediathekGui; -import mediathek.tool.ApplicationConfiguration; -import mediathek.tool.swing.LookAndFeelType; -import mediathek.tool.swing.SwingUIFontChanger; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.controlsfx.glyphfont.FontAwesome; -import org.controlsfx.glyphfont.GlyphFont; -import org.controlsfx.glyphfont.GlyphFontRegistry; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.ActionEvent; - -public class ChangeGlobalFontSetting extends AbstractAction { - private static final Logger logger = LogManager.getLogger(); - private JMenuItem menuItem; - private boolean sizeChanged; - private Stage window; - - public ChangeGlobalFontSetting() { - putValue(Action.NAME, "Globale Schriftgröße ändern..."); - } - - public void setMenuItem(JMenuItem menuItem) { - this.menuItem = menuItem; - } - - private int getCurrentSize() { - int result = -1; - Font font; - - switch (LookAndFeelType.get(UIManager.getLookAndFeel().getClass().getName())) { - case Windows -> { - font = (Font) UIManager.getDefaults().get(SwingUIFontChanger.WINDOWS_DEFAULT_FONT); - result = font.getSize(); - } - case Nimbus -> { - font = (Font) UIManager.getDefaults().get(SwingUIFontChanger.NIMBUS_DEFAULT_FONT); - result = font.getSize(); - } - } - return result; - } - - @Override - public void actionPerformed(ActionEvent e) { - var initialSize = getCurrentSize(); - sizeChanged = false; - - Platform.runLater(() -> { - createWindow(); - window.setScene(new Scene(createLayout(initialSize))); - window.show(); - }); - } - - private FlowPane createLayout(int initialSize) { - Label label = new Label("Schriftgröße:"); - final Spinner spinner = new Spinner<>(); - spinner.setEditable(true); - - var valueFactory = new SpinnerValueFactory.IntegerSpinnerValueFactory(12, 48, initialSize); - spinner.setValueFactory(valueFactory); - spinner.valueProperty().addListener((observableValue, oldValue, newValue) -> SwingUtilities.invokeLater(() -> spinnerUpdate(newValue))); - - FlowPane root = new FlowPane(); - root.setHgap(10); - root.setVgap(10); - root.setPadding(new Insets(10)); - - GlyphFont fontAwesome = GlyphFontRegistry.font("FontAwesome"); - Button btnReset = new Button("", fontAwesome.create(FontAwesome.Glyph.TRASH_ALT)); - btnReset.setTooltip(new Tooltip("Schriftgröße zurücksetzen")); - - btnReset.setOnAction(evt -> resetAction()); - root.getChildren().addAll(label, spinner, btnReset); - - return root; - } - - private void spinnerUpdate(int newValue) { - logger.info("Updating Swing UI font size to {}", newValue); - SwingUIFontChanger fc = new SwingUIFontChanger(); - fc.changeFontSize(newValue); - ApplicationConfiguration.getConfiguration().setProperty(ApplicationConfiguration.APPLICATION_UI_FONT_SIZE, (float) newValue); - - SwingUtilities.updateComponentTreeUI(MediathekGui.ui()); - sizeChanged = true; - } - - private void resetAction() { - logger.info("Resetting Swing UI to default font size"); - ApplicationConfiguration.getConfiguration().clearProperty(ApplicationConfiguration.APPLICATION_UI_FONT_SIZE); - sizeChanged = true; - window.close(); - } - - private void createWindow() { - window = new Stage(); - window.setTitle("Globale Schriftgröße ändern"); - window.setResizable(false); - window.setOnShowing(evt -> SwingUtilities.invokeLater(() -> menuItem.setEnabled(false))); - window.setOnHidden(evt -> SwingUtilities.invokeLater(() -> { - menuItem.setEnabled(true); - if (sizeChanged) { - var result = JOptionPane.showConfirmDialog(MediathekGui.ui(), - """ - Die Änderung der Schriftgröße erfordert einen Neustart. - Möchten Sie MediathekView nun beenden? - """, Konstanten.PROGRAMMNAME, JOptionPane.YES_NO_OPTION); - if (result == JOptionPane.YES_OPTION) - SwingUtilities.invokeLater(() -> MediathekGui.ui().beenden(false, false)); - } - })); - } -} diff --git a/src/main/java/mediathek/mainwindow/MediathekGui.java b/src/main/java/mediathek/mainwindow/MediathekGui.java index 9923ee2bbd..fa181fb39f 100644 --- a/src/main/java/mediathek/mainwindow/MediathekGui.java +++ b/src/main/java/mediathek/mainwindow/MediathekGui.java @@ -867,13 +867,7 @@ private void createHelpMenu() { installAdditionalHelpEntries(); } - protected void installChangeGlobalFontSettingMenuEntry() { - //unused on macOS and Windows - } - protected void installAdditionalHelpEntries() { - installChangeGlobalFontSettingMenuEntry(); - jMenuHilfe.addSeparator(); jMenuHilfe.add(new ShowAboutAction()); } diff --git a/src/main/java/mediathek/tool/swing/SwingUIFontChanger.java b/src/main/java/mediathek/tool/swing/SwingUIFontChanger.java deleted file mode 100644 index a0863b9e2e..0000000000 --- a/src/main/java/mediathek/tool/swing/SwingUIFontChanger.java +++ /dev/null @@ -1,65 +0,0 @@ -package mediathek.tool.swing; - -import mediathek.config.Config; -import org.apache.commons.lang3.SystemUtils; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import javax.swing.*; -import java.awt.*; -import java.util.ArrayList; -import java.util.Arrays; - -/** - * Utility class which changes the font size for each swing font key. - * Useful for enhancing the font size of an application. - */ -@Deprecated public class SwingUIFontChanger { - private static final Logger logger = LogManager.getLogger(); - public static final String NIMBUS_DEFAULT_FONT = "defaultFont"; - public static final String WINDOWS_DEFAULT_FONT = "Table.font"; - - public void changeFontSize(float size) { - //do not change size on macOS - if (SystemUtils.IS_OS_MAC_OSX) - return; - - switch (LookAndFeelType.get(UIManager.getLookAndFeel().getClass().getName())) { - case Windows -> changeWindowsFontSize(size); - case Nimbus -> changeNimbusFontSize(size); - default -> logger.error("Unknown Look&Feel, cannot change font size"); - } - } - - private void changeNimbusFontSize(float size) { - var defaults = UIManager.getLookAndFeel().getDefaults(); - Font font = (Font) defaults.get(NIMBUS_DEFAULT_FONT); - font = font.deriveFont(size); - logger.info("Changing Nimbus font size"); - defaults.put("defaultFont", font); - } - - private void changeWindowsFontSize(float size) { - var keyList = new ArrayList(); - var defaults = UIManager.getDefaults(); - var keys = defaults.keys(); - while (keys.hasMoreElements()) { - if (keys.nextElement() instanceof String str_key) { - if (str_key.endsWith(".font")) - keyList.add(str_key); - } - } - - var ui_keys = keyList.stream().distinct().sorted().toArray(String[]::new); - - if (Config.isDebugModeEnabled()) - Arrays.stream(ui_keys).forEach(logger::debug); - - logger.info("Changing Windows font sizes to {}", size); - Arrays.stream(ui_keys).forEach(key -> { - var font = defaults.getFont(key); - font = font.deriveFont(size); - UIManager.put(key, font); - }); - } -} diff --git a/src/main/java/mediathek/x11/MediathekGuiX11.java b/src/main/java/mediathek/x11/MediathekGuiX11.java index 18c813a624..84655e293e 100644 --- a/src/main/java/mediathek/x11/MediathekGuiX11.java +++ b/src/main/java/mediathek/x11/MediathekGuiX11.java @@ -2,7 +2,6 @@ import mediathek.config.Konstanten; import mediathek.config.MVConfig; -import mediathek.gui.actions.ChangeGlobalFontSetting; import mediathek.mainwindow.MediathekGui; import mediathek.tool.notification.GenericNotificationCenter; import mediathek.tool.notification.INotificationCenter; @@ -36,14 +35,6 @@ private void setupX11WindowManagerClassName() { } } - @Override - protected void installChangeGlobalFontSettingMenuEntry() { - jMenuHilfe.addSeparator(); - var action = new ChangeGlobalFontSetting(); - var item = jMenuHilfe.add(action); - action.setMenuItem(item); - } - @Override protected INotificationCenter getNotificationCenter() { var notificationCenter = new LinuxNotificationCenter(); From 51c6fd5b49594029970a38e001e695ab71a176f5 Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 8 Jul 2022 10:02:27 +0200 Subject: [PATCH 150/422] -add FlatLaf Jide OSS support Former-commit-id: 74e48948c58e8f03ca36b025a1cebf93ead11e11 --- pom.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pom.xml b/pom.xml index 7d1415ee32..a06be9809c 100755 --- a/pom.xml +++ b/pom.xml @@ -169,6 +169,16 @@ flatlaf-swingx ${flatlaf.version} + + com.formdev + flatlaf-jide-oss + ${flatlaf.version} + + + com.formdev + jide-oss + 3.7.12 + es.blackleg From 545b02256ec1af83ab7c8f5efebc12ec60fe5294 Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 8 Jul 2022 10:27:02 +0200 Subject: [PATCH 151/422] - code refactor Former-commit-id: 9dc79fa13c05b0e01e708912b6e66f7a099950b3 --- .../tabs/tab_film/FilmDescriptionPanel.java | 34 ++++++++++++++----- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/main/java/mediathek/gui/tabs/tab_film/FilmDescriptionPanel.java b/src/main/java/mediathek/gui/tabs/tab_film/FilmDescriptionPanel.java index e4ba14dda5..72a24a22a3 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/FilmDescriptionPanel.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/FilmDescriptionPanel.java @@ -13,17 +13,17 @@ import net.miginfocom.swing.MigLayout; import org.jdesktop.swingx.JXHyperlink; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; import java.net.URL; public class FilmDescriptionPanel extends JPanel { - private static final Dimension ICON_DIMENSION = new Dimension(96,96); private final AGuiTabPanel currentTab; private final JScrollPane scrollPane1 = new JScrollPane(); private final JPopupMenu popupMenu = new JPopupMenu(); - private final JLabel lblIcon = new JLabel(); + private final SenderIconLabel lblIcon = new SenderIconLabel(); private final JLabel lblThema = new JLabel(); private final JLabel lblTitel = new JLabel(); private final JTextArea textArea = new JTextArea(); @@ -139,8 +139,7 @@ private void showFilmDescription(@NotNull DatenFilm film) { hyperlink.setURI(new URL(film.getWebsiteLink()).toURI()); hyperlink.setText("Link zur Webseite"); hyperlink.setClicked(false); - } - catch (Exception e) { + } catch (Exception e) { //logger hyperlink.setText("Link nicht verfügbar"); } @@ -148,10 +147,27 @@ private void showFilmDescription(@NotNull DatenFilm film) { textArea.setText(film.getDescription()); SwingUtilities.invokeLater(() -> scrollPane1.getVerticalScrollBar().setValue(0)); - MVSenderIconCache.get(film.getSender()).ifPresentOrElse(icon -> { - var imageDim = new Dimension(icon.getIconWidth(), icon.getIconHeight()); - var destDim = GuiFunktionen.calculateFittedDimension(imageDim, ICON_DIMENSION); - lblIcon.setIcon(new ScaledImageIcon(icon, destDim.width, destDim.height)); - }, () -> lblIcon.setIcon(null)); + lblIcon.setSender(film.getSender()); + } + + class SenderIconLabel extends JLabel { + private static final Dimension ICON_DIMENSION = new Dimension(96, 96); + + public SenderIconLabel() { + setText(""); + setIcon(null); + } + + public void setSender(@Nullable String sender) { + if (sender == null) { + setIcon(null); + } else { + MVSenderIconCache.get(sender).ifPresentOrElse(icon -> { + var imageDim = new Dimension(icon.getIconWidth(), icon.getIconHeight()); + var destDim = GuiFunktionen.calculateFittedDimension(imageDim, ICON_DIMENSION); + lblIcon.setIcon(new ScaledImageIcon(icon, destDim.width, destDim.height)); + }, () -> lblIcon.setIcon(null)); + } + } } } From 25fd066d9255cffbb5de27fc76bf380359876faa Mon Sep 17 00:00:00 2001 From: Christian F Date: Sun, 10 Jul 2022 14:05:42 +0200 Subject: [PATCH 152/422] remove stopwatch code Former-commit-id: 9564418f2cab12dd4d7faefa5db5df965067f86a --- src/main/java/mediathek/Main.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/java/mediathek/Main.java b/src/main/java/mediathek/Main.java index a354e16bae..f8681b103b 100644 --- a/src/main/java/mediathek/Main.java +++ b/src/main/java/mediathek/Main.java @@ -1,7 +1,6 @@ package mediathek; import com.formdev.flatlaf.FlatLightLaf; -import com.google.common.base.Stopwatch; import com.sun.jna.platform.win32.VersionHelpers; import javafx.application.Platform; import javafx.embed.swing.JFXPanel; @@ -597,7 +596,6 @@ private static void startGuiMode() { private static MediathekGui getPlatformWindow() { MediathekGui window; - Stopwatch watch = Stopwatch.createStarted(); if (SystemUtils.IS_OS_MAC_OSX) { window = new MediathekGuiMac(); @@ -608,9 +606,6 @@ private static MediathekGui getPlatformWindow() { } else throw new IllegalStateException("Unknown operating system detected! Cannot create main window"); - watch.stop(); - logger.trace("getPlatformWindow(): {}", watch); - return window; } } From b6fe23fefa1c68f51a03b33e962a3b9161e5be34 Mon Sep 17 00:00:00 2001 From: Christian F Date: Mon, 11 Jul 2022 21:16:02 +0300 Subject: [PATCH 153/422] - terminate app on non.supported operating systems Former-commit-id: 52d4c50440ca8aad364153c96156b40a51584877 --- src/main/java/mediathek/Main.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/main/java/mediathek/Main.java b/src/main/java/mediathek/Main.java index f8681b103b..b8dfd8c1de 100644 --- a/src/main/java/mediathek/Main.java +++ b/src/main/java/mediathek/Main.java @@ -595,16 +595,25 @@ private static void startGuiMode() { } private static MediathekGui getPlatformWindow() { - MediathekGui window; + MediathekGui window = null; if (SystemUtils.IS_OS_MAC_OSX) { window = new MediathekGuiMac(); } else if (SystemUtils.IS_OS_WINDOWS) { window = new MediathekGuiWindows(); - } else if (SystemUtils.IS_OS_UNIX) { + } else if (SystemUtils.IS_OS_LINUX) { window = new MediathekGuiX11(); - } else - throw new IllegalStateException("Unknown operating system detected! Cannot create main window"); + } else { + JOptionPane.showMessageDialog(null, + """ + Sie führen MediathekView auf einem nicht unterstützten Betriebssystem aus. + Es werden nur macOS, Windows und Linux unterstützt. + + Das Programm wird beendet, da die Funktionsfähigkeit nicht gewährleistet werden kann.""", + Konstanten.PROGRAMMNAME, + JOptionPane.ERROR_MESSAGE); + System.exit(2); + } return window; } From aac540e42e7bf8134c0fcce090771754c451b3ef Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 13 Jul 2022 14:01:25 +0200 Subject: [PATCH 154/422] - do not use FXML for PSet buttons Former-commit-id: ac6a17cc157292359af9a83e7d0ec1482076aa4d --- .../mediathek/gui/tabs/tab_film/GuiFilme.java | 2 +- .../buttonsPanel/ButtonsPanelController.java | 64 +++++++------------ .../res/programm/fxml/pset_buttons.fxml | 18 ------ 3 files changed, 24 insertions(+), 60 deletions(-) delete mode 100644 src/main/resources/mediathek/res/programm/fxml/pset_buttons.fxml diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index 1cd965e17b..d675cabf02 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -278,7 +278,7 @@ private void setupFilmActionPanel() { private void setupPsetButtonsPanel() { JavaFxUtils.invokeInFxThreadAndWait(() -> { try { - psetController = ButtonsPanelController.install(fxPsetButtonsPanel, this); + psetController = new ButtonsPanelController(this, fxPsetButtonsPanel); psetController.setOnCloseRequest(e -> { MessageBus.getMessageBus().publishAsync(new ButtonsPanelVisibilityChangedEvent(false)); e.consume(); diff --git a/src/main/java/mediathek/javafx/buttonsPanel/ButtonsPanelController.java b/src/main/java/mediathek/javafx/buttonsPanel/ButtonsPanelController.java index ba3a8fcb7c..c8dc8d1295 100644 --- a/src/main/java/mediathek/javafx/buttonsPanel/ButtonsPanelController.java +++ b/src/main/java/mediathek/javafx/buttonsPanel/ButtonsPanelController.java @@ -4,14 +4,9 @@ import javafx.embed.swing.JFXPanel; import javafx.event.Event; import javafx.event.EventHandler; -import javafx.fxml.FXML; -import javafx.fxml.FXMLLoader; -import javafx.fxml.Initializable; +import javafx.geometry.Insets; import javafx.scene.Scene; -import javafx.scene.control.Button; -import javafx.scene.control.Label; -import javafx.scene.control.Tab; -import javafx.scene.control.TabPane; +import javafx.scene.control.*; import javafx.scene.layout.Background; import javafx.scene.layout.BackgroundFill; import javafx.scene.layout.TilePane; @@ -22,39 +17,34 @@ import mediathek.javafx.tool.JavaFxUtils; import mediathek.tool.MessageBus; import net.engio.mbassy.listener.Handler; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; import javax.swing.*; -import java.io.IOException; -import java.net.URL; -import java.util.ResourceBundle; -public class ButtonsPanelController implements Initializable { - private static final Logger logger = LogManager.getLogger(); - @FXML - private Tab buttonsTab; - @FXML - private TilePane tilePane; +public class ButtonsPanelController { + private final GuiFilme guiFilme; + private final TilePane tilePane = new TilePane(); + private final Tab buttonsTab = new Tab("Buttons"); - public void setGuiFilme(GuiFilme guiFilme) { + public ButtonsPanelController(@NotNull GuiFilme guiFilme, @NotNull JFXPanel parent) { this.guiFilme = guiFilme; - } - - private GuiFilme guiFilme; - public static ButtonsPanelController install(JFXPanel parent, GuiFilme guiFilme) throws IOException { - logger.trace("install"); + tilePane.setPadding(new Insets(5, 5, 5, 5)); + tilePane.setVgap(5d); + tilePane.setHgap(5d); - FXMLLoader loader = new FXMLLoader(); - loader.setLocation(ButtonsPanelController.class.getResource("/mediathek/res/programm/fxml/pset_buttons.fxml")); + ScrollPane sp = new ScrollPane(); + sp.setFitToHeight(true); + sp.setFitToWidth(true); + sp.setContent(tilePane); + buttonsTab.setContent(sp); - TabPane buttonsPane = loader.load(); - final ButtonsPanelController buttonsController = loader.getController(); - buttonsController.setGuiFilme(guiFilme); - parent.setScene(new Scene(buttonsPane)); + TabPane tabPane = new TabPane(); + tabPane.setPrefHeight(110d); + tabPane.getTabs().add(buttonsTab); - return buttonsController; + parent.setScene(new Scene(tabPane)); + MessageBus.getMessageBus().subscribe(this); } public void setOnCloseRequest(EventHandler e) { @@ -62,8 +52,6 @@ public void setOnCloseRequest(EventHandler e) { } public void setupButtonLayout() { - logger.trace("setupButtonLayout called"); - tilePane.getChildren().clear(); var listeButton = Daten.listePset.getListeButton(); final var children = tilePane.getChildren(); @@ -79,15 +67,14 @@ public void setupButtonLayout() { } else { var b = new Button(psetName); if (psetColor != null) - b.setBackground(new Background(new BackgroundFill(JavaFxUtils.toFXColor(psetColor),null,null))); + b.setBackground(new Background(new BackgroundFill(JavaFxUtils.toFXColor(psetColor), null, null))); b.setOnAction(e -> { System.out.println("EXECUTING PSET BUTTON"); SwingUtilities.invokeLater(() -> guiFilme.playerStarten(pset)); }); children.add(b); } - } - else { + } else { children.add(new Label("")); } } @@ -97,9 +84,4 @@ public void setupButtonLayout() { private void handleProgramSetChangedEvent(ProgramSetChangedEvent e) { Platform.runLater(this::setupButtonLayout); } - - @Override - public void initialize(URL url, ResourceBundle resourceBundle) { - MessageBus.getMessageBus().subscribe(this); - } } diff --git a/src/main/resources/mediathek/res/programm/fxml/pset_buttons.fxml b/src/main/resources/mediathek/res/programm/fxml/pset_buttons.fxml deleted file mode 100644 index e7c7de2ca7..0000000000 --- a/src/main/resources/mediathek/res/programm/fxml/pset_buttons.fxml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - From c358b3a593bc77ff2dd446989905dae80bd4b49f Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 13 Jul 2022 17:10:21 +0200 Subject: [PATCH 155/422] - fix incorrect detection of install4j external update mechanism Former-commit-id: 45f0bd1cfae4fa2da68e90b3a8cbfb4e987252b6 --- CHANGELOG.md | 1 + .../java/mediathek/config/Konstanten.java | 1 - .../mediathek/mainwindow/MediathekGui.java | 3 +-- .../java/mediathek/tool/GuiFunktionen.java | 20 +++++++++++++++++++ .../mediathek/update/ProgramUpdateCheck.java | 8 +++----- 5 files changed, 25 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa52658432..ec0bbe27cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - **BUGFIX:** *"Filme/Blacklist öffnen..."* Eintrag wurde in *"Filme/Blacklist bearbeiten..."* umbenannt und die Icons vereinheitlicht. - **BUGFIX:** Manchmal inkorrekte Darstellung des Bookmark-Status im Film-Tab wurde korrigiert. - **BUGFIX:** Sendericons werden nun vollständig und hochauflösend gerendert. +- **BUGFIX:** Update-Möglichkeit innerhalb des Programms wird nun richtig ein- und ausgeblendet, je nach verwendetem Installationstyp. - **FEATURE:** Die Toolbars können nun zum Schweben in allen Tabs abgetrennt werden. - **FEATURE:** Tab Filmbeschreibung wurde neu designed und stellt nun auch das Sendericon dar. - **FEATURE:** Icons wurden durch hochauflösende Vektorvarianten ersetzt. diff --git a/src/main/java/mediathek/config/Konstanten.java b/src/main/java/mediathek/config/Konstanten.java index f6106d10a7..c1d023f624 100644 --- a/src/main/java/mediathek/config/Konstanten.java +++ b/src/main/java/mediathek/config/Konstanten.java @@ -32,7 +32,6 @@ public class Konstanten { * Is this a nightly or a production build? */ public static final boolean APP_IS_NIGHTLY = true; - public static final String EXTERNAL_UPDATE_PROPERTY = "externalUpdateCheck"; public static final String MACOS_OFFICIAL_APP = "OSX_OFFICIAL_APP"; public static final String FORMAT_ZIP = ".zip"; diff --git a/src/main/java/mediathek/mainwindow/MediathekGui.java b/src/main/java/mediathek/mainwindow/MediathekGui.java index fa181fb39f..f7b5efd94b 100644 --- a/src/main/java/mediathek/mainwindow/MediathekGui.java +++ b/src/main/java/mediathek/mainwindow/MediathekGui.java @@ -858,8 +858,7 @@ private void createHelpMenu() { jMenuHilfe.add(new ResetFilterDialogPosition(this)); jMenuHilfe.addSeparator(); //do not show menu entry if we have external update support - var externalUpdateCheck = System.getProperty(Konstanten.EXTERNAL_UPDATE_PROPERTY); - if (externalUpdateCheck == null || !externalUpdateCheck.equalsIgnoreCase("true")) { + if (GuiFunktionen.isNotUsingExternalUpdater()) { jMenuHilfe.add(searchProgramUpdateAction); } jMenuHilfe.add(new ShowProgramInfosAction()); diff --git a/src/main/java/mediathek/tool/GuiFunktionen.java b/src/main/java/mediathek/tool/GuiFunktionen.java index 64932a0b28..b38a0b8084 100644 --- a/src/main/java/mediathek/tool/GuiFunktionen.java +++ b/src/main/java/mediathek/tool/GuiFunktionen.java @@ -25,6 +25,26 @@ public class GuiFunktionen { private static final int UPDATE_FILME_AUTO = 2; private static final Logger logger = LogManager.getLogger(); + /** + * Property string to indicate usage of install4j's external updater. + */ + private static final String EXTERNAL_UPDATE_PROPERTY = "externalUpdateCheck"; + + /** + * Check whether or not we are using Install4j's external update mechanism. + * + * @return true if it is NOT used, true otherwise. + */ + public static boolean isNotUsingExternalUpdater() { + var externalUpdateCheck = System.getProperty(EXTERNAL_UPDATE_PROPERTY); + boolean ret = false; + if (externalUpdateCheck != null) { + if (externalUpdateCheck.equalsIgnoreCase("true") || externalUpdateCheck.isEmpty()) + ret = true; + } + + return !ret; + } /** * Determine the image's size while keeping aspect ratio within a given boundary box. diff --git a/src/main/java/mediathek/update/ProgramUpdateCheck.java b/src/main/java/mediathek/update/ProgramUpdateCheck.java index f558ccaa7c..c1f94475f3 100644 --- a/src/main/java/mediathek/update/ProgramUpdateCheck.java +++ b/src/main/java/mediathek/update/ProgramUpdateCheck.java @@ -1,13 +1,13 @@ package mediathek.update; import mediathek.config.Daten; -import mediathek.config.Konstanten; import mediathek.config.MVConfig; import mediathek.daten.DatenPset; import mediathek.daten.ListePset; import mediathek.daten.ListePsetVorlagen; import mediathek.gui.dialog.DialogNewSet; import mediathek.mainwindow.MediathekGui; +import mediathek.tool.GuiFunktionen; import mediathek.tool.GuiFunktionenProgramme; import mediathek.tool.NetUtils; import mediathek.tool.TimerPool; @@ -113,13 +113,11 @@ private void performUpdateCheck() { //we have internet... SwingUtilities.invokeLater(() -> gui.enableUpdateMenuItem(false)); - var externalUpdateCheck = System.getProperty(Konstanten.EXTERNAL_UPDATE_PROPERTY); - if (externalUpdateCheck == null || !externalUpdateCheck.equalsIgnoreCase("true")) { + if (GuiFunktionen.isNotUsingExternalUpdater()) { searchForProgramUpdate(); } - else { + else logger.info("External Update Mechanism in use -> skip program update check"); - } checkForPsetUpdates(); } else From 8dc5fe12e9b7368d5bc2cb6b5b06e12af4077a14 Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 13 Jul 2022 20:12:07 +0200 Subject: [PATCH 156/422] - initial install4j updater config action included -> untested! Former-commit-id: 41cad706d4c4f885d35b6758701197fdecb32af7 --- CHANGELOG.md | 2 +- pom.xml | 12 +++++++++ .../ConfigureExternalUpdaterAction.java | 27 +++++++++++++++++++ .../mediathek/mainwindow/MediathekGui.java | 9 +++++++ 4 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 src/main/java/mediathek/gui/actions/external_updater/ConfigureExternalUpdaterAction.java diff --git a/CHANGELOG.md b/CHANGELOG.md index ec0bbe27cb..19beec1874 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ **14.0.0** - +- **TODO:** ConfigureExternalUpdaterAction fertig programmieren! - User Interface wurde primär für neue macOS-Versionen überarbeitet. - Das Programm überprüft beim Start ob die JVM-Parameter korrekt sind und gibt ansonsten eine Warnung aus. Dies kann über die Paramter `-nj` oder `--no-jvm-param-checks` deaktiviert werden. - Das Programm prüft unter Linux ob der `sun.java2d.uiScale`-Parameter korrekt angegeben wurde. Im Fehlerfall wird ein Warndialog angezeigt. diff --git a/pom.xml b/pom.xml index a06be9809c..8e53f01d14 100755 --- a/pom.xml +++ b/pom.xml @@ -61,6 +61,11 @@ file:///${project.basedir}/maven-repository + + install4j repo + https://maven.ej-technologies.com/repository + + oss.sonatype.org-snapshot https://oss.sonatype.org/content/repositories/snapshots @@ -139,9 +144,16 @@ ./install4j9.0.5 ${env.LICENSE_KEY_9} + 9.0.7 + + com.install4j + install4j-runtime + ${install4j.version} + + org.jfree jfreechart diff --git a/src/main/java/mediathek/gui/actions/external_updater/ConfigureExternalUpdaterAction.java b/src/main/java/mediathek/gui/actions/external_updater/ConfigureExternalUpdaterAction.java new file mode 100644 index 0000000000..05762f11b1 --- /dev/null +++ b/src/main/java/mediathek/gui/actions/external_updater/ConfigureExternalUpdaterAction.java @@ -0,0 +1,27 @@ +package mediathek.gui.actions.external_updater; + +import com.install4j.api.update.UpdateSchedule; +import com.install4j.api.update.UpdateScheduleRegistry; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class ConfigureExternalUpdaterAction extends AbstractAction { + private static final Logger logger = LogManager.getLogger(); + public ConfigureExternalUpdaterAction() { + putValue(Action.NAME, "install4j Updater konfigurieren..."); + } + + @Override + public void actionPerformed(ActionEvent e) { + UpdateScheduleRegistry.setUpdateSchedule(UpdateSchedule.NEVER); + var schedule = UpdateScheduleRegistry.getUpdateSchedule(); + if (schedule != null) { + logger.trace("I4j schedule: " + schedule); + } + else + logger.warn("Install4j update schedule is null -> cannot configure"); + } +} diff --git a/src/main/java/mediathek/mainwindow/MediathekGui.java b/src/main/java/mediathek/mainwindow/MediathekGui.java index f7b5efd94b..5013401135 100644 --- a/src/main/java/mediathek/mainwindow/MediathekGui.java +++ b/src/main/java/mediathek/mainwindow/MediathekGui.java @@ -16,6 +16,7 @@ import mediathek.gui.MVTray; import mediathek.gui.TabPaneIndex; import mediathek.gui.actions.*; +import mediathek.gui.actions.external_updater.ConfigureExternalUpdaterAction; import mediathek.gui.actions.import_actions.ImportOldAbosAction; import mediathek.gui.actions.import_actions.ImportOldBlacklistAction; import mediathek.gui.actions.import_actions.ImportOldReplacementListAction; @@ -105,6 +106,7 @@ public class MediathekGui extends JFrame { private final SearchProgramUpdateAction searchProgramUpdateAction; private final MemoryMonitorAction showMemoryMonitorAction = new MemoryMonitorAction(this); private final InfoDialog filmInfo; + private final ConfigureExternalUpdaterAction configureExternalUpdaterAction = new ConfigureExternalUpdaterAction(); public StatusBar swingStatusBar; public GuiFilme tabFilme; public GuiDownloads tabDownloads; @@ -861,6 +863,13 @@ private void createHelpMenu() { if (GuiFunktionen.isNotUsingExternalUpdater()) { jMenuHilfe.add(searchProgramUpdateAction); } + else { + // add install4j Update configurator (windows only) + if (SystemUtils.IS_OS_WINDOWS) { + jMenuHilfe.add(configureExternalUpdaterAction); + jMenuHilfe.addSeparator(); + } + } jMenuHilfe.add(new ShowProgramInfosAction()); installAdditionalHelpEntries(); From cb1ed48abf24e011caa60aabf7334edca3cc5384 Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 13 Jul 2022 23:14:32 +0200 Subject: [PATCH 157/422] - convert Buttons Pset panel to swing Former-commit-id: 9af0e4e80baf91cd91da2cfd1b2e7d7757a6420f --- .../ButtonsPanelVisibilityChangedEvent.java | 9 -- .../mediathek/gui/tabs/tab_film/GuiFilme.java | 93 +++++++++---------- .../gui/tabs/tab_film/PsetButtonsPanel.java | 75 +++++++++++++++ .../buttonsPanel/ButtonsPanelController.java | 87 ----------------- 4 files changed, 117 insertions(+), 147 deletions(-) delete mode 100644 src/main/java/mediathek/gui/messages/ButtonsPanelVisibilityChangedEvent.java create mode 100644 src/main/java/mediathek/gui/tabs/tab_film/PsetButtonsPanel.java delete mode 100644 src/main/java/mediathek/javafx/buttonsPanel/ButtonsPanelController.java diff --git a/src/main/java/mediathek/gui/messages/ButtonsPanelVisibilityChangedEvent.java b/src/main/java/mediathek/gui/messages/ButtonsPanelVisibilityChangedEvent.java deleted file mode 100644 index 277a48bd00..0000000000 --- a/src/main/java/mediathek/gui/messages/ButtonsPanelVisibilityChangedEvent.java +++ /dev/null @@ -1,9 +0,0 @@ -package mediathek.gui.messages; - -public class ButtonsPanelVisibilityChangedEvent extends BaseEvent { - public boolean visible; - - public ButtonsPanelVisibilityChangedEvent(boolean visible) { - this.visible = visible; - } -} diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index d675cabf02..7afe2ac28e 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -12,7 +12,6 @@ import javafx.animation.PauseTransition; import javafx.application.Platform; import javafx.beans.value.ChangeListener; -import javafx.embed.swing.JFXPanel; import javafx.util.Duration; import mediathek.config.Daten; import mediathek.config.Konstanten; @@ -36,10 +35,8 @@ import mediathek.gui.messages.history.DownloadHistoryChangedEvent; import mediathek.gui.tabs.AGuiTabPanel; import mediathek.javafx.bookmark.BookmarkWindowController; -import mediathek.javafx.buttonsPanel.ButtonsPanelController; import mediathek.javafx.filterpanel.FXSearchControlFieldMode; import mediathek.javafx.filterpanel.FilmActionPanel; -import mediathek.javafx.tool.JavaFxUtils; import mediathek.mainwindow.MediathekGui; import mediathek.tool.*; import mediathek.tool.cellrenderer.CellRendererFilme; @@ -70,6 +67,7 @@ import java.util.List; import java.util.*; import java.util.function.Consumer; +import java.util.function.IntConsumer; public class GuiFilme extends AGuiTabPanel { @@ -91,15 +89,16 @@ public class GuiFilme extends AGuiTabPanel { public final PlayFilmAction playFilmAction = new PlayFilmAction(this); public final SaveFilmAction saveFilmAction = new SaveFilmAction(); public final BookmarkFilmAction bookmarkFilmAction = new BookmarkFilmAction(); + protected final JTabbedPane psetButtonsTab = new JTabbedPane(); private final MarkFilmAsSeenAction markFilmAsSeenAction = new MarkFilmAsSeenAction(); private final MarkFilmAsUnseenAction markFilmAsUnseenAction = new MarkFilmAsUnseenAction(); private final JScrollPane filmListScrollPane = new JScrollPane(); private final JPanel extensionArea = new JPanel(); private final JCheckBoxMenuItem cbkShowDescription = new JCheckBoxMenuItem("Beschreibung anzeigen"); - private final JFXPanel fxPsetButtonsPanel = new JFXPanel(); private final SeenHistoryController historyController = new SeenHistoryController(); private final JToolBar toolBar = new JToolBar(); + private final JCheckBoxMenuItem cbShowButtons = new JCheckBoxMenuItem("Buttons anzeigen"); /** * The JavaFx Film action popup panel. */ @@ -109,20 +108,14 @@ public class GuiFilme extends AGuiTabPanel { protected JComboBox filterSelectionComboBox = new JComboBox<>(new FilterSelectionComboBoxModel()); protected PauseTransition reloadTableDataTransition = new PauseTransition(Duration.millis(250d)); protected FilterVisibilityToggleButton btnToggleFilterDialogVisibility = new FilterVisibilityToggleButton(toggleFilterDialogVisibilityAction); + protected PsetButtonsPanel psetButtonsPanel; private Optional bookmarkWindowController = Optional.empty(); private boolean stopBeob; - private JCheckBoxMenuItem cbShowButtons; - /** - * We need a strong reference here for message bus to work properly. Otherwise the buttons - * panel controller will not receive change messages. - */ - private ButtonsPanelController psetController; private MVFilmTable tabelle; /** * We perform model filtering in the background the keep UI thread alive. */ private ListenableFuture modelFuture; - public GuiFilme(Daten aDaten, MediathekGui mediathekGui) { super(); daten = aDaten; @@ -137,12 +130,12 @@ public GuiFilme(Daten aDaten, MediathekGui mediathekGui) { // add film description panel extensionArea.add(descriptionTab); - extensionArea.add(fxPsetButtonsPanel); + extensionArea.add(psetButtonsTab); setupFilmListTable(); setupFilmSelectionPropertyListener(); setupDescriptionTab(tabelle, cbkShowDescription, ApplicationConfiguration.FILM_SHOW_DESCRIPTION); - setupPsetButtonsPanel(); + setupPsetButtonsTab(); setupFilmActionPanel(); start_init(); @@ -231,22 +224,26 @@ private void createExtensionArea() { add(extensionArea, BorderLayout.SOUTH); } - @Handler - private void handleButtonsPanelVisibilityChanged(ButtonsPanelVisibilityChangedEvent evt) { - SwingUtilities.invokeLater(() -> cbShowButtons.setSelected(evt.visible)); - SwingUtilities.invokeLater(() -> fxPsetButtonsPanel.setVisible(evt.visible)); - } - public void installViewMenuEntry(JMenu jMenuAnsicht) { - cbShowButtons = new JCheckBoxMenuItem("Buttons anzeigen"); - if (!SystemUtils.IS_OS_MAC_OSX) - cbShowButtons.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F11, 0)); - cbShowButtons.setSelected(ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.APPLICATION_BUTTONS_PANEL_VISIBLE, false)); - cbShowButtons.addActionListener(e -> MessageBus.getMessageBus().publishAsync(new ButtonsPanelVisibilityChangedEvent(cbShowButtons.isSelected()))); - jMenuAnsicht.add(cbShowButtons, 0); } + /** + * Show description panel based on settings. + */ + protected void makeButtonsTabVisible(boolean visible) { + if (visible) { + if (psetButtonsTab.indexOfComponent(psetButtonsPanel) == -1) { + psetButtonsTab.add(psetButtonsPanel, 0); + psetButtonsTab.setTitleAt(0, "Buttons"); + } + } else { + if (psetButtonsTab.indexOfComponent(psetButtonsPanel) != -1) { + psetButtonsTab.remove(psetButtonsPanel); + } + } + } + @Override public void installMenuEntries(JMenu menu) { JMenuItem miMarkFilmAsSeen = new JMenuItem("Filme als gesehen markieren"); @@ -275,35 +272,29 @@ private void setupFilmActionPanel() { filmActionPanel = new FilmActionPanel(btnToggleFilterDialogVisibility); } - private void setupPsetButtonsPanel() { - JavaFxUtils.invokeInFxThreadAndWait(() -> { - try { - psetController = new ButtonsPanelController(this, fxPsetButtonsPanel); - psetController.setOnCloseRequest(e -> { - MessageBus.getMessageBus().publishAsync(new ButtonsPanelVisibilityChangedEvent(false)); - e.consume(); - }); - psetController.setupButtonLayout(); - } catch (Exception ex) { - logger.error("setupPsetButtonsPanel", ex); - } - }); + private void setupPsetButtonsTab() { + var initialVisibility = ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.APPLICATION_BUTTONS_PANEL_VISIBLE, false); + setupButtonsMenuItem(initialVisibility); - var config = ApplicationConfiguration.getConfiguration(); - SwingUtilities.invokeLater(() -> fxPsetButtonsPanel.addComponentListener(new ComponentAdapter() { - @Override - public void componentShown(ComponentEvent e) { - config.setProperty(ApplicationConfiguration.APPLICATION_BUTTONS_PANEL_VISIBLE, true); - } + psetButtonsPanel = new PsetButtonsPanel(this); + psetButtonsPanel.putClientProperty("JTabbedPane.tabClosable", true); + psetButtonsPanel.putClientProperty("JTabbedPane.tabCloseCallback", (IntConsumer) tabIndex -> cbShowButtons.doClick()); + psetButtonsPanel.install(psetButtonsTab); - @Override - public void componentHidden(ComponentEvent e) { - config.setProperty(ApplicationConfiguration.APPLICATION_BUTTONS_PANEL_VISIBLE, false); - } - })); + makeButtonsTabVisible(initialVisibility); + } + + private void setupButtonsMenuItem(boolean initialVisibility) { + var config = ApplicationConfiguration.getConfiguration(); - final var visible = config.getBoolean(ApplicationConfiguration.APPLICATION_BUTTONS_PANEL_VISIBLE, false); - fxPsetButtonsPanel.setVisible(visible); + if (!SystemUtils.IS_OS_MAC_OSX) + cbShowButtons.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F11, 0)); + cbShowButtons.setSelected(initialVisibility); + cbShowButtons.addActionListener(l -> { + boolean visible = cbShowButtons.isSelected(); + makeButtonsTabVisible(visible); + config.setProperty(ApplicationConfiguration.APPLICATION_BUTTONS_PANEL_VISIBLE, visible); + }); } private void onComponentShown() { diff --git a/src/main/java/mediathek/gui/tabs/tab_film/PsetButtonsPanel.java b/src/main/java/mediathek/gui/tabs/tab_film/PsetButtonsPanel.java new file mode 100644 index 0000000000..97d07614ed --- /dev/null +++ b/src/main/java/mediathek/gui/tabs/tab_film/PsetButtonsPanel.java @@ -0,0 +1,75 @@ +package mediathek.gui.tabs.tab_film; + +import mediathek.config.Daten; +import mediathek.daten.DatenPset; +import mediathek.gui.messages.ProgramSetChangedEvent; +import mediathek.tool.MessageBus; +import net.engio.mbassy.listener.Handler; +import org.jdesktop.swingx.WrapLayout; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.*; + +public class PsetButtonsPanel extends JPanel { + private static final int DEFAULT_HEIGHT = 90; + private final JPanel btnPanel = new JPanel(); + + private final GuiFilme guiFilme; + + public PsetButtonsPanel(@NotNull GuiFilme guiFilme) { + this.guiFilme = guiFilme; + + setLayout(new BorderLayout()); + setPreferredSize(new Dimension(Integer.MAX_VALUE, DEFAULT_HEIGHT)); + setMinimumSize(new Dimension(100, DEFAULT_HEIGHT)); + + btnPanel.setLayout(new WrapLayout(FlowLayout.LEFT, 5, 5)); + + JScrollPane sp = new JScrollPane(); + add(sp, BorderLayout.CENTER); + + sp.setViewportView(btnPanel); + + setupButtonLayout(); + + MessageBus.getMessageBus().subscribe(this); + } + + public void install(@NotNull JTabbedPane tabbedPane) { + tabbedPane.add("Buttons", this); + } + + @Handler + private void handleProgramSetChangedEvent(ProgramSetChangedEvent e) { + System.out.println("Handle PSET CHANGE"); + SwingUtilities.invokeLater(this::setupButtonLayout); + } + + protected void setupButtonLayout() { + btnPanel.removeAll(); + + for (var pset : Daten.listePset.getListeButton()) { + final var psetName = pset.arr[DatenPset.PROGRAMMSET_NAME]; + final var psetColor = pset.getFarbe(); + if (!pset.isFreeLine()) { + if (pset.isLabel()) { + var l = new JLabel(psetName); + if (psetColor != null) + l.setForeground(psetColor); + btnPanel.add(l); + } else { + var b = new JButton(psetName); + if (psetColor != null) + b.setBackground(psetColor); + b.addActionListener(l -> guiFilme.playerStarten(pset)); + btnPanel.add(b); + } + } else { + btnPanel.add(new JLabel("")); + } + } + validate(); + repaint(); + } +} diff --git a/src/main/java/mediathek/javafx/buttonsPanel/ButtonsPanelController.java b/src/main/java/mediathek/javafx/buttonsPanel/ButtonsPanelController.java deleted file mode 100644 index c8dc8d1295..0000000000 --- a/src/main/java/mediathek/javafx/buttonsPanel/ButtonsPanelController.java +++ /dev/null @@ -1,87 +0,0 @@ -package mediathek.javafx.buttonsPanel; - -import javafx.application.Platform; -import javafx.embed.swing.JFXPanel; -import javafx.event.Event; -import javafx.event.EventHandler; -import javafx.geometry.Insets; -import javafx.scene.Scene; -import javafx.scene.control.*; -import javafx.scene.layout.Background; -import javafx.scene.layout.BackgroundFill; -import javafx.scene.layout.TilePane; -import mediathek.config.Daten; -import mediathek.daten.DatenPset; -import mediathek.gui.messages.ProgramSetChangedEvent; -import mediathek.gui.tabs.tab_film.GuiFilme; -import mediathek.javafx.tool.JavaFxUtils; -import mediathek.tool.MessageBus; -import net.engio.mbassy.listener.Handler; -import org.jetbrains.annotations.NotNull; - -import javax.swing.*; - -public class ButtonsPanelController { - private final GuiFilme guiFilme; - private final TilePane tilePane = new TilePane(); - private final Tab buttonsTab = new Tab("Buttons"); - - public ButtonsPanelController(@NotNull GuiFilme guiFilme, @NotNull JFXPanel parent) { - this.guiFilme = guiFilme; - - tilePane.setPadding(new Insets(5, 5, 5, 5)); - tilePane.setVgap(5d); - tilePane.setHgap(5d); - - ScrollPane sp = new ScrollPane(); - sp.setFitToHeight(true); - sp.setFitToWidth(true); - sp.setContent(tilePane); - buttonsTab.setContent(sp); - - TabPane tabPane = new TabPane(); - tabPane.setPrefHeight(110d); - tabPane.getTabs().add(buttonsTab); - - parent.setScene(new Scene(tabPane)); - MessageBus.getMessageBus().subscribe(this); - } - - public void setOnCloseRequest(EventHandler e) { - buttonsTab.setOnCloseRequest(e); - } - - public void setupButtonLayout() { - tilePane.getChildren().clear(); - var listeButton = Daten.listePset.getListeButton(); - final var children = tilePane.getChildren(); - for (var pset : listeButton) { - final var psetName = pset.arr[DatenPset.PROGRAMMSET_NAME]; - final var psetColor = pset.getFarbe(); - if (!pset.isFreeLine()) { - if (pset.isLabel()) { - var l = new Label(psetName); - if (psetColor != null) - l.setTextFill(JavaFxUtils.toFXColor(psetColor)); - children.add(l); - } else { - var b = new Button(psetName); - if (psetColor != null) - b.setBackground(new Background(new BackgroundFill(JavaFxUtils.toFXColor(psetColor), null, null))); - b.setOnAction(e -> { - System.out.println("EXECUTING PSET BUTTON"); - SwingUtilities.invokeLater(() -> guiFilme.playerStarten(pset)); - }); - children.add(b); - } - } else { - children.add(new Label("")); - } - } - } - - @Handler - private void handleProgramSetChangedEvent(ProgramSetChangedEvent e) { - Platform.runLater(this::setupButtonLayout); - } -} From 5aab97410684d1adc8c9f07c430bd6c16086c5e2 Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 13 Jul 2022 23:39:04 +0200 Subject: [PATCH 158/422] - select table alternating row color based on FlatLaf dark/bright mode Former-commit-id: 88c4a238cd3be1fe689679a7c79792bce3b6bd89 --- src/main/java/mediathek/Main.java | 33 ++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/main/java/mediathek/Main.java b/src/main/java/mediathek/Main.java index b8dfd8c1de..31652d8000 100644 --- a/src/main/java/mediathek/Main.java +++ b/src/main/java/mediathek/Main.java @@ -1,5 +1,6 @@ package mediathek; +import com.formdev.flatlaf.FlatLaf; import com.formdev.flatlaf.FlatLightLaf; import com.sun.jna.platform.win32.VersionHelpers; import javafx.application.Platform; @@ -30,6 +31,7 @@ import org.apache.logging.log4j.core.config.AppenderRef; import org.apache.logging.log4j.core.filter.ThresholdFilter; import org.apache.logging.log4j.core.layout.PatternLayout; +import org.jetbrains.annotations.NotNull; import picocli.CommandLine; import javax.imageio.ImageIO; @@ -271,6 +273,7 @@ private static void setupDockIcon() { private static void setupFlatLaf() { FlatLightLaf.setup(); + //FlatDarkLaf.setup(); UIManager.put("TabbedPane.showTabSeparators", true); // install alternate row color only for windows >8 and macOS, Linux @@ -280,7 +283,35 @@ private static void setupFlatLaf() { } else installAlternateRowColor = SystemUtils.IS_OS_MAC_OSX || SystemUtils.IS_OS_LINUX; if (installAlternateRowColor) - UIManager.put("Table.alternateRowColor", JTABLE_ALTERNATE_ROW_COLOR); + UIManager.put("Table.alternateRowColor", getAlternatingRowColor()); + } + + /** + * Return the alternating row color based on L&F setting. + * @return alternating color for dark or light L&Fs. + */ + private static Color getAlternatingRowColor() { + Color color; + + if (!FlatLaf.isLafDark()) { + return JTABLE_ALTERNATE_ROW_COLOR; + } + else { + var tableBg = UIManager.getColor("Table.background"); + color = brightenColor(tableBg, 0.25f); + } + return color; + } + + /** + * Calculate a brighter color by factor based on HSB values. + * @param originalColor the original color. + * @param factor the factor to brighten. + * @return the new brighter color. + */ + private static @NotNull Color brightenColor(@NotNull Color originalColor, float factor) { + float[] hsb = Color.RGBtoHSB(originalColor.getRed(), originalColor.getGreen(),originalColor.getBlue(), null); + return Color.getHSBColor(hsb[0], hsb[1], factor * (1f + hsb[2])); } /** From 8e87e781f45b78420e3466cd35fedc7e21e12752 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 14 Jul 2022 00:36:52 +0200 Subject: [PATCH 159/422] - rename SearchControlFieldMode.kt - change search pattern color based on L&F dark mode Former-commit-id: c5172b9b8c49b2292adcaaa74d666596ec16ec94 --- .../mediathek/gui/tabs/tab_film/GuiFilme.java | 53 ++++++++++++++----- .../tabs/tab_film/GuiFilmeModelHelper.java | 4 +- ...FieldMode.kt => SearchControlFieldMode.kt} | 2 +- 3 files changed, 43 insertions(+), 16 deletions(-) rename src/main/java/mediathek/javafx/filterpanel/{FXSearchControlFieldMode.kt => SearchControlFieldMode.kt} (63%) diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index 7afe2ac28e..59e42aa508 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.formdev.flatlaf.FlatClientProperties; +import com.formdev.flatlaf.FlatLaf; import com.formdev.flatlaf.extras.FlatSVGIcon; import com.formdev.flatlaf.icons.FlatSearchWithHistoryIcon; import com.google.common.util.concurrent.FutureCallback; @@ -35,8 +36,8 @@ import mediathek.gui.messages.history.DownloadHistoryChangedEvent; import mediathek.gui.tabs.AGuiTabPanel; import mediathek.javafx.bookmark.BookmarkWindowController; -import mediathek.javafx.filterpanel.FXSearchControlFieldMode; import mediathek.javafx.filterpanel.FilmActionPanel; +import mediathek.javafx.filterpanel.SearchControlFieldMode; import mediathek.mainwindow.MediathekGui; import mediathek.tool.*; import mediathek.tool.cellrenderer.CellRendererFilme; @@ -766,8 +767,8 @@ public class SearchField extends JTextField { private static final String SEARCHMODE_PROPERTY_STRING = "searchMode"; private final SearchHistoryButton searchHistoryButton = new SearchHistoryButton(); private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); - private final Color DEFAULT_FOREGROUND_COLOR = (Color) UIManager.get("TextField.foreground"); - private FXSearchControlFieldMode searchMode; + private final Color DEFAULT_FOREGROUND_COLOR = UIManager.getColor("TextField.foreground"); + private SearchControlFieldMode searchMode; public SearchField() { super("", 20); @@ -780,9 +781,9 @@ public SearchField() { //put placeholder text boolean bSearchThroughDescription = ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.SEARCH_USE_FILM_DESCRIPTIONS, false); if (bSearchThroughDescription) - setSearchMode(FXSearchControlFieldMode.IRGENDWO); + setSearchMode(SearchControlFieldMode.IRGENDWO); else - setSearchMode(FXSearchControlFieldMode.THEMA_TITEL); + setSearchMode(SearchControlFieldMode.THEMA_TITEL); addActionListener(l -> { //System.out.println("SEARCH FIRED FROM NEW SEARCH FIELD"); @@ -804,11 +805,11 @@ public SearchField() { putClientProperty("JTextField.clearCallback", (Consumer) textField -> clearSearchField()); } - public FXSearchControlFieldMode getSearchMode() { + public SearchControlFieldMode getSearchMode() { return searchMode; } - public void setSearchMode(FXSearchControlFieldMode mode) { + public void setSearchMode(SearchControlFieldMode mode) { var oldValue = searchMode; searchMode = mode; pcs.firePropertyChange(SEARCHMODE_PROPERTY_STRING, oldValue, mode); @@ -841,11 +842,26 @@ private void doCheck() { private void setForegroundTextColor(String text) { if (Filter.isPattern(text)) - setForeground(Color.BLUE); + setForeground(getPatternColor()); else setForeground(DEFAULT_FOREGROUND_COLOR); } + /** + * Get the pattern text color based on L&F dark mode. + * @return adjusted color for current L&F + */ + private Color getPatternColor() { + Color color; + if (FlatLaf.isLafDark()) { + color = UIManager.getColor("Hyperlink.linkColor"); + } + else + color = Color.BLUE; + + return color; + } + private boolean isPatternValid(String text) { return Filter.makePatternNoCache(text) != null; } @@ -874,7 +890,7 @@ private void clearSearchField() { */ private void setupHelperTexts() { String text; - if (searchMode == FXSearchControlFieldMode.THEMA_TITEL) + if (searchMode == SearchControlFieldMode.THEMA_TITEL) text = "Thema/Titel"; else text = "Thema/Titel/Beschreibung"; @@ -906,7 +922,7 @@ public void keyPressed(KeyEvent e) { class ToggleSearchFieldToggleButton extends JToggleButton { public ToggleSearchFieldToggleButton() { FlatSVGIcon selectedIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/envelope-open-text.svg"); - selectedIcon.setColorFilter(new FlatSVGIcon.ColorFilter(color -> Color.BLUE)); + selectedIcon.setColorFilter(new FlatSVGIcon.ColorFilter(color -> getSelectedColor())); FlatSVGIcon normalIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/envelope-open-text.svg"); normalIcon.setColorFilter(new FlatSVGIcon.ColorFilter(color -> Color.GRAY)); setIcon(normalIcon); @@ -919,21 +935,32 @@ public ToggleSearchFieldToggleButton() { addActionListener(l -> { switch (getSearchMode()) { case IRGENDWO -> { - setSearchMode(FXSearchControlFieldMode.THEMA_TITEL); + setSearchMode(SearchControlFieldMode.THEMA_TITEL); setupToolTip(false); } case THEMA_TITEL -> { - setSearchMode(FXSearchControlFieldMode.IRGENDWO); + setSearchMode(SearchControlFieldMode.IRGENDWO); setupToolTip(true); } } //update config - ApplicationConfiguration.getConfiguration().setProperty(ApplicationConfiguration.SEARCH_USE_FILM_DESCRIPTIONS, getSearchMode() == FXSearchControlFieldMode.IRGENDWO); + ApplicationConfiguration.getConfiguration().setProperty(ApplicationConfiguration.SEARCH_USE_FILM_DESCRIPTIONS, getSearchMode() == SearchControlFieldMode.IRGENDWO); loadTable(); }); } + private Color getSelectedColor() { + Color color; + if (FlatLaf.isLafDark()) { + color = UIManager.getColor("Hyperlink.linkColor"); + } + else + color = Color.BLUE; + + return color; + } + private void setupToolTip(boolean active) { if (active) setToolTipText("Suche in Beschreibung aktiviert"); diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilmeModelHelper.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilmeModelHelper.java index 4ea0bb98c6..3797814a5a 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilmeModelHelper.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilmeModelHelper.java @@ -9,9 +9,9 @@ import mediathek.gui.tabs.tab_film.searchfilters.FinalStageFilterNoPatternWithDescription; import mediathek.gui.tabs.tab_film.searchfilters.FinalStagePatternFilter; import mediathek.gui.tabs.tab_film.searchfilters.FinalStagePatternFilterWithDescription; -import mediathek.javafx.filterpanel.FXSearchControlFieldMode; import mediathek.javafx.filterpanel.FilmActionPanel; import mediathek.javafx.filterpanel.FilmLengthSlider; +import mediathek.javafx.filterpanel.SearchControlFieldMode; import mediathek.tool.Filter; import mediathek.tool.models.TModelFilm; import org.jetbrains.annotations.NotNull; @@ -109,7 +109,7 @@ private void updateFilterVars() { dontShowTrailers = filmActionPanel.dontShowTrailers.getValue(); dontShowGebaerdensprache = filmActionPanel.dontShowSignLanguage.getValue(); dontShowAudioVersions = filmActionPanel.dontShowAudioVersions.getValue(); - searchThroughDescriptions = newSearchField.getSearchMode() == FXSearchControlFieldMode.IRGENDWO; + searchThroughDescriptions = newSearchField.getSearchMode() == SearchControlFieldMode.IRGENDWO; arrIrgendwo = evaluateThemaTitel(); } diff --git a/src/main/java/mediathek/javafx/filterpanel/FXSearchControlFieldMode.kt b/src/main/java/mediathek/javafx/filterpanel/SearchControlFieldMode.kt similarity index 63% rename from src/main/java/mediathek/javafx/filterpanel/FXSearchControlFieldMode.kt rename to src/main/java/mediathek/javafx/filterpanel/SearchControlFieldMode.kt index 938c619491..1cf05505d9 100644 --- a/src/main/java/mediathek/javafx/filterpanel/FXSearchControlFieldMode.kt +++ b/src/main/java/mediathek/javafx/filterpanel/SearchControlFieldMode.kt @@ -1,5 +1,5 @@ package mediathek.javafx.filterpanel -enum class FXSearchControlFieldMode { +enum class SearchControlFieldMode { THEMA_TITEL, IRGENDWO } \ No newline at end of file From cc7615b55c7464b1c2a9324afb8a15b8c8d3d7fd Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 14 Jul 2022 01:14:30 +0200 Subject: [PATCH 160/422] - convert abo info panel to swing Former-commit-id: cb05565fca9f3b456ddd8dfe401e3e165c1e0187 --- .../gui/abo/AboInformationController.kt | 67 ---------------- .../mediathek/gui/abo/ManageAboPanel.java | 79 ++++++++++++------- .../fxml/abo/abo_information_panel.fxml | 17 ---- 3 files changed, 49 insertions(+), 114 deletions(-) delete mode 100644 src/main/java/mediathek/gui/abo/AboInformationController.kt delete mode 100644 src/main/resources/mediathek/res/programm/fxml/abo/abo_information_panel.fxml diff --git a/src/main/java/mediathek/gui/abo/AboInformationController.kt b/src/main/java/mediathek/gui/abo/AboInformationController.kt deleted file mode 100644 index 2052ca1488..0000000000 --- a/src/main/java/mediathek/gui/abo/AboInformationController.kt +++ /dev/null @@ -1,67 +0,0 @@ -package mediathek.gui.abo - -import javafx.fxml.FXML -import javafx.fxml.Initializable -import javafx.scene.control.Label -import mediathek.config.Daten -import mediathek.daten.abo.DatenAbo -import mediathek.gui.messages.AboListChangedEvent -import mediathek.javafx.tool.JavaFxUtils -import mediathek.tool.MessageBus -import net.engio.mbassy.listener.Handler -import java.net.URL -import java.util.* - -class AboInformationController : Initializable { - @FXML - private lateinit var totalAbos: Label - - @FXML - private lateinit var activeAbos: Label - - @FXML - private lateinit var inactiveAbos: Label - - private fun updateDisplayText() { - val listeAbo = Daten.getInstance().listeAbo - val numAbos = listeAbo.size - - if (numAbos == 1) { - totalAbos.text = "Gesamt: 1 Abo" - } else { - totalAbos.text = "Gesamt: $numAbos Abos" - } - - activeAbos.text = "${numActiveAbos()} eingeschaltet" - inactiveAbos.text = "${numInactiveAbos()} ausgeschaltet" - } - - /** - * Get the number of abos which are active and used. - * - * @return num of used abos - */ - private fun numActiveAbos(): Long { - return Daten.getInstance().listeAbo.stream().filter { obj: DatenAbo -> obj.isActive }.count() - } - - /** - * Get the number of abos which are created but offline. - * - * @return number of abos which are offline - */ - private fun numInactiveAbos(): Long { - return Daten.getInstance().listeAbo.stream().filter { abo: DatenAbo -> !abo.isActive }.count() - } - - @Handler - @Suppress("UNUSED_PARAMETER") - private fun handleAboChangedEvent(e: AboListChangedEvent) { - JavaFxUtils.invokeInFxThreadAndWait { updateDisplayText() } - } - - override fun initialize(location: URL?, resources: ResourceBundle?) { - MessageBus.messageBus.subscribe(this) - updateDisplayText() - } -} \ No newline at end of file diff --git a/src/main/java/mediathek/gui/abo/ManageAboPanel.java b/src/main/java/mediathek/gui/abo/ManageAboPanel.java index d290d6a20b..6bc1839587 100644 --- a/src/main/java/mediathek/gui/abo/ManageAboPanel.java +++ b/src/main/java/mediathek/gui/abo/ManageAboPanel.java @@ -1,9 +1,7 @@ package mediathek.gui.abo; import javafx.embed.swing.JFXPanel; -import javafx.fxml.FXMLLoader; import javafx.scene.Scene; -import javafx.scene.layout.HBox; import mediathek.config.Daten; import mediathek.daten.abo.AboTags; import mediathek.daten.abo.DatenAbo; @@ -23,12 +21,12 @@ import net.engio.mbassy.listener.Handler; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.jdesktop.swingx.JXStatusBar; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; -import java.net.URL; public class ManageAboPanel extends JPanel { private static final String ACTION_MAP_KEY_EDIT_ABO = "edit_abo"; @@ -38,14 +36,12 @@ public class ManageAboPanel extends JPanel { private final Daten daten; private final CreateNewAboAction createAboAction = new CreateNewAboAction(Daten.getInstance().getListeAbo()); private final JFXPanel toolBarPanel = new JFXPanel(); - private final JFXPanel infoPanel = new JFXPanel(); + private final JXStatusBar infoPanel = new JXStatusBar(); + private final JLabel totalAbos = new JLabel("totalAbos"); + private final JLabel activeAbos = new JLabel("activeAbos"); + private final JLabel inactiveAbos = new JLabel("inactiveAbos"); private FXAboToolBar toolBar; private JScrollPane jScrollPane1; - /* - * controller must be kept in variable for strong ref, otherwise GC will erase controller and therefore - * update of abos in dialog will stop working... - */ - private AboInformationController infoController; public ManageAboPanel() { super(); @@ -56,6 +52,7 @@ public ManageAboPanel() { setupToolBar(); setupInfoPanel(); + updateInfoText(); MessageBus.getMessageBus().subscribe(this); @@ -89,21 +86,9 @@ public void addObjectData(TModelAbo model, String sender) { } private void setupInfoPanel() { - JavaFxUtils.invokeInFxThreadAndWait(() -> { - try { - URL url = getClass().getResource("/mediathek/res/programm/fxml/abo/abo_information_panel.fxml"); - - FXMLLoader loader = new FXMLLoader(); - loader.setLocation(url); - - HBox infoPane = loader.load(); - infoPanel.setScene(new Scene(infoPane)); - - infoController = loader.getController(); - } catch (Exception ex) { - ex.printStackTrace(); - } - }); + infoPanel.add(totalAbos); + infoPanel.add(activeAbos); + infoPanel.add(inactiveAbos); } private void initializeTable() { @@ -139,7 +124,41 @@ public void tabelleSpeichern() { @Handler private void handleAboListChanged(AboListChangedEvent e) { - SwingUtilities.invokeLater(this::tabelleLaden); + SwingUtilities.invokeLater(() -> { + tabelleLaden(); + updateInfoText(); + }); + } + + /** + * Get the number of abos which are active and used. + * + * @return num of used abos + */ + private long numActiveAbos() { + return Daten.getInstance().getListeAbo().stream().filter(DatenAbo::isActive).count(); + } + + /** + * Get the number of abos which are created but offline. + * + * @return number of abos which are offline + */ + private long numInactiveAbos() { + return Daten.getInstance().getListeAbo().stream().filter(abo -> !abo.isActive()).count(); + } + + private void updateInfoText() { + var listeAbo = Daten.getInstance().getListeAbo(); + var numAbos = listeAbo.size(); + + if (numAbos == 1) + totalAbos.setText("Gesamt: 1 Abo"); + else + totalAbos.setText(String.format("Gesamt: %d Abos", numAbos)); + + activeAbos.setText(String.format("%d eingeschaltet", numActiveAbos())); + inactiveAbos.setText(String.format("%d ausgeschaltet", numInactiveAbos())); } private void setupKeyMap() { @@ -230,7 +249,7 @@ private void aboLoeschen() { String text; if (rows.length == 1) { final int delRow = tabelle.convertRowIndexToModel(rows[0]); - var abo = (DatenAbo)tabelle.getModel().getValueAt(delRow, DatenAbo.ABO_REF); + var abo = (DatenAbo) tabelle.getModel().getValueAt(delRow, DatenAbo.ABO_REF); text = '"' + abo.getName() + "\" löschen?"; } else { text = "Möchten Sie wirklich " + rows.length + " Abos löschen?"; @@ -242,7 +261,7 @@ private void aboLoeschen() { final var listeAbo = daten.getListeAbo(); for (var row : rows) { final int modelRow = tabelle.convertRowIndexToModel(row); - var abo = (DatenAbo)tabelle.getModel().getValueAt(modelRow, DatenAbo.ABO_REF); + var abo = (DatenAbo) tabelle.getModel().getValueAt(modelRow, DatenAbo.ABO_REF); listeAbo.remove(abo); } } catch (Exception e) { @@ -278,7 +297,7 @@ public void editAbo() { final int[] rows = tabelle.getSelectedRows(); int modelRow = tabelle.convertRowIndexToModel(tabelle.getSelectedRow()); - var editedAbo = (DatenAbo)tabelle.getModel().getValueAt(modelRow, DatenAbo.ABO_REF); + var editedAbo = (DatenAbo) tabelle.getModel().getValueAt(modelRow, DatenAbo.ABO_REF); DialogEditAbo dialog = new DialogEditAbo(MediathekGui.ui(), editedAbo, tabelle.getSelectedRowCount() > 1); dialog.setTitle("Abo ändern"); @@ -297,7 +316,7 @@ public void editAbo() { } modelRow = tabelle.convertRowIndexToModel(row); - var curSelAbo = (DatenAbo)tabelle.getModel().getValueAt(modelRow, DatenAbo.ABO_REF); + var curSelAbo = (DatenAbo) tabelle.getModel().getValueAt(modelRow, DatenAbo.ABO_REF); AboTags.fromIndex(b).ifPresent(tag -> { switch (tag) { @@ -323,7 +342,7 @@ private void changeAboActiveState(boolean ein) { if (rows.length > 0) { for (int row : rows) { int modelRow = tabelle.convertRowIndexToModel(row); - var akt = (DatenAbo)tabelle.getModel().getValueAt(modelRow, DatenAbo.ABO_REF); + var akt = (DatenAbo) tabelle.getModel().getValueAt(modelRow, DatenAbo.ABO_REF); akt.setActive(ein); } tabelleLaden(); diff --git a/src/main/resources/mediathek/res/programm/fxml/abo/abo_information_panel.fxml b/src/main/resources/mediathek/res/programm/fxml/abo/abo_information_panel.fxml deleted file mode 100644 index f9b5a474f8..0000000000 --- a/src/main/resources/mediathek/res/programm/fxml/abo/abo_information_panel.fxml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - From 3280d6748d16faf580079ada34287604c327af36 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 14 Jul 2022 02:04:27 +0200 Subject: [PATCH 161/422] - implement new global font menu Former-commit-id: 5acf86a06f87cc45362067d5356b41bd8a22c5f3 --- .../mediathek/mainwindow/MediathekGui.java | 150 ++++++++++++++++++ .../tool/cellrenderer/CellRendererFilme.java | 1 - .../tool/listener/BeobTableHeader.java | 36 ----- .../mediathek/tool/table/MVFilmTable.java | 19 --- .../java/mediathek/tool/table/MVTable.java | 31 +--- 5 files changed, 152 insertions(+), 85 deletions(-) diff --git a/src/main/java/mediathek/mainwindow/MediathekGui.java b/src/main/java/mediathek/mainwindow/MediathekGui.java index 5013401135..dff6f853b3 100644 --- a/src/main/java/mediathek/mainwindow/MediathekGui.java +++ b/src/main/java/mediathek/mainwindow/MediathekGui.java @@ -1,5 +1,8 @@ package mediathek.mainwindow; +import com.formdev.flatlaf.FlatLaf; +import com.formdev.flatlaf.extras.FlatAnimatedLafChange; +import com.formdev.flatlaf.ui.FlatUIUtils; import javafx.application.Platform; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; @@ -53,10 +56,15 @@ import org.jetbrains.annotations.NotNull; import javax.swing.*; +import javax.swing.text.StyleContext; import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.NoSuchElementException; import java.util.concurrent.CompletableFuture; @@ -107,6 +115,8 @@ public class MediathekGui extends JFrame { private final MemoryMonitorAction showMemoryMonitorAction = new MemoryMonitorAction(this); private final InfoDialog filmInfo; private final ConfigureExternalUpdaterAction configureExternalUpdaterAction = new ConfigureExternalUpdaterAction(); + private final JMenu fontMenu = new JMenu("Schrift"); + private final String[] availableFontFamilyNames; public StatusBar swingStatusBar; public GuiFilme tabFilme; public GuiDownloads tabDownloads; @@ -144,10 +154,15 @@ public class MediathekGui extends JFrame { private IndicatorThread progressIndicatorThread; private ManageAboAction manageAboAction; private AutomaticFilmlistUpdate automaticFilmlistUpdate; + private int initialFontMenuItemCount = -1; public MediathekGui() { ui = this; + availableFontFamilyNames = GraphicsEnvironment.getLocalGraphicsEnvironment() + .getAvailableFontFamilyNames().clone(); + Arrays.sort( availableFontFamilyNames ); + setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); loadFilmListAction = new LoadFilmListAction(this); @@ -397,6 +412,8 @@ private void createMenuBar() { jMenuAbos.setText("Abos"); jMenuBar.add(jMenuAbos); + jMenuBar.add(fontMenu); + jMenuAnsicht.setMnemonic('a'); jMenuAnsicht.setText("Ansicht"); jMenuBar.add(jMenuAnsicht); @@ -839,6 +856,138 @@ private void createViewMenu() { jMenuAnsicht.add(manageBookmarkAction); } + private void createFontMenu() { + var restoreFontMenuItem = new JMenuItem(); + restoreFontMenuItem.setText("Schrift zurücksetzen"); + restoreFontMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_0, Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); + restoreFontMenuItem.addActionListener(e -> restoreFont()); + fontMenu.add(restoreFontMenuItem); + + var incrFontMenuItem = new JMenuItem(); + incrFontMenuItem.setText("Schrift vergrößern"); + incrFontMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); + incrFontMenuItem.addActionListener(e -> increaseFontSize()); + fontMenu.add(incrFontMenuItem); + + var decrFontMenuItem = new JMenuItem(); + decrFontMenuItem.setText("Schrift verkleinern"); + decrFontMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); + decrFontMenuItem.addActionListener(e -> decreaseFontSize()); + fontMenu.add(decrFontMenuItem); + + updateFontMenuItems(); + } + + private void restoreFont() { + UIManager.put( "defaultFont", null ); + updateFontMenuItems(); + FlatLaf.updateUI(); + } + + private void increaseFontSize() { + Font font = UIManager.getFont( "defaultFont" ); + Font newFont = font.deriveFont( (float) (font.getSize() + 1) ); + UIManager.put( "defaultFont", newFont ); + + updateFontMenuItems(); + FlatLaf.updateUI(); + } + + private void decreaseFontSize() { + Font font = UIManager.getFont( "defaultFont" ); + Font newFont = font.deriveFont( (float) Math.max( font.getSize() - 1, 10 ) ); + UIManager.put( "defaultFont", newFont ); + + updateFontMenuItems(); + FlatLaf.updateUI(); + } + + private void updateFontMenuItems() { + if( initialFontMenuItemCount < 0 ) + initialFontMenuItemCount = fontMenu.getItemCount(); + else { + // remove old font items + for( int i = fontMenu.getItemCount() - 1; i >= initialFontMenuItemCount; i-- ) + fontMenu.remove( i ); + } + + // get current font + Font currentFont = UIManager.getFont( "Label.font" ); + String currentFamily = currentFont.getFamily(); + String currentSize = Integer.toString( currentFont.getSize() ); + + // add font families + fontMenu.addSeparator(); + ArrayList families = new ArrayList<>( Arrays.asList( + "Arial", "Cantarell", "Comic Sans MS", "DejaVu Sans", + "Dialog", "Liberation Sans", "Noto Sans", "Roboto", + "SansSerif", "Segoe UI", "Serif", "Tahoma", "Ubuntu", "Verdana" ) ); + if( !families.contains( currentFamily ) ) + families.add( currentFamily ); + families.sort( String.CASE_INSENSITIVE_ORDER ); + + ButtonGroup familiesGroup = new ButtonGroup(); + for( String family : families ) { + if( Arrays.binarySearch( availableFontFamilyNames, family ) < 0 ) + continue; // not available + + JCheckBoxMenuItem item = new JCheckBoxMenuItem( family ); + item.setSelected( family.equals( currentFamily ) ); + item.addActionListener( this::fontFamilyChanged ); + fontMenu.add( item ); + + familiesGroup.add( item ); + } + + // add font sizes + fontMenu.addSeparator(); + ArrayList sizes = new ArrayList<>( Arrays.asList( + "10", "11", "12", "14", "16", "18", "20", "24", "28" ) ); + if( !sizes.contains( currentSize ) ) + sizes.add( currentSize ); + sizes.sort( String.CASE_INSENSITIVE_ORDER ); + + ButtonGroup sizesGroup = new ButtonGroup(); + for( String size : sizes ) { + JCheckBoxMenuItem item = new JCheckBoxMenuItem( size ); + item.setSelected( size.equals( currentSize ) ); + item.addActionListener( this::fontSizeChanged ); + fontMenu.add( item ); + + sizesGroup.add( item ); + } + + // enabled/disable items + boolean enabled = UIManager.getLookAndFeel() instanceof FlatLaf; + for( Component item : fontMenu.getMenuComponents() ) + item.setEnabled( enabled ); + } + + private void fontFamilyChanged( ActionEvent e ) { + String fontFamily = e.getActionCommand(); + + FlatAnimatedLafChange.showSnapshot(); + + Font font = UIManager.getFont( "defaultFont" ); + Font newFont = StyleContext.getDefaultStyleContext().getFont( fontFamily, font.getStyle(), font.getSize() ); + // StyleContext.getFont() may return a UIResource, which would cause loosing user scale factor on Windows + newFont = FlatUIUtils.nonUIResource( newFont ); + UIManager.put( "defaultFont", newFont ); + + FlatLaf.updateUI(); + FlatAnimatedLafChange.hideSnapshotWithAnimation(); + } + + private void fontSizeChanged( ActionEvent e ) { + String fontSizeStr = e.getActionCommand(); + + Font font = UIManager.getFont( "defaultFont" ); + Font newFont = font.deriveFont( (float) Integer.parseInt( fontSizeStr ) ); + UIManager.put( "defaultFont", newFont ); + + FlatLaf.updateUI(); + } + @Handler private void handleFilmlistWriteStartEvent(FilmListWriteStartEvent e) { SwingUtilities.invokeLater(() -> loadFilmListAction.setEnabled(false)); @@ -887,6 +1036,7 @@ protected void initMenus() { tabFilme.installMenuEntries(jMenuFilme); tabDownloads.installMenuEntries(jMenuDownload); + createFontMenu(); createViewMenu(); tabFilme.installViewMenuEntry(jMenuAnsicht); diff --git a/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java b/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java index a9344af54d..72b4fefe99 100644 --- a/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java +++ b/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java @@ -94,7 +94,6 @@ public Component getTableCellRendererComponent( final boolean isBookMarked = datenFilm.isBookmarked(); final var mvTable = (MVTable) table; - setFont((mvTable.getDefaultFont())); //shortcut if we want to have line breaks, use text areas and skip the rest if (mvTable.isLineBreak()) { diff --git a/src/main/java/mediathek/tool/listener/BeobTableHeader.java b/src/main/java/mediathek/tool/listener/BeobTableHeader.java index 7b91010a8e..d184f12636 100644 --- a/src/main/java/mediathek/tool/listener/BeobTableHeader.java +++ b/src/main/java/mediathek/tool/listener/BeobTableHeader.java @@ -1,7 +1,6 @@ package mediathek.tool.listener; import mediathek.config.MVConfig; -import mediathek.tool.SVGIconUtilities; import mediathek.tool.table.MVTable; import org.jetbrains.annotations.NotNull; @@ -13,10 +12,6 @@ * Rechte Maustaste in der Tabelle (Kontextmenü) */ public class BeobTableHeader extends MouseAdapter { - /** - * Size factor to increase/decrease the current font size. - */ - private static final float FONT_SIZE_FACTOR = 2f; protected final MVTable tabelle; private final String[] columns; private final boolean[] spaltenAnzeigen; @@ -31,14 +26,11 @@ public class BeobTableHeader extends MouseAdapter { private final boolean displaySenderIconMenus; private final MVConfig.Configs configKey; private JCheckBoxMenuItem[] box; - private JMenuItem miIncreaseFont; - private JMenuItem miDecreaseFont; /** * Indicate whether the used table (and cell renderer) is capable of changing font size. */ private boolean fontSizeChangeCapable; private JMenuItem miResetColumns; - private JMenuItem miResetFontSize; /** * Context Menu for manipulation of table visual appearance from table header. @@ -74,31 +66,6 @@ private void createStaticMenuEntries() { miResetColumns = new JMenuItem("Spalten zurücksetzen"); miResetColumns.addActionListener(e -> tabelle.resetTabelle()); - miDecreaseFont = new JMenuItem("Schrift verkleinern"); - miDecreaseFont.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/minus.svg")); - miDecreaseFont.addActionListener(e -> { - var oldFont = tabelle.getDefaultFont(); - final var oldSize = oldFont.getSize2D(); - final var newSize = oldSize - FONT_SIZE_FACTOR; - tabelle.setDefaultFont(oldFont.deriveFont(newSize)); - tabelle.calculateRowHeight(); - }); - - miIncreaseFont = new JMenuItem("Schrift vergrößern"); - miIncreaseFont.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/plus.svg")); - miIncreaseFont.addActionListener(e -> { - var oldFont = tabelle.getDefaultFont(); - final var oldSize = oldFont.getSize2D(); - final var newSize = oldSize + FONT_SIZE_FACTOR; - tabelle.setDefaultFont(oldFont.deriveFont(newSize)); - tabelle.calculateRowHeight(); - }); - - miResetFontSize = new JMenuItem("Schriftgröße zurücksetzen"); - miResetFontSize.addActionListener(e -> { - tabelle.setDefaultFont(UIManager.getDefaults().getFont("Table.font")); - tabelle.calculateRowHeight(); - }); } @Override @@ -198,9 +165,6 @@ protected JPopupMenu prepareMenu() { if (isFontSizeChangeCapable()) { jPopupMenu.addSeparator(); - jPopupMenu.add(miIncreaseFont); - jPopupMenu.add(miDecreaseFont); - jPopupMenu.add(miResetFontSize); } return jPopupMenu; diff --git a/src/main/java/mediathek/tool/table/MVFilmTable.java b/src/main/java/mediathek/tool/table/MVFilmTable.java index a892c94120..0c0af97829 100644 --- a/src/main/java/mediathek/tool/table/MVFilmTable.java +++ b/src/main/java/mediathek/tool/table/MVFilmTable.java @@ -3,7 +3,6 @@ import mediathek.config.MVConfig; import mediathek.daten.DatenFilm; import mediathek.gui.tabs.tab_film.GuiFilme; -import mediathek.tool.ApplicationConfiguration; import mediathek.tool.FilmSize; import mediathek.tool.models.TModelFilm; import org.apache.logging.log4j.LogManager; @@ -45,24 +44,6 @@ public MVFilmTable() { }); } - @Override - protected void loadDefaultFontSize() { - var config = ApplicationConfiguration.getConfiguration(); - try { - final var fontSize = config.getFloat(ApplicationConfiguration.TAB_FILM_FONT_SIZE); - var newFont = getDefaultFont().deriveFont(fontSize); - setDefaultFont(newFont); - } - catch (Exception ignored) {} - } - - @Override - protected void saveDefaultFontSize() { - var config = ApplicationConfiguration.getConfiguration(); - final var fontSize = getDefaultFont().getSize2D(); - config.setProperty(ApplicationConfiguration.TAB_FILM_FONT_SIZE, fontSize); - } - private void resetFilmeTab(int i) { //logger.debug("resetFilmeTab()"); diff --git a/src/main/java/mediathek/tool/table/MVTable.java b/src/main/java/mediathek/tool/table/MVTable.java index a890038b5c..552c3315ad 100644 --- a/src/main/java/mediathek/tool/table/MVTable.java +++ b/src/main/java/mediathek/tool/table/MVTable.java @@ -12,7 +12,6 @@ import javax.swing.RowSorter.SortKey; import javax.swing.table.AbstractTableModel; import javax.swing.table.TableColumnModel; -import java.awt.*; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -34,10 +33,6 @@ public abstract class MVTable extends JTable { protected final Optional showIconsConfigKey; protected final Optional smallSenderIconConfigKey; protected List listeSortKeys; - /** - * This is the UI provided default font used for calculating the size area - */ - private Font defaultFont = UIManager.getDefaults().getFont("Table.font"); private boolean showSenderIcon; private boolean lineBreak = true; @@ -65,7 +60,6 @@ public MVTable(int maxColumns, boolean @NotNull [] visibleColumStore, showIconsConfigKey.ifPresent( key -> showSenderIcon = Boolean.parseBoolean(MVConfig.get(key))); smallSenderIconConfigKey.ifPresent(key -> useSmallSenderIcons = Boolean.parseBoolean(MVConfig.get(key))); - loadDefaultFontSize(); calculateRowHeight(); applyWindowsSevenTableEffects(); @@ -82,27 +76,6 @@ private void applyWindowsSevenTableEffects() { } } - /** - * Load font size from settings and replace default font. - */ - protected void loadDefaultFontSize() { - //unused here - } - - /** - * Store default font size in settings - */ - protected void saveDefaultFontSize() { - //unused here - } - - public Font getDefaultFont() { return defaultFont;} - - public void setDefaultFont(Font newFont) { - defaultFont = newFont; - saveDefaultFontSize(); - } - private SortKey sortKeyLesen(String s, String strSortOrder) { SortKey sk; @@ -151,7 +124,7 @@ public void invertSelection() { */ private int getSizeArea() { final int sizeArea; - var fm = getFontMetrics(defaultFont); + var fm = getFontMetrics(getFont()); final var height = fm.getHeight(); if (lineBreak) { @@ -169,7 +142,7 @@ private int getSizeArea() { */ public void calculateRowHeight() { var sizeArea = getSizeArea(); - var fm = getFontMetrics(defaultFont); + var fm = getFontMetrics(getFont()); var height = fm.getHeight() + 5; // add some extra spacing for the height From 48a5747977e43ac3e445777e20c6885823ec5a54 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 14 Jul 2022 02:06:46 +0200 Subject: [PATCH 162/422] - deactivate font menu on macOS Former-commit-id: a3e49a6a3cc8ceae607734a875696ff31fdce2ef --- src/main/java/mediathek/mainwindow/MediathekGui.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/mediathek/mainwindow/MediathekGui.java b/src/main/java/mediathek/mainwindow/MediathekGui.java index dff6f853b3..2fea850523 100644 --- a/src/main/java/mediathek/mainwindow/MediathekGui.java +++ b/src/main/java/mediathek/mainwindow/MediathekGui.java @@ -412,7 +412,8 @@ private void createMenuBar() { jMenuAbos.setText("Abos"); jMenuBar.add(jMenuAbos); - jMenuBar.add(fontMenu); + if (!SystemUtils.IS_OS_MAC_OSX) + jMenuBar.add(fontMenu); jMenuAnsicht.setMnemonic('a'); jMenuAnsicht.setText("Ansicht"); From e5313c4def51d25306d80d47b8c947c719cad066 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 14 Jul 2022 02:18:52 +0200 Subject: [PATCH 163/422] - remove unused font changing code Former-commit-id: fb045009f5324f7f9a65944b4161235758ed6fa6 --- .../mediathek/gui/tabs/tab_film/GuiFilme.java | 1 - .../mediathek/tool/listener/BeobTableHeader.java | 16 ---------------- 2 files changed, 17 deletions(-) diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index 59e42aa508..488fbadc7c 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -385,7 +385,6 @@ private void setupHeaderPopupMenu() { true, MVConfig.Configs.SYSTEM_TAB_FILME_LINEBREAK); - headerListener.setFontSizeChangeCapable(SystemUtils.IS_OS_LINUX); tabelle.getTableHeader().addMouseListener(headerListener); } diff --git a/src/main/java/mediathek/tool/listener/BeobTableHeader.java b/src/main/java/mediathek/tool/listener/BeobTableHeader.java index d184f12636..806be4bb03 100644 --- a/src/main/java/mediathek/tool/listener/BeobTableHeader.java +++ b/src/main/java/mediathek/tool/listener/BeobTableHeader.java @@ -26,10 +26,6 @@ public class BeobTableHeader extends MouseAdapter { private final boolean displaySenderIconMenus; private final MVConfig.Configs configKey; private JCheckBoxMenuItem[] box; - /** - * Indicate whether the used table (and cell renderer) is capable of changing font size. - */ - private boolean fontSizeChangeCapable; private JMenuItem miResetColumns; /** @@ -163,21 +159,9 @@ protected JPopupMenu prepareMenu() { // Tabellenspalten zurücksetzen jPopupMenu.add(miResetColumns); - if (isFontSizeChangeCapable()) { - jPopupMenu.addSeparator(); - } - return jPopupMenu; } - public boolean isFontSizeChangeCapable() { - return fontSizeChangeCapable; - } - - public void setFontSizeChangeCapable(boolean fontSizeChangeCapable) { - this.fontSizeChangeCapable = fontSizeChangeCapable; - } - private void showMenu(MouseEvent evt) { var popupMenu = prepareMenu(); popupMenu.show(evt.getComponent(), evt.getX(), evt.getY()); From e0466f0e96301f605e55343ff6ba3f01e2caddad Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 14 Jul 2022 02:44:26 +0200 Subject: [PATCH 164/422] - document font change possibility Former-commit-id: b25527b1f053c7e68511f5bea77a6e1f34f615fa --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19beec1874..795b2f72db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ - **FEATURE:** Geoinformationen werden nun als *gesperrt/entsperrt* Icons dargestellt. Detailinformationen werden über Tooltip-Text eingeblendet. - **FEATURE:** Ein manchmal unsichtbarer Filter-Dialog kann nun über das Menü *Hilfe/Filterdialog-Position zurücksetzen* auf den sichtbaren Monitorbereich zurückgesetzt werden. - **FEATURE:** Untertitelverfügbarkeit wird nun über ein `cc`-Icon an Anfang des Titels angezeigt. Die Spalte `UT` wird in einer späteren Programmversion entfernt werden. +- **FEATURE(Windows/Linux):** Schriftgröße kann für das gesamte Programm über das Menü "*Schrift*" geändert werden. **13.9.1** From 47b3d905e261e7b03ce32cd159d8e850386b42b9 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 14 Jul 2022 02:46:12 +0200 Subject: [PATCH 165/422] - remove unused config keys Former-commit-id: 08a5d35d96370d75a7e78382e115c40dc5412d34 --- src/main/java/mediathek/tool/ApplicationConfiguration.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/mediathek/tool/ApplicationConfiguration.java b/src/main/java/mediathek/tool/ApplicationConfiguration.java index e7e1ac6f79..299c1c8d97 100644 --- a/src/main/java/mediathek/tool/ApplicationConfiguration.java +++ b/src/main/java/mediathek/tool/ApplicationConfiguration.java @@ -42,7 +42,6 @@ public class ApplicationConfiguration { public static final String APPLICATION_UI_BANDWIDTH_MONITOR_VISIBLE = "application.ui.bandwidth_monitor.visible"; public static final String APPLICATION_UI_USE_TRAY = "application.ui.tray.use"; - public static final String APPLICATION_UI_FONT_SIZE = "application.ui.font_size"; public static final String APPLICATION_UI_DOWNLOAD_TAB_DIVIDER_LOCATION = "application.ui.download.tab.divider.location"; public static final String APPLICATION_UI_BOOKMARKLIST = "application.ui.bookmarklist"; @@ -68,7 +67,6 @@ public class ApplicationConfiguration { "searchfield.film.search_through_description"; public static final String FILM_SHOW_DESCRIPTION = "film.show_description"; public static final String CONFIG_AUTOMATIC_UPDATE_CHECK = "application.automatic_update_check"; - public static final String TAB_FILM_FONT_SIZE = "tab.film.font_size"; public static final String CLI_CLIENT_DOWNLOAD_LIST_FORMAT = "cli.client.download_list_format"; /** * logger for {@link TimerTaskListener} inner class. From 5e6f084b2e37172773ae2bdd634b9392b160cf8d Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 14 Jul 2022 03:00:00 +0200 Subject: [PATCH 166/422] - replace JavaFX alerts with swing - code cleanup Former-commit-id: f7a78e4fbfda66bf7850c75e717e88516d944e12 --- src/main/java/mediathek/Main.java | 11 ++++------- .../gui/dialogEinstellungen/PanelPsetLang.java | 12 +++--------- .../mediathek/gui/tabs/tab_film/JDownloadHelper.kt | 11 ++++------- src/main/java/mediathek/tool/SwingErrorDialog.java | 3 ++- .../java/mediathek/tool/javafx/FXErrorDialog.java | 9 --------- 5 files changed, 13 insertions(+), 33 deletions(-) diff --git a/src/main/java/mediathek/Main.java b/src/main/java/mediathek/Main.java index 31652d8000..ddbe9dfe08 100644 --- a/src/main/java/mediathek/Main.java +++ b/src/main/java/mediathek/Main.java @@ -14,7 +14,6 @@ import mediathek.mainwindow.MediathekGui; import mediathek.tool.*; import mediathek.tool.affinity.Affinity; -import mediathek.tool.javafx.FXErrorDialog; import mediathek.tool.migrator.SettingsMigrator; import mediathek.tool.swing.ThreadCheckingRepaintManager; import mediathek.windows.MediathekGuiWindows; @@ -497,13 +496,11 @@ private static void migrateSeenHistory() { } catch (Exception e) { logger.error("migrateSeenHistory", e); splashScreen.ifPresent(SplashScreen::close); - FXErrorDialog.showErrorDialogWithoutParent(Konstanten.PROGRAMMNAME, - "Migration fehlgeschlagen", + SwingErrorDialog.showExceptionMessage(null, """ - Bei der Migration der Historie der Filme ist ein Fehler aufgetreten. - Das Programm kann nicht fortfahren und wird beendet. - - Bitte überprüfen Sie die Fehlermeldung und suchen Sie Hilfe im Forum. + Bei der Migration der Historie der Filme ist ein Fehler aufgetreten.
                              + Das Programm kann nicht fortfahren und wird beendet.

                              + Bitte überprüfen Sie die Fehlermeldung und suchen Sie Hilfe im Forum. """, e); System.exit(99); } diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java b/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java index 28f7bfd477..d381e9e7a3 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java +++ b/src/main/java/mediathek/gui/dialogEinstellungen/PanelPsetLang.java @@ -1,6 +1,5 @@ package mediathek.gui.dialogEinstellungen; -import javafx.scene.control.Alert; import mediathek.config.Daten; import mediathek.config.Konstanten; import mediathek.config.MVColor; @@ -15,8 +14,6 @@ import mediathek.gui.PanelVorlage; import mediathek.gui.dialog.DialogHilfe; import mediathek.gui.messages.ProgramSetChangedEvent; -import mediathek.javafx.tool.JFXHiddenApplication; -import mediathek.javafx.tool.JavaFxUtils; import mediathek.mainwindow.MediathekGui; import mediathek.tool.*; import mediathek.tool.cellrenderer.CellRendererProgramme; @@ -767,12 +764,9 @@ private void setExport() { IoXmlSchreiben configWriter = new IoXmlSchreiben(); configWriter.exportPset(liste.toArray(new DatenPset[0]), ziel); - JavaFxUtils.invokeInFxThreadAndWait(() -> { - Alert alert = new Alert(Alert.AlertType.INFORMATION); - alert.setHeaderText("Programmset exportieren"); - alert.setContentText("Das Programmset wurde erfolgreich exportiert."); - JFXHiddenApplication.showAlert(alert, MediathekGui.ui()); - }); + JOptionPane.showMessageDialog(this, + "Das Programmset wurde erfolgreich exportiert.", + Konstanten.PROGRAMMNAME, JOptionPane.INFORMATION_MESSAGE); } } else { NoSelectionErrorDialog.show(this); diff --git a/src/main/java/mediathek/gui/tabs/tab_film/JDownloadHelper.kt b/src/main/java/mediathek/gui/tabs/tab_film/JDownloadHelper.kt index 313b6d51cc..e925ab605b 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/JDownloadHelper.kt +++ b/src/main/java/mediathek/gui/tabs/tab_film/JDownloadHelper.kt @@ -1,13 +1,12 @@ package mediathek.gui.tabs.tab_film -import javafx.application.Platform import mediathek.config.Konstanten import mediathek.controller.history.SeenHistoryController import mediathek.daten.DatenFilm import mediathek.daten.FilmResolution import mediathek.mainwindow.MediathekGui +import mediathek.tool.SwingErrorDialog import mediathek.tool.http.MVHttpClient -import mediathek.tool.javafx.FXErrorDialog import okhttp3.FormBody import okhttp3.HttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl @@ -47,11 +46,9 @@ class JDownloadHelper { showErrorMessage() } catch (e: Exception) { logger.error("downloadUrl", e) - Platform.runLater { - FXErrorDialog.showErrorDialog(Konstanten.PROGRAMMNAME, "Download nicht möglich", - "Die URL konnte nicht mit JDownloader geladen werden.\n" + - "Bitte wenden Sie sich bei Bedarf an das Forum.", e) - } + SwingErrorDialog.showExceptionMessage(MediathekGui.ui(), + "Die URL konnte nicht mit JDownloader geladen werden.
                              " + + "Bitte wenden Sie sich bei Bedarf an das Forum.",e) } } diff --git a/src/main/java/mediathek/tool/SwingErrorDialog.java b/src/main/java/mediathek/tool/SwingErrorDialog.java index 737abaa6db..79d35dfd0c 100644 --- a/src/main/java/mediathek/tool/SwingErrorDialog.java +++ b/src/main/java/mediathek/tool/SwingErrorDialog.java @@ -2,6 +2,7 @@ import mediathek.config.Konstanten; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; @@ -9,7 +10,7 @@ import java.io.StringWriter; public class SwingErrorDialog { - public static void showExceptionMessage(@NotNull Component parentComponent, + public static void showExceptionMessage(@Nullable Component parentComponent, @NotNull String messageText, @NotNull Exception exception) throws HeadlessException { diff --git a/src/main/java/mediathek/tool/javafx/FXErrorDialog.java b/src/main/java/mediathek/tool/javafx/FXErrorDialog.java index 318d7f324a..f8eaf4bfbb 100644 --- a/src/main/java/mediathek/tool/javafx/FXErrorDialog.java +++ b/src/main/java/mediathek/tool/javafx/FXErrorDialog.java @@ -7,7 +7,6 @@ import javafx.scene.layout.Priority; import javafx.stage.Modality; import mediathek.javafx.tool.JFXHiddenApplication; -import mediathek.javafx.tool.JavaFxUtils; import mediathek.mainwindow.MediathekGui; import org.jetbrains.annotations.NotNull; @@ -68,12 +67,4 @@ public static void showErrorDialog(String title, String header, String detailedE alert.initOwner(JFXHiddenApplication.getPrimaryStage()); JFXHiddenApplication.showAlert(alert, MediathekGui.ui()); } - - public static void showErrorDialogWithoutParent(String title, String header, String detailedErrorMessage, @NotNull Exception ex) { - JavaFxUtils.invokeInFxThreadAndWait(() -> { - Alert alert = getBaseAlert(title,header, detailedErrorMessage); - createExceptionContent(alert, ex); - alert.showAndWait(); - }); - } } From 30aa26a42b6aa439922688c3160e702cb3a219bc Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 14 Jul 2022 09:10:14 +0200 Subject: [PATCH 167/422] - refactor font manager Former-commit-id: 595bb295cfb5f0f321733e016692b37336c1a1b1 --- .../mediathek/mainwindow/FontManager.java | 142 ++++++++++++++++++ .../mediathek/mainwindow/MediathekGui.java | 137 +---------------- 2 files changed, 149 insertions(+), 130 deletions(-) create mode 100644 src/main/java/mediathek/mainwindow/FontManager.java diff --git a/src/main/java/mediathek/mainwindow/FontManager.java b/src/main/java/mediathek/mainwindow/FontManager.java new file mode 100644 index 0000000000..abe985a733 --- /dev/null +++ b/src/main/java/mediathek/mainwindow/FontManager.java @@ -0,0 +1,142 @@ +package mediathek.mainwindow; + +import com.formdev.flatlaf.FlatLaf; +import com.formdev.flatlaf.extras.FlatAnimatedLafChange; +import com.formdev.flatlaf.ui.FlatUIUtils; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import javax.swing.text.StyleContext; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Helper class to globally change to L&F font sizes + */ +public class FontManager { + private final MediathekGui mediathekGui; + private final String[] availableFontFamilyNames; + private int initialFontMenuItemCount = -1; + + public FontManager(@NotNull MediathekGui mediathekGui) { + this.mediathekGui = mediathekGui; + availableFontFamilyNames = GraphicsEnvironment.getLocalGraphicsEnvironment() + .getAvailableFontFamilyNames().clone(); + Arrays.sort(availableFontFamilyNames); + } + + /** + * Reset used L&F font back to default. + */ + public void resetFont() { + UIManager.put("defaultFont", null); + updateFontMenuItems(); + FlatLaf.updateUI(); + } + + public void increaseFontSize() { + Font font = UIManager.getFont("defaultFont"); + Font newFont = font.deriveFont((float) (font.getSize() + 1)); + UIManager.put("defaultFont", newFont); + + updateFontMenuItems(); + FlatLaf.updateUI(); + } + + public void decreaseFontSize() { + Font font = UIManager.getFont("defaultFont"); + Font newFont = font.deriveFont((float) Math.max(font.getSize() - 1, 10)); + UIManager.put("defaultFont", newFont); + + updateFontMenuItems(); + FlatLaf.updateUI(); + } + + public void updateFontMenuItems() { + if (initialFontMenuItemCount < 0) + initialFontMenuItemCount = mediathekGui.fontMenu.getItemCount(); + else { + // remove old font items + for (int i = mediathekGui.fontMenu.getItemCount() - 1; i >= initialFontMenuItemCount; i--) + mediathekGui.fontMenu.remove(i); + } + + // get current font + Font currentFont = UIManager.getFont("Label.font"); + String currentFamily = currentFont.getFamily(); + String currentSize = Integer.toString(currentFont.getSize()); + + // add font families + mediathekGui.fontMenu.addSeparator(); + ArrayList families = new ArrayList<>(Arrays.asList( + "Arial", "Cantarell", "Comic Sans MS", "DejaVu Sans", + "Dialog", "Liberation Sans", "Noto Sans", "Roboto", + "SansSerif", "Segoe UI", "Serif", "Tahoma", "Ubuntu", "Verdana")); + if (!families.contains(currentFamily)) + families.add(currentFamily); + families.sort(String.CASE_INSENSITIVE_ORDER); + + ButtonGroup familiesGroup = new ButtonGroup(); + for (String family : families) { + if (Arrays.binarySearch(availableFontFamilyNames, family) < 0) + continue; // not available + + JCheckBoxMenuItem item = new JCheckBoxMenuItem(family); + item.setSelected(family.equals(currentFamily)); + item.addActionListener(this::fontFamilyChanged); + mediathekGui.fontMenu.add(item); + + familiesGroup.add(item); + } + + // add font sizes + mediathekGui.fontMenu.addSeparator(); + ArrayList sizes = new ArrayList<>(Arrays.asList( + "10", "11", "12", "14", "16", "18", "20", "24", "28")); + if (!sizes.contains(currentSize)) + sizes.add(currentSize); + sizes.sort(String.CASE_INSENSITIVE_ORDER); + + ButtonGroup sizesGroup = new ButtonGroup(); + for (String size : sizes) { + JCheckBoxMenuItem item = new JCheckBoxMenuItem(size); + item.setSelected(size.equals(currentSize)); + item.addActionListener(this::fontSizeChanged); + mediathekGui.fontMenu.add(item); + + sizesGroup.add(item); + } + + // enabled/disable items + boolean enabled = UIManager.getLookAndFeel() instanceof FlatLaf; + for (Component item : mediathekGui.fontMenu.getMenuComponents()) + item.setEnabled(enabled); + } + + private void fontFamilyChanged(ActionEvent e) { + String fontFamily = e.getActionCommand(); + + FlatAnimatedLafChange.showSnapshot(); + + Font font = UIManager.getFont("defaultFont"); + Font newFont = StyleContext.getDefaultStyleContext().getFont(fontFamily, font.getStyle(), font.getSize()); + // StyleContext.getFont() may return a UIResource, which would cause loosing user scale factor on Windows + newFont = FlatUIUtils.nonUIResource(newFont); + UIManager.put("defaultFont", newFont); + + FlatLaf.updateUI(); + FlatAnimatedLafChange.hideSnapshotWithAnimation(); + } + + private void fontSizeChanged(ActionEvent e) { + String fontSizeStr = e.getActionCommand(); + + Font font = UIManager.getFont("defaultFont"); + Font newFont = font.deriveFont((float) Integer.parseInt(fontSizeStr)); + UIManager.put("defaultFont", newFont); + + FlatLaf.updateUI(); + } +} diff --git a/src/main/java/mediathek/mainwindow/MediathekGui.java b/src/main/java/mediathek/mainwindow/MediathekGui.java index 2fea850523..ef46d8cc80 100644 --- a/src/main/java/mediathek/mainwindow/MediathekGui.java +++ b/src/main/java/mediathek/mainwindow/MediathekGui.java @@ -1,8 +1,5 @@ package mediathek.mainwindow; -import com.formdev.flatlaf.FlatLaf; -import com.formdev.flatlaf.extras.FlatAnimatedLafChange; -import com.formdev.flatlaf.ui.FlatUIUtils; import javafx.application.Platform; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; @@ -56,15 +53,11 @@ import org.jetbrains.annotations.NotNull; import javax.swing.*; -import javax.swing.text.StyleContext; import java.awt.*; -import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.NoSuchElementException; import java.util.concurrent.CompletableFuture; @@ -115,8 +108,7 @@ public class MediathekGui extends JFrame { private final MemoryMonitorAction showMemoryMonitorAction = new MemoryMonitorAction(this); private final InfoDialog filmInfo; private final ConfigureExternalUpdaterAction configureExternalUpdaterAction = new ConfigureExternalUpdaterAction(); - private final JMenu fontMenu = new JMenu("Schrift"); - private final String[] availableFontFamilyNames; + final JMenu fontMenu = new JMenu("Schrift"); public StatusBar swingStatusBar; public GuiFilme tabFilme; public GuiDownloads tabDownloads; @@ -141,6 +133,7 @@ public class MediathekGui extends JFrame { protected Configuration config = ApplicationConfiguration.getConfiguration(); protected JToolBar commonToolBar = new JToolBar(); protected ManageBookmarkAction manageBookmarkAction = new ManageBookmarkAction(this); + protected FontManager fontManager = new FontManager(this); /** * Bandwidth monitoring for downloads. */ @@ -154,15 +147,10 @@ public class MediathekGui extends JFrame { private IndicatorThread progressIndicatorThread; private ManageAboAction manageAboAction; private AutomaticFilmlistUpdate automaticFilmlistUpdate; - private int initialFontMenuItemCount = -1; public MediathekGui() { ui = this; - availableFontFamilyNames = GraphicsEnvironment.getLocalGraphicsEnvironment() - .getAvailableFontFamilyNames().clone(); - Arrays.sort( availableFontFamilyNames ); - setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); loadFilmListAction = new LoadFilmListAction(this); @@ -861,132 +849,22 @@ private void createFontMenu() { var restoreFontMenuItem = new JMenuItem(); restoreFontMenuItem.setText("Schrift zurücksetzen"); restoreFontMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_0, Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); - restoreFontMenuItem.addActionListener(e -> restoreFont()); + restoreFontMenuItem.addActionListener(e -> fontManager.resetFont()); fontMenu.add(restoreFontMenuItem); var incrFontMenuItem = new JMenuItem(); incrFontMenuItem.setText("Schrift vergrößern"); incrFontMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); - incrFontMenuItem.addActionListener(e -> increaseFontSize()); + incrFontMenuItem.addActionListener(e -> fontManager.increaseFontSize()); fontMenu.add(incrFontMenuItem); var decrFontMenuItem = new JMenuItem(); decrFontMenuItem.setText("Schrift verkleinern"); decrFontMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); - decrFontMenuItem.addActionListener(e -> decreaseFontSize()); + decrFontMenuItem.addActionListener(e -> fontManager.decreaseFontSize()); fontMenu.add(decrFontMenuItem); - updateFontMenuItems(); - } - - private void restoreFont() { - UIManager.put( "defaultFont", null ); - updateFontMenuItems(); - FlatLaf.updateUI(); - } - - private void increaseFontSize() { - Font font = UIManager.getFont( "defaultFont" ); - Font newFont = font.deriveFont( (float) (font.getSize() + 1) ); - UIManager.put( "defaultFont", newFont ); - - updateFontMenuItems(); - FlatLaf.updateUI(); - } - - private void decreaseFontSize() { - Font font = UIManager.getFont( "defaultFont" ); - Font newFont = font.deriveFont( (float) Math.max( font.getSize() - 1, 10 ) ); - UIManager.put( "defaultFont", newFont ); - - updateFontMenuItems(); - FlatLaf.updateUI(); - } - - private void updateFontMenuItems() { - if( initialFontMenuItemCount < 0 ) - initialFontMenuItemCount = fontMenu.getItemCount(); - else { - // remove old font items - for( int i = fontMenu.getItemCount() - 1; i >= initialFontMenuItemCount; i-- ) - fontMenu.remove( i ); - } - - // get current font - Font currentFont = UIManager.getFont( "Label.font" ); - String currentFamily = currentFont.getFamily(); - String currentSize = Integer.toString( currentFont.getSize() ); - - // add font families - fontMenu.addSeparator(); - ArrayList families = new ArrayList<>( Arrays.asList( - "Arial", "Cantarell", "Comic Sans MS", "DejaVu Sans", - "Dialog", "Liberation Sans", "Noto Sans", "Roboto", - "SansSerif", "Segoe UI", "Serif", "Tahoma", "Ubuntu", "Verdana" ) ); - if( !families.contains( currentFamily ) ) - families.add( currentFamily ); - families.sort( String.CASE_INSENSITIVE_ORDER ); - - ButtonGroup familiesGroup = new ButtonGroup(); - for( String family : families ) { - if( Arrays.binarySearch( availableFontFamilyNames, family ) < 0 ) - continue; // not available - - JCheckBoxMenuItem item = new JCheckBoxMenuItem( family ); - item.setSelected( family.equals( currentFamily ) ); - item.addActionListener( this::fontFamilyChanged ); - fontMenu.add( item ); - - familiesGroup.add( item ); - } - - // add font sizes - fontMenu.addSeparator(); - ArrayList sizes = new ArrayList<>( Arrays.asList( - "10", "11", "12", "14", "16", "18", "20", "24", "28" ) ); - if( !sizes.contains( currentSize ) ) - sizes.add( currentSize ); - sizes.sort( String.CASE_INSENSITIVE_ORDER ); - - ButtonGroup sizesGroup = new ButtonGroup(); - for( String size : sizes ) { - JCheckBoxMenuItem item = new JCheckBoxMenuItem( size ); - item.setSelected( size.equals( currentSize ) ); - item.addActionListener( this::fontSizeChanged ); - fontMenu.add( item ); - - sizesGroup.add( item ); - } - - // enabled/disable items - boolean enabled = UIManager.getLookAndFeel() instanceof FlatLaf; - for( Component item : fontMenu.getMenuComponents() ) - item.setEnabled( enabled ); - } - - private void fontFamilyChanged( ActionEvent e ) { - String fontFamily = e.getActionCommand(); - - FlatAnimatedLafChange.showSnapshot(); - - Font font = UIManager.getFont( "defaultFont" ); - Font newFont = StyleContext.getDefaultStyleContext().getFont( fontFamily, font.getStyle(), font.getSize() ); - // StyleContext.getFont() may return a UIResource, which would cause loosing user scale factor on Windows - newFont = FlatUIUtils.nonUIResource( newFont ); - UIManager.put( "defaultFont", newFont ); - - FlatLaf.updateUI(); - FlatAnimatedLafChange.hideSnapshotWithAnimation(); - } - - private void fontSizeChanged( ActionEvent e ) { - String fontSizeStr = e.getActionCommand(); - - Font font = UIManager.getFont( "defaultFont" ); - Font newFont = font.deriveFont( (float) Integer.parseInt( fontSizeStr ) ); - UIManager.put( "defaultFont", newFont ); - - FlatLaf.updateUI(); + fontManager.updateFontMenuItems(); } @Handler @@ -1012,8 +890,7 @@ private void createHelpMenu() { //do not show menu entry if we have external update support if (GuiFunktionen.isNotUsingExternalUpdater()) { jMenuHilfe.add(searchProgramUpdateAction); - } - else { + } else { // add install4j Update configurator (windows only) if (SystemUtils.IS_OS_WINDOWS) { jMenuHilfe.add(configureExternalUpdaterAction); From c95b190a3d13105e279022446ca53eb87a4ee760 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 14 Jul 2022 10:17:39 +0200 Subject: [PATCH 168/422] - convert abo dialog back to swing Former-commit-id: e291078e33f1092fdb5944f960b468f3b19fd8e2 --- .../java/mediathek/gui/abo/FXAboToolBar.kt | 49 ------------ .../mediathek/gui/abo/ManageAboPanel.java | 78 ++++++++++++------- .../gui/actions/CreateNewAboAction.kt | 1 + .../res/programm/fxml/abo/abo_toolbar.fxml | 66 ---------------- 4 files changed, 50 insertions(+), 144 deletions(-) delete mode 100644 src/main/java/mediathek/gui/abo/FXAboToolBar.kt delete mode 100644 src/main/resources/mediathek/res/programm/fxml/abo/abo_toolbar.fxml diff --git a/src/main/java/mediathek/gui/abo/FXAboToolBar.kt b/src/main/java/mediathek/gui/abo/FXAboToolBar.kt deleted file mode 100644 index fbd5f22459..0000000000 --- a/src/main/java/mediathek/gui/abo/FXAboToolBar.kt +++ /dev/null @@ -1,49 +0,0 @@ -package mediathek.gui.abo - -import ca.odell.glazedlists.javafx.EventObservableList -import javafx.fxml.FXML -import javafx.fxml.FXMLLoader -import javafx.fxml.Initializable -import javafx.scene.control.Button -import javafx.scene.control.ComboBox -import javafx.scene.control.ToolBar -import mediathek.tool.SenderListModel -import org.apache.logging.log4j.LogManager -import java.io.IOException -import java.net.URL -import java.util.* - -class FXAboToolBar : ToolBar(), Initializable { - @FXML lateinit var cbSender: ComboBox - - @FXML lateinit var btnOn: Button - - @FXML lateinit var btnOff: Button - - @FXML lateinit var btnDelete: Button - - @FXML lateinit var btnEdit: Button - - @FXML lateinit var btnNewAbo: Button - - override fun initialize(url: URL?, resourceBundle: ResourceBundle?) { - cbSender.items = EventObservableList(SenderListModel()) - cbSender.selectionModel.select(0) - } - - companion object { - private val logger = LogManager.getLogger() - } - - init { - try { - val url = FXAboToolBar::class.java.getResource("/mediathek/res/programm/fxml/abo/abo_toolbar.fxml") - val fxmlLoader = FXMLLoader(url) - fxmlLoader.setRoot(this) - fxmlLoader.setController(this) - fxmlLoader.load() - } catch (e: IOException) { - logger.error("Failed to load FXML!", e) - } - } -} \ No newline at end of file diff --git a/src/main/java/mediathek/gui/abo/ManageAboPanel.java b/src/main/java/mediathek/gui/abo/ManageAboPanel.java index 6bc1839587..b4ce51ce07 100644 --- a/src/main/java/mediathek/gui/abo/ManageAboPanel.java +++ b/src/main/java/mediathek/gui/abo/ManageAboPanel.java @@ -1,18 +1,17 @@ package mediathek.gui.abo; -import javafx.embed.swing.JFXPanel; -import javafx.scene.Scene; +import ca.odell.glazedlists.swing.GlazedListsSwing; import mediathek.config.Daten; import mediathek.daten.abo.AboTags; import mediathek.daten.abo.DatenAbo; import mediathek.gui.actions.CreateNewAboAction; import mediathek.gui.dialog.DialogEditAbo; import mediathek.gui.messages.AboListChangedEvent; -import mediathek.javafx.tool.JavaFxUtils; import mediathek.mainwindow.MediathekGui; import mediathek.tool.MessageBus; import mediathek.tool.NoSelectionErrorDialog; import mediathek.tool.SVGIconUtilities; +import mediathek.tool.SenderListModel; import mediathek.tool.cellrenderer.CellRendererAbo; import mediathek.tool.listener.BeobTableHeader; import mediathek.tool.models.TModelAbo; @@ -35,12 +34,12 @@ public class ManageAboPanel extends JPanel { private final MVTable tabelle = new MVAbosTable(); private final Daten daten; private final CreateNewAboAction createAboAction = new CreateNewAboAction(Daten.getInstance().getListeAbo()); - private final JFXPanel toolBarPanel = new JFXPanel(); private final JXStatusBar infoPanel = new JXStatusBar(); private final JLabel totalAbos = new JLabel("totalAbos"); private final JLabel activeAbos = new JLabel("activeAbos"); private final JLabel inactiveAbos = new JLabel("inactiveAbos"); - private FXAboToolBar toolBar; + private final JToolBar swingToolBar = new JToolBar(); + private final JComboBox senderCombo = new JComboBox<>(); private JScrollPane jScrollPane1; public ManageAboPanel() { @@ -100,20 +99,42 @@ private void initializeTable() { } private void setupToolBar() { - CreateNewAboAction newAboAction = new CreateNewAboAction(Daten.getInstance().getListeAbo()); - JavaFxUtils.invokeInFxThreadAndWait(() -> { - toolBar = new FXAboToolBar(); - toolBar.btnOn.setOnAction(e -> SwingUtilities.invokeLater(() -> changeAboActiveState(true))); - toolBar.btnOff.setOnAction(e -> SwingUtilities.invokeLater(() -> changeAboActiveState(false))); - toolBar.btnDelete.setOnAction(e -> SwingUtilities.invokeLater(this::aboLoeschen)); - toolBar.btnEdit.setOnAction(e -> SwingUtilities.invokeLater(this::editAbo)); - - toolBar.btnNewAbo.setOnAction(e -> SwingUtilities.invokeLater(() -> newAboAction.actionPerformed(null))); - - toolBar.cbSender.setOnAction(e -> SwingUtilities.invokeLater(this::tabelleLaden)); - - toolBarPanel.setScene(new Scene(toolBar)); - }); + JButton button = new JButton(); + button.setToolTipText("Abos einschalten"); + button.addActionListener(l -> changeAboActiveState(true)); + button.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/check.svg")); + swingToolBar.add(button); + + button = new JButton(); + button.setToolTipText("Abos ausschalten"); + button.addActionListener(l -> changeAboActiveState(false)); + button.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/xmark.svg", 16f)); + swingToolBar.add(button); + swingToolBar.addSeparator(); + + button = new JButton(createAboAction); + button.setText(""); + swingToolBar.add(button); + + button = new JButton(); + button.setToolTipText("Abos löschen"); + button.addActionListener(l -> aboLoeschen()); + button.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/trash-can.svg")); + swingToolBar.add(button); + + button = new JButton(); + button.setToolTipText("Abo ändern"); + button.addActionListener(l -> editAbo()); + button.setIcon(SVGIconUtilities.createSVGIcon("icons/fontawesome/pen-to-square.svg")); + swingToolBar.add(button); + swingToolBar.addSeparator(); + + swingToolBar.add(new JLabel("Abos für Sender:")); + senderCombo.setMaximumSize(new Dimension(150, Integer.MAX_VALUE)); + senderCombo.setModel(GlazedListsSwing.eventComboBoxModel(new SenderListModel())); + senderCombo.setSelectedIndex(0); + senderCombo.addActionListener(l -> tabelleLaden()); + swingToolBar.add(senderCombo); } public void tabelleSpeichern() { @@ -232,15 +253,14 @@ private void initListeners() { private void tabelleLaden() { tabelle.getSpalten(); - JavaFxUtils.invokeInFxThreadAndWait(() -> { - final String selectedItem = toolBar.cbSender.getValue(); - if (selectedItem != null) { - SwingUtilities.invokeLater(() -> { - addObjectData((TModelAbo) tabelle.getModel(), selectedItem); - tabelle.setSpalten(); - }); - } - }); + String selectedItem = null; + var item = senderCombo.getSelectedItem(); + if (item != null) + selectedItem = item.toString(); + if (selectedItem != null) { + addObjectData((TModelAbo) tabelle.getModel(), selectedItem); + tabelle.setSpalten(); + } } private void aboLoeschen() { @@ -368,7 +388,7 @@ private void initComponents() { jTable1.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); jScrollPane1.setViewportView(jTable1); - add(toolBarPanel, BorderLayout.NORTH); + add(swingToolBar, BorderLayout.NORTH); add(jScrollPane1, BorderLayout.CENTER); add(infoPanel, BorderLayout.SOUTH); } diff --git a/src/main/java/mediathek/gui/actions/CreateNewAboAction.kt b/src/main/java/mediathek/gui/actions/CreateNewAboAction.kt index f5d802565d..f59810a768 100644 --- a/src/main/java/mediathek/gui/actions/CreateNewAboAction.kt +++ b/src/main/java/mediathek/gui/actions/CreateNewAboAction.kt @@ -12,6 +12,7 @@ class CreateNewAboAction(private val listeAbo: ListeAbo) : AbstractAction() { init { putValue(NAME, "Abo anlegen...") + putValue(SHORT_DESCRIPTION, "Abo anlegen") putValue(SMALL_ICON, SVGIconUtilities.createSVGIcon("icons/fontawesome/plus.svg")) } } \ No newline at end of file diff --git a/src/main/resources/mediathek/res/programm/fxml/abo/abo_toolbar.fxml b/src/main/resources/mediathek/res/programm/fxml/abo/abo_toolbar.fxml deleted file mode 100644 index 43c5669bd6..0000000000 --- a/src/main/resources/mediathek/res/programm/fxml/abo/abo_toolbar.fxml +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - -
                              -
                              -
                              - -
                              -
                              From 05a34d4d179b6fb7ab5d4f0e83dd080deebb18d7 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 14 Jul 2022 10:37:18 +0200 Subject: [PATCH 169/422] - reduce size of sender icons a bit Former-commit-id: 25a29ef664c7e2c936e3d5d99f5d0bb8b52e54f1 --- .../java/mediathek/tool/cellrenderer/CellRendererBase.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/mediathek/tool/cellrenderer/CellRendererBase.java b/src/main/java/mediathek/tool/cellrenderer/CellRendererBase.java index daf3116430..867ce21cdb 100644 --- a/src/main/java/mediathek/tool/cellrenderer/CellRendererBase.java +++ b/src/main/java/mediathek/tool/cellrenderer/CellRendererBase.java @@ -26,6 +26,10 @@ public class CellRendererBase extends DefaultTableCellRenderer { * @param sender Name of the sender. */ protected void setSenderIcon(@NotNull String sender, @NotNull Dimension targetDim) { + // make target dims for icon slightly smaller + targetDim.width -= 4; + targetDim.height -= 4; + var key = new SenderCacheKey(sender, targetDim); final AtomicReference cachedIcon = new AtomicReference<>(); cachedIcon.set(senderCellIconCache.getOrDefault(key, null)); @@ -39,10 +43,11 @@ protected void setSenderIcon(@NotNull String sender, @NotNull Dimension targetDi } if (cachedIcon.get() != null) { - setHorizontalAlignment(SwingConstants.CENTER); setText(""); setIcon(cachedIcon.get()); } + setVerticalAlignment(SwingConstants.CENTER); + setHorizontalAlignment(SwingConstants.CENTER); } /** From 02eabccd9e1b1cdc1887821aa1c66a75b0a0ea6a Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 14 Jul 2022 10:43:48 +0200 Subject: [PATCH 170/422] - reduce size of sender icons only on linux Former-commit-id: a0bdfd1783b8233065c1afad102a49858a07d82c --- .../java/mediathek/tool/cellrenderer/CellRendererBase.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/mediathek/tool/cellrenderer/CellRendererBase.java b/src/main/java/mediathek/tool/cellrenderer/CellRendererBase.java index 867ce21cdb..34d46b7a34 100644 --- a/src/main/java/mediathek/tool/cellrenderer/CellRendererBase.java +++ b/src/main/java/mediathek/tool/cellrenderer/CellRendererBase.java @@ -3,6 +3,7 @@ import com.formdev.flatlaf.util.ScaledImageIcon; import mediathek.tool.GuiFunktionen; import mediathek.tool.sender_icon_cache.MVSenderIconCache; +import org.apache.commons.lang3.SystemUtils; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -27,8 +28,10 @@ public class CellRendererBase extends DefaultTableCellRenderer { */ protected void setSenderIcon(@NotNull String sender, @NotNull Dimension targetDim) { // make target dims for icon slightly smaller - targetDim.width -= 4; - targetDim.height -= 4; + if (SystemUtils.IS_OS_LINUX) { + targetDim.width -= 4; + targetDim.height -= 4; + } var key = new SenderCacheKey(sender, targetDim); final AtomicReference cachedIcon = new AtomicReference<>(); From a135e8ee785737c673ee4aaf7b8e561a23361411 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 14 Jul 2022 11:07:54 +0200 Subject: [PATCH 171/422] -convert to swing Former-commit-id: d129b2d239ba9c96951bb06bc88fa457c6aa3613 --- .../mediathek/update/DialogHinweisUpdate.java | 87 ++++++++----------- .../mediathek/update/DialogHinweisUpdate.jfd | 22 +++-- .../update/ProgrammUpdateSuchen.java | 1 - 3 files changed, 50 insertions(+), 60 deletions(-) diff --git a/src/main/java/mediathek/update/DialogHinweisUpdate.java b/src/main/java/mediathek/update/DialogHinweisUpdate.java index f35f7cc925..64c1b1b502 100644 --- a/src/main/java/mediathek/update/DialogHinweisUpdate.java +++ b/src/main/java/mediathek/update/DialogHinweisUpdate.java @@ -19,28 +19,24 @@ */ package mediathek.update; -import javafx.application.Platform; -import javafx.embed.swing.JFXPanel; -import javafx.geometry.Insets; -import javafx.scene.Scene; -import javafx.scene.control.Hyperlink; -import javafx.scene.layout.Background; -import javafx.scene.layout.BackgroundFill; -import javafx.scene.layout.CornerRadii; -import javafx.scene.paint.Color; import mediathek.config.Konstanten; import mediathek.gui.actions.UrlHyperlinkAction; import mediathek.tool.EscapeKeyHandler; +import net.miginfocom.layout.AC; +import net.miginfocom.layout.CC; +import net.miginfocom.layout.LC; +import net.miginfocom.swing.MigLayout; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.jdesktop.swingx.JXHyperlink; import javax.swing.*; +import java.awt.*; import java.net.URISyntaxException; -@SuppressWarnings("serial") public class DialogHinweisUpdate extends JDialog { - private static final Logger logger = LogManager.getLogger(DialogHinweisUpdate.class); + private static final Logger logger = LogManager.getLogger(); public DialogHinweisUpdate(JFrame parent, String ttext) { super(parent, true); @@ -52,20 +48,15 @@ public DialogHinweisUpdate(JFrame parent, String ttext) { jButtonOk.addActionListener(e -> dispose()); jTextArea1.setText(ttext); - Platform.runLater(() -> { - Hyperlink link = new Hyperlink("Link zur Website"); - link.setBackground(new Background(new BackgroundFill(Color.rgb(236,236,236), CornerRadii.EMPTY, Insets.EMPTY))); - link.setOnAction(e -> SwingUtilities.invokeLater(() -> { - try { - UrlHyperlinkAction.openURL(parent, Konstanten.ADRESSE_DOWNLOAD); - } catch (URISyntaxException ex) { - logger.error(ex); - } - })); - hyperLinkPanel.setScene(new Scene(link)); + hyperLink.addActionListener(l -> { + try { + UrlHyperlinkAction.openURL(parent, Konstanten.ADRESSE_DOWNLOAD); + } catch (URISyntaxException ex) { + logger.error(ex); + } }); - - setSize(450,getHeight()); + //setSize(450,getHeight()); + pack(); } /** This method is called from within the constructor to @@ -79,12 +70,23 @@ private void initComponents() { var jScrollPane1 = new JScrollPane(); jTextArea1 = new JTextArea(); jButtonOk = new JButton(); - hyperLinkPanel = new JFXPanel(); + hyperLink = new JXHyperlink(); //======== this ======== setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); setTitle("Programminformationen"); //NON-NLS + setMinimumSize(new Dimension(480, 100)); var contentPane = getContentPane(); + contentPane.setLayout(new MigLayout( + new LC().insets("5").hideMode(3).gridGap("5", "5"), //NON-NLS + // columns + new AC() + .grow().fill(), + // rows + new AC() + .grow().fill().gap() + .fill().gap() + .fill())); //======== jScrollPane1 ======== { @@ -96,39 +98,18 @@ private void initComponents() { jTextArea1.setRows(5); jTextArea1.setText("\n\n"); //NON-NLS jTextArea1.setWrapStyleWord(true); + jTextArea1.setMinimumSize(new Dimension(459, 85)); jScrollPane1.setViewportView(jTextArea1); } + contentPane.add(jScrollPane1, new CC().cell(0, 0)); //---- jButtonOk ---- jButtonOk.setText("Schlie\u00dfen"); //NON-NLS + contentPane.add(jButtonOk, new CC().cell(0, 2).alignX("right").growX(0)); //NON-NLS - GroupLayout contentPaneLayout = new GroupLayout(contentPane); - contentPane.setLayout(contentPaneLayout); - contentPaneLayout.setHorizontalGroup( - contentPaneLayout.createParallelGroup() - .addGroup(GroupLayout.Alignment.TRAILING, contentPaneLayout.createSequentialGroup() - .addGroup(contentPaneLayout.createParallelGroup(GroupLayout.Alignment.TRAILING) - .addComponent(hyperLinkPanel, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(contentPaneLayout.createSequentialGroup() - .addContainerGap() - .addGroup(contentPaneLayout.createParallelGroup(GroupLayout.Alignment.TRAILING) - .addGroup(contentPaneLayout.createSequentialGroup() - .addGap(0, 0, Short.MAX_VALUE) - .addComponent(jButtonOk)) - .addComponent(jScrollPane1, GroupLayout.Alignment.LEADING)))) - .addGap(5, 5, 5)) - ); - contentPaneLayout.setVerticalGroup( - contentPaneLayout.createParallelGroup() - .addGroup(GroupLayout.Alignment.TRAILING, contentPaneLayout.createSequentialGroup() - .addContainerGap() - .addComponent(jScrollPane1, GroupLayout.DEFAULT_SIZE, 208, Short.MAX_VALUE) - .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) - .addComponent(hyperLinkPanel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .addGap(2, 2, 2) - .addComponent(jButtonOk) - .addContainerGap()) - ); + //---- hyperLink ---- + hyperLink.setText("Link zur Webseite"); //NON-NLS + contentPane.add(hyperLink, new CC().cell(0, 1)); pack(); setLocationRelativeTo(getOwner()); }// //GEN-END:initComponents @@ -136,6 +117,6 @@ private void initComponents() { // Generated using JFormDesigner non-commercial license private JTextArea jTextArea1; private JButton jButtonOk; - private JFXPanel hyperLinkPanel; + private JXHyperlink hyperLink; // End of variables declaration//GEN-END:variables } diff --git a/src/main/java/mediathek/update/DialogHinweisUpdate.jfd b/src/main/java/mediathek/update/DialogHinweisUpdate.jfd index 1d6370e226..0629419d1a 100644 --- a/src/main/java/mediathek/update/DialogHinweisUpdate.jfd +++ b/src/main/java/mediathek/update/DialogHinweisUpdate.jfd @@ -1,15 +1,17 @@ -JFDML JFormDesigner: "7.0.0.0.142" Java: "1.8.0_152-release" encoding: "UTF-8" +JFDML JFormDesigner: "7.0.5.1.409" Java: "11.0.15" encoding: "UTF-8" new FormModel { contentType: "form/swing" root: new FormRoot { - add( new FormWindow( "javax.swing.JDialog", new FormLayoutManager( class org.jdesktop.layout.GroupLayout ) { - "$horizontalGroup": "par l {seq t {par t {comp hyperLinkPanel:::::x, seq {space :::p, par t {seq t {space :0:0:x, comp jButtonOk:::p::p}, comp jScrollPane1::l:::x}}}, space :p:5:p}}" - "$verticalGroup": "par l {seq t {space :p::p, comp jScrollPane1::::208:x, space :::p, comp hyperLinkPanel:::p::p, space :p:2:p, comp jButtonOk:::p::p, space :::p}}" + add( new FormWindow( "javax.swing.JDialog", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { + "$layoutConstraints": "insets 5,hidemode 3,gap 5 5" + "$columnConstraints": "[grow,fill]" + "$rowConstraints": "[grow,fill][fill][fill]" } ) { name: "this" "defaultCloseOperation": 2 "title": "Programminformationen" + "minimumSize": new java.awt.Dimension( 480, 100 ) add( new FormContainer( "javax.swing.JScrollPane", new FormLayoutManager( class javax.swing.JScrollPane ) ) { name: "jScrollPane1" auxiliary() { @@ -23,14 +25,22 @@ new FormModel { "rows": 5 "text": "\n\n" "wrapStyleWord": true + "minimumSize": new java.awt.Dimension( 459, 85 ) } ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" } ) add( new FormComponent( "javax.swing.JButton" ) { name: "jButtonOk" "text": "Schließen" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 2,alignx right,growx 0" } ) - add( new FormComponent( "javafx.embed.swing.JFXPanel" ) { - name: "hyperLinkPanel" + add( new FormComponent( "org.jdesktop.swingx.JXHyperlink" ) { + name: "hyperLink" + "text": "Link zur Webseite" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 1" } ) }, new FormLayoutConstraints( null ) { "size": new java.awt.Dimension( 475, 280 ) diff --git a/src/main/java/mediathek/update/ProgrammUpdateSuchen.java b/src/main/java/mediathek/update/ProgrammUpdateSuchen.java index e501926a5b..e96900f6d6 100644 --- a/src/main/java/mediathek/update/ProgrammUpdateSuchen.java +++ b/src/main/java/mediathek/update/ProgrammUpdateSuchen.java @@ -96,7 +96,6 @@ private void displayInfoMessages(boolean showAll, boolean silent) { } } if (!text.isEmpty()) { - //TODO add new dialog with web view here! JDialog dlg = new DialogHinweisUpdate(null, text.toString()); dlg.setVisible(true); MVConfig.add(MVConfig.Configs.SYSTEM_HINWEIS_NR_ANGEZEIGT, Integer.toString(index)); From 1b7ff1e9bc866aec476b33b56df523cedeff9eac Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 14 Jul 2022 11:55:07 +0200 Subject: [PATCH 172/422] - remove unneeded JavaFX init workaround Former-commit-id: 76b322b39fe9c7b45c7db6226a9e7167f9e41c8d --- src/main/java/mediathek/Main.java | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/main/java/mediathek/Main.java b/src/main/java/mediathek/Main.java index ddbe9dfe08..c00ccd3186 100644 --- a/src/main/java/mediathek/Main.java +++ b/src/main/java/mediathek/Main.java @@ -4,7 +4,6 @@ import com.formdev.flatlaf.FlatLightLaf; import com.sun.jna.platform.win32.VersionHelpers; import javafx.application.Platform; -import javafx.embed.swing.JFXPanel; import mediathek.config.*; import mediathek.controller.history.SeenHistoryMigrator; import mediathek.gui.dialog.DialogStarteinstellungen; @@ -428,7 +427,7 @@ public static void main(final String... args) { setupCpuAffinity(); - initializeJavaFX(); + Platform.setImplicitExit(false); removeMediaDb(); @@ -506,14 +505,6 @@ private static void migrateSeenHistory() { } } - @SuppressWarnings("unused") - private static void initializeJavaFX() { - //JavaFX stuff - Platform.setImplicitExit(false); - //necessary to init JavaFX before loading config data - var dummy = new JFXPanel(); - } - private static void loadConfigurationData() { var daten = Daten.getInstance(); From 6bc21520b348410f23b537ec82fb4cb78b910018 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 14 Jul 2022 12:56:38 +0200 Subject: [PATCH 173/422] - fix exception when reading incorrect Date lines in downloadAbos.txt Former-commit-id: 32a6b876237bbb13dbc56b54d3ea77a82807cf37 --- CHANGELOG.md | 1 + .../java/mediathek/controller/history/MVUsedUrl.java | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 795b2f72db..0cc6eb1790 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - **BUGFIX:** Manchmal inkorrekte Darstellung des Bookmark-Status im Film-Tab wurde korrigiert. - **BUGFIX:** Sendericons werden nun vollständig und hochauflösend gerendert. - **BUGFIX:** Update-Möglichkeit innerhalb des Programms wird nun richtig ein- und ausgeblendet, je nach verwendetem Installationstyp. +- **BUGFIX:** Potentieller Absturz beim Einlesen der Abohistorie wurde behoben. - **FEATURE:** Die Toolbars können nun zum Schweben in allen Tabs abgetrennt werden. - **FEATURE:** Tab Filmbeschreibung wurde neu designed und stellt nun auch das Sendericon dar. - **FEATURE:** Icons wurden durch hochauflösende Vektorvarianten ersetzt. diff --git a/src/main/java/mediathek/controller/history/MVUsedUrl.java b/src/main/java/mediathek/controller/history/MVUsedUrl.java index 2e0f759deb..34510baee1 100644 --- a/src/main/java/mediathek/controller/history/MVUsedUrl.java +++ b/src/main/java/mediathek/controller/history/MVUsedUrl.java @@ -23,6 +23,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import java.time.DateTimeException; import java.time.LocalDate; import java.time.format.DateTimeFormatter; @@ -41,7 +42,16 @@ public class MVUsedUrl { private final String url; public MVUsedUrl(String date, String thema, String title, String url) { - this.datum = LocalDate.parse(date, DATE_TIME_FORMATTER); + LocalDate tempDate; + try { + tempDate = LocalDate.parse(date, DATE_TIME_FORMATTER); + } + catch (DateTimeException ex) { + logger.error("Failed to parse date: \"{}\" for thema: \"{}\", titel: \"{}\" and URL: \"{}\"", date, thema, title, url); + logger.error("Resetting date to current date..."); + tempDate = LocalDate.now(); + } + this.datum = tempDate; this.thema = thema; this.titel = title; this.url = url; From 3c5cb6fbdac4e51d68023019b5ffa0fd80ddb05d Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 14 Jul 2022 13:22:23 +0200 Subject: [PATCH 174/422] - cleanup/delete old unused database files from older MV versions Former-commit-id: 34918895755cfa925a6973cf51ee49d85ce0d4a3 --- src/main/java/mediathek/Main.java | 33 +++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/main/java/mediathek/Main.java b/src/main/java/mediathek/Main.java index c00ccd3186..4d36d8201a 100644 --- a/src/main/java/mediathek/Main.java +++ b/src/main/java/mediathek/Main.java @@ -430,6 +430,8 @@ public static void main(final String... args) { Platform.setImplicitExit(false); removeMediaDb(); + deleteOldFilmDatabaseFiles(); + deleteOldUserAgentsDatabase(); JFXHiddenApplication.launchApplication(); checkMemoryRequirements(); @@ -528,6 +530,37 @@ private static void loadConfigurationData() { } } + private static void deleteOldUserAgentsDatabase() { + try { + var settingsPath = StandardLocations.getSettingsDirectory(); + var agentDb = settingsPath.resolve("user_agents.mv.db"); + Files.deleteIfExists(agentDb); + } + catch (IOException e) { + logger.error("Error deleting old user agent database occured", e); + } + } + + private static void deleteOldFilmDatabaseFiles() { + var settingsPath = StandardLocations.getSettingsDirectory(); + var dbFolder = settingsPath.resolve("database"); + var traceFile = settingsPath.resolve("databasemediathekview.trace.db"); + + if (!Files.exists(dbFolder)) + return; + + try (var walk = Files.walk(dbFolder)) { + walk.sorted(Comparator.reverseOrder()) + .map(Path::toFile) + //.peek(System.out::println) + .forEach(File::delete); + + Files.deleteIfExists(traceFile); + } + catch (Exception ex) { + logger.error("Got an error deleting old database directory", ex); + } + } @SuppressWarnings("ResultOfMethodCallIgnored") private static void deleteSettingsDirectory() { try (var walk = Files.walk(StandardLocations.getSettingsDirectory())) { From 1c7b59fc6a5a27b34594556f01a679d7847199db Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 14 Jul 2022 13:49:01 +0200 Subject: [PATCH 175/422] - reduce number of used threads in TimerPool.kt Former-commit-id: 750119456936ecc1d034e58654ff04591c3c726d --- src/main/java/mediathek/tool/TimerPool.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/mediathek/tool/TimerPool.kt b/src/main/java/mediathek/tool/TimerPool.kt index 9109323a92..6c56e3191a 100644 --- a/src/main/java/mediathek/tool/TimerPool.kt +++ b/src/main/java/mediathek/tool/TimerPool.kt @@ -13,16 +13,14 @@ object TimerPool { private val logger: Logger = LogManager.getLogger() @JvmStatic - val timerPool = - ScheduledThreadPoolExecutor((Runtime.getRuntime().availableProcessors() / 2).coerceIn(2, 4), - TimerPoolThreadFactory()) + val timerPool = ScheduledThreadPoolExecutor(2, TimerPoolThreadFactory()) init { logger.trace("Initializing timer pool...") //get rid of cancelled tasks immediately... timerPool.removeOnCancelPolicy = true timerPool.allowCoreThreadTimeOut(true) - timerPool.setKeepAliveTime(1, TimeUnit.MINUTES) + timerPool.setKeepAliveTime(30, TimeUnit.SECONDS) timerPool.scheduleWithFixedDelay({ messageBus.publishAsync(TimerEvent()) }, 4, 1, TimeUnit.SECONDS) } From 62b2d3b2d0e12d63fb423bd5de6113f74892bd1e Mon Sep 17 00:00:00 2001 From: Alexander F Date: Thu, 14 Jul 2022 15:23:39 +0200 Subject: [PATCH 176/422] =?UTF-8?q?Java=2018=20jdk=20f=C3=BCr=20adoptium?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Former-commit-id: e2a22fa3664d948249fd4e39d9289da7cb00d998 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8e53f01d14..6959b8b4a7 100755 --- a/pom.xml +++ b/pom.xml @@ -126,7 +126,7 @@ mediathek.Main - 17/jdk-17.0.2+8 + 18/jdk-18.0.1+10 18/18.0.1 18 From 704352f6271d9db366f7c446fb21f475d59e68b8 Mon Sep 17 00:00:00 2001 From: Alexander F Date: Fri, 15 Jul 2022 08:03:07 +0200 Subject: [PATCH 177/422] =?UTF-8?q?Java=20Version=20auf=2018=20f=C3=BCr=20?= =?UTF-8?q?nightly=20builds=20erh=C3=B6ht?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Former-commit-id: 07032a6b9108709d25b4283e3edd3af17d1f83bf --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3a015fdac9..8245efb599 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -3,7 +3,7 @@ variables: MAVEN_OPTS: "-Djava.awt.headless=true -Dmaven.repo.local=./.m2/repository" MAVEN_CLI_OPTS: "-B -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn" -image: maven:3.8-eclipse-temurin-17 +image: maven:3.8-eclipse-temurin-18 cache: From 3a22b659426c324b29e9602f06b2e8e1368c5eec Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 15 Jul 2022 11:46:57 +0200 Subject: [PATCH 178/422] - reduce memory footprint of DatenFilm Former-commit-id: f2edff88ec68c6a03ad6bed2efcf786e029409e9 --- src/main/java/mediathek/daten/DatenFilm.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/mediathek/daten/DatenFilm.java b/src/main/java/mediathek/daten/DatenFilm.java index d90142db5f..bab805b468 100644 --- a/src/main/java/mediathek/daten/DatenFilm.java +++ b/src/main/java/mediathek/daten/DatenFilm.java @@ -74,7 +74,7 @@ public class DatenFilm implements Comparable { /** * film length in seconds. */ - private long filmLength; + private int filmLength; private String websiteLink; private String description; /** @@ -381,7 +381,7 @@ public int compareTo(@NotNull DatenFilm other) { * * @return filmlength in seconds, or 0. */ - public long getFilmLength() { + public int getFilmLength() { return filmLength; } @@ -391,8 +391,8 @@ public long getFilmLength() { * * @return result in seconds or 0. */ - private long parseTimeToSeconds() { - long seconds = 0; + private int parseTimeToSeconds() { + int seconds = 0; final String[] split = StringUtils.split(dauer, ':'); // if empty, don't try to split and return early... if (split == null || split.length == 0) { @@ -400,9 +400,9 @@ private long parseTimeToSeconds() { } else { try { - seconds += Long.parseLong(split[0]) * 3600; //hour - seconds += Long.parseLong(split[1]) * 60; //minute - seconds += Long.parseLong(split[2]); //second + seconds += Integer.parseInt(split[0]) * 3600; //hour + seconds += Integer.parseInt(split[1]) * 60; //minute + seconds += Integer.parseInt(split[2]); //second } catch (Exception e) { seconds = 0; } From 81a4cb62c18cbd3225eedefa3e5cd2c9c0feacd9 Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 15 Jul 2022 13:11:31 +0200 Subject: [PATCH 179/422] - fix incorrect Film Test Former-commit-id: 5751a90396d8c008febf54b2b9c084d62fb4b408 --- .../java/mSearch/daten/DatenFilmTest.java | 25 +++++-------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/src/test/java/mSearch/daten/DatenFilmTest.java b/src/test/java/mSearch/daten/DatenFilmTest.java index b003e474ca..92a488a1dd 100644 --- a/src/test/java/mSearch/daten/DatenFilmTest.java +++ b/src/test/java/mSearch/daten/DatenFilmTest.java @@ -1,37 +1,24 @@ package mSearch.daten; import mediathek.daten.DatenFilm; -import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; import java.util.stream.Stream; -import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.params.provider.Arguments.arguments; -import static org.mockito.Mockito.spy; @ExtendWith(MockitoExtension.class) class DatenFilmTest { - @Spy - static DatenFilm testEntity; - - @BeforeAll - static void initializeTestEntity() { - DatenFilm df = new DatenFilm(); - testEntity = spy(df); - - } - static Stream filmLengthEdgeCases() { return Stream.of( arguments("01:21:30", 4890L), - arguments((String)null, 0L), // Die Methode fängt alle Exceptions + arguments(null, 0L), // Die Methode fängt alle Exceptions arguments("01:91:65", 9125L), // Minuten und Sekungen > 59 werden auch verarbeitet! arguments("01:31", 0L), // Es müssen immer Stunden:Minuten:Sekunden eingegeben werden arguments("1:0:0", 3600L), // Es müssen keine führenden Nullen verwendet werden @@ -42,12 +29,12 @@ static Stream filmLengthEdgeCases() { @ParameterizedTest @MethodSource("filmLengthEdgeCases") void testFilmLengthCalculation(String input, long expected) { + DatenFilm df = new DatenFilm(); - testEntity.setDauer(input); - testEntity.init(); - - assertThat(expected).isEqualTo(testEntity.getFilmLength()); + df.setDauer(input); + df.init(); + Assertions.assertEquals(expected, df.getFilmLength()); } } \ No newline at end of file From 6cdd618baa3fa68a3f82f577f90b06504bec8d48 Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 15 Jul 2022 14:59:42 +0200 Subject: [PATCH 180/422] - Optimize memory footprint and code refactor - remove groesse variable - unify duration and filmLength variables Former-commit-id: 6af5a5a510d05736f2a69f60af2798a1d03c0fdd --- .../starter/DirectHttpDownload.java | 2 +- .../java/mediathek/daten/DatenDownload.java | 6 +- src/main/java/mediathek/daten/DatenFilm.java | 157 +++++++----------- .../daten/blacklist/ListeBlacklist.java | 2 +- .../filmlisten/reader/FilmListReader.java | 4 +- .../filmlisten/writer/FilmListWriter.java | 4 +- .../gui/dialog/DialogAddDownload.java | 6 +- .../gui/dialog/DialogEditDownload.java | 6 +- .../gui/filmInformation/InfoDialog.kt | 4 +- .../tabs/tab_film/GuiFilmeModelHelper.java | 2 +- .../javafx/bookmark/BookmarkData.java | 4 +- src/main/java/mediathek/tool/FilmSize.java | 19 +-- src/main/java/mediathek/tool/MVInfoFile.kt | 2 +- .../tool/cellrenderer/CellRendererFilme.java | 2 +- .../mediathek/tool/models/TModelFilm.java | 4 +- .../java/mSearch/daten/DatenFilmTest.java | 2 +- 16 files changed, 93 insertions(+), 133 deletions(-) diff --git a/src/main/java/mediathek/controller/starter/DirectHttpDownload.java b/src/main/java/mediathek/controller/starter/DirectHttpDownload.java index 009768c58d..4ebf795b04 100644 --- a/src/main/java/mediathek/controller/starter/DirectHttpDownload.java +++ b/src/main/java/mediathek/controller/starter/DirectHttpDownload.java @@ -429,7 +429,7 @@ private boolean abbrechen_() { private void bereitsAnschauen(DatenDownload datenDownload) { if (datenDownload.film != null && datenDownload.start != null) { - final long filmLength = datenDownload.film.getFilmLength(); + var filmLength = datenDownload.film.getFilmLength(); if (filmLength > 0 && datenDownload.start.restSekunden > 0 && datenDownload.mVFilmSize.getAktSize() > 0 diff --git a/src/main/java/mediathek/daten/DatenDownload.java b/src/main/java/mediathek/daten/DatenDownload.java index 34c4a6edee..164abe1e33 100644 --- a/src/main/java/mediathek/daten/DatenDownload.java +++ b/src/main/java/mediathek/daten/DatenDownload.java @@ -126,7 +126,7 @@ public DatenDownload(DatenPset pSet, DatenFilm film, byte quelle, DatenAbo abo, arr[DOWNLOAD_URL_SUBTITLE] = film.getUrlSubtitle(); arr[DOWNLOAD_DATUM] = film.getSendeDatum(); arr[DOWNLOAD_ZEIT] = film.getSendeZeit(); - arr[DOWNLOAD_DAUER] = film.getDauer(); + arr[DOWNLOAD_DAUER] = film.getFilmLengthAsString(); arr[DOWNLOAD_HD] = film.isHighQuality() ? "1" : "0"; arr[DOWNLOAD_UT] = film.hasSubtitle() ? "1" : "0"; arr[DOWNLOAD_QUELLE] = String.valueOf(quelle); @@ -351,7 +351,7 @@ public void writeConfigEntry(XMLStreamWriter writer) { public void setGroesseFromFilm() { if (film != null) { if (film.getUrlNormalQuality().equals(arr[DOWNLOAD_URL])) { - mVFilmSize.setSize(film.getSize()); + mVFilmSize.setSize(film.getFileSize().toString()); } else { mVFilmSize.setSize(0); } @@ -363,7 +363,7 @@ public void setGroesse(@NotNull String groesse) { if (!groesse.isEmpty()) { mVFilmSize.setSize(groesse); } else { - mVFilmSize.setSize(film.getDateigroesse(arr[DOWNLOAD_URL])); + mVFilmSize.setSize(film.getFileSizeForUrl(arr[DOWNLOAD_URL])); } } } diff --git a/src/main/java/mediathek/daten/DatenFilm.java b/src/main/java/mediathek/daten/DatenFilm.java index bab805b468..b7b6000151 100644 --- a/src/main/java/mediathek/daten/DatenFilm.java +++ b/src/main/java/mediathek/daten/DatenFilm.java @@ -7,6 +7,7 @@ import mediathek.tool.GermanStringSorter; import mediathek.tool.datum.DatumFilm; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.time.DurationFormatUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; @@ -70,11 +71,7 @@ public class DatenFilm implements Comparable { /** * File size in MByte */ - private FilmSize filmSize; - /** - * film length in seconds. - */ - private int filmLength; + private FilmSize filmSize = new FilmSize(); private String websiteLink; private String description; /** @@ -100,20 +97,16 @@ public class DatenFilm implements Comparable { private Optional subtitle_url = Optional.empty(); private String datum = ""; private String sendeZeit = ""; - private String dauer = ""; - private String groesse = ""; /** * Normal quality URL. */ private String url_normal_quality = ""; /** - * film duration in seconds. - * getDauer() stores the same info as a String + * film duration or film length in seconds. */ - private int duration; + private int filmLength; public DatenFilm() { - filmSize = new FilmSize(0); // Dateigröße in MByte databaseFilmNumber = FILM_COUNTER.getAndIncrement(); } @@ -122,7 +115,6 @@ public DatenFilm(@NotNull DatenFilm other) { this.bookmark = other.bookmark; this.datumFilm = other.datumFilm; this.filmSize = other.filmSize; - this.filmLength = other.filmLength; this.databaseFilmNumber = other.databaseFilmNumber; this.websiteLink = other.websiteLink; this.description = other.description; @@ -136,14 +128,51 @@ public DatenFilm(@NotNull DatenFilm other) { this.subtitle_url = other.subtitle_url; this.datum = other.datum; this.sendeZeit = other.sendeZeit; - this.dauer = other.dauer; - this.groesse = other.groesse; this.url_normal_quality = other.url_normal_quality; - this.duration = other.duration; + this.filmLength = other.filmLength; + } + + /** + * URLs are considered compressed if they contain a '|'-symbol in the text. + * They need to be decompressed before use. + * @param requestedUrl the string to be checked. + * @return true if url is compressed, false otherwise. + */ + public static boolean isUrlCompressed(@NotNull String requestedUrl) { + final int indexPipe = requestedUrl.indexOf(COMPRESSION_MARKER); + return indexPipe != -1; + } + + /** + * Get the filmlength or duration. + * + * @return filmlength/duration in seconds, or 0. + */ + public int getFilmLength() { + return filmLength; } - public int getDuration() { - return duration; + /** + * Set the film's length or duration. + * + * @param dauer Input string in format "HH:MM:SS". + */ + public void setFilmLength(String dauer) { + //bail out early if there is nothing to split... + if (dauer == null || dauer.isEmpty()) { + filmLength = 0; + } + else { + final String[] split = StringUtils.split(dauer, ':'); + + try { + filmLength += Integer.parseInt(split[0]) * 3600; //hour + filmLength += Integer.parseInt(split[1]) * 60; //minute + filmLength += Integer.parseInt(split[2]); //second + } catch (Exception e) { + filmLength = 0; + } + } } public @Nullable DatenAbo getAbo() { @@ -249,7 +278,7 @@ public int getFilmNr() { * * @return The size in MByte */ - public FilmSize getFilmSize() { + public FilmSize getFileSize() { return filmSize; } @@ -341,9 +370,9 @@ public String getUrlFuerAufloesung(FilmResolution.Enum resolution) { }; } - public String getDateigroesse(String url) { + public String getFileSizeForUrl(@NotNull String url) { if (url.equalsIgnoreCase(getUrlNormalQuality())) { - return getSize(); + return getFileSize().toString(); } else { return FileSize.getFileLengthFromUrl(url); } @@ -376,41 +405,6 @@ public int compareTo(@NotNull DatenFilm other) { return ret; } - /** - * Get the filmlength in seconds. - * - * @return filmlength in seconds, or 0. - */ - public int getFilmLength() { - return filmLength; - } - - /** - * Convert HH:MM:SS string into seconds. - * Or set to 0 in case of error. - * - * @return result in seconds or 0. - */ - private int parseTimeToSeconds() { - int seconds = 0; - final String[] split = StringUtils.split(dauer, ':'); - // if empty, don't try to split and return early... - if (split == null || split.length == 0) { - return 0; - } - else { - try { - seconds += Integer.parseInt(split[0]) * 3600; //hour - seconds += Integer.parseInt(split[1]) * 60; //minute - seconds += Integer.parseInt(split[2]); //second - } catch (Exception e) { - seconds = 0; - } - - return seconds; - } - } - private void setDatum() { if (!getSendeDatum().isEmpty()) { // nur dann gibts ein Datum @@ -427,9 +421,6 @@ private void setDatum() { } public void init() { - filmSize = new FilmSize(this); - filmLength = parseTimeToSeconds(); - setDatum(); } @@ -461,17 +452,6 @@ private String getUrlNormalOrRequested(@NotNull FilmResolution.Enum resolution) return ret; } - /** - * URLs are considered compressed if they contain a '|'-symbol in the text. - * They need to be decompressed before use. - * @param requestedUrl the string to be checked. - * @return true if url is compressed, false otherwise. - */ - public static boolean isUrlCompressed(@NotNull String requestedUrl) { - final int indexPipe = requestedUrl.indexOf(COMPRESSION_MARKER); - return indexPipe != -1; - } - public String decompressUrl(@NotNull final String requestedUrl) throws NumberFormatException, IndexOutOfBoundsException { final int indexPipe = requestedUrl.indexOf(COMPRESSION_MARKER); final int i = Integer.parseInt(requestedUrl.substring(0, indexPipe)); @@ -532,39 +512,20 @@ public void setSendeZeit(String sendeZeit) { this.sendeZeit = sendeZeit; } - public String getDauer() { - return dauer; - } - - public void setDauer(String dauer) { - this.dauer = dauer; - - //bail out early if there is nothing to split... - if (dauer == null || dauer.isEmpty()) { - duration = 0; - } + /** + * Get the film's length or duration formatted as a string in "HH:MM:SS" format. + * Similar to {@link DatenFilm#getFilmLength()}. + * + * @return film length or duration as String. + */ + public String getFilmLengthAsString() { + if (filmLength == 0) + return ""; else { - //FIXME gefällt mir nicht - final String[] split = StringUtils.split(this.dauer, ':'); - - try { - duration += Integer.parseInt(split[0]) * 3600; //hour - duration += Integer.parseInt(split[1]) * 60; //minute - duration += Integer.parseInt(split[2]); //second - } catch (Exception e) { - duration = 0; - } + return DurationFormatUtils.formatDuration(filmLength * 1000L,"HH:mm:ss", true); } } - public String getSize() { - return groesse; - } - - public void setSize(String size) { - this.groesse = size; - } - public String getUrlNormalQuality() { return url_normal_quality; } diff --git a/src/main/java/mediathek/daten/blacklist/ListeBlacklist.java b/src/main/java/mediathek/daten/blacklist/ListeBlacklist.java index a8fc3b3dcb..a94de913e3 100644 --- a/src/main/java/mediathek/daten/blacklist/ListeBlacklist.java +++ b/src/main/java/mediathek/daten/blacklist/ListeBlacklist.java @@ -315,7 +315,7 @@ private boolean checkIfFilmIsInFuture(@NotNull DatenFilm film) { * @return true if film should be displayed */ private boolean checkFilmLength(@NotNull DatenFilm film) { - final long filmLength = film.getFilmLength(); + var filmLength = film.getFilmLength(); return !(filmLength != 0 && minimumFilmLength > filmLength); } diff --git a/src/main/java/mediathek/filmlisten/reader/FilmListReader.java b/src/main/java/mediathek/filmlisten/reader/FilmListReader.java index cc0bd747e7..859793c2a3 100644 --- a/src/main/java/mediathek/filmlisten/reader/FilmListReader.java +++ b/src/main/java/mediathek/filmlisten/reader/FilmListReader.java @@ -210,12 +210,12 @@ private void parseSendedatum(JsonParser jp, DatenFilm datenFilm) throws IOExcept } private void parseDauer(JsonParser jp, DatenFilm datenFilm) throws IOException { - datenFilm.setDauer(checkedString(jp)); + datenFilm.setFilmLength(checkedString(jp)); } private void parseGroesse(JsonParser jp, DatenFilm datenFilm) throws IOException { String value = checkedString(jp); - datenFilm.setSize(value); + datenFilm.getFileSize().setSize(value); } /** diff --git a/src/main/java/mediathek/filmlisten/writer/FilmListWriter.java b/src/main/java/mediathek/filmlisten/writer/FilmListWriter.java index 18adc5d93c..83ead0e2cf 100644 --- a/src/main/java/mediathek/filmlisten/writer/FilmListWriter.java +++ b/src/main/java/mediathek/filmlisten/writer/FilmListWriter.java @@ -139,8 +139,8 @@ private void writeEntry(DatenFilm datenFilm, JsonGenerator jg) throws IOExceptio writeTitel(jg, datenFilm); jg.writeString(datenFilm.getSendeDatum()); writeZeit(jg, datenFilm); - jg.writeString(datenFilm.getDauer()); - jg.writeString(datenFilm.getSize()); + jg.writeString(datenFilm.getFilmLengthAsString()); + jg.writeString(datenFilm.getFileSize().toString()); jg.writeString(datenFilm.getDescription()); jg.writeString(datenFilm.getUrlNormalQuality()); jg.writeString(datenFilm.getWebsiteLink()); diff --git a/src/main/java/mediathek/gui/dialog/DialogAddDownload.java b/src/main/java/mediathek/gui/dialog/DialogAddDownload.java index f7b09d4d7a..cf7342f1dc 100644 --- a/src/main/java/mediathek/gui/dialog/DialogAddDownload.java +++ b/src/main/java/mediathek/gui/dialog/DialogAddDownload.java @@ -130,7 +130,7 @@ private void launchResolutionFutures() { var decoratedPool = Daten.getInstance().getDecoratedPool(); hqFuture = decoratedPool.submit(() -> { var url = datenFilm.getUrlFuerAufloesung(FilmResolution.Enum.HIGH_QUALITY); - return datenFilm.getDateigroesse(url); + return datenFilm.getFileSizeForUrl(url); }); Futures.addCallback(hqFuture, new FutureCallback<>() { @@ -158,7 +158,7 @@ public void onFailure(@NotNull Throwable t) { hochFuture = decoratedPool.submit(() -> { var url = datenFilm.getUrlNormalQuality(); - return datenFilm.getDateigroesse(url); + return datenFilm.getFileSizeForUrl(url); }); Futures.addCallback(hochFuture, new FutureCallback<>() { @Override @@ -183,7 +183,7 @@ public void onFailure(@NotNull Throwable t) { kleinFuture = decoratedPool.submit(() -> { var url = datenFilm.getUrlFuerAufloesung(FilmResolution.Enum.LOW); - return datenFilm.getDateigroesse(url); + return datenFilm.getFileSizeForUrl(url); }); Futures.addCallback(kleinFuture, new FutureCallback<>() { @Override diff --git a/src/main/java/mediathek/gui/dialog/DialogEditDownload.java b/src/main/java/mediathek/gui/dialog/DialogEditDownload.java index 39719cbbfb..38506a1fd7 100644 --- a/src/main/java/mediathek/gui/dialog/DialogEditDownload.java +++ b/src/main/java/mediathek/gui/dialog/DialogEditDownload.java @@ -86,7 +86,7 @@ private void setupResolutionButtons() { if (datenDownload.film != null) { jRadioButtonResHi.setEnabled(!gestartet); jRadioButtonResHi.setSelected(datenDownload.arr[DatenDownload.DOWNLOAD_URL].equals(datenDownload.film.getUrlFuerAufloesung(FilmResolution.Enum.NORMAL))); - dateiGroesse_Hoch = datenDownload.film.getDateigroesse(datenDownload.film.getUrlFuerAufloesung(FilmResolution.Enum.NORMAL)); + dateiGroesse_Hoch = datenDownload.film.getFileSizeForUrl(datenDownload.film.getUrlFuerAufloesung(FilmResolution.Enum.NORMAL)); if (!dateiGroesse_Hoch.isEmpty()) { jRadioButtonResHi.setText(jRadioButtonResHi.getText() + " [ " + dateiGroesse_Hoch + " MB ]"); } @@ -94,7 +94,7 @@ private void setupResolutionButtons() { if (!datenDownload.film.getUrlHighQuality().isEmpty()) { jRadioButtonResHd.setEnabled(!gestartet); jRadioButtonResHd.setSelected(datenDownload.arr[DatenDownload.DOWNLOAD_URL].equals(datenDownload.film.getUrlFuerAufloesung(FilmResolution.Enum.HIGH_QUALITY))); - dateiGroesse_HD = datenDownload.film.getDateigroesse(datenDownload.film.getUrlFuerAufloesung(FilmResolution.Enum.HIGH_QUALITY)); + dateiGroesse_HD = datenDownload.film.getFileSizeForUrl(datenDownload.film.getUrlFuerAufloesung(FilmResolution.Enum.HIGH_QUALITY)); if (!dateiGroesse_HD.isEmpty()) { jRadioButtonResHd.setText(jRadioButtonResHd.getText() + " [ " + dateiGroesse_HD + " MB ]"); } @@ -103,7 +103,7 @@ private void setupResolutionButtons() { if (!datenDownload.film.getUrlLowQuality().isEmpty()) { jRadioButtonResLo.setEnabled(!gestartet); jRadioButtonResLo.setSelected(datenDownload.arr[DatenDownload.DOWNLOAD_URL].equals(datenDownload.film.getUrlFuerAufloesung(FilmResolution.Enum.LOW))); - dateiGroesse_Klein = datenDownload.film.getDateigroesse(datenDownload.film.getUrlFuerAufloesung(FilmResolution.Enum.LOW)); + dateiGroesse_Klein = datenDownload.film.getFileSizeForUrl(datenDownload.film.getUrlFuerAufloesung(FilmResolution.Enum.LOW)); if (!dateiGroesse_Klein.isEmpty()) { jRadioButtonResLo.setText(jRadioButtonResLo.getText() + " [ " + dateiGroesse_Klein + " MB ]"); } diff --git a/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt b/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt index 1aacfb5435..528215f349 100644 --- a/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt +++ b/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt @@ -139,12 +139,12 @@ class InfoDialog(parent: Window?) : JDialog(parent) { lblSender.graphic = SenderIcon(JavaFxUtils.toBufferedImage(icon)) } lblGeo.text = currentFilm!!.geo.orElse("") - lblSize.text = currentFilm!!.size + lblSize.text = currentFilm!!.fileSize.toString() lblThema.text = currentFilm!!.thema lblTitle.text = currentFilm!!.title lblDate.text = currentFilm!!.sendeDatum lblUhrzeit.text = currentFilm!!.sendeZeit - lblDuration.text = currentFilm!!.dauer + lblDuration.text = currentFilm!!.filmLengthAsString cbHq.isSelected = currentFilm!!.isHighQuality cbSubtitle.isSelected = currentFilm!!.hasSubtitle() hyperlink.tooltip = Tooltip(currentFilm!!.websiteLink) diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilmeModelHelper.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilmeModelHelper.java index 3797814a5a..37495cbfc6 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilmeModelHelper.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilmeModelHelper.java @@ -220,7 +220,7 @@ private boolean seenCheck(DatenFilm film) { } private boolean minLengthCheck(DatenFilm film) { - final long filmLength = film.getFilmLength(); + var filmLength = film.getFilmLength(); if (filmLength == 0) return true; // always show entries with length 0, which are internally "no length" else diff --git a/src/main/java/mediathek/javafx/bookmark/BookmarkData.java b/src/main/java/mediathek/javafx/bookmark/BookmarkData.java index 14ba211755..ffc02b71c1 100644 --- a/src/main/java/mediathek/javafx/bookmark/BookmarkData.java +++ b/src/main/java/mediathek/javafx/bookmark/BookmarkData.java @@ -56,7 +56,7 @@ public void setThema(String url){} public String getTitel(){ return this.titel; } public void setTitel(String url){ this.titel = url;} - public String getDauer(){ return ((filmdata != null) ? filmdata.getDauer(): ""); } + public String getDauer(){ return ((filmdata != null) ? filmdata.getFilmLengthAsString(): ""); } public void setDauer(String dauer){} public String getDescription(){ return ((filmdata != null) ? filmdata.getDescription(): ""); } @@ -165,7 +165,7 @@ public DatenFilm getDataAsDatenFilm() { Film.setUrlHighQuality(getHighQualityUrl()); Film.setUrlLowQuality(getUrlKlein()); Film.setSender(getSender()); - Film.setDauer(getDauer()); + Film.setFilmLength(getDauer()); } return Film; } diff --git a/src/main/java/mediathek/tool/FilmSize.java b/src/main/java/mediathek/tool/FilmSize.java index 8df0a01b71..9f87292d6f 100644 --- a/src/main/java/mediathek/tool/FilmSize.java +++ b/src/main/java/mediathek/tool/FilmSize.java @@ -1,32 +1,31 @@ package mediathek.tool; -import mediathek.daten.DatenFilm; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; /** - * Store film size in bytes. + * Store film size in Megabytes. */ public class FilmSize implements Comparable { private int size; private static final Logger logger = LogManager.getLogger(); - public FilmSize(final int size) { - this.size = size; + public FilmSize() { + this.size = 0; } - public FilmSize(DatenFilm film) { - if (film.getSize().equalsIgnoreCase("<1")) { - film.setSize("1"); + public void setSize(String strSize) { + if (strSize.equalsIgnoreCase("<1")) { + size = 1; } try { - if (!film.getSize().isEmpty()) { - size = Integer.parseInt(film.getSize()); + if (!strSize.isEmpty()) { + size = Integer.parseInt(strSize); } } catch (NumberFormatException ex) { - logger.error("String: {}", film.getSize(),ex); + logger.error("String: {}", strSize,ex); size = 0; } } diff --git a/src/main/java/mediathek/tool/MVInfoFile.kt b/src/main/java/mediathek/tool/MVInfoFile.kt index b944bf098d..b56faa0556 100644 --- a/src/main/java/mediathek/tool/MVInfoFile.kt +++ b/src/main/java/mediathek/tool/MVInfoFile.kt @@ -27,7 +27,7 @@ open class MVInfoFile { sb = appendFormattedTableLine(sb, formatString, FILM_TITEL, film.title).append(System.lineSeparator()) sb = appendFormattedTableLine(sb, formatString, FILM_DATUM, film.sendeDatum) sb = appendFormattedTableLine(sb, formatString, FILM_ZEIT, film.sendeZeit) - sb = appendFormattedTableLine(sb, formatString, FILM_DAUER, film.dauer) + sb = appendFormattedTableLine(sb, formatString, FILM_DAUER, film.filmLengthAsString) if (fileSize > FileSize.INVALID_SIZE) sb = appendFormattedTableLine(sb, formatString, FILM_GROESSE, FileUtils.humanReadableByteCountBinary(fileSize)) else diff --git a/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java b/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java index 72b4fefe99..80cdb83b0a 100644 --- a/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java +++ b/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java @@ -111,7 +111,7 @@ public Component getTableCellRendererComponent( //here comes the content... switch (columnModelIndex) { case DatenFilm.FILM_DAUER: - setText(datenFilm.getDauer()); + setText(datenFilm.getFilmLengthAsString()); break; case DatenFilm.FILM_ABSPIELEN: diff --git a/src/main/java/mediathek/tool/models/TModelFilm.java b/src/main/java/mediathek/tool/models/TModelFilm.java index 5a3b55085d..469bdbcb4f 100644 --- a/src/main/java/mediathek/tool/models/TModelFilm.java +++ b/src/main/java/mediathek/tool/models/TModelFilm.java @@ -98,8 +98,8 @@ public Object getValueAt(int row, int column) { case DatenFilm.FILM_ABSPIELEN, DatenFilm.FILM_AUFZEICHNEN, DatenFilm.FILM_MERKEN -> ""; case DatenFilm.FILM_DATUM -> film.getDatumFilm(); case DatenFilm.FILM_ZEIT -> film.getSendeZeit(); - case DatenFilm.FILM_DAUER -> film.getDuration(); - case DatenFilm.FILM_GROESSE -> film.getFilmSize(); + case DatenFilm.FILM_DAUER -> film.getFilmLength(); + case DatenFilm.FILM_GROESSE -> film.getFileSize(); case DatenFilm.FILM_HD -> film.isHighQuality(); case DatenFilm.FILM_UT -> film.hasSubtitle(); case DatenFilm.FILM_GEO -> film.getGeo().orElse(""); diff --git a/src/test/java/mSearch/daten/DatenFilmTest.java b/src/test/java/mSearch/daten/DatenFilmTest.java index 92a488a1dd..7a0187c144 100644 --- a/src/test/java/mSearch/daten/DatenFilmTest.java +++ b/src/test/java/mSearch/daten/DatenFilmTest.java @@ -31,7 +31,7 @@ static Stream filmLengthEdgeCases() { void testFilmLengthCalculation(String input, long expected) { DatenFilm df = new DatenFilm(); - df.setDauer(input); + df.setFilmLength(input); df.init(); Assertions.assertEquals(expected, df.getFilmLength()); From aa5b790bb1b4df255f69afaa280cba06b25d64aa Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 15 Jul 2022 15:14:43 +0200 Subject: [PATCH 181/422] - code cleanup Former-commit-id: 7829d85eb3586f81dacf8fd3414e9593d1ff50e0 --- src/main/java/mediathek/daten/DatenFilm.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/mediathek/daten/DatenFilm.java b/src/main/java/mediathek/daten/DatenFilm.java index b7b6000151..46c3742ce7 100644 --- a/src/main/java/mediathek/daten/DatenFilm.java +++ b/src/main/java/mediathek/daten/DatenFilm.java @@ -62,16 +62,16 @@ public class DatenFilm implements Comparable { * Internal film number, used for storage in database */ private final int databaseFilmNumber; + /** + * File size in MByte + */ + private final FilmSize filmSize = new FilmSize(); private DatenAbo abo; private BookmarkData bookmark; /** * film date stored IN SECONDS!!! */ private DatumFilm datumFilm = DatumFilm.UNDEFINED_FILM_DATE; - /** - * File size in MByte - */ - private FilmSize filmSize = new FilmSize(); private String websiteLink; private String description; /** @@ -114,7 +114,7 @@ public DatenFilm(@NotNull DatenFilm other) { this.abo = other.abo; this.bookmark = other.bookmark; this.datumFilm = other.datumFilm; - this.filmSize = other.filmSize; + this.filmSize.setSize(other.filmSize.toString()); this.databaseFilmNumber = other.databaseFilmNumber; this.websiteLink = other.websiteLink; this.description = other.description; From a52e4b16207b764f328c12b2ae0cc817d210023b Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 15 Jul 2022 15:36:44 +0200 Subject: [PATCH 182/422] - disable update settings checkbox when using external updater Former-commit-id: 3d6f2766b7d3d21f8f58f7b5084fcb1ae1703d17 --- CHANGELOG.md | 1 + .../dialogEinstellungen/allgemein/PanelEinstellungen.java | 5 +++++ src/main/java/mediathek/tool/GuiFunktionen.java | 3 +++ 3 files changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0cc6eb1790..2dedbbdd17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Das Programm überprüft beim Start ob die JVM-Parameter korrekt sind und gibt ansonsten eine Warnung aus. Dies kann über die Paramter `-nj` oder `--no-jvm-param-checks` deaktiviert werden. - Das Programm prüft unter Linux ob der `sun.java2d.uiScale`-Parameter korrekt angegeben wurde. Im Fehlerfall wird ein Warndialog angezeigt. - *Globale Schriftgröße ändern...*-Befehl wurde unter Linux entfernt da es keine Auswirkungen mehr hat. +- **BUGFIX:** Updater-Konfigurationsmöglichkeit wird nur noch in den Einstellungen aktiviert dargestellt, wenn sie auch Auswirkungen auf den Update-Mechanismus hat. - **BUGFIX:** *"Filme/Blacklist öffnen..."* Eintrag wurde in *"Filme/Blacklist bearbeiten..."* umbenannt und die Icons vereinheitlicht. - **BUGFIX:** Manchmal inkorrekte Darstellung des Bookmark-Status im Film-Tab wurde korrigiert. - **BUGFIX:** Sendericons werden nun vollständig und hochauflösend gerendert. diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/allgemein/PanelEinstellungen.java b/src/main/java/mediathek/gui/dialogEinstellungen/allgemein/PanelEinstellungen.java index 446a12a991..4e86aad241 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/allgemein/PanelEinstellungen.java +++ b/src/main/java/mediathek/gui/dialogEinstellungen/allgemein/PanelEinstellungen.java @@ -4,6 +4,7 @@ import mediathek.gui.messages.*; import mediathek.mainwindow.MediathekGui; import mediathek.tool.ApplicationConfiguration; +import mediathek.tool.GuiFunktionen; import mediathek.tool.MessageBus; import mediathek.tool.sender_icon_cache.MVSenderIconCache; import net.engio.mbassy.listener.Handler; @@ -131,6 +132,10 @@ public PanelEinstellungen() { cbAutomaticUpdateChecks.addActionListener(this::cbAutomaticUpdateChecksActionPerformed); cbAutomaticUpdateChecks.setSelected(ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.CONFIG_AUTOMATIC_UPDATE_CHECK,true)); + if (GuiFunktionen.isUsingExternalUpdater()) { + cbAutomaticUpdateChecks.setEnabled(false); + cbAutomaticUpdateChecks.setToolTipText("Diese Option ist deaktiviert, da ein externer Updater verwendet wird."); + } } @Handler diff --git a/src/main/java/mediathek/tool/GuiFunktionen.java b/src/main/java/mediathek/tool/GuiFunktionen.java index b38a0b8084..558ef3ba9a 100644 --- a/src/main/java/mediathek/tool/GuiFunktionen.java +++ b/src/main/java/mediathek/tool/GuiFunktionen.java @@ -46,6 +46,9 @@ public static boolean isNotUsingExternalUpdater() { return !ret; } + public static boolean isUsingExternalUpdater() { + return !isNotUsingExternalUpdater(); + } /** * Determine the image's size while keeping aspect ratio within a given boundary box. * From eb45d6cd51d2f7c6ce59c1b0bcb6d54b79e71350 Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 15 Jul 2022 16:26:27 +0200 Subject: [PATCH 183/422] - move URLs into urlMap - rename some functions Former-commit-id: 73df5d58728f543dab5cd9a13865f4ab5332a43d --- .../java/mediathek/daten/DatenDownload.java | 4 +- src/main/java/mediathek/daten/DatenFilm.java | 92 +++++++++---------- .../filmlisten/reader/FilmListReader.java | 4 +- .../filmlisten/writer/FilmListWriter.java | 4 +- .../gui/dialog/DialogAddDownload.java | 2 +- .../gui/filmInformation/InfoDialog.kt | 6 +- .../tabs/tab_film/FilmDescriptionPanel.java | 4 +- .../mediathek/gui/tabs/tab_film/GuiFilme.java | 4 +- .../gui/tabs/tab_film/JDownloadHelper.kt | 6 +- .../javafx/bookmark/BookmarkData.java | 4 +- src/main/java/mediathek/tool/MVInfoFile.kt | 2 +- 11 files changed, 64 insertions(+), 68 deletions(-) diff --git a/src/main/java/mediathek/daten/DatenDownload.java b/src/main/java/mediathek/daten/DatenDownload.java index 164abe1e33..deddc084c5 100644 --- a/src/main/java/mediathek/daten/DatenDownload.java +++ b/src/main/java/mediathek/daten/DatenDownload.java @@ -123,7 +123,7 @@ public DatenDownload(DatenPset pSet, DatenFilm film, byte quelle, DatenAbo abo, arr[DOWNLOAD_THEMA] = film.getThema(); arr[DOWNLOAD_TITEL] = film.getTitle(); arr[DOWNLOAD_FILM_URL] = film.getUrlNormalQuality(); - arr[DOWNLOAD_URL_SUBTITLE] = film.getUrlSubtitle(); + arr[DOWNLOAD_URL_SUBTITLE] = film.getSubtitleUrl(); arr[DOWNLOAD_DATUM] = film.getSendeDatum(); arr[DOWNLOAD_ZEIT] = film.getSendeZeit(); arr[DOWNLOAD_DAUER] = film.getFilmLengthAsString(); @@ -141,7 +141,7 @@ public DatenDownload(DatenPset pSet, DatenFilm film, byte quelle, DatenAbo abo, arr[DatenDownload.DOWNLOAD_SPOTLIGHT] = pSet.arr[DatenPset.PROGRAMMSET_SPOTLIGHT]; arr[DatenDownload.DOWNLOAD_GEO] = film.getGeo().orElse(""); - websiteUrl = film.getWebsiteLink(); + websiteUrl = film.getWebsiteUrl(); // und jetzt noch die Dateigröße für die entsp. URL setGroesse(""); diff --git a/src/main/java/mediathek/daten/DatenFilm.java b/src/main/java/mediathek/daten/DatenFilm.java index 46c3742ce7..5b43f5e2dd 100644 --- a/src/main/java/mediathek/daten/DatenFilm.java +++ b/src/main/java/mediathek/daten/DatenFilm.java @@ -13,6 +13,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.EnumMap; import java.util.EnumSet; import java.util.Optional; import java.util.concurrent.atomic.AtomicInteger; @@ -66,22 +67,17 @@ public class DatenFilm implements Comparable { * File size in MByte */ private final FilmSize filmSize = new FilmSize(); + /** + * Stores all URLs, some keys may not exist. + */ + private final EnumMap urlMap = new EnumMap<>(MapKeys.class); private DatenAbo abo; private BookmarkData bookmark; /** * film date stored IN SECONDS!!! */ private DatumFilm datumFilm = DatumFilm.UNDEFINED_FILM_DATE; - private String websiteLink; private String description; - /** - * Low quality URL. - */ - private String url_low_quality = ""; - /** - * High Quality (formerly known as HD) URL if available. - */ - private Optional url_high_quality = Optional.empty(); private String datumLong = ""; private String sender = ""; private String thema = ""; @@ -91,16 +87,8 @@ public class DatenFilm implements Comparable { * Empty means viewable without restrictions. */ private Optional availableInCountries = Optional.empty(); - /** - * URL to the subtitle file, if available. - */ - private Optional subtitle_url = Optional.empty(); private String datum = ""; private String sendeZeit = ""; - /** - * Normal quality URL. - */ - private String url_normal_quality = ""; /** * film duration or film length in seconds. */ @@ -116,19 +104,15 @@ public DatenFilm(@NotNull DatenFilm other) { this.datumFilm = other.datumFilm; this.filmSize.setSize(other.filmSize.toString()); this.databaseFilmNumber = other.databaseFilmNumber; - this.websiteLink = other.websiteLink; this.description = other.description; - this.url_low_quality = other.url_low_quality; - this.url_high_quality = other.url_high_quality; this.datumLong = other.datumLong; this.sender = other.sender; this.thema = other.thema; this.titel = other.titel; this.availableInCountries = other.availableInCountries; - this.subtitle_url = other.subtitle_url; + this.urlMap.putAll(other.urlMap); this.datum = other.datum; this.sendeZeit = other.sendeZeit; - this.url_normal_quality = other.url_normal_quality; this.filmLength = other.filmLength; } @@ -188,22 +172,25 @@ public DatumFilm getDatumFilm() { } public String getUrlLowQuality() { - return url_low_quality; + return urlMap.getOrDefault(MapKeys.LOW_QUALITY_URL, ""); } - public void setUrlLowQuality(String url_low_quality) { - this.url_low_quality = url_low_quality; + public void setUrlLowQuality(@NotNull String url_low_quality) { + if (url_low_quality.isEmpty()) + urlMap.remove(MapKeys.LOW_QUALITY_URL); + else + urlMap.put(MapKeys.LOW_QUALITY_URL, url_low_quality); } public String getUrlHighQuality() { - return url_high_quality.orElse(""); + return urlMap.getOrDefault(MapKeys.HIGH_QUALITY_URL, ""); } - public void setUrlHighQuality(String urlHd) { - if (!urlHd.isEmpty()) - url_high_quality = Optional.of(urlHd); + public void setUrlHighQuality(@NotNull String urlHd) { + if (urlHd.isEmpty()) + urlMap.remove(MapKeys.HIGH_QUALITY_URL); else - url_high_quality = Optional.empty(); + urlMap.put(MapKeys.HIGH_QUALITY_URL, urlHd); } public String getDatumLong() { @@ -302,13 +289,16 @@ public void setDescription(final String desc) { } } - public String getWebsiteLink() { - return StringUtils.defaultString(websiteLink); + public String getWebsiteUrl() { + return urlMap.getOrDefault(MapKeys.WEBSITE_URL, ""); } - public void setWebsiteLink(String link) { - if (link != null && !link.isEmpty()) { - websiteLink = link; + public void setWebsiteUrl(String link) { + if (link == null || link.isEmpty()) { + urlMap.remove(MapKeys.WEBSITE_URL); + } + else { + urlMap.put(MapKeys.WEBSITE_URL, link); } } @@ -359,7 +349,7 @@ public boolean hasBurnedInSubtitles() { } public boolean hasSubtitle() { - return subtitle_url.isPresent(); + return urlMap.containsKey(MapKeys.SUBTITLE_URL); } //TODO This function might not be necessary as getUrlNormalOrRequested does almost the same @@ -384,7 +374,7 @@ public String getFileSizeForUrl(@NotNull String url) { * @return a unique "hash" string */ public String getUniqueHash() { - return (getSender() + getThema()).toLowerCase() + getUrlNormalQuality() + getWebsiteLink(); + return (getSender() + getThema()).toLowerCase() + getUrlNormalQuality() + getWebsiteUrl(); } /** @@ -393,7 +383,7 @@ public String getUniqueHash() { * @return true if HQ url is not empty. */ public boolean isHighQuality() { - return url_high_quality.isPresent(); + return urlMap.containsKey(MapKeys.HIGH_QUALITY_URL); } @Override @@ -527,22 +517,26 @@ public String getFilmLengthAsString() { } public String getUrlNormalQuality() { - return url_normal_quality; + return urlMap.getOrDefault(MapKeys.NORMAL_QUALITY_URL, ""); } - public void setUrlNormalQuality(String url_normal_quality) { - this.url_normal_quality = url_normal_quality; + public void setUrlNormalQuality(@NotNull String url_normal_quality) { + if (url_normal_quality.isEmpty()) + urlMap.remove(MapKeys.NORMAL_QUALITY_URL); + else + urlMap.put(MapKeys.NORMAL_QUALITY_URL, url_normal_quality); } - public String getUrlSubtitle() { - return subtitle_url.orElse(""); + public String getSubtitleUrl() { + return urlMap.getOrDefault(MapKeys.SUBTITLE_URL,""); } - public void setUrlSubtitle(String urlSubtitle) { - if (!urlSubtitle.isEmpty()) - subtitle_url = Optional.of(urlSubtitle); - else - subtitle_url = Optional.empty(); + public void setSubtitleUrl(@NotNull String urlSubtitle) { + if (urlSubtitle.isEmpty()) + urlMap.remove(MapKeys.SUBTITLE_URL); + else { + urlMap.put(MapKeys.SUBTITLE_URL, urlSubtitle); + } } public Optional getGeo() { @@ -579,4 +573,6 @@ public void setBookmark(BookmarkData bookmark) { public boolean isBookmarked() { return this.bookmark != null; } + + enum MapKeys {SUBTITLE_URL, WEBSITE_URL, LOW_QUALITY_URL, NORMAL_QUALITY_URL, HIGH_QUALITY_URL} } diff --git a/src/main/java/mediathek/filmlisten/reader/FilmListReader.java b/src/main/java/mediathek/filmlisten/reader/FilmListReader.java index 859793c2a3..78e5fb177d 100644 --- a/src/main/java/mediathek/filmlisten/reader/FilmListReader.java +++ b/src/main/java/mediathek/filmlisten/reader/FilmListReader.java @@ -96,7 +96,7 @@ private void parseNeu(JsonParser jp, DatenFilm datenFilm) throws IOException { protected void parseWebsiteLink(JsonParser jp, DatenFilm datenFilm) throws IOException { final String value = jp.nextTextValue(); if (value != null && !value.isEmpty()) { - datenFilm.setWebsiteLink(value); + datenFilm.setWebsiteUrl(value); } } @@ -190,7 +190,7 @@ private void skipFieldDescriptions(JsonParser jp) throws IOException { } private void parseUrlSubtitle(JsonParser jp, DatenFilm datenFilm) throws IOException { - datenFilm.setUrlSubtitle(checkedString(jp)); + datenFilm.setSubtitleUrl(checkedString(jp)); } private void parseUrlKlein(JsonParser jp, DatenFilm datenFilm) throws IOException { diff --git a/src/main/java/mediathek/filmlisten/writer/FilmListWriter.java b/src/main/java/mediathek/filmlisten/writer/FilmListWriter.java index 83ead0e2cf..c8ab03ec49 100644 --- a/src/main/java/mediathek/filmlisten/writer/FilmListWriter.java +++ b/src/main/java/mediathek/filmlisten/writer/FilmListWriter.java @@ -143,8 +143,8 @@ private void writeEntry(DatenFilm datenFilm, JsonGenerator jg) throws IOExceptio jg.writeString(datenFilm.getFileSize().toString()); jg.writeString(datenFilm.getDescription()); jg.writeString(datenFilm.getUrlNormalQuality()); - jg.writeString(datenFilm.getWebsiteLink()); - jg.writeString(datenFilm.getUrlSubtitle()); + jg.writeString(datenFilm.getWebsiteUrl()); + jg.writeString(datenFilm.getSubtitleUrl()); skipEntry(jg); //DatenFilm.FILM_URL_RTMP writeLowQualityUrl(jg, datenFilm); skipEntry(jg); //DatenFilm.URL_RTMP_KLEIN diff --git a/src/main/java/mediathek/gui/dialog/DialogAddDownload.java b/src/main/java/mediathek/gui/dialog/DialogAddDownload.java index cf7342f1dc..bd926f3007 100644 --- a/src/main/java/mediathek/gui/dialog/DialogAddDownload.java +++ b/src/main/java/mediathek/gui/dialog/DialogAddDownload.java @@ -528,7 +528,7 @@ private void setupResolutionButtons() { jCheckBoxInfodatei.setSelected(Boolean.parseBoolean(pSet.arr[DatenPset.PROGRAMMSET_INFODATEI])); - if (datenFilm.getUrlSubtitle().isEmpty()) { + if (datenFilm.getSubtitleUrl().isEmpty()) { // dann gibts keinen Subtitle jCheckBoxSubtitle.setEnabled(false); } else { diff --git a/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt b/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt index 528215f349..24f2c1fc9d 100644 --- a/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt +++ b/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt @@ -147,7 +147,7 @@ class InfoDialog(parent: Window?) : JDialog(parent) { lblDuration.text = currentFilm!!.filmLengthAsString cbHq.isSelected = currentFilm!!.isHighQuality cbSubtitle.isSelected = currentFilm!!.hasSubtitle() - hyperlink.tooltip = Tooltip(currentFilm!!.websiteLink) + hyperlink.tooltip = Tooltip(currentFilm!!.websiteUrl) hyperlink.isDisable = false lblAbo.text = currentFilm!!.abo?.name } @@ -224,7 +224,7 @@ class InfoDialog(parent: Window?) : JDialog(parent) { SwingUtilities.invokeLater { if (currentFilm != null) { try { - UrlHyperlinkAction.openURL(null, currentFilm!!.websiteLink) + UrlHyperlinkAction.openURL(null, currentFilm!!.websiteUrl) } catch (ex: URISyntaxException) { ex.printStackTrace() } @@ -244,7 +244,7 @@ class InfoDialog(parent: Window?) : JDialog(parent) { private fun createCopyUrlContextMenu() : ContextMenu { val contextMenu = ContextMenu() val mi = MenuItem("URL kopieren") - mi.onAction = EventHandler { SwingUtilities.invokeLater { GuiFunktionen.copyToClipboard(currentFilm!!.websiteLink) } } + mi.onAction = EventHandler { SwingUtilities.invokeLater { GuiFunktionen.copyToClipboard(currentFilm!!.websiteUrl) } } contextMenu.items.add(mi) return contextMenu } diff --git a/src/main/java/mediathek/gui/tabs/tab_film/FilmDescriptionPanel.java b/src/main/java/mediathek/gui/tabs/tab_film/FilmDescriptionPanel.java index 72a24a22a3..d07932785f 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/FilmDescriptionPanel.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/FilmDescriptionPanel.java @@ -136,14 +136,14 @@ private void showFilmDescription(@NotNull DatenFilm film) { hyperlink.setVisible(true); try { - hyperlink.setURI(new URL(film.getWebsiteLink()).toURI()); + hyperlink.setURI(new URL(film.getWebsiteUrl()).toURI()); hyperlink.setText("Link zur Webseite"); hyperlink.setClicked(false); } catch (Exception e) { //logger hyperlink.setText("Link nicht verfügbar"); } - hyperlink.setToolTipText(film.getWebsiteLink()); + hyperlink.setToolTipText(film.getWebsiteUrl()); textArea.setText(film.getDescription()); SwingUtilities.invokeLater(() -> scrollPane1.getVerticalScrollBar().setValue(0)); diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index 488fbadc7c..9cad08a7e2 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -1396,10 +1396,10 @@ private void setupFilmUrlCopyToClipboardEntries(@NotNull JMenu parentMenu, @NotN } } - if (!film.getUrlSubtitle().isEmpty()) { + if (!film.getSubtitleUrl().isEmpty()) { item = new JMenuItem("Untertitel-URL"); - item.addActionListener(e -> GuiFunktionen.copyToClipboard(film.getUrlSubtitle())); + item.addActionListener(e -> GuiFunktionen.copyToClipboard(film.getSubtitleUrl())); parentMenu.add(item); } } diff --git a/src/main/java/mediathek/gui/tabs/tab_film/JDownloadHelper.kt b/src/main/java/mediathek/gui/tabs/tab_film/JDownloadHelper.kt index e925ab605b..b1f525ea38 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/JDownloadHelper.kt +++ b/src/main/java/mediathek/gui/tabs/tab_film/JDownloadHelper.kt @@ -95,15 +95,15 @@ class JDownloadHelper { val miWebsiteToJd = JMenuItem("Webseiten-URL an JDownloader übergeben") miWebsiteToJd.addActionListener { try { - val webSiteUrl = film.websiteLink.toHttpUrl() + val webSiteUrl = film.websiteUrl.toHttpUrl() downloadUrl(webSiteUrl, film) } catch (e: IllegalArgumentException) { - logger.error("Illegal Website URL found: {}", film.websiteLink) + logger.error("Illegal Website URL found: {}", film.websiteUrl) } } jPopupMenu.add(miWebsiteToJd) - if (film.websiteLink.isBlank()) { + if (film.websiteUrl.isBlank()) { miWebsiteToJd.isEnabled = false } } diff --git a/src/main/java/mediathek/javafx/bookmark/BookmarkData.java b/src/main/java/mediathek/javafx/bookmark/BookmarkData.java index ffc02b71c1..21a31f89dc 100644 --- a/src/main/java/mediathek/javafx/bookmark/BookmarkData.java +++ b/src/main/java/mediathek/javafx/bookmark/BookmarkData.java @@ -96,7 +96,7 @@ public boolean hasURL() { @JsonIgnore public boolean hasWebURL() { - return (this.filmdata != null && !this.filmdata.getWebsiteLink().isEmpty()); + return (this.filmdata != null && !this.filmdata.getWebsiteUrl().isEmpty()); } /** @@ -132,7 +132,7 @@ public DatenFilm getDatenFilm() { @JsonIgnore public String getWebUrl() { - return (this.filmdata != null) ? this.filmdata.getWebsiteLink() : null; + return (this.filmdata != null) ? this.filmdata.getWebsiteUrl() : null; } @JsonIgnore diff --git a/src/main/java/mediathek/tool/MVInfoFile.kt b/src/main/java/mediathek/tool/MVInfoFile.kt index b56faa0556..f9c42193b3 100644 --- a/src/main/java/mediathek/tool/MVInfoFile.kt +++ b/src/main/java/mediathek/tool/MVInfoFile.kt @@ -36,7 +36,7 @@ open class MVInfoFile { sb.append(System.lineSeparator()) sb.append("Website") sb.append(System.lineSeparator()) - sb.append(film.websiteLink) + sb.append(film.websiteUrl) sb.append(System.lineSeparator()) sb.append(System.lineSeparator()) sb.append(FILM_URL) From ae4cf8b66a778937c6eef435b283d25415509e01 Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 15 Jul 2022 16:51:17 +0200 Subject: [PATCH 184/422] - always return 1 for internal film number als preparation for removal Former-commit-id: 360fc32c32b2006420a4a735d7589018eee87791 --- CHANGELOG.md | 2 ++ src/main/java/mediathek/daten/DatenDownload.java | 2 +- src/main/java/mediathek/daten/DatenFilm.java | 15 +++------------ .../resources/mediathek/file/hilfetext_pset.txt | 2 +- 4 files changed, 7 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2dedbbdd17..fdda76cb8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ - User Interface wurde primär für neue macOS-Versionen überarbeitet. - Das Programm überprüft beim Start ob die JVM-Parameter korrekt sind und gibt ansonsten eine Warnung aus. Dies kann über die Paramter `-nj` oder `--no-jvm-param-checks` deaktiviert werden. - Das Programm prüft unter Linux ob der `sun.java2d.uiScale`-Parameter korrekt angegeben wurde. Im Fehlerfall wird ein Warndialog angezeigt. +- Die Spalte für Filmnummer liefert bis zur vollständigen Entfernung **immer** `1` zurück. +- Der PSet-Parameter `%i` liefert anstatt der internen Filmnummer nun die aktuelle Zeit seit 1970 in Millisekunden. - *Globale Schriftgröße ändern...*-Befehl wurde unter Linux entfernt da es keine Auswirkungen mehr hat. - **BUGFIX:** Updater-Konfigurationsmöglichkeit wird nur noch in den Einstellungen aktiviert dargestellt, wenn sie auch Auswirkungen auf den Update-Mechanismus hat. - **BUGFIX:** *"Filme/Blacklist öffnen..."* Eintrag wurde in *"Filme/Blacklist bearbeiten..."* umbenannt und die Icons vereinheitlicht. diff --git a/src/main/java/mediathek/daten/DatenDownload.java b/src/main/java/mediathek/daten/DatenDownload.java index deddc084c5..1c5513def5 100644 --- a/src/main/java/mediathek/daten/DatenDownload.java +++ b/src/main/java/mediathek/daten/DatenDownload.java @@ -787,7 +787,7 @@ private String replaceString(String replStr, DatenFilm film) { replStr = StringUtils.replace(replStr, "%5", getHMS(HMSTag.MINUTE, film.getSendeZeit().isEmpty() ? getJetzt_HH_MM_SS() : film.getSendeZeit())); replStr = StringUtils.replace(replStr, "%6", getHMS(HMSTag.SECOND, film.getSendeZeit().isEmpty() ? getJetzt_HH_MM_SS() : film.getSendeZeit())); - replStr = StringUtils.replace(replStr, "%i", String.valueOf(film.getFilmNr())); + replStr = StringUtils.replace(replStr, "%i", String.valueOf(System.currentTimeMillis())); String res = ""; if (arr[DOWNLOAD_URL].equals(film.getUrlFuerAufloesung(FilmResolution.Enum.NORMAL))) { diff --git a/src/main/java/mediathek/daten/DatenFilm.java b/src/main/java/mediathek/daten/DatenFilm.java index 5b43f5e2dd..1df30898a6 100644 --- a/src/main/java/mediathek/daten/DatenFilm.java +++ b/src/main/java/mediathek/daten/DatenFilm.java @@ -16,7 +16,6 @@ import java.util.EnumMap; import java.util.EnumSet; import java.util.Optional; -import java.util.concurrent.atomic.AtomicInteger; /* * TODO: @@ -52,17 +51,9 @@ public class DatenFilm implements Comparable { * Compressed URLs are missing the base normal quality URL and are indicated by the pipe-symbol. */ public static final char COMPRESSION_MARKER = '|'; - /** - * The database instance for all descriptions. - */ - private final static AtomicInteger FILM_COUNTER = new AtomicInteger(0); private static final GermanStringSorter sorter = GermanStringSorter.getInstance(); private static final Logger logger = LogManager.getLogger(DatenFilm.class); private final EnumSet flags = EnumSet.noneOf(DatenFilmFlags.class); - /** - * Internal film number, used for storage in database - */ - private final int databaseFilmNumber; /** * File size in MByte */ @@ -95,7 +86,6 @@ public class DatenFilm implements Comparable { private int filmLength; public DatenFilm() { - databaseFilmNumber = FILM_COUNTER.getAndIncrement(); } public DatenFilm(@NotNull DatenFilm other) { @@ -103,7 +93,6 @@ public DatenFilm(@NotNull DatenFilm other) { this.bookmark = other.bookmark; this.datumFilm = other.datumFilm; this.filmSize.setSize(other.filmSize.toString()); - this.databaseFilmNumber = other.databaseFilmNumber; this.description = other.description; this.datumLong = other.datumLong; this.sender = other.sender; @@ -254,10 +243,12 @@ public void setSignLanguage(boolean val) { * This is used internally for the database id AND * for the old MV code that might access it for various stuff. * + * This number is UNUSED and always returns 1 until it is completely removed. + * * @return the original internal film number */ public int getFilmNr() { - return databaseFilmNumber; + return 1; } /** diff --git a/src/main/resources/mediathek/file/hilfetext_pset.txt b/src/main/resources/mediathek/file/hilfetext_pset.txt index add85de527..bb653ea655 100644 --- a/src/main/resources/mediathek/file/hilfetext_pset.txt +++ b/src/main/resources/mediathek/file/hilfetext_pset.txt @@ -61,7 +61,7 @@ Zeit in der Form: SSMMss z.B. 152059 (15:20:59) %N Originaldateiname des Films (der kann sehr kryptisch und lang sein) %S Suffix des Originaldateinamens des Films (z.B. "mp4") -%i Filmnummer (die ändert sich beim Neuladen der Filmliste!) +%i ehemals die interne Filmnummer, nun die aktuelle Uhrzeit in Millisekunden seit 1970 %q Qualität des Films ("HD", "H", "L") %Z Hashwert der URL, z.B.: 1433245578 From 62a5809898b97f0f35b74eac3cd63c259db4eb18 Mon Sep 17 00:00:00 2001 From: Christian F Date: Sat, 16 Jul 2022 11:25:18 +0200 Subject: [PATCH 185/422] - move bookmarkData and abo to data map Former-commit-id: 21bb7167f34f1dd46ade84f87d8bb88c64e26507 --- src/main/java/mediathek/daten/DatenFilm.java | 84 ++++++++++--------- .../filmlisten/reader/FilmListReader.java | 6 +- .../filmlisten/writer/FilmListWriter.java | 8 +- .../gui/dialog/DialogAddDownload.java | 8 +- .../gui/dialog/DialogEditDownload.java | 4 +- .../gui/tabs/tab_downloads/GuiDownloads.java | 4 +- .../javafx/bookmark/BookmarkData.java | 10 +-- 7 files changed, 63 insertions(+), 61 deletions(-) diff --git a/src/main/java/mediathek/daten/DatenFilm.java b/src/main/java/mediathek/daten/DatenFilm.java index 1df30898a6..77f8eb264c 100644 --- a/src/main/java/mediathek/daten/DatenFilm.java +++ b/src/main/java/mediathek/daten/DatenFilm.java @@ -61,9 +61,7 @@ public class DatenFilm implements Comparable { /** * Stores all URLs, some keys may not exist. */ - private final EnumMap urlMap = new EnumMap<>(MapKeys.class); - private DatenAbo abo; - private BookmarkData bookmark; + private final EnumMap dataMap = new EnumMap<>(MapKeys.class); /** * film date stored IN SECONDS!!! */ @@ -89,8 +87,6 @@ public DatenFilm() { } public DatenFilm(@NotNull DatenFilm other) { - this.abo = other.abo; - this.bookmark = other.bookmark; this.datumFilm = other.datumFilm; this.filmSize.setSize(other.filmSize.toString()); this.description = other.description; @@ -99,7 +95,7 @@ public DatenFilm(@NotNull DatenFilm other) { this.thema = other.thema; this.titel = other.titel; this.availableInCountries = other.availableInCountries; - this.urlMap.putAll(other.urlMap); + this.dataMap.putAll(other.dataMap); this.datum = other.datum; this.sendeZeit = other.sendeZeit; this.filmLength = other.filmLength; @@ -111,7 +107,7 @@ public DatenFilm(@NotNull DatenFilm other) { * @param requestedUrl the string to be checked. * @return true if url is compressed, false otherwise. */ - public static boolean isUrlCompressed(@NotNull String requestedUrl) { + public static boolean isCompressedUrl(@NotNull String requestedUrl) { final int indexPipe = requestedUrl.indexOf(COMPRESSION_MARKER); return indexPipe != -1; } @@ -149,37 +145,40 @@ public void setFilmLength(String dauer) { } public @Nullable DatenAbo getAbo() { - return abo; + return (DatenAbo) dataMap.getOrDefault(MapKeys.ABO_DATA, null); } - public void setAbo(DatenAbo abo) { - this.abo = abo; + public void setAbo(@Nullable DatenAbo abo) { + if (abo == null) + dataMap.remove(MapKeys.ABO_DATA); + else + dataMap.put(MapKeys.ABO_DATA, abo); } public DatumFilm getDatumFilm() { return datumFilm; } - public String getUrlLowQuality() { - return urlMap.getOrDefault(MapKeys.LOW_QUALITY_URL, ""); + public String getLowQualityUrl() { + return (String)dataMap.getOrDefault(MapKeys.LOW_QUALITY_URL, ""); } - public void setUrlLowQuality(@NotNull String url_low_quality) { + public void setLowQualityUrl(@NotNull String url_low_quality) { if (url_low_quality.isEmpty()) - urlMap.remove(MapKeys.LOW_QUALITY_URL); + dataMap.remove(MapKeys.LOW_QUALITY_URL); else - urlMap.put(MapKeys.LOW_QUALITY_URL, url_low_quality); + dataMap.put(MapKeys.LOW_QUALITY_URL, url_low_quality); } - public String getUrlHighQuality() { - return urlMap.getOrDefault(MapKeys.HIGH_QUALITY_URL, ""); + public String getHighQualityUrl() { + return (String)dataMap.getOrDefault(MapKeys.HIGH_QUALITY_URL, ""); } - public void setUrlHighQuality(@NotNull String urlHd) { + public void setHighQualityUrl(@NotNull String urlHd) { if (urlHd.isEmpty()) - urlMap.remove(MapKeys.HIGH_QUALITY_URL); + dataMap.remove(MapKeys.HIGH_QUALITY_URL); else - urlMap.put(MapKeys.HIGH_QUALITY_URL, urlHd); + dataMap.put(MapKeys.HIGH_QUALITY_URL, urlHd); } public String getDatumLong() { @@ -281,15 +280,15 @@ public void setDescription(final String desc) { } public String getWebsiteUrl() { - return urlMap.getOrDefault(MapKeys.WEBSITE_URL, ""); + return (String)dataMap.getOrDefault(MapKeys.WEBSITE_URL, ""); } public void setWebsiteUrl(String link) { if (link == null || link.isEmpty()) { - urlMap.remove(MapKeys.WEBSITE_URL); + dataMap.remove(MapKeys.WEBSITE_URL); } else { - urlMap.put(MapKeys.WEBSITE_URL, link); + dataMap.put(MapKeys.WEBSITE_URL, link); } } @@ -340,7 +339,7 @@ public boolean hasBurnedInSubtitles() { } public boolean hasSubtitle() { - return urlMap.containsKey(MapKeys.SUBTITLE_URL); + return dataMap.containsKey(MapKeys.SUBTITLE_URL); } //TODO This function might not be necessary as getUrlNormalOrRequested does almost the same @@ -374,7 +373,7 @@ public String getUniqueHash() { * @return true if HQ url is not empty. */ public boolean isHighQuality() { - return urlMap.containsKey(MapKeys.HIGH_QUALITY_URL); + return dataMap.containsKey(MapKeys.HIGH_QUALITY_URL); } @Override @@ -420,7 +419,7 @@ private String getUrlNormalOrRequested(@NotNull FilmResolution.Enum resolution) ret = getUrlNormalQuality(); else { try { - if (isUrlCompressed(requestedUrl)) + if (isCompressedUrl(requestedUrl)) ret = decompressUrl(requestedUrl); else ret = requestedUrl; @@ -447,8 +446,8 @@ public String decompressUrl(@NotNull final String requestedUrl) throws NumberFor */ private String getUrlByResolution(@NotNull final FilmResolution.Enum resolution) { return switch (resolution) { - case HIGH_QUALITY -> getUrlHighQuality(); - case LOW -> getUrlLowQuality(); + case HIGH_QUALITY -> getHighQualityUrl(); + case LOW -> getLowQualityUrl(); default -> getUrlNormalQuality(); }; } @@ -508,25 +507,25 @@ public String getFilmLengthAsString() { } public String getUrlNormalQuality() { - return urlMap.getOrDefault(MapKeys.NORMAL_QUALITY_URL, ""); + return (String)dataMap.getOrDefault(MapKeys.NORMAL_QUALITY_URL, ""); } - public void setUrlNormalQuality(@NotNull String url_normal_quality) { + public void setNormalQualityUrl(@NotNull String url_normal_quality) { if (url_normal_quality.isEmpty()) - urlMap.remove(MapKeys.NORMAL_QUALITY_URL); + dataMap.remove(MapKeys.NORMAL_QUALITY_URL); else - urlMap.put(MapKeys.NORMAL_QUALITY_URL, url_normal_quality); + dataMap.put(MapKeys.NORMAL_QUALITY_URL, url_normal_quality); } public String getSubtitleUrl() { - return urlMap.getOrDefault(MapKeys.SUBTITLE_URL,""); + return (String)dataMap.getOrDefault(MapKeys.SUBTITLE_URL,""); } public void setSubtitleUrl(@NotNull String urlSubtitle) { if (urlSubtitle.isEmpty()) - urlMap.remove(MapKeys.SUBTITLE_URL); + dataMap.remove(MapKeys.SUBTITLE_URL); else { - urlMap.put(MapKeys.SUBTITLE_URL, urlSubtitle); + dataMap.put(MapKeys.SUBTITLE_URL, urlSubtitle); } } @@ -543,8 +542,8 @@ public void setGeo(Optional availableInCountries) { * * @return BookmarkData entry */ - public BookmarkData getBookmark() { - return this.bookmark; + public @Nullable BookmarkData getBookmark() { + return (BookmarkData) dataMap.getOrDefault(MapKeys.BOOKMARK_DATA, null); } /** @@ -552,8 +551,11 @@ public BookmarkData getBookmark() { * * @param bookmark Bookmark entry */ - public void setBookmark(BookmarkData bookmark) { - this.bookmark = bookmark; + public void setBookmark(@Nullable BookmarkData bookmark) { + if (bookmark == null) + dataMap.remove(MapKeys.BOOKMARK_DATA); + else + dataMap.put(MapKeys.BOOKMARK_DATA, bookmark); } /** @@ -562,8 +564,8 @@ public void setBookmark(BookmarkData bookmark) { * @return boolean true */ public boolean isBookmarked() { - return this.bookmark != null; + return dataMap.containsKey(MapKeys.BOOKMARK_DATA); } - enum MapKeys {SUBTITLE_URL, WEBSITE_URL, LOW_QUALITY_URL, NORMAL_QUALITY_URL, HIGH_QUALITY_URL} + enum MapKeys {SUBTITLE_URL, WEBSITE_URL, LOW_QUALITY_URL, NORMAL_QUALITY_URL, HIGH_QUALITY_URL, BOOKMARK_DATA, ABO_DATA} } diff --git a/src/main/java/mediathek/filmlisten/reader/FilmListReader.java b/src/main/java/mediathek/filmlisten/reader/FilmListReader.java index 78e5fb177d..ad3b29cb28 100644 --- a/src/main/java/mediathek/filmlisten/reader/FilmListReader.java +++ b/src/main/java/mediathek/filmlisten/reader/FilmListReader.java @@ -194,11 +194,11 @@ private void parseUrlSubtitle(JsonParser jp, DatenFilm datenFilm) throws IOExcep } private void parseUrlKlein(JsonParser jp, DatenFilm datenFilm) throws IOException { - datenFilm.setUrlLowQuality(checkedString(jp)); + datenFilm.setLowQualityUrl(checkedString(jp)); } private void parseUrlHd(JsonParser jp, DatenFilm datenFilm) throws IOException { - datenFilm.setUrlHighQuality(checkedString(jp)); + datenFilm.setHighQualityUrl(checkedString(jp)); } private void parseDatumLong(JsonParser jp, DatenFilm datenFilm) throws IOException { @@ -269,7 +269,7 @@ private void parseTitel(JsonParser jp, DatenFilm datenFilm) throws IOException { } private void parseUrl(JsonParser jp, DatenFilm datenFilm) throws IOException { - datenFilm.setUrlNormalQuality(checkedString(jp)); + datenFilm.setNormalQualityUrl(checkedString(jp)); } private void parseLivestream(DatenFilm datenFilm) { diff --git a/src/main/java/mediathek/filmlisten/writer/FilmListWriter.java b/src/main/java/mediathek/filmlisten/writer/FilmListWriter.java index c8ab03ec49..a982c4dd7c 100644 --- a/src/main/java/mediathek/filmlisten/writer/FilmListWriter.java +++ b/src/main/java/mediathek/filmlisten/writer/FilmListWriter.java @@ -159,9 +159,9 @@ private void writeEntry(DatenFilm datenFilm, JsonGenerator jg) throws IOExceptio } private void writeLowQualityUrl(@NotNull JsonGenerator jg, @NotNull DatenFilm datenFilm) throws IOException { - String url = datenFilm.getUrlLowQuality(); + String url = datenFilm.getLowQualityUrl(); if (decompressUrls) { - if (DatenFilm.isUrlCompressed(url)) { + if (DatenFilm.isCompressedUrl(url)) { url = datenFilm.decompressUrl(url); } } @@ -170,9 +170,9 @@ private void writeLowQualityUrl(@NotNull JsonGenerator jg, @NotNull DatenFilm da } private void writeHighQualityUrl(@NotNull JsonGenerator jg, @NotNull DatenFilm datenFilm) throws IOException { - String url = datenFilm.getUrlHighQuality(); + String url = datenFilm.getHighQualityUrl(); if (decompressUrls) { - if (DatenFilm.isUrlCompressed(url)) { + if (DatenFilm.isCompressedUrl(url)) { url = datenFilm.decompressUrl(url); } } diff --git a/src/main/java/mediathek/gui/dialog/DialogAddDownload.java b/src/main/java/mediathek/gui/dialog/DialogAddDownload.java index bd926f3007..36c7ee1e9f 100644 --- a/src/main/java/mediathek/gui/dialog/DialogAddDownload.java +++ b/src/main/java/mediathek/gui/dialog/DialogAddDownload.java @@ -98,10 +98,10 @@ public void actionPerformed(ActionEvent e) { } }; jRadioButtonAufloesungHd.addActionListener(listener); - jRadioButtonAufloesungHd.setEnabled(!datenFilm.getUrlHighQuality().isEmpty()); + jRadioButtonAufloesungHd.setEnabled(!datenFilm.getHighQualityUrl().isEmpty()); jRadioButtonAufloesungKlein.addActionListener(listener); - jRadioButtonAufloesungKlein.setEnabled(!datenFilm.getUrlLowQuality().isEmpty()); + jRadioButtonAufloesungKlein.setEnabled(!datenFilm.getLowQualityUrl().isEmpty()); jRadioButtonAufloesungHoch.addActionListener(listener); jRadioButtonAufloesungHoch.setSelected(true); @@ -503,12 +503,12 @@ public static void saveComboPfad(JComboBox jcb, String orgPath) { private boolean isHighQualityRequested() { return pSet.arr[DatenPset.PROGRAMMSET_AUFLOESUNG].equals(FilmResolution.Enum.HIGH_QUALITY.toString()) - && !datenFilm.getUrlHighQuality().isEmpty(); + && !datenFilm.getHighQualityUrl().isEmpty(); } private boolean isLowQualityRequested() { return pSet.arr[DatenPset.PROGRAMMSET_AUFLOESUNG].equals(FilmResolution.Enum.LOW.toString()) && - !datenFilm.getUrlLowQuality().isEmpty(); + !datenFilm.getLowQualityUrl().isEmpty(); } private boolean highQualityMandated; diff --git a/src/main/java/mediathek/gui/dialog/DialogEditDownload.java b/src/main/java/mediathek/gui/dialog/DialogEditDownload.java index 38506a1fd7..94900003f0 100644 --- a/src/main/java/mediathek/gui/dialog/DialogEditDownload.java +++ b/src/main/java/mediathek/gui/dialog/DialogEditDownload.java @@ -91,7 +91,7 @@ private void setupResolutionButtons() { jRadioButtonResHi.setText(jRadioButtonResHi.getText() + " [ " + dateiGroesse_Hoch + " MB ]"); } - if (!datenDownload.film.getUrlHighQuality().isEmpty()) { + if (!datenDownload.film.getHighQualityUrl().isEmpty()) { jRadioButtonResHd.setEnabled(!gestartet); jRadioButtonResHd.setSelected(datenDownload.arr[DatenDownload.DOWNLOAD_URL].equals(datenDownload.film.getUrlFuerAufloesung(FilmResolution.Enum.HIGH_QUALITY))); dateiGroesse_HD = datenDownload.film.getFileSizeForUrl(datenDownload.film.getUrlFuerAufloesung(FilmResolution.Enum.HIGH_QUALITY)); @@ -100,7 +100,7 @@ private void setupResolutionButtons() { } } - if (!datenDownload.film.getUrlLowQuality().isEmpty()) { + if (!datenDownload.film.getLowQualityUrl().isEmpty()) { jRadioButtonResLo.setEnabled(!gestartet); jRadioButtonResLo.setSelected(datenDownload.arr[DatenDownload.DOWNLOAD_URL].equals(datenDownload.film.getUrlFuerAufloesung(FilmResolution.Enum.LOW))); dateiGroesse_Klein = datenDownload.film.getFileSizeForUrl(datenDownload.film.getUrlFuerAufloesung(FilmResolution.Enum.LOW)); diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java index c1c7dd9100..d48616f663 100644 --- a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java @@ -1439,8 +1439,8 @@ private void showMenu(MouseEvent evt) { if (dl.film != null) { DatenFilm filmClone = new DatenFilm(dl.film); // und jetzt die tatsächlichen URLs des Downloads eintragen - filmClone.setUrlNormalQuality(dl.arr[DatenDownload.DOWNLOAD_URL]); - filmClone.setUrlLowQuality(""); + filmClone.setNormalQualityUrl(dl.arr[DatenDownload.DOWNLOAD_URL]); + filmClone.setLowQualityUrl(""); // und starten daten.getStarterClass().urlMitProgrammStarten(gruppe, filmClone, ""); } diff --git a/src/main/java/mediathek/javafx/bookmark/BookmarkData.java b/src/main/java/mediathek/javafx/bookmark/BookmarkData.java index 21a31f89dc..4f7004c5b6 100644 --- a/src/main/java/mediathek/javafx/bookmark/BookmarkData.java +++ b/src/main/java/mediathek/javafx/bookmark/BookmarkData.java @@ -37,8 +37,8 @@ public BookmarkData(DatenFilm filmdata) { this.sender = filmdata.getSender(); this.titel = filmdata.getTitle(); this.senddate = filmdata.getSendeDatum(); - this.highQualityUrl = filmdata.getUrlHighQuality(); - this.urlKlein = filmdata.getUrlLowQuality(); + this.highQualityUrl = filmdata.getHighQualityUrl(); + this.urlKlein = filmdata.getLowQualityUrl(); this.filmdata = filmdata; this.willExpire = false; } @@ -161,9 +161,9 @@ public DatenFilm getDataAsDatenFilm() { Film = new DatenFilm(); Film.setThema(getThema()); Film.setTitle(getTitel()); - Film.setUrlNormalQuality(getUrl()); - Film.setUrlHighQuality(getHighQualityUrl()); - Film.setUrlLowQuality(getUrlKlein()); + Film.setNormalQualityUrl(getUrl()); + Film.setHighQualityUrl(getHighQualityUrl()); + Film.setLowQualityUrl(getUrlKlein()); Film.setSender(getSender()); Film.setFilmLength(getDauer()); } From 121ad5582678e407b2fb0a586b859ad6f2b72c17 Mon Sep 17 00:00:00 2001 From: Christian F Date: Sun, 17 Jul 2022 11:34:30 +0200 Subject: [PATCH 186/422] - fixed #538 in previous rework commits. Former-commit-id: 898891a5903e0514813358c6dddaaa9131bfd09f --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fdda76cb8d..5dd221dbd4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Der PSet-Parameter `%i` liefert anstatt der internen Filmnummer nun die aktuelle Zeit seit 1970 in Millisekunden. - *Globale Schriftgröße ändern...*-Befehl wurde unter Linux entfernt da es keine Auswirkungen mehr hat. - **BUGFIX:** Updater-Konfigurationsmöglichkeit wird nur noch in den Einstellungen aktiviert dargestellt, wenn sie auch Auswirkungen auf den Update-Mechanismus hat. +- **BUGFIX:** Fehler in der Anzeige im Statusfeld des Download-Tab wurde behoben. - **BUGFIX:** *"Filme/Blacklist öffnen..."* Eintrag wurde in *"Filme/Blacklist bearbeiten..."* umbenannt und die Icons vereinheitlicht. - **BUGFIX:** Manchmal inkorrekte Darstellung des Bookmark-Status im Film-Tab wurde korrigiert. - **BUGFIX:** Sendericons werden nun vollständig und hochauflösend gerendert. From 52612c0b7f428273ebffc36484d14f39286e9fe7 Mon Sep 17 00:00:00 2001 From: Christian F Date: Sun, 17 Jul 2022 11:59:28 +0200 Subject: [PATCH 187/422] - fix incorrect VM parameters Former-commit-id: 1d2733c2b7fb512b3f9a56cf4dc52da6efd7246a --- .install4j/mediathekview_arm.install4j | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.install4j/mediathekview_arm.install4j b/.install4j/mediathekview_arm.install4j index 14386d4753..69869e197f 100644 --- a/.install4j/mediathekview_arm.install4j +++ b/.install4j/mediathekview_arm.install4j @@ -33,7 +33,7 @@ - + @@ -48,7 +48,7 @@ - + From 28090a1cd9a6c040c9493d28e0f8b9d475449465 Mon Sep 17 00:00:00 2001 From: Christian F Date: Sun, 17 Jul 2022 12:02:30 +0200 Subject: [PATCH 188/422] - fix naming conventions Former-commit-id: 9dff54adb9d130592f1879ded92f197d6f985655 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6959b8b4a7..ec4ef1d788 100755 --- a/pom.xml +++ b/pom.xml @@ -135,7 +135,7 @@ -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=compact -XX:MaxRAMPercentage=50.0 -XX:+UseStringDeduplication -Dfile.encoding=UTF-8 -DexternalUpdateCheck - + -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=compact -XX:MaxRAMPercentage=50.0 -XX:+UseStringDeduplication --add-opens java.desktop/sun.awt.X11=ALL-UNNAMED -Dfile.encoding=UTF-8 -DexternalUpdateCheck From 225dd8e8a0cae17c1e8e2f0f0881c40de1fdad99 Mon Sep 17 00:00:00 2001 From: Alexander F Date: Sun, 17 Jul 2022 12:32:48 +0200 Subject: [PATCH 189/422] Aktualisierung install4j auf 9.0.7. Aktualisierung arm Profil auf 64bit. Aktualisierung deploy und build skripte Former-commit-id: 76ee93e96b07e9038e73692bee7948389cd7103e --- .github/workflows/nightly.yml | 18 +++++----- .github/workflows/release.yml | 32 ++++++++--------- .gitlab-ci.yml | 40 +++++++++++----------- .install4j/mediathekview_arm.install4j | 18 +++++----- .install4j/mediathekview_linux.install4j | 2 +- .install4j/mediathekview_windows.install4j | 2 +- pom.xml | 2 +- 7 files changed, 57 insertions(+), 57 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 8eb2a23986..6084475063 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -56,19 +56,19 @@ jobs: include: - os: linux architecture: 64bit - maven_profiles: "linux,64bit,install4j" + maven_profiles: "linux_64bit,install4j" - os: arm - architecture: 32bit - maven_profiles: "arm,!64bit,32bit,install4j,!linux" - - os: windows architecture: 64bit - maven_profiles: "windows,64bit,install4j,!linux" + maven_profiles: "!linux_64bit,linux_arm_64bit,install4j" - os: windows - architecture: 32bit - maven_profiles: "windows32,!64bit,32bit,install4j,!linux" + architecture: 64bit + maven_profiles: "!linux_64bit,windows_64bit,install4j" + # - os: windows + # architecture: 32bit + # maven_profiles: "windows32,!64bit,32bit,install4j,!linux" - os: macOS architecture: 64bit - maven_profiles: "mac,!linux" + maven_profiles: "!linux_64bit,mac_intel" steps: - uses: actions/checkout@v2 with: @@ -85,7 +85,7 @@ jobs: - name: Download install4j uses: wei/curl@v1 with: - args: -fsSL 'https://download-gcdn.ej-technologies.com/install4j/install4j_unix_9_0_5.tar.gz' --output install4j.tar.gz + args: -fsSL 'https://download-gcdn.ej-technologies.com/install4j/install4j_unix_9_0_7.tar.gz' --output install4j.tar.gz - name: Extract install4j run: tar -zxvf install4j.tar.gz diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 83eb83b849..3f01e54e77 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,11 +19,11 @@ jobs: id: check-if-changed with: paths: .github/workflows src/ res/ pom.xml .install4j/ .mvn/ - - name: Set up JDK 17 + - name: Set up JDK 18 if: steps.check-if-changed.outputs.changed == 'true' uses: actions/setup-java@v1 with: - java-version: 17 + java-version: 18 - name: Cache local Maven repository if: steps.check-if-changed.outputs.changed == 'true' @@ -57,28 +57,28 @@ jobs: include: - os: linux architecture: 64bit - maven_profiles: "linux,64bit,install4j" + maven_profiles: "linux_64bit,install4j" - os: arm - architecture: 32bit - maven_profiles: "arm,!64bit,32bit,install4j,!linux" - - os: windows architecture: 64bit - maven_profiles: "windows,64bit,install4j,!linux" + maven_profiles: "!linux_64bit,linux_arm_64bit,install4j" - os: windows - architecture: 32bit - maven_profiles: "windows32,!64bit,32bit,install4j,!linux" - - os: macOS architecture: 64bit - maven_profiles: "mac,!linux" + maven_profiles: "!linux_64bit,windows_64bit,install4j" + # - os: windows + # architecture: 32bit + # maven_profiles: "windows32,!64bit,32bit,install4j,!linux" + # - os: macOS + # architecture: 64bit + # maven_profiles: "mac,!linux" steps: - uses: actions/checkout@v2 with: ref: develop - - name: Set up JDK 17 + - name: Set up JDK 18 uses: actions/setup-java@v2 with: distribution: 'temurin' - java-version: '16' + java-version: '18' - uses: AdoptOpenJDK/install-jdk@v1 with: version: '8' @@ -87,7 +87,7 @@ jobs: - name: Download install4j uses: wei/curl@v1 with: - args: -fsSL 'https://download-gcdn.ej-technologies.com/install4j/install4j_unix_9_0_5.tar.gz' --output install4j.tar.gz + args: -fsSL 'https://download-gcdn.ej-technologies.com/install4j/install4j_unix_9_0_7.tar.gz' --output install4j.tar.gz - name: Extract install4j run: tar -zxvf install4j.tar.gz @@ -125,10 +125,10 @@ jobs: - uses: actions/checkout@v2 with: ref: develop - - name: Set up JDK 17 + - name: Set up JDK 18 uses: actions/setup-java@v1 with: - java-version: 17 + java-version: 18 - name: Get version run: echo "VERSION=$( ./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout )" >> $GITHUB_ENV - uses: actions/download-artifact@v2 diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8245efb599..a16665fd91 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -22,8 +22,8 @@ default: - ssh-keyscan -p 52150 mediathekview.de >> ~/.ssh/known_hosts - ssh-keyscan -p 52150 5.1.76.243 >> ~/.ssh/known_hosts - chmod 644 ~/.ssh/known_hosts - - wget -q https://download-gcdn.ej-technologies.com/install4j/install4j_unix_9_0_5.tar.gz - - tar -zxf install4j_unix_9_0_5.tar.gz + - wget -q https://download-gcdn.ej-technologies.com/install4j/install4j_unix_9_0_7.tar.gz -O install4j.tar.gz + - tar -zxf install4j.tar.gz - java -version - ${INSTALL4J_JAVA_HOME}/bin/java -version - mvn -v @@ -34,13 +34,13 @@ stages: - deploy-nightly-lin - deploy-nightly-linarm - deploy-nightly-win - - deploy-nightly-win32 + # - deploy-nightly-win32 - deploy-nightly-mac # - deploy-nightly-mac-as - deploy-lin - deploy-linarm - deploy-win - - deploy-win32 + # - deploy-win32 Build and Deploy nightly Linux: stage: deploy-nightly-lin @@ -56,7 +56,7 @@ Build and Deploy nightly Linux ARM: stage: deploy-nightly-linarm script: - mvn -B package -P!linux_64bit,linux_arm_64bit,install4j $MAVEN_CLI_OPTS - - /skripte/deploy.sh nightly linux-armhf $CI_COMMIT_SHA + - /skripte/deploy.sh nightly linux-aarch64 $CI_COMMIT_SHA rules: - if: '$CI_PIPELINE_SOURCE == "schedule" && $CI_COMMIT_BRANCH == "develop"' @@ -68,13 +68,13 @@ Build and Deploy nightly Windows: rules: - if: '$CI_PIPELINE_SOURCE == "schedule" && $CI_COMMIT_BRANCH == "develop"' -Build and Deploy nightly Windows 32bit: - stage: deploy-nightly-win32 - script: - - mvn -B package -P!linux_64bit,windows_32bit,install4j $MAVEN_CLI_OPTS - - /skripte/deploy.sh nightly win32 $CI_COMMIT_SHA - rules: - - if: '$CI_PIPELINE_SOURCE == "schedule" && $CI_COMMIT_BRANCH == "develop"' +# Build and Deploy nightly Windows 32bit: +# stage: deploy-nightly-win32 +# script: +# - mvn -B package -P!linux_64bit,windows_32bit,install4j $MAVEN_CLI_OPTS +# - /skripte/deploy.sh nightly win32 $CI_COMMIT_SHA +# rules: +# - if: '$CI_PIPELINE_SOURCE == "schedule" && $CI_COMMIT_BRANCH == "develop"' Build and Deploy nightly Mac Intel: stage: deploy-nightly-mac @@ -108,7 +108,7 @@ Build and Deploy Linux ARM: stage: deploy-linarm script: - mvn -B package -P!linux_64bit,linux_arm_64bit,install4j $MAVEN_CLI_OPTS - - /skripte/deploy.sh release linux-armhf + - /skripte/deploy.sh release linux-aarch64 rules: - if: $CI_COMMIT_TAG @@ -120,10 +120,10 @@ Build and Deploy Windows: rules: - if: $CI_COMMIT_TAG -Build and Deploy Windows 32bit: - stage: deploy-win32 - script: - - mvn -B package -P!linux_64bit,windows_32bit,install4j $MAVEN_CLI_OPTS - - /skripte/deploy.sh release win32 - rules: - - if: $CI_COMMIT_TAG +# Build and Deploy Windows 32bit: +# stage: deploy-win32 +# script: +# - mvn -B package -P!linux_64bit,windows_32bit,install4j $MAVEN_CLI_OPTS +# - /skripte/deploy.sh release win32 +# rules: +# - if: $CI_COMMIT_TAG diff --git a/.install4j/mediathekview_arm.install4j b/.install4j/mediathekview_arm.install4j index 69869e197f..8fc123f70f 100644 --- a/.install4j/mediathekview_arm.install4j +++ b/.install4j/mediathekview_arm.install4j @@ -1,7 +1,7 @@ - + - + @@ -61,7 +61,7 @@ - + @@ -1004,22 +1004,22 @@ return true; - - + + - - + + - - + + diff --git a/.install4j/mediathekview_linux.install4j b/.install4j/mediathekview_linux.install4j index 14026c7cdc..67e4416b5d 100644 --- a/.install4j/mediathekview_linux.install4j +++ b/.install4j/mediathekview_linux.install4j @@ -1,5 +1,5 @@ - + diff --git a/.install4j/mediathekview_windows.install4j b/.install4j/mediathekview_windows.install4j index 2bb29188ad..66611995ca 100644 --- a/.install4j/mediathekview_windows.install4j +++ b/.install4j/mediathekview_windows.install4j @@ -1,5 +1,5 @@ - + diff --git a/pom.xml b/pom.xml index ec4ef1d788..d972aa6485 100755 --- a/pom.xml +++ b/pom.xml @@ -141,7 +141,7 @@ -Dfile.encoding=UTF-8 -DexternalUpdateCheck - ./install4j9.0.5 + ./install4j${install4j.version} ${env.LICENSE_KEY_9} 9.0.7 From 095d8c11a70557183754c0c4bb044d3c3a543380 Mon Sep 17 00:00:00 2001 From: Alexander F Date: Sun, 17 Jul 2022 12:52:46 +0200 Subject: [PATCH 190/422] binary Dateien aus dem res Ordner entfernt und in ein seperates Repo verfrachtet. Former-commit-id: 6b2bbedd532b5586b01e6b7adc45c16f3b154ce5 --- .gitlab-ci.yml | 3 + res/.resinfo | 7 + res/bin/aria2-remote.sh | 69 ---- res/bin/ffmpeg.exe | 3 - res/bin/ffmpeg.txt | 743 ---------------------------------------- res/bin/ffmpeg32.exe | 3 - res/bin/ffmpeg32.txt | 114 ------ 7 files changed, 10 insertions(+), 932 deletions(-) create mode 100644 res/.resinfo delete mode 100755 res/bin/aria2-remote.sh delete mode 100644 res/bin/ffmpeg.exe delete mode 100644 res/bin/ffmpeg.txt delete mode 100755 res/bin/ffmpeg32.exe delete mode 100644 res/bin/ffmpeg32.txt diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a16665fd91..872f3877f1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -29,6 +29,9 @@ default: - mvn -v - mvn clean - mvn install4j:install-license -Pinstall4j + - git clone https://gitlab.com/mediathekview/mediathekview-buildres.git tmpres + - mv tmpres/bin res/ + - rm -rf tmpres stages: - deploy-nightly-lin diff --git a/res/.resinfo b/res/.resinfo new file mode 100644 index 0000000000..3611f599ac --- /dev/null +++ b/res/.resinfo @@ -0,0 +1,7 @@ +Die Ressourcen, wie ffmpeg und Shellskripte aus dem bin Ordner findest du hier: +https://gitlab.com/mediathekview/mediathekview-buildres + +Im Repo benutze folgende Befehle um die Ressourcen zu erhalten: +git clone https://gitlab.com/mediathekview/mediathekview-buildres.git tmpres +mv tmpres/bin res/ +rm -rf tmpres diff --git a/res/bin/aria2-remote.sh b/res/bin/aria2-remote.sh deleted file mode 100755 index 987d49285c..0000000000 --- a/res/bin/aria2-remote.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/bash - -# Das Skript muss evtl. noch -# "ausführbar" gemacht werden!!! - -# Programmparameter in MediathekView -# %f ** - -# oder ohne gesetztes Passwort -# %f ** - -# Start von aria2 auf dem Server -# aria2c --enable-rpc --rpc-listen-all --dir=/ZIELPFAD/FILME - - -log=/tmp/MediathekView-aria2.log -echo "Running $0 $*" >$log - -url=$1 -filename=$(basename "$2") - -aria2_server_url=$3 -aria2_server_secret=$4 - -#echo >>$log -#echo "MediathekView-aria2 parameters:" >>$log -#while [ -n "$1" ] -#do -# echo "$1" >>$log -# shift -#done - -echo >>$log -echo "url: " $url >>$log -echo "filename: " $filename >>$log -echo "aria2_server_url: " $aria2_server_url >>$log -echo "aria2_server_secret: " $aria2_server_secret >>$log - -id=medview -method=aria2.addUri - -if [ -n "$filename" ] -then - options=",{\"out\":\"${filename}\"}" -else - options="" -fi - -params="[\"token:${aria2_server_secret}\",[\"${url}\"]${options}]" -params_base64enc=$(echo "${params}" | base64 -w 0 -) -params_base64enc_urlenc=${params_base64enc//=/%3D} - -get="${aria2_server_url}?id=${id}&method=${method}¶ms=${params_base64enc_urlenc}" - -echo >>$log -echo "executing: $get" >>$log - -result=$(curl -S -k "$get") - -echo >>$log -echo "result: $result" >>$log - -if [[ $result =~ '"result":' ]] -then - exit 0 -else - exit 1 -fi - diff --git a/res/bin/ffmpeg.exe b/res/bin/ffmpeg.exe deleted file mode 100644 index d6f10660c0..0000000000 --- a/res/bin/ffmpeg.exe +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0557f26bb233a46339cd54c18887eb115cafec8d22ff1f67ac2681ed3a4f5e3f -size 121831424 diff --git a/res/bin/ffmpeg.txt b/res/bin/ffmpeg.txt deleted file mode 100644 index a1966c9ddc..0000000000 --- a/res/bin/ffmpeg.txt +++ /dev/null @@ -1,743 +0,0 @@ -FFmpeg 64-bit static Windows build from www.gyan.dev - -Version: 5.0.1-full_build-www.gyan.dev - -License: GPL v3 - -Source Code: https://github.com/FFmpeg/FFmpeg/commit/9687cae2b4 - -release-full build configuration: - -ARCH x86 (generic) -big-endian no -runtime cpu detection yes -standalone assembly yes -x86 assembler nasm -MMX enabled yes -MMXEXT enabled yes -3DNow! enabled yes -3DNow! extended enabled yes -SSE enabled yes -SSSE3 enabled yes -AESNI enabled yes -AVX enabled yes -AVX2 enabled yes -AVX-512 enabled yes -XOP enabled yes -FMA3 enabled yes -FMA4 enabled yes -i686 features enabled yes -CMOV is fast yes -EBX available yes -EBP available yes -debug symbols yes -strip symbols yes -optimize for size no -optimizations yes -static yes -shared no -postprocessing support yes -network support yes -threading support pthreads -safe bitstream reader yes -texi2html enabled no -perl enabled yes -pod2man enabled yes -makeinfo enabled yes -makeinfo supports HTML yes -xmllint enabled yes - -External libraries: -avisynth libilbc libtheora -bzlib liblensfun libtwolame -chromaprint libmodplug libuavs3d -frei0r libmp3lame libvidstab -gmp libmysofa libvmaf -gnutls libopencore_amrnb libvo_amrwbenc -iconv libopencore_amrwb libvorbis -ladspa libopenjpeg libvpx -libaom libopenmpt libwebp -libass libopus libx264 -libbluray libplacebo libx265 -libbs2b librav1e libxavs2 -libcaca librist libxml2 -libcdio librubberband libxvid -libdav1d libshaderc libzimg -libdavs2 libshine libzmq -libflite libsnappy libzvbi -libfontconfig libsoxr lzma -libfreetype libspeex mediafoundation -libfribidi libsrt sdl2 -libgme libssh zlib -libgsm libsvtav1 - -External libraries providing hardware acceleration: -amf d3d11va nvdec -cuda dxva2 nvenc -cuda_llvm ffnvcodec opencl -cuvid libmfx vulkan - -Libraries: -avcodec avformat swresample -avdevice avutil swscale -avfilter postproc - -Programs: -ffmpeg ffplay ffprobe - -Enabled decoders: -aac fourxm pcm_u32be -aac_fixed fraps pcm_u32le -aac_latm frwu pcm_u8 -aasc g2m pcm_vidc -ac3 g723_1 pcx -ac3_fixed g729 pfm -acelp_kelvin gdv pgm -adpcm_4xm gem pgmyuv -adpcm_adx gif pgssub -adpcm_afc gremlin_dpcm pgx -adpcm_agm gsm photocd -adpcm_aica gsm_ms pictor -adpcm_argo h261 pixlet -adpcm_ct h263 pjs -adpcm_dtk h263i png -adpcm_ea h263p ppm -adpcm_ea_maxis_xa h264 prores -adpcm_ea_r1 h264_cuvid prosumer -adpcm_ea_r2 h264_qsv psd -adpcm_ea_r3 hap ptx -adpcm_ea_xas hca qcelp -adpcm_g722 hcom qdm2 -adpcm_g726 hevc qdmc -adpcm_g726le hevc_cuvid qdraw -adpcm_ima_acorn hevc_qsv qpeg -adpcm_ima_alp hnm4_video qtrle -adpcm_ima_amv hq_hqa r10k -adpcm_ima_apc hqx r210 -adpcm_ima_apm huffyuv ra_144 -adpcm_ima_cunning hymt ra_288 -adpcm_ima_dat4 iac ralf -adpcm_ima_dk3 idcin rasc -adpcm_ima_dk4 idf rawvideo -adpcm_ima_ea_eacs iff_ilbm realtext -adpcm_ima_ea_sead ilbc rl2 -adpcm_ima_iss imc roq -adpcm_ima_moflex imm4 roq_dpcm -adpcm_ima_mtf imm5 rpza -adpcm_ima_oki indeo2 rscc -adpcm_ima_qt indeo3 rv10 -adpcm_ima_rad indeo4 rv20 -adpcm_ima_smjpeg indeo5 rv30 -adpcm_ima_ssi interplay_acm rv40 -adpcm_ima_wav interplay_dpcm s302m -adpcm_ima_ws interplay_video sami -adpcm_ms ipu sanm -adpcm_mtaf jacosub sbc -adpcm_psx jpeg2000 scpr -adpcm_sbpro_2 jpegls screenpresso -adpcm_sbpro_3 jv sdx2_dpcm -adpcm_sbpro_4 kgv1 sga -adpcm_swf kmvc sgi -adpcm_thp lagarith sgirle -adpcm_thp_le libaom_av1 sheervideo -adpcm_vima libdav1d shorten -adpcm_xa libdavs2 simbiosis_imx -adpcm_yamaha libgsm sipr -adpcm_zork libgsm_ms siren -agm libilbc smackaud -aic libopencore_amrnb smacker -alac libopencore_amrwb smc -alias_pix libopenjpeg smvjpeg -als libopus snow -amrnb libspeex sol_dpcm -amrwb libuavs3d sonic -amv libvorbis sp5x -anm libvpx_vp8 speedhq -ansi libvpx_vp9 speex -ape libzvbi_teletext srgc -apng loco srt -aptx lscr ssa -aptx_hd m101 stl -arbc mace3 subrip -argo mace6 subviewer -ass magicyuv subviewer1 -asv1 mdec sunrast -asv2 metasound svq1 -atrac1 microdvd svq3 -atrac3 mimic tak -atrac3al mjpeg targa -atrac3p mjpeg_cuvid targa_y216 -atrac3pal mjpeg_qsv tdsc -atrac9 mjpegb text -aura mlp theora -aura2 mmvideo thp -av1 mobiclip tiertexseqvideo -av1_cuvid motionpixels tiff -av1_qsv movtext tmv -avrn mp1 truehd -avrp mp1float truemotion1 -avs mp2 truemotion2 -avui mp2float truemotion2rt -ayuv mp3 truespeech -bethsoftvid mp3adu tscc -bfi mp3adufloat tscc2 -bink mp3float tta -binkaudio_dct mp3on4 twinvq -binkaudio_rdft mp3on4float txd -bintext mpc7 ulti -bitpacked mpc8 utvideo -bmp mpeg1_cuvid v210 -bmv_audio mpeg1video v210x -bmv_video mpeg2_cuvid v308 -brender_pix mpeg2_qsv v408 -c93 mpeg2video v410 -cavs mpeg4 vb -ccaption mpeg4_cuvid vble -cdgraphics mpegvideo vc1 -cdtoons mpl2 vc1_cuvid -cdxl msa1 vc1_qsv -cfhd mscc vc1image -cinepak msmpeg4v1 vcr1 -clearvideo msmpeg4v2 vmdaudio -cljr msmpeg4v3 vmdvideo -cllc msnsiren vmnc -comfortnoise msp2 vorbis -cook msrle vp3 -cpia mss1 vp4 -cri mss2 vp5 -cscd msvideo1 vp6 -cyuv mszh vp6a -dca mts2 vp6f -dds mv30 vp7 -derf_dpcm mvc1 vp8 -dfa mvc2 vp8_cuvid -dirac mvdv vp8_qsv -dnxhd mvha vp9 -dolby_e mwsc vp9_cuvid -dpx mxpeg vp9_qsv -dsd_lsbf nellymoser vplayer -dsd_lsbf_planar notchlc vqa -dsd_msbf nuv wavpack -dsd_msbf_planar on2avc wcmv -dsicinaudio opus webp -dsicinvideo paf_audio webvtt -dss_sp paf_video wmalossless -dst pam wmapro -dvaudio pbm wmav1 -dvbsub pcm_alaw wmav2 -dvdsub pcm_bluray wmavoice -dvvideo pcm_dvd wmv1 -dxa pcm_f16le wmv2 -dxtory pcm_f24le wmv3 -dxv pcm_f32be wmv3image -eac3 pcm_f32le wnv1 -eacmv pcm_f64be wrapped_avframe -eamad pcm_f64le ws_snd1 -eatgq pcm_lxf xan_dpcm -eatgv pcm_mulaw xan_wc3 -eatqi pcm_s16be xan_wc4 -eightbps pcm_s16be_planar xbin -eightsvx_exp pcm_s16le xbm -eightsvx_fib pcm_s16le_planar xface -escape124 pcm_s24be xl -escape130 pcm_s24daud xma1 -evrc pcm_s24le xma2 -exr pcm_s24le_planar xpm -fastaudio pcm_s32be xsub -ffv1 pcm_s32le xwd -ffvhuff pcm_s32le_planar y41p -ffwavesynth pcm_s64be ylc -fic pcm_s64le yop -fits pcm_s8 yuv4 -flac pcm_s8_planar zero12v -flashsv pcm_sga zerocodec -flashsv2 pcm_u16be zlib -flic pcm_u16le zmbv -flv pcm_u24be -fmvc pcm_u24le - -Enabled encoders: -a64multi jpeg2000 pcm_u16le -a64multi5 jpegls pcm_u24be -aac libaom_av1 pcm_u24le -aac_mf libgsm pcm_u32be -ac3 libgsm_ms pcm_u32le -ac3_fixed libilbc pcm_u8 -ac3_mf libmp3lame pcm_vidc -adpcm_adx libopencore_amrnb pcx -adpcm_argo libopenjpeg pfm -adpcm_g722 libopus pgm -adpcm_g726 librav1e pgmyuv -adpcm_g726le libshine png -adpcm_ima_alp libspeex ppm -adpcm_ima_amv libsvtav1 prores -adpcm_ima_apm libtheora prores_aw -adpcm_ima_qt libtwolame prores_ks -adpcm_ima_ssi libvo_amrwbenc qtrle -adpcm_ima_wav libvorbis r10k -adpcm_ima_ws libvpx_vp8 r210 -adpcm_ms libvpx_vp9 ra_144 -adpcm_swf libwebp rawvideo -adpcm_yamaha libwebp_anim roq -alac libx264 roq_dpcm -alias_pix libx264rgb rpza -amv libx265 rv10 -apng libxavs2 rv20 -aptx libxvid s302m -aptx_hd ljpeg sbc -ass magicyuv sgi -asv1 mjpeg smc -asv2 mjpeg_qsv snow -avrp mlp sonic -avui movtext sonic_ls -ayuv mp2 speedhq -bitpacked mp2fixed srt -bmp mp3_mf ssa -cfhd mpeg1video subrip -cinepak mpeg2_qsv sunrast -cljr mpeg2video svq1 -comfortnoise mpeg4 targa -dca msmpeg4v2 text -dnxhd msmpeg4v3 tiff -dpx msvideo1 truehd -dvbsub nellymoser tta -dvdsub opus ttml -dvvideo pam utvideo -eac3 pbm v210 -exr pcm_alaw v308 -ffv1 pcm_dvd v408 -ffvhuff pcm_f32be v410 -fits pcm_f32le vc2 -flac pcm_f64be vorbis -flashsv pcm_f64le vp9_qsv -flashsv2 pcm_mulaw wavpack -flv pcm_s16be webvtt -g723_1 pcm_s16be_planar wmav1 -gif pcm_s16le wmav2 -h261 pcm_s16le_planar wmv1 -h263 pcm_s24be wmv2 -h263p pcm_s24daud wrapped_avframe -h264_amf pcm_s24le xbm -h264_mf pcm_s24le_planar xface -h264_nvenc pcm_s32be xsub -h264_qsv pcm_s32le xwd -hap pcm_s32le_planar y41p -hevc_amf pcm_s64be yuv4 -hevc_mf pcm_s64le zlib -hevc_nvenc pcm_s8 zmbv -hevc_qsv pcm_s8_planar -huffyuv pcm_u16be - -Enabled hwaccels: -av1_d3d11va hevc_nvdec vc1_nvdec -av1_d3d11va2 mjpeg_nvdec vp8_nvdec -av1_dxva2 mpeg1_nvdec vp9_d3d11va -av1_nvdec mpeg2_d3d11va vp9_d3d11va2 -h264_d3d11va mpeg2_d3d11va2 vp9_dxva2 -h264_d3d11va2 mpeg2_dxva2 vp9_nvdec -h264_dxva2 mpeg2_nvdec wmv3_d3d11va -h264_nvdec mpeg4_nvdec wmv3_d3d11va2 -hevc_d3d11va vc1_d3d11va wmv3_dxva2 -hevc_d3d11va2 vc1_d3d11va2 wmv3_nvdec -hevc_dxva2 vc1_dxva2 - -Enabled parsers: -aac dvbsub mpegvideo -aac_latm dvd_nav opus -ac3 dvdsub png -adx flac pnm -amr g723_1 rv30 -av1 g729 rv40 -avs2 gif sbc -avs3 gsm sipr -bmp h261 tak -cavsvideo h263 vc1 -cook h264 vorbis -cri hevc vp3 -dca ipu vp8 -dirac jpeg2000 vp9 -dnxhd mjpeg webp -dolby_e mlp xbm -dpx mpeg4video xma -dvaudio mpegaudio - -Enabled demuxers: -aa ico pcm_f64le -aac idcin pcm_mulaw -aax idf pcm_s16be -ac3 iff pcm_s16le -ace ifv pcm_s24be -acm ilbc pcm_s24le -act image2 pcm_s32be -adf image2_alias_pix pcm_s32le -adp image2_brender_pix pcm_s8 -ads image2pipe pcm_u16be -adx image_bmp_pipe pcm_u16le -aea image_cri_pipe pcm_u24be -afc image_dds_pipe pcm_u24le -aiff image_dpx_pipe pcm_u32be -aix image_exr_pipe pcm_u32le -alp image_gem_pipe pcm_u8 -amr image_gif_pipe pcm_vidc -amrnb image_j2k_pipe pjs -amrwb image_jpeg_pipe pmp -anm image_jpegls_pipe pp_bnk -apc image_pam_pipe pva -ape image_pbm_pipe pvf -apm image_pcx_pipe qcp -apng image_pgm_pipe r3d -aptx image_pgmyuv_pipe rawvideo -aptx_hd image_pgx_pipe realtext -aqtitle image_photocd_pipe redspark -argo_asf image_pictor_pipe rl2 -argo_brp image_png_pipe rm -argo_cvg image_ppm_pipe roq -asf image_psd_pipe rpl -asf_o image_qdraw_pipe rsd -ass image_sgi_pipe rso -ast image_sunrast_pipe rtp -au image_svg_pipe rtsp -av1 image_tiff_pipe s337m -avi image_webp_pipe sami -avisynth image_xbm_pipe sap -avr image_xpm_pipe sbc -avs image_xwd_pipe sbg -avs2 imf scc -avs3 ingenient scd -bethsoftvid ipmovie sdp -bfi ipu sdr2 -bfstm ircam sds -bink iss sdx -binka iv8 segafilm -bintext ivf ser -bit ivr sga -bitpacked jacosub shorten -bmv jv siff -boa kux simbiosis_imx -brstm kvag sln -c93 libgme smacker -caf libmodplug smjpeg -cavsvideo libopenmpt smush -cdg live_flv sol -cdxl lmlm4 sox -cine loas spdif -codec2 lrc srt -codec2raw luodat stl -concat lvf str -dash lxf subviewer -data m4v subviewer1 -daud matroska sup -dcstr mca svag -derf mcc svs -dfa mgsts swf -dhav microdvd tak -dirac mjpeg tedcaptions -dnxhd mjpeg_2000 thp -dsf mlp threedostr -dsicin mlv tiertexseq -dss mm tmv -dts mmf truehd -dtshd mods tta -dv moflex tty -dvbsub mov txd -dvbtxt mp3 ty -dxa mpc v210 -ea mpc8 v210x -ea_cdata mpegps vag -eac3 mpegts vc1 -epaf mpegtsraw vc1t -ffmetadata mpegvideo vividas -filmstrip mpjpeg vivo -fits mpl2 vmd -flac mpsub vobsub -flic msf voc -flv msnwc_tcp vpk -fourxm msp vplayer -frm mtaf vqf -fsb mtv w64 -fwse musx wav -g722 mv wc3 -g723_1 mvi webm_dash_manifest -g726 mxf webvtt -g726le mxg wsaud -g729 nc wsd -gdv nistsphere wsvqa -genh nsp wtv -gif nsv wv -gsm nut wve -gxf nuv xa -h261 obu xbin -h263 ogg xmv -h264 oma xvag -hca paf xwma -hcom pcm_alaw yop -hevc pcm_f32be yuv4mpegpipe -hls pcm_f32le -hnm pcm_f64be - -Enabled muxers: -a64 h264 pcm_s24be -ac3 hash pcm_s24le -adts hds pcm_s32be -adx hevc pcm_s32le -aiff hls pcm_s8 -alp ico pcm_u16be -amr ilbc pcm_u16le -amv image2 pcm_u24be -apm image2pipe pcm_u24le -apng ipod pcm_u32be -aptx ircam pcm_u32le -aptx_hd ismv pcm_u8 -argo_asf ivf pcm_vidc -argo_cvg jacosub psp -asf kvag rawvideo -asf_stream latm rm -ass lrc roq -ast m4v rso -au matroska rtp -avi matroska_audio rtp_mpegts -avm2 md5 rtsp -avs2 microdvd sap -avs3 mjpeg sbc -bit mkvtimestamp_v2 scc -caf mlp segafilm -cavsvideo mmf segment -chromaprint mov smjpeg -codec2 mp2 smoothstreaming -codec2raw mp3 sox -crc mp4 spdif -dash mpeg1system spx -data mpeg1vcd srt -daud mpeg1video stream_segment -dirac mpeg2dvd streamhash -dnxhd mpeg2svcd sup -dts mpeg2video swf -dv mpeg2vob tee -eac3 mpegts tg2 -f4v mpjpeg tgp -ffmetadata mxf truehd -fifo mxf_d10 tta -fifo_test mxf_opatom ttml -filmstrip null uncodedframecrc -fits nut vc1 -flac obu vc1t -flv oga voc -framecrc ogg w64 -framehash ogv wav -framemd5 oma webm -g722 opus webm_chunk -g723_1 pcm_alaw webm_dash_manifest -g726 pcm_f32be webp -g726le pcm_f32le webvtt -gif pcm_f64be wsaud -gsm pcm_f64le wtv -gxf pcm_mulaw wv -h261 pcm_s16be yuv4mpegpipe -h263 pcm_s16le - -Enabled protocols: -async http rtmpe -bluray httpproxy rtmps -cache https rtmpt -concat icecast rtmpte -concatf librist rtmpts -crypto libsrt rtp -data libssh srtp -ffrtmpcrypt libzmq subfile -ffrtmphttp md5 tcp -file mmsh tee -ftp mmst tls -gopher pipe udp -gophers prompeg udplite -hls rtmp - -Enabled filters: -abench dedot pad_opencl -abitscope deesser pal100bars -acompressor deflate pal75bars -acontrast deflicker palettegen -acopy deinterlace_qsv paletteuse -acrossfade dejudder pan -acrossover delogo perms -acrusher derain perspective -acue deshake phase -addroi deshake_opencl photosensitivity -adeclick despill pixdesctest -adeclip detelecine pixscope -adecorrelate dilation pp -adelay dilation_opencl pp7 -adenorm displace premultiply -aderivative dnn_classify prewitt -adrawgraph dnn_detect prewitt_opencl -adynamicequalizer dnn_processing program_opencl -adynamicsmooth doubleweave pseudocolor -aecho drawbox psnr -aemphasis drawgraph pullup -aeval drawgrid qp -aevalsrc drawtext random -aexciter drmeter readeia608 -afade dynaudnorm readvitc -afftdn earwax realtime -afftfilt ebur128 remap -afifo edgedetect removegrain -afir elbg removelogo -afirsrc entropy repeatfields -aformat epx replaygain -afreqshift eq reverse -afwtdn equalizer rgbashift -agate erosion rgbtestsrc -agraphmonitor erosion_opencl roberts -ahistogram estdif roberts_opencl -aiir exposure rotate -aintegral extractplanes rubberband -ainterleave extrastereo sab -alatency fade scale -alimiter fftdnoiz scale2ref -allpass fftfilt scale_cuda -allrgb field scale_qsv -allyuv fieldhint scale_vulkan -aloop fieldmatch scdet -alphaextract fieldorder scharr -alphamerge fifo scroll -amerge fillborders segment -ametadata find_rect select -amix firequalizer selectivecolor -amovie flanger sendcmd -amplify flip_vulkan separatefields -amultiply flite setdar -anequalizer floodfill setfield -anlmdn format setparams -anlmf fps setpts -anlms framepack setrange -anoisesrc framerate setsar -anull framestep settb -anullsink freezedetect shear -anullsrc freezeframes showcqt -apad frei0r showfreqs -aperms frei0r_src showinfo -aphasemeter fspp showpalette -aphaser gblur showspatial -aphaseshift gblur_vulkan showspectrum -apsyclip geq showspectrumpic -apulsator gradfun showvolume -arealtime gradients showwaves -aresample graphmonitor showwavespic -areverse grayworld shuffleframes -arnndn greyedge shufflepixels -asdr guided shuffleplanes -asegment haas sidechaincompress -aselect haldclut sidechaingate -asendcmd haldclutsrc sidedata -asetnsamples hdcd sierpinski -asetpts headphone signalstats -asetrate hflip signature -asettb hflip_vulkan silencedetect -ashowinfo highpass silenceremove -asidedata highshelf sinc -asoftclip hilbert sine -aspectralstats histeq smartblur -asplit histogram smptebars -ass hqdn3d smptehdbars -astats hqx sobel -astreamselect hstack sobel_opencl -asubboost hsvhold sofalizer -asubcut hsvkey spectrumsynth -asupercut hue speechnorm -asuperpass huesaturation split -asuperstop hwdownload spp -atadenoise hwmap sr -atempo hwupload ssim -atilt hwupload_cuda stereo3d -atrim hysteresis stereotools -avectorscope identity stereowiden -avgblur idet streamselect -avgblur_opencl il subtitles -avgblur_vulkan inflate super2xsai -axcorrelate interlace superequalizer -azmq interleave surround -bandpass join swaprect -bandreject kerndeint swapuv -bass kirsch tblend -bbox ladspa telecine -bench lagfun testsrc -bilateral latency testsrc2 -biquad lenscorrection thistogram -bitplanenoise lensfun threshold -blackdetect libplacebo thumbnail -blackframe libvmaf thumbnail_cuda -blend life tile -bm3d limitdiff tinterlace -boxblur limiter tlut2 -boxblur_opencl loop tmedian -bs2b loudnorm tmidequalizer -bwdif lowpass tmix -cas lowshelf tonemap -cellauto lumakey tonemap_opencl -channelmap lut tpad -channelsplit lut1d transpose -chorus lut2 transpose_opencl -chromaber_vulkan lut3d transpose_vulkan -chromahold lutrgb treble -chromakey lutyuv tremolo -chromanr mandelbrot trim -chromashift maskedclamp unpremultiply -ciescope maskedmax unsharp -codecview maskedmerge unsharp_opencl -color maskedmin untile -colorbalance maskedthreshold v360 -colorchannelmixer maskfun vaguedenoiser -colorcontrast mcompand varblur -colorcorrect median vectorscope -colorhold mergeplanes vflip -colorize mestimate vflip_vulkan -colorkey metadata vfrdet -colorkey_opencl midequalizer vibrance -colorlevels minterpolate vibrato -colormatrix mix vidstabdetect -colorspace monochrome vidstabtransform -colorspectrum morpho vif -colortemperature movie vignette -compand mpdecimate vmafmotion -compensationdelay mptestsrc volume -concat msad volumedetect -convolution negate vpp_qsv -convolution_opencl nlmeans vstack -convolve nlmeans_opencl w3fdif -copy nnedi waveform -cover_rect noformat weave -crop noise xbr -cropdetect normalize xcorrelate -crossfeed null xfade -crystalizer nullsink xfade_opencl -cue nullsrc xmedian -curves openclsrc xstack -datascope oscilloscope yadif -dblur overlay yadif_cuda -dcshift overlay_cuda yaepblur -dctdnoiz overlay_opencl yuvtestsrc -deband overlay_qsv zmq -deblock overlay_vulkan zoompan -decimate owdenoise zscale -deconvolve pad - -Enabled bsfs: -aac_adtstoasc hapqa_extract pcm_rechunk -av1_frame_merge hevc_metadata prores_metadata -av1_frame_split hevc_mp4toannexb remove_extradata -av1_metadata imx_dump_header setts -chomp mjpeg2jpeg text2movsub -dca_core mjpega_dump_header trace_headers -dump_extradata mov2textsub truehd_core -eac3_core mp3_header_decompress vp9_metadata -extract_extradata mpeg2_metadata vp9_raw_reorder -filter_units mpeg4_unpack_bframes vp9_superframe -h264_metadata noise vp9_superframe_split -h264_mp4toannexb null -h264_redundant_pps opus_metadata - -Enabled indevs: -dshow lavfi vfwcap -gdigrab libcdio - -Enabled outdevs: -caca sdl2 diff --git a/res/bin/ffmpeg32.exe b/res/bin/ffmpeg32.exe deleted file mode 100755 index a1d3858960..0000000000 --- a/res/bin/ffmpeg32.exe +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2c9d51e085f9c2921616a040554cd3e621c265f01aa2d13fac918dcdc6e9ae3a -size 53479424 diff --git a/res/bin/ffmpeg32.txt b/res/bin/ffmpeg32.txt deleted file mode 100644 index fbcec27d0b..0000000000 --- a/res/bin/ffmpeg32.txt +++ /dev/null @@ -1,114 +0,0 @@ -Zeranoe FFmpeg Builds - -Build: ffmpeg-4.3-win32-static - -Configuration: - --enable-gpl - --enable-version3 - --enable-sdl2 - --enable-fontconfig - --enable-gnutls - --enable-iconv - --enable-libass - --enable-libdav1d - --enable-libbluray - --enable-libfreetype - --enable-libmp3lame - --enable-libopencore-amrnb - --enable-libopencore-amrwb - --enable-libopenjpeg - --enable-libopus - --enable-libshine - --enable-libsnappy - --enable-libsoxr - --enable-libsrt - --enable-libtheora - --enable-libtwolame - --enable-libvpx - --enable-libwavpack - --enable-libwebp - --enable-libx264 - --enable-libx265 - --enable-libxml2 - --enable-libzimg - --enable-lzma - --enable-zlib - --enable-gmp - --enable-libvidstab - --enable-libvmaf - --enable-libvorbis - --enable-libvo-amrwbenc - --enable-libmysofa - --enable-libspeex - --enable-libxvid - --enable-libaom - --enable-libgsm - --disable-w32threads - --enable-libmfx - --enable-ffnvcodec - --enable-cuda-llvm - --enable-cuvid - --enable-d3d11va - --enable-nvenc - --enable-nvdec - --enable-dxva2 - --enable-avisynth - --enable-libopenmpt - --enable-amf - -Libraries: - SDL 20200619-fb6395c - Fontconfig 2.13.92 - GnuTLS 3.6.14 - libiconv 1.16 - libass 20200615-7959e61 - dav1d 20200621-54f9206 - libbluray 20200517-245baa7 - FreeType 2.10.2 - LAME 3.100 - OpenCORE AMR 20170731-07a5be4 - OpenJPEG 20200610-25fb144 - Opus 20200618-f8ed894 - shine 20190420-76ea4f0 - Snappy 1.1.8 - libsoxr 20180224-945b592 - SRT 20200520-9f7068d - Theora 20200618-f98989a - TwoLAME 0.4.0 - vpx 20200619-d9a69a1 - WavPack 5.3.0 - WebP 20200505-e3c259a - x264 20200615-4c9b076 - x265 20200529-73ca1d7 - libxml2 2.9.10 - z.lib 20200604-6dec143 - XZ Utils 5.2.5 - zlib 1.2.11 - GMP 6.2.0 - vid.stab 20190213-aeabc8d - VMAF 20200617-3ff32b8 - Vorbis 20200616-5fd186e - VisualOn AMR-WB 20141107-3b3fcd0 - libmysofa 20200614-90f0089 - Speex 20191110-7db954e - Xvid 1.3.5 - aom 20200620-8c113ea - GSM 1.0.19 - libmfx 1.28 - nv-codec-headers 20191126-250292d - AviSynth+ 20200619-1eb7ce6 - OpenMPT 20200621-9082512 - AMF 20200515-802f92e - -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 . From 49010f9c9613966520515fafdda1ecf59689901a Mon Sep 17 00:00:00 2001 From: Christian F Date: Mon, 18 Jul 2022 07:08:44 +0200 Subject: [PATCH 191/422] - remove stopwatch code --- src/main/java/mediathek/daten/ListeAbo.java | 19 +++++++------------ .../daten/blacklist/ListeBlacklist.java | 7 ------- .../java/mediathek/filmlisten/FilmeLaden.java | 4 ---- 3 files changed, 7 insertions(+), 23 deletions(-) diff --git a/src/main/java/mediathek/daten/ListeAbo.java b/src/main/java/mediathek/daten/ListeAbo.java index 6ab5e41551..87a6d4385f 100644 --- a/src/main/java/mediathek/daten/ListeAbo.java +++ b/src/main/java/mediathek/daten/ListeAbo.java @@ -19,7 +19,6 @@ */ package mediathek.daten; -import com.google.common.base.Stopwatch; import mediathek.config.Daten; import mediathek.config.MVConfig; import mediathek.daten.abo.DatenAbo; @@ -28,8 +27,6 @@ import mediathek.gui.messages.AboListChangedEvent; import mediathek.mainwindow.MediathekGui; import mediathek.tool.*; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -38,7 +35,6 @@ public class ListeAbo extends ArrayList { private static final String[] LEER = {""}; - private static final Logger logger = LogManager.getLogger(); private int nr; private int parseMinSize() { @@ -193,13 +189,15 @@ private void assignAboToFilm(@NotNull DatenFilm film) { ifPresentOrElse(film::setAbo, () -> deleteAboInFilm(film)); } + /** + * Hier wird tatsächlich für jeden Film die Liste der Abos durchsucht. + * Braucht länger. + * @param listeFilme Die Filmliste + * @param aboLoeschen abo löschen? + */ public void setAboFuerFilm(ListeFilme listeFilme, boolean aboLoeschen) { - Stopwatch stopwatch = Stopwatch.createStarted(); - // hier wird tatsächlich für jeden Film die Liste der Abos durchsucht - // braucht länger - if (this.isEmpty() && aboLoeschen) { - listeFilme.parallelStream().forEach(this::deleteAboInFilm); + listeFilme.forEach(this::deleteAboInFilm); return; } @@ -218,8 +216,5 @@ public void setAboFuerFilm(ListeFilme listeFilme, boolean aboLoeschen) { datenAbo.setThemaFilterPattern(LEER); datenAbo.setIrgendwoFilterPattern(LEER); }); - - stopwatch.stop(); - logger.debug("setAboFuerFilm: {}", stopwatch); } } diff --git a/src/main/java/mediathek/daten/blacklist/ListeBlacklist.java b/src/main/java/mediathek/daten/blacklist/ListeBlacklist.java index a94de913e3..6b9a78bd85 100644 --- a/src/main/java/mediathek/daten/blacklist/ListeBlacklist.java +++ b/src/main/java/mediathek/daten/blacklist/ListeBlacklist.java @@ -1,6 +1,5 @@ package mediathek.daten.blacklist; -import com.google.common.base.Stopwatch; import mediathek.config.Daten; import mediathek.config.MVConfig; import mediathek.daten.DatenFilm; @@ -11,8 +10,6 @@ import mediathek.tool.ApplicationConfiguration; import mediathek.tool.Filter; import mediathek.tool.MessageBus; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; @@ -21,7 +18,6 @@ public class ListeBlacklist extends ArrayList { - private static final Logger logger = LogManager.getLogger(ListeBlacklist.class); private final GeoblockingPredicate geoblockingPredicate = new GeoblockingPredicate(); /** * This specifies the lower boundary for all films to be shown or not. @@ -92,7 +88,6 @@ public synchronized void clear() { * Main filtering routine */ public synchronized void filterListe() { - Stopwatch stopwatch = Stopwatch.createStarted(); final Daten daten = Daten.getInstance(); final ListeFilme completeFilmList = daten.getListeFilme(); final ListeFilme filteredList = daten.getListeFilmeNachBlackList(); @@ -116,8 +111,6 @@ public synchronized void filterListe() { setupNewEntries(); } - stopwatch.stop(); - logger.trace("Complete filtering took: {}", stopwatch); } /** diff --git a/src/main/java/mediathek/filmlisten/FilmeLaden.java b/src/main/java/mediathek/filmlisten/FilmeLaden.java index f69903b126..920cb8ed77 100644 --- a/src/main/java/mediathek/filmlisten/FilmeLaden.java +++ b/src/main/java/mediathek/filmlisten/FilmeLaden.java @@ -1,6 +1,5 @@ package mediathek.filmlisten; -import com.google.common.base.Stopwatch; import mediathek.config.Daten; import mediathek.config.Konstanten; import mediathek.config.StandardLocations; @@ -338,7 +337,6 @@ private void fillHash(ListeFilme listeFilme) { private void findAndMarkNewFilms(ListeFilme listeFilme) { listeFilme.neueFilme = false; - Stopwatch stopwatch = Stopwatch.createStarted(); listeFilme.parallelStream() .peek(film -> film.setNew(false)) .filter(film -> !hashSet.contains(film.getUrlNormalQuality())) @@ -346,8 +344,6 @@ private void findAndMarkNewFilms(ListeFilme listeFilme) { film.setNew(true); listeFilme.neueFilme = true; }); - stopwatch.stop(); - logger.debug("findAndMarkNewFilms() took: {}", stopwatch); hashSet.clear(); } From 2d624050ce102335cadfa63d0b3c6a0dfda3bff1 Mon Sep 17 00:00:00 2001 From: Christian F Date: Mon, 18 Jul 2022 07:58:43 +0200 Subject: [PATCH 192/422] - provide "some" progress information that abos are active --- .../gui/abo/InfiniteProgressPanel.java | 411 ++++++++++++++++++ .../java/mediathek/gui/abo/ManageAboDialog.kt | 2 +- .../mediathek/gui/abo/ManageAboPanel.java | 33 +- 3 files changed, 440 insertions(+), 6 deletions(-) create mode 100644 src/main/java/mediathek/gui/abo/InfiniteProgressPanel.java diff --git a/src/main/java/mediathek/gui/abo/InfiniteProgressPanel.java b/src/main/java/mediathek/gui/abo/InfiniteProgressPanel.java new file mode 100644 index 0000000000..ae70c10633 --- /dev/null +++ b/src/main/java/mediathek/gui/abo/InfiniteProgressPanel.java @@ -0,0 +1,411 @@ +package mediathek.gui.abo; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.font.FontRenderContext; +import java.awt.font.TextLayout; +import java.awt.geom.*; + +/** + * An infinite progress panel displays a rotating figure and + * a message to notice the user of a long, duration unknown + * task. The shape and the text are drawn upon a white veil + * which alpha level (or shield value) lets the underlying + * component shine through. This panel is meant to be used + * asa glass pane in the window performing the long + * operation. + *

                              + * On the contrary to regular glass panes, you don't need to + * set it visible or not by yourself. Once you've started the + * animation all the mouse events are intercepted by this + * panel, preventing them from being forwared to the + * underlying components. + *

                              + * The panel can be controlled by the start(), + * stop() and interrupt() methods. + *

                              + * Example: + *

                              + *
                              InfiniteProgressPanel pane = new InfiniteProgressPanel();
                              + * frame.setGlassPane(pane);
                              + * pane.start()
                              + *

                              + * Several properties can be configured at creation time. The + * message and its font can be changed at runtime. Changing the + * font can be done using setFont() and + * setForeground(). + * + * @author Romain Guy + * @version 1.0 + */ + +public class InfiniteProgressPanel extends JComponent implements MouseListener +{ + /** Contains the bars composing the circular shape. */ + protected Area[] ticker = null; + /** The animation thread is responsible for fade in/out and rotation. */ + protected Thread animation = null; + /** Notifies whether the animation is running or not. */ + protected boolean started = false; + /** Alpha level of the veil, used for fade in/out. */ + protected int alphaLevel = 0; + /** Duration of the veil's fade in/out. */ + protected int rampDelay = 300; + /** Alpha level of the veil. */ + protected float shield = 0.70f; + /** Message displayed below the circular shape. */ + protected String text = ""; + /** Amount of bars composing the circular shape. */ + protected int barsCount = 14; + /** Amount of frames per seconde. Lowers this to save CPU. */ + protected float fps = 15.0f; + /** Rendering hints to set anti aliasing. */ + protected RenderingHints hints = null; + + /** + * Creates a new progress panel with default values:
                              + *
                                + *
                              • No message
                              • + *
                              • 14 bars
                              • + *
                              • Veil's alpha level is 70%
                              • + *
                              • 15 frames per second
                              • + *
                              • Fade in/out last 300 ms
                              • + *
                              + */ + public InfiniteProgressPanel() + { + this(""); + } + + /** + * Creates a new progress panel with default values:
                              + *
                                + *
                              • 14 bars
                              • + *
                              • Veil's alpha level is 70%
                              • + *
                              • 15 frames per second
                              • + *
                              • Fade in/out last 300 ms
                              • + *
                              + * @param text The message to be displayed. Can be null or empty. + */ + public InfiniteProgressPanel(String text) + { + this(text, 14); + } + + /** + * Creates a new progress panel with default values:
                              + *
                                + *
                              • Veil's alpha level is 70%
                              • + *
                              • 15 frames per second
                              • + *
                              • Fade in/out last 300 ms
                              • + *
                              + * @param text The message to be displayed. Can be null or empty. + * @param barsCount The amount of bars composing the circular shape + */ + public InfiniteProgressPanel(String text, int barsCount) + { + this(text, barsCount, 0.70f); + } + + /** + * Creates a new progress panel with default values:
                              + *
                                + *
                              • 15 frames per second
                              • + *
                              • Fade in/out last 300 ms
                              • + *
                              + * @param text The message to be displayed. Can be null or empty. + * @param barsCount The amount of bars composing the circular shape. + * @param shield The alpha level between 0.0 and 1.0 of the colored + * shield (or veil). + */ + public InfiniteProgressPanel(String text, int barsCount, float shield) + { + this(text, barsCount, shield, 15.0f); + } + + /** + * Creates a new progress panel with default values:
                              + *
                                + *
                              • Fade in/out last 300 ms
                              • + *
                              + * @param text The message to be displayed. Can be null or empty. + * @param barsCount The amount of bars composing the circular shape. + * @param shield The alpha level between 0.0 and 1.0 of the colored + * shield (or veil). + * @param fps The number of frames per second. Lower this value to + * decrease CPU usage. + */ + public InfiniteProgressPanel(String text, int barsCount, float shield, float fps) + { + this(text, barsCount, shield, fps, 300); + } + + /** + * Creates a new progress panel. + * @param text The message to be displayed. Can be null or empty. + * @param barsCount The amount of bars composing the circular shape. + * @param shield The alpha level between 0.0 and 1.0 of the colored + * shield (or veil). + * @param fps The number of frames per second. Lower this value to + * decrease CPU usage. + * @param rampDelay The duration, in milli seconds, of the fade in and + * the fade out of the veil. + */ + public InfiniteProgressPanel(String text, int barsCount, float shield, float fps, int rampDelay) + { + this.text = text; + this.rampDelay = Math.max(rampDelay, 0); + this.shield = Math.max(shield, 0.0f); + this.fps = fps > 0.0f ? fps : 15.0f; + this.barsCount = barsCount > 0 ? barsCount : 14; + + this.hints = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + this.hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + this.hints.put(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); + } + + /** + * Changes the displayed message at runtime. + * + * @param text The message to be displayed. Can be null or empty. + */ + public void setText(String text) + { + this.text = text; + repaint(); + } + + /** + * Returns the current displayed message. + */ + public String getText() + { + return text; + } + + /** + * Starts the waiting animation by fading the veil in, then + * rotating the shapes. This method handles the visibility + * of the glass pane. + */ + public void start() + { + addMouseListener(this); + setVisible(true); + ticker = buildTicker(); + animation = new Thread(new Animator(true)); + animation.start(); + } + + /** + * Stops the waiting animation by stopping the rotation + * of the circular shape and then by fading out the veil. + * This methods sets the panel invisible at the end. + */ + public void stop() + { + if (animation != null) { + animation.interrupt(); + animation = null; + animation = new Thread(new Animator(false)); + animation.start(); + } + } + + /** + * Interrupts the animation, whatever its state is. You + * can use it when you need to stop the animation without + * running the fade out phase. + * This method sets the panel invisible at the end. + */ + public void interrupt() + { + if (animation != null) { + animation.interrupt(); + animation = null; + + removeMouseListener(this); + setVisible(false); + } + } + + public void paintComponent(Graphics g) + { + if (started) + { + int width = getWidth(); + + double maxY = 0.0; + + Graphics2D g2 = (Graphics2D) g; + g2.setRenderingHints(hints); + + g2.setColor(new Color(255, 255, 255, (int) (alphaLevel * shield))); + g2.fillRect(0, 0, getWidth(), getHeight()); + + for (int i = 0; i < ticker.length; i++) + { + int channel = 224 - 128 / (i + 1); + g2.setColor(new Color(channel, channel, channel, alphaLevel)); + g2.fill(ticker[i]); + + Rectangle2D bounds = ticker[i].getBounds2D(); + if (bounds.getMaxY() > maxY) + maxY = bounds.getMaxY(); + } + + if (text != null && !text.isEmpty()) + { + FontRenderContext context = g2.getFontRenderContext(); + TextLayout layout = new TextLayout(text, getFont(), context); + Rectangle2D bounds = layout.getBounds(); + g2.setColor(getForeground()); + layout.draw(g2, (float) (width - bounds.getWidth()) / 2, + (float) (maxY + layout.getLeading() + 2 * layout.getAscent())); + } + } + } + + /** + * Builds the circular shape and returns the result as an array of + * Area. Each Area is one of the bars + * composing the shape. + */ + private Area[] buildTicker() + { + Area[] ticker = new Area[barsCount]; + Point2D.Double center = new Point2D.Double((double) getWidth() / 2, (double) getHeight() / 2); + double fixedAngle = 2.0 * Math.PI / ((double) barsCount); + + for (double i = 0.0; i < (double) barsCount; i++) + { + Area primitive = buildPrimitive(); + + AffineTransform toCenter = AffineTransform.getTranslateInstance(center.getX(), center.getY()); + AffineTransform toBorder = AffineTransform.getTranslateInstance(45.0, -6.0); + AffineTransform toCircle = AffineTransform.getRotateInstance(-i * fixedAngle, center.getX(), center.getY()); + + AffineTransform toWheel = new AffineTransform(); + toWheel.concatenate(toCenter); + toWheel.concatenate(toBorder); + + primitive.transform(toWheel); + primitive.transform(toCircle); + + ticker[(int) i] = primitive; + } + + return ticker; + } + + /** + * Builds a bar. + */ + private Area buildPrimitive() + { + Rectangle2D.Double body = new Rectangle2D.Double(6, 0, 30, 12); + Ellipse2D.Double head = new Ellipse2D.Double(0, 0, 12, 12); + Ellipse2D.Double tail = new Ellipse2D.Double(30, 0, 12, 12); + + Area tick = new Area(body); + tick.add(new Area(head)); + tick.add(new Area(tail)); + + return tick; + } + + /** + * Animation thread. + */ + private class Animator implements Runnable + { + private boolean rampUp = true; + + protected Animator(boolean rampUp) + { + this.rampUp = rampUp; + } + + public void run() + { + Point2D.Double center = new Point2D.Double((double) getWidth() / 2, (double) getHeight() / 2); + double fixedIncrement = 2.0 * Math.PI / ((double) barsCount); + AffineTransform toCircle = AffineTransform.getRotateInstance(fixedIncrement, center.getX(), center.getY()); + + long start = System.currentTimeMillis(); + if (rampDelay == 0) + alphaLevel = rampUp ? 255 : 0; + + started = true; + boolean inRamp = rampUp; + + while (!Thread.interrupted()) + { + if (!inRamp) + { + for (Area area : ticker) + area.transform(toCircle); + } + + repaint(); + + if (rampUp) + { + if (alphaLevel < 255) + { + alphaLevel = (int) (255 * (System.currentTimeMillis() - start) / rampDelay); + if (alphaLevel >= 255) + { + alphaLevel = 255; + inRamp = false; + } + } + } else if (alphaLevel > 0) { + alphaLevel = (int) (255 - (255 * (System.currentTimeMillis() - start) / rampDelay)); + if (alphaLevel <= 0) + { + alphaLevel = 0; + break; + } + } + + try + { + Thread.sleep(inRamp ? 10 : (int) (1000 / fps)); + } catch (InterruptedException ie) { + break; + } + Thread.yield(); + } + + if (!rampUp) + { + started = false; + // there has been an EDT violation warning... + SwingUtilities.invokeLater(() -> { + repaint(); + + setVisible(false); + removeMouseListener(InfiniteProgressPanel.this); + }); + } + } + } + + public void mouseClicked(MouseEvent e) { + } + + public void mousePressed(MouseEvent e) { + } + + public void mouseReleased(MouseEvent e) { + } + + public void mouseEntered(MouseEvent e) { + } + + public void mouseExited(MouseEvent e) { + } +} \ No newline at end of file diff --git a/src/main/java/mediathek/gui/abo/ManageAboDialog.kt b/src/main/java/mediathek/gui/abo/ManageAboDialog.kt index 0b18c663b6..c316f26153 100644 --- a/src/main/java/mediathek/gui/abo/ManageAboDialog.kt +++ b/src/main/java/mediathek/gui/abo/ManageAboDialog.kt @@ -62,7 +62,7 @@ class ManageAboDialog(owner: Frame?) : JDialog(owner) { defaultCloseOperation = DISPOSE_ON_CLOSE isResizable = true isModal = true - aboPanel = ManageAboPanel() + aboPanel = ManageAboPanel(this) val contentPane = contentPane contentPane.layout = BorderLayout() contentPane.add(aboPanel, BorderLayout.CENTER) diff --git a/src/main/java/mediathek/gui/abo/ManageAboPanel.java b/src/main/java/mediathek/gui/abo/ManageAboPanel.java index b4ce51ce07..1e09977017 100644 --- a/src/main/java/mediathek/gui/abo/ManageAboPanel.java +++ b/src/main/java/mediathek/gui/abo/ManageAboPanel.java @@ -21,6 +21,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jdesktop.swingx.JXStatusBar; +import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.*; @@ -40,10 +41,10 @@ public class ManageAboPanel extends JPanel { private final JLabel inactiveAbos = new JLabel("inactiveAbos"); private final JToolBar swingToolBar = new JToolBar(); private final JComboBox senderCombo = new JComboBox<>(); + private final InfiniteProgressPanel infiniteProgressPanel = new InfiniteProgressPanel(); private JScrollPane jScrollPane1; - public ManageAboPanel() { - super(); + public ManageAboPanel(@NotNull JDialog dialog) { daten = Daten.getInstance(); initComponents(); @@ -58,6 +59,8 @@ public ManageAboPanel() { initListeners(); initializeTable(); + + dialog.setGlassPane(infiniteProgressPanel); } public void addObjectData(TModelAbo model, String sender) { @@ -292,7 +295,8 @@ private void aboLoeschen() { selectFirstRow(); - daten.getListeAbo().aenderungMelden(); + var worker = new ChangeWorker(); + worker.execute(); } else { NoSelectionErrorDialog.show(this); } @@ -354,7 +358,8 @@ public void editAbo() { } tabelleLaden(); - daten.getListeAbo().aenderungMelden(); + var worker = new ChangeWorker(); + worker.execute(); } private void changeAboActiveState(boolean ein) { @@ -372,7 +377,8 @@ private void changeAboActiveState(boolean ein) { tabelle.addRowSelectionInterval(row, row); } - daten.getListeAbo().aenderungMelden(); + var worker = new ChangeWorker(); + worker.execute(); } else { NoSelectionErrorDialog.show(this); } @@ -392,4 +398,21 @@ private void initComponents() { add(jScrollPane1, BorderLayout.CENTER); add(infoPanel, BorderLayout.SOUTH); } + + class ChangeWorker extends SwingWorker { + public ChangeWorker() { + infiniteProgressPanel.start(); + } + + @Override + protected void done() { + infiniteProgressPanel.stop(); + } + + @Override + protected Void doInBackground() { + daten.getListeAbo().aenderungMelden(); + return null; + } + } } From ab1f6d7c9cbfe1d6c1758e03b2252d71bb5df92f Mon Sep 17 00:00:00 2001 From: Christian F Date: Mon, 18 Jul 2022 11:50:10 +0200 Subject: [PATCH 193/422] - give feedback when abo workers are active - remove unused variable - remove change abo and delete abo menu items from popup --- CHANGELOG.md | 2 ++ src/main/java/mediathek/daten/DatenFilm.java | 1 + src/main/java/mediathek/daten/ListeAbo.java | 27 ++++++++++++++++--- .../mediathek/gui/abo/ManageAboPanel.java | 7 +++-- .../mediathek/gui/tabs/tab_film/GuiFilme.java | 17 +++--------- .../javafx/filterpanel/FilmActionPanel.java | 2 -- .../mediathek/mainwindow/MediathekGui.java | 9 ++++--- .../abo => tool}/InfiniteProgressPanel.java | 2 +- 8 files changed, 39 insertions(+), 28 deletions(-) rename src/main/java/mediathek/{gui/abo => tool}/InfiniteProgressPanel.java (99%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5dd221dbd4..a68c44f27f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,9 @@ - Die Spalte für Filmnummer liefert bis zur vollständigen Entfernung **immer** `1` zurück. - Der PSet-Parameter `%i` liefert anstatt der internen Filmnummer nun die aktuelle Zeit seit 1970 in Millisekunden. - *Globale Schriftgröße ändern...*-Befehl wurde unter Linux entfernt da es keine Auswirkungen mehr hat. +- Im Tab "Filme" wurden *"Abo löschen"* und *"Abo ändern"* aufgrund der teilweise langen Ausführungszeiten und dem damit zusammenhängenden Einfrieren der Oberfläche entfernt. Bitte nutzen Sie wie dafür vorgesehen den "Abos verwalten" Dialog. - **BUGFIX:** Updater-Konfigurationsmöglichkeit wird nur noch in den Einstellungen aktiviert dargestellt, wenn sie auch Auswirkungen auf den Update-Mechanismus hat. +- **BUGFIX:** Oberfläche gibt nun verbessert Hinweise, wenn Abos verarbeitet werden. - **BUGFIX:** Fehler in der Anzeige im Statusfeld des Download-Tab wurde behoben. - **BUGFIX:** *"Filme/Blacklist öffnen..."* Eintrag wurde in *"Filme/Blacklist bearbeiten..."* umbenannt und die Icons vereinheitlicht. - **BUGFIX:** Manchmal inkorrekte Darstellung des Bookmark-Status im Film-Tab wurde korrigiert. diff --git a/src/main/java/mediathek/daten/DatenFilm.java b/src/main/java/mediathek/daten/DatenFilm.java index 77f8eb264c..d642c75b98 100644 --- a/src/main/java/mediathek/daten/DatenFilm.java +++ b/src/main/java/mediathek/daten/DatenFilm.java @@ -354,6 +354,7 @@ public String getFileSizeForUrl(@NotNull String url) { if (url.equalsIgnoreCase(getUrlNormalQuality())) { return getFileSize().toString(); } else { + //FIXME this is blocking EDT! return FileSize.getFileLengthFromUrl(url); } } diff --git a/src/main/java/mediathek/daten/ListeAbo.java b/src/main/java/mediathek/daten/ListeAbo.java index 87a6d4385f..0d5200f1c6 100644 --- a/src/main/java/mediathek/daten/ListeAbo.java +++ b/src/main/java/mediathek/daten/ListeAbo.java @@ -76,7 +76,7 @@ public void addAbo(String aboname, String filmSender, String filmThema, String f aenderungMelden(); Collections.sort(this); } else { - MVMessageDialog.showMessageDialog(null, "Abo existiert bereits", "Abo anlegen", JOptionPane.INFORMATION_MESSAGE); + MVMessageDialog.showMessageDialog(MediathekGui.ui(), "Abo existiert bereits", "Abo anlegen", JOptionPane.INFORMATION_MESSAGE); } } } @@ -101,8 +101,8 @@ public void aboLoeschen(@NotNull DatenAbo abo) { public void aenderungMelden() { // Filmliste anpassen - setAboFuerFilm(Daten.getInstance().getListeFilme(), true); - MessageBus.getMessageBus().publishAsync(new AboListChangedEvent()); + var worker = new ChangeWorker(); + worker.execute(); } public ArrayList getPfade() { @@ -217,4 +217,25 @@ public void setAboFuerFilm(ListeFilme listeFilme, boolean aboLoeschen) { datenAbo.setIrgendwoFilterPattern(LEER); }); } + + class ChangeWorker extends SwingWorker { + public ChangeWorker() { + SwingUtilities.invokeLater(() -> { + MediathekGui.ui().infiniteProgressPanel.setText("Verarbeite Abos..."); + MediathekGui.ui().infiniteProgressPanel.start(); + }); + } + @Override + protected Void doInBackground() { + setAboFuerFilm(Daten.getInstance().getListeFilme(), true); + MessageBus.getMessageBus().publishAsync(new AboListChangedEvent()); + return null; + } + + @Override + protected void done() { + MediathekGui.ui().infiniteProgressPanel.setText(""); + MediathekGui.ui().infiniteProgressPanel.stop(); + } + } } diff --git a/src/main/java/mediathek/gui/abo/ManageAboPanel.java b/src/main/java/mediathek/gui/abo/ManageAboPanel.java index 1e09977017..19b8599d25 100644 --- a/src/main/java/mediathek/gui/abo/ManageAboPanel.java +++ b/src/main/java/mediathek/gui/abo/ManageAboPanel.java @@ -8,10 +8,7 @@ import mediathek.gui.dialog.DialogEditAbo; import mediathek.gui.messages.AboListChangedEvent; import mediathek.mainwindow.MediathekGui; -import mediathek.tool.MessageBus; -import mediathek.tool.NoSelectionErrorDialog; -import mediathek.tool.SVGIconUtilities; -import mediathek.tool.SenderListModel; +import mediathek.tool.*; import mediathek.tool.cellrenderer.CellRendererAbo; import mediathek.tool.listener.BeobTableHeader; import mediathek.tool.models.TModelAbo; @@ -402,11 +399,13 @@ private void initComponents() { class ChangeWorker extends SwingWorker { public ChangeWorker() { infiniteProgressPanel.start(); + infiniteProgressPanel.setText("Verarbeite Abos..."); } @Override protected void done() { infiniteProgressPanel.stop(); + infiniteProgressPanel.setText(""); } @Override diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index 9cad08a7e2..7a15510567 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -1220,26 +1220,17 @@ private void showMenu(MouseEvent evt) { JMenu submenueAbo = new JMenu("Abo"); jPopupMenu.add(submenueAbo); // Abo anlegen - JMenuItem itemAboLoeschen = new JMenuItem("Abo Löschen"); JMenuItem itemAbo = new JMenuItem("Abo mit Sender und Thema anlegen"); JMenuItem itemAboMitTitel = new JMenuItem("Abo mit Sender und Thema und Titel anlegen"); - JMenuItem itemChangeAboFilter = new JMenuItem("Abo ändern"); Optional res = getFilm(nr); res.ifPresent(film -> { - if ((daten.getListeAbo().getAboFuerFilm_schnell(film, false /*die Länge nicht prüfen*/)) - != null) { - // gibts schon, dann löschen + if ((daten.getListeAbo().getAboFuerFilm_schnell(film, false)) != null) { + // gibts schon -> deaktivieren... itemAbo.setEnabled(false); itemAboMitTitel.setEnabled(false); - itemAboLoeschen.addActionListener(beobAbo); - - // dann können wir auch ändern - itemChangeAboFilter.addActionListener(new BeobChangeAbo()); } else { - itemAboLoeschen.setEnabled(false); - itemChangeAboFilter.setEnabled(false); - // neues Abo anlegen + // neues Abo anlegen möglich... itemAbo.addActionListener(beobAbo); itemAboMitTitel.addActionListener(beobAboMitTitel); } @@ -1251,8 +1242,6 @@ private void showMenu(MouseEvent evt) { } }); - submenueAbo.add(itemAboLoeschen); - submenueAbo.add(itemChangeAboFilter); submenueAbo.add(itemAbo); submenueAbo.add(itemAboMitTitel); diff --git a/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java b/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java index 8750f3e682..b7bcdfffd5 100644 --- a/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java +++ b/src/main/java/mediathek/javafx/filterpanel/FilmActionPanel.java @@ -10,7 +10,6 @@ import javafx.scene.control.ComboBox; import javafx.util.StringConverter; import mediathek.config.Daten; -import mediathek.gui.actions.ManageAboAction; import mediathek.mainwindow.MediathekGui; import mediathek.tool.FilterConfiguration; import mediathek.tool.FilterDTO; @@ -50,7 +49,6 @@ public class FilmActionPanel { public ComboBox themaBox; public RangeSlider filmLengthSlider; public JDialog filterDialog; - public ManageAboAction manageAboAction; /** Stores the list of thema strings used for autocompletion. */ private SuggestionProvider themaSuggestionProvider; diff --git a/src/main/java/mediathek/mainwindow/MediathekGui.java b/src/main/java/mediathek/mainwindow/MediathekGui.java index ef46d8cc80..acba1b033c 100644 --- a/src/main/java/mediathek/mainwindow/MediathekGui.java +++ b/src/main/java/mediathek/mainwindow/MediathekGui.java @@ -84,6 +84,7 @@ public class MediathekGui extends JFrame { */ private static MediathekGui ui; public final LoadFilmListAction loadFilmListAction; + public final InfiniteProgressPanel infiniteProgressPanel = new InfiniteProgressPanel(); /** * Number of active downloads */ @@ -92,6 +93,7 @@ public class MediathekGui extends JFrame { protected final JTabbedPane tabbedPane = new JTabbedPane(); protected final JMenu jMenuHilfe = new JMenu(); protected final SettingsAction settingsAction = new SettingsAction(this); + final JMenu fontMenu = new JMenu("Schrift"); private final JMenu jMenuDatei = new JMenu(); private final JMenu jMenuFilme = new JMenu(); private final JMenuBar jMenuBar = new JMenuBar(); @@ -108,7 +110,7 @@ public class MediathekGui extends JFrame { private final MemoryMonitorAction showMemoryMonitorAction = new MemoryMonitorAction(this); private final InfoDialog filmInfo; private final ConfigureExternalUpdaterAction configureExternalUpdaterAction = new ConfigureExternalUpdaterAction(); - final JMenu fontMenu = new JMenu("Schrift"); + private final ManageAboAction manageAboAction = new ManageAboAction(); public StatusBar swingStatusBar; public GuiFilme tabFilme; public GuiDownloads tabDownloads; @@ -145,12 +147,13 @@ public class MediathekGui extends JFrame { * Progress indicator thread for OS X and windows. */ private IndicatorThread progressIndicatorThread; - private ManageAboAction manageAboAction; private AutomaticFilmlistUpdate automaticFilmlistUpdate; public MediathekGui() { ui = this; + setGlassPane(infiniteProgressPanel); + setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); loadFilmListAction = new LoadFilmListAction(this); @@ -938,8 +941,6 @@ private void createAboMenu() { jMenuAbos.add(new CreateNewAboAction(daten.getListeAbo())); jMenuAbos.add(new ShowAboHistoryAction(MediathekGui.ui())); jMenuAbos.addSeparator(); - manageAboAction = new ManageAboAction(); - tabFilme.filmActionPanel.manageAboAction = manageAboAction; jMenuAbos.add(manageAboAction); } diff --git a/src/main/java/mediathek/gui/abo/InfiniteProgressPanel.java b/src/main/java/mediathek/tool/InfiniteProgressPanel.java similarity index 99% rename from src/main/java/mediathek/gui/abo/InfiniteProgressPanel.java rename to src/main/java/mediathek/tool/InfiniteProgressPanel.java index ae70c10633..816e831de1 100644 --- a/src/main/java/mediathek/gui/abo/InfiniteProgressPanel.java +++ b/src/main/java/mediathek/tool/InfiniteProgressPanel.java @@ -1,4 +1,4 @@ -package mediathek.gui.abo; +package mediathek.tool; import javax.swing.*; import java.awt.*; From 1087671e739718ad88eb1b988a7e3cdbb2bea814 Mon Sep 17 00:00:00 2001 From: Christian F Date: Mon, 18 Jul 2022 12:05:22 +0200 Subject: [PATCH 194/422] -reenable film number as otherwise film selection in various tables does not work anymore --- CHANGELOG.md | 1 - src/main/java/mediathek/daten/DatenDownload.java | 2 +- src/main/java/mediathek/daten/DatenFilm.java | 15 +++++---------- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a68c44f27f..8038ab856a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,6 @@ - User Interface wurde primär für neue macOS-Versionen überarbeitet. - Das Programm überprüft beim Start ob die JVM-Parameter korrekt sind und gibt ansonsten eine Warnung aus. Dies kann über die Paramter `-nj` oder `--no-jvm-param-checks` deaktiviert werden. - Das Programm prüft unter Linux ob der `sun.java2d.uiScale`-Parameter korrekt angegeben wurde. Im Fehlerfall wird ein Warndialog angezeigt. -- Die Spalte für Filmnummer liefert bis zur vollständigen Entfernung **immer** `1` zurück. - Der PSet-Parameter `%i` liefert anstatt der internen Filmnummer nun die aktuelle Zeit seit 1970 in Millisekunden. - *Globale Schriftgröße ändern...*-Befehl wurde unter Linux entfernt da es keine Auswirkungen mehr hat. - Im Tab "Filme" wurden *"Abo löschen"* und *"Abo ändern"* aufgrund der teilweise langen Ausführungszeiten und dem damit zusammenhängenden Einfrieren der Oberfläche entfernt. Bitte nutzen Sie wie dafür vorgesehen den "Abos verwalten" Dialog. diff --git a/src/main/java/mediathek/daten/DatenDownload.java b/src/main/java/mediathek/daten/DatenDownload.java index 1c5513def5..00cf5985b8 100644 --- a/src/main/java/mediathek/daten/DatenDownload.java +++ b/src/main/java/mediathek/daten/DatenDownload.java @@ -118,7 +118,7 @@ public DatenDownload(DatenPset pSet, DatenFilm film, byte quelle, DatenAbo abo, this.film = film; this.pSet = pSet; this.abo = abo; - arr[DOWNLOAD_FILM_NR] = Integer.toString(film.getFilmNr()); + arr[DOWNLOAD_FILM_NR] = Long.toString(film.getFilmNr()); arr[DOWNLOAD_SENDER] = film.getSender(); arr[DOWNLOAD_THEMA] = film.getThema(); arr[DOWNLOAD_TITEL] = film.getTitle(); diff --git a/src/main/java/mediathek/daten/DatenFilm.java b/src/main/java/mediathek/daten/DatenFilm.java index d642c75b98..a441bf68dc 100644 --- a/src/main/java/mediathek/daten/DatenFilm.java +++ b/src/main/java/mediathek/daten/DatenFilm.java @@ -16,14 +16,7 @@ import java.util.EnumMap; import java.util.EnumSet; import java.util.Optional; - -/* - * TODO: - * - Remove the Database Stuff from this Class to own Classes and a real OR-Mapping - * - Finalize a Real Entity - * - Write test cases for each Method - * - Write JavaDoc for each of the new Methods that were split from this moloch - */ +import java.util.concurrent.atomic.AtomicInteger; public class DatenFilm implements Comparable { public static final int FILM_NR = 0; // wird vor dem Speichern gelöscht! @@ -53,6 +46,7 @@ public class DatenFilm implements Comparable { public static final char COMPRESSION_MARKER = '|'; private static final GermanStringSorter sorter = GermanStringSorter.getInstance(); private static final Logger logger = LogManager.getLogger(DatenFilm.class); + private final static AtomicInteger FILMNR_GENERATOR = new AtomicInteger(0); private final EnumSet flags = EnumSet.noneOf(DatenFilmFlags.class); /** * File size in MByte @@ -84,6 +78,7 @@ public class DatenFilm implements Comparable { private int filmLength; public DatenFilm() { + dataMap.put(MapKeys.FILM_NR, FILMNR_GENERATOR.getAndIncrement()); } public DatenFilm(@NotNull DatenFilm other) { @@ -247,7 +242,7 @@ public void setSignLanguage(boolean val) { * @return the original internal film number */ public int getFilmNr() { - return 1; + return (int)dataMap.get(MapKeys.FILM_NR); } /** @@ -568,5 +563,5 @@ public boolean isBookmarked() { return dataMap.containsKey(MapKeys.BOOKMARK_DATA); } - enum MapKeys {SUBTITLE_URL, WEBSITE_URL, LOW_QUALITY_URL, NORMAL_QUALITY_URL, HIGH_QUALITY_URL, BOOKMARK_DATA, ABO_DATA} + enum MapKeys {FILM_NR, SUBTITLE_URL, WEBSITE_URL, LOW_QUALITY_URL, NORMAL_QUALITY_URL, HIGH_QUALITY_URL, BOOKMARK_DATA, ABO_DATA} } From 91f1d6ede60647c8c75369f8bfdd60f20e2b7594 Mon Sep 17 00:00:00 2001 From: Christian F Date: Mon, 18 Jul 2022 14:21:51 +0200 Subject: [PATCH 195/422] - remember position of MemoryMonitorDialog --- .../gui/actions/MemoryMonitorAction.java | 16 +--- .../gui/dialog/MemoryMonitorDialog.java | 86 +++++++++++++++++++ .../mediathek/mainwindow/MediathekGui.java | 3 +- .../tool/ApplicationConfiguration.java | 20 +++-- 4 files changed, 102 insertions(+), 23 deletions(-) create mode 100644 src/main/java/mediathek/gui/dialog/MemoryMonitorDialog.java diff --git a/src/main/java/mediathek/gui/actions/MemoryMonitorAction.java b/src/main/java/mediathek/gui/actions/MemoryMonitorAction.java index 03346c4fde..a45cacb4af 100644 --- a/src/main/java/mediathek/gui/actions/MemoryMonitorAction.java +++ b/src/main/java/mediathek/gui/actions/MemoryMonitorAction.java @@ -1,12 +1,10 @@ package mediathek.gui.actions; -import mediathek.mainwindow.MemoryUsagePanel; +import mediathek.gui.dialog.MemoryMonitorDialog; import org.jetbrains.annotations.NotNull; import javax.swing.*; -import java.awt.*; import java.awt.event.ActionEvent; -import java.util.concurrent.TimeUnit; public class MemoryMonitorAction extends AbstractAction { private MemoryMonitorDialog dialog; @@ -34,16 +32,4 @@ public void actionPerformed(ActionEvent e) { showMemoryMonitor(); } - static class MemoryMonitorDialog extends JDialog { - public MemoryMonitorDialog(@NotNull JFrame parent) { - super(parent, "Speicherverbrauch", false); - setType(Type.UTILITY); - - MemoryUsagePanel panel = new MemoryUsagePanel(2, TimeUnit.MINUTES); - panel.setPreferredSize(new Dimension(480, 240)); - getContentPane().add(panel, BorderLayout.CENTER); - pack(); - panel.new MemoryUsageDataGenerator(1, TimeUnit.SECONDS).start(); - } - } } diff --git a/src/main/java/mediathek/gui/dialog/MemoryMonitorDialog.java b/src/main/java/mediathek/gui/dialog/MemoryMonitorDialog.java new file mode 100644 index 0000000000..3ffa0db3bb --- /dev/null +++ b/src/main/java/mediathek/gui/dialog/MemoryMonitorDialog.java @@ -0,0 +1,86 @@ +package mediathek.gui.dialog; + +import mediathek.mainwindow.MemoryUsagePanel; +import mediathek.tool.ApplicationConfiguration; +import org.apache.commons.configuration2.sync.LockMode; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.util.NoSuchElementException; +import java.util.concurrent.TimeUnit; + +public class MemoryMonitorDialog extends JDialog { + public MemoryMonitorDialog(@NotNull JFrame parent) { + super(parent, "Speicherverbrauch", false); + setType(Type.UTILITY); + + MemoryUsagePanel panel = new MemoryUsagePanel(2, TimeUnit.MINUTES); + panel.setPreferredSize(new Dimension(480, 240)); + getContentPane().add(panel, BorderLayout.CENTER); + pack(); + panel.new MemoryUsageDataGenerator(1, TimeUnit.SECONDS).start(); + + restoreLocation(); + + addComponentListener(new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent e) { + saveLocation(); + } + + @Override + public void componentMoved(ComponentEvent e) { + saveLocation(); + } + + @Override + public void componentShown(ComponentEvent e) { + ApplicationConfiguration.getConfiguration().setProperty(ApplicationConfiguration.MemoryMonitorDialog.VISIBLE, true); + } + + @Override + public void componentHidden(ComponentEvent e) { + ApplicationConfiguration.getConfiguration().setProperty(ApplicationConfiguration.MemoryMonitorDialog.VISIBLE, false); + } + }); + } + + private void restoreLocation() { + var config = ApplicationConfiguration.getConfiguration(); + config.lock(LockMode.READ); + try { + var newLocation = new Point(); + newLocation.x = config.getInt(ApplicationConfiguration.MemoryMonitorDialog.X); + newLocation.y = config.getInt(ApplicationConfiguration.MemoryMonitorDialog.Y); + setLocation(newLocation); + + int w = config.getInt(ApplicationConfiguration.MemoryMonitorDialog.WIDTH, -1); + int h = config.getInt(ApplicationConfiguration.MemoryMonitorDialog.HEIGHT, -1); + if (w != -1 && h != -1) { + setPreferredSize(new Dimension(w, h)); + } + } catch (NoSuchElementException ignored) { + } finally { + config.unlock(LockMode.READ); + } + } + + private void saveLocation() { + if (!isVisible()) + return; + var config = ApplicationConfiguration.getConfiguration(); + try { + config.lock(LockMode.WRITE); + var location = getLocationOnScreen(); + config.setProperty(ApplicationConfiguration.MemoryMonitorDialog.X, location.x); + config.setProperty(ApplicationConfiguration.MemoryMonitorDialog.Y, location.y); + config.setProperty(ApplicationConfiguration.MemoryMonitorDialog.WIDTH, getWidth()); + config.setProperty(ApplicationConfiguration.MemoryMonitorDialog.HEIGHT, getHeight()); + } finally { + config.unlock(LockMode.WRITE); + } + } +} diff --git a/src/main/java/mediathek/mainwindow/MediathekGui.java b/src/main/java/mediathek/mainwindow/MediathekGui.java index acba1b033c..70d322e81a 100644 --- a/src/main/java/mediathek/mainwindow/MediathekGui.java +++ b/src/main/java/mediathek/mainwindow/MediathekGui.java @@ -439,7 +439,8 @@ protected void workaroundJavaFxInitializationBug() { } private void createMemoryMonitor() { - if (Config.isDebugModeEnabled()) + boolean visible = ApplicationConfiguration.getConfiguration().getBoolean(ApplicationConfiguration.MemoryMonitorDialog.VISIBLE, false); + if (visible) showMemoryMonitorAction.showMemoryMonitor(); } diff --git a/src/main/java/mediathek/tool/ApplicationConfiguration.java b/src/main/java/mediathek/tool/ApplicationConfiguration.java index 299c1c8d97..942fca2532 100644 --- a/src/main/java/mediathek/tool/ApplicationConfiguration.java +++ b/src/main/java/mediathek/tool/ApplicationConfiguration.java @@ -72,6 +72,10 @@ public class ApplicationConfiguration { * logger for {@link TimerTaskListener} inner class. */ private static final Logger logger = LogManager.getLogger(); + /** + * A custom small thread scheduler exclusively for config changes. + */ + private final ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(2); private XMLConfiguration config; private FileHandler handler; /** @@ -93,8 +97,7 @@ private ApplicationConfiguration() { config.setProperty("config.major", version.getMajor()); config.setProperty("config.minor", version.getMinor()); config.setProperty("config.patch", version.getPatch()); - } - finally { + } finally { config.unlock(LockMode.WRITE); } } @@ -220,6 +223,14 @@ public static class FilmInfoDialog { public static final String FILM_INFO_LOCATION_Y = "film.information.location.y"; } + public static class MemoryMonitorDialog { + public static final String VISIBLE = "memory_monitor.visible"; + public static final String X = "memory_monitor.x"; + public static final String Y = "memory_monitor.y"; + public static final String WIDTH = "memory_monitor.width"; + public static final String HEIGHT = "memory_monitor.height"; + } + public static class SettingsDialog { public static final String WIDTH = "application.ui.settings_dialog.width"; public static final String HEIGHT = "application.ui.settings_dialog.height"; @@ -227,11 +238,6 @@ public static class SettingsDialog { public static final String Y = "application.ui.settings_dialog.y"; } - /** - * A custom small thread scheduler exclusively for config changes. - */ - private final ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(2); - /** * This class will issue a timer to write config to file 5 seconds after onEvent call. In case * this listener is called several times in a row the timer will get reset in order to ensure that From b374c77bea27dc3409c722f67862469186cda1a8 Mon Sep 17 00:00:00 2001 From: Christian F Date: Mon, 18 Jul 2022 14:44:43 +0200 Subject: [PATCH 196/422] - remember position of MemoryMonitorDialog --- .../gui/dialog/DialogEditDownload.jfd | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 src/main/java/mediathek/gui/dialog/DialogEditDownload.jfd diff --git a/src/main/java/mediathek/gui/dialog/DialogEditDownload.jfd b/src/main/java/mediathek/gui/dialog/DialogEditDownload.jfd new file mode 100644 index 0000000000..064b042911 --- /dev/null +++ b/src/main/java/mediathek/gui/dialog/DialogEditDownload.jfd @@ -0,0 +1,104 @@ +JFDML JFormDesigner: "7.0.6.0.1131" Java: "11.0.15" encoding: "UTF-8" + +new FormModel { + contentType: "form/swing" + root: new FormRoot { + add( new FormWindow( "javax.swing.JDialog", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { + "$layoutConstraints": "fill,insets 5,hidemode 3,gap 5 5" + "$columnConstraints": "[565,fill]" + "$rowConstraints": "[grow,fill][fill][fill]" + } ) { + name: "this" + "defaultCloseOperation": 2 + "title": "Download editieren" + "minimumSize": new java.awt.Dimension( 500, 0 ) + add( new FormContainer( "javax.swing.JScrollPane", new FormLayoutManager( class javax.swing.JScrollPane ) ) { + name: "jScrollPane1" + add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.GridBagLayout ) { + "$columnSpecs": "0:1.0" + "$rowSpecs": "0" + "$hGap": 5 + "$vGap": 5 + "$alignTop": true + "$alignLeft": true + } ) { + name: "jPanelExtra" + } ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0,grow" + } ) + add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { + "$layoutConstraints": "insets 0,hidemode 3,gap 5 5" + "$columnConstraints": "[fill][fill][fill][fill]" + "$rowConstraints": "[fill]" + } ) { + name: "jPanelRes" + "border": new javax.swing.border.TitledBorder( "" ) + add( new FormComponent( "javax.swing.JLabel" ) { + name: "jLabelRes" + "text": "Download-Qualität:" + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" + } ) + add( new FormComponent( "javax.swing.JRadioButton" ) { + name: "jRadioButtonResHd" + "$buttonGroup": new FormReference( "buttonGroup1" ) + "text": "Höchste/Hoch" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 1 0" + } ) + add( new FormComponent( "javax.swing.JRadioButton" ) { + name: "jRadioButtonResHi" + "$buttonGroup": new FormReference( "buttonGroup1" ) + "text": "Mittel" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 2 0" + } ) + add( new FormComponent( "javax.swing.JRadioButton" ) { + name: "jRadioButtonResLo" + "$buttonGroup": new FormReference( "buttonGroup1" ) + "text": "Niedrig" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 3 0" + } ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 1" + } ) + add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { + "$columnConstraints": "[fill][fill]" + "$rowConstraints": "[fill]" + "$layoutConstraints": "insets 0,hidemode 3,alignx right,gap 5 5" + } ) { + name: "panel1" + add( new FormComponent( "javax.swing.JButton" ) { + name: "jButtonOk" + "text": "Ok" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" + } ) + add( new FormComponent( "javax.swing.JButton" ) { + name: "jButtonAbbrechen" + "text": "Abbrechen" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 1 0" + } ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 2" + } ) + }, new FormLayoutConstraints( null ) { + "size": new java.awt.Dimension( 570, 300 ) + "location": new java.awt.Point( 0, 0 ) + } ) + add( new FormNonVisual( "javax.swing.ButtonGroup" ) { + name: "buttonGroup1" + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + }, new FormLayoutConstraints( null ) { + "location": new java.awt.Point( 0, 316 ) + } ) + } +} From 29bba27fddb3b62107a961747cd6d4cb47550fe7 Mon Sep 17 00:00:00 2001 From: Christian F Date: Mon, 18 Jul 2022 14:45:34 +0200 Subject: [PATCH 197/422] Revert "- remember position of MemoryMonitorDialog" This reverts commit b374c77bea27dc3409c722f67862469186cda1a8. --- .../gui/dialog/DialogEditDownload.jfd | 104 ------------------ 1 file changed, 104 deletions(-) delete mode 100644 src/main/java/mediathek/gui/dialog/DialogEditDownload.jfd diff --git a/src/main/java/mediathek/gui/dialog/DialogEditDownload.jfd b/src/main/java/mediathek/gui/dialog/DialogEditDownload.jfd deleted file mode 100644 index 064b042911..0000000000 --- a/src/main/java/mediathek/gui/dialog/DialogEditDownload.jfd +++ /dev/null @@ -1,104 +0,0 @@ -JFDML JFormDesigner: "7.0.6.0.1131" Java: "11.0.15" encoding: "UTF-8" - -new FormModel { - contentType: "form/swing" - root: new FormRoot { - add( new FormWindow( "javax.swing.JDialog", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { - "$layoutConstraints": "fill,insets 5,hidemode 3,gap 5 5" - "$columnConstraints": "[565,fill]" - "$rowConstraints": "[grow,fill][fill][fill]" - } ) { - name: "this" - "defaultCloseOperation": 2 - "title": "Download editieren" - "minimumSize": new java.awt.Dimension( 500, 0 ) - add( new FormContainer( "javax.swing.JScrollPane", new FormLayoutManager( class javax.swing.JScrollPane ) ) { - name: "jScrollPane1" - add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.GridBagLayout ) { - "$columnSpecs": "0:1.0" - "$rowSpecs": "0" - "$hGap": 5 - "$vGap": 5 - "$alignTop": true - "$alignLeft": true - } ) { - name: "jPanelExtra" - } ) - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 0,grow" - } ) - add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { - "$layoutConstraints": "insets 0,hidemode 3,gap 5 5" - "$columnConstraints": "[fill][fill][fill][fill]" - "$rowConstraints": "[fill]" - } ) { - name: "jPanelRes" - "border": new javax.swing.border.TitledBorder( "" ) - add( new FormComponent( "javax.swing.JLabel" ) { - name: "jLabelRes" - "text": "Download-Qualität:" - auxiliary() { - "JavaCodeGenerator.variableLocal": true - } - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 0" - } ) - add( new FormComponent( "javax.swing.JRadioButton" ) { - name: "jRadioButtonResHd" - "$buttonGroup": new FormReference( "buttonGroup1" ) - "text": "Höchste/Hoch" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 1 0" - } ) - add( new FormComponent( "javax.swing.JRadioButton" ) { - name: "jRadioButtonResHi" - "$buttonGroup": new FormReference( "buttonGroup1" ) - "text": "Mittel" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 2 0" - } ) - add( new FormComponent( "javax.swing.JRadioButton" ) { - name: "jRadioButtonResLo" - "$buttonGroup": new FormReference( "buttonGroup1" ) - "text": "Niedrig" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 3 0" - } ) - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 1" - } ) - add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { - "$columnConstraints": "[fill][fill]" - "$rowConstraints": "[fill]" - "$layoutConstraints": "insets 0,hidemode 3,alignx right,gap 5 5" - } ) { - name: "panel1" - add( new FormComponent( "javax.swing.JButton" ) { - name: "jButtonOk" - "text": "Ok" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 0" - } ) - add( new FormComponent( "javax.swing.JButton" ) { - name: "jButtonAbbrechen" - "text": "Abbrechen" - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 1 0" - } ) - }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 2" - } ) - }, new FormLayoutConstraints( null ) { - "size": new java.awt.Dimension( 570, 300 ) - "location": new java.awt.Point( 0, 0 ) - } ) - add( new FormNonVisual( "javax.swing.ButtonGroup" ) { - name: "buttonGroup1" - auxiliary() { - "JavaCodeGenerator.variableLocal": true - } - }, new FormLayoutConstraints( null ) { - "location": new java.awt.Point( 0, 316 ) - } ) - } -} From 4711d42a51238b1d0cc4762b7af799ecc3197ed5 Mon Sep 17 00:00:00 2001 From: Christian F Date: Mon, 18 Jul 2022 15:08:41 +0200 Subject: [PATCH 198/422] - remember position of DialogEditDownload - fixes #666 --- .../gui/dialog/DialogEditDownload.java | 89 +++++++++++++++---- .../tool/ApplicationConfiguration.java | 7 ++ 2 files changed, 77 insertions(+), 19 deletions(-) diff --git a/src/main/java/mediathek/gui/dialog/DialogEditDownload.java b/src/main/java/mediathek/gui/dialog/DialogEditDownload.java index 94900003f0..bd5f81f984 100644 --- a/src/main/java/mediathek/gui/dialog/DialogEditDownload.java +++ b/src/main/java/mediathek/gui/dialog/DialogEditDownload.java @@ -5,9 +5,11 @@ import mediathek.daten.DatenProg; import mediathek.daten.FilmResolution; import mediathek.file.GetFile; +import mediathek.tool.ApplicationConfiguration; import mediathek.tool.EscapeKeyHandler; import mediathek.tool.MVMessageDialog; import mediathek.tool.SVGIconUtilities; +import org.apache.commons.configuration2.sync.LockMode; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -18,7 +20,10 @@ import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; import java.io.File; +import java.util.NoSuchElementException; public class DialogEditDownload extends JDialog { private final DatenDownload datenDownload; @@ -39,9 +44,8 @@ public class DialogEditDownload extends JDialog { private final JFrame parent; private final String orgProgArray; private FilmResolution.Enum resolution = FilmResolution.Enum.NORMAL; - private final JLabel jLabelFilmHD = new JLabel(); - private final JLabel jLabelFilmUT = new JLabel(); - private static ImageIcon ja_sw_16; + private final JCheckBox cbHighQuality = new JCheckBox(); + private final JCheckBox cbSubtitleAvailable = new JCheckBox(); private final TableColumnModel columnModel; public DialogEditDownload(JFrame parent, boolean modal, DatenDownload ddownload, boolean ggestartet, TableColumnModel colModel) { @@ -50,10 +54,11 @@ public DialogEditDownload(JFrame parent, boolean modal, DatenDownload ddownload, this.parent = parent; datenDownload = ddownload; gestartet = ggestartet; - jScrollPane1.getVerticalScrollBar().setUnitIncrement(16); - ja_sw_16 = SVGIconUtilities.createSVGIcon("icons/fontawesome/check.svg"); columnModel = colModel; + cbSubtitleAvailable.setEnabled(false); + cbHighQuality.setEnabled(false); + orgProgArray = datenDownload.arr[DatenDownload.DOWNLOAD_PROGRAMM_AUFRUF_ARRAY]; mVPanelDownloadZiel = new MVPanelDownloadZiel(parent, datenDownload, false); mVPanelDownloadZiel.setBorder(BorderFactory.createLineBorder(new Color(204, 204, 204))); @@ -72,6 +77,56 @@ public DialogEditDownload(JFrame parent, boolean modal, DatenDownload ddownload, setupResolutionButtons(); setExtra(); + + restoreLocation(); + + addComponentListener(new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent e) { + saveLocation(); + } + + @Override + public void componentMoved(ComponentEvent e) { + saveLocation(); + } + }); + } + + private void restoreLocation() { + var config = ApplicationConfiguration.getConfiguration(); + config.lock(LockMode.READ); + try { + var newLocation = new Point(); + newLocation.x = config.getInt(ApplicationConfiguration.EditDownloadDialog.X); + newLocation.y = config.getInt(ApplicationConfiguration.EditDownloadDialog.Y); + setLocation(newLocation); + + int w = config.getInt(ApplicationConfiguration.EditDownloadDialog.WIDTH, -1); + int h = config.getInt(ApplicationConfiguration.EditDownloadDialog.HEIGHT, -1); + if (w != -1 && h != -1) { + setSize(w,h); + } + } catch (NoSuchElementException ignored) { + } finally { + config.unlock(LockMode.READ); + } + } + + private void saveLocation() { + if (!isVisible()) + return; + var config = ApplicationConfiguration.getConfiguration(); + try { + config.lock(LockMode.WRITE); + var location = getLocationOnScreen(); + config.setProperty(ApplicationConfiguration.EditDownloadDialog.X, location.x); + config.setProperty(ApplicationConfiguration.EditDownloadDialog.Y, location.y); + config.setProperty(ApplicationConfiguration.EditDownloadDialog.WIDTH, getWidth()); + config.setProperty(ApplicationConfiguration.EditDownloadDialog.HEIGHT, getHeight()); + } finally { + config.unlock(LockMode.WRITE); + } } private void setupResolutionButtons() { @@ -130,8 +185,6 @@ private FilmResolution.Enum getRadioButtonResolution() { private void changeRes() { // RadioButton sind nur enabled wenn "datenDownload.film" vorhanden var res = getRadioButtonResolution(); - //var test = res.toString(); - //var test2 = res.name(); datenDownload.arr[DatenDownload.DOWNLOAD_URL] = datenDownload.film.getUrlFuerAufloesung(res); textfeldListe[DatenDownload.DOWNLOAD_URL].setText(datenDownload.arr[DatenDownload.DOWNLOAD_URL]); @@ -287,33 +340,31 @@ private void addExtraFeld(int i, GridBagLayout gridbag, GridBagConstraints c) { jPanelExtra.add(jCheckBoxSpotlight); break; case DatenDownload.DOWNLOAD_HD: - jLabelFilmHD.setOpaque(false); - jLabelFilmHD.setIcon(ja_sw_16); + cbHighQuality.setSelected(true); gridbag.setConstraints(labelListe[i], c); jPanelExtra.add(labelListe[i]); c.gridx = 1; c.weightx = 10; - gridbag.setConstraints(jLabelFilmHD, c); - jPanelExtra.add(jLabelFilmHD); + gridbag.setConstraints(cbHighQuality, c); + jPanelExtra.add(cbHighQuality); if (datenDownload.film != null) { - jLabelFilmHD.setVisible(datenDownload.film.isHighQuality()); + cbHighQuality.setVisible(datenDownload.film.isHighQuality()); } else { - jLabelFilmHD.setVisible(false); + cbHighQuality.setVisible(false); } break; case DatenDownload.DOWNLOAD_UT: - jLabelFilmUT.setOpaque(false); - jLabelFilmUT.setIcon(ja_sw_16); + cbSubtitleAvailable.setSelected(true); gridbag.setConstraints(labelListe[i], c); jPanelExtra.add(labelListe[i]); c.gridx = 1; c.weightx = 10; - gridbag.setConstraints(jLabelFilmUT, c); - jPanelExtra.add(jLabelFilmUT); + gridbag.setConstraints(cbSubtitleAvailable, c); + jPanelExtra.add(cbSubtitleAvailable); if (datenDownload.film != null) { - jLabelFilmUT.setVisible(datenDownload.film.hasSubtitle()); + cbSubtitleAvailable.setVisible(datenDownload.film.hasSubtitle()); } else { - jLabelFilmUT.setVisible(false); + cbSubtitleAvailable.setVisible(false); } break; case DatenDownload.DOWNLOAD_PROGRAMM_AUFRUF: diff --git a/src/main/java/mediathek/tool/ApplicationConfiguration.java b/src/main/java/mediathek/tool/ApplicationConfiguration.java index 942fca2532..a77aa6afa7 100644 --- a/src/main/java/mediathek/tool/ApplicationConfiguration.java +++ b/src/main/java/mediathek/tool/ApplicationConfiguration.java @@ -231,6 +231,13 @@ public static class MemoryMonitorDialog { public static final String HEIGHT = "memory_monitor.height"; } + public static class EditDownloadDialog { + public static final String X = "edit_download_dialog.x"; + public static final String Y = "edit_download_dialog.y"; + public static final String WIDTH = "edit_download_dialog.width"; + public static final String HEIGHT = "edit_download_dialog.height"; + } + public static class SettingsDialog { public static final String WIDTH = "application.ui.settings_dialog.width"; public static final String HEIGHT = "application.ui.settings_dialog.height"; From 840178757f9881c3a7da0fcf8e62a94eba7fe6da Mon Sep 17 00:00:00 2001 From: Christian F Date: Mon, 18 Jul 2022 16:15:12 +0200 Subject: [PATCH 199/422] fix mouse listener not being removed properly --- src/main/java/mediathek/tool/InfiniteProgressPanel.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/mediathek/tool/InfiniteProgressPanel.java b/src/main/java/mediathek/tool/InfiniteProgressPanel.java index 816e831de1..0c84f8507a 100644 --- a/src/main/java/mediathek/tool/InfiniteProgressPanel.java +++ b/src/main/java/mediathek/tool/InfiniteProgressPanel.java @@ -383,12 +383,11 @@ public void run() if (!rampUp) { started = false; + removeMouseListener(InfiniteProgressPanel.this); // there has been an EDT violation warning... SwingUtilities.invokeLater(() -> { repaint(); - setVisible(false); - removeMouseListener(InfiniteProgressPanel.this); }); } } From 7a4373afdf0b25b4a92a75f5ee762bf059038a05 Mon Sep 17 00:00:00 2001 From: Christian F Date: Mon, 18 Jul 2022 16:34:19 +0200 Subject: [PATCH 200/422] fix mouse listener not being removed properly --- src/main/java/mediathek/daten/ListeAbo.java | 25 ++----------------- .../mediathek/mainwindow/MediathekGui.java | 3 --- .../mediathek/tool/InfiniteProgressPanel.java | 14 +++++++---- 3 files changed, 11 insertions(+), 31 deletions(-) diff --git a/src/main/java/mediathek/daten/ListeAbo.java b/src/main/java/mediathek/daten/ListeAbo.java index 0d5200f1c6..32436e53bb 100644 --- a/src/main/java/mediathek/daten/ListeAbo.java +++ b/src/main/java/mediathek/daten/ListeAbo.java @@ -101,8 +101,8 @@ public void aboLoeschen(@NotNull DatenAbo abo) { public void aenderungMelden() { // Filmliste anpassen - var worker = new ChangeWorker(); - worker.execute(); + setAboFuerFilm(Daten.getInstance().getListeFilme(), true); + MessageBus.getMessageBus().publishAsync(new AboListChangedEvent()); } public ArrayList getPfade() { @@ -217,25 +217,4 @@ public void setAboFuerFilm(ListeFilme listeFilme, boolean aboLoeschen) { datenAbo.setIrgendwoFilterPattern(LEER); }); } - - class ChangeWorker extends SwingWorker { - public ChangeWorker() { - SwingUtilities.invokeLater(() -> { - MediathekGui.ui().infiniteProgressPanel.setText("Verarbeite Abos..."); - MediathekGui.ui().infiniteProgressPanel.start(); - }); - } - @Override - protected Void doInBackground() { - setAboFuerFilm(Daten.getInstance().getListeFilme(), true); - MessageBus.getMessageBus().publishAsync(new AboListChangedEvent()); - return null; - } - - @Override - protected void done() { - MediathekGui.ui().infiniteProgressPanel.setText(""); - MediathekGui.ui().infiniteProgressPanel.stop(); - } - } } diff --git a/src/main/java/mediathek/mainwindow/MediathekGui.java b/src/main/java/mediathek/mainwindow/MediathekGui.java index 70d322e81a..3942b5c53a 100644 --- a/src/main/java/mediathek/mainwindow/MediathekGui.java +++ b/src/main/java/mediathek/mainwindow/MediathekGui.java @@ -84,7 +84,6 @@ public class MediathekGui extends JFrame { */ private static MediathekGui ui; public final LoadFilmListAction loadFilmListAction; - public final InfiniteProgressPanel infiniteProgressPanel = new InfiniteProgressPanel(); /** * Number of active downloads */ @@ -152,8 +151,6 @@ public class MediathekGui extends JFrame { public MediathekGui() { ui = this; - setGlassPane(infiniteProgressPanel); - setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); loadFilmListAction = new LoadFilmListAction(this); diff --git a/src/main/java/mediathek/tool/InfiniteProgressPanel.java b/src/main/java/mediathek/tool/InfiniteProgressPanel.java index 0c84f8507a..1bf643df61 100644 --- a/src/main/java/mediathek/tool/InfiniteProgressPanel.java +++ b/src/main/java/mediathek/tool/InfiniteProgressPanel.java @@ -206,6 +206,8 @@ public void start() */ public void stop() { + //removeMouseListener(this); + if (animation != null) { animation.interrupt(); animation = null; @@ -226,11 +228,14 @@ public void interrupt() animation.interrupt(); animation = null; - removeMouseListener(this); - setVisible(false); + tidy(); } } + private void tidy() { + removeMouseListener(this); + setVisible(false); + } public void paintComponent(Graphics g) { if (started) @@ -321,7 +326,7 @@ private Area buildPrimitive() */ private class Animator implements Runnable { - private boolean rampUp = true; + private final boolean rampUp; protected Animator(boolean rampUp) { @@ -383,11 +388,10 @@ public void run() if (!rampUp) { started = false; - removeMouseListener(InfiniteProgressPanel.this); // there has been an EDT violation warning... SwingUtilities.invokeLater(() -> { repaint(); - setVisible(false); + tidy(); }); } } From 5e8cacaf86d24df2a5f82b8b7839804f9a777247 Mon Sep 17 00:00:00 2001 From: Christian F Date: Mon, 18 Jul 2022 19:29:39 +0200 Subject: [PATCH 201/422] - remove download information text in tab downloads -> display in global status bar when necessary --- CHANGELOG.md | 3 +- .../gui/tabs/tab_downloads/GuiDownloads.java | 97 ------------------- .../mainwindow/DownloadInformationLabel.java | 19 +++- 3 files changed, 20 insertions(+), 99 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8038ab856a..6190fbc8fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,9 @@ - Der PSet-Parameter `%i` liefert anstatt der internen Filmnummer nun die aktuelle Zeit seit 1970 in Millisekunden. - *Globale Schriftgröße ändern...*-Befehl wurde unter Linux entfernt da es keine Auswirkungen mehr hat. - Im Tab "Filme" wurden *"Abo löschen"* und *"Abo ändern"* aufgrund der teilweise langen Ausführungszeiten und dem damit zusammenhängenden Einfrieren der Oberfläche entfernt. Bitte nutzen Sie wie dafür vorgesehen den "Abos verwalten" Dialog. +- Im Tab "Downloads" wurde die Textanzeige zu laufenden Downloads entfernt. Die Informationen werden nun in der globalen Statuszeile der App bei Bedarf angezeigt. - **BUGFIX:** Updater-Konfigurationsmöglichkeit wird nur noch in den Einstellungen aktiviert dargestellt, wenn sie auch Auswirkungen auf den Update-Mechanismus hat. -- **BUGFIX:** Oberfläche gibt nun verbessert Hinweise, wenn Abos verarbeitet werden. +- **BUGFIX:** Die Oberfläche gibt nun verbesserte Hinweise, wenn Abos verarbeitet werden (nur im "Abo verwalten" Dialog) - **BUGFIX:** Fehler in der Anzeige im Statusfeld des Download-Tab wurde behoben. - **BUGFIX:** *"Filme/Blacklist öffnen..."* Eintrag wurde in *"Filme/Blacklist bearbeiten..."* umbenannt und die Icons vereinheitlicht. - **BUGFIX:** Manchmal inkorrekte Darstellung des Bookmark-Status im Film-Tab wurde korrigiert. diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java index d48616f663..8cfe8e2b83 100644 --- a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java @@ -53,7 +53,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; public class GuiDownloads extends AGuiTabPanel { @@ -77,12 +76,7 @@ public class GuiDownloads extends AGuiTabPanel { private final static int[] COLUMNS_DISABLED = {DatenDownload.DOWNLOAD_BUTTON_START, DatenDownload.DOWNLOAD_BUTTON_DEL, DatenDownload.DOWNLOAD_REF, DatenDownload.DOWNLOAD_URL_RTMP}; private static final Logger logger = LogManager.getLogger(GuiDownloads.class); - private static final String HEAD = "\n" - + "" - + ""; - private static final String END = ""; private final AtomicLong _lastUpdate = new AtomicLong(0); - private final AtomicBoolean tabVisible = new AtomicBoolean(false); private final JCheckBoxMenuItem cbShowDownloadDescription = new JCheckBoxMenuItem("Filmbeschreibung anzeigen"); private final Configuration config = ApplicationConfiguration.getConfiguration(); private final MarkFilmAsSeenAction markFilmAsSeenAction = new MarkFilmAsSeenAction(); @@ -135,7 +129,6 @@ public class GuiDownloads extends AGuiTabPanel { private JButton btnClear; private JSpinner jSpinnerAnzahlDownloads; private JSpinner jSpinner1; - private JEditorPane txtDownload; private JScrollPane downloadListScrollPane; public GuiDownloads(Daten aDaten, MediathekGui mediathekGui) { @@ -169,8 +162,6 @@ public GuiDownloads(Daten aDaten, MediathekGui mediathekGui) { setupFilterPanel(); - setupComponentListener(); - if (Taskbar.isTaskbarSupported()) setupTaskbarMenu(); } @@ -253,20 +244,6 @@ private void initTable() { } } - private void setupComponentListener() { - addComponentListener(new ComponentAdapter() { - @Override - public void componentShown(ComponentEvent e) { - tabVisible.set(true); - } - - @Override - public void componentHidden(ComponentEvent e) { - tabVisible.set(false); - } - }); - } - private void setupFilterPanel() { final boolean visible = MVConfig.getBool(MVConfig.Configs.SYSTEM_TAB_DOWNLOAD_FILTER_VIS); updateFilterVisibility(visible); @@ -322,16 +299,6 @@ private void setupTaskbarMenu() { } } - @Handler - private void handleDownloadInfoUpdate(DownloadInfoUpdateAvailableEvent e) { - if (tabVisible.get()) { - SwingUtilities.invokeLater(() -> { - if (txtDownload.isShowing()) - setInfoText(); - }); - } - } - private void setupDownloadRateLimitSpinner() { //restore spinner setting from config final int oldDownloadLimit = ApplicationConfiguration.getConfiguration().getInt(ApplicationConfiguration.DOWNLOAD_RATE_LIMIT, 0); @@ -484,7 +451,6 @@ private void init() { final int location = config.getInt(ApplicationConfiguration.APPLICATION_UI_DOWNLOAD_TAB_DIVIDER_LOCATION, Konstanten.GUIDOWNLOAD_DIVIDER_LOCATION); jSplitPane1.setDividerLocation(location); - setupInfoPanel(); daten.getFilmeLaden().addAdListener(new ListenerFilmeLaden() { @Override public void start(ListenerFilmeLadenEvent event) { @@ -504,55 +470,6 @@ public void fertig(ListenerFilmeLadenEvent event) { }); } - private void setupInfoPanel() { - txtDownload.setText(""); - txtDownload.setEditable(false); - txtDownload.setFocusable(false); - txtDownload.setContentType("text/html"); - } - - private void setInfoText() { - if (daten.getListeDownloads().getStarts().total_starts == 0) { - txtDownload.setText(""); - return; - } - - String info = HEAD; - - final var downloadInfos = daten.getDownloadInfos(); - // Größe - final long byteAlleDownloads = downloadInfos.getByteAlleDownloads(); - final long byteAktDownloads = downloadInfos.getByteAktDownloads(); - if (byteAlleDownloads > 0 || byteAktDownloads > 0) { - info += "
                              "; - info += "Größe:
                              "; - if (byteAktDownloads > 0) { - info += FileSize.convertSize(byteAktDownloads) + " von " - + FileSize.convertSize(byteAlleDownloads) + " MByte" + "
                              "; - } else { - info += FileSize.convertSize(byteAlleDownloads) + " MByte" + ""; - } - } - // Restzeit - final long timeRestAktDownloads = downloadInfos.getTimeRestAktDownloads(); - final long timeRestAllDownloads = downloadInfos.getTimeRestAllDownloads(); - if (timeRestAktDownloads > 0 && timeRestAllDownloads > 0) { - info += "
                              "; - info += "Restzeit:
                              " + "laufende: " - + downloadInfos.getRestzeit() + ",
                              alle: " + downloadInfos.getGesamtRestzeit() + "
                              "; - } else if (timeRestAktDownloads > 0) { - info += "
                              "; - info += "Restzeit:
                              laufende: " + downloadInfos.getRestzeit() + "
                              "; - } else if (timeRestAllDownloads > 0) { - info += "
                              "; - info += "Restzeit:
                              alle: " + downloadInfos.getGesamtRestzeit() + "
                              "; - } - - info += END; - - txtDownload.setText(info); - } - @Handler private void handleRestartDownloadEvent(RestartDownloadEvent e) { reloadAndSave(); @@ -1102,8 +1019,6 @@ private void initComponents() { var lblBandwidth = new JLabel(); var jLabel1 = new JLabel(); jSpinner1 = new JSpinner(); - var spDownload = new JScrollPane(); - txtDownload = new JEditorPane(); var downloadListArea = new JPanel(); downloadListScrollPane = new JScrollPane(); @@ -1193,18 +1108,6 @@ private void initComponents() { panel2.add(jSpinner1, new CC().cell(1, 1)); } jPanelFilterExtern.add(panel2, new CC().cell(0, 1)); - - //======== spDownload ======== - { - spDownload.setPreferredSize(new Dimension(14, 150)); - - //---- txtDownload ---- - txtDownload.setEditable(false); - txtDownload.setOpaque(false); - txtDownload.setPreferredSize(new Dimension(10, 500)); - spDownload.setViewportView(txtDownload); - } - jPanelFilterExtern.add(spDownload, new CC().cell(0, 2)); } jSplitPane1.setLeftComponent(jPanelFilterExtern); diff --git a/src/main/java/mediathek/mainwindow/DownloadInformationLabel.java b/src/main/java/mediathek/mainwindow/DownloadInformationLabel.java index 77a0078833..033b9752c7 100644 --- a/src/main/java/mediathek/mainwindow/DownloadInformationLabel.java +++ b/src/main/java/mediathek/mainwindow/DownloadInformationLabel.java @@ -2,6 +2,7 @@ import mediathek.config.Daten; import mediathek.gui.messages.DownloadInfoUpdateAvailableEvent; +import mediathek.tool.FileSize; import mediathek.tool.MessageBus; import net.engio.mbassy.listener.Handler; @@ -35,9 +36,25 @@ private String getInfoTextDownloads() { textLinks += (info.running == 1) ? "1 läuft" : info.running + " laufen"; - if (info.running > 0) + if (info.running > 0) { textLinks += " (" + daten.getDownloadInfos().getBandwidthStr() + ')'; + var infos = daten.getDownloadInfos(); + final long byteAlleDownloads = infos.getByteAlleDownloads(); + final long byteAktDownloads = infos.getByteAktDownloads(); + if (byteAlleDownloads > 0 || byteAktDownloads > 0) { + textLinks += " ("; + textLinks += "Größe: "; + if (byteAktDownloads > 0) { + textLinks += FileSize.convertSize(byteAktDownloads) + " von " + + FileSize.convertSize(byteAlleDownloads) + " MByte)"; + } else { + textLinks += FileSize.convertSize(byteAlleDownloads) + " MByte)"; + } + } + + } + textLinks += (info.initialized == 1) ? ", 1 wartet" : ", " + info.initialized + " warten"; if (info.finished > 0) From 75ea702e4f0f322c980b9114bf92e176b6a99fd0 Mon Sep 17 00:00:00 2001 From: Christian F Date: Tue, 19 Jul 2022 17:33:17 +0200 Subject: [PATCH 202/422] - make confirmation dialogs for restart of already finished downloads auto-dismissable with safe values. --- CHANGELOG.md | 1 + .../gui/tabs/tab_downloads/GuiDownloads.java | 19 +++++++----- .../java/mediathek/tool/GuiFunktionen.java | 30 +++++++++++++++++++ 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6190fbc8fb..d76757ae01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ - **FEATURE:** Geoinformationen werden nun als *gesperrt/entsperrt* Icons dargestellt. Detailinformationen werden über Tooltip-Text eingeblendet. - **FEATURE:** Ein manchmal unsichtbarer Filter-Dialog kann nun über das Menü *Hilfe/Filterdialog-Position zurücksetzen* auf den sichtbaren Monitorbereich zurückgesetzt werden. - **FEATURE:** Untertitelverfügbarkeit wird nun über ein `cc`-Icon an Anfang des Titels angezeigt. Die Spalte `UT` wird in einer späteren Programmversion entfernt werden. +- **FEATURE:** Im Tab Download werden die Dialoge bzgl. Neustarts fertiger Downloads nun automatisch nach 10 Sekunden geschlossen wenn kein Userinput passiert. Es wird dann jeweils die "Nein"-Option ausgewählt um bereits vorhandene Downloads zu schützen. - **FEATURE(Windows/Linux):** Schriftgröße kann für das gesamte Programm über das Menü "*Schrift*" geändert werden. **13.9.1** diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java index 8cfe8e2b83..2bbaaa3f83 100644 --- a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java @@ -53,6 +53,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; public class GuiDownloads extends AGuiTabPanel { @@ -821,16 +822,17 @@ public void startAllDownloadsAtSpecificTime() { if (download.start.status > Start.STATUS_RUN) { // wenn er noch läuft gibts nix // wenn er schon fertig ist, erst mal fragen vor dem erneuten Starten - //TODO in auto dialog umwandeln! - int a = JOptionPane.showConfirmDialog(mediathekGui, "Film nochmal starten? ==> " + download.arr[DatenDownload.DOWNLOAD_TITEL], - "Fertiger Download", JOptionPane.YES_NO_OPTION); - if (a != JOptionPane.YES_OPTION) { + int reply = GuiFunktionen.createDismissableMessageDialog(mediathekGui, "Fertiger Download", + "Film nochmal starten? ==> " + download.arr[DatenDownload.DOWNLOAD_TITEL], + JOptionPane.YES_NO_OPTION,JOptionPane.NO_OPTION, 10, TimeUnit.SECONDS, + JOptionPane.QUESTION_MESSAGE); + if (reply != JOptionPane.YES_OPTION) { // weiter mit der nächsten URL continue; } listeUrlsDownloadsAbbrechen.add(download); if (download.isFromAbo()) { - // wenn er schon feritg ist und ein Abos ist, Url auch aus dem Logfile löschen, der Film ist damit wieder auf "Anfang" + // wenn er schon fertig ist und ein Abo ist, Url auch aus dem Logfile löschen, der Film ist damit wieder auf "Anfang" daten.getAboHistoryController().removeUrl(download.arr[DatenDownload.DOWNLOAD_HISTORY_URL]); } } @@ -899,7 +901,6 @@ private void filmStartenWiederholenStoppen(boolean processAllDownloads, boolean } if (download.start.status > Start.STATUS_RUN) { // wenn er schon fertig ist, erst mal fragen vor dem erneuten Starten - //TODO in auto dialog umwandeln! if (antwort == -1) { // nur einmal fragen String text; @@ -909,8 +910,10 @@ private void filmStartenWiederholenStoppen(boolean processAllDownloads, boolean } else { text = "Film nochmal starten? ==> " + download.arr[DatenDownload.DOWNLOAD_TITEL]; } - antwort = JOptionPane.showConfirmDialog(mediathekGui, text, - "Fertiger Download", JOptionPane.YES_NO_CANCEL_OPTION); + antwort = GuiFunktionen.createDismissableMessageDialog(mediathekGui, "Fertiger Download", + text, + JOptionPane.YES_NO_CANCEL_OPTION,JOptionPane.NO_OPTION, 10, TimeUnit.SECONDS, + JOptionPane.QUESTION_MESSAGE); } if (antwort == JOptionPane.CANCEL_OPTION) { //============================= diff --git a/src/main/java/mediathek/tool/GuiFunktionen.java b/src/main/java/mediathek/tool/GuiFunktionen.java index 558ef3ba9a..6f0717c081 100644 --- a/src/main/java/mediathek/tool/GuiFunktionen.java +++ b/src/main/java/mediathek/tool/GuiFunktionen.java @@ -6,12 +6,15 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.intellij.lang.annotations.MagicConstant; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; import java.awt.datatransfer.StringSelection; import java.awt.event.InputEvent; import java.io.File; +import java.util.concurrent.TimeUnit; public class GuiFunktionen { @@ -46,6 +49,33 @@ public static boolean isNotUsingExternalUpdater() { return !ret; } + /** + * Create an auto-dismissable JOptionPane-like dialog with return value. + * Uses internally {@link JOptionPane} to create and handle the values. + * @param parentComponent the parent object. + * @param title the dialog title. + * @param message the message. + * @param optionType Currently I support only DEFAULT ok-dialog and YES_NO_OPTION confirmDialog. + * @param defaultValue the default value to return. + * @param defaultDelay the number of time units to wait until close. + * @param timeUnit The time unit of defaultDelay. + * @param style the warning, error or information style from {@link JOptionPane}. + * @return the clicked value or the defaultValue. + */ + public static int createDismissableMessageDialog(@Nullable Component parentComponent, + @NotNull String title, + @NotNull String message, + @MagicConstant(intValues = {JOptionPane.YES_NO_OPTION, JOptionPane.DEFAULT_OPTION, JOptionPane.YES_NO_CANCEL_OPTION}) int optionType, + @MagicConstant(intValues = {JOptionPane.OK_OPTION, JOptionPane.CANCEL_OPTION, JOptionPane.YES_OPTION, JOptionPane.NO_OPTION}) int defaultValue, + int defaultDelay, @NotNull TimeUnit timeUnit, + @MagicConstant(intValues = {JOptionPane.WARNING_MESSAGE, JOptionPane.QUESTION_MESSAGE, JOptionPane.INFORMATION_MESSAGE, JOptionPane.ERROR_MESSAGE}) int style) { + var op = new JOptionPane(message, style, optionType, null, null); + var dialog = op.createDialog(parentComponent, title); + new Timer((int)TimeUnit.MILLISECONDS.convert(defaultDelay,timeUnit), e -> op.setValue(defaultValue)).start(); + dialog.setVisible(true); + return (int)op.getValue(); + } + public static boolean isUsingExternalUpdater() { return !isNotUsingExternalUpdater(); } From 290001c155ace86648a8c6f23e4d2d7bd4e37706 Mon Sep 17 00:00:00 2001 From: Christian F Date: Tue, 19 Jul 2022 17:37:59 +0200 Subject: [PATCH 203/422] - due to change to swing the filter dialog won't hide tooltips anymore - fixes #572 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d76757ae01..3ec0026eb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - Im Tab "Filme" wurden *"Abo löschen"* und *"Abo ändern"* aufgrund der teilweise langen Ausführungszeiten und dem damit zusammenhängenden Einfrieren der Oberfläche entfernt. Bitte nutzen Sie wie dafür vorgesehen den "Abos verwalten" Dialog. - Im Tab "Downloads" wurde die Textanzeige zu laufenden Downloads entfernt. Die Informationen werden nun in der globalen Statuszeile der App bei Bedarf angezeigt. - **BUGFIX:** Updater-Konfigurationsmöglichkeit wird nur noch in den Einstellungen aktiviert dargestellt, wenn sie auch Auswirkungen auf den Update-Mechanismus hat. +- **BUGFIX:** Filter-Fenster überlagert nicht mehr Tooltip-Anzeigen. - **BUGFIX:** Die Oberfläche gibt nun verbesserte Hinweise, wenn Abos verarbeitet werden (nur im "Abo verwalten" Dialog) - **BUGFIX:** Fehler in der Anzeige im Statusfeld des Download-Tab wurde behoben. - **BUGFIX:** *"Filme/Blacklist öffnen..."* Eintrag wurde in *"Filme/Blacklist bearbeiten..."* umbenannt und die Icons vereinheitlicht. From d840b399125d160735cb6849c5e107fc9bf1ae06 Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 20 Jul 2022 09:10:19 +0200 Subject: [PATCH 204/422] - implement progress gui for film export --- .../gui/actions/FilmListExportAction.java | 54 ++++++++++++++++--- 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/src/main/java/mediathek/gui/actions/FilmListExportAction.java b/src/main/java/mediathek/gui/actions/FilmListExportAction.java index bed00837dc..47b2970112 100644 --- a/src/main/java/mediathek/gui/actions/FilmListExportAction.java +++ b/src/main/java/mediathek/gui/actions/FilmListExportAction.java @@ -5,9 +5,11 @@ import mediathek.filmlisten.writer.FilmListWriter; import mediathek.mainwindow.MediathekGui; import mediathek.tool.FileDialogs; +import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.event.ActionEvent; +import java.io.File; /** * Exports the current film list to JSON file. @@ -36,9 +38,51 @@ private void showSuccess() { public void actionPerformed(ActionEvent e) { setEnabled(false); + ProgressMonitor monitor = new ProgressMonitor(MediathekGui.ui(), "Exportiere Filmliste", "", 0, 100); + monitor.setMillisToPopup(100); + monitor.setMillisToDecideToPopup(100); var selectedFile = FileDialogs.chooseSaveFileLocation(MediathekGui.ui(), "Lesbare Filmliste sichern", ""); if (selectedFile != null) { + + ExportWorker worker = new ExportWorker(selectedFile); + worker.addPropertyChangeListener(evt -> { + if ("progress".equals(evt.getPropertyName())) { + int progress = (int) evt.getNewValue(); + monitor.setProgress(progress); + } + }); + worker.execute(); + } else { + JOptionPane.showMessageDialog(MediathekGui.ui(), "Export wurde abgebrochen", Konstanten.PROGRAMMNAME, JOptionPane.WARNING_MESSAGE); + setEnabled(true); + } + } + + class ExportWorker extends SwingWorker { + private final File selectedFile; + + public ExportWorker(@NotNull File selectedFile) { + this.selectedFile = selectedFile; + } + + @Override + protected void done() { try { + System.out.println("Done"); + var result = get(); + if (result) + showSuccess(); + else + showError(); + } catch (Exception e) { + showError(); + } + setEnabled(true); + } + + @Override + protected Boolean doInBackground() throws Exception { + if (selectedFile != null) { FilmListWriter writer = new FilmListWriter(true); // do not "compress" the sender tag writer.setCompressSenderTag(false); @@ -46,15 +90,11 @@ public void actionPerformed(ActionEvent e) { writer.setDecompressUrls(true); writer.writeFilmList(selectedFile.getAbsolutePath(), Daten.getInstance().getListeFilme(), - prog -> { - }); - showSuccess(); - } catch (Exception ex) { - showError(); + prog -> setProgress((int) Math.round(100d * prog))); } - } - setEnabled(true); + return true; + } } } From e02f9141a2661ea6777dece3fbe8f5fb7ee5319d Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 20 Jul 2022 12:04:25 +0200 Subject: [PATCH 205/422] - convert Info Dialog back to swing --- .../gui/filmInformation/InfoDialog.kt | 336 +++++++++--------- 1 file changed, 164 insertions(+), 172 deletions(-) diff --git a/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt b/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt index 24f2c1fc9d..eb331d554f 100644 --- a/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt +++ b/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt @@ -1,62 +1,56 @@ package mediathek.gui.filmInformation -import javafx.application.Platform -import javafx.embed.swing.JFXPanel -import javafx.embed.swing.SwingFXUtils -import javafx.event.EventHandler -import javafx.geometry.Pos -import javafx.scene.Scene -import javafx.scene.control.* -import javafx.scene.image.ImageView +import com.formdev.flatlaf.util.ScaledImageIcon +import com.jidesoft.swing.MultilineLabel import mediathek.daten.DatenFilm import mediathek.gui.actions.UrlHyperlinkAction -import mediathek.javafx.tool.JavaFxUtils +import mediathek.mainwindow.MediathekGui import mediathek.tool.ApplicationConfiguration import mediathek.tool.GuiFunktionen +import mediathek.tool.SwingErrorDialog import mediathek.tool.sender_icon_cache.MVSenderIconCache +import net.miginfocom.layout.AC import net.miginfocom.layout.CC +import net.miginfocom.layout.LC +import net.miginfocom.swing.MigLayout import org.apache.commons.configuration2.sync.LockMode -import org.tbee.javafx.scene.layout.MigPane -import java.awt.BorderLayout +import org.jdesktop.swingx.JXHyperlink +import java.awt.Desktop +import java.awt.Dimension import java.awt.Point import java.awt.Window import java.awt.event.ComponentAdapter import java.awt.event.ComponentEvent import java.awt.event.WindowAdapter import java.awt.event.WindowEvent -import java.awt.image.BufferedImage +import java.net.URI import java.net.URISyntaxException -import javax.swing.ImageIcon -import javax.swing.JDialog -import javax.swing.SwingUtilities +import javax.swing.* + class InfoDialog(parent: Window?) : JDialog(parent) { private val config = ApplicationConfiguration.getConfiguration() private var currentFilm: DatenFilm? = null - private val lblSender = Label() - private val lblThema = Label() - private val lblTitle = Label() - private val lblDate = Label() - private val lblUhrzeit = Label() - private val lblDuration = Label() - private val lblSize = Label() - private val cbHq = DisabledCheckBox() - private val cbSubtitle = DisabledCheckBox() - private val lblGeo = Label() - private val lblAbo = Label() - private val hyperlink = Hyperlink("Link zur Webseite") - private val lblDescription = TextArea() + private val lblSender = JLabel() + private val lblThema = MultilineLabel() + private val lblTitel = MultilineLabel() + private val lblDate = JLabel() + private val lblUhrzeit = JLabel() + private val lblDuration = JLabel() + private val lblSize = JLabel() + private val lblGeo = JLabel() + private val cbHq = SwingDisabledCheckBox() + private val cbSubtitle = SwingDisabledCheckBox() + private val lblAbo = JLabel() + private val hyperlink = SwingHyperlink() + private val descScrollPane = JScrollPane() + private val lblDescription = JTextArea() - private fun installContextMenu(component: Label) { - val ctMenu = ContextMenu() - val mi = MenuItem("Text in die Zwischenablage kopieren") - mi.onAction = EventHandler { - GuiFunktionen.copyToClipboard(component.text) + internal class SwingDisabledCheckBox : JCheckBox() { + init { + isEnabled = false } - ctMenu.items.add(mi) - component.contextMenu = ctMenu } - /** * Restore window position from config settings. */ @@ -95,61 +89,56 @@ class InfoDialog(parent: Window?) : JDialog(parent) { } private fun clearControls() { - Platform.runLater { - lblDescription.text = "" - lblAbo.text = "" - lblGeo.text = "" - lblSender.text = "" - lblSender.graphic = null - lblSize.text = "" - lblThema.text = "" - lblTitle.text = "" - lblDate.text = "" - lblUhrzeit.text = "" - lblDuration.text = "" - cbHq.isSelected = false - cbSubtitle.isSelected = false - hyperlink.tooltip = null - hyperlink.isDisable = true - } - } + lblThema.text = "" + lblSender.text = "" + lblSender.icon = null + lblTitel.text = "" + lblDate.text = "" + lblUhrzeit.text = "" + lblDuration.text = "" + lblSender.text = "" + lblGeo.text = "" + cbHq.isSelected = false + cbSubtitle.isSelected = false + lblAbo.text = "" + hyperlink.toolTipText = "" + hyperlink.isEnabled = false + lblDescription.text = "" - /** - * A sender icon with a fixed height of 32 pixel while maintaining aspect ratio. - */ - internal class SenderIcon(b_img: BufferedImage) : ImageView() { - init { - fitHeight = 32.0 - isPreserveRatio = true - image = SwingFXUtils.toFXImage(b_img, null) - } + pack() } + companion object { + private val DEFAULT_SENDER_DIMENSION = Dimension(64,64) + } private fun updateTextFields() { if (currentFilm == null) { clearControls() } else { - Platform.runLater { - val desc = currentFilm!!.description.trim { it <= ' ' } - lblDescription.text = desc - lblDescription.scrollTop = 0.0 - lblDescription.scrollLeft = 0.0 - MVSenderIconCache[currentFilm!!.sender].ifPresent { icon: ImageIcon? -> - lblSender.text = "" - lblSender.graphic = SenderIcon(JavaFxUtils.toBufferedImage(icon)) - } - lblGeo.text = currentFilm!!.geo.orElse("") - lblSize.text = currentFilm!!.fileSize.toString() - lblThema.text = currentFilm!!.thema - lblTitle.text = currentFilm!!.title - lblDate.text = currentFilm!!.sendeDatum - lblUhrzeit.text = currentFilm!!.sendeZeit - lblDuration.text = currentFilm!!.filmLengthAsString - cbHq.isSelected = currentFilm!!.isHighQuality - cbSubtitle.isSelected = currentFilm!!.hasSubtitle() - hyperlink.tooltip = Tooltip(currentFilm!!.websiteUrl) - hyperlink.isDisable = false - lblAbo.text = currentFilm!!.abo?.name + MVSenderIconCache[currentFilm!!.sender].ifPresent { icon: ImageIcon? -> + lblSender.text = "" + val imageDim = Dimension(icon!!.iconWidth, icon.iconHeight) + val destDim = GuiFunktionen.calculateFittedDimension(imageDim, DEFAULT_SENDER_DIMENSION) + lblSender.icon = ScaledImageIcon(icon, destDim.width, destDim.height) + } + lblThema.text = currentFilm!!.thema + lblTitel.text = currentFilm!!.title + lblDate.text = currentFilm!!.sendeDatum + lblUhrzeit.text = currentFilm!!.sendeZeit + lblDuration.text = currentFilm!!.filmLengthAsString + lblSize.text = currentFilm!!.fileSize.toString() + lblGeo.text = currentFilm!!.geo.orElse("") + cbHq.isSelected = currentFilm!!.isHighQuality + cbSubtitle.isSelected = currentFilm!!.hasSubtitle() + lblAbo.text = currentFilm!!.abo?.name + hyperlink.isEnabled = true + hyperlink.toolTipText = currentFilm!!.websiteUrl + hyperlink.isClicked = false + + val desc = currentFilm!!.description.trim { it <= ' ' } + lblDescription.text = desc + SwingUtilities.invokeLater { + descScrollPane.verticalScrollBar.value = 0 } } } @@ -161,104 +150,107 @@ class InfoDialog(parent: Window?) : JDialog(parent) { */ fun updateCurrentFilm(film: DatenFilm?) { currentFilm = film - if (isVisible) updateTextFields() + if (isVisible) { + updateTextFields() + pack() + } } - private fun buildLayout() { - contentPane.layout = BorderLayout() - val newFxPanel = JFXPanel() - contentPane.add(newFxPanel, BorderLayout.CENTER) - Platform.runLater { - val migPane = MigPane( - "hidemode 3", //columns - "[fill,shrink 0]" + - "[fill]", //rows - "[]" + - "[]" + - "[]" + - "[]" + - "[]" + - "[]" + - "[]" + - "[]" + - "[]" + - "[]" + - "[]" + - "[]" + - "[]" + - "[fill,grow]") - migPane.add(RightOrientedLabel("Sender:"), CC().cell(0, 0)) - migPane.add(lblSender, CC().cell(1, 0)) - - migPane.add(RightOrientedLabel("Thema:"), CC().cell(0, 1)) - lblThema.isWrapText = true - installContextMenu(lblThema) - migPane.add(lblThema, CC().cell(1, 1).growY()) - - migPane.add(RightOrientedLabel("Titel:"), CC().cell(0, 2)) - lblTitle.isWrapText = true - installContextMenu(lblTitle) - migPane.add(lblTitle, CC().cell(1, 2).growY()) + internal class SwingHyperlink : JXHyperlink() { + private fun openUrl(url : String) { + try { + UrlHyperlinkAction.openURL(null, url) + } catch (ex: URISyntaxException) { + ex.printStackTrace() + } + } - migPane.add(RightOrientedLabel("Datum:"), CC().cell(0, 3)) - migPane.add(lblDate, CC().cell(1, 3)) - migPane.add(RightOrientedLabel("Uhrzeit:"), CC().cell(0, 4)) - migPane.add(lblUhrzeit, CC().cell(1, 4)) - migPane.add(RightOrientedLabel("Dauer:"), CC().cell(0, 5)) - migPane.add(lblDuration, CC().cell(1, 5)) - migPane.add(RightOrientedLabel("Größe (MB):"), CC().cell(0, 6)) - migPane.add(lblSize, CC().cell(1, 6)) - migPane.add(RightOrientedLabel("HQ:"), CC().cell(0, 7)) - migPane.add(cbHq, CC().cell(1, 7)) - migPane.add(RightOrientedLabel("Untertitel:"), CC().cell(0, 8)) - migPane.add(cbSubtitle, CC().cell(1, 8)) - migPane.add(RightOrientedLabel("Geo:"), CC().cell(0, 9)) - migPane.add(lblGeo, CC().cell(1, 9)) - migPane.add(RightOrientedLabel("Abo:"), CC().cell(0, 10)) - migPane.add(lblAbo, CC().cell(1, 10)) - migPane.add(RightOrientedLabel("Beschreibung:"), CC().cell(0, 12)) + init { + this.text = "Link zur Webseite" - hyperlink.contextMenu = createCopyUrlContextMenu() - hyperlink.isUnderline = true - hyperlink.onAction = EventHandler { - SwingUtilities.invokeLater { - if (currentFilm != null) { - try { - UrlHyperlinkAction.openURL(null, currentFilm!!.websiteUrl) - } catch (ex: URISyntaxException) { - ex.printStackTrace() + addActionListener { + if (toolTipText.isNotEmpty()) { + if (Desktop.isDesktopSupported()) { + val d = Desktop.getDesktop() + if (d.isSupported(Desktop.Action.BROWSE)) { + try { + d.browse(URI(toolTipText)) + } + catch (ex: Exception) { + SwingErrorDialog.showExceptionMessage( + MediathekGui.ui(), + "Es trat ein Fehler beim Öffnen des Links auf.\nSollte dies häufiger auftreten kontaktieren Sie bitte das Entwicklerteam.", + ex) + } + } else { + openUrl(toolTipText) } } + else { + openUrl(toolTipText) + } + } } - migPane.add(hyperlink, CC().cell(0, 11).spanX(2)) - - lblDescription.isWrapText = true - lblDescription.prefRowCount = 4 - lblDescription.isEditable = false - migPane.add(lblDescription, CC().cell(0, 13).spanX(2).growY().growX().minHeight("60")) - newFxPanel.scene = Scene(migPane) } } - private fun createCopyUrlContextMenu() : ContextMenu { - val contextMenu = ContextMenu() - val mi = MenuItem("URL kopieren") - mi.onAction = EventHandler { SwingUtilities.invokeLater { GuiFunktionen.copyToClipboard(currentFilm!!.websiteUrl) } } - contextMenu.items.add(mi) - return contextMenu - } - - internal class RightOrientedLabel(label: String?) : Label(label) { - init { - alignment = Pos.BASELINE_RIGHT - } - } - - internal class DisabledCheckBox : CheckBox() { - init { - isDisable = true - } + private fun buildLayout() { + layout = MigLayout( + LC().insets("5").hideMode(3), + // columns + AC() + .fill().gap() + .size("250!").fill(), + // rows + AC() + .gap() + .gap() + .gap() + .gap() + .gap() + .gap() + .gap() + .gap() + .gap() + .gap() + .gap() + .gap() + .gap() + .gap()) + + add(JLabel("Sender:"), CC().cell(0, 0)) + add(lblSender, CC().cell(1, 0)) + add(JLabel("Thema:"), CC().cell(0, 1)) + lblThema.lineWrap = true + add(lblThema, CC().cell(1, 1).growY()) + add(JLabel("Titel:"), CC().cell(0, 2)) + lblTitel.lineWrap = true + add(lblTitel, CC().cell(1, 2).growY()) + + add(JLabel("Datum:"), CC().cell(0, 3)) + add(lblDate, CC().cell(1, 3)) + add(JLabel("Uhrzeit:"), CC().cell(0, 4)) + add(lblUhrzeit, CC().cell(1, 4)) + add(JLabel("Dauer:"), CC().cell(0, 5)) + add(lblDuration, CC().cell(1, 5)) + add(JLabel("Größe (MB):"), CC().cell(0, 6)) + add(lblSize, CC().cell(1, 6)) + add(JLabel("HQ:"), CC().cell(0, 7)) + add(cbHq, CC().cell(1, 7)) + add(JLabel("Untertitel:"), CC().cell(0, 8)) + add(cbSubtitle, CC().cell(1, 8)) + add(JLabel("Geo:"), CC().cell(0, 9)) + add(lblGeo, CC().cell(1, 9)) + add(JLabel("Abo:"), CC().cell(0, 10)) + add(lblAbo, CC().cell(1, 10)) + add(hyperlink, CC().cell(0, 11).spanX(2)) + add(JLabel("Beschreibung:"), CC().cell(0, 12)) + lblDescription.isEditable = false + lblDescription.lineWrap = true + lblDescription.wrapStyleWord = true + descScrollPane.setViewportView(lblDescription) + add(descScrollPane, CC().cell(0, 13).spanX(2).growY().growX().height("90!")) } init { @@ -266,7 +258,7 @@ class InfoDialog(parent: Window?) : JDialog(parent) { title = "Filminformation" isResizable = false //hardcode size as linux hates pack() - setSize(325, 520) + //setSize(350, 520) defaultCloseOperation = DISPOSE_ON_CLOSE buildLayout() updateTextFields() From 3c3099d9cbfc81af6a0d2e354e25bd53bbee5ed9 Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 20 Jul 2022 12:17:10 +0200 Subject: [PATCH 206/422] - try to fix sizing issue on linux --- src/main/java/mediathek/gui/filmInformation/InfoDialog.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt b/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt index eb331d554f..e9aadc8223 100644 --- a/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt +++ b/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt @@ -197,7 +197,7 @@ class InfoDialog(parent: Window?) : JDialog(parent) { private fun buildLayout() { layout = MigLayout( - LC().insets("5").hideMode(3), + LC().insets("5").hideMode(3).debug(), // columns AC() .fill().gap() @@ -259,8 +259,10 @@ class InfoDialog(parent: Window?) : JDialog(parent) { isResizable = false //hardcode size as linux hates pack() //setSize(350, 520) + minimumSize = Dimension(350,450) defaultCloseOperation = DISPOSE_ON_CLOSE buildLayout() + pack() updateTextFields() restoreLocation() val wasVisible = config.getBoolean(ApplicationConfiguration.FilmInfoDialog.FILM_INFO_VISIBLE, false) From 7275c73c0dd2b132c09463f9cc9b14d8a31511ed Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 20 Jul 2022 12:23:30 +0200 Subject: [PATCH 207/422] - try to fix sizing issue on linux --- src/main/java/mediathek/gui/filmInformation/InfoDialog.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt b/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt index e9aadc8223..a7d924c57c 100644 --- a/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt +++ b/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt @@ -104,8 +104,7 @@ class InfoDialog(parent: Window?) : JDialog(parent) { hyperlink.toolTipText = "" hyperlink.isEnabled = false lblDescription.text = "" - - pack() + SwingUtilities.invokeLater { pack() } } companion object { @@ -139,6 +138,7 @@ class InfoDialog(parent: Window?) : JDialog(parent) { lblDescription.text = desc SwingUtilities.invokeLater { descScrollPane.verticalScrollBar.value = 0 + pack() } } } @@ -152,7 +152,7 @@ class InfoDialog(parent: Window?) : JDialog(parent) { currentFilm = film if (isVisible) { updateTextFields() - pack() + //pack() } } @@ -259,7 +259,7 @@ class InfoDialog(parent: Window?) : JDialog(parent) { isResizable = false //hardcode size as linux hates pack() //setSize(350, 520) - minimumSize = Dimension(350,450) +// minimumSize = Dimension(350,450) defaultCloseOperation = DISPOSE_ON_CLOSE buildLayout() pack() From b13ac0f8613114a5e48c195a214a889c2613fa33 Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 20 Jul 2022 12:33:07 +0200 Subject: [PATCH 208/422] - try to fix sizing issue on linux --- .../mediathek/gui/filmInformation/InfoDialog.kt | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt b/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt index a7d924c57c..20819a348f 100644 --- a/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt +++ b/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt @@ -25,6 +25,7 @@ import java.awt.event.WindowAdapter import java.awt.event.WindowEvent import java.net.URI import java.net.URISyntaxException +import java.util.concurrent.atomic.AtomicBoolean import javax.swing.* @@ -45,6 +46,8 @@ class InfoDialog(parent: Window?) : JDialog(parent) { private val hyperlink = SwingHyperlink() private val descScrollPane = JScrollPane() private val lblDescription = JTextArea() + private val isPacking = AtomicBoolean(false) + internal class SwingDisabledCheckBox : JCheckBox() { init { @@ -67,6 +70,13 @@ class InfoDialog(parent: Window?) : JDialog(parent) { } } + override fun pack() { + isPacking.set(true) + super.pack() + restoreLocation() + isPacking.set(false) + } + /** * Save window position to config. */ @@ -282,7 +292,10 @@ class InfoDialog(parent: Window?) : JDialog(parent) { //addFilmlistLoadListener(); addComponentListener(object : ComponentAdapter() { override fun componentMoved(e: ComponentEvent) { - if (isVisible) saveLocation() + if (isVisible) { + if (!isPacking.get()) + saveLocation() + } } }) } From b194aafda37dfac918763c265beda87477546b2e Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 20 Jul 2022 12:39:08 +0200 Subject: [PATCH 209/422] - revert trying to fix dialog on linux --- CHANGELOG.md | 1 + .../gui/filmInformation/InfoDialog.kt | 37 +++++++------------ 2 files changed, 15 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ec0026eb6..95d8b08a9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - *Globale Schriftgröße ändern...*-Befehl wurde unter Linux entfernt da es keine Auswirkungen mehr hat. - Im Tab "Filme" wurden *"Abo löschen"* und *"Abo ändern"* aufgrund der teilweise langen Ausführungszeiten und dem damit zusammenhängenden Einfrieren der Oberfläche entfernt. Bitte nutzen Sie wie dafür vorgesehen den "Abos verwalten" Dialog. - Im Tab "Downloads" wurde die Textanzeige zu laufenden Downloads entfernt. Die Informationen werden nun in der globalen Statuszeile der App bei Bedarf angezeigt. +- Der Filminformationsdialog wurde neu geschrieben. Unter Linux kann es vorkommen dass der Dialog bis an den oberen Bildschirmrand "wandert". Dies ist ein Fehler von Java. - **BUGFIX:** Updater-Konfigurationsmöglichkeit wird nur noch in den Einstellungen aktiviert dargestellt, wenn sie auch Auswirkungen auf den Update-Mechanismus hat. - **BUGFIX:** Filter-Fenster überlagert nicht mehr Tooltip-Anzeigen. - **BUGFIX:** Die Oberfläche gibt nun verbesserte Hinweise, wenn Abos verarbeitet werden (nur im "Abo verwalten" Dialog) diff --git a/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt b/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt index 20819a348f..f69612945e 100644 --- a/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt +++ b/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt @@ -25,7 +25,6 @@ import java.awt.event.WindowAdapter import java.awt.event.WindowEvent import java.net.URI import java.net.URISyntaxException -import java.util.concurrent.atomic.AtomicBoolean import javax.swing.* @@ -46,7 +45,6 @@ class InfoDialog(parent: Window?) : JDialog(parent) { private val hyperlink = SwingHyperlink() private val descScrollPane = JScrollPane() private val lblDescription = JTextArea() - private val isPacking = AtomicBoolean(false) internal class SwingDisabledCheckBox : JCheckBox() { @@ -54,6 +52,7 @@ class InfoDialog(parent: Window?) : JDialog(parent) { isEnabled = false } } + /** * Restore window position from config settings. */ @@ -70,13 +69,6 @@ class InfoDialog(parent: Window?) : JDialog(parent) { } } - override fun pack() { - isPacking.set(true) - super.pack() - restoreLocation() - isPacking.set(false) - } - /** * Save window position to config. */ @@ -118,8 +110,9 @@ class InfoDialog(parent: Window?) : JDialog(parent) { } companion object { - private val DEFAULT_SENDER_DIMENSION = Dimension(64,64) + private val DEFAULT_SENDER_DIMENSION = Dimension(64, 64) } + private fun updateTextFields() { if (currentFilm == null) { clearControls() @@ -167,7 +160,7 @@ class InfoDialog(parent: Window?) : JDialog(parent) { } internal class SwingHyperlink : JXHyperlink() { - private fun openUrl(url : String) { + private fun openUrl(url: String) { try { UrlHyperlinkAction.openURL(null, url) } catch (ex: URISyntaxException) { @@ -185,18 +178,17 @@ class InfoDialog(parent: Window?) : JDialog(parent) { if (d.isSupported(Desktop.Action.BROWSE)) { try { d.browse(URI(toolTipText)) - } - catch (ex: Exception) { + } catch (ex: Exception) { SwingErrorDialog.showExceptionMessage( MediathekGui.ui(), "Es trat ein Fehler beim Öffnen des Links auf.\nSollte dies häufiger auftreten kontaktieren Sie bitte das Entwicklerteam.", - ex) + ex + ) } } else { openUrl(toolTipText) } - } - else { + } else { openUrl(toolTipText) } @@ -207,7 +199,7 @@ class InfoDialog(parent: Window?) : JDialog(parent) { private fun buildLayout() { layout = MigLayout( - LC().insets("5").hideMode(3).debug(), + LC().insets("5").hideMode(3), // columns AC() .fill().gap() @@ -227,7 +219,8 @@ class InfoDialog(parent: Window?) : JDialog(parent) { .gap() .gap() .gap() - .gap()) + .gap() + ) add(JLabel("Sender:"), CC().cell(0, 0)) add(lblSender, CC().cell(1, 0)) @@ -267,12 +260,11 @@ class InfoDialog(parent: Window?) : JDialog(parent) { type = Type.UTILITY title = "Filminformation" isResizable = false - //hardcode size as linux hates pack() - //setSize(350, 520) -// minimumSize = Dimension(350,450) defaultCloseOperation = DISPOSE_ON_CLOSE + buildLayout() pack() + updateTextFields() restoreLocation() val wasVisible = config.getBoolean(ApplicationConfiguration.FilmInfoDialog.FILM_INFO_VISIBLE, false) @@ -293,8 +285,7 @@ class InfoDialog(parent: Window?) : JDialog(parent) { addComponentListener(object : ComponentAdapter() { override fun componentMoved(e: ComponentEvent) { if (isVisible) { - if (!isPacking.get()) - saveLocation() + saveLocation() } } }) From 0ba01ebed61b14ff82dd9c0479defc1f6eb8a2be Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 20 Jul 2022 13:38:34 +0200 Subject: [PATCH 210/422] Integrate Windows ARM64 profile --- pom.xml | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/pom.xml b/pom.xml index d972aa6485..7d7e0f214d 100755 --- a/pom.xml +++ b/pom.xml @@ -827,6 +827,58 @@ + + windows_arm64 + + + windows + aarch64 + + + + win + windows + + + + + org.apache.maven.plugins + maven-shade-plugin + ${maven-shade-plugin.version} + + + package + + shade + + + + + res/* + *:*:*:linux + *:*:*:mac + + + false + + + ${mainclass} + + true + + + + + + + + + + + + linux_64bit From 37496bcd2feec0b4f330cc4475448fdc99efbc4e Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 20 Jul 2022 13:41:23 +0200 Subject: [PATCH 211/422] - add remark to win ARM64 target --- pom.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pom.xml b/pom.xml index 7d7e0f214d..87e9cce53c 100755 --- a/pom.xml +++ b/pom.xml @@ -828,6 +828,9 @@ + windows_arm64 From e35cdb3845763af66b9193b607cbae2d6912c838 Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 20 Jul 2022 13:50:34 +0200 Subject: [PATCH 212/422] - use checkedPack --- .../gui/filmInformation/InfoDialog.kt | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt b/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt index f69612945e..559531634d 100644 --- a/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt +++ b/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt @@ -14,6 +14,7 @@ import net.miginfocom.layout.CC import net.miginfocom.layout.LC import net.miginfocom.swing.MigLayout import org.apache.commons.configuration2.sync.LockMode +import org.apache.commons.lang3.SystemUtils import org.jdesktop.swingx.JXHyperlink import java.awt.Desktop import java.awt.Dimension @@ -106,7 +107,9 @@ class InfoDialog(parent: Window?) : JDialog(parent) { hyperlink.toolTipText = "" hyperlink.isEnabled = false lblDescription.text = "" - SwingUtilities.invokeLater { pack() } + SwingUtilities.invokeLater { + checkedPack() + } } companion object { @@ -141,7 +144,7 @@ class InfoDialog(parent: Window?) : JDialog(parent) { lblDescription.text = desc SwingUtilities.invokeLater { descScrollPane.verticalScrollBar.value = 0 - pack() + checkedPack() } } } @@ -155,7 +158,6 @@ class InfoDialog(parent: Window?) : JDialog(parent) { currentFilm = film if (isVisible) { updateTextFields() - //pack() } } @@ -256,6 +258,15 @@ class InfoDialog(parent: Window?) : JDialog(parent) { add(descScrollPane, CC().cell(0, 13).spanX(2).growY().growX().height("90!")) } + /** + * Only pack dialog when we are NOT running linux... + */ + private fun checkedPack() { + //only pack only OS OTHER THAN Linux + if (!SystemUtils.IS_OS_LINUX) + pack() + } + init { type = Type.UTILITY title = "Filminformation" @@ -263,14 +274,19 @@ class InfoDialog(parent: Window?) : JDialog(parent) { defaultCloseOperation = DISPOSE_ON_CLOSE buildLayout() - pack() + checkedPack() + if (SystemUtils.IS_OS_LINUX) { + val linuxSize = Dimension(350, 550) + setSize(linuxSize.width, linuxSize.height) + minimumSize = linuxSize + preferredSize = linuxSize + maximumSize = linuxSize + } updateTextFields() restoreLocation() - val wasVisible = config.getBoolean(ApplicationConfiguration.FilmInfoDialog.FILM_INFO_VISIBLE, false) - if (wasVisible) { - isVisible = true - } + isVisible = config.getBoolean(ApplicationConfiguration.FilmInfoDialog.FILM_INFO_VISIBLE, false) + addWindowListener(object : WindowAdapter() { override fun windowOpened(e: WindowEvent) { config.setProperty(ApplicationConfiguration.FilmInfoDialog.FILM_INFO_VISIBLE, true) From bed8b51f3e8b1e91946917cfb9207d56ccff754b Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 20 Jul 2022 14:14:41 +0200 Subject: [PATCH 213/422] - reduce linux height --- src/main/java/mediathek/gui/filmInformation/InfoDialog.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt b/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt index 559531634d..872dff1ee2 100644 --- a/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt +++ b/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt @@ -276,7 +276,7 @@ class InfoDialog(parent: Window?) : JDialog(parent) { buildLayout() checkedPack() if (SystemUtils.IS_OS_LINUX) { - val linuxSize = Dimension(350, 550) + val linuxSize = Dimension(350, 450) setSize(linuxSize.width, linuxSize.height) minimumSize = linuxSize preferredSize = linuxSize From 3294692623a250caaade44af08575c3abb7bf2ac Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 20 Jul 2022 14:18:02 +0200 Subject: [PATCH 214/422] change doc --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95d8b08a9b..a7e4454dd9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ **14.0.0** - **TODO:** ConfigureExternalUpdaterAction fertig programmieren! +- **TODO:** check info dialog size on linux - User Interface wurde primär für neue macOS-Versionen überarbeitet. - Das Programm überprüft beim Start ob die JVM-Parameter korrekt sind und gibt ansonsten eine Warnung aus. Dies kann über die Paramter `-nj` oder `--no-jvm-param-checks` deaktiviert werden. - Das Programm prüft unter Linux ob der `sun.java2d.uiScale`-Parameter korrekt angegeben wurde. Im Fehlerfall wird ein Warndialog angezeigt. @@ -7,7 +8,6 @@ - *Globale Schriftgröße ändern...*-Befehl wurde unter Linux entfernt da es keine Auswirkungen mehr hat. - Im Tab "Filme" wurden *"Abo löschen"* und *"Abo ändern"* aufgrund der teilweise langen Ausführungszeiten und dem damit zusammenhängenden Einfrieren der Oberfläche entfernt. Bitte nutzen Sie wie dafür vorgesehen den "Abos verwalten" Dialog. - Im Tab "Downloads" wurde die Textanzeige zu laufenden Downloads entfernt. Die Informationen werden nun in der globalen Statuszeile der App bei Bedarf angezeigt. -- Der Filminformationsdialog wurde neu geschrieben. Unter Linux kann es vorkommen dass der Dialog bis an den oberen Bildschirmrand "wandert". Dies ist ein Fehler von Java. - **BUGFIX:** Updater-Konfigurationsmöglichkeit wird nur noch in den Einstellungen aktiviert dargestellt, wenn sie auch Auswirkungen auf den Update-Mechanismus hat. - **BUGFIX:** Filter-Fenster überlagert nicht mehr Tooltip-Anzeigen. - **BUGFIX:** Die Oberfläche gibt nun verbesserte Hinweise, wenn Abos verarbeitet werden (nur im "Abo verwalten" Dialog) From de2f6f15fff46d40984066f0c30f54cdcc877345 Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 20 Jul 2022 17:28:37 +0200 Subject: [PATCH 215/422] - make dialog resizable on linux as it sucks in automagically packing the dialog --- .../gui/filmInformation/InfoDialog.kt | 32 ++++++++++++++----- .../tool/ApplicationConfiguration.java | 8 +++-- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt b/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt index 872dff1ee2..e241f3ed02 100644 --- a/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt +++ b/src/main/java/mediathek/gui/filmInformation/InfoDialog.kt @@ -61,9 +61,16 @@ class InfoDialog(parent: Window?) : JDialog(parent) { config.lock(LockMode.READ) try { val newLocation = Point() - newLocation.x = config.getInt(ApplicationConfiguration.FilmInfoDialog.FILM_INFO_LOCATION_X) - newLocation.y = config.getInt(ApplicationConfiguration.FilmInfoDialog.FILM_INFO_LOCATION_Y) + newLocation.x = config.getInt(ApplicationConfiguration.FilmInfoDialog.X) + newLocation.y = config.getInt(ApplicationConfiguration.FilmInfoDialog.Y) location = newLocation + + if (SystemUtils.IS_OS_LINUX) { + val w: Int = config.getInt(ApplicationConfiguration.FilmInfoDialog.WIDTH) + val h: Int = config.getInt(ApplicationConfiguration.FilmInfoDialog.HEIGHT) + if (w > 50 && h > 50) + size = Dimension(w, h) + } } catch (ignored: NoSuchElementException) { } finally { config.unlock(LockMode.READ) @@ -79,8 +86,12 @@ class InfoDialog(parent: Window?) : JDialog(parent) { config.lock(LockMode.WRITE) try { val location = locationOnScreen - config.setProperty(ApplicationConfiguration.FilmInfoDialog.FILM_INFO_LOCATION_X, location.x) - config.setProperty(ApplicationConfiguration.FilmInfoDialog.FILM_INFO_LOCATION_Y, location.y) + config.setProperty(ApplicationConfiguration.FilmInfoDialog.X, location.x) + config.setProperty(ApplicationConfiguration.FilmInfoDialog.Y, location.y) + if (SystemUtils.IS_OS_LINUX) { + config.setProperty(ApplicationConfiguration.FilmInfoDialog.WIDTH, width) + config.setProperty(ApplicationConfiguration.FilmInfoDialog.HEIGHT, height) + } } finally { config.unlock(LockMode.WRITE) } @@ -270,7 +281,8 @@ class InfoDialog(parent: Window?) : JDialog(parent) { init { type = Type.UTILITY title = "Filminformation" - isResizable = false + if (!SystemUtils.IS_OS_LINUX) + isResizable = false defaultCloseOperation = DISPOSE_ON_CLOSE buildLayout() @@ -285,20 +297,24 @@ class InfoDialog(parent: Window?) : JDialog(parent) { updateTextFields() restoreLocation() - isVisible = config.getBoolean(ApplicationConfiguration.FilmInfoDialog.FILM_INFO_VISIBLE, false) + isVisible = config.getBoolean(ApplicationConfiguration.FilmInfoDialog.VISIBLE, false) addWindowListener(object : WindowAdapter() { override fun windowOpened(e: WindowEvent) { - config.setProperty(ApplicationConfiguration.FilmInfoDialog.FILM_INFO_VISIBLE, true) + config.setProperty(ApplicationConfiguration.FilmInfoDialog.VISIBLE, true) } override fun windowClosed(e: WindowEvent) { - config.setProperty(ApplicationConfiguration.FilmInfoDialog.FILM_INFO_VISIBLE, false) + config.setProperty(ApplicationConfiguration.FilmInfoDialog.VISIBLE, false) } }) //addFilmlistLoadListener(); addComponentListener(object : ComponentAdapter() { + override fun componentResized(e: ComponentEvent?) { + saveLocation() + } + override fun componentMoved(e: ComponentEvent) { if (isVisible) { saveLocation() diff --git a/src/main/java/mediathek/tool/ApplicationConfiguration.java b/src/main/java/mediathek/tool/ApplicationConfiguration.java index a77aa6afa7..28c01f9e88 100644 --- a/src/main/java/mediathek/tool/ApplicationConfiguration.java +++ b/src/main/java/mediathek/tool/ApplicationConfiguration.java @@ -218,9 +218,11 @@ public static class FilterDialog { } public static class FilmInfoDialog { - public static final String FILM_INFO_VISIBLE = "film.information.visible"; - public static final String FILM_INFO_LOCATION_X = "film.information.location.x"; - public static final String FILM_INFO_LOCATION_Y = "film.information.location.y"; + public static final String VISIBLE = "film.information.visible"; + public static final String X = "film.information.location.x"; + public static final String Y = "film.information.location.y"; + public static final String WIDTH = "film.information.location.width"; + public static final String HEIGHT = "film.information.location.height"; } public static class MemoryMonitorDialog { From 74f29e836970781f124d98347111ebef05fb7ef6 Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 20 Jul 2022 23:10:44 +0200 Subject: [PATCH 216/422] - disable transparent title bar on macOS - initialize environment properties before any GUI stuff is executed, otherwise AWT does not work as expected. --- src/main/java/mediathek/Main.java | 3 ++- src/main/java/mediathek/mac/MediathekGuiMac.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/mediathek/Main.java b/src/main/java/mediathek/Main.java index 4d36d8201a..15109707dd 100644 --- a/src/main/java/mediathek/Main.java +++ b/src/main/java/mediathek/Main.java @@ -392,8 +392,9 @@ public static void main(final String... args) { System.exit(1); } + setupEnvironmentProperties(); + EventQueue.invokeLater(() -> { - setupEnvironmentProperties(); CommandLine cmd = new CommandLine(Config.class); try { diff --git a/src/main/java/mediathek/mac/MediathekGuiMac.java b/src/main/java/mediathek/mac/MediathekGuiMac.java index f9089a0638..316c270fa0 100644 --- a/src/main/java/mediathek/mac/MediathekGuiMac.java +++ b/src/main/java/mediathek/mac/MediathekGuiMac.java @@ -131,7 +131,7 @@ private void setupUserInterfaceForOsx() { if (SystemInfo.isMacFullWindowContentSupported) { getRootPane().putClientProperty("apple.awt.fullWindowContent", true); - getRootPane().putClientProperty("apple.awt.transparentTitleBar", true); + //getRootPane().putClientProperty("apple.awt.transparentTitleBar", true); commonToolBar.add(Box.createHorizontalStrut(70),0); } From 53573891c544595e85112d128305b2ba9c5ee67b Mon Sep 17 00:00:00 2001 From: Christian F Date: Wed, 20 Jul 2022 23:12:34 +0200 Subject: [PATCH 217/422] - reenable transparent window otherwise toolbar is not shown... --- src/main/java/mediathek/mac/MediathekGuiMac.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/mediathek/mac/MediathekGuiMac.java b/src/main/java/mediathek/mac/MediathekGuiMac.java index 316c270fa0..f9089a0638 100644 --- a/src/main/java/mediathek/mac/MediathekGuiMac.java +++ b/src/main/java/mediathek/mac/MediathekGuiMac.java @@ -131,7 +131,7 @@ private void setupUserInterfaceForOsx() { if (SystemInfo.isMacFullWindowContentSupported) { getRootPane().putClientProperty("apple.awt.fullWindowContent", true); - //getRootPane().putClientProperty("apple.awt.transparentTitleBar", true); + getRootPane().putClientProperty("apple.awt.transparentTitleBar", true); commonToolBar.add(Box.createHorizontalStrut(70),0); } From c54744db6dc261a4cfbc4d921061285ab0a6cd3b Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 21 Jul 2022 10:04:20 +0200 Subject: [PATCH 218/422] - query download file size only on click in tab downloads - fix bug in table model where duplicates occured after reloadTable --- CHANGELOG.md | 4 +- .../java/mediathek/daten/DatenDownload.java | 12 +- .../java/mediathek/daten/ListeDownloads.java | 1 + .../gui/tabs/tab_downloads/GuiDownloads.java | 111 ++++++++++++------ 4 files changed, 86 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a7e4454dd9..8bdcb99c5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,5 @@ **14.0.0** - **TODO:** ConfigureExternalUpdaterAction fertig programmieren! -- **TODO:** check info dialog size on linux - User Interface wurde primär für neue macOS-Versionen überarbeitet. - Das Programm überprüft beim Start ob die JVM-Parameter korrekt sind und gibt ansonsten eine Warnung aus. Dies kann über die Paramter `-nj` oder `--no-jvm-param-checks` deaktiviert werden. - Das Programm prüft unter Linux ob der `sun.java2d.uiScale`-Parameter korrekt angegeben wurde. Im Fehlerfall wird ein Warndialog angezeigt. @@ -17,6 +16,7 @@ - **BUGFIX:** Sendericons werden nun vollständig und hochauflösend gerendert. - **BUGFIX:** Update-Möglichkeit innerhalb des Programms wird nun richtig ein- und ausgeblendet, je nach verwendetem Installationstyp. - **BUGFIX:** Potentieller Absturz beim Einlesen der Abohistorie wurde behoben. +- **BUGFIX:** Das Hinzufügen großer Mengen an Downloads blockiert nun nicht mehr das Programm. - **FEATURE:** Die Toolbars können nun zum Schweben in allen Tabs abgetrennt werden. - **FEATURE:** Tab Filmbeschreibung wurde neu designed und stellt nun auch das Sendericon dar. - **FEATURE:** Icons wurden durch hochauflösende Vektorvarianten ersetzt. @@ -25,7 +25,7 @@ - **FEATURE:** Ein manchmal unsichtbarer Filter-Dialog kann nun über das Menü *Hilfe/Filterdialog-Position zurücksetzen* auf den sichtbaren Monitorbereich zurückgesetzt werden. - **FEATURE:** Untertitelverfügbarkeit wird nun über ein `cc`-Icon an Anfang des Titels angezeigt. Die Spalte `UT` wird in einer späteren Programmversion entfernt werden. - **FEATURE:** Im Tab Download werden die Dialoge bzgl. Neustarts fertiger Downloads nun automatisch nach 10 Sekunden geschlossen wenn kein Userinput passiert. Es wird dann jeweils die "Nein"-Option ausgewählt um bereits vorhandene Downloads zu schützen. -- **FEATURE(Windows/Linux):** Schriftgröße kann für das gesamte Programm über das Menü "*Schrift*" geändert werden. +- **FEATURE(Windows/Linux):** Schriftgröße und -art kann für das gesamte Programm über das Menü "*Schrift*" geändert werden. **13.9.1** diff --git a/src/main/java/mediathek/daten/DatenDownload.java b/src/main/java/mediathek/daten/DatenDownload.java index 00cf5985b8..b5921d3862 100644 --- a/src/main/java/mediathek/daten/DatenDownload.java +++ b/src/main/java/mediathek/daten/DatenDownload.java @@ -362,12 +362,20 @@ public void setGroesse(@NotNull String groesse) { if (film != null) { if (!groesse.isEmpty()) { mVFilmSize.setSize(groesse); - } else { - mVFilmSize.setSize(film.getFileSizeForUrl(arr[DOWNLOAD_URL])); } } } + /** + * Request the file size live from Internet. + * Might cause delays when network is slow. + */ + public void queryLiveSize() { + if (film != null) { + mVFilmSize.setSize(film.getFileSizeForUrl(arr[DOWNLOAD_URL])); + } + } + public void init() { datumFilm = getDatumForObject(); try { diff --git a/src/main/java/mediathek/daten/ListeDownloads.java b/src/main/java/mediathek/daten/ListeDownloads.java index bb80ac4117..6aff44927a 100644 --- a/src/main/java/mediathek/daten/ListeDownloads.java +++ b/src/main/java/mediathek/daten/ListeDownloads.java @@ -227,6 +227,7 @@ public synchronized void getModel(TModelDownload tModel, boolean onlyAbos, boole boolean onlyNotStarted, boolean onlyStarted, boolean onlyWaiting, boolean onlyRun, boolean onlyFinished) { Object[] object; tModel.setRowCount(0); + tModel.getDataVector().clear(); for (DatenDownload download : this) { if (download.istZurueckgestellt()) { continue; diff --git a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java index 2bbaaa3f83..cbd5d3dfe5 100644 --- a/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java +++ b/src/main/java/mediathek/gui/tabs/tab_downloads/GuiDownloads.java @@ -151,6 +151,7 @@ public GuiDownloads(Daten aDaten, MediathekGui mediathekGui) { init(); setupFilmSelectionPropertyListener(); + setupDownloadSizeSelectionUpdater(); initTable(); @@ -199,6 +200,40 @@ public void actionPerformed(ActionEvent e) { } } + private void updateFilmSizes(int[] rows) { + boolean updateNeeded = false; + + for (var row : rows) { + var indexRow = tabelle.convertRowIndexToModel(row); + var listeDownloads = daten.getListeDownloads(); + var dlInfo = listeDownloads.get(indexRow); + if (dlInfo != null) { + if (dlInfo.mVFilmSize.getSize() != 0) + continue; + + if (dlInfo.film != null) { + var oldSize = dlInfo.mVFilmSize.getSize(); + dlInfo.queryLiveSize(); + if (dlInfo.mVFilmSize.getSize() != oldSize) + updateNeeded = true; + } + } else + logger.error("Could not get download object"); + } + + if (updateNeeded) + reloadTable(); + } + + private void setupDownloadSizeSelectionUpdater() { + tabelle.getSelectionModel().addListSelectionListener(l -> { + if (!l.getValueIsAdjusting()) { + var rows = tabelle.getSelectedRows(); + updateFilmSizes(rows); + } + }); + } + /** * Update the property with the current number of selected entries from the JTable. */ @@ -255,7 +290,7 @@ private void setupFilterPanel() { jSplitPane1.setDividerLocation(location); jSplitPane1.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, pce -> { if (jPanelFilterExtern.isVisible()) { - config.setProperty(ApplicationConfiguration.APPLICATION_UI_DOWNLOAD_TAB_DIVIDER_LOCATION,jSplitPane1.getDividerLocation()); + config.setProperty(ApplicationConfiguration.APPLICATION_UI_DOWNLOAD_TAB_DIVIDER_LOCATION, jSplitPane1.getDividerLocation()); } }); } @@ -263,7 +298,7 @@ private void setupFilterPanel() { @Handler private void handleParallelDownloadNumberChange(ParallelDownloadNumberChangedEvent e) { SwingUtilities.invokeLater(() -> { - final int maxNumDownloads = ApplicationConfiguration.getConfiguration().getInt(ApplicationConfiguration.DOWNLOAD_MAX_SIMULTANEOUS_NUM,1); + final int maxNumDownloads = ApplicationConfiguration.getConfiguration().getInt(ApplicationConfiguration.DOWNLOAD_MAX_SIMULTANEOUS_NUM, 1); jSpinnerAnzahlDownloads.setValue(maxNumDownloads); }); } @@ -442,9 +477,9 @@ private void init() { }); jSpinnerAnzahlDownloads.setModel(new SpinnerNumberModel(1, 1, 9, 1)); - jSpinnerAnzahlDownloads.setValue(config.getInt(ApplicationConfiguration.DOWNLOAD_MAX_SIMULTANEOUS_NUM,1)); + jSpinnerAnzahlDownloads.setValue(config.getInt(ApplicationConfiguration.DOWNLOAD_MAX_SIMULTANEOUS_NUM, 1)); jSpinnerAnzahlDownloads.addChangeListener(l -> { - final int maxNumDownloads = ((Number)jSpinnerAnzahlDownloads.getModel().getValue()).intValue(); + final int maxNumDownloads = ((Number) jSpinnerAnzahlDownloads.getModel().getValue()).intValue(); config.setProperty(ApplicationConfiguration.DOWNLOAD_MAX_SIMULTANEOUS_NUM, maxNumDownloads); MessageBus.getMessageBus().publishAsync(new ParallelDownloadNumberChangedEvent()); }); @@ -772,8 +807,7 @@ public void downloadLoeschen(boolean permanentDeletion) { } } - private @NotNull List addAllDownloadsToList() - { + private @NotNull List addAllDownloadsToList() { final var rowCount = tabelle.getRowCount(); final var tableModel = tabelle.getModel(); List destList = new ArrayList<>(); @@ -824,7 +858,7 @@ public void startAllDownloadsAtSpecificTime() { // wenn er schon fertig ist, erst mal fragen vor dem erneuten Starten int reply = GuiFunktionen.createDismissableMessageDialog(mediathekGui, "Fertiger Download", "Film nochmal starten? ==> " + download.arr[DatenDownload.DOWNLOAD_TITEL], - JOptionPane.YES_NO_OPTION,JOptionPane.NO_OPTION, 10, TimeUnit.SECONDS, + JOptionPane.YES_NO_OPTION, JOptionPane.NO_OPTION, 10, TimeUnit.SECONDS, JOptionPane.QUESTION_MESSAGE); if (reply != JOptionPane.YES_OPTION) { // weiter mit der nächsten URL @@ -912,7 +946,7 @@ private void filmStartenWiederholenStoppen(boolean processAllDownloads, boolean } antwort = GuiFunktionen.createDismissableMessageDialog(mediathekGui, "Fertiger Download", text, - JOptionPane.YES_NO_CANCEL_OPTION,JOptionPane.NO_OPTION, 10, TimeUnit.SECONDS, + JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.NO_OPTION, 10, TimeUnit.SECONDS, JOptionPane.QUESTION_MESSAGE); } if (antwort == JOptionPane.CANCEL_OPTION) { @@ -1036,30 +1070,30 @@ private void initComponents() { { jPanelFilterExtern.setPreferredSize(new Dimension(200, 644)); jPanelFilterExtern.setLayout(new MigLayout( - new LC().insets("0").hideMode(3).gridGap("0", "0"), //NON-NLS - // columns - new AC() - .grow().fill(), - // rows - new AC() - .gap() - .fill().gap() - .grow().fill())); + new LC().insets("0").hideMode(3).gridGap("0", "0"), //NON-NLS + // columns + new AC() + .grow().fill(), + // rows + new AC() + .gap() + .fill().gap() + .grow().fill())); //======== panel3 ======== { panel3.setBorder(new TitledBorder("Anzeige")); //NON-NLS panel3.setLayout(new MigLayout( - new LC().insets("5").hideMode(3).gridGap("5", "5"), //NON-NLS - // columns - new AC() - .fill().gap() - .grow().fill(), - // rows - new AC() - .fill().gap() - .fill().gap() - .fill())); + new LC().insets("5").hideMode(3).gridGap("5", "5"), //NON-NLS + // columns + new AC() + .fill().gap() + .grow().fill(), + // rows + new AC() + .fill().gap() + .fill().gap() + .fill())); //---- label1 ---- label1.setText("Typ:"); //NON-NLS @@ -1082,15 +1116,15 @@ private void initComponents() { { panel2.setBorder(new TitledBorder("Downloads")); //NON-NLS panel2.setLayout(new MigLayout( - new LC().insets("5").hideMode(3).gridGap("5", "5"), //NON-NLS - // columns - new AC() - .fill().gap() - .fill(), - // rows - new AC() - .gap() - .fill())); + new LC().insets("5").hideMode(3).gridGap("5", "5"), //NON-NLS + // columns + new AC() + .fill().gap() + .fill(), + // rows + new AC() + .gap() + .fill())); //---- jLabel3 ---- jLabel3.setText("gleichzeitig:"); //NON-NLS @@ -1243,6 +1277,7 @@ private void buttonTable(int row, int column) { } } } + private void showMenu(MouseEvent evt) { p = evt.getPoint(); final int nr = tabelle.rowAtPoint(p); @@ -1391,7 +1426,7 @@ private final class ViewCategoryListener implements ActionListener { public void actionPerformed(ActionEvent e) { JComboBox source = (JComboBox) e.getSource(); - switch ((String)source.getModel().getSelectedItem()) { + switch ((String) source.getModel().getSelectedItem()) { case COMBO_VIEW_ALL -> { onlyNotStarted = false; onlyStarted = false; @@ -1447,7 +1482,7 @@ private final class DisplayCategoryListener implements ActionListener { @Override public void actionPerformed(ActionEvent e) { JComboBox source = (JComboBox) e.getSource(); - switch ((String)source.getModel().getSelectedItem()) { + switch ((String) source.getModel().getSelectedItem()) { case COMBO_DISPLAY_ALL -> { onlyAbos = false; onlyDownloads = false; From 43118803b1c5e0b9c80ed943a81659dfb25e265f Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 21 Jul 2022 10:15:57 +0200 Subject: [PATCH 219/422] - make scrollbars 16 pixels instead of 10 for win and linux users --- src/main/java/mediathek/Main.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/mediathek/Main.java b/src/main/java/mediathek/Main.java index 15109707dd..2f55e4d87a 100644 --- a/src/main/java/mediathek/Main.java +++ b/src/main/java/mediathek/Main.java @@ -273,6 +273,9 @@ private static void setupFlatLaf() { FlatLightLaf.setup(); //FlatDarkLaf.setup(); + // win and linux users complain about scrollbars being too small... + if (!SystemUtils.IS_OS_MAC_OSX) + UIManager.put( "ScrollBar.width", 16 ); UIManager.put("TabbedPane.showTabSeparators", true); // install alternate row color only for windows >8 and macOS, Linux boolean installAlternateRowColor; From dbee1fe7229fa3c9571fc0a36ec24b9a26162a33 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 21 Jul 2022 12:32:57 +0200 Subject: [PATCH 220/422] - fixed bug where film size of manual downloads wasn't properly stored and retrieved from config file --- CHANGELOG.md | 1 + .../java/mediathek/controller/IoXmlLesen.java | 2 +- .../java/mediathek/daten/DatenDownload.java | 135 +++++++++++------- 3 files changed, 84 insertions(+), 54 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bdcb99c5d..1801bd55b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - **BUGFIX:** Update-Möglichkeit innerhalb des Programms wird nun richtig ein- und ausgeblendet, je nach verwendetem Installationstyp. - **BUGFIX:** Potentieller Absturz beim Einlesen der Abohistorie wurde behoben. - **BUGFIX:** Das Hinzufügen großer Mengen an Downloads blockiert nun nicht mehr das Programm. +- **BUGFIX:** Die Dateigrößen von manuellen Downloads werden nun - sofern vorhanden - richtig verarbeitet. - **FEATURE:** Die Toolbars können nun zum Schweben in allen Tabs abgetrennt werden. - **FEATURE:** Tab Filmbeschreibung wurde neu designed und stellt nun auch das Sendericon dar. - **FEATURE:** Icons wurden durch hochauflösende Vektorvarianten ersetzt. diff --git a/src/main/java/mediathek/controller/IoXmlLesen.java b/src/main/java/mediathek/controller/IoXmlLesen.java index 316c5baa71..da62ebf8f5 100644 --- a/src/main/java/mediathek/controller/IoXmlLesen.java +++ b/src/main/java/mediathek/controller/IoXmlLesen.java @@ -133,7 +133,7 @@ private void readBlacklistRuleEntry(XMLStreamReader parser) { private void readDownloadEntry(XMLStreamReader parser) { try { - var dl = DatenDownload.getFromConfig(parser); + var dl = DatenDownload.readFromConfig(parser); // abo entries will be generated...but we need this for CLI so far if (!dl.isFromAbo()) daten.getListeDownloads().add(dl); diff --git a/src/main/java/mediathek/daten/DatenDownload.java b/src/main/java/mediathek/daten/DatenDownload.java index b5921d3862..9f2322a00a 100644 --- a/src/main/java/mediathek/daten/DatenDownload.java +++ b/src/main/java/mediathek/daten/DatenDownload.java @@ -152,11 +152,12 @@ public DatenDownload(DatenPset pSet, DatenFilm film, byte quelle, DatenAbo abo, /** * Read the download data from config. + * * @param parser The parser for the config file * @return A valid DatenDownload object when everything went smooth. * @throws XMLStreamException when something went wrong */ - public static DatenDownload getFromConfig(XMLStreamReader parser) throws XMLStreamException { + public static DatenDownload readFromConfig(@NotNull XMLStreamReader parser) throws XMLStreamException { DatenDownload dl = new DatenDownload(); final int maxElem = dl.arr.length; @@ -197,24 +198,14 @@ public static boolean anzeigen(int i) { } } - public void startDownload() { - // Start erstellen und zur Liste hinzufügen - this.start = new Start(); - - try (var historyController = new SeenHistoryController()){ - historyController.markSeen(film); - } - - MessageBus.getMessageBus().publishAsync(new StartEvent()); - } - /** * Starts all downloads from list but fire only one update event. + * * @param downloads the list of downloads */ public static void startenDownloads(ArrayList downloads) { // Start erstellen und zur Liste hinzufügen - try (var historyController = new SeenHistoryController()){ + try (var historyController = new SeenHistoryController()) { for (DatenDownload d : downloads) { d.start = new Start(); historyController.markSeen(d.film); @@ -239,15 +230,44 @@ public static String getTextBandbreite(long b) { } } - private enum DMYTag { - DAY, - MONTH, - YEAR + private static String datumDrehen(String datum) { + String ret = ""; + if (!datum.isEmpty()) { + try { + if (datum.length() == 10) { + String tmp = datum.substring(6); // Jahr + tmp += '.' + datum.substring(3, 5); // Monat + tmp += '.' + datum.substring(0, 2); // Tag + ret = tmp; + } + } catch (Exception ex) { + logger.error("Datum: {}", datum, ex); + } + } + return ret; + } + + private static String datumDatumZeitReinigen(String datum) { + String ret = StringUtils.replace(datum, ":", ""); + ret = StringUtils.replace(ret, ".", ""); + return ret; + } + + public void startDownload() { + // Start erstellen und zur Liste hinzufügen + this.start = new Start(); + + try (var historyController = new SeenHistoryController()) { + historyController.markSeen(film); + } + + MessageBus.getMessageBus().publishAsync(new StartEvent()); } /** * Return a specific string based on the specified tag. - * @param tag Specifies what to return from the date string (xx.xx.xxxx) + * + * @param tag Specifies what to return from the date string (xx.xx.xxxx) * @param datum a date string * @return Return the string part specified by tag */ @@ -269,15 +289,10 @@ private String getDMY(DMYTag tag, String datum) { return ret; } - private enum HMSTag { - HOUR, - MINUTE, - SECOND - } - /** * Return a specific string based on the specified tag. - * @param tag Specifies what to return from the time string ("HH:mm:ss") + * + * @param tag Specifies what to return from the time string ("HH:mm:ss") * @param zeit a time string * @return Return the string part specified by tag */ @@ -299,33 +314,18 @@ private String getHMS(HMSTag tag, String zeit) { return ret; } - private static String datumDrehen(String datum) { - String ret = ""; - if (!datum.isEmpty()) { - try { - if (datum.length() == 10) { - String tmp = datum.substring(6); // Jahr - tmp += '.' + datum.substring(3, 5); // Monat - tmp += '.' + datum.substring(0, 2); // Tag - ret = tmp; - } - } catch (Exception ex) { - logger.error("Datum: {}", datum, ex); - } - } - return ret; - } - - private static String datumDatumZeitReinigen(String datum) { - String ret = StringUtils.replace(datum, ":", ""); - ret = StringUtils.replace(ret, ".", ""); - return ret; + private void writeEntry(@NotNull XMLStreamWriter writer, int index, @NotNull String data) throws XMLStreamException { + writer.writeCharacters("\t"); //Tab + writer.writeStartElement(XML_NAMES[index]); + writer.writeCharacters(data); + writer.writeEndElement(); + writer.writeCharacters("\n"); } /** * Store the download data in config file. * - * @param writer the writer to the config file. + * @param writer the writer to the config file. */ public void writeConfigEntry(XMLStreamWriter writer) { final int xmlMax = arr.length; @@ -333,12 +333,17 @@ public void writeConfigEntry(XMLStreamWriter writer) { writer.writeStartElement(TAG); writer.writeCharacters("\n"); for (int i = 0; i < xmlMax; ++i) { - if (!arr[i].isEmpty()) { - writer.writeCharacters("\t"); //Tab - writer.writeStartElement(XML_NAMES[i]); - writer.writeCharacters(arr[i]); - writer.writeEndElement(); - writer.writeCharacters("\n"); + switch (i) { + case DatenDownload.DOWNLOAD_GROESSE -> { + var size = mVFilmSize.getSize(); + size /= FileSize.ONE_MiB; + writeEntry(writer, DatenDownload.DOWNLOAD_GROESSE, Long.toString(size)); + } + default -> { + if (!arr[i].isEmpty()) { + writeEntry(writer, i, arr[i]); + } + } } } writer.writeEndElement(); @@ -392,6 +397,18 @@ public void init() { arr[DOWNLOAD_HD] = "0"; arr[DOWNLOAD_UT] = "0"; } + + /* + * reader reads only into arr but does not properly fill the mVFilmSize... + * do it manually + */ + if (!arr[DatenDownload.DOWNLOAD_GROESSE].isEmpty()) { + try { + var size = Long.parseLong(arr[DatenDownload.DOWNLOAD_GROESSE]); + mVFilmSize.setSize(size * FileSize.ONE_MiB); + } catch (NumberFormatException ignored) { + } + } } public boolean istZurueckgestellt() { @@ -893,4 +910,16 @@ public int compareTo(DatenDownload arg0) { } return ret; } + + private enum DMYTag { + DAY, + MONTH, + YEAR + } + + private enum HMSTag { + HOUR, + MINUTE, + SECOND + } } From a2cff2ed0325f9257e6f647c34ed1cb1bfbdf73b Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 21 Jul 2022 16:04:31 +0200 Subject: [PATCH 221/422] - implement more icons (GEO, HQ, UT) in title label in order to get rid of table columns --- CHANGELOG.md | 8 +- .../java/mediathek/tool/CompoundIcon.java | 289 ++++++++++++++++++ .../tool/cellrenderer/CellRendererFilme.java | 58 +++- .../icons/derreisende77/high-quality.svg | 11 + 4 files changed, 356 insertions(+), 10 deletions(-) create mode 100644 src/main/java/mediathek/tool/CompoundIcon.java create mode 100644 src/main/resources/icons/derreisende77/high-quality.svg diff --git a/CHANGELOG.md b/CHANGELOG.md index 1801bd55b7..3a8155e51c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - *Globale Schriftgröße ändern...*-Befehl wurde unter Linux entfernt da es keine Auswirkungen mehr hat. - Im Tab "Filme" wurden *"Abo löschen"* und *"Abo ändern"* aufgrund der teilweise langen Ausführungszeiten und dem damit zusammenhängenden Einfrieren der Oberfläche entfernt. Bitte nutzen Sie wie dafür vorgesehen den "Abos verwalten" Dialog. - Im Tab "Downloads" wurde die Textanzeige zu laufenden Downloads entfernt. Die Informationen werden nun in der globalen Statuszeile der App bei Bedarf angezeigt. +- Die Spalten `HQ`, `UT` und `Geo` sind aufgrund der neuen Icons grundsätzlich überflüssig. - **BUGFIX:** Updater-Konfigurationsmöglichkeit wird nur noch in den Einstellungen aktiviert dargestellt, wenn sie auch Auswirkungen auf den Update-Mechanismus hat. - **BUGFIX:** Filter-Fenster überlagert nicht mehr Tooltip-Anzeigen. - **BUGFIX:** Die Oberfläche gibt nun verbesserte Hinweise, wenn Abos verarbeitet werden (nur im "Abo verwalten" Dialog) @@ -22,11 +23,14 @@ - **FEATURE:** Tab Filmbeschreibung wurde neu designed und stellt nun auch das Sendericon dar. - **FEATURE:** Icons wurden durch hochauflösende Vektorvarianten ersetzt. - **FEATURE:** Neues Suchfeld inklusive Suchhistorie. -- **FEATURE:** Geoinformationen werden nun als *gesperrt/entsperrt* Icons dargestellt. Detailinformationen werden über Tooltip-Text eingeblendet. -- **FEATURE:** Ein manchmal unsichtbarer Filter-Dialog kann nun über das Menü *Hilfe/Filterdialog-Position zurücksetzen* auf den sichtbaren Monitorbereich zurückgesetzt werden. +- **FEATURE:** Geoinformationen in der Spalte `Geo` im Tab "Filme" werden nun als *gesperrt/entsperrt* Icons dargestellt. Detailinformationen werden über Tooltip-Text eingeblendet. - **FEATURE:** Untertitelverfügbarkeit wird nun über ein `cc`-Icon an Anfang des Titels angezeigt. Die Spalte `UT` wird in einer späteren Programmversion entfernt werden. +- **FEATURE:** Inhalte in HQ werden mit einem `HQ` Icon im Titel im Tab "Filme" dargestellt. +- **FEATURE:** Geogesperrte Inhalte werden mit einem Schloß versehen. +- **FEATURE:** Ein manchmal unsichtbarer Filter-Dialog kann nun über das Menü *Hilfe/Filterdialog-Position zurücksetzen* auf den sichtbaren Monitorbereich zurückgesetzt werden. - **FEATURE:** Im Tab Download werden die Dialoge bzgl. Neustarts fertiger Downloads nun automatisch nach 10 Sekunden geschlossen wenn kein Userinput passiert. Es wird dann jeweils die "Nein"-Option ausgewählt um bereits vorhandene Downloads zu schützen. - **FEATURE(Windows/Linux):** Schriftgröße und -art kann für das gesamte Programm über das Menü "*Schrift*" geändert werden. +- **FEATURE(Windows/Linux):** Die Breite der Scrollbars wurde etwas erhöht um die Sichtbarkeit zu verbessern. **13.9.1** diff --git a/src/main/java/mediathek/tool/CompoundIcon.java b/src/main/java/mediathek/tool/CompoundIcon.java new file mode 100644 index 0000000000..f7bcf58bbc --- /dev/null +++ b/src/main/java/mediathek/tool/CompoundIcon.java @@ -0,0 +1,289 @@ +package mediathek.tool; + +import javax.swing.*; +import java.awt.*; + +/** + * The CompoundIcon will paint two, or more, Icons as a single Icon. The + * Icons are painted in the order in which they are added. + * + * The Icons are layed out on the specified axis: + *
                                + *
                              • X-Axis (horizontally) + *
                              • Y-Axis (vertically) + *
                              • Z-Axis (stacked) + *
                              + * + */ +public class CompoundIcon implements Icon +{ + public enum Axis + { + X_AXIS, + Y_AXIS, + Z_AXIS; + } + + public final static float TOP = 0.0f; + public final static float LEFT = 0.0f; + public final static float CENTER = 0.5f; + public final static float BOTTOM = 1.0f; + public final static float RIGHT = 1.0f; + + private final Icon[] icons; + + private final Axis axis; + + private final int gap; + + private float alignmentX = CENTER; + private float alignmentY = CENTER; + + /** + * Convenience contructor for creating a CompoundIcon where the + * icons are layed out on on the X-AXIS, the gap is 0 and the + * X/Y alignments will default to CENTER. + * + * @param icons the Icons to be painted as part of the CompoundIcon + */ + public CompoundIcon(Icon... icons) + { + this(Axis.X_AXIS, icons); + } + + /** + * Convenience contructor for creating a CompoundIcon where the + * gap is 0 and the X/Y alignments will default to CENTER. + * + * @param axis the axis used to lay out the icons for painting. + * Must be one of the Axis enums: X_AXIS, Y_AXIS, Z_Axis. + * @param icons the Icons to be painted as part of the CompoundIcon + */ + public CompoundIcon(Axis axis, Icon... icons) + { + this(axis, 0, icons); + } + + /** + * Convenience contructor for creating a CompoundIcon where the + * X/Y alignments will default to CENTER. + * + * @param axis the axis used to lay out the icons for painting + * Must be one of the Axis enums: X_AXIS, Y_AXIS, Z_Axis. + * @param gap the gap between the icons + * @param icons the Icons to be painted as part of the CompoundIcon + */ + public CompoundIcon(Axis axis, int gap, Icon... icons) + { + this(axis, gap, CENTER, CENTER, icons); + } + + /** + * Create a CompoundIcon specifying all the properties. + * + * @param axis the axis used to lay out the icons for painting + * Must be one of the Axis enums: X_AXIS, Y_AXIS, Z_Axis. + * @param gap the gap between the icons + * @param alignmentX the X alignment of the icons. Common values are + * LEFT, CENTER, RIGHT. Can be any value between 0.0 and 1.0 + * @param alignmentY the Y alignment of the icons. Common values are + * TOP, CENTER, BOTTOM. Can be any value between 0.0 and 1.0 + * @param icons the Icons to be painted as part of the CompoundIcon + */ + public CompoundIcon(Axis axis, int gap, float alignmentX, float alignmentY, Icon... icons) + { + this.axis = axis; + this.gap = gap; + this.alignmentX = alignmentX > 1.0f ? 1.0f : Math.max(alignmentX, 0.0f); + this.alignmentY = alignmentY > 1.0f ? 1.0f : Math.max(alignmentY, 0.0f); + + for (int i = 0; i < icons.length; i++) + { + if (icons[i] == null) + { + String message = "Icon (" + i + ") cannot be null"; + throw new IllegalArgumentException( message ); + } + } + + this.icons = icons; + } + + /** + * Get the Axis along which each icon is painted. + * + * @return the Axis + */ + public Axis getAxis() + { + return axis; + } + + /** + * Get the gap between each icon + * + * @return the gap in pixels + */ + public int getGap() + { + return gap; + } + + /** + * Get the alignment of the icon on the x-axis + * + * @return the alignment + */ + public float getAlignmentX() + { + return alignmentX; + } + + /** + * Get the alignment of the icon on the y-axis + * + * @return the alignment + */ + public float getAlignmentY() + { + return alignmentY; + } + + /** + * Get the number of Icons contained in this CompoundIcon. + * + * @return the total number of Icons + */ + public int getIconCount() + { + return icons.length; + } + + /** + * Get the Icon at the specified index. + * + * @param index the index of the Icon to be returned + * @return the Icon at the specifed index + * @exception IndexOutOfBoundsException if the index is out of range + */ + public Icon getIcon(int index) + { + return icons[ index ]; + } +// +// Implement the Icon Interface +// + /** + * Gets the width of this icon. + * + * @return the width of the icon in pixels. + */ + @Override + public int getIconWidth() + { + int width = 0; + + // Add the width of all Icons while also including the gap + + if (axis == Axis.X_AXIS) + { + width += (icons.length - 1) * gap; + + for (Icon icon : icons) + width += icon.getIconWidth(); + } + else // Just find the maximum width + { + for (Icon icon : icons) + width = Math.max(width, icon.getIconWidth()); + } + + return width; + } + + /** + * Gets the height of this icon. + * + * @return the height of the icon in pixels. + */ + @Override + public int getIconHeight() + { + int height = 0; + + // Add the height of all Icons while also including the gap + + if (axis == Axis.Y_AXIS) + { + height += (icons.length - 1) * gap; + + for (Icon icon : icons) + height += icon.getIconHeight(); + } + else // Just find the maximum height + { + for (Icon icon : icons) + height = Math.max(height, icon.getIconHeight()); + } + + return height; + } + + /** + * Paint the icons of this compound icon at the specified location + * + * @param c The component on which the icon is painted + * @param g the graphics context + * @param x the X coordinate of the icon's top-left corner + * @param y the Y coordinate of the icon's top-left corner + */ + @Override + public void paintIcon(Component c, Graphics g, int x, int y) + { + if (axis == Axis.X_AXIS) + { + int height = getIconHeight(); + + for (Icon icon : icons) + { + int iconY = getOffset(height, icon.getIconHeight(), alignmentY); + icon.paintIcon(c, g, x, y + iconY); + x += icon.getIconWidth() + gap; + } + } + else if (axis == Axis.Y_AXIS) + { + int width = getIconWidth(); + + for (Icon icon : icons) + { + int iconX = getOffset(width, icon.getIconWidth(), alignmentX); + icon.paintIcon(c, g, x + iconX, y); + y += icon.getIconHeight() + gap; + } + } + else // must be Z_AXIS + { + int width = getIconWidth(); + int height = getIconHeight(); + + for (Icon icon : icons) + { + int iconX = getOffset(width, icon.getIconWidth(), alignmentX); + int iconY = getOffset(height, icon.getIconHeight(), alignmentY); + icon.paintIcon(c, g, x + iconX, y + iconY); + } + } + } + + /* + * When the icon value is smaller than the maximum value of all icons the + * icon needs to be aligned appropriately. Calculate the offset to be used + * when painting the icon to achieve the proper alignment. + */ + private int getOffset(int maxValue, int iconValue, float alignment) + { + float offset = (maxValue - iconValue) * alignment; + return Math.round(offset); + } +} \ No newline at end of file diff --git a/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java b/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java index 80cdb83b0a..b167a67ff8 100644 --- a/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java +++ b/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java @@ -7,6 +7,8 @@ import mediathek.controller.starter.Start; import mediathek.daten.DatenDownload; import mediathek.daten.DatenFilm; +import mediathek.tool.ApplicationConfiguration; +import mediathek.tool.CompoundIcon; import mediathek.tool.SVGIconUtilities; import mediathek.tool.table.MVTable; import org.apache.logging.log4j.LogManager; @@ -15,6 +17,8 @@ import javax.swing.*; import java.awt.*; +import java.util.ArrayList; +import java.util.List; public class CellRendererFilme extends CellRendererBaseWithStart { private static final Logger logger = LogManager.getLogger(CellRendererFilme.class); @@ -31,6 +35,14 @@ public class CellRendererFilme extends CellRendererBaseWithStart { private final FlatSVGIcon subtitleIcon; private final FlatSVGIcon subtitleIconSelected; + + private final FlatSVGIcon highQualityIcon; + private final FlatSVGIcon highQualityIconSelected; + /** + * Temporary storage for the icons that will be assembled to a compound icon. + */ + private final List iconList = new ArrayList<>(); + public CellRendererFilme() { selectedDownloadIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/download.svg"); selectedDownloadIcon.setColorFilter(whiteColorFilter); @@ -58,6 +70,10 @@ public CellRendererFilme() { subtitleIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/closed-captioning.svg"); subtitleIconSelected = SVGIconUtilities.createSVGIcon("icons/fontawesome/closed-captioning.svg"); subtitleIconSelected.setColorFilter(whiteColorFilter); + + highQualityIcon = SVGIconUtilities.createSVGIcon("icons/derreisende77/high-quality.svg"); + highQualityIconSelected = SVGIconUtilities.createSVGIcon("icons/derreisende77/high-quality.svg"); + highQualityIconSelected.setColorFilter(whiteColorFilter); } private JTextArea createTextArea(String content) { @@ -139,7 +155,7 @@ public Component getTableCellRendererComponent( if (columnWidth < table.getFontMetrics(table.getFont()).stringWidth(title)) setToolTipText(title); setText(title); - setSubtitleIcon(datenFilm, isSelected); + setIndicatorIcons(datenFilm, isSelected); break; case DatenFilm.FILM_GEO: @@ -156,19 +172,45 @@ public Component getTableCellRendererComponent( } /** - * Show "cc" icon when subtitle is available - * @param datenFilm film information + * Show "CC" and/or "HQ" icon(s) when supported by the film. + * + * @param datenFilm film information * @param isSelected is row selected. */ - private void setSubtitleIcon(@NotNull DatenFilm datenFilm, boolean isSelected) { + private void setIndicatorIcons(@NotNull DatenFilm datenFilm, boolean isSelected) { + datenFilm.getGeo().ifPresent(geoString -> { + if (!geoString.contains(config.getString(ApplicationConfiguration.GEO_LOCATION))) { + //locked + if (isSelected) + iconList.add(lockedIconSelected); + else + iconList.add(lockedIcon); + } + }); + + if (datenFilm.isHighQuality()) { + if (isSelected) + iconList.add(highQualityIconSelected); + else + iconList.add(highQualityIcon); + } + if (datenFilm.hasSubtitle()) { - Icon icon; if (isSelected) - icon = subtitleIconSelected; + iconList.add(subtitleIconSelected); else - icon = subtitleIcon; - setIcon(icon); + iconList.add(subtitleIcon); } + + Icon icon; + if (iconList.size() == 1) + icon = iconList.get(0); + else + icon = new CompoundIcon(CompoundIcon.Axis.X_AXIS, 3, iconList.toArray(new Icon[0])); + setIcon(icon); + + //always clear at the end + iconList.clear(); } /** diff --git a/src/main/resources/icons/derreisende77/high-quality.svg b/src/main/resources/icons/derreisende77/high-quality.svg new file mode 100644 index 0000000000..ddb7be0415 --- /dev/null +++ b/src/main/resources/icons/derreisende77/high-quality.svg @@ -0,0 +1,11 @@ + + + + + + + + + From d0873e97f850ee74620010d3619fc167054776b8 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 21 Jul 2022 20:01:06 +0200 Subject: [PATCH 222/422] - do not use custom color for geo blocked selected entries. It hurts the eyes and we have an icon for locked entries --- CHANGELOG.md | 1 + src/main/java/mediathek/config/MVColor.java | 2 -- .../tool/cellrenderer/CellRendererBaseWithStart.java | 7 ++----- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a8155e51c..d365084909 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ - **BUGFIX:** Potentieller Absturz beim Einlesen der Abohistorie wurde behoben. - **BUGFIX:** Das Hinzufügen großer Mengen an Downloads blockiert nun nicht mehr das Programm. - **BUGFIX:** Die Dateigrößen von manuellen Downloads werden nun - sofern vorhanden - richtig verarbeitet. +- **BUGFIX:** geo-geblockte Filmeinträge verwenden nun bei der Auswahl die Farben des Themes anstatt einer eigens definierten. - **FEATURE:** Die Toolbars können nun zum Schweben in allen Tabs abgetrennt werden. - **FEATURE:** Tab Filmbeschreibung wurde neu designed und stellt nun auch das Sendericon dar. - **FEATURE:** Icons wurden durch hochauflösende Vektorvarianten ersetzt. diff --git a/src/main/java/mediathek/config/MVColor.java b/src/main/java/mediathek/config/MVColor.java index b608db0a35..31d35f6daa 100644 --- a/src/main/java/mediathek/config/MVColor.java +++ b/src/main/java/mediathek/config/MVColor.java @@ -32,7 +32,6 @@ public class MVColor { public static final MVC FILM_NEU = new MVC(MVConfig.Configs.FARBE__FILM_NEU, new Color(0, 0, 240), "Filme, neue"); public static final MVC FILM_BOOKMARKED = new MVC(MVConfig.Configs.FARBE__FILM_BOOKMARKED, new Color(204, 238, 255), "Filme, gemerkt"); public static final MVC FILM_GEOBLOCK_BACKGROUND = new MVC(MVConfig.Configs.FARBE__FILM_GEOBLOCK_BACKGROUND, new Color(255, 254, 230), "Film, geogeblockt"); - public static final MVC FILM_GEOBLOCK_BACKGROUND_SEL = new MVC(MVConfig.Configs.FARBE__FILM_GEOBLOCK_BACKGROUND_SEL, new Color(255, 251, 179), "Film, geogeblockt, selektiert"); // Tabelle Downloads public static final MVC DOWNLOAD_IST_ABO = new MVC(MVConfig.Configs.FARBE__DOWNLOAD_IST_ABO, new Color(138, 67, 0), "Download ist ein Abo"); @@ -71,7 +70,6 @@ public MVColor() { liste.add(FILM_NEU); liste.add(FILM_BOOKMARKED); liste.add(FILM_GEOBLOCK_BACKGROUND); - liste.add(FILM_GEOBLOCK_BACKGROUND_SEL); liste.add(DOWNLOAD_IST_ABO); liste.add(DOWNLOAD_IST_DIREKTER_DOWNLOAD); liste.add(DOWNLOAD_ANSEHEN); diff --git a/src/main/java/mediathek/tool/cellrenderer/CellRendererBaseWithStart.java b/src/main/java/mediathek/tool/cellrenderer/CellRendererBaseWithStart.java index 95a8ba00e7..f9be76248c 100644 --- a/src/main/java/mediathek/tool/cellrenderer/CellRendererBaseWithStart.java +++ b/src/main/java/mediathek/tool/cellrenderer/CellRendererBaseWithStart.java @@ -119,13 +119,10 @@ protected void setBackgroundColor(final Component c, final Start s, final boolea } private void setGeoblockingBackgroundColor(final Component c, final boolean isSelected) { - final Color color; if (isSelected) - color = MVColor.FILM_GEOBLOCK_BACKGROUND_SEL.color; - else - color = MVColor.FILM_GEOBLOCK_BACKGROUND.color; + return; - c.setBackground(color); + c.setBackground(MVColor.FILM_GEOBLOCK_BACKGROUND.color); } protected void setupGeoblockingBackground(final Component c, final String geo, final boolean isSelected) { From 7ff8091f0bc2e43e60e9a573c5242f9b5f72c0d5 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 21 Jul 2022 21:57:10 +0200 Subject: [PATCH 223/422] - use icon for livestream indication instead of color --- CHANGELOG.md | 1 + src/main/java/mediathek/config/MVColor.java | 2 -- .../bookmark/BookmarkWindowController.java | 4 +--- .../tool/cellrenderer/CellRendererFilme.java | 18 ++++++++++++++---- .../resources/icons/fontawesome/tower-cell.svg | 1 + 5 files changed, 17 insertions(+), 9 deletions(-) create mode 100755 src/main/resources/icons/fontawesome/tower-cell.svg diff --git a/CHANGELOG.md b/CHANGELOG.md index d365084909..6a8fce06bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - **BUGFIX:** Das Hinzufügen großer Mengen an Downloads blockiert nun nicht mehr das Programm. - **BUGFIX:** Die Dateigrößen von manuellen Downloads werden nun - sofern vorhanden - richtig verarbeitet. - **BUGFIX:** geo-geblockte Filmeinträge verwenden nun bei der Auswahl die Farben des Themes anstatt einer eigens definierten. +- **FEATURE:** Livestreams werden mit einem Icon markiert anstatt einer dunklen Farbe. - **FEATURE:** Die Toolbars können nun zum Schweben in allen Tabs abgetrennt werden. - **FEATURE:** Tab Filmbeschreibung wurde neu designed und stellt nun auch das Sendericon dar. - **FEATURE:** Icons wurden durch hochauflösende Vektorvarianten ersetzt. diff --git a/src/main/java/mediathek/config/MVColor.java b/src/main/java/mediathek/config/MVColor.java index 31d35f6daa..0722e6323f 100644 --- a/src/main/java/mediathek/config/MVColor.java +++ b/src/main/java/mediathek/config/MVColor.java @@ -27,7 +27,6 @@ public class MVColor { // Tabelle Filme - public static final MVC FILM_LIVESTREAM = new MVC(MVConfig.Configs.FARBE__FILM_LIVESTREAM, new Color(130, 0, 0), "Filme, Livestreams"); public static final MVC FILM_HISTORY = new MVC(MVConfig.Configs.FARBE__FILM_HISTORY, new Color(225, 225, 225), "Filme, gesehen"); public static final MVC FILM_NEU = new MVC(MVConfig.Configs.FARBE__FILM_NEU, new Color(0, 0, 240), "Filme, neue"); public static final MVC FILM_BOOKMARKED = new MVC(MVConfig.Configs.FARBE__FILM_BOOKMARKED, new Color(204, 238, 255), "Filme, gemerkt"); @@ -65,7 +64,6 @@ public class MVColor { public static final int MVC_MAX = 2; public MVColor() { - liste.add(FILM_LIVESTREAM); liste.add(FILM_HISTORY); liste.add(FILM_NEU); liste.add(FILM_BOOKMARKED); diff --git a/src/main/java/mediathek/javafx/bookmark/BookmarkWindowController.java b/src/main/java/mediathek/javafx/bookmark/BookmarkWindowController.java index 0545a7e216..df13679fea 100644 --- a/src/main/java/mediathek/javafx/bookmark/BookmarkWindowController.java +++ b/src/main/java/mediathek/javafx/bookmark/BookmarkWindowController.java @@ -82,7 +82,6 @@ public class BookmarkWindowController implements Initializable { private final BookmarkDataList listeBookmarkList; private FilteredList filteredBookmarkList; private Color ColorExpired; - private Color ColorLive; private Background BackgroundSeen; private Background BackgroundSelected; private final SeenHistoryController history = new SeenHistoryController(); @@ -321,7 +320,7 @@ protected void updateItem(BookmarkData data, boolean empty) { } else { setBackground(isSelected() ? BackgroundSelected : data.getSeen() ? BackgroundSeen : Background.EMPTY); // set foreground color: - Color fillcolor = isSelected() ? Color.WHITE : data.isNotInFilmList() ? ColorExpired : data.isLiveStream() ? ColorLive : null; + Color fillcolor = isSelected() ? Color.WHITE : data.isNotInFilmList() ? ColorExpired : null; if (fillcolor != null) { this.getChildren().forEach((n) -> ((Labeled) n).setTextFill(fillcolor)); } @@ -844,7 +843,6 @@ private void initSettings() { Color colorSeen = convertMVCAWTColor(FILM_HISTORY); Color colorNew = convertMVCAWTColor(FILM_NEU); ColorExpired = convertMVCAWTColor(DOWNLOAD_FEHLER); - ColorLive = convertMVCAWTColor(FILM_LIVESTREAM); BackgroundSeen = new Background(new BackgroundFill(colorSeen, CornerRadii.EMPTY, Insets.EMPTY)); BackgroundSelected = new Background(new BackgroundFill(colorNew, CornerRadii.EMPTY, Insets.EMPTY)); } diff --git a/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java b/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java index b167a67ff8..220567fde6 100644 --- a/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java +++ b/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java @@ -38,6 +38,8 @@ public class CellRendererFilme extends CellRendererBaseWithStart { private final FlatSVGIcon highQualityIcon; private final FlatSVGIcon highQualityIconSelected; + private final FlatSVGIcon liveStreamIcon; + private final FlatSVGIcon liveStreamIconSelected; /** * Temporary storage for the icons that will be assembled to a compound icon. */ @@ -74,6 +76,10 @@ public CellRendererFilme() { highQualityIcon = SVGIconUtilities.createSVGIcon("icons/derreisende77/high-quality.svg"); highQualityIconSelected = SVGIconUtilities.createSVGIcon("icons/derreisende77/high-quality.svg"); highQualityIconSelected.setColorFilter(whiteColorFilter); + + liveStreamIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/tower-cell.svg"); + liveStreamIconSelected = SVGIconUtilities.createSVGIcon("icons/fontawesome/tower-cell.svg"); + liveStreamIconSelected.setColorFilter(whiteColorFilter); } private JTextArea createTextArea(String content) { @@ -202,6 +208,13 @@ private void setIndicatorIcons(@NotNull DatenFilm datenFilm, boolean isSelected) iconList.add(subtitleIcon); } + if (datenFilm.isLivestream()) { + if (isSelected) + iconList.add(liveStreamIconSelected); + else + iconList.add(liveStreamIcon); + } + Icon icon; if (iconList.size() == 1) icon = iconList.get(0); @@ -236,10 +249,7 @@ private void applyColorSettings(Component c, @NotNull DatenFilm datenFilm, Daten setBackgroundColor(c, datenDownload.start, isSelected); } else { //not a start, set specific background colors - if (datenFilm.isLivestream()) { - // bei livestreams keine History anzeigen - c.setForeground(MVColor.FILM_LIVESTREAM.color); - } else if (hasBeenSeen) { + if (hasBeenSeen) { if (!isSelected) { c.setBackground(MVColor.FILM_HISTORY.color); } diff --git a/src/main/resources/icons/fontawesome/tower-cell.svg b/src/main/resources/icons/fontawesome/tower-cell.svg new file mode 100755 index 0000000000..c3f754bd6f --- /dev/null +++ b/src/main/resources/icons/fontawesome/tower-cell.svg @@ -0,0 +1 @@ + \ No newline at end of file From dab43465cd254c12874758acaf14eed049b87623 Mon Sep 17 00:00:00 2001 From: Christian F Date: Thu, 21 Jul 2022 22:37:03 +0200 Subject: [PATCH 224/422] - remove use of geoblocking background color --- src/main/java/mediathek/config/MVColor.java | 2 - .../CellRendererBaseWithStart.java | 89 ++++++++++++++----- .../cellrenderer/CellRendererDownloads.java | 12 +-- .../tool/cellrenderer/CellRendererFilme.java | 84 ----------------- 4 files changed, 71 insertions(+), 116 deletions(-) diff --git a/src/main/java/mediathek/config/MVColor.java b/src/main/java/mediathek/config/MVColor.java index 0722e6323f..c51f026a27 100644 --- a/src/main/java/mediathek/config/MVColor.java +++ b/src/main/java/mediathek/config/MVColor.java @@ -30,7 +30,6 @@ public class MVColor { public static final MVC FILM_HISTORY = new MVC(MVConfig.Configs.FARBE__FILM_HISTORY, new Color(225, 225, 225), "Filme, gesehen"); public static final MVC FILM_NEU = new MVC(MVConfig.Configs.FARBE__FILM_NEU, new Color(0, 0, 240), "Filme, neue"); public static final MVC FILM_BOOKMARKED = new MVC(MVConfig.Configs.FARBE__FILM_BOOKMARKED, new Color(204, 238, 255), "Filme, gemerkt"); - public static final MVC FILM_GEOBLOCK_BACKGROUND = new MVC(MVConfig.Configs.FARBE__FILM_GEOBLOCK_BACKGROUND, new Color(255, 254, 230), "Film, geogeblockt"); // Tabelle Downloads public static final MVC DOWNLOAD_IST_ABO = new MVC(MVConfig.Configs.FARBE__DOWNLOAD_IST_ABO, new Color(138, 67, 0), "Download ist ein Abo"); @@ -67,7 +66,6 @@ public MVColor() { liste.add(FILM_HISTORY); liste.add(FILM_NEU); liste.add(FILM_BOOKMARKED); - liste.add(FILM_GEOBLOCK_BACKGROUND); liste.add(DOWNLOAD_IST_ABO); liste.add(DOWNLOAD_IST_DIREKTER_DOWNLOAD); liste.add(DOWNLOAD_ANSEHEN); diff --git a/src/main/java/mediathek/tool/cellrenderer/CellRendererBaseWithStart.java b/src/main/java/mediathek/tool/cellrenderer/CellRendererBaseWithStart.java index f9be76248c..6e8c739640 100644 --- a/src/main/java/mediathek/tool/cellrenderer/CellRendererBaseWithStart.java +++ b/src/main/java/mediathek/tool/cellrenderer/CellRendererBaseWithStart.java @@ -4,16 +4,17 @@ import mediathek.config.MVColor; import mediathek.controller.starter.Start; import mediathek.daten.DatenFilm; -import mediathek.gui.messages.GeoStateChangedEvent; import mediathek.tool.ApplicationConfiguration; +import mediathek.tool.CompoundIcon; import mediathek.tool.MessageBus; import mediathek.tool.SVGIconUtilities; -import net.engio.mbassy.listener.Handler; import org.apache.commons.configuration2.Configuration; import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.*; +import java.util.ArrayList; +import java.util.List; /** * CellRenderer base class for all custom renderer associated with a Start. @@ -24,22 +25,40 @@ public class CellRendererBaseWithStart extends CellRendererBase { protected final FlatSVGIcon lockedIconSelected; protected final FlatSVGIcon unlockedIcon; protected final FlatSVGIcon unlockedIconSelected; - protected boolean geoMelden; + /** + * Temporary storage for the icons that will be assembled to a compound icon. + */ + private final List iconList = new ArrayList<>(); + private final FlatSVGIcon subtitleIcon; + private final FlatSVGIcon subtitleIconSelected; + private final FlatSVGIcon highQualityIcon; + private final FlatSVGIcon highQualityIconSelected; + private final FlatSVGIcon liveStreamIcon; + private final FlatSVGIcon liveStreamIconSelected; protected FlatSVGIcon.ColorFilter whiteColorFilter = new FlatSVGIcon.ColorFilter(color -> Color.WHITE); public CellRendererBaseWithStart() { MessageBus.getMessageBus().subscribe(this); - geoMelden = config.getBoolean(ApplicationConfiguration.GEO_REPORT, false); lockedIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/lock.svg"); - lockedIconSelected = SVGIconUtilities.createSVGIcon("icons/fontawesome/lock.svg"); lockedIconSelected.setColorFilter(whiteColorFilter); unlockedIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/lock-open.svg"); - unlockedIconSelected = SVGIconUtilities.createSVGIcon("icons/fontawesome/lock-open.svg"); unlockedIconSelected.setColorFilter(whiteColorFilter); + + subtitleIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/closed-captioning.svg"); + subtitleIconSelected = SVGIconUtilities.createSVGIcon("icons/fontawesome/closed-captioning.svg"); + subtitleIconSelected.setColorFilter(whiteColorFilter); + + highQualityIcon = SVGIconUtilities.createSVGIcon("icons/derreisende77/high-quality.svg"); + highQualityIconSelected = SVGIconUtilities.createSVGIcon("icons/derreisende77/high-quality.svg"); + highQualityIconSelected.setColorFilter(whiteColorFilter); + + liveStreamIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/tower-cell.svg"); + liveStreamIconSelected = SVGIconUtilities.createSVGIcon("icons/fontawesome/tower-cell.svg"); + liveStreamIconSelected.setColorFilter(whiteColorFilter); } protected void drawGeolocationIcons(@NotNull DatenFilm film, boolean isSelected) { @@ -69,11 +88,6 @@ protected void drawGeolocationIcons(@NotNull DatenFilm film, boolean isSelected) }); } - @Handler - private void handleGeoStateChanged(GeoStateChangedEvent e) { - SwingUtilities.invokeLater(() -> geoMelden = config.getBoolean(ApplicationConfiguration.GEO_REPORT, false)); - } - protected void resetComponent() { setBackground(null); setForeground(null); @@ -118,17 +132,52 @@ protected void setBackgroundColor(final Component c, final Start s, final boolea } } - private void setGeoblockingBackgroundColor(final Component c, final boolean isSelected) { - if (isSelected) - return; + /** + * Show "CC" and/or "HQ" icon(s) when supported by the film. + * + * @param datenFilm film information + * @param isSelected is row selected. + */ + protected void setIndicatorIcons(@NotNull DatenFilm datenFilm, boolean isSelected) { + datenFilm.getGeo().ifPresent(geoString -> { + if (!geoString.contains(config.getString(ApplicationConfiguration.GEO_LOCATION))) { + //locked + if (isSelected) + iconList.add(lockedIconSelected); + else + iconList.add(lockedIcon); + } + }); + + if (datenFilm.isHighQuality()) { + if (isSelected) + iconList.add(highQualityIconSelected); + else + iconList.add(highQualityIcon); + } - c.setBackground(MVColor.FILM_GEOBLOCK_BACKGROUND.color); - } + if (datenFilm.hasSubtitle()) { + if (isSelected) + iconList.add(subtitleIconSelected); + else + iconList.add(subtitleIcon); + } - protected void setupGeoblockingBackground(final Component c, final String geo, final boolean isSelected) { - if (!geo.isEmpty()) { - if (!geo.contains(config.getString(ApplicationConfiguration.GEO_LOCATION))) - setGeoblockingBackgroundColor(c, isSelected); + if (datenFilm.isLivestream()) { + if (isSelected) + iconList.add(liveStreamIconSelected); + else + iconList.add(liveStreamIcon); } + + Icon icon; + if (iconList.size() == 1) + icon = iconList.get(0); + else + icon = new CompoundIcon(CompoundIcon.Axis.X_AXIS, 3, iconList.toArray(new Icon[0])); + setIcon(icon); + + //always clear at the end + iconList.clear(); } } diff --git a/src/main/java/mediathek/tool/cellrenderer/CellRendererDownloads.java b/src/main/java/mediathek/tool/cellrenderer/CellRendererDownloads.java index c4338827d0..bc5dd3eb73 100644 --- a/src/main/java/mediathek/tool/cellrenderer/CellRendererDownloads.java +++ b/src/main/java/mediathek/tool/cellrenderer/CellRendererDownloads.java @@ -108,7 +108,6 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole handleAboColumn(textArea, datenDownload); } setBackgroundColor(textArea, datenDownload.start, isSelected); - handleGeoBlocking(textArea, datenDownload, isSelected); return textArea; } } @@ -213,11 +212,12 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole var columnWidth = table.getColumnModel().getColumn(columnModelIndex).getWidth(); if (columnWidth < table.getFontMetrics(table.getFont()).stringWidth(title)) setToolTipText(title); + + setIndicatorIcons(datenDownload.film, isSelected); } } setBackgroundColor(this, datenDownload.start, isSelected); - handleGeoBlocking(this, datenDownload, isSelected); } catch (Exception ex) { logger.error(ex); } @@ -275,14 +275,6 @@ private void handleAboColumn(final DatenDownload datenDownload) { } } - private void handleGeoBlocking(Component c, final DatenDownload datenDownload, final boolean isSelected) { - if (geoMelden) { - if (datenDownload.start == null) { - setupGeoblockingBackground(c, datenDownload.arr[DatenDownload.DOWNLOAD_GEO], isSelected); - } - } - } - private void handleButtonDeleteColumn(final DatenDownload datenDownload, final boolean isSelected) { setHorizontalAlignment(SwingConstants.CENTER); if (datenDownload.start != null) { diff --git a/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java b/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java index 220567fde6..bce3d5887c 100644 --- a/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java +++ b/src/main/java/mediathek/tool/cellrenderer/CellRendererFilme.java @@ -7,8 +7,6 @@ import mediathek.controller.starter.Start; import mediathek.daten.DatenDownload; import mediathek.daten.DatenFilm; -import mediathek.tool.ApplicationConfiguration; -import mediathek.tool.CompoundIcon; import mediathek.tool.SVGIconUtilities; import mediathek.tool.table.MVTable; import org.apache.logging.log4j.LogManager; @@ -17,8 +15,6 @@ import javax.swing.*; import java.awt.*; -import java.util.ArrayList; -import java.util.List; public class CellRendererFilme extends CellRendererBaseWithStart { private static final Logger logger = LogManager.getLogger(CellRendererFilme.class); @@ -33,18 +29,6 @@ public class CellRendererFilme extends CellRendererBaseWithStart { private final FlatSVGIcon normalBookmarkIcon; private final FlatSVGIcon selectedBookmarkIconHighlighted; - private final FlatSVGIcon subtitleIcon; - private final FlatSVGIcon subtitleIconSelected; - - private final FlatSVGIcon highQualityIcon; - private final FlatSVGIcon highQualityIconSelected; - private final FlatSVGIcon liveStreamIcon; - private final FlatSVGIcon liveStreamIconSelected; - /** - * Temporary storage for the icons that will be assembled to a compound icon. - */ - private final List iconList = new ArrayList<>(); - public CellRendererFilme() { selectedDownloadIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/download.svg"); selectedDownloadIcon.setColorFilter(whiteColorFilter); @@ -68,18 +52,6 @@ public CellRendererFilme() { selectedBookmarkIconHighlighted.setColorFilter(new FlatSVGIcon.ColorFilter(color -> Color.ORANGE)); normalBookmarkIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/bookmark.svg"); - - subtitleIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/closed-captioning.svg"); - subtitleIconSelected = SVGIconUtilities.createSVGIcon("icons/fontawesome/closed-captioning.svg"); - subtitleIconSelected.setColorFilter(whiteColorFilter); - - highQualityIcon = SVGIconUtilities.createSVGIcon("icons/derreisende77/high-quality.svg"); - highQualityIconSelected = SVGIconUtilities.createSVGIcon("icons/derreisende77/high-quality.svg"); - highQualityIconSelected.setColorFilter(whiteColorFilter); - - liveStreamIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/tower-cell.svg"); - liveStreamIconSelected = SVGIconUtilities.createSVGIcon("icons/fontawesome/tower-cell.svg"); - liveStreamIconSelected.setColorFilter(whiteColorFilter); } private JTextArea createTextArea(String content) { @@ -177,55 +149,6 @@ public Component getTableCellRendererComponent( return this; } - /** - * Show "CC" and/or "HQ" icon(s) when supported by the film. - * - * @param datenFilm film information - * @param isSelected is row selected. - */ - private void setIndicatorIcons(@NotNull DatenFilm datenFilm, boolean isSelected) { - datenFilm.getGeo().ifPresent(geoString -> { - if (!geoString.contains(config.getString(ApplicationConfiguration.GEO_LOCATION))) { - //locked - if (isSelected) - iconList.add(lockedIconSelected); - else - iconList.add(lockedIcon); - } - }); - - if (datenFilm.isHighQuality()) { - if (isSelected) - iconList.add(highQualityIconSelected); - else - iconList.add(highQualityIcon); - } - - if (datenFilm.hasSubtitle()) { - if (isSelected) - iconList.add(subtitleIconSelected); - else - iconList.add(subtitleIcon); - } - - if (datenFilm.isLivestream()) { - if (isSelected) - iconList.add(liveStreamIconSelected); - else - iconList.add(liveStreamIcon); - } - - Icon icon; - if (iconList.size() == 1) - icon = iconList.get(0); - else - icon = new CompoundIcon(CompoundIcon.Axis.X_AXIS, 3, iconList.toArray(new Icon[0])); - setIcon(icon); - - //always clear at the end - iconList.clear(); - } - /** * Apply the specific horizontal alignment to the cell based on column * @@ -260,13 +183,6 @@ private void applyColorSettings(Component c, @NotNull DatenFilm datenFilm, Daten } else if (isBookMarked && !isSelected) { c.setBackground(MVColor.FILM_BOOKMARKED.color); } - - if (geoMelden) { - //only apply geo block colors when we haven´t changed the background for seen history - if (!hasBeenSeen) { - setupGeoblockingBackground(c, datenFilm.getGeo().orElse(""), isSelected); - } - } } } From 4e96f8c7cacbf1aee57c87af85c167a8adf0df19 Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 22 Jul 2022 07:09:05 +0200 Subject: [PATCH 225/422] - add icon for audio description --- .../CellRendererBaseWithStart.java | 13 +++ .../mediathek/tool/table/MVFilmTable.java | 90 ++++++++++--------- .../icons/fontawesome/audio-description.svg | 1 + 3 files changed, 60 insertions(+), 44 deletions(-) create mode 100755 src/main/resources/icons/fontawesome/audio-description.svg diff --git a/src/main/java/mediathek/tool/cellrenderer/CellRendererBaseWithStart.java b/src/main/java/mediathek/tool/cellrenderer/CellRendererBaseWithStart.java index 6e8c739640..3a3bf54a52 100644 --- a/src/main/java/mediathek/tool/cellrenderer/CellRendererBaseWithStart.java +++ b/src/main/java/mediathek/tool/cellrenderer/CellRendererBaseWithStart.java @@ -35,6 +35,8 @@ public class CellRendererBaseWithStart extends CellRendererBase { private final FlatSVGIcon highQualityIconSelected; private final FlatSVGIcon liveStreamIcon; private final FlatSVGIcon liveStreamIconSelected; + private final FlatSVGIcon audioDescription; + private final FlatSVGIcon audioDescriptionSelected; protected FlatSVGIcon.ColorFilter whiteColorFilter = new FlatSVGIcon.ColorFilter(color -> Color.WHITE); public CellRendererBaseWithStart() { @@ -59,6 +61,10 @@ public CellRendererBaseWithStart() { liveStreamIcon = SVGIconUtilities.createSVGIcon("icons/fontawesome/tower-cell.svg"); liveStreamIconSelected = SVGIconUtilities.createSVGIcon("icons/fontawesome/tower-cell.svg"); liveStreamIconSelected.setColorFilter(whiteColorFilter); + + audioDescription = SVGIconUtilities.createSVGIcon("icons/fontawesome/audio-description.svg"); + audioDescriptionSelected = SVGIconUtilities.createSVGIcon("icons/fontawesome/audio-description.svg"); + audioDescriptionSelected.setColorFilter(whiteColorFilter); } protected void drawGeolocationIcons(@NotNull DatenFilm film, boolean isSelected) { @@ -156,6 +162,13 @@ protected void setIndicatorIcons(@NotNull DatenFilm datenFilm, boolean isSelecte iconList.add(highQualityIcon); } + if (datenFilm.isAudioVersion()) { + if (isSelected) + iconList.add(audioDescriptionSelected); + else + iconList.add(audioDescription); + } + if (datenFilm.hasSubtitle()) { if (isSelected) iconList.add(subtitleIconSelected); diff --git a/src/main/java/mediathek/tool/table/MVFilmTable.java b/src/main/java/mediathek/tool/table/MVFilmTable.java index 0c0af97829..5f11b741ac 100644 --- a/src/main/java/mediathek/tool/table/MVFilmTable.java +++ b/src/main/java/mediathek/tool/table/MVFilmTable.java @@ -19,11 +19,11 @@ public class MVFilmTable extends MVTable { private static final Logger logger = LogManager.getLogger(); - private MyRowSorter sorter; /** * Stores the selected FILM NUMBERS that need to be restored after updates. */ private final List selectedFilmNumbers = new ArrayList<>(); + private MyRowSorter sorter; public MVFilmTable() { super(DatenFilm.MAX_ELEM, GuiFilme.VISIBLE_COLUMNS, @@ -152,49 +152,6 @@ public void setSpalten() { } } - static class MyRowSorter extends TableRowSorter { - //private static final Logger rsLogger = LogManager.getLogger(); - - public MyRowSorter(M model) { - super(model); - } - - @Override - public void setModel(M model) { - super.setModel(model); - // do not sort buttons - setSortable(DatenFilm.FILM_ABSPIELEN, false); - setSortable(DatenFilm.FILM_AUFZEICHNEN, false); - //compare to FilmSize->int instead of String - setComparator(DatenFilm.FILM_GROESSE, (Comparator) FilmSize::compareTo); - // deactivate german collator used in DatenFilm as it slows down sorting as hell... - setComparator(DatenFilm.FILM_SENDER, (Comparator) String::compareTo); - setComparator(DatenFilm.FILM_ZEIT, (Comparator) String::compareTo); - setComparator(DatenFilm.FILM_URL, (Comparator) String::compareTo); - } - - @Override - public void setSortKeys(List sortKeys) { - //FIXME something is wrong in MVTable with setting sort keys - if (sortKeys != null) { - if (sortKeys.size() > 1) { - //rsLogger.error("BULLSHIT SORTKEYS IN"); - sortKeys.remove(1); - } - } - super.setSortKeys(sortKeys); -/* - var list = getSortKeys(); - if (list != null) { - rsLogger.debug("SORT KEYS:"); - for (var key : list) { - rsLogger.debug("COLUMN: " + key.getColumn() + ",SortOrder: " + key.getSortOrder().toString()); - } - } -*/ - } - } - /** * Scroll to a selected row, but center it in the middle of the visible rectangle. * @param rowIndex The row to be displayed centered. @@ -221,6 +178,7 @@ protected void scrollToIndexDelegate(int index) { // film list moves selected entries into center of JTable view scrollToSelectionCentered(index); } + @Override protected void restoreSelectedTableRows() { if (!selectedFilmNumbers.isEmpty()) { @@ -265,4 +223,48 @@ public void saveSelectedTableRows() { } } } + + static class MyRowSorter extends TableRowSorter { + //private static final Logger rsLogger = LogManager.getLogger(); + + public MyRowSorter(M model) { + super(model); + } + + @Override + public void setModel(M model) { + super.setModel(model); + // do not sort buttons + setSortable(DatenFilm.FILM_ABSPIELEN, false); + setSortable(DatenFilm.FILM_AUFZEICHNEN, false); + setSortable(DatenFilm.FILM_URL, false); + //compare to FilmSize->int instead of String + setComparator(DatenFilm.FILM_GROESSE, (Comparator) FilmSize::compareTo); + // deactivate german collator used in DatenFilm as it slows down sorting as hell... + setComparator(DatenFilm.FILM_SENDER, (Comparator) String::compareTo); + setComparator(DatenFilm.FILM_ZEIT, (Comparator) String::compareTo); + //setComparator(DatenFilm.FILM_URL, (Comparator) String::compareTo); + } + + @Override + public void setSortKeys(List sortKeys) { + //FIXME something is wrong in MVTable with setting sort keys + if (sortKeys != null) { + if (sortKeys.size() > 1) { + //rsLogger.error("BULLSHIT SORTKEYS IN"); + sortKeys.remove(1); + } + } + super.setSortKeys(sortKeys); +/* + var list = getSortKeys(); + if (list != null) { + rsLogger.debug("SORT KEYS:"); + for (var key : list) { + rsLogger.debug("COLUMN: " + key.getColumn() + ",SortOrder: " + key.getSortOrder().toString()); + } + } +*/ + } + } } diff --git a/src/main/resources/icons/fontawesome/audio-description.svg b/src/main/resources/icons/fontawesome/audio-description.svg new file mode 100755 index 0000000000..1420c83fe8 --- /dev/null +++ b/src/main/resources/icons/fontawesome/audio-description.svg @@ -0,0 +1 @@ + \ No newline at end of file From b551bf917d22e030fc51d138c8c5ea392c223481 Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 22 Jul 2022 08:41:35 +0200 Subject: [PATCH 226/422] - use latest FlatLaf - harmonize pattern color usage - do not make pattern colors configurable but use L&F defaults - cleanup sort key handling --- pom.xml | 2 +- src/main/java/mediathek/config/MVColor.java | 27 ++++++++++------ .../dialogEinstellungen/PanelBlacklist.java | 22 +++++++++++-- .../mediathek/gui/tabs/tab_film/GuiFilme.java | 32 +++---------------- src/main/java/mediathek/tool/Filter.java | 32 +++---------------- .../java/mediathek/tool/GuiFunktionen.java | 12 +++++++ .../mediathek/tool/table/MVFilmTable.java | 27 ++++++---------- 7 files changed, 70 insertions(+), 84 deletions(-) diff --git a/pom.xml b/pom.xml index 87e9cce53c..c720fd6b83 100755 --- a/pom.xml +++ b/pom.xml @@ -94,7 +94,7 @@ 3.12.0 11.1.1 1.0.1 - 2.4-SNAPSHOT + 2.4 31.1-jre 1.1.2 2.13.3 diff --git a/src/main/java/mediathek/config/MVColor.java b/src/main/java/mediathek/config/MVColor.java index c51f026a27..524a79e065 100644 --- a/src/main/java/mediathek/config/MVColor.java +++ b/src/main/java/mediathek/config/MVColor.java @@ -19,8 +19,10 @@ */ package mediathek.config; +import com.formdev.flatlaf.FlatLaf; import mediathek.tool.MVC; +import javax.swing.*; import java.awt.*; import java.util.ArrayList; @@ -45,22 +47,16 @@ public class MVColor { public static final MVC DOWNLOAD_FEHLER = new MVC(MVConfig.Configs.FARBE__DOWNLOAD_FEHLER, new Color(241, 188, 221), "Download, fehlerhaft"); public static final MVC DOWNLOAD_FEHLER_SEL = new MVC(MVConfig.Configs.FARBE__DOWNLOAD_FEHLER_SEL, new Color(206, 92, 128), "Download, fehlerhaft, selektiert"); - // Filter wenn RegEx - public static final MVC FILTER_REGEX = new MVC(MVConfig.Configs.FARBE__FILTER_REGEX, new Color(153, 214, 255), "Filter ist RegEx"); - public static final MVC FILTER_REGEX_FEHLER = new MVC(MVConfig.Configs.FARBE__FILTER_REGEX_FEHLER, Color.RED, "Filter ist Regex, fehlerhaft"); - // ProgrammGui public static final MVC BUTTON_SET_ABSPIELEN = new MVC(MVConfig.Configs.FARBE__BUTTON_SET_ABSPIELEN, new Color(205, 255, 191), "Einstellungen Sets, Button Abspielen"); - // DialogDownload public static final MVC DOWNLOAD_DATEINAME_EXISTIERT = new MVC(MVConfig.Configs.FARBE__DOWNLOAD_DATEINAME_EXISTIERT, new Color(190, 0, 0), "Download, Dateiname existiert schon"); public static final MVC DOWNLOAD_DATEINAME_NEU = new MVC(MVConfig.Configs.FARBE__DOWNLOAD_DATEINAME_NEU, new Color(0, 140, 0), "Download, Dateiname ist neu"); public static final MVC DOWNLOAD_DATEINAME_ALT = new MVC(MVConfig.Configs.FARBE__DOWNLOAD_DATEINAME_ALT, new Color(0, 0, 200), "Download, Dateiname ist der alte"); - - public ArrayList liste = new ArrayList<>(); public static final int MVC_TEXT = 0; public static final int MVC_COLOR = 1; public static final int MVC_MAX = 2; + public ArrayList liste = new ArrayList<>(); public MVColor() { liste.add(FILM_HISTORY); @@ -77,14 +73,27 @@ public MVColor() { liste.add(DOWNLOAD_FERTIG_SEL); liste.add(DOWNLOAD_FEHLER); liste.add(DOWNLOAD_FEHLER_SEL); - liste.add(FILTER_REGEX); - liste.add(FILTER_REGEX_FEHLER); liste.add(BUTTON_SET_ABSPIELEN); liste.add(DOWNLOAD_DATEINAME_EXISTIERT); liste.add(DOWNLOAD_DATEINAME_NEU); liste.add(DOWNLOAD_DATEINAME_ALT); } + /** + * Get the pattern text color based on L&F dark mode. + * + * @return adjusted color for current L&F + */ + public static Color getRegExPatternColor() { + Color color; + if (FlatLaf.isLafDark()) { + color = UIManager.getColor("Hyperlink.linkColor"); + } else + color = Color.BLUE; + + return color; + } + public final void load() { liste.stream().filter(mvc -> !MVConfig.get(mvc.configs).isEmpty()).forEach(mvc -> { try { diff --git a/src/main/java/mediathek/gui/dialogEinstellungen/PanelBlacklist.java b/src/main/java/mediathek/gui/dialogEinstellungen/PanelBlacklist.java index 3a109c5e6b..881358d13b 100644 --- a/src/main/java/mediathek/gui/dialogEinstellungen/PanelBlacklist.java +++ b/src/main/java/mediathek/gui/dialogEinstellungen/PanelBlacklist.java @@ -2,6 +2,7 @@ import mediathek.config.Daten; import mediathek.config.Konstanten; +import mediathek.config.MVColor; import mediathek.config.MVConfig; import mediathek.daten.blacklist.BlacklistRule; import mediathek.file.GetFile; @@ -232,8 +233,25 @@ public void changedUpdate(DocumentEvent e) { } private void tus() { - Filter.validatePatternInput(jTextFieldThemaTitel); - Filter.validatePatternInput(jTextFieldTitel); + validatePatternInput(jTextFieldThemaTitel); + validatePatternInput(jTextFieldTitel); + } + + /** + * Check if entry in JTextField is a regexp pattern and its validity. + * If a recognized pattern is invalid, change the background color of the JTextField. + * + * @param tf The control that will be validated + */ + private void validatePatternInput(JTextField tf) { + String text = tf.getText(); + if (Filter.isPattern(text)) { + tf.setForeground(MVColor.getRegExPatternColor()); + GuiFunktionen.showErrorIndication(tf, Filter.makePatternNoCache(text) == null); + } else { + GuiFunktionen.showErrorIndication(tf, false); + tf.setForeground(UIManager.getColor("TextField.foreground")); + } } }; jTextFieldTitel.getDocument().addDocumentListener(documentListener); diff --git a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java index 7a15510567..c37e6f8295 100644 --- a/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java +++ b/src/main/java/mediathek/gui/tabs/tab_film/GuiFilme.java @@ -16,6 +16,7 @@ import javafx.util.Duration; import mediathek.config.Daten; import mediathek.config.Konstanten; +import mediathek.config.MVColor; import mediathek.config.MVConfig; import mediathek.controller.history.SeenHistoryController; import mediathek.controller.starter.Start; @@ -766,7 +767,6 @@ public class SearchField extends JTextField { private static final String SEARCHMODE_PROPERTY_STRING = "searchMode"; private final SearchHistoryButton searchHistoryButton = new SearchHistoryButton(); private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); - private final Color DEFAULT_FOREGROUND_COLOR = UIManager.getColor("TextField.foreground"); private SearchControlFieldMode searchMode; public SearchField() { @@ -841,24 +841,9 @@ private void doCheck() { private void setForegroundTextColor(String text) { if (Filter.isPattern(text)) - setForeground(getPatternColor()); + setForeground(MVColor.getRegExPatternColor()); else - setForeground(DEFAULT_FOREGROUND_COLOR); - } - - /** - * Get the pattern text color based on L&F dark mode. - * @return adjusted color for current L&F - */ - private Color getPatternColor() { - Color color; - if (FlatLaf.isLafDark()) { - color = UIManager.getColor("Hyperlink.linkColor"); - } - else - color = Color.BLUE; - - return color; + setForeground(UIManager.getColor("TextField.foreground")); } private boolean isPatternValid(String text) { @@ -867,16 +852,9 @@ private boolean isPatternValid(String text) { private void checkPatternValidity(String text) { if (Filter.isPattern(text)) - showErrorIndication(!isPatternValid(text)); - else - showErrorIndication(false); - } - - private void showErrorIndication(boolean hasError) { - if (hasError) - putClientProperty("JComponent.outline", "error"); + GuiFunktionen.showErrorIndication(this,!isPatternValid(text)); else - putClientProperty("JComponent.outline", ""); + GuiFunktionen.showErrorIndication(this, false); } private void clearSearchField() { diff --git a/src/main/java/mediathek/tool/Filter.java b/src/main/java/mediathek/tool/Filter.java index 73287757f6..7ad73a4406 100644 --- a/src/main/java/mediathek/tool/Filter.java +++ b/src/main/java/mediathek/tool/Filter.java @@ -3,7 +3,6 @@ import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; -import mediathek.config.MVColor; import mediathek.daten.DatenFilm; import mediathek.daten.abo.DatenAbo; import org.apache.commons.lang3.StringUtils; @@ -11,14 +10,16 @@ import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; -import javax.swing.*; -import java.awt.*; import java.util.HashSet; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; public class Filter { + /** + * Stores the regexp strings that were rejected as invalid. + */ + public static final Set regExpErrorList = new HashSet<>(); /** * The cache for already compiled RegExp. * Entries will be removed if the haven´t been accessed for more than 5 minutes. @@ -27,10 +28,6 @@ public class Filter { .expireAfterAccess(5, TimeUnit.MINUTES) .build(new PatternCacheLoader()); private static final Logger logger = LogManager.getLogger(Filter.class); - /** - * Stores the regexp strings that were rejected as invalid. - */ - public static final Set regExpErrorList = new HashSet<>(); public static boolean aboExistiertBereits(DatenAbo aboExistiert, DatenAbo aboPruefen) { // prüfen ob "aboExistiert" das "aboPrüfen" mit abdeckt, also die gleichen (oder mehr) @@ -248,6 +245,7 @@ public static Pattern makePattern(final String regExpStr) { /** * Create pattern without using the cache. * Used for interactive search field where cache pollution is not wanted. + * * @param regExpStr the regexp pattern * @return Pattern if successful, otherwise null. */ @@ -276,26 +274,6 @@ public static boolean regExpErrorsOccured() { return !regExpErrorList.isEmpty(); } - /** - * Check if entry in JTextField is a regexp pattern and its validity. - * If a recognized pattern is invalid, change the background color of the JTextField. - * - * @param tf The control that will be validated - */ - public static void validatePatternInput(JTextField tf) { - String text = tf.getText(); - if (Filter.isPattern(text)) { - if (Filter.makePattern(text) == null) { - //soll Pattern sein, ist aber falsch - tf.setBackground(MVColor.FILTER_REGEX_FEHLER.color); - } else { - tf.setBackground(MVColor.FILTER_REGEX.color); - } - } else { - tf.setBackground(Color.WHITE); - } - } - /** * This loader will compile regexp patterns when they are not in cache. */ diff --git a/src/main/java/mediathek/tool/GuiFunktionen.java b/src/main/java/mediathek/tool/GuiFunktionen.java index 6f0717c081..b7d6814d98 100644 --- a/src/main/java/mediathek/tool/GuiFunktionen.java +++ b/src/main/java/mediathek/tool/GuiFunktionen.java @@ -76,6 +76,18 @@ public static int createDismissableMessageDialog(@Nullable Component parentCompo return (int)op.getValue(); } + /** + * Show a "red box" around a component to indicate error condition + * @param component the target + * @param hasError if true, set error box around component, otherwise remove it. + */ + public static void showErrorIndication(@NotNull JComponent component, boolean hasError) { + if (hasError) + component.putClientProperty("JComponent.outline", "error"); + else + component.putClientProperty("JComponent.outline", ""); + } + public static boolean isUsingExternalUpdater() { return !isNotUsingExternalUpdater(); } diff --git a/src/main/java/mediathek/tool/table/MVFilmTable.java b/src/main/java/mediathek/tool/table/MVFilmTable.java index 5f11b741ac..b55ddd3a20 100644 --- a/src/main/java/mediathek/tool/table/MVFilmTable.java +++ b/src/main/java/mediathek/tool/table/MVFilmTable.java @@ -34,13 +34,13 @@ public MVFilmTable() { setAutoCreateRowSorter(false); addPropertyChangeListener("model", evt -> { - //System.out.println("TABLE MODEL CHANGED"); + var model = (TableModel) evt.getNewValue(); if (sorter == null) { - sorter = new MyRowSorter<>(getModel()); + sorter = new MyRowSorter<>(model); //sorter.addRowSorterListener(evt1 -> System.out.println("SORT ORDER HAS CHANGED")); } + sorter.setModel(model); setRowSorter(sorter); - sorter.setModel(getModel()); }); } @@ -225,8 +225,6 @@ public void saveSelectedTableRows() { } static class MyRowSorter extends TableRowSorter { - //private static final Logger rsLogger = LogManager.getLogger(); - public MyRowSorter(M model) { super(model); } @@ -234,6 +232,8 @@ public MyRowSorter(M model) { @Override public void setModel(M model) { super.setModel(model); + + //must be set after each model change // do not sort buttons setSortable(DatenFilm.FILM_ABSPIELEN, false); setSortable(DatenFilm.FILM_AUFZEICHNEN, false); @@ -244,27 +244,18 @@ public void setModel(M model) { setComparator(DatenFilm.FILM_SENDER, (Comparator) String::compareTo); setComparator(DatenFilm.FILM_ZEIT, (Comparator) String::compareTo); //setComparator(DatenFilm.FILM_URL, (Comparator) String::compareTo); + setComparator(DatenFilm.FILM_DAUER, Comparator.naturalOrder()); } @Override public void setSortKeys(List sortKeys) { - //FIXME something is wrong in MVTable with setting sort keys + // MV config stores only ONE sort key + // here we make sure that only one will be set on the table... if (sortKeys != null) { - if (sortKeys.size() > 1) { - //rsLogger.error("BULLSHIT SORTKEYS IN"); + while (sortKeys.size() > 1) sortKeys.remove(1); - } } super.setSortKeys(sortKeys); -/* - var list = getSortKeys(); - if (list != null) { - rsLogger.debug("SORT KEYS:"); - for (var key : list) { - rsLogger.debug("COLUMN: " + key.getColumn() + ",SortOrder: " + key.getSortOrder().toString()); - } - } -*/ } } } From 1bb9a9d926d899b12e7979dede235b5d4e725802 Mon Sep 17 00:00:00 2001 From: Christian F Date: Fri, 22 Jul 2022 20:25:09 +0200 Subject: [PATCH 227/422] - convert splash screen to swing --- src/main/java/mediathek/Main.java | 6 +- src/main/java/mediathek/SplashScreen.java | 193 ++++++++++++------ .../res/programm/fxml/splashscreen.fxml | 22 -- 3 files changed, 129 insertions(+), 92 deletions(-) delete mode 100644 src/main/resources/mediathek/res/programm/fxml/splashscreen.fxml diff --git a/src/main/java/mediathek/Main.java b/src/main/java/mediathek/Main.java index 2f55e4d87a..f57e7473c2 100644 --- a/src/main/java/mediathek/Main.java +++ b/src/main/java/mediathek/Main.java @@ -466,7 +466,7 @@ public static void main(final String... args) { } else { logger.warn("Debugger detected -> Splash screen disabled..."); } - splashScreen.ifPresent(SplashScreen::show); + splashScreen.ifPresent(splash -> splash.setVisible(true)); migrateOldConfigSettings(); @@ -638,10 +638,10 @@ private static void startGuiMode() { splashScreen.ifPresent(SplashScreen::close); window.setVisible(true); /* - on windows there is a strange behaviour that the main window gets sent behind + on windows and linux there is a strange behaviour that the main window gets sent behind other open windows after the splash screen is closed. */ - if (SystemUtils.IS_OS_WINDOWS) { + if (!SystemUtils.IS_OS_MAC_OSX) { window.toFront(); window.requestFocus(); } diff --git a/src/main/java/mediathek/SplashScreen.java b/src/main/java/mediathek/SplashScreen.java index 1927bf6895..bb5bf5b5d3 100644 --- a/src/main/java/mediathek/SplashScreen.java +++ b/src/main/java/mediathek/SplashScreen.java @@ -1,54 +1,68 @@ +/* + * Created by JFormDesigner on Fri Jul 22 12:01:51 CEST 2022 + */ + package mediathek; -import javafx.application.Platform; -import javafx.fxml.FXML; -import javafx.fxml.FXMLLoader; -import javafx.scene.Scene; -import javafx.scene.control.Label; -import javafx.scene.control.ProgressBar; -import javafx.scene.image.Image; -import javafx.stage.Stage; -import javafx.stage.StageStyle; import mediathek.config.Konstanten; -import mediathek.javafx.tool.JavaFxUtils; import mediathek.tool.TimerPool; import mediathek.tool.UIProgressState; import org.apache.commons.lang3.SystemUtils; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import java.io.IOException; -import java.net.URL; +import javax.swing.*; +import java.awt.*; import java.util.EnumSet; import java.util.Optional; import java.util.concurrent.TimeUnit; -public class SplashScreen { - private static final Logger LOG = LogManager.getLogger(SplashScreen.class); +public class SplashScreen extends JWindow { private static final double MAXIMUM_STEPS = EnumSet.allOf(UIProgressState.class).size() - 1d; + public JLabel versionLabel; + private double curSteps; + private JLabel appTitleLabel; + private JLabel imageLabel; + private JProgressBar progressBar; + private JLabel statusLabel; - @FXML - private Label appName; + public SplashScreen() { + super(); + initComponents(); - @FXML - private Label appVersion; + getContentPane().setBackground(Color.BLACK); - @FXML - private Label progressText; + var res = String.format("Version: %s (%s %s)", Konstanten.MVVERSION, getOsName(), SystemUtils.OS_ARCH); + versionLabel.setText(res); - @FXML - private ProgressBar progressBar; + progressBar.setValue(0); + setLocationRelativeTo(null); - private double curSteps; - private Stage window; + //strange behaviour on win where window will not come to front or stay there... + if (SystemUtils.IS_OS_WINDOWS) { + if (isAlwaysOnTopSupported()) + setAlwaysOnTop(true); + } + } + + public void update(UIProgressState state) { + curSteps++; + int pct = (int) Math.round(100 * (curSteps / MAXIMUM_STEPS)); + updateStatus(state.toString(), pct); + } + + public void close() { + TimerPool.getTimerPool().schedule(() -> + SwingUtilities.invokeLater(() -> setVisible(false)), 2, TimeUnit.SECONDS); + Main.splashScreen = Optional.empty(); + } /** * Return "modern" macOS string for mac instead of legacy "Mac OS X". * According to apple dev docs even "old" 10.6 is now named macOS. + * * @return "macOS" for mac otherwise the java OS name */ private String getOsName() { - final String osName; + String osName; if (SystemUtils.IS_OS_MAC_OSX) osName = "macOS"; else @@ -57,47 +71,92 @@ private String getOsName() { return osName; } - public void show() { - JavaFxUtils.invokeInFxThreadAndWait(() -> { - window = new Stage(StageStyle.UNDECORATED); - window.getIcons().add(new Image("/mediathek/res/MediathekView.png")); - - URL url = getClass().getResource("/mediathek/res/programm/fxml/splashscreen.fxml"); - - FXMLLoader fxmlLoader = new FXMLLoader(); - fxmlLoader.setLocation(url); - fxmlLoader.setController(this); - - try { - Scene scene = new Scene(fxmlLoader.load()); - window.setScene(scene); - - appName.setText(Konstanten.PROGRAMMNAME); - appVersion.setText("Version: " + Konstanten.MVVERSION + " (" + getOsName() + ")"); - progressBar.prefWidthProperty().bind(scene.widthProperty()); - - window.setScene(scene); - window.show(); - window.centerOnScreen(); - } catch (IOException ioException) { - LOG.error("Can't find/load the splash screen FXML description!", ioException); - } - }); - } - - public void close() { - TimerPool.getTimerPool().schedule(() -> JavaFxUtils.invokeInFxThreadAndWait(() -> { - window.close(); - Main.splashScreen = Optional.empty(); // delete reference as we are not working anymore - }),2, TimeUnit.SECONDS); + /** + * Updates the percent complete bar and the associated status text. + * + * @param statusText The new status text to display. + * @param percentComplete The new percentage. + */ + public void updateStatus(String statusText, int percentComplete) { + appTitleLabel.paintImmediately(0, 0, appTitleLabel.getWidth(), appTitleLabel.getHeight()); + imageLabel.paintImmediately(0, 0, imageLabel.getWidth(), imageLabel.getHeight()); + versionLabel.paintImmediately(0, 0, versionLabel.getWidth(), versionLabel.getHeight()); + statusLabel.setText(statusText); + statusLabel.paintImmediately(0, 0, statusLabel.getWidth(), statusLabel.getHeight()); + progressBar.setValue(percentComplete); + progressBar.paintImmediately(0, 0, progressBar.getWidth(), progressBar.getHeight()); } - public void update(UIProgressState state) { - Platform.runLater(() -> { - curSteps++; - final double p = (curSteps / MAXIMUM_STEPS); - progressBar.setProgress(p); - progressText.setText(state.toString()); - }); + private void initComponents() { + appTitleLabel = new JLabel(); + versionLabel = new JLabel(); + imageLabel = new JLabel(); + progressBar = new JProgressBar(); + statusLabel = new JLabel(); + + setMinimumSize(new Dimension(640, 480)); + setBackground(Color.black); + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + setAutoRequestFocus(false); + setForeground(Color.black); + var contentPane = getContentPane(); + + appTitleLabel.setText(Konstanten.PROGRAMMNAME); + appTitleLabel.setFont(appTitleLabel.getFont().deriveFont(appTitleLabel.getFont().getStyle() | Font.BOLD, appTitleLabel.getFont().getSize() + 45f)); + appTitleLabel.setForeground(Color.white); + appTitleLabel.setBackground(Color.black); + appTitleLabel.setOpaque(true); + + versionLabel.setText("Version"); + versionLabel.setOpaque(true); + versionLabel.setForeground(Color.white); + versionLabel.setBackground(Color.black); + + imageLabel.setIcon(new ImageIcon(getClass().getResource("/mediathek/res/MediathekView.png"))); + imageLabel.setBackground(Color.black); + imageLabel.setOpaque(true); + + progressBar.setValue(50); + progressBar.setPreferredSize(new Dimension(146, 10)); + + statusLabel.setText("Status Text Message is here"); + statusLabel.setForeground(Color.white); + statusLabel.setBackground(Color.black); + statusLabel.setOpaque(true); + + GroupLayout contentPaneLayout = new GroupLayout(contentPane); + contentPane.setLayout(contentPaneLayout); + contentPaneLayout.setHorizontalGroup( + contentPaneLayout.createParallelGroup() + .addGroup(contentPaneLayout.createSequentialGroup() + .addContainerGap() + .addGroup(contentPaneLayout.createParallelGroup() + .addComponent(versionLabel, GroupLayout.DEFAULT_SIZE, 628, Short.MAX_VALUE) + .addComponent(progressBar, GroupLayout.DEFAULT_SIZE, 628, Short.MAX_VALUE) + .addGroup(contentPaneLayout.createSequentialGroup() + .addComponent(appTitleLabel) + .addGap(0, 257, Short.MAX_VALUE)) + .addComponent(statusLabel, GroupLayout.DEFAULT_SIZE, 628, Short.MAX_VALUE) + .addGroup(GroupLayout.Alignment.TRAILING, contentPaneLayout.createSequentialGroup() + .addGap(0, 372, Short.MAX_VALUE) + .addComponent(imageLabel))) + .addContainerGap()) + ); + contentPaneLayout.setVerticalGroup( + contentPaneLayout.createParallelGroup() + .addGroup(contentPaneLayout.createSequentialGroup() + .addContainerGap() + .addComponent(appTitleLabel) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addComponent(versionLabel) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, 94, Short.MAX_VALUE) + .addComponent(imageLabel) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addComponent(statusLabel) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addComponent(progressBar, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .addContainerGap()) + ); + pack(); } } diff --git a/src/main/resources/mediathek/res/programm/fxml/splashscreen.fxml b/src/main/resources/mediathek/res/programm/fxml/splashscreen.fxml deleted file mode 100644 index e83d67fa3c..0000000000 --- a/src/main/resources/mediathek/res/programm/fxml/splashscreen.fxml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - From 3a8a13a12b5d2f2e7d919991f23c6dd6d2ca8035 Mon Sep 17 00:00:00 2001 From: Christian F Date: Sat, 23 Jul 2022 12:05:21 +0200 Subject: [PATCH 228/422] - code cleanup --- src/main/java/mediathek/tool/table/MVFilmTable.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/mediathek/tool/table/MVFilmTable.java b/src/main/java/mediathek/tool/table/MVFilmTable.java index b55ddd3a20..57950a9c44 100644 --- a/src/main/java/mediathek/tool/table/MVFilmTable.java +++ b/src/main/java/mediathek/tool/table/MVFilmTable.java @@ -32,15 +32,16 @@ public MVFilmTable() { Optional.of(MVConfig.Configs.SYSTEM_EIGENSCHAFTEN_TABELLE_FILME)); setAutoCreateRowSorter(false); - addPropertyChangeListener("model", evt -> { + //we need to setup sorter later as the model is invalid at ctor point... var model = (TableModel) evt.getNewValue(); if (sorter == null) { sorter = new MyRowSorter<>(model); - //sorter.addRowSorterListener(evt1 -> System.out.println("SORT ORDER HAS CHANGED")); + sorter.setModel(model); + setRowSorter(sorter); } - sorter.setModel(model); - setRowSorter(sorter); + else + sorter.setModel(model); }); } From 1216c7fb036d8af13a5a09bc162d8bae13bf0d17 Mon Sep 17 00:00:00 2001 From: Christian F Date: Mon, 25 Jul 2022 15:07:45 +0200 Subject: [PATCH 229/422] - convert about dialog to swing --- .../mediathek/gui/actions/ShowAboutAction.kt | 2 +- .../mediathek/gui/dialog/AboutDialog.java | 260 ++++++++++++++++++ .../java/mediathek/gui/dialog/AboutDialog.jfd | 191 +++++++++++++ .../gui/dialog/about/AboutController.kt | 131 --------- .../mediathek/gui/dialog/about/AboutDialog.kt | 25 -- .../mediathek/res/programm/fxml/about.fxml | 79 ------ 6 files changed, 452 insertions(+), 236 deletions(-) create mode 100644 src/main/java/mediathek/gui/dialog/AboutDialog.java create mode 100644 src/main/java/mediathek/gui/dialog/AboutDialog.jfd delete mode 100644 src/main/java/mediathek/gui/dialog/about/AboutController.kt delete mode 100644 src/main/java/mediathek/gui/dialog/about/AboutDialog.kt delete mode 100644 src/main/resources/mediathek/res/programm/fxml/about.fxml diff --git a/src/main/java/mediathek/gui/actions/ShowAboutAction.kt b/src/main/java/mediathek/gui/actions/ShowAboutAction.kt index 0075ec5650..c7d6b90b84 100644 --- a/src/main/java/mediathek/gui/actions/ShowAboutAction.kt +++ b/src/main/java/mediathek/gui/actions/ShowAboutAction.kt @@ -1,6 +1,6 @@ package mediathek.gui.actions -import mediathek.gui.dialog.about.AboutDialog +import mediathek.gui.dialog.AboutDialog import mediathek.mainwindow.MediathekGui import mediathek.tool.GuiFunktionen import java.awt.event.ActionEvent diff --git a/src/main/java/mediathek/gui/dialog/AboutDialog.java b/src/main/java/mediathek/gui/dialog/AboutDialog.java new file mode 100644 index 0000000000..f4949d1a33 --- /dev/null +++ b/src/main/java/mediathek/gui/dialog/AboutDialog.java @@ -0,0 +1,260 @@ +/* + * Created by JFormDesigner on Sat Jul 23 12:40:16 CEST 2022 + */ + +package mediathek.gui.dialog; + +import mediathek.config.Konstanten; +import net.miginfocom.layout.AC; +import net.miginfocom.layout.CC; +import net.miginfocom.layout.LC; +import net.miginfocom.swing.MigLayout; +import org.apache.commons.lang3.SystemUtils; +import org.jdesktop.swingx.JXHyperlink; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.*; +import java.net.URL; + +/** + * @author Christian Franzke + */ +public class AboutDialog extends JDialog { + + public AboutDialog(Window owner) { + super(owner); + initComponents(); + + lblVersion.setText(String.format("Version %s (%s)", Konstanten.MVVERSION, SystemUtils.OS_ARCH)); + + hyperlinkHomepage.addActionListener(l -> browseToUrl(Konstanten.ADRESSE_WEBSITE)); + hyperlinkGuiDonation.addActionListener(l -> browseToUrl("https://paypal.me/ChristianFranzke")); + hyperlinkServerDonation.addActionListener(l -> browseToUrl(Konstanten.ADRESSE_DONATION)); + hyperlinkForum.addActionListener(l -> browseToUrl(Konstanten.ADRESSE_FORUM)); + hyperlinkOnlineHelp.addActionListener(l -> browseToUrl(Konstanten.ADRESSE_ONLINE_HELP)); + + hyperlinkJetBrains.addActionListener(l -> browseToUrl("https://www.jetbrains.com")); + hyperlinkEjTechnologies.addActionListener(l -> browseToUrl("https://www.ej-technologies.com")); + + + SwingUtilities.invokeLater(() -> scrollPane1.getVerticalScrollBar().setValue(0)); + } + + private void showError() { + JOptionPane.showMessageDialog(this, "Es konnte kein Browser geöffnet werden.", + Konstanten.PROGRAMMNAME, JOptionPane.ERROR_MESSAGE); + } + + private void browseToUrl(@NotNull String url) { + if (Desktop.isDesktopSupported()) { + var desktop = Desktop.getDesktop(); + if (desktop.isSupported(Desktop.Action.BROWSE)) { + try { + desktop.browse(new URL(url).toURI()); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + else { + showError(); + } + } + else { + showError(); + } + } + + private void initComponents() { + // JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents + // Generated using JFormDesigner non-commercial license + var label1 = new JLabel(); + var panel1 = new JPanel(); + var label2 = new JLabel(); + lblVersion = new JLabel(); + var tabbedPane1 = new JTabbedPane(); + var panel2 = new JPanel(); + var scrollPane3 = new JScrollPane(); + var textPane1 = new JTextPane(); + var panel3 = new JPanel(); + scrollPane1 = new JScrollPane(); + var textPane2 = new JTextPane(); + var panel5 = new JPanel(); + hyperlinkHomepage = new JXHyperlink(); + hyperlinkGuiDonation = new JXHyperlink(); + hyperlinkServerDonation = new JXHyperlink(); + hyperlinkForum = new JXHyperlink(); + hyperlinkOnlineHelp = new JXHyperlink(); + var label4 = new JLabel(); + var panel4 = new JPanel(); + hyperlinkJetBrains = new JXHyperlink(); + hyperlinkEjTechnologies = new JXHyperlink(); + + //======== this ======== + setTitle("\u00dcber dieses Programm"); //NON-NLS + setModal(true); + setMinimumSize(new Dimension(725, 470)); + setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + setResizable(false); + var contentPane = getContentPane(); + contentPane.setLayout(new MigLayout( + new LC().fill().insets("5").hideMode(3).gridGap("5", "5"), //NON-NLS + // columns + new AC() + .size("316").fill().gap() //NON-NLS + .fill(), + // rows + new AC() + .fill().gap() + .fill().gap() + .fill())); + + //---- label1 ---- + label1.setIcon(new ImageIcon(getClass().getResource("/mediathek/res/MediathekView.png"))); //NON-NLS + contentPane.add(label1, new CC().cell(0, 0)); + + //======== panel1 ======== + { + panel1.setLayout(new MigLayout( + new LC().insets("0").hideMode(3).gridGap("0", "0"), //NON-NLS + // columns + new AC() + .grow().align("left"), //NON-NLS + // rows + new AC() + .fill().gap() + .fill().gap("rel") //NON-NLS + .grow().fill())); + + //---- label2 ---- + label2.setText("MediathekView"); //NON-NLS + label2.setFont(label2.getFont().deriveFont(label2.getFont().getStyle() | Font.BOLD, label2.getFont().getSize() + 35f)); + panel1.add(label2, new CC().cell(0, 0)); + + //---- lblVersion ---- + lblVersion.setText("Version"); //NON-NLS + panel1.add(lblVersion, new CC().cell(0, 1)); + + //======== tabbedPane1 ======== + { + tabbedPane1.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); + + //======== panel2 ======== + { + panel2.setPreferredSize(new Dimension(196, 220)); + panel2.setLayout(new BorderLayout()); + + //======== scrollPane3 ======== + { + scrollPane3.setOpaque(false); + scrollPane3.setMinimumSize(new Dimension(196, 162)); + + //---- textPane1 ---- + textPane1.setText("MediathekView-Client:\nChristian Franzke (derreisende77)\n\nMediathekView-Server:\nNicklas Wiegandt (nicklas2751)\nPeter W. (pidoubleyou)\nSascha Wiegandt (thesasch)\n\nServer-Administration:\nAlexander Finkh\u00e4user (alex1702)"); //NON-NLS + scrollPane3.setViewportView(textPane1); + } + panel2.add(scrollPane3, BorderLayout.CENTER); + } + tabbedPane1.addTab("aktive Entwickler", panel2); //NON-NLS + + //======== panel3 ======== + { + panel3.setLayout(new BorderLayout()); + + //======== scrollPane1 ======== + { + + //---- textPane2 ---- + textPane2.setText("Gr\u00fcnder des Programms:\nXaver W. (xaverW)\n\nWeitere Beteiligte:\nsiedlerchr\nstyrol\nzxsd\napoleon\nhostis\npmshell\nclel\nthausherr\nklauswich"); //NON-NLS + scrollPane1.setViewportView(textPane2); + } + panel3.add(scrollPane1, BorderLayout.CENTER); + } + tabbedPane1.addTab("ehemalige Mitwirkende", panel3); //NON-NLS + } + panel1.add(tabbedPane1, new CC().cell(0, 2).grow()); + } + contentPane.add(panel1, new CC().cell(1, 0, 1, 2).grow()); + + //======== panel5 ======== + { + panel5.setLayout(new MigLayout( + new LC().fillX().insets("5").hideMode(3).gridGap("5", "5"), //NON-NLS + // columns + new AC() + .size("316").fill(), //NON-NLS + // rows + new AC() + .fill().gap() + .fill().gap() + .fill().gap() + .fill().gap() + .fill())); + + //---- hyperlinkHomepage ---- + hyperlinkHomepage.setText("Homepage"); //NON-NLS + panel5.add(hyperlinkHomepage, new CC().cell(0, 0)); + + //---- hyperlinkGuiDonation ---- + hyperlinkGuiDonation.setText("Spende an den Entwickler des Programms"); //NON-NLS + panel5.add(hyperlinkGuiDonation, new CC().cell(0, 1)); + + //---- hyperlinkServerDonation ---- + hyperlinkServerDonation.setText("Spende f\u00fcr den Server-Betrieb"); //NON-NLS + panel5.add(hyperlinkServerDonation, new CC().cell(0, 2)); + + //---- hyperlinkForum ---- + hyperlinkForum.setText("Hilfe-Forum"); //NON-NLS + panel5.add(hyperlinkForum, new CC().cell(0, 3)); + + //---- hyperlinkOnlineHelp ---- + hyperlinkOnlineHelp.setText("Online-Anleitung"); //NON-NLS + panel5.add(hyperlinkOnlineHelp, new CC().cell(0, 4)); + } + contentPane.add(panel5, new CC().cell(0, 1).growX()); + + //---- label4 ---- + label4.setText("Die Entwicklung wird unterst\u00fctzt von:"); //NON-NLS + label4.setVerticalAlignment(SwingConstants.TOP); + contentPane.add(label4, new CC().cell(0, 2)); + + //======== panel4 ======== + { + panel4.setLayout(new MigLayout( + new LC().insets("0").hideMode(3).gridGap("0", "0"), //NON-NLS + // columns + new AC() + .grow().align("left"), //NON-NLS + // rows + new AC() + .fill().gap() + .fill())); + + //---- hyperlinkJetBrains ---- + hyperlinkJetBrains.setText("JetBrains IntelliJ"); //NON-NLS + hyperlinkJetBrains.setVerticalAlignment(SwingConstants.TOP); + panel4.add(hyperlinkJetBrains, new CC().cell(0, 0).growX()); + + //---- hyperlinkEjTechnologies ---- + hyperlinkEjTechnologies.setText("ej-technologies install4j & JProfiler"); //NON-NLS + panel4.add(hyperlinkEjTechnologies, new CC().cell(0, 1).growX()); + } + contentPane.add(panel4, new CC().cell(1, 2)); + pack(); + setLocationRelativeTo(getOwner()); + // JFormDesigner - End of component initialization //GEN-END:initComponents + } + + // JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables + // Generated using JFormDesigner non-commercial license + private JLabel lblVersion; + private JScrollPane scrollPane1; + private JXHyperlink hyperlinkHomepage; + private JXHyperlink hyperlinkGuiDonation; + private JXHyperlink hyperlinkServerDonation; + private JXHyperlink hyperlinkForum; + private JXHyperlink hyperlinkOnlineHelp; + private JXHyperlink hyperlinkJetBrains; + private JXHyperlink hyperlinkEjTechnologies; + // JFormDesigner - End of variables declaration //GEN-END:variables +} diff --git a/src/main/java/mediathek/gui/dialog/AboutDialog.jfd b/src/main/java/mediathek/gui/dialog/AboutDialog.jfd new file mode 100644 index 0000000000..45df4c85c1 --- /dev/null +++ b/src/main/java/mediathek/gui/dialog/AboutDialog.jfd @@ -0,0 +1,191 @@ +JFDML JFormDesigner: "7.0.7.0.1134" Java: "11.0.15" encoding: "UTF-8" + +new FormModel { + contentType: "form/swing" + root: new FormRoot { + add( new FormWindow( "javax.swing.JDialog", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { + "$layoutConstraints": "fill,insets 5,hidemode 3,gap 5 5" + "$columnConstraints": "[316,fill][fill]" + "$rowConstraints": "[fill][fill][fill]" + } ) { + name: "this" + "title": "Über dieses Programm" + "modal": true + "minimumSize": new java.awt.Dimension( 725, 470 ) + "defaultCloseOperation": 2 + "resizable": false + add( new FormComponent( "javax.swing.JLabel" ) { + name: "label1" + "icon": new com.jformdesigner.model.SwingIcon( 0, "/mediathek/res/MediathekView.png" ) + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" + } ) + add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { + "$layoutConstraints": "insets 0,hidemode 3,gap 0 0" + "$columnConstraints": "[grow,left]" + "$rowConstraints": "[fill][fill]rel[grow,fill]" + } ) { + name: "panel1" + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + add( new FormComponent( "javax.swing.JLabel" ) { + name: "label2" + "text": "MediathekView" + "font": new com.jformdesigner.model.SwingDerivedFont( null, 1, 35, false ) + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" + } ) + add( new FormComponent( "javax.swing.JLabel" ) { + name: "lblVersion" + "text": "Version" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 1" + } ) + add( new FormContainer( "javax.swing.JTabbedPane", new FormLayoutManager( class javax.swing.JTabbedPane ) ) { + name: "tabbedPane1" + "tabLayoutPolicy": 1 + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) { + name: "panel2" + "preferredSize": new java.awt.Dimension( 196, 220 ) + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + add( new FormContainer( "javax.swing.JScrollPane", new FormLayoutManager( class javax.swing.JScrollPane ) ) { + name: "scrollPane3" + "opaque": false + "minimumSize": new java.awt.Dimension( 196, 162 ) + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + add( new FormComponent( "javax.swing.JTextPane" ) { + name: "textPane1" + "text": "MediathekView-Client:\nChristian Franzke (derreisende77)\n\nMediathekView-Server:\nNicklas Wiegandt (nicklas2751)\nPeter W. (pidoubleyou)\nSascha Wiegandt (thesasch)\n\nServer-Administration:\nAlexander Finkhäuser (alex1702)" + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + } ) + }, new FormLayoutConstraints( class java.lang.String ) { + "value": "Center" + } ) + }, new FormLayoutConstraints( null ) { + "title": "aktive Entwickler" + } ) + add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) { + name: "panel3" + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + add( new FormContainer( "javax.swing.JScrollPane", new FormLayoutManager( class javax.swing.JScrollPane ) ) { + name: "scrollPane1" + add( new FormComponent( "javax.swing.JTextPane" ) { + name: "textPane2" + "text": "Gründer des Programms:\nXaver W. (xaverW)\n\nWeitere Beteiligte:\nsiedlerchr\nstyrol\nzxsd\napoleon\nhostis\npmshell\nclel\nthausherr\nklauswich" + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + } ) + }, new FormLayoutConstraints( class java.lang.String ) { + "value": "Center" + } ) + }, new FormLayoutConstraints( null ) { + "title": "ehemalige Mitwirkende" + } ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 2,grow" + } ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 1 0 1 2,grow" + } ) + add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { + "$columnConstraints": "[316,fill]" + "$rowConstraints": "[fill][fill][fill][fill][fill]" + "$layoutConstraints": "fillx,insets 5,hidemode 3,gap 5 5" + } ) { + name: "panel5" + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + add( new FormComponent( "org.jdesktop.swingx.JXHyperlink" ) { + name: "hyperlinkHomepage" + "text": "Homepage" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" + } ) + add( new FormComponent( "org.jdesktop.swingx.JXHyperlink" ) { + name: "hyperlinkGuiDonation" + "text": "Spende an den Entwickler des Programms" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 1" + } ) + add( new FormComponent( "org.jdesktop.swingx.JXHyperlink" ) { + name: "hyperlinkServerDonation" + "text": "Spende für den Server-Betrieb" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 2" + } ) + add( new FormComponent( "org.jdesktop.swingx.JXHyperlink" ) { + name: "hyperlinkForum" + "text": "Hilfe-Forum" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 3" + } ) + add( new FormComponent( "org.jdesktop.swingx.JXHyperlink" ) { + name: "hyperlinkOnlineHelp" + "text": "Online-Anleitung" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 4" + } ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 1,growx" + } ) + add( new FormComponent( "javax.swing.JLabel" ) { + name: "label4" + "text": "Die Entwicklung wird unterstützt von:" + "verticalAlignment": 1 + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 2" + } ) + add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { + "$layoutConstraints": "insets 0,hidemode 3,gap 0 0" + "$columnConstraints": "[grow,left]" + "$rowConstraints": "[fill][fill]" + } ) { + name: "panel4" + auxiliary() { + "JavaCodeGenerator.variableLocal": true + } + add( new FormComponent( "org.jdesktop.swingx.JXHyperlink" ) { + name: "hyperlinkJetBrains" + "text": "JetBrains IntelliJ" + "verticalAlignment": 1 + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0,growx" + } ) + add( new FormComponent( "org.jdesktop.swingx.JXHyperlink" ) { + name: "hyperlinkEjTechnologies" + "text": "ej-technologies install4j & JProfiler" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 1,growx" + } ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 1 2" + } ) + }, new FormLayoutConstraints( null ) { + "location": new java.awt.Point( 0, 0 ) + "size": new java.awt.Dimension( 750, 465 ) + } ) + } +} diff --git a/src/main/java/mediathek/gui/dialog/about/AboutController.kt b/src/main/java/mediathek/gui/dialog/about/AboutController.kt deleted file mode 100644 index 0edcf33d42..0000000000 --- a/src/main/java/mediathek/gui/dialog/about/AboutController.kt +++ /dev/null @@ -1,131 +0,0 @@ -package mediathek.gui.dialog.about - -import javafx.fxml.FXML -import javafx.fxml.FXMLLoader -import javafx.scene.control.Hyperlink -import javafx.scene.control.Label -import javafx.scene.layout.AnchorPane -import mediathek.config.Konstanten -import mediathek.gui.actions.UrlHyperlinkAction -import mediathek.mainwindow.MediathekGui -import org.apache.logging.log4j.LogManager -import java.io.IOException -import java.net.URI -import java.net.URISyntaxException -import javax.swing.SwingUtilities - -class AboutController : AnchorPane() { - companion object { - private val logger = LogManager.getLogger() - } - - @FXML - private lateinit var homepage: Hyperlink - - @FXML - private lateinit var serverDonationHyperlink: Hyperlink - - @FXML - private lateinit var forum: Hyperlink - - @FXML - private lateinit var anleitung: Hyperlink - - @FXML - private lateinit var developerDonationHyperlink: Hyperlink - - @FXML - private lateinit var version: Label - - @FXML - private fun homepageLinkClicked() { - SwingUtilities.invokeLater { - try { - UrlHyperlinkAction.openURI(MediathekGui.ui(), URI(Konstanten.ADRESSE_WEBSITE)) - } catch (uriSyntaxException: URISyntaxException) { - uriSyntaxException.printStackTrace() - } - } - } - - @FXML - private fun developerDonationLinkClicked() { - SwingUtilities.invokeLater { - try { - UrlHyperlinkAction.openURL(MediathekGui.ui(), "https://paypal.me/ChristianFranzke") - } catch (uriSyntaxException: URISyntaxException) { - uriSyntaxException.printStackTrace() - } - } - } - - @FXML - private fun serverDonationLinkClicked() { - SwingUtilities.invokeLater { - try { - UrlHyperlinkAction.openURI(MediathekGui.ui(), URI(Konstanten.ADRESSE_DONATION)) - } catch (uriSyntaxException: URISyntaxException) { - uriSyntaxException.printStackTrace() - } - } - } - - @FXML - private fun forumLinkClicked() { - SwingUtilities.invokeLater { - try { - UrlHyperlinkAction.openURI(MediathekGui.ui(), URI(Konstanten.ADRESSE_FORUM)) - } catch (uriSyntaxException: URISyntaxException) { - uriSyntaxException.printStackTrace() - } - } - } - - @FXML - private fun anleitungLinkClicked() { - SwingUtilities.invokeLater { - try { - UrlHyperlinkAction.openURI(MediathekGui.ui(), URI(Konstanten.ADRESSE_ANLEITUNG)) - } catch (uriSyntaxException: URISyntaxException) { - uriSyntaxException.printStackTrace() - } - } - } - - @FXML - private fun jetbrainsLinkClicked() { - SwingUtilities.invokeLater { - try { - UrlHyperlinkAction.openURL(MediathekGui.ui(), "https://www.jetbrains.com") - } catch (uriSyntaxException: URISyntaxException) { - uriSyntaxException.printStackTrace() - } - } - } - - @FXML - private fun ejLinkClicked() { - SwingUtilities.invokeLater { - try { - UrlHyperlinkAction.openURL(MediathekGui.ui(), "https://www.ej-technologies.com") - } catch (uriSyntaxException: URISyntaxException) { - uriSyntaxException.printStackTrace() - } - } - } - - init { - - try { - val url = javaClass.getResource("/mediathek/res/programm/fxml/about.fxml") - val fxmlLoader = FXMLLoader(url) - fxmlLoader.setRoot(this) - fxmlLoader.setController(this) - fxmlLoader.load() - } catch (e: IOException) { - logger.error("Failed to load FXML!") - } - - version.text = "Version ${Konstanten.MVVERSION}" - } -} \ No newline at end of file diff --git a/src/main/java/mediathek/gui/dialog/about/AboutDialog.kt b/src/main/java/mediathek/gui/dialog/about/AboutDialog.kt deleted file mode 100644 index 0b517ba16a..0000000000 --- a/src/main/java/mediathek/gui/dialog/about/AboutDialog.kt +++ /dev/null @@ -1,25 +0,0 @@ -package mediathek.gui.dialog.about - -import javafx.embed.swing.JFXPanel -import javafx.scene.Scene -import mediathek.javafx.tool.JavaFxUtils -import mediathek.tool.EscapeKeyHandler -import java.awt.BorderLayout -import java.awt.Frame -import javax.swing.JDialog -import javax.swing.SwingUtilities - -class AboutDialog(owner: Frame?) : JDialog(owner, "Über dieses Programm", true) { - init { - defaultCloseOperation = DISPOSE_ON_CLOSE - isResizable = false - EscapeKeyHandler.installHandler(this) { dispose() } - contentPane.layout = BorderLayout() - val fxPanel = JFXPanel() - contentPane.add(fxPanel, BorderLayout.CENTER) - JavaFxUtils.invokeInFxThreadAndWait { - fxPanel.scene = Scene(AboutController()) - SwingUtilities.invokeLater { pack() } - } - } -} \ No newline at end of file diff --git a/src/main/resources/mediathek/res/programm/fxml/about.fxml b/src/main/resources/mediathek/res/programm/fxml/about.fxml deleted file mode 100644 index 15b48be839..0000000000 --- a/src/main/resources/mediathek/res/programm/fxml/about.fxml +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -